commit
6f2da36b10
@ -0,0 +1,123 @@
|
|||||||
|
# Kaleidoscope-TypingBreaks
|
||||||
|
|
||||||
|
Typing on the keyboard for an extended period of time may lead to injuries,
|
||||||
|
which is why it is highly recommended to take frequent breaks from the
|
||||||
|
keyboard - and from the computer as well. But sometimes - more often than one
|
||||||
|
would wish to admit - we tend to forget about this, and plow through, at the
|
||||||
|
cost of hand's health.
|
||||||
|
|
||||||
|
No more.
|
||||||
|
|
||||||
|
With the `TypingBreaks` plugin, we can instruct the keyboard to lock itself up
|
||||||
|
after some time, or after a number of key presses. It will stay locked for a few
|
||||||
|
minutes (or whatever amount we told it to), forcing us to take a break.
|
||||||
|
|
||||||
|
## Using the plugin
|
||||||
|
|
||||||
|
The plugin comes with reasonable defaults (see below), and can be used out of
|
||||||
|
the box, without any further configuration:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <Kaleidoscope.h>
|
||||||
|
#include <Kaleidoscope-EEPROM-Settings.h>
|
||||||
|
#include <Kaleidoscope-TypingBreaks.h>
|
||||||
|
|
||||||
|
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings, TypingBreaks);
|
||||||
|
|
||||||
|
void setup (void) {
|
||||||
|
Kaleidoscope.setup ();
|
||||||
|
|
||||||
|
TypingBreaks.settings.idle_time_limit = 60;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugin methods
|
||||||
|
|
||||||
|
The plugin provides a single object, `TypingBreaks`, with the following
|
||||||
|
properties. All times are in seconds.
|
||||||
|
|
||||||
|
### `.settings.idle_time_limit`
|
||||||
|
|
||||||
|
> The amount of time that can pass between two pressed keys, before the plugin
|
||||||
|
> considers it a new session, and starts all timers and counters over.
|
||||||
|
>
|
||||||
|
> Defaults to 300 seconds (5 minutes).
|
||||||
|
|
||||||
|
### `.settings.lock_time_out`
|
||||||
|
|
||||||
|
> The length of the session, after which the keyboard will be locked.
|
||||||
|
>
|
||||||
|
> Defaults to 2700 seconds (45 minutes).
|
||||||
|
|
||||||
|
### `.settings.lock_length`
|
||||||
|
|
||||||
|
> The length until the keyboard lock is held. Any key pressed while the lock is
|
||||||
|
> active, will be discarded.
|
||||||
|
>
|
||||||
|
> Defaults to 300 seconds (5 minutes).
|
||||||
|
|
||||||
|
### `.settings.left_hand_max_keys`
|
||||||
|
|
||||||
|
> It is possible to lock the keyboard after a number of keys pressed, too. If
|
||||||
|
> this happens sooner than the timeout, the keyboard will still be locked.
|
||||||
|
>
|
||||||
|
> This property controls how many keys can be pressed on the left side.
|
||||||
|
>
|
||||||
|
> Defaults to 0 (off).
|
||||||
|
|
||||||
|
### `.settings.right_hand_max_keys`
|
||||||
|
|
||||||
|
> It is possible to lock the keyboard after a number of keys pressed, too. If
|
||||||
|
> this happens sooner than the timeout, the keyboard will still be locked.
|
||||||
|
>
|
||||||
|
> This property controls how many keys can be pressed on the right side.
|
||||||
|
>
|
||||||
|
> Defaults to 0 (off).
|
||||||
|
|
||||||
|
## Focus commands
|
||||||
|
|
||||||
|
### `typingbreaks.idleTimeLimit [limit]`
|
||||||
|
|
||||||
|
> Get or set the `.settings.idle_time_limit` property.
|
||||||
|
|
||||||
|
### `typingbreaks.lockTimeOut [time_out]`
|
||||||
|
|
||||||
|
> Get or set the `.settings.lock_time_out` property.
|
||||||
|
|
||||||
|
### `typingbreaks.lockLength [length]`
|
||||||
|
|
||||||
|
> Get or set the `.settings.lock_length` property.
|
||||||
|
|
||||||
|
### `typingbreaks.leftMaxKeys [max]`
|
||||||
|
|
||||||
|
> Get or set the `.settings.left_hand_max_keys` property.
|
||||||
|
|
||||||
|
### `typingbreaks.rightMaxKeys [max]`
|
||||||
|
|
||||||
|
> Get or set the `.settings.right_hand_max_keys` property.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
* [Kaleidoscope-EEPROM-Settings](EEPROM-Settings.md)
|
||||||
|
|
||||||
|
## Further reading
|
||||||
|
|
||||||
|
Starting from the [example][plugin:example] is the recommended way of getting
|
||||||
|
started with the plugin.
|
||||||
|
|
||||||
|
[plugin:example]: ../../examples/TypingBreaks/TypingBreaks.ino
|
||||||
|
|
||||||
|
## Upgrading
|
||||||
|
|
||||||
|
Older versions of the plugin used to provide EEPROM storage for the settings
|
||||||
|
only optionally, when it was explicitly enabled via the
|
||||||
|
`TypingBreaks.enableEEPROM()` method. Similarly, the Focus hooks were optional
|
||||||
|
too.
|
||||||
|
|
||||||
|
Both of them are unconditionally enabled now, because they add so much to the
|
||||||
|
plugin. This means that any calls to `TypingBreaks.enableEEPROM()` can be safely
|
||||||
|
removed, the method is a no-op by now.
|
||||||
|
|
||||||
|
Storing the settable settings in EEPROM makes it depend on
|
||||||
|
`Kaleidoscope-EEPROM-Settings`, which should be initialized before this plugin
|
||||||
|
is.
|
@ -0,0 +1,54 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* Kaleidoscope-TypingBreaks -- Enforced typing breaks
|
||||||
|
* Copyright (C) 2017, 2018 Keyboard.io, Inc
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software
|
||||||
|
* Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Kaleidoscope.h>
|
||||||
|
#include <Kaleidoscope-EEPROM-Settings.h>
|
||||||
|
#include <Kaleidoscope-TypingBreaks.h>
|
||||||
|
|
||||||
|
// *INDENT-OFF*
|
||||||
|
const Key keymaps[][ROWS][COLS] PROGMEM = {
|
||||||
|
[0] = KEYMAP_STACKED
|
||||||
|
(
|
||||||
|
Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
|
||||||
|
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
|
||||||
|
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
|
||||||
|
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
|
||||||
|
|
||||||
|
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
|
||||||
|
Key_skip,
|
||||||
|
|
||||||
|
Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip,
|
||||||
|
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
|
||||||
|
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
|
||||||
|
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
|
||||||
|
|
||||||
|
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
|
||||||
|
Key_skip),
|
||||||
|
};
|
||||||
|
// *INDENT-ON*
|
||||||
|
|
||||||
|
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings, TypingBreaks);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Kaleidoscope.setup();
|
||||||
|
|
||||||
|
TypingBreaks.settings.idle_time_limit = 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
Kaleidoscope.loop();
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* Kaleidoscope-TypingBreaks -- Enforced typing breaks
|
||||||
|
* Copyright (C) 2017 Keyboard.io, Inc
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software
|
||||||
|
* Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kaleidoscope/plugin/TypingBreaks.h>
|
@ -0,0 +1,214 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* Kaleidoscope-TypingBreaks -- Enforced typing breaks
|
||||||
|
* Copyright (C) 2017, 2018 Keyboard.io, Inc
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software
|
||||||
|
* Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Kaleidoscope-TypingBreaks.h>
|
||||||
|
#include <Kaleidoscope-EEPROM-Settings.h>
|
||||||
|
#include <Kaleidoscope-FocusSerial.h>
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
namespace plugin {
|
||||||
|
|
||||||
|
TypingBreaks::settings_t TypingBreaks::settings = {
|
||||||
|
.idle_time_limit = 300, // 5m
|
||||||
|
.lock_time_out = 2700, // 45m
|
||||||
|
.lock_length = 300, // 5m
|
||||||
|
.left_hand_max_keys = 0,
|
||||||
|
.right_hand_max_keys = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t TypingBreaks::session_start_time_;
|
||||||
|
uint32_t TypingBreaks::last_key_time_;
|
||||||
|
uint32_t TypingBreaks::lock_start_time_;
|
||||||
|
uint16_t TypingBreaks::left_hand_keys_;
|
||||||
|
uint16_t TypingBreaks::right_hand_keys_;
|
||||||
|
uint16_t TypingBreaks::settings_base_;
|
||||||
|
|
||||||
|
EventHandlerResult TypingBreaks::onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state) {
|
||||||
|
uint32_t lock_length = settings.lock_length * 1000;
|
||||||
|
uint32_t idle_time_limit = settings.idle_time_limit * 1000;
|
||||||
|
uint32_t lock_time_out = settings.lock_time_out * 1000;
|
||||||
|
|
||||||
|
// If we are locked, and didn't time out yet, no key has to be pressed.
|
||||||
|
if (lock_start_time_ && (millis() - lock_start_time_ <= lock_length)) {
|
||||||
|
return EventHandlerResult::EVENT_CONSUMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are locked...
|
||||||
|
if (lock_start_time_) {
|
||||||
|
// ...and the lock has not expired yet
|
||||||
|
if (millis() - lock_start_time_ <= lock_length) {
|
||||||
|
return EventHandlerResult::EVENT_CONSUMED; // remain locked
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...otherwise clear the lock
|
||||||
|
lock_start_time_ = 0;
|
||||||
|
left_hand_keys_ = right_hand_keys_ = 0;
|
||||||
|
session_start_time_ = millis();
|
||||||
|
|
||||||
|
TypingBreak(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any other case, we are not locked yet! (or we just unlocked)
|
||||||
|
|
||||||
|
// Are we still in the same session?
|
||||||
|
if (last_key_time_ && (millis() - last_key_time_) >= idle_time_limit) {
|
||||||
|
// No, we are not. Clear timers and start over.
|
||||||
|
lock_start_time_ = 0;
|
||||||
|
left_hand_keys_ = right_hand_keys_ = 0;
|
||||||
|
session_start_time_ = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a limit on the left hand, and we reached it, lock up!
|
||||||
|
if (settings.left_hand_max_keys && left_hand_keys_ >= settings.left_hand_max_keys) {
|
||||||
|
lock_start_time_ = millis();
|
||||||
|
TypingBreak(true);
|
||||||
|
return EventHandlerResult::EVENT_CONSUMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a limit on the right hand, and we reached it, lock up!
|
||||||
|
if (settings.right_hand_max_keys && right_hand_keys_ >= settings.right_hand_max_keys) {
|
||||||
|
lock_start_time_ = millis();
|
||||||
|
TypingBreak(true);
|
||||||
|
return EventHandlerResult::EVENT_CONSUMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lock_time_out) {
|
||||||
|
// Is the session longer than lock_time_out?
|
||||||
|
if (millis() - session_start_time_ >= lock_time_out) {
|
||||||
|
// Yeah, it is.
|
||||||
|
lock_start_time_ = last_key_time_;
|
||||||
|
TypingBreak(true);
|
||||||
|
return EventHandlerResult::EVENT_CONSUMED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// So it seems we did not need to lock up. In this case, lets increase key
|
||||||
|
// counters if need be.
|
||||||
|
|
||||||
|
if (keyToggledOn(key_state)) {
|
||||||
|
if (col <= COLS / 2)
|
||||||
|
left_hand_keys_++;
|
||||||
|
else
|
||||||
|
right_hand_keys_++;
|
||||||
|
last_key_time_ = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventHandlerResult TypingBreaks::onSetup() {
|
||||||
|
settings_base_ = ::EEPROMSettings.requestSlice(sizeof(settings));
|
||||||
|
|
||||||
|
// If idleTime is max, assume that EEPROM is uninitialized, and store the
|
||||||
|
// defaults.
|
||||||
|
uint32_t idle_time;
|
||||||
|
EEPROM.get(settings_base_, idle_time);
|
||||||
|
if (idle_time == 0xffffffff) {
|
||||||
|
EEPROM.put(settings_base_, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
EEPROM.get(settings_base_, settings);
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FOCUS_HOOK_TYPINGBREAKS FOCUS_HOOK(TypingBreaks.focusHook, \
|
||||||
|
"typingbreaks.idleTimeLimit\n" \
|
||||||
|
"typingbreaks.lockTimeOut\n" \
|
||||||
|
"typingbreaks.lockLength\n" \
|
||||||
|
"typingbreaks.leftMaxKeys\n" \
|
||||||
|
"typingbreaks.rightMaxKeys")
|
||||||
|
|
||||||
|
EventHandlerResult TypingBreaks::onFocusEvent(const char *command) {
|
||||||
|
enum {
|
||||||
|
IDLE_TIME_LIMIT,
|
||||||
|
LOCK_TIMEOUT,
|
||||||
|
LOCK_LENGTH,
|
||||||
|
LEFT_MAX,
|
||||||
|
RIGHT_MAX,
|
||||||
|
} subCommand;
|
||||||
|
|
||||||
|
if (::Focus.handleHelp(command, PSTR("typingbreaks.idleTimeLimit\n"
|
||||||
|
"typingbreaks.lockTimeOut\n"
|
||||||
|
"typingbreaks.lockLength\n"
|
||||||
|
"typingbreaks.leftMaxKeys\n"
|
||||||
|
"typingbreaks.rightMaxKeys")))
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
|
||||||
|
if (strncmp_P(command, PSTR("typingbreaks."), 13) != 0)
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
if (strcmp_P(command + 13, PSTR("idleTimeLimit")) == 0)
|
||||||
|
subCommand = IDLE_TIME_LIMIT;
|
||||||
|
else if (strcmp_P(command + 13, PSTR("lockTimeOut")) == 0)
|
||||||
|
subCommand = LOCK_TIMEOUT;
|
||||||
|
else if (strcmp_P(command + 13, PSTR("lockLength")) == 0)
|
||||||
|
subCommand = LOCK_LENGTH;
|
||||||
|
else if (strcmp_P(command + 13, PSTR("leftMaxKeys")) == 0)
|
||||||
|
subCommand = LEFT_MAX;
|
||||||
|
else if (strcmp_P(command + 13, PSTR("rightMaxKeys")) == 0)
|
||||||
|
subCommand = RIGHT_MAX;
|
||||||
|
else
|
||||||
|
return EventHandlerResult::OK;
|
||||||
|
|
||||||
|
switch (subCommand) {
|
||||||
|
case IDLE_TIME_LIMIT:
|
||||||
|
if (Serial.peek() == '\n') {
|
||||||
|
Serial.println(settings.idle_time_limit);
|
||||||
|
} else {
|
||||||
|
settings.idle_time_limit = Serial.parseInt();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LOCK_TIMEOUT:
|
||||||
|
if (Serial.peek() == '\n') {
|
||||||
|
Serial.println(settings.lock_time_out);
|
||||||
|
} else {
|
||||||
|
settings.lock_time_out = Serial.parseInt();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LOCK_LENGTH:
|
||||||
|
if (Serial.peek() == '\n') {
|
||||||
|
Serial.println(settings.lock_length);
|
||||||
|
} else {
|
||||||
|
settings.lock_length = Serial.parseInt();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LEFT_MAX:
|
||||||
|
if (Serial.peek() == '\n') {
|
||||||
|
Serial.println(settings.left_hand_max_keys);
|
||||||
|
} else {
|
||||||
|
settings.left_hand_max_keys = Serial.parseInt();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RIGHT_MAX:
|
||||||
|
if (Serial.peek() == '\n') {
|
||||||
|
Serial.println(settings.right_hand_max_keys);
|
||||||
|
} else {
|
||||||
|
settings.right_hand_max_keys = Serial.parseInt();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
EEPROM.put(settings_base_, settings);
|
||||||
|
return EventHandlerResult::EVENT_CONSUMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kaleidoscope::plugin::TypingBreaks TypingBreaks;
|
||||||
|
|
||||||
|
__attribute__((weak)) void TypingBreak(bool isLocked) {
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* Kaleidoscope-TypingBreaks -- Enforced typing breaks
|
||||||
|
* Copyright (C) 2017, 2018 Keyboard.io, Inc
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software
|
||||||
|
* Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Kaleidoscope.h>
|
||||||
|
|
||||||
|
#define _DEPRECATED_MESSAGE_ENABLE_EEPROM \
|
||||||
|
"EEPROM is now enabled automatically, and the .enableEEPROM()\n" \
|
||||||
|
"method is therefore obsolete. You can safely remove it."
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
namespace plugin {
|
||||||
|
|
||||||
|
class TypingBreaks : public kaleidoscope::Plugin {
|
||||||
|
public:
|
||||||
|
TypingBreaks(void) {}
|
||||||
|
|
||||||
|
static void enableEEPROM(void) DEPRECATED(ENABLE_EEPROM) {}
|
||||||
|
|
||||||
|
typedef struct settings_t {
|
||||||
|
uint16_t idle_time_limit;
|
||||||
|
uint16_t lock_time_out;
|
||||||
|
uint16_t lock_length;
|
||||||
|
uint16_t left_hand_max_keys;
|
||||||
|
uint16_t right_hand_max_keys;
|
||||||
|
} settings_t;
|
||||||
|
|
||||||
|
static settings_t settings;
|
||||||
|
|
||||||
|
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state);
|
||||||
|
EventHandlerResult onFocusEvent(const char *command);
|
||||||
|
EventHandlerResult onSetup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static uint32_t session_start_time_;
|
||||||
|
static uint32_t lock_start_time_;
|
||||||
|
static uint32_t last_key_time_;
|
||||||
|
static uint16_t left_hand_keys_;
|
||||||
|
static uint16_t right_hand_keys_;
|
||||||
|
|
||||||
|
static uint16_t settings_base_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern kaleidoscope::plugin::TypingBreaks TypingBreaks;
|
||||||
|
|
||||||
|
void TypingBreak(bool is_locked);
|
Loading…
Reference in new issue