Closed ezuk closed 4 years ago
I would love for a way to support this with a timeout rather than key up (perhaps in addition to?), that way I could press and hold a "key"
Timeouts are kind of difficult on the implementation side of things (no event to go on, just a callback), but yeah, it'd be neat to have that in combination with the keyup.
I think we can have timeout for this as it won't conflict with a following key press, I mean if the key is hold and another non matching key is pressed we interrupt the timer and just cancel the hold. Now that I read what I wrote I wonder what would be the benefit of holding a key in a two keys chording. @Tuckie can you explain what you are thinking about? I have a working timer/timeout code here I came with because I tried to implement a "clean" double tap combined with single tap and mods on hold on one single key (which of course failed because you'll then need a buffer to store key pressed while the timeout has not ran out to identify the single tap, works great for slooow typists though :)). So basically the timeout routine uses ATMEGA32 TIMER3 and can be stopped from the outside
/* timer3.h
* f. marlier 2016
*/
void tap_timeout(uint8_t ms);
void timer3_stop(void);
bool halted_from_code;
/* timer3.c
* f. marlier 2016
* ATMEGA32U4 8 bit AVR 16 MHz
* CTC mode, the OCR2 defines the top value for the counter hence it resolution.
* an interrupt can be generated using the OFC2 value.
* The interrupt handling routine can be used to for updating the top value.
*
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include "debug.h"
#include "timer3.h"
static uint8_t timer3_out;
volatile uint8_t timer3_count;
bool halted_from_code;
void timer3_init(void)
{
TCCR3A = 0x0;
// prescaler fCPU/64 | CTC top OCR3A update immediate TOV3 Max
TCCR3B = (1 << CS30)|(1 << CS31)|(1 << WGM32);
// 250 increments to go to 1ms
OCR3A = 250;
}
void timer3_start(void)
{
TIMSK3 |= (1 << OCIE3A);
dprintf("TIMER STARTED\n");
}
void timer3_stop(void)
{
halted_from_code=true;
dprintf("TIMER HALTED FROM OUTSIDE\n");
}
void tap_timeout(uint8_t ms)
{
timer3_out = ms;
timer3_count = 0;
halted_from_code = false;
uint8_t timer3_count_local;
timer3_init();
timer3_start();
// loop
while (timer3_count_local < timer3_out && !halted_from_code) {
timer3_count_local = timer3_count;
// scan for input
keyboard_task();
dprintf("timer3(%u)\n",timer3_count);
}
TCCR3B = 0x0;
dprintf("TIMER HALTED\n");
}
ISR(TIMER3_COMPA_vect)
{
timer3_count++;
}
One thing @jackhumbert and I tried to do here is keep it simple, to get a basic implementation out and nail the macro syntax and the data structure. That's also why we want to limit to just two-key chords at first. I think once we have something simple working we can go from there, just imho.
can you explain what you are thinking about?
@mecanogrh Mainly in use for software that treats a held button differently than a tap.
@Tuckie mmm you mean Autokeys or Karabiner? I actually got a planck to get rid of these :) Are we really talking about planting hooks to be able to support software input handlers?!
@mecanogrh no, just key mapping in games :smiley: (where the mapping is hard-coded for held keys)
I have some ideas.
matrix_key_count
on every key release. If it returns zero, a chord has just been finished.@ezuk What is your goal with this? A firmware implementation of Plover?
No.
On Wed, May 18, 2016, 23:26 Eric Tang notifications@github.com wrote:
@ezuk https://github.com/ezuk What is your goal with this? A firmware implementation of Plover?
— You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub https://github.com/jackhumbert/qmk_firmware/issues/168#issuecomment-220218890
It's for keyboard shortcuts, not steno.
FYI, @FromtonRouge has implemented a cool chording system in QMK:
https://github.com/FromtonRouge/qmk_firmware/tree/master/keyboard/ergodox_ez/keymaps/fromtonrouge
I have an idea how to implement chording simply, for any reasonable (say, 24, but easily changeable with overriding a #define
) number of keys. Wouldn't support timeouts, though. Will need to experiment a bit with some C preprocessor macro magic to see if I can make it easy to use too.
(My goal is to be able to implement Plover or another Steno variant in the firmware, or at least, be able to do so, using the chording support as a base.)
What's the current status of this? Looks like process_chording.c
hardcodes KC_A
for an enter+space chord…
There's some existing chording stuff, what I want to do, exists in my head only for now. Hopefully I'll have some time tomorrow to prepare a PoC.
Any progress on chording ideas? I would love the ability to map two simultaneous key presses (KC_S and KC_D) to temporarily switching to a different layer when held down. This would enable me to keep my hands on the home row when the layer I switch to maps hjkl to VIM-style arrow keys.
Hi! Any update on this? I'm wanting to set up a Plover/steno based one handed chording layout. @algernon, the idea of being able to set up Plover in firmware is super exciting!
@wezzynl It's not QMK solution but if you are on Mac, you can look into "karabiner-element. It's a very low level keyboard tweak https://pqrs.org/osx/karabiner/
Here are some of the example https://pqrs.org/osx/karabiner/complex_modifications/ (search for Vim)
@dunkarooftop
Hey, thanks for the heads-up. However, it is possible now to at least do what I want with process_combo
. E.g. holding down the S and D keys simultanously will go into SuperDuper mode and shifts layers so H, J, K, L become arrow keys and some more fun stuff.
See this issue: #1214
@dunkarooftop LOL, I only just noticed that #1214 is actually your own issue :) 👍
I was also playing around with the timers while looking for a way to execute some code once certain delay passes. The closest I managed to do is using matrix_scan_user
. I was thinking if something similar exists already.
Here's the code I use for my preonic:
#include "preonic.h"
// Scheduler implementation
// Usage:
// run_after(150, cb, 1337)
#define SCHED_SIZE 16
typedef void (*SCHEDULED_CALLBACK)(int);
typedef struct scheduled_t {
int timer;
int ms;
int data;
SCHEDULED_CALLBACK cb;
} SCHEDULED;
SCHEDULED scheduled[SCHED_SIZE] = {0};
int sched_i = 0;
int run_after(int ms, SCHEDULED_CALLBACK cb, int data) {
int index = sched_i;
SCHEDULED sch = {.timer = timer_read(), .ms = ms, .data = data, .cb = cb};
scheduled[sched_i] = sch;
sched_i = (sched_i + 1) % SCHED_SIZE;
return index;
}
void cancel(int id) {
scheduled[id].cb = 0;
}
bool cancelled(int id) {
return scheduled[id].cb == 0;
}
void scheduler_process(void) {
for (int i = 0; i < SCHED_SIZE; i++) {
if (scheduled[i].cb && timer_elapsed(scheduled[i].timer) > scheduled[i].ms) {
SCHEDULED_CALLBACK cb = scheduled[i].cb;
scheduled[i].cb = 0;
cb(scheduled[i].data);
}
}
}
void matrix_scan_user(void) {
scheduler_process();
}
Worked pretty great.
@eltang & @millipz : About plover inside the firmware: I don't think the plover dictionary fits on my Ergodox EZ shine. On the github project openstenoproject/plover, plover is claimed to have 140,000 dictionary entries. My compiler tells me that there is 32,256 bytes for the firmware, meaning you would have to compress a dictionary entry into less than two bits. You can try to accomplish this by making some standard rule and then save the exceptions, so the word "tub" doens't need a dictionary entry.
I am busy making a chorded keyboard (fhombsch/qmk_firmware:dev_branch on ergodox_ez keybaord) using the Macro feature, so my complete keyboard is handled by the process_record_user() function. It doesn't contain timers. My approach might be useful for macros, as well. It is not much limited in the number of keys to be pressed simultaneously.
I use small dictionaries for initial consonant, vowel and final consonant separately, and they don't need to store natural combinations. This makes up for a total of about 300 dictionary entries. For symbols, I just use more basic lookup tables which would also be suitable for macros. So I am done with roughly 500×8 bytes = 4k memory for the keymap itself. Maybe parts of my code can be useful for you. Kind regards, Fabian
This issue has been automatically marked as stale because it has not had activity in the last 90 days. It will be closed in the next 30 days unless it is tagged properly or other activity occurs.
For maintainers: Please label with bug
, in progress
, on hold
, discussion
or to do
to prevent the issue from being re-flagged.
This issue has been automatically closed because it has not had activity in the last 30 days. If this issue is still valid, re-open the issue and let us know.
Some notes from a call with @jackhumbert on chording:
would be good to implement chording as
CC_
(instead ofKC_
). And then a key whereCC_
is used would only fire on keyup.There would be a dictionary under the layout that looks something like:
If you define
CC_S
for example but then not define a chord for it, it would just send S on keyup. This way you get consistent behavior in your layout if you want it -- all keys firing on keyup if you want them to.We would not make a distinction about which key in the chord gets pressed first.
We would support up to 2 keys per individual chord initially.
You'd need to include
chording.h
to enable this on your layout.There would be a makefile option to flip this off to save on filesize if needed.