-
Notifications
You must be signed in to change notification settings - Fork 0
Home
-
Check which device you would like to use with
sudo evtest
-
The first line compiles the code including relevant libraries.
Then the second line runs the program on event##. Additionally the -E option preserves the environment.*
gcc $(pkg-config --libs --cflags libevdev) -o evmorse evmorse.c
sudo -E ./evmorse $(id -u) /dev/input/event0
- Try typing! Once you see the output you can get a better understanding of when commands execute.
*$(id -u) passes through the user id to the program.
This is necessary for lowering the permissions from sudo to match the user which is helpful when running commands such as playerctl.
Once setuid is used to change from root to the uid it is impossible to escalate back to root privilege. Setuid documentation
IMPORTANT!
Keycodes are 8 higher in X11 than in the kernel (Ex. Numpad minus is 82 in xev, but 74 in evtest/evmorse)
Variables [nlstate, shiftstate, ctrlstate, altstate] can be used to have different behavior when modifiers are pressed or the numlock is on/off.
The code is separated into two functions.
run_hold -- For long key presses we can execute a command as the key is held down (think of a fast-forward function on a remote)
run_pattern -- This is for executing commands based on a morse pattern (01 = short | long; 110 = long | long | short; etc.)
For a key that is defined in both functions we may not want to run the pattern after the hold function, thus we must reset the value of the pattern variable with
memset(pattern, 0, sizeof(pattern));
Here is a general example of how bindings might be set.
static void run_hold() {
int len = strlen(pattern);
+ // vvv Your code goes here vvv
switch (keyc) {
case 163:
! system("playerctl position 5+");
! memset(pattern, 0, sizeof(pattern));
break;
case 165:
system("playerctl position 5-");
memset(pattern, 0, sizeof(pattern));
break;
}
+ // ^^^ Your code goes here ^^^
}
static void run_pattern() {
int len = strlen(pattern);
if (len >= 1) {
+ // vvv Your code goes here vvv
switch (keyc) {
case 164:
system("playerctl play-pause");
break;
case 163:
! for(int i = 0; i < len; i++) {
! system("playerctl next");
! }
break;
case 165:
for(int i = 0; i < len; i++) {
system("playerctl previous");
}
break;
}
+ // ^^^ Your code goes here ^^^
memset(pattern, 0, sizeof(pattern));
}
}
keyc
is the variable holding the key code value.
system()
allows us to run commands as if they were run from the terminal.
Commands are ran as the UID returned by $(id -u).
The above example enables:
Key 163 -
- Key press results in "playerctl next" executing (next song)
- Holding the key results in "playerctl position 5+" (in run_hold) executing.
- This is then repeated allowing for a fast forward action
- The command is followed by a
memset
which prevents the pattern from running afterwards. (No next song after release)
Another important operator is strcmp()
.
This allows us to compare the pattern with a str to separate by the morse pattern. Ex:
if (strcmp(pattern, "00") == 0) { // double short press
system("");
} else if (strcmp(pattern, "1") == 0) { // one long press
system("");
} else if (!strcmp(pattern, "01")) { // Alternative way of writing strcmp (short, long)/ ! == not
}
There are various methods, each with their own merits / drawbacks.
Disabling Keys
The expiration timing from no key press is defined in timer.it_value.tv_sec
and timer.it_value.tv_usec
The interval
value controls the interval on which the run_hold function is ran.
A key press would have a value of 1 after around 250ms. If we keep holding for around 300ms (30ms x 10; default interval is 10) the run_hold function is executed.
} else if (hold % interval == (interval - 1)) {
The formula in this line can be altered to delay the first trigger of run_hold by subtracting from hold
like so:
} else if ((hold-5) % interval == (interval - 1)) {
Referring back to the earlier example, holding for 550ms would fast forward and repeat every 300ms; whereas, any key presses released prior to 550ms would go to the next song (after the expiration timing; default 300ms).
If my explanation of timings suck here is a picture that hopefully sucks less.