成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

借evdev之力 Linux全局熱鍵魔改造

Half / 3077人閱讀

摘要:開始動(dòng)工就我的需求而言,我腦子里第一個(gè)浮現(xiàn)的抽象機(jī)制就是狀態(tài)機(jī),不知為何。一個(gè)比較合適的設(shè)計(jì)是兩層的自動(dòng)機(jī),第一層用來為這三個(gè)一組的事件分組,分好組之后成為第二層狀態(tài)機(jī)的輸入,一個(gè)二元組。

霓虹語標(biāo)題我都想好了。evdevの力を貸して、Linuxでホットキーの魔改造

Linux用戶就像Minecraft玩家,雖然大家玩的都是Minecraft,但是,臥槽,我們一定是在玩不同的游戲(見到建筑師的MC作品時(shí)來自小白的驚嘆)。要讓自己的Linux別人不會(huì)用,別人的Linux自己不會(huì)用,最重要的當(dāng)然是要把快捷鍵改得驚天地泣鬼神。

作為一個(gè)Vim癮君子,我的需求就是手盡量不要離開主鍵盤區(qū)。對(duì),方向鍵我都不想按。于是我想通過一些組合鍵去實(shí)現(xiàn)上下左右。有人認(rèn)為CapsLock按起來方便,我自己比較喜歡按Alt,因?yàn)榫驮诳崭矜I旁邊,觸手可及??偟膩碚f,我希望Alt+[H|J|K|L]分別變成左下上右,Alt+0變成Home, Alt+4變成End。

我自己試用過很多修改鍵位或者添加熱鍵的工具,包括著名的AutoKey。很可惜,它們大都不好用。比如說,我采取這樣的操作序列Alt Down, K Down, K Up, Alt Up,這些軟件大多會(huì)采取在檢測(cè)到K Down的時(shí)候,同時(shí)發(fā)出Alt Up, ↑ Down,以便撤銷掉先前一個(gè)Alt Down的作用,然后發(fā)出鍵按下的事件。這里有一個(gè)問題,就是很多GUI在Alt Down, Alt Up之后,會(huì)喚出菜單,從而失焦于輸入框。

另外一方面就是,真的不是哪里都能用。至少,你不可能拿它去玩賽車游戲。你也不能在TTY中繼續(xù)使用這些熱鍵。此外還有許許多多的地方不能使用。熱鍵是一種會(huì)上癮的東西,在它失效的時(shí)候,你就會(huì)有戒斷綜合征。想摔鍵盤。

evdev和uinput

我試過從GNOME和X11入手,貌似沒有什么好用的方案。XGrabKeyboard一定程度上可以做到接管的效果,但是要指定窗口,似乎還會(huì)讓窗口失焦??偠灾晕⒂悬c(diǎn)太繞。不過Linux最好的一點(diǎn),就是它很裸露。如果從高于驅(qū)動(dòng)低于X11的層入手,興許會(huì)有比較好的效果。

evdev內(nèi)核中通用的輸入設(shè)備驅(qū)動(dòng),它為設(shè)備提供了/dev/input下字符設(shè)備接口。它非常底層,內(nèi)核在進(jìn)行中斷處理后,第一時(shí)間就將輸入數(shù)據(jù)交由它處理。但是它一點(diǎn)都不反直覺,甚至還提供了很好用的工具libevdev,可以直接用Python處理消息。不少人改游戲手柄都是通過evdev進(jìn)行的。

uinput是一個(gè)特殊的虛擬設(shè)備,它允許你直接在用戶態(tài)向內(nèi)核插入輸入事件 —— 一般而言就是直接向/dev/uinput寫數(shù)據(jù),不過當(dāng)然,要服從libevdev提供的接口/數(shù)據(jù)結(jié)構(gòu)。這些事件之后會(huì)在另一個(gè)evdev字符設(shè)備里,假裝是物理設(shè)備的輸入,被X的libinput取出來或者由TTY轉(zhuǎn)為stdin。

Quick Start

這種實(shí)驗(yàn)性的東西就不用C寫了。evdev提供了十分好用的Python Bindings,我們可以直接在Python里寫我們的快捷鍵配置。我們先用pip install evdev安裝它,然后在用它提供的示例腳本來測(cè)試一下evdev輸入的究竟是什么東西:sudo python -m evdev.evtest /dev/input/by-path/platform-i8042-serio-0-event-kbd(這里我鍵盤的路徑是i8042鍵盤控制器上的,你需要根據(jù)你電腦上的配置來調(diào)整)

time 1504189579.19    type 4 (EV_MSC), code 4    (MSC_SCAN), value 21
time 1504189579.19    type 1 (EV_KEY), code 21   (KEY_Y), value 1
time 1504189579.19    --------- SYN_REPORT --------
time 1504189579.28    type 4 (EV_MSC), code 4    (MSC_SCAN), value 21
time 1504189579.28    type 1 (EV_KEY), code 21   (KEY_Y), value 0
time 1504189579.28    --------- SYN_REPORT --------
time 1504189579.29    type 4 (EV_MSC), code 4    (MSC_SCAN), value 18
time 1504189579.29    type 1 (EV_KEY), code 18   (KEY_E), value 1
time 1504189579.29    --------- SYN_REPORT --------
time 1504189579.4     type 4 (EV_MSC), code 4    (MSC_SCAN), value 18
time 1504189579.4     type 1 (EV_KEY), code 18   (KEY_E), value 0
time 1504189579.4     --------- SYN_REPORT --------
time 1504189579.48    type 4 (EV_MSC), code 4    (MSC_SCAN), value 31
time 1504189579.48    type 1 (EV_KEY), code 31   (KEY_S), value 1
time 1504189579.48    --------- SYN_REPORT --------
time 1504189579.64    type 4 (EV_MSC), code 4    (MSC_SCAN), value 31
time 1504189579.64    type 1 (EV_KEY), code 31   (KEY_S), value 0
time 1504189579.64    --------- SYN_REPORT --------

