diff --git a/DAEMON.md b/DAEMON.md index 88cf37a..681b212 100644 --- a/DAEMON.md +++ b/DAEMON.md @@ -128,6 +128,37 @@ Macros are a more advanced form of key binding, controlled with the `macro` comm Assigning a macro to a key will cause its binding to be ignored; for instance, `macro a:+b,-b` will cause A to generate a B character regardless of its binding. However, `macro lctrl+a:+b,-b` will cause A to generate a B only when Ctrl is also held down. +### Macro playback delay + +There are two types of playback delay that can be set with macros; global and local. Setting a _global delay_ value introduces a time delay between events during macro execution or playback. _Local delay_ allows setting the delay after an individual event, overriding the global delay value for that event. Thus global delay can be used to set the overall playback speed of macros and local delays can be used to tune individual events within a macro. + +All delay values are specified in microseconds (us) and are positive values from `0` to `UINT_MAX - 1`. This means delays range from 0 to just over 1 hour (4,294,967,294us, 4,294 seconds, 71 minutes, or 1.19 hours). A value of zero (0) represents no delay between actions. + +#### Global macro delay (default delay) + +Global delay allows macro playback speed to be changed. It sets the time between (actually after) each recorded macro event. If global delay is set to 1 microsecond then a 1 ms delay will follow each individual macro event when the macro is triggered. + +The _global delay_ is set with the ckb-daemon's existing (in testing branch) `delay` command followed by an unsigned integer representing the number of microseconds to wait after each macro action and before the next. + +Global delay can also be set to `on` which maintains backwards compatibility with the current development of `ckb-daemon` for long macro playback. That is, setting the global delay to `on` introduces a 30us and a 100us delay based on the macro's length during playback. + +**NOTE**: This setting also introduces a delay after the last macro action. This functionality exists in the current testing branch and was left as-is. It is still to be determined if this is a bug or a feature. + +**Examples:** +* `delay 1000` sets a 1,000us delay between action playback. +* `delay on` sets long macro delay; 30us for actions between 20 and 200, 100us for actions > 200. +* `delay off` sets no delay (same as 0). +* `delay 0` sets no delay (same as off). +* `delay spearmint-potato` is invalid input, sets no delay (same as off). + +#### Local macro delay (keystroke delay) + +Local Delay allows each macro action to have a post-action delay associated with it. This allows a macro to vary it's playback speed for each event. If no local delay is specified for a macro action, then the global `delay` (above) is used. All delay values are in microsecods (us) as with the global delay setting. + +***Examples:*** +* `macro g5:+d,-d,+e=5000,-e,+l,-l=10000,+a,-a,+y,-y=1000000,+enter,-enter` define a macro for `g5` with a 5,000us delay between the `e` down and `e` up actions. A 1,000us delay between `l` up and `a` down, a delay of one second (1,000,000us) after `y` up and before `enter`, and the global delay for all other actions. +* `macro g5:+d,-d=0` use default delay between `d` down and `d` up and no delay (0us) after `d` up. This removes the noted feature/bug (above) where the last action has a trailing delay associated with it. + DPI and mouse settings ---------------------- diff --git a/src/ckb-daemon/command.c b/src/ckb-daemon/command.c index 9acffe7..a58adb4 100644 --- a/src/ckb-daemon/command.c +++ b/src/ckb-daemon/command.c @@ -198,9 +198,20 @@ int readcmd(usbdevice* kb, const char* line){ } continue; } - case DELAY: - kb->delay = (!strcmp (word, "on")); // independendant from parameter to handle false commands like "delay off" + case DELAY: { + long int delay; + if(sscanf(word, "%ld", &delay) == 1 && 0 <= delay && delay < UINT_MAX) { + // Add delay of `newdelay` microseconds to macro playback + kb->delay = (unsigned int)delay; + } else if(strcmp(word, "on") == 0) { + // allow previous syntax, `delay on` means use old `long macro delay` + kb->delay = UINT_MAX; + } else { + // bad parameter to handle false commands like "delay off" + kb->delay = 0; // No delay. + } continue; + } default:; } diff --git a/src/ckb-daemon/input.c b/src/ckb-daemon/input.c index 78a5c39..2335170 100644 --- a/src/ckb-daemon/input.c +++ b/src/ckb-daemon/input.c @@ -35,9 +35,16 @@ static void inputupdate_keys(usbdevice* kb){ os_mousemove(kb, action->rel_x, action->rel_y); else { os_keypress(kb, action->scan, action->down); - if (kb->delay) { - if (a > 200) usleep (100); - else if (a > 20) usleep(30); + if (action->delay != UINT_MAX) { // local delay set + usleep(action->delay); + } else if (kb->delay != UINT_MAX) { // use default global delay + usleep(kb->delay); + } else { // use old long macro delay code + if (a > 200) { + usleep (100); + } else if (a > 20) { + usleep(30); + } } } } @@ -241,7 +248,7 @@ static void _cmd_macro(usbmode* mode, const char* keys, const char* assignment){ int empty = 1; int left = strlen(keys), right = strlen(assignment); int position = 0, field = 0; - char keyname[12]; + char keyname[24]; while(position < left && sscanf(keys + position, "%10[^+]%n", keyname, &field) == 1){ int keycode; if((sscanf(keyname, "#%d", &keycode) && keycode >= 0 && keycode < N_KEYS_INPUT) @@ -276,9 +283,23 @@ static void _cmd_macro(usbmode* mode, const char* keys, const char* assignment){ // Scan the actions position = 0; field = 0; - while(position < right && sscanf(assignment + position, "%11[^,]%n", keyname, &field) == 1){ + // max action = old 11 chars plus 12 chars which is the max 32-bit int 4294967295 size + while(position < right && sscanf(assignment + position, "%23[^,]%n", keyname, &field) == 1){ if(!strcmp(keyname, "clear")) break; + + // Check for local key delay of the form '[+-]=' + long int long_delay; // scanned delay value, used to keep delay in range. + unsigned int delay = UINT_MAX; // computed delay value. UINT_MAX means use global delay value. + char real_keyname[12]; // temp to hold the left side (key) of the = + int scan_matches = sscanf(keyname, "%11[^=]=%ld", real_keyname, &long_delay); + if (scan_matches == 2) { + if (0 <= long_delay && long_delay < UINT_MAX) { + delay = (unsigned int)long_delay; + strcpy(keyname, real_keyname); // keyname[24], real_keyname[12] + } + } + int down = (keyname[0] == '+'); if(down || keyname[0] == '-'){ int keycode; @@ -287,6 +308,7 @@ static void _cmd_macro(usbmode* mode, const char* keys, const char* assignment){ // Set a key numerically macro.actions[macro.actioncount].scan = keymap[keycode].scan; macro.actions[macro.actioncount].down = down; + macro.actions[macro.actioncount].delay = delay; macro.actioncount++; } else { // Find this key in the keymap @@ -294,6 +316,7 @@ static void _cmd_macro(usbmode* mode, const char* keys, const char* assignment){ if(keymap[i].name && !strcmp(keyname + 1, keymap[i].name)){ macro.actions[macro.actioncount].scan = keymap[i].scan; macro.actions[macro.actioncount].down = down; + macro.actions[macro.actioncount].delay = delay; macro.actioncount++; break; } diff --git a/src/ckb-daemon/structures.h b/src/ckb-daemon/structures.h index 0a284e5..e145a07 100644 --- a/src/ckb-daemon/structures.h +++ b/src/ckb-daemon/structures.h @@ -28,6 +28,7 @@ typedef struct { short scan; // Key scancode, OR short rel_x, rel_y; // Mouse movement char down; // 0 for keyup, 1 for keydown (ignored if rel_x != 0 || rel_y != 0) + uint delay; // us delay after action; UINT_MAX for use global delay } macroaction; // Key macro @@ -247,7 +248,7 @@ typedef struct { // Color dithering in use char dither; // Flag to check, if large macros should be sent delayed - char delay; + uint delay; } usbdevice; #endif // STRUCTURES_H