![如何根据按住按键的时间重新映射键盘按键](https://linux22.com/image/97724/%E5%A6%82%E4%BD%95%E6%A0%B9%E6%8D%AE%E6%8C%89%E4%BD%8F%E6%8C%89%E9%94%AE%E7%9A%84%E6%97%B6%E9%97%B4%E9%87%8D%E6%96%B0%E6%98%A0%E5%B0%84%E9%94%AE%E7%9B%98%E6%8C%89%E9%94%AE.png)
我想重新映射数字键盘上的按键,以便它们根据按下按键的时间长短而表现不同。这是一个例子:
如果我按住 Numpad 9 键少于 300 毫秒,它将发送“上一个选项卡”键命令Ctrl+Tab
如果我按住数字键盘 9 键 300-599 毫秒,它将发送“新选项卡”键命令Ctrl+T
如果我按住数字键盘 9 键 600-899 毫秒,它将发送“关闭选项卡/窗口”键命令Ctrl+W
如果我按住数字键盘 9 键超过 899 毫秒,它不会执行任何操作,以防我错过了所需的时间窗口。
在 Windows 上,我可以使用 AutoHotKey 来完成此操作,在 OS XI 上可以使用 ControllerMate 来完成此操作,但我在 UNIX/Linux 上找不到允许根据按键保持时间进行按键重新映射的工具。
如果您知道可以解决我的问题的工具,请确保提供一个脚本或代码示例来演示我上面描述的条件按键持续时间行为。它不需要是解决我的示例的完整代码,但它应该足以让我将其重新用于我的示例。
答案1
我刚刚写了这个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;
}
用于showkey -a
获取绑定键码:
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$
将绑定键码 5 及其命令(例如 run /tmp/.a.out
)放入 ~/.bashrc 中:
bind '"5":"/tmp/a.out\n"'
请注意,源代码中相关的键码也需要更改(十六进制值sudo showkey -a
也可以从上面获取):
int c = 0x35;
编译(在我的示例中输出为/tmp/a.out
):
cc filename.c -lcurses
示范:
小键盘 5,短按打开新选项卡,中按打开 gedit,长按打开 gnome-terminal。
这并不直接适用于 gnome 桌面管理器上的任何窗口,但我认为它应该让您了解如何(困难)实现它。它也可以在虚拟控制台(Ctrl+Alt+N)中工作,并且可以在某些终端仿真器(例如 konsole、gnome-terminal、xterm)中工作。
p/s:我不是程序员,所以如果这段代码没有优化,请原谅我。
[更新]
前面的答案只能在 shell 中工作并且需要焦点,所以我认为解析 /dev/input/eventX 是在整个 X 会话中工作的解决方案。
我不想重新发明轮子。我玩弄evtest
实用程序并修改了底部部分evtest.c用我自己的代码:
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;
}
}
}
}
}
}
请注意,您应该更改用户名(小白是我的用户名)部分。这也是if ( (ev[i].code == 76) ) {
我的 Numpad 5 键码,您可能需要手动打印 ev[i].code 进行双重确认。当然,您也应该更改视频路径:)
直接编译并测试它(``部分是为了得到正确的/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}')" ` &
请注意,这/by-id/
在 Fedora 24 中不起作用,因此我将其更改为 /by-path/。卡利没有这样的问题。
我的桌面管理器是 gdm3:
$ cat /etc/X11/default-display-manager
/usr/sbin/gdm3
因此,我将这一行放入/etc/gdm3/PostLogin/Default
gdm 启动时以 root 身份运行此命令(/etc/X11/Xsession.d/*
不起作用):
/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 &
由于未知原因/etc/gdm/PostLogin/Default
在 Fedora 24' gdm 上不起作用,这给了我“没有权限“当检查/tmp/l_gdmE
日志时。手动运行没有问题。
示范:
小键盘5,瞬间按下(<=0.2秒)将被忽略,短按(0.2至0.5秒)打开nautilus
,中按(0.5至1秒)打开vlc
播放视频,长按(1至2秒) open gnome-terminal
,然后超时按(2秒) open gedit
。
[再次更新]
[1] 添加了多个按键流程并修复了notify-send
定义失败的问题DBUS_SESSION_BUS_ADDRESS
。 [2] 添加XDG_CURRENT_DESKTOP
并GNOME_DESKTOP_SESSION_ID
确保 konsole 使用 gnome 主题 gui(如果您不使用 gnome,请更改它)。
请注意,此代码不处理组合键流,例如Ctrl+ t。
更新:
有多个设备接口,/dev/input/by-path/XXX-eventN 条目顺序是随机的。所以我将命令更改为/etc/gdm3/PostLogin/Default
如下(Chesen
是我的键盘名称,对于您的情况,您应该将其更改为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 &
您可以尝试以下中的 eventN 摘录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
$
在上面的示例中,只有sudo cat /dev/input/event7
当单击 Razer 鼠标上的 12 位数字时才会打印奇怪的输出,该鼠标具有上面使用的模式“sysrq kbd leds event7” grep -P '^(?=.*sysrq)(?=.*leds)'
(您的模式可能会有所不同)。sudo cat /dev/input/event6
仅当单击中间的向上/向下键时才会打印奇怪的输出。当sudo cat /dev/input/event5
移动鼠标并滚动滚轮时,会打印奇怪的输出。
[更新:支持重新插拔键盘线重新加载程序]
以下内容应该是自我解释:
$ 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 &
答案2
您可能会找到一个适用于一组特定程序的工具,但不会有全局可用的工具,因为与时间相关的行为是在 X 中的应用程序中完成的,而不是由窗口系统完成的。
答案3
你检查过 Xmodmap 吗?
xmodmap 是一个用于修改 Xorg 中的键盘映射和指针按钮映射的实用程序