Cara memetakan kembali tombol keyboard berdasarkan berapa lama Anda menahan tombol

9

Saya ingin memetakan kembali tombol pada papan angka saya sehingga mereka berperilaku berbeda tergantung pada berapa lama tombol itu ditekan. Ini sebuah contoh:

Jika saya menahan tombol Numpad 9 kurang dari 300ms itu akan mengirim perintah kunci "tab sebelumnya" Ctrl+Tab

Jika saya menahan tombol Numpad 9 untuk 300-599ms itu akan mengirim perintah tombol "tab baru" Ctrl+T

Jika saya menahan tombol Numpad 9 untuk 600-899ms, ia akan mengirimkan perintah tombol "close tab / window" Ctrl+W

Jika saya menahan tombol Numpad 9 lebih dari 899 ms, itu tidak melakukan apa-apa kalau-kalau saya melewatkan jendela waktu yang saya inginkan.

Pada Windows saya bisa melakukan ini dengan AutoHotKey dan pada OS XI bisa melakukan ini dengan ControllerMate, tetapi saya tidak dapat menemukan alat di UNIX / Linux yang memungkinkan pemetaan ulang kunci berdasarkan berapa lama kunci dipegang.

Jika Anda mengetahui alat yang dapat memecahkan masalah saya, pastikan untuk memberikan sampel skrip atau kode yang menunjukkan perilaku durasi penahanan kunci bersyarat yang saya jelaskan di atas. Tidak perlu kode lengkap untuk menyelesaikan contoh saya, tetapi harus cukup bagi saya untuk menggunakan kembali contoh saya.

kanoko
sumber
Ini adalah hal yang aneh untuk dilakukan. Bagaimana Anda menghitung waktu pers Anda 600 milidetik? : D +1 untuk ide gila.
Wildcard
Hanya untuk menambahkan beberapa bumbu ke dalam hidup Anda, Anda harus menambahkan jendela waktu dari 347 hingga 350 ms yang akan mematikan komputer secara paksa. ;)
Wildcard
@Wildcard Saya benar-benar menggunakan tombol angka pada Razer Naga saya untuk ini dan ketika saya pertama kali menerapkan ide dengan AutoHotKey pada Windows saya menggunakan windows waktu 300-400ms, tapi sekarang saya telah menggunakan sistem ini untuk sementara waktu, saya menggunakan use jendela waktu sekitar 200ms terpisah, dan saya bisa mendapatkan jendela waktu yang diinginkan sekitar 99% dari waktu. Ini sangat mirip dengan cara Anda berkomunikasi dengan kode morse.
kanoko

Jawaban:

7

Saya baru saja menulis ini di 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; 

}

Gunakan showkey -auntuk mendapatkan kode kunci bind:

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$ 

Masukkan kode kunci bind 5 dan perintahnya (mis. Jalankan /tmp/.a.out) di ~ / .bashrc:

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

Perhatikan bahwa kode kunci yang relevan perlu diubah dalam kode sumber juga (nilai hex bisa dapatkan dari sudo showkey -aatas juga):

int c = 0x35;

Kompilasi dengan (output ke /tmp/a.outdalam contoh saya):

cc filename.c -lcurses

Demonstrasi:

Numpad 5, tekan pendek buka tab baru, tekan menengah buka gedit, dan tekan lama buka terminal gnome.

masukkan deskripsi gambar di sini

Ini tidak langsung dapat diterapkan di jendela mana pun di gnome desktop manager, tetapi saya pikir itu harus memberi Anda beberapa ide bagaimana (sulit) untuk mengimplementasikannya. Ia bekerja di Virtual Console (Ctrl + Alt + N) juga, dan bekerja di beberapa terminal emulator (misalnya konsole, gnome-terminal, xterm).

p / s: Saya bukan programmer ac, jadi maafkan saya jika kode ini tidak dioptimalkan.

[MEMPERBARUI]

Jawaban sebelumnya hanya bekerja di shell dan fokus yang diperlukan, jadi saya pikir parse / dev / input / eventX adalah solusi untuk bekerja di seluruh sesi X.

Saya tidak ingin menemukan kembali roda. Saya bermain-main dengan evtestutilitas dan memodifikasi bagian bawah evtest.c dengan kode saya sendiri:

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

Perhatikan bahwa Anda harus mengubah nama pengguna ( xiaobai adalah nama pengguna saya). Dan juga if ( (ev[i].code == 76) ) {kode kunci Numpad 5 saya, Anda mungkin perlu mencetak kode ev [i] secara manual untuk mengonfirmasi ganda. Dan tentu saja Anda harus mengubah jalur video juga :)

Kompilasi dan uji langsung dengan (bagian `` untuk mendapatkan yang benar /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}')" ` &

Catatan yang /by-id/tidak berfungsi di Fedora 24, jadi saya mengubahnya ke / by-path /. Kali tidak ada masalah seperti itu.