這里我按了yes三個(gè)鍵,可以看到,每一個(gè)動(dòng)作(按下或釋放,表現(xiàn)在EV_KEY的value的1或0上),都會(huì)產(chǎn)生三個(gè)事件,分別是EV_MSC,EV_KEYEV_SYN。根據(jù) https://lp007819.wordpress.com/2013/02/12/再談linux-input子系統(tǒng)/的說法,事實(shí)上是有四個(gè)消息的發(fā)出,但是第一個(gè)通常不被支持(隱身了),第二個(gè)MSC_SCAN通常會(huì)被應(yīng)用程序忽略,第三個(gè)EV_KEY才是真正會(huì)被接收的,第四個(gè)是同步,可以看到就是用來產(chǎn)生萌萌的分界線的(笑)

有了這一層認(rèn)識(shí)我們就知道,三個(gè)事件合起來,才是一次真正的輸入。

開始動(dòng)工

就我的需求而言,我腦子里第一個(gè)浮現(xiàn)的抽象機(jī)制就是狀態(tài)機(jī),不知為何。一個(gè)比較合適的設(shè)計(jì)是兩層的自動(dòng)機(jī),第一層用來為這三個(gè)一組的事件分組,分好組之后成為第二層狀態(tài)機(jī)的輸入,一個(gè)二元組(key-scan-code, up/down/hold)。第二層狀態(tài)機(jī)我花了好些時(shí)間去構(gòu)思,結(jié)果大概是這樣的:

State Input Pattern Transition Action
Normal (Left Alt, Down) Alt -
Normal ELSE Normal inject
Alt (J/K/H/L/0/4, *) Mapped mapped
Alt (Left Alt, Up) Normal inject_alt_down, inject_alt_up
Alt ELSE Inject inject_alt_down, inject
Inject (J/K/H/L/0/4, *) Mapped inject_alt_up, mapped
Inject (Left Alt, Up) Normal inject_alt_up
Inject ELSE Inject inject
Mapped (J/K/H/L/0/4, *) Mapped mapped
Mapped (Left Alt, Up) Normal -
Mapped ELSE Inject inject_alt_down, inject

是不是頭都暈了。把它畫出來或許會(huì)比較清楚,不過我也沒這個(gè)閑心拿繪圖軟件再畫一遍了。有四個(gè)狀態(tài),分別是:

正常狀態(tài) (Normal),除了Alt以外所有鍵都直接發(fā)射到uinput里

剛按了下Alt鍵 (Alt),現(xiàn)在還不能確定Alt鍵會(huì)否形成組合鍵

插入狀態(tài) (Inject),不是我們想要的熱鍵,連剛才的Alt一起發(fā)射到uinput里

映射狀態(tài) (Mapped),是熱鍵,把映射過之后的鍵發(fā)射出去,比如按下了K就發(fā)射Up

Inject和Mapped狀態(tài)之間轉(zhuǎn)換時(shí),還要把一些Alt鍵的動(dòng)作補(bǔ)充一下,以防誤導(dǎo)其它應(yīng)用程序。

關(guān)于evdev本身的使用上,evdev的文檔已經(jīng)說得非常詳盡。在這個(gè)腳本里,僅僅用到了少量的功能,比如從/dev/input里讀事件,我是block read,但是你也可以用select或者epoll去異步完成這些操作。:

# 留意!需要Root權(quán)限!
dev = evdev.InputDevice("/dev/input/by-path/platform-i8042-serio-0-event-kbd")

for event in dev.read_loop():
    kev = evdev.categorize(event)
    ks.input(kev) # 第一層狀態(tài)機(jī)
    process(ks) # 第二層狀態(tài)機(jī),以第一層狀態(tài)的結(jié)果為輸入

Inject這樣的操作就是往uinput里面一次過寫三個(gè)事件(含同步):

ui = evdev.UInput()

def __inject(keycode, keystate):
    global ui
 
    ui.write(ecodes.EV_MSC, ecodes.MSC_SCAN, keycode)
    ui.write(ecodes.EV_KEY, keycode, keystate)
    ui.syn()

當(dāng)然,最重要的一點(diǎn)是怎樣做到獨(dú)占讀,也就是托管整個(gè)設(shè)備的事件處理不讓它側(cè)漏呢?我們?yōu)樗O(shè)置GRAB。Grab了之后,整個(gè)系統(tǒng)只有當(dāng)前進(jìn)程讀得到鍵盤的輸入。如果這個(gè)設(shè)備已經(jīng)被別人Grab了,這個(gè)操作就會(huì)失敗。

dev.grab()
dev.ungrab()

整份代碼已經(jīng)上傳到 https://github.com/Shihira/la...,歡迎斧正。

參考資料

https://lp007819.wordpress.com/2013/02/12/再談linux-input子系統(tǒng)/ ,可能需要梯子

https://python-evdev.readthed...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/40793.html

相關(guān)文章

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<