霓虹标题。evdevの力を购しテ、Linuxでホットキーの魔法改变Linux用户就像Minecraft玩家一样,虽然大家都玩Minecraft,但是,shit,我们玩的肯定是不一样的游戏(见建筑师的MC作品小白的感叹)。如果你想让自己的Linux别人用不了,自己也用不了别人的Linux,最重要的就是把快捷键改成shocking。作为一个Vim迷,我的需要是尽可能让我的手远离主键盘区域。是的,我什至不想按箭头键。所以想通过一些组合键来实现上下左右。有些人认为CapsLock方便按下。我更喜欢按Alt,因为它就在空格键旁边并且触手可及。一般来说,我希望Alt+[H|J|K|L]分别变成left,bottom,top,right,Alt+0变成Home,Alt+4变成End。我尝试过很多修改键或添加热键的工具,包括著名的AutoKey。不幸的是,它们中的大多数都不起作用。比如我拿这样一个操作顺序AltDown,KDown,KUp,AltUp,这些软件大多会在检测到KDown的时候发出AltUp,↑Down,从而撤销之前的AltDown功能,然后发出按下↑键的事件。这里有个问题,就是很多GUI会在AltDown、AltUp后调出菜单,失去对输入框的焦点。另一方面,它确实不是在任何地方都有效。至少,你不能用它玩赛车游戏。您也不能在TTY中继续使用这些热键。另外,还有很多很多地方是不能用的。热键是一种让人上瘾的东西,当它失败时,你就会患上戒断综合症。想放下键盘。我已经为evdev和uinput尝试了GNOME和X11,但似乎没有有用的解决方案。XGrabKeyboard一定程度上可以达到接管的效果,但是要指定窗口,好像窗口会失焦。总而言之,有点太复杂了。但是关于Linux的最好的事情之一就是它是裸机。如果从比X11低的驱动程序更高的层开始,也许会有更好的结果。evdev内核中的通用输入设备驱动为设备提供/dev/input下的字符设备接口。这是非常低级的。内核进行中断处理后,会将输入的数据交给它处理。但它一点也不反直觉,甚至还提供了一个非常好用的工具libevdev,可以直接在Python中处理消息。许多人通过evdev更换游戏手柄。uinput是一个特殊的虚拟设备,允许你在用户模式下直接向内核中插入输入事件——一般是直接向/dev/uinput写入数据,但当然要以libevdev提供的接口/数据结构为准。然后这些事件会在另一个evdev字符设备中,假装是物理设备的输入,会被X的libinput获取或者从TTY转换成stdin。像QuickStart这样的实验性的东西不需要用C写。evdev提供了非常有用的PythonBindings,我们可以直接用Python写我们的快捷键配置。我们首先使用pipinstallevdev安装它,然后使用它提供的示例脚本来测试evdev输入的内容:sudopython-mevdev.evtest/dev/input/by-path/platform-i8042-serio-0-event-kbd(我这里键盘的路径是在i8042键盘控制器上,需要根据自己电脑上的配置调整)time1504189579.19type4(EV_MSC),code4(MSC_SCAN),value21time1504189579.19type1(EV_KEY),code21(KEY_Y),value1time1504189579.19------SYN_REPORT------time1504189579.28type4(EV_MSC),code4(MSC_SCAN),value21time1504189579.28type1(EV_KEY),code21(KEY_Y),value0time1504189579.28------SYN_REPORT------time1504189579.29type4(EV_MSC),code4(MSC_SCAN),value18time1504189579.29type1(EV_KEY),code18(KEY_E),value1time1504189579.29------SYN_REPORT------time1504189579.4type4(EV_MSC),code4(MSC_SCAN),value18time1504189579.4type1(EV_KEY),code18(KEY_E),value0time1504189579.4------SYN_REPORT------time1504189579.48type4(EV_MSC),code4(MSC_SCAN),value31time1504189579.48type1(EV_KEY),code31(KEY_S),value1time1504189579.48------SYN_REPORT------time1504189579.64type4(EV_MSC),code4(MSC_SCAN),value31time1504189579.64type1(EV_KEY),code31(KEY_S),value0time1504189579.64----------SYN_REPORT--------这里我按了yes三个键,可以可以看出,每一个动作(按下或松开,表示为EV_KEY的值为1或0),都会产生三个事件,分别是EV_MSC、EV_KEY和EV_SYN根据https://lp007819.wordpress.com/2013/02/12/让我们谈谈linux-input子系统/。事实上,发送的消息有四个,但通常不支持第一个(隐身),而第二个MSC_SCAN通常被应用程序忽略。第三个EV_KEY是真的收到了,第四个是同步的。可以看到是用来生成可爱的边界的(笑)。有了这个层次的理解,我们知道当三个事件结合在一起时,就是一个真实的输入。就我的需求而言,出于某种原因,第一个想到的抽象机制是状态机。更合适的设计是双层自动机。第一层用于将事件分为三组。分组后成为第二层状态机的输入。二进制组(键扫描码,上/下/按住)。我花了很多时间构思第二层状态机,结果大概是这样的:Alt,向上)Normalinject_alt_down,inject_alt_upAltELSEInjectinject_alt_down,injectInject(J/K/H/L/0/4,*)Mappedinject_alt_up,mappedInject(左Alt,向上)Normalinject_alt_upInjectELSEInjectinjectMapped(J/K/H/L/0/0/4,*))Alt,Up)Normal-MappedELSEInjectinject_alt_down,注入晕。画出来可能更清楚,但是我没有时间用画图软件再画一遍。有四种状态,分别是:正常状态(Normal),除Alt外的所有键都直接送入uinput,只是按下了Alt键(Alt),还不确定Alt键是否会形成组合键插入状态(Inject),不是我们想要的热键,连刚才的Alt都推出到uinput来映射状态(Mapped)。在它们之间切换时,应添加一些Alt键操作,以防止误导其他应用程序。关于evdev本身的使用,evdev的文档已经很详细了。在这个脚本中,只使用了几个函数,比如从/dev/input读取事件,我是blockread,但是你也可以使用select或者epoll来异步完成这些操作。:#注意!需要根权限!dev=evdev.InputDevice('/dev/input/by-path/platform-i8042-serio-0-event-kbd')foreventindev.read_loop():kev=evdev.categorize(event)ks.input(kev)#一级状态机process(ks)#二级状态机,一级状态的结果作为输入Inject。这个操作是一次性向uinput写入三个事件(包括同步):ui=evdev.UInput()def__inject(keycode,keystate):globaluiui.write(ecodes.EV_MSC,ecodes.MSC_SCAN,keycode)ui.write(ecodes.EV_KEY,keycode,keystate)ui.syn()当然,最重要的一点是如何实现独占读取,即承载整个设备的事件处理,使其不泄漏?我们为它设置了GRAB。Grab之后,整个系统只有当前进程可以读取键盘输入。如果设备已经被其他人抢走,则操作将失败。dev.grab()dev.ungrab()整个代码已经上传到https://github.com/Shihira/la...欢迎指正。参考资料:https://lp007819.wordpress.com/2013/02/12/说说linux-input子系统/,你可能需要一个梯子https://python-evdev.readthed...