Manajer desktop saya adalah gdm3:

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

Jadi, saya meletakkan baris ini /etc/gdm3/PostLogin/Defaultuntuk menjalankan perintah ini sebagai root pada gdm startup ( /etc/X11/Xsession.d/*tidak berfungsi):

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

Untuk alasan yang tidak diketahui / etc/gdm/PostLogin/Defaulttidak bekerja pada Fedora 24 'gdm yang memberi saya " Izin ditolak " saat memeriksa /tmp/l_gdmElog. Secara manual menjalankan tidak ada masalah.

Demonstrasi:

Numpad 5, tekan cepat (<= 0,2 detik) akan diabaikan, tekan pendek (0,2 hingga 0,5 detik) terbuka nautilus, tekan sedang (0,5 hingga 1 detik) terbuka vlcuntuk memutar video, tekan lama (1 hingga 2 detik) terbuka gnome-terminal, dan batas waktu-tekan (2 detik) terbuka gedit.

masukkan deskripsi gambar di sini

Saya mengunggah kode lengkap (hanya satu file) di sini .

[PERBARUI lagi]

[1] Menambahkan aliran banyak kunci dan diperbaiki notify-sendgagal oleh define DBUS_SESSION_BUS_ADDRESS. [2] Ditambahkan XDG_CURRENT_DESKTOPdan GNOME_DESKTOP_SESSION_IDuntuk memastikan konsole menggunakan gnome theme gui (Ubah jika Anda tidak menggunakan gnome).

Saya memperbarui kode saya di sini .

Perhatikan bahwa kode ini tidak menangani aliran kunci kombinasi, misalnya Ctrl+ t.

MEMPERBARUI:

Ada beberapa antarmuka perangkat yang urutan entri / dev / input / by-path / XXX-eventN adalah acak. Jadi saya mengubah perintah /etc/gdm3/PostLogin/Defaultseperti di bawah ini ( Chesenadalah nama keyboard saya, untuk kasus Anda, Anda harus mengubahnya menjadi grep Razersebagai gantinya):

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

Anda dapat mencoba ekstrak eventN dari 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 
$ 

Dalam contoh di atas, hanya sudo cat /dev/input/event7akan mencetak output aneh ketika mengklik 12 digit pada mouse Razer, yang memiliki pola "sysrq kbd leds event7" untuk digunakan di grep -P '^(?=.*sysrq)(?=.*leds)'atas (pola Anda mungkin berbeda). sudo cat /dev/input/event6akan mencetak output aneh hanya ketika mengklik tombol tengah atas / bawah. Sementara sudo cat /dev/input/event5akan mencetak output aneh ketika menggerakkan mouse Anda dan menggulirkan roda.

[Pembaruan: Mendukung kabel keyboard Replug untuk memuat ulang program]

Berikut ini harus penjelasan sendiri:

$ 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 &
林果 皞
sumber
saya berasumsi metode ini memerlukan jendela terminal untuk fokus saat melakukan penekanan tombol? apakah ada cara untuk mengatasi ini?
kanoko
@kanoko Saya sudah memperbarui solusinya.
林果 皞
Terima kasih, saya sangat menghargai upaya yang Anda lakukan dalam hal ini. Saya akan mencoba ini. apakah menurut Anda solusi ini akan berdampak nyata pada penggunaan CPU jika saya mengaturnya dengan 12 hotkey yang berbeda?
kanoko
@kanoko Saya telah memperbarui kode lagi untuk bermain-main dengan beberapa tombol. IMHO saya tidak berpikir itu berdampak nyata pada cpu karena 10+ jika-lain terlalu halus, dan hanya menjalankan pemeriksaan setelah membaca (fd, ev, sizeof (struct input_event) * 64); pernyataan, yaitu hanya menjalankan if-elsesetiap tekan tombol, sementara saya juga menambahkan if (currCode >= 59) && (currCode <= 81)untuk membatasi rentang sebelumnya if-else.
林果 皞
1
kamu luar biasa !!! terima kasih banyak atas semua bantuannya. Jika Anda pernah mendapatkan kesempatan untuk mencoba ini dengan mouse MMO numpad seperti Razer Naga, saya bersumpah itu akan mengubah hidup Anda. Saya bisa menunjukkan kepada Anda pemetaan kunci saya jika Anda tertarik.
kanoko
1

Anda mungkin menemukan alat yang berfungsi dengan sekumpulan program tertentu, tetapi tidak akan ada alat yang dapat digunakan secara global karena perilaku terkait waktu dilakukan dalam aplikasi di X, bukan oleh sistem windowing.

Thomas Dickey
sumber
0

apakah Anda memeriksa Xmodmap?

xmodmap adalah utilitas untuk memodifikasi pemetaan tombol dan pemetaan tombol penunjuk di Xorg

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

Kamaraj
sumber
2
Tetapi tidak tahu tentang penundaan waktu :-)
Thomas Dickey