I just wrote this in C:
#include <stdio.h>
#include <curses.h>
#include <time.h> //time(0)
#include <sys/time.h> // gettimeofday()
#include <stdlib.h>
void waitFor (unsigned int secs) {
//credit: http://stackoverflow.com/a/3930477/1074998
unsigned int retTime = time(0) + secs; // Get finishing time.
while (time(0) < retTime); // Loop until it arrives.
}
int
main(void) {
struct timeval t0, t1, t2, t3;
double elapsedTime;
clock_t elapsed_t = 0;
int c = 0x35;
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
halfdelay(5); //increae the number if not working //adjust below `if (elapsedTime <= 0.n)` if this changed
printf("\nSTART again\n");
elapsed_t = 0;
gettimeofday(&t0, NULL);
float diff;
int first = 1;
int atleast_one = 0;
while( getch() == c) { //while repeating same char, else(ffff ffff in my system) break
int atleast_one = 1;
if (first == 1) {
gettimeofday(&t1, NULL);
first = 0;
}
//printf("DEBUG 1 %x!\n", c);
gettimeofday(&t2, NULL);
elapsedTime = (t2.tv_sec - t1.tv_sec) + ((t2.tv_usec - t1.tv_usec)/1000000.0);
if (elapsedTime > 1) { //hit max time
printf("Hit Max, quit now. %f\n", elapsedTime);
system("gnome-terminal");
//waitFor(4);
int cdd;
while ((cdd = getch()) != '\n' && cdd != EOF);
endwin();
exit(0);
}
if(halfdelay(1) == ERR) { //increae the number if not working
//printf("DEBUG 2\n");
//waitFor(4);
break;
}
else {
//printf("DEBUG 3\n");
}
}
if (atleast_one == 0) {
//gettimeofday(&t1, NULL);
t1 = t0;
}
gettimeofday(&t3, NULL);
elapsedTime = (t3.tv_sec - t1.tv_sec) + ((t3.tv_usec - t1.tv_usec)/1000000.0);
printf("Normal quit %f\n", elapsedTime);
if (elapsedTime > 0.6) { //this number based on halfdelay above
system("gedit &");
//system("xdotool key shift+left &");
//system("mplayer -vo caca -quiet 'video.mp4' &");
//waitFor(4);
}
else if (elapsedTime <= 0.6) {
system("xdotool key ctrl+shift+t &");
//waitFor(4);
}
int cdd;
while ( (cdd = getch() ) != '\n' && cdd != EOF);
endwin();
return 0;
}
Use showkey -a
to get the bind keycode:
xb@dnxb:/tmp$ sudo showkey -a
Press any keys - Ctrl-D will terminate this program
^[[24~ 27 0033 0x1b #pressed F12
91 0133 0x5b
50 0062 0x32
52 0064 0x34
126 0176 0x7e
5 53 0065 0x35 #pressed Numpad 5, 5 is the keycode used in `bind`
^C 3 0003 0x03
^D 4 0004 0x04
xb@dnxb:/tmp$
Put the bind keycode 5 and its command(e.g. run /tmp/.a.out
) in ~/.bashrc:
bind '"5":"/tmp/a.out\n"'
Note that relevant keycode need to change in the source code too (the hex value can get from sudo showkey -a
above too):
int c = 0x35;
Compile with (output to /tmp/a.out
in my example):
cc filename.c -lcurses
Demonstration:
Numpad 5, short press open new tab, medium press open gedit, and long press open gnome-terminal.
This is not direct applicable in any window on gnome desktop manager, but i think it should give you some idea how (hard) to implement it. It work in Virtual Console(Ctrl+Alt+N) too, and work in some terminal emulator (e.g. konsole, gnome-terminal, xterm).
p/s: I'm not a c programmer, so forgive me if this code is not optimized.
[UPDATE]
The previous answer only work in shell and required focus, so i think parse the /dev/input/eventX is the solution to work in entire X session.
I don't want to reinvent the wheel. I play around with evtest
utility and modified the bottom part of evtest.c with my own code:
int onHold = 0;
struct timeval t0;
double elapsedTime;
int hitMax = 0;
while (1) {
rd = read(fd, ev, sizeof(struct input_event) * 64);
if (rd < (int) sizeof(struct input_event)) {
perror("\nevtest: error reading");
return 1;
}
system("echo 'running' >/tmp/l_is_running 2>/tmp/l_isrunning_E &");
for (i = 0; i < rd / sizeof(struct input_event); i++) {
//system("date >/tmp/l_date 2>/tmp/l_dateE &");
if (ev[i].type == EV_KEY) {
if ( (ev[i].code == 76) ) {
if (!onHold) {
onHold = 1;
t0 = ev[i].time;
hitMax = 0;
}
if (!hitMax) { //to avoid hitMax still do the time checking instruction, you can remove hitMax checking if you think it's overkill, but still hitMax itself is necessary to avoid every (max) 2 seconds will repeatly system();
elapsedTime = (ev[i].time.tv_sec - t0.tv_sec) + ((ev[i].time.tv_usec - t0.tv_usec)/1000000.0);
printf("elapsedTime: %f\n", elapsedTime);
if (elapsedTime > 2) {
hitMax = 1;
printf("perform max time action\n");
system("su - xiaobai -c 'export DISPLAY=:0; gedit &'");
}
}
if (ev[i].value == 0) {
printf("reseted ...... %d\n", ev[i].value);
onHold = 0;
if (!hitMax) {
if (elapsedTime > 1) { //just ensure lower than max 2 seconds
system("su - xiaobai -c 'export DISPLAY=:0; gnome-terminal &'");
} else if (elapsedTime > 0.5) {
system("su - xiaobai -c \"export DISPLAY=:0; vlc '/home/xiaobai/Downloads/videos/test/Pokémon Red_Blue_Yellow Gym Leader Battle Theme Remix-CbJTkx7QUJU.mp4' &\"");
} else if (elapsedTime > 0.2) {
system("su - xiaobai -c 'export DISPLAY=:0; nautilus &'");
}
} else { //else's max system() already perform
hitMax = 0;
}
}
}
}
}
}
Note that you should change the username (xiaobai is my username) part. And also the if ( (ev[i].code == 76) ) {
is my Numpad 5 keycode, you might need to manually print the ev[i].code to double confirm. And of course you should change the video path too :)
Compile and test it directly with (the `` part is in order to get the correct /dev/input/eventN
):
$ gcc /home/put_your_path/my_long_press.c -o /home/put_your_path/my_long_press; sudo /home/put_your_path/my_long_press `ls -la /dev/input/by-path/* | grep kbd | echo "/dev/input/""$(awk -F'/' '{print $NF}')" ` &
Note that /by-id/
doesn't work in Fedora 24, so i change it to /by-path/. Kali no such problem.
My desktop manager is gdm3:
$ cat /etc/X11/default-display-manager
/usr/sbin/gdm3
So, i put this line in /etc/gdm3/PostLogin/Default
to run this command as root on gdm startup (/etc/X11/Xsession.d/*
doesn't work):
/home/put_your_path/my_long_press `ls -la /dev/input/by-id/* | grep kbd | echo "/dev/input/""$(awk -F'/' '{print $NF}')" 2>/tmp/l_gdm` 2>/tmp/l_gdmE &
For unknown reason /etc/gdm/PostLogin/Default
doesn't work on Fedora 24' gdm which give me "Permission denied" when check /tmp/l_gdmE
log. Manually run no problem though.
Demonstration:
Numpad 5, instant-press (<=0.2 second) will be ignored, short-press (0.2 to 0.5 second) open nautilus
, medium-press (0.5 to 1 second) open vlc
to play video, long-press (1 to 2 seconds) open gnome-terminal
, and timeout-press (2 seconds) open gedit
.
I uploaded the full code(only one file) here.
[UPDATE again]
[1] Added multiple keys flow and fixed notify-send
failed by define DBUS_SESSION_BUS_ADDRESS
.
[2] Added XDG_CURRENT_DESKTOP
and GNOME_DESKTOP_SESSION_ID
to ensure konsole use gnome theme gui (Change it if you're not using gnome).
I updated my code here.
Note that this code doesn't handle for combination keys flow, e.g. Ctrl+t.
UPDATE:
There's multiple device interfaces which the /dev/input/by-path/XXX-eventN entries sequence is random. So I change the command in /etc/gdm3/PostLogin/Default
as below (Chesen
is my keyboard name, for your case, you should changed it to grep Razer
instead):
/your_path/my_long_press "$(cat /proc/bus/input/devices | grep -i Chesen -A 4 | grep -P '^(?=.*sysrq)(?=.*leds)' | tr ' ' '\n' | ls /dev/input/`grep event`)" 2>/tmp/l_gdmE &
You can try the eventN extract from cat /proc/bus/input/devices | grep -i Razer -A 4
:
$ cat /proc/bus/input/devices | grep -i Razer -A 4
N: Name="Razer Razer Naga Chroma"
P: Phys=usb-0000:00:14.0-1.3/input0
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.3/3-1.3:1.0/0003:1532:0053.0003/input/input6
U: Uniq=
H: Handlers=mouse2 event5
--
N: Name="Razer Razer Naga Chroma"
P: Phys=usb-0000:00:14.0-1.3/input1
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.3/3-1.3:1.1/0003:1532:0053.0004/input/input7
U: Uniq=
H: Handlers=sysrq kbd event6
--
N: Name="Razer Razer Naga Chroma"
P: Phys=usb-0000:00:14.0-1.3/input2
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.3/3-1.3:1.2/0003:1532:0053.0005/input/input8
U: Uniq=
H: Handlers=sysrq kbd leds event7
$
In this example above, only sudo cat /dev/input/event7
will print bizarre output when click the 12 digits on Razer mouse, which has the pattern "sysrq kbd leds event7" to use in grep -P '^(?=.*sysrq)(?=.*leds)'
above (your pattern might vary). sudo cat /dev/input/event6
will print bizarre output only when click the middle up/down key. While sudo cat /dev/input/event5
will print bizarre output when move your mouse and scrolling the wheel.
[Update: Support Replug keyboard cable to reload the program]
The following should be self-explanation:
$ lsusb #to know my keyboard is idVendor 0a81 and idProduct 0101
...
Bus 001 Device 003: ID 0a81:0101 Chesen Electronics Corp. Keyboard
$ cat /etc/udev/rules.d/52-hole-keyboard.rules #add this line with your idVendor and idProduct above in custom udev rules file
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0a81", ATTR{idProduct}=="0101", MODE="0666", GROUP="plugdev", RUN+="/bin/bash -c 'echo 1 > /tmp/chesen_plugged'"
$ cat /usr/local/bin/inotifyChesenPlugged #A long run listener script to listen for modification of /tmp/chesen_plugged #Ensures `inotifywait` has been installed first.
touch /tmp/chesen_plugged
while inotifywait -q -e modify /tmp/chesen_plugged >/dev/null; do
killall -9 my_long_press
/usr/local/bin/startLongPress &
done
$ cat /usr/local/bin/startLongPress #the executable script run the long press executable #Change with your pattern as explained above.
#!/bin/bash
<YOUR_DIR>/my_long_press "$(cat /proc/bus/input/devices | grep -i Chesen -A 4 | grep -P '^(?=.*sysrq)(?=.*leds)' | tr ' ' '\n' | ls /dev/input/`grep event`)" 2>/tmp/l_gdmE) & disown
$ cat /etc/gdm3/PostLogin/Default #the executable startup script run listener and long press script
/usr/local/bin/inotifyChesenPlugged &
/usr/local/bin/startLongPress &
Best Answer
It seems that logitech provides a special driver on various OS's other than Linux that does exactly this swap by sending an undocumented magic command to the keyboard.
Some people have monitored the communication to find the codes sent to some other similar logitech keyboards such as the 760 and the 810. You might try one of those.
Failing that, you can use
xmodmap
to swap the key assignments. We see that your keyboard sends 2 different keycodes depending on whether Fn is pressed or not. Usexmodmap -pke
to list all the key mappings in a file, then keep just the ones referring to the wanted keycodes. For example you would haveand so on. Simply invert the numbers:
Then pass this filename as argument to
xmodmap
to have it update the mapping.