Closed BeatVids closed 8 years ago
very nice algo! I don't think latency is a factor here. Actually getting it implemented might be a little challenging, but that's something @jackhumbert can speak to better than I can.
I think you can try this out by moving leader_time = timer_read();
to just outside the if (!leading && keycode == KC_LEAD)
block in quantum.c
. Here's the diff:
@@ -335,10 +335,10 @@ bool process_record_quantum(keyrecord_t *record) {
#ifndef DISABLE_LEADER
// Leader key set-up
if (record->event.pressed) {
+ leader_time = timer_read();
if (!leading && keycode == KC_LEAD) {
leader_start();
leading = true;
- leader_time = timer_read();
leader_sequence_size = 0;
leader_sequence[0] = 0;
leader_sequence[1] = 0;
You can then change the timeout to 100ms or whatever your preference is. Feel free to mess with other stuff!
That's awesome! @BeatVids let us know what it feels like in practice, maybe we'll make it the default.
Man that's amazing that you thought of it that fast without even testing it out. I'm not confident in writing anything unless I try it out! I'm also surprised it was that simple. Thanks Jack for explaining it so well!
It seems to be working perfect for me. I'm super happy with 250ms for now, it feels really relaxed as I no longer have a strict "deadline", and I can comfortably do a full macro with one hand without being really explosive, but that's just me of course. I do think it's default worthy already!
One further concept I have is to auto break the timer if the last possible macro has been typed. Let's say we have macros A+S and A+S+D. If we use A+S, then we have to wait because there is another possible macro we can use (A+S+D). But if we use A+S+D, there is no other possible macro we can use afterwards, so just run the macro immediately.
*KC_LEAD is pressed* -- *Timer starts at 0*
94ms pass
*KC_A is pressed* -- *Timer is restarted to 0*
90ms pass
*KC_S is pressed* -- *Timer is restarted to 0*
90ms pass
*KC_D is pressed*
*A+S+D is the last possible combination, break, and run macro*
A simpler alternative, is to create a variable to set the maximum number of keys for macros, (3 is already the case currently).
*KC_LEAD is pressed* -- *Timer starts at 0*
94ms pass
*KC_A is pressed* -- *Timer is restarted to 0*
90ms pass
*KC_S is pressed* -- *Timer is restarted to 0*
90ms pass
*KC_D is pressed*
*MAX_LEADER_KEYS (3) HAVE BEEN PRESSED, NO NEED TO WAIT, BREAK, AND RUN MACRO*
With both of these ideas, we no longer have to wait an additional restarted timer. Assuming the timeout is set to 100ms, the macro will run at 274ms, instead of 374ms (in my case 524ms).
This sounds like a challenge, especially the first one, but I completely understand if it will never see the light of day. I hope you like the concept though :)
So the dictionary is actually just a shortcut for the header of an if block:
#define LEADER_DICTIONARY() if (leading && timer_elapsed(leader_time) > LEADER_TIMEOUT)
Rather than using the LEADER_DICTIONARY() {
, I think you could use this instead, which will fire things as soon as the third key is pressed:
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 3)) {
Keep in mind that 3 keys to a sequence was kind of arbitrary - this doesn't really need to have a max, and it could be 2 (for faster executions) or 20 as long as you have the SEQ_*_KEYS to back it up.
Hi Jack! Just tried it! It is indeed very noticeable and snappy once I get to the third key. I will keep it on my keymap forever now, thanks!
I'd love to hear your thoughts on the first method if you have any. It's probably the most efficient way macros can be done. Maybe instead of automatically detecting possible combinations, the user can be responsible for dictating if A+S needs to wait in case the user presses A+S+D, or run A+S immediately. If a setting were to be created that the user can manually toggle, it should be easier than creating a complex algorithm. But it's no biggie, it's only a matter of milliseconds, just another idea!
One last thing I'd like to bring up, the register_code/unregister_code format is the only option for the leader key? I've tried the return MACRO() format, and it doesn't work, so I'm assuming it's the only way.
I ask this because macros can turn into many lines, even if it is short. If there is a cleaner way to put macros in the dictionary, please let me know, otherwise, I'd like to share a function I have in my .bashrc to automatically write our macros for us:
qmkmacro() {
string="$@"
for ((c=0;c<${#string};++c)); do
CAPS=0
char=${string:c:1}
[[ $char =~ [A-Z~!@#$%^\&*()] ]] && CAPS=1
char=$(echo $char | tr '[:lower:]' '[:upper:]')
[[ $char == "" ]] && char="SPC"
[[ $char == "." ]] && char="DOT"
[[ $char == "!" ]] && char="1"
[[ $char == "@" ]] && char="2"
[[ $char == "#" ]] && char="3"
[[ $char == "$" ]] && char="4"
[[ $char == "%" ]] && char="5"
[[ $char == "^" ]] && char="6"
[[ $char == "&" ]] && char="7"
[[ $char == "*" ]] && char="8"
[[ $char == "(" ]] && char="9"
[[ $char == ")" ]] && char="0"
[ $CAPS == 1 ] && echo "register_code(KC_LSFT);"
echo "register_code(KC_$char);"
echo "unregister_code(KC_$char);"
[ $CAPS == 1 ] && echo "unregister_code(KC_LSFT);"
echo
done
}
Then we can run our function like this:
qmkmacro "Ergodox EZ"
Which generates this:
register_code(KC_LSFT);
register_code(KC_E);
unregister_code(KC_E);
unregister_code(KC_LSFT);
register_code(KC_R);
unregister_code(KC_R);
register_code(KC_G);
unregister_code(KC_G);
register_code(KC_O);
unregister_code(KC_O);
register_code(KC_D);
unregister_code(KC_D);
register_code(KC_O);
unregister_code(KC_O);
register_code(KC_X);
unregister_code(KC_X);
register_code(KC_SPC);
unregister_code(KC_SPC);
register_code(KC_LSFT);
register_code(KC_E);
unregister_code(KC_E);
unregister_code(KC_LSFT);
register_code(KC_LSFT);
register_code(KC_Z);
unregister_code(KC_Z);
unregister_code(KC_LSFT);
Doesn't support all the characters yet, but if anyone wants me to, I'll go ahead and work on it more. The only thing that will need tweaking is the last "EZ" at the end, it would be better to share the KC_LSFT for both E and Z, but that's about it!
@BeatVids I believe you can just put #include "action_macro.h"
at the top of your keymap file and put action_macro_play(action_get_macro(&(keyrecord_t){ .event.pressed = 1 }, id, 0);
in your dictionary. If the macro executes something on the key release, put action_macro_play(action_get_macro(&(keyrecord_t){ .event.pressed = 0 }, id, 0);
below that.
Here's something I've been thinking about for text macros:
bool shift_us_qwerty[0x80] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 31
0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, // 32 - 63
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 64 - 95
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 // 96 - 127
};
uint8_t ascii_us_qwerty[0x80] = {
0, 0, 0, 0, 0, 0, 0, 0, KC_BSPC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KC_ESC, 0, 0, 0, 0, // 0 - 31
KC_SPC, KC_1, KC_QUOT, KC_3, KC_4, KC_5, KC_7, KC_QUOT, KC_9, KC_0, KC_8, KC_EQL, KC_COMM, KC_MINS, KC_DOT, // 32 - 46
KC_SLSH, KC_0, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_SCLN, KC_SCLN, KC_COMM, KC_EQL, // 47 - 61
KC_DOT, KC_SLSH, KC_2, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, // 62 - 78
KC_O, KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_6, // 79 - 94
KC_MINS, KC_GRV, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G, KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O, // 95 - 111
KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W, KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_GRV, KC_DEL // 112 - 127
};
void send_string(char str[]) {
for (int i = 0; str[i] != 0; i++) {
if (shift_us_qwerty[str[i]]) {
register_code(KC_LSFT);
register_code(ascii_us_qwerty[str[i]]);
unregister_code(ascii_us_qwerty[str[i]]);
unregister_code(KC_LSFT);
} else {
register_code(ascii_us_qwerty[str[i]]);
unregister_code(ascii_us_qwerty[str[i]]);
}
}
}
You can then use send_string("Mr. Poopy Butthole");
and it should output that string. It supports ASCII, and the dictionaries I have there are for US Qwerty layouts, but others could easily be supported in similarly named arrays. Adding a way to handle other mods/keys should be trivial. Sticking literal keys in <>
, and treating mods as toggles may make sense. Maybe even []
for down and {}
for up or something. I know there are some other macro formats (AHK, etc), so if you think anything would make sense here, let me know.
On the "quick" sequence you described, the SEQ_TWO_KEYS()
is defined like this:
#define SEQ_TWO_KEYS(key1, key2) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == 0)
So if you want to completely ignore the last character, you can do this instead of SEQ_TWO_KEYS
:
if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2))
You'll also need to modify that LEADER_DICTIONARY() {
line to be this:
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 2)) {
The caveat here is that you'll need to put your ending block, this:
leading = false;
leader_end();
inside each of the SEQ
blocks, instead of the above them, like this:
// LEADER_DICTIONARY eqv
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 2)) {
// SEQ_TWO_KEYS eqv
if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2)) {
leading = false;
leader_end();
send_string("Wharb");
}
}
@jackhumbert You stole my idea (or maybe it's the other way around)! I was actually planning to throw the strings in flash and support other characters like \n
.
You can support that by editing ascii_us_qwerty
- the first line are the control characters of the ascii table. \r
is another that could be KC_ENT
. It might be neat to use some others for the mods, but it could be complicated to format vs using some sort of markup (like <>[]{}
).
I was gonna make \r
convert to KC_RETURN
instead, but I haven't had a chance to test if that keycode actually works or not. Why would modifiers other than Shift be useful, though?
I've never seen anyone use KC_RETURN, so I don't know how useful that would be.
There's actually enough room in the control space there that we could store the mod state bits there (0bxxxxx
):
/* Mod bits: 43210
* bit 0 ||||+- Control
* bit 1 |||+-- Shift
* bit 2 ||+--- Alt
* bit 3 |+---- Gui
* bit 4 +----- LR flag(Left:0, Right:1)
*/
We could throw them in the strings like \x04
for KC_LALT
being turned-on/toggled. We wouldn't be able to use \x00
to clear though, since that's the string terminator, but maybe we could use \x7F
instead.
The modifiers can actually be stored in the high nibbles of the characters in the string (I think), as ASCII doesn't use anything past 0x7F
. That might be a little hard to represent, though. Using rarely-used ASCII codes works too, but that feels like an unnecessary hack to me. I guess codes above 0x7F
can be used if the modifiers are to be stored separately. Again, why is this useful? I thought this was a function for sending strings.
That's true - I was debating trying to support some of the extended ascii, though. I think there's enough unused stuff there that we could discard 32 characters for the mod stuff.
This is just an easier way to send text macros - don't be a hater.
I wasn't saying this function wasn't useful; I'm just not too sure why modifiers other than Shift need to be supported.
@BeatVids let me know if that works for you - there's a situation where the leader mode won't exit with those settings if you hit the wrong key sequence. The workaround for that is having another if block at the end of your dictionary (still inside of it though) like this to catch the wrong seq after the timeout:
if (timer_elapsed(leader_time) > LEADER_TIMEOUT) {
leading = false;
leader_end();
}
Hi guys! Sorry, I'm a bit overwhelmed on where to begin now with all the stuff you guys talked about.
So I guess I'll focus on the text macros. I really like the send_string("Mr. Poopy Butthole");
syntax, that looks really promising and very simple. I also don't use qwerty unfortunately, I use Colemak, so would would I have to change?
I'm also open to using @eltang's suggestion for the MACRO() syntax, but I would totally like to give the send_string format a go, since it looks pretty cool, and will be much more convenient in the long run.
Do you use Colemak on your OS, or do have it configured in your keymap? Setting up a ascii_us_colemak
would make sense, but a function to convert qwerty keycodes to colemak ones would be cool too (and something that's been on my list).
To be honest, the main reason why I bought an Ergodox initially was so I can have Colemak configured to the keymap so I no longer have to tamper with the OS. So yes, it's configured on my keymap.
Nice. Then you shouldn't have to do anything - the send_string should work as-is, since it's sending characters directly to your OS.
SWEET! I got it to work! @ezuk I think this is also default worthy. Is there any reason why this isn't in the docs?
The send_string
function? I'm working on adding it now ;)
Awesome! :)
@jackhumbert do you think we should maybe transition the leader to per-key timeout like that? I quite like the approach.
Sure - it's an easy change :)
Oh I see! Sorry about that Jack for being too early to the party :)
After seeing your discussion with @eltang , I never knew there was a difference between RETURN and ENTER. You guys are so funny!
This thread totally derailed, but I'm glad it did, because I always hoped for a function for strings precisely like the send_string one. But about the "last possible combination" macro, I'm not sure if my idea was clear and if you understood it. Also, I personally wasn't able to understand what you suggested. If I really feel like it would benefit the leader key, I'll bring it up again in the future if that's ok.
@BeatVids The only problem I see with the function right now is that it can cause stack overflows if the code calls it too many times. The reason is that all strings are stored in RAM by default, and since each character is one byte, the remaining space for the stack and the heap is reduced considerably.
@jackhumbert You don't mind me making a PR to fix this, do you?
I added the newline as well as tab, so you can do stuff like this:
send_string("if yes\n\tpeanut butter\nelse\n\trice snacks");
This could be easily used to debug things as well.
I was running with the desire of wanting a 2-key sequence to fire as soon as it's hit, ignoring the last key. Otherwise, I wasn't sure what you meant by first method. Feel free to bring it up now or in another thread in the future.
Ok I'll give it another go. Let's say I have I have 3 leader key sequences: AA BB BBB
If I do KC_LEAD, A, A, it would be sweet if the macro ran immediately after the second A is input.
But for KC_LEAD, B, B, I wouldn't want it to run immediately after the second B, since what if the user wanted BBB instead?
So I was thinking maybe something can be made to detect if there is a possibility of another sequence.
Are there any possible sequences after pressing BB? Yes, BBB is left!
Are there any possible sequences after pressing AA? No there's not!
The above algorithm to automatically do this may be too complex, so here's an alternative that may be easier to make: The user can define in their dictionary that AA has permission to run immediately as it is typed. They can also make it so BB runs before BBB, so BBB is impossible to use, but that would be the user's responsibility.
Hope that was clear this time. And if you understood me the first time, my apologies, it would be my fault for not understanding your code in return. If you think this is too much and not worth your time, feel free to say "No Mr. Poopy Butthole, I will not do this!" No worries :)
With the way the dictionary's being stored right now, each of the blocks is run independently, so scanning through them like that isn't possible.
Your second implementation is what I was trying to do earlier, but the structure is designed to do one at time, once everything has ended. Maybe you could make a separate dictionary with the blocks I mentioned before your normal one. Here's an example:
// quick-fire dictionary
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 2)) {
// SEQ_TWO_KEYS eqv
if (leader_sequence[0] == (KC_A) && leader_sequence[1] == (KC_A)) {
leading = false;
leader_end();
send_string("Wharb");
}
}
LEADER_DICTIONARY() {
leading = false;
leader_end();
SEQ_THREE_KEYS(KC_A, KC_A, KC_A) {
send_string("Oh wow");
}
}
In the code above, AAA will never trigger, because AA will always be caught first. You can remove the
leading = false;
leader_end();
lines from the first block, which will allow both to be triggered, but AA will always fire before AAA does. I can see how this might be useful, but it's something we explicitly tried to avoid when writing the implementation.
"Mr. Poopy Butthole" is a Rick & Morty reference - hopefully that wasn't taken as name-calling :)
Sweet! That's just what I was looking for, thanks Jack! Sorry about not understanding you earlier, putting it as a separate dictionary helped me understand what you meant.
I will probably divide my dictionary like so, here is a draft if this helps any github browser in the future:
// Instant One
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 1)) {
if (leader_sequence[0] == (KC_A)) {
leading = false;
leader_end();
send_string("Instant One!\n\t");
}
}
// Instant Two
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 2)) {
//This gets overwritten by Instant One!
if (leader_sequence[0] == (KC_A) && leader_sequence[1] == (KC_A)) {
leading = false;
leader_end();
send_string("Instant Two!\n\t");
}
if (leader_sequence[0] == (KC_B) && leader_sequence[1] == (KC_B)) {
leading = false;
leader_end();
send_string("Instant Two!\n\t");
}
}
// Instant Three
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 3)) {
if (leader_sequence[0] == (KC_C) && leader_sequence[1] == (KC_C) && leader_sequence[1] == (KC_C)) {
leading = false;
leader_end();
send_string("Instant Three!\n\t");
}
}
I didn't get the reference, but there's no way I took it like that. :) Thank you for the help Jack, you helped me A TON today. Good luck with completing the send_string function!
The idea of timeout per key press would make it possible to have temporary layer active as long as you keep pressing keys. This could make using arrows on alpha keys more comfortable as layer with arrow keys would be automatically deactivated when you stop navigation.
@BeatVids Just a heads-up: as of #376, you must use SEND_STRING
rather than send_string
in your keymap.
Thanks @eltang ! Really appreciate the heads up!
What does PSTR do anyways?
#define SEND_STRING(str) send_string(PSTR(str))
Do you know if there are any codes for the brightness keys?
I believe they are considered their own keys, much like VolUp VolDwn, etc.
In Linux they are called XF86MonBrightnessUp + XF86MonBrightnessDown.
They would be useful on devices that use them, such as laptops, etc. I'm surprised they weren't in the extensive key name list, that's the only general thing I can think of that wasn't in it.
That macro forces the strings to stay in the flash memory. Otherwise, they're kept in the RAM during the whole time your keyboard is operating, and you can imagine how much memory that could eat up if you have more than a few strings.
There are, but only for OS X. If you're curious, they're KC_SLCK
and KC_PAUS
.
That makes perfect sense, thanks for clearing that up.
Darn, I don't use OS X, but it's interesting that they use ScrollLock and Pause for that.
Is there a certain reason why brightnessup + brightnessdown are currently omitted?
In OSes like Windows, they're not regular keycodes. Usually, they're implemented with special drivers.
Gotcha Eric. That sucks that they're not regular keycodes. I try to make my keymap work on all OSs identically, as best as possible. Unfortunately, screen brightness won't be a part of that. Thanks for the heads up once again.
did we make the change to time-out on per key basis? I too like that approach.
Not officially (yet), but you can make the one line change mentioned if you'd like to try it out!
I am reopening this because I need to make the change in the default keymap.
With this addition to the default keymap, I recommend adding SEQ_FOUR_KEYS
and even SEQ_FIVE_KEYS
, since getting that many keystrokes will now be feasible.
I personally will probably use some four key combos, but most likely not any more than that, but five keys may benefit others as well.
Some common ones: ASAP, IMHO, and the most important BYOB of course, and whatever else everyone likes using the leader key for :smile:
Sure! Added at 98f0807 :)
Did you know - you can make a sequence out of literally whatever is in your keymap? eg M(1)
, MO(2)
, KC_SCLN
, FN1
etc? :)
That's awesome, even SEQ_ONE_KEY(KC_LEAD) { SEND_STRING("leadception"); }
works :laughing:
Thanks a lot for the four/five additions Jack, really appreciate it. Just tested them, and they work as expected. :tada:
I want to test this modification of the leader key because I think it might prove to be less stressful on my wrist then hammering down a fast paced key sequence.
But… I can't get it to work, here's what I've done:
./qmk_firmware/quantum/process_keycode/process_leader.c
I moved leader_time = timer_read();
like suggested by @jackhumbert here.To test if this mod works I replaced this code in my keymap.c
…
#define LEADER_TIMEOUT 300
LEADER_EXTERNS();
void matrix_scan_user(void) {
// LEADER FUNCTIONS
LEADER_DICTIONARY() {
leading = false;
leader_end();
SEQ_ONE_KEY(KC_I) {
SEND_STRING ("https://");
}
SEQ_THREE_KEYS(KC_M, KC_I, KC_L) {
SEND_STRING ("![]()");
//TAP_ONCE (KC_LEFT); TAP_ONCE (KC_LEFT); TAP_ONCE (KC_LEFT);
}
}
};
… with the example code from @BeatVids, so that I got this:
#define LEADER_TIMEOUT 300
LEADER_EXTERNS();
void matrix_scan_user(void) {
// LEADER FUNCTIONS
// Instant One
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 1)) {
if (leader_sequence[0] == (KC_A)) {
leading = false;
leader_end();
send_string("Instant One!\n\t");
}
}
// Instant Two
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 2)) {
//This gets overwritten by Instant One!
if (leader_sequence[0] == (KC_A) && leader_sequence[1] == (KC_A)) {
leading = false;
leader_end();
send_string("Instant Two!\n\t");
}
if (leader_sequence[0] == (KC_B) && leader_sequence[1] == (KC_B)) {
leading = false;
leader_end();
send_string("Instant Two!\n\t");
}
}
// Instant Three
if (leading && (timer_elapsed(leader_time) > LEADER_TIMEOUT || leader_sequence_size >= 3)) {
if (leader_sequence[0] == (KC_C) && leader_sequence[1] == (KC_C) && leader_sequence[1] == (KC_C)) {
leading = false;
leader_end();
send_string("Instant Three!\n\t");
}
}
};
None of the sequences work.KC_LEAD
->KC_A
yields a SPACE
, not the string. Anyone knows whats missing here?
Hi gentlemen, it's me again with another wacky idea that I hope you would keep in mind!
Right now, the leader key expects everything to be done by a certain amount of time, in this case like you guys provide as an example: 300ms. I'm personally impressed that you guys have it so fast. I would prefer something more comfortable and slow, but unfortunately I would feel the lag too. So I have a suggestion that will be the best of both worlds.
Instead of 300ms for 1-3 keys altogether, how about having the time be for EACH keypress (100ms per key).
For every key pressed after the leader key, the timer is RESET, and it waits another 100ms.
Here's a brief rundown:
In total, 284ms passed, and then the A+S macro was ran.
Single + double macros will be potentially faster than the current state, for example:
In total, 180ms passed, instead of waiting all 300!
But if we want to get to 3 keys now, it may take a little longer (depending on the user), but it will be a lot more relaxed and balanced (100ms per key instead of 300 for 1-3 keys).
The benefit of this is that the leader key will be both time efficient, and relaxed as possible, and will allow us to go beyond SEQ_THREE_KEYS if we wanted to (although I know Erez mentioned that it kills the point; however, that was not a reason for this proposal!)
If I confused you in any way, please let me know. I'm certain latency is a factor, but I don't believe it to be a significant one.