Pull to refresh

Горячие клавиши ctrl+shift+[key] и переключение языков по ctrl+shift (xorg-server 1.12.*)

Здравствуйте.

Данная статья является продолжением http://habrahabr.ru/post/87408/. Напомню, что проблема заключается в следующем: комбинация клавиш для переключения раскладки срабатывает на нажатие, а не на отпускание (как, например, в Windows). Это приводит к тому, что комбинации ctrl+shift+[key] уже не срабатывают. В Ubuntu, начиная с версии 11.04, проблема с горячими клавишами решена, но в большинстве дистрибутивов проблема осталась. С выходом xorg-server версии 1.12.1, патч по указанной выше ссылке не актуален.


Итак, инструкция для решения проблемы в ArchLinux.

Сначала необходимо получить файлы для сборки xorg-server, например с помощью abs. После синхронизации дерева abs, файлы будут доступны в /var/abs/extra/xorg-server. Далее нам необходимо подготовить эти файлы для сборки пакета с нашем патчем:

$ cp -R /var/abs/extra/xorg-server /var/abs/local/ && cd /var/abs/local/xorg-server


Создаем файл hotkeys.patch и записываем в него следующее:

diff --git a/xkb/xkbActions.c b/xkb/xkbActions.c
index c473df1..fff66f7 100644
--- a/xkb/xkbActions.c
+++ b/xkb/xkbActions.c
@@ -340,23 +340,83 @@ _XkbFilterLatchState(XkbSrvInfoPtr xkbi,
     return 1;
 }
 
+static int xkbSwitchGroupOnRelease(void)
+{
+    /* TODO: user configuring */
+    return TRUE;
+}
+
+static void xkbUpdateLockedGroup(XkbSrvInfoPtr xkbi, XkbAction* pAction)
+{
+    XkbGroupAction ga = pAction->group;
+    if (ga.flags&XkbSA_GroupAbsolute)
+       xkbi->state.locked_group= XkbSAGroup(&ga);
+    else xkbi->state.locked_group+= XkbSAGroup(&ga);
+}
+
+static XkbFilterPtr _XkbNextFreeFilter(XkbSrvInfoPtr xkbi);
+
 static int
-_XkbFilterLockState(XkbSrvInfoPtr xkbi,
+_XkbFilterLockGroup(XkbSrvInfoPtr   xkbi,
                     XkbFilterPtr filter, unsigned keycode, XkbAction *pAction)
 {
-    if (pAction && (pAction->type == XkbSA_LockGroup)) {
-        if (pAction->group.flags & XkbSA_GroupAbsolute)
-            xkbi->state.locked_group = XkbSAGroup(&pAction->group);
-        else
-            xkbi->state.locked_group += XkbSAGroup(&pAction->group);
-        return 1;
+        
+    int sendEvent = 1;
+
+    if (!xkbSwitchGroupOnRelease()) {
+       xkbUpdateLockedGroup(xkbi, pAction);
+       return sendEvent;
+    }
+    
+    /* Delay switch till button release */
+    if (filter->keycode==0) {          /* initial press */
+       filter->keycode = keycode;
+       filter->active = 1;
+       filter->filterOthers = 0; /* for what? */
+       filter->filter = _XkbFilterLockGroup;
+
+       /* filter->priv = 0; */
+       filter->upAction = *pAction;
+
+       /* Ok, now we need to simulate the action which would go if this action didn't block it.
+          XkbSA_SetMods is the one: it is to set modifier' flag up. */
+       {
+           XkbStateRec fake_state = xkbi->state;
+           XkbAction act;
+
+           fake_state.mods = 0;
+           act = XkbGetKeyAction(xkbi, &fake_state, keycode);
+
+           /* KLUDGE: XkbSA_SetMods only? */
+           if (act.type == XkbSA_SetMods) { 
+               XkbFilterPtr filter = _XkbNextFreeFilter(xkbi);
+               sendEvent = _XkbFilterSetState(xkbi,filter,keycode,&act);
+           }
+       }
     }
+    else {
+       /* do nothing if some button else is pressed */
+       if (!pAction)
+           xkbUpdateLockedGroup(xkbi, &filter->upAction);
+       filter->active = 0;
+    }
+
+    return sendEvent;
+}
+
+static int
+_XkbFilterLockMods(    XkbSrvInfoPtr   xkbi,
+                       XkbFilterPtr    filter,
+                       unsigned        keycode,
+                       XkbAction *     pAction)
+{
+
     if (filter->keycode == 0) { /* initial press */
         filter->keycode = keycode;
         filter->active = 1;
         filter->filterOthers = 0;
         filter->priv = xkbi->state.locked_mods & pAction->mods.mask;
-        filter->filter = _XkbFilterLockState;
+        filter->filter = _XkbFilterLockMods;
         filter->upAction = *pAction;
         if (!(filter->upAction.mods.flags & XkbSA_LockNoLock))
             xkbi->state.locked_mods |= pAction->mods.mask;
@@ -1129,10 +1189,13 @@ XkbHandleActions(DeviceIntPtr dev, DeviceIntPtr kbd, DeviceEvent *event)
                 sendEvent = _XkbFilterLatchState(xkbi, filter, key, &act);
                 break;
             case XkbSA_LockMods:
-            case XkbSA_LockGroup:
                 filter = _XkbNextFreeFilter(xkbi);
-                sendEvent = _XkbFilterLockState(xkbi, filter, key, &act);
+                sendEvent=_XkbFilterLockMods(xkbi,filter,key,&act);
                 break;
+            case XkbSA_LockGroup:
+                filter = _XkbNextFreeFilter(xkbi);
+                sendEvent = _XkbFilterLockGroup(xkbi,filter,key,&act);
+				break;
             case XkbSA_ISOLock:
                 filter = _XkbNextFreeFilter(xkbi);
                 sendEvent = _XkbFilterISOLock(xkbi, filter, key, &act);

Меняем PKGBUILD следующим образом:

...
source=(
...
10-quirks.conf
hotkeys.patch) # Добавляем в списке наш патч
...
sha1sums=('922b963901f4711449b53fb32ce3e14f5b642766'
...
'993798f3d22ad672d769dae5f48d1fa068d5578f'
'd908acf9fe169a7479b4cedeffc5ab3f08d958fc') #Добавляем sha1 сумму от файла hotkeys.patch
...
build() {
...
# После применения всех патчей и перед началом сборки применяем наш патч
#HotKeys
patch -Np1 -i "${srcdir}/hotkeys.patch"
autoreconf -fi
./configure --prefix=/usr \
...


Сохраняем, собираем (makepkg), устанавливаем (pacman -U xorg-server xorg-server-common) и перезапускам иксы.

Для того, чтобы в будущем xorg-server сам не обновился, рекомендую в файле /etc/pacman.conf добавить настройку (на свой страх и риск, разумеется):

IgnorePkg = xorg-server xorg-server-common


Вот и все. Всем спасибо за внимание.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.