Cómo reasignar las teclas del teclado en función de cuánto tiempo mantienes presionada la tecla

Cómo reasignar las teclas del teclado en función de cuánto tiempo mantienes presionada la tecla

Me gustaría reasignar las teclas de mi teclado numérico para que se comporten de manera diferente dependiendo de cuánto tiempo se presiona la tecla. He aquí un ejemplo:

Si mantengo presionada la tecla Numpad 9 durante menos de 300 ms, enviará el comando de teclado "pestaña anterior" Ctrl+Tab

Si mantengo presionada la tecla Numpad 9 durante 300-599 ms, enviará el comando de teclado "nueva pestaña" Ctrl+T

Si mantengo presionada la tecla Numpad 9 durante 600-899 ms, enviará el comando de teclado "cerrar pestaña/ventana" Ctrl+W

Si mantengo presionada la tecla Numpad 9 durante más de 899 ms, no hace nada en caso de que pierda la ventana de tiempo que quería.

En Windows podría hacer esto con AutoHotKey y en OS XI podría hacerlo con ControllerMate, pero no puedo encontrar una herramienta en UNIX/Linux que permita la reasignación de claves en función de cuánto tiempo se mantiene presionada una clave.

Si conoce una herramienta que pueda resolver mi problema, asegúrese de proporcionar un script o código de muestra que demuestre el comportamiento de duración de retención de clave condicional que describí anteriormente. No es necesario que sea el código completo para resolver mi ejemplo, pero debería ser suficiente para reutilizarlo para mi ejemplo.

Respuesta1

Acabo de escribir esto enC:

#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; 

}

Úselo showkey -apara obtener el código clave de enlace:

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$ 

Coloque el código clave de enlace 5 y su comando (por ejemplo, ejecutar /tmp/.a.out) en ~/.bashrc:

bind '"5":"/tmp/a.out\n"'

Tenga en cuenta que el código clave relevante también debe cambiarse en el código fuente (el valor hexadecimal sudo showkey -atambién puede obtenerse desde arriba):

int c = 0x35;

Compilar con (salida /tmp/a.outen mi ejemplo):

cc filename.c -lcurses

Demostración:

Teclado numérico 5, presione brevemente para abrir una nueva pestaña, presione con el medio para abrir gedit y presione prolongadamente para abrir gnome-terminal.

ingrese la descripción de la imagen aquí

Esto no es aplicable directamente en ninguna ventana del administrador de escritorio gnome, pero creo que debería darle una idea de cómo (difícil) implementarlo. También funciona en la consola virtual (Ctrl+Alt+N) y en algún emulador de terminal (por ejemplo, konsole, gnome-terminal, xterm).

p/s: no soy programador de CA, así que perdónenme si este código no está optimizado.

[ACTUALIZAR]

La respuesta anterior solo funciona en Shell y requiere enfoque, así que creo que analizar /dev/input/eventX es la solución para funcionar en toda la sesión X.

No quiero reinventar la rueda. Jugué con evtestla utilidad y modifiqué la parte inferior deevtest.ccon mi propio código:

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;
                    }
                }
            }
        }
    }
}

Tenga en cuenta que debe cambiar el nombre de usuario (xiaobaies mi nombre de usuario) parte. Y también if ( (ev[i].code == 76) ) {es mi código clave del teclado numérico 5, es posible que deba imprimir manualmente el código ev[i].para confirmar dos veces. Y, por supuesto, también deberías cambiar la ruta del vídeo :)

Compílelo y pruébelo directamente con (la parte `` es para obtener lo correcto /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}')" ` &

Tenga en cuenta que /by-id/no funciona en Fedora 24, así que lo cambio a /by-path/. Kali no tiene ese problema.

Mi administrador de escritorio es gdm3:

$ cat /etc/X11/default-display-manager 
/usr/sbin/gdm3

Entonces, puse esta línea /etc/gdm3/PostLogin/Defaultpara ejecutar este comando como root al iniciar gdm ( /etc/X11/Xsession.d/*no funciona):

/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 &

Por motivo desconocido/ etc/gdm/PostLogin/Defaultno funciona en Fedora 24' gdm, lo cual me da "Permiso denegado"cuando verifique /tmp/l_gdmEel registro. Sin embargo, ejecutar manualmente no hay problema.

Demostración:

Teclado numérico 5, se ignorará la pulsación instantánea (<=0,2 segundos), la pulsación corta (de 0,2 a 0,5 segundos) para abrir nautilus, la pulsación media (de 0,5 a 1 segundo) para abrir vlcpara reproducir el vídeo, la pulsación larga (de 1 a 2 segundos) abrir gnome-terminaly tiempo de espera: presione (2 segundos) abrir gedit.

ingrese la descripción de la imagen aquí

Subí el código completo (solo un archivo) aquí.

[ACTUALIZAR de nuevo]

[1] Se agregó un flujo de claves múltiples y se corrigió notify-sendel error definido DBUS_SESSION_BUS_ADDRESS. [2] Se agregó XDG_CURRENT_DESKTOPy GNOME_DESKTOP_SESSION_IDpara garantizar que la consola use la interfaz gráfica de usuario del tema gnome (cámbielo si no está usando gnome).

Actualicé mi código aquí.

Tenga en cuenta que este código no maneja el flujo de teclas combinadas, por ejemplo, Ctrl+ t.

ACTUALIZAR:

Hay múltiples interfaces de dispositivos cuya secuencia de entradas /dev/input/by-path/XXX-eventN es aleatoria. Entonces cambio el comando /etc/gdm3/PostLogin/Defaultcomo se muestra a continuación ( Chesenes el nombre de mi teclado, para su caso, debería cambiarlo a grep Razer):

/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 &

Puedes probar el extracto eventN de 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 
$ 

En este ejemplo anterior, solo sudo cat /dev/input/event7se imprimirá una salida extraña cuando haga clic en los 12 dígitos del mouse Razer, que tiene el patrón "sysrq kbd leds event7" para usar arriba grep -P '^(?=.*sysrq)(?=.*leds)'(su patrón puede variar). sudo cat /dev/input/event6imprimirá una salida extraña solo cuando haga clic en la tecla central arriba/abajo. Mientras que sudo cat /dev/input/event5se imprimirá una salida extraña cuando mueva el mouse y desplace la rueda.

[Actualización: soporte para volver a conectar el cable del teclado para recargar el programa]

Lo siguiente debería ser una autoexplicación:

$ 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 &

Respuesta2

Es posible que encuentre una herramienta que funcione con un conjunto particular de programas, pero no habrá ninguna herramienta utilizable globalmente porque el comportamiento relacionado con el tiempo se realiza en aplicaciones en X, en lugar de hacerlo mediante el sistema de ventanas.

Respuesta3

¿Revisaste el Xmodmap?

xmodmap es una utilidad para modificar mapas de teclas y asignaciones de botones de puntero en Xorg

https://wiki.archlinux.org/index.php/Xmodmap

información relacionada