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 &