C xtest为每个Unicode字符发出按键

时间:2017-06-01 17:42:53

标签: c linux unicode keyboard x11

我想制作一个模拟按键的程序。我想我大部分时间已经完成但是我做错了我想因为它没有做我期望它做的事情。我做了一个小例子程序来说明这个问题。主要问题是,如果我想生成大写字母,它不适用于像'zZ'这样的字符串。它只生成小写字母'zz'。虽然像'! $ & _ >'等符号可以正常工作(需要在我的德语键盘布局上移位),甚至像''这样的多字节符号。我在做的是:

前导码:

因此,基本上模拟按键的主要问题首先是从用户到用户的布局,最重要的是修改键。因此,如果你走了天真的路线并获得一个带有XStringToKeysym()的密钥,那么从密钥组XKeysymToKeycode()获取一个密钥代码并触发该事件,它就像大多数“新手”所期望的那样(就像我一样)。这里的问题是,多个密钥被映射到相同的密钥代码。就像'a''A'的keysysm映射到相同的键码一样,因为它们位于键盘上与该键码相关联的同一物理按钮上。因此,如果您从上面开始路线,最终会得到相同的键码,尽管键盘不同但映射到相同的键/键码。并且通常没有办法解决这个问题,因为不清楚'A'首先是如何存在的。 shift + a或caps + a或你有一个花式键盘,上面有'a''A'按钮。另一个问题是我如何发出按键,这些按键甚至不在运行该应用程序的那个人的键盘上。如果我想键入'Ä'(德语变音符号),就像在英语版面上按键的那样。这不起作用,因为XKeysymToKeycode()不会为此返回正确的密钥代码,因为该布局没有密钥映射。

我的方法:

我想要做的就是找到一个没有被使用的密钥代码。您可以使用255-8个密钥代码,但普通键盘上只有约110个键,因此通常还有一些空间。我试图找到其中一个在当前布局上未映射的密钥代码,并使用它在其上分配我自己的密钥。然后我通过迭代我的字符串并将其传递给XStringToKeysym()来获取我的char中的密钥,这给了我适当的密钥。如果''在大多数情况下没有映射到我知道的任何键盘布局。所以我将它映射到未使用的键码并用XTestFakeKeyEvent()按下它并对字符串中的每个字符重复一次。这适用于所有可以想到的花哨字形,但它不适用于简单的字母,我真的不知道为什么:(在我的调试会话中,键盘和键码似乎是正确的,只是XTestFakeKeyEvent()没有在这种情况下做正确的事情。可能我在键盘映射部分搞砸了一些东西,但我不确定这里的问题是什么,我希望有个好主意,可以帮我找到解决问题的方法。< / p>

我只是在strings数组中使用这个unicode表示法,因为我不想在这里的例子中处理这个问题。假设存在从任意输入字符串生成此代码的代码。

请注意,下面的代码可能会破坏您的键盘映射,使您无法再键入和使用键盘,并且需要重新启动X-Server / PC ...我希望它能够不处于当前状态(在这里工作正常)只要知道你是否摆弄代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <unistd.h>

//gcc -g enigo2.c -lXtst -lX11

int main(int argc, char *argv[])
{
  Display *dpy;
  dpy = XOpenDisplay(NULL);

  //my test string already transformed into unicode
  //ready to be consumed by XStringToKeysym
  const char *strings[] = {

      "U1f4a3",// 

      "U007A", //z
      "U005A", //Z
      "U002f", //'/'
      "U005D", //]

      "U003a", //:
      "U002a", //*
      "U0020", //' '
      "U0079", //y

      "U0059", //Y
      "U0020", //' '
      "U0031", //1
      "U0021", //!

      "U0020", //' '
      "U0036", //6
      "U0026", //&
      "U0020", //' '

      "U0034", //4
      "U0024", //$
      "U0020", //' '
      "U002D", //-

      "U005F", //_
      "U0020", //' '
      "U003C", //<
      "U003E", //>

      "U0063", //c
      "U0043", //C
      "U006f", //o
      "U004f", //O

      "U00e4", //ä
      "U00c4", //Ä
      "U00fc", //ü
      "U00dc", //Ü
  };

  KeySym *keysyms = NULL;
  int keysyms_per_keycode = 0;
  int scratch_keycode = 0; // Scratch space for temporary keycode bindings
  int keycode_low, keycode_high;
  //get the range of keycodes usually from 8 - 255
  XDisplayKeycodes(dpy, &keycode_low, &keycode_high);
  //get all the mapped keysyms available
  keysyms = XGetKeyboardMapping(
    dpy, 
    keycode_low, 
    keycode_high - keycode_low, 
    &keysyms_per_keycode);

  //find unused keycode for unmapped keysyms so we can 
  //hook up our own keycode and map every keysym on it
  //so we just need to 'click' our once unmapped keycode
  int i;
  for (i = keycode_low; i <= keycode_high; i++)
  {
    int j = 0;
    int key_is_empty = 1;
    for (j = 0; j < keysyms_per_keycode; j++)
    {
      int symindex = (i - keycode_low) * keysyms_per_keycode + j;
      // test for debugging to looking at those value
      // KeySym sym_at_index = keysyms[symindex];
      // char *symname;
      // symname = XKeysymToString(keysyms[symindex]);

      if(keysyms[symindex] != 0) {
        key_is_empty = 0;
      } else {
        break;
      }
    }
    if(key_is_empty) {
      scratch_keycode = i;
      break;
    }
  }
  XFree(keysyms);
  XFlush(dpy);

  usleep(200 * 1000);

  int arraysize = 33;
  for (int i = 0; i < arraysize; i++)
  {

    //find the keysym for the given unicode char
    //map that keysym to our previous unmapped keycode
    //click that keycode/'button' with our keysym on it
    KeySym sym = XStringToKeysym(strings[i]);
    KeySym keysym_list[] = { sym };
    XChangeKeyboardMapping(dpy, scratch_keycode, 1, keysym_list, 1);
    KeyCode code = scratch_keycode;

    usleep(90 * 1000);
    XTestFakeKeyEvent(dpy, code, True, 0);
    XFlush(dpy);

    usleep(90 * 1000);
    XTestFakeKeyEvent(dpy, code, False, 0);
    XFlush(dpy);
  }

  //revert scratch keycode
  {
    KeySym keysym_list[] = { 0 };
    XChangeKeyboardMapping(dpy, scratch_keycode, 1, keysym_list, 1);
  }

  usleep(100 * 1000);

  XCloseDisplay(dpy);

  return 0;
}

1 个答案:

答案 0 :(得分:2)

当您将给定键码的单个键值发送到XChangeKeyboardMapping并且它是一个字母时,它会自动填充shift和capslock修饰符的正确大写和小写等效项。也就是说,

之后
XChangeKeyboardMapping(dpy, scratch_keycode, 1, &keysym, 1);

scratch_keycode的键码映射有效地(在我的机器上)改变为

tolower(keysym), toupper(keysym), tolower(keysym), toupper(keysym), tolower(keysym), toupper(keysym), 0, 0, 0, 0, ...

为了抑制此行为,请为每个键码发送2个相同的键符号:

KeySym keysym_list[2] = { sym, sym  };
XChangeKeyboardMapping(dpy, scratch_keycode, 2, keysym_list, 1);

这将使用相同的按键填充移位和未移位的位置。