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

dus*_*n.b 5 c linux x11 keyboard unicode

我想制作一个程序来模拟按键操作。我想我已经基本完成了,但我猜我做错了什么,因为它没有做我期望它做的事情。我做了一个小示例程序来说明这个问题。主要问题是,如果我想生成大写字母,它不适用于'zZ'. 它只生成小写字母'zz'。尽管诸如'! $ & _ >'等符号工作正常(需要在我的德语键盘布局上进行转换),甚至像 '' 这样的多字节符号。我在做什么是这样的:

前言:

所以基本上模拟按键的主要问题首先是布局随用户而变化,最重要的是修饰键。因此,如果您走幼稚的路线并获得一个XStringToKeysym()键符,XKeysymToKeycode()并从该键符中获取键码并触发该事件,那么它不会像大多数“新手”所期望的那样(像我一样)。这里的问题是,多个键符被映射到相同的键码。像keysysm为'a''A',因为他们是在键盘上的同一physikal按钮链接到的键码映射到相同的键码。因此,如果您从上面走这条路线,您最终会得到相同的键码,尽管键符不同但映射到相同的按钮/键码。通常没有办法解决这个问题,因为不清楚如何'A'首先出现。shift+a 或 caps+a 或者你有一个漂亮的键盘,上面有一个'a''A'按钮。另一个问题是我如何为那些甚至不在运行该应用程序的人的键盘上的按钮发出按键。如果我想输入'Ä'(德语变音),就像在英文布局上按下什么键一样。这不起作用,因为XKeysymToKeycode()不会为此返回正确的键码,因为该布局没有键符映射。

我的方法:

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

我只是在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;
}
Run Code Online (Sandbox Code Playgroud)

n. *_* m. 3

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

XChangeKeyboardMapping(dpy, scratch_keycode, 1, &keysym, 1);
Run Code Online (Sandbox Code Playgroud)

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

tolower(keysym), toupper(keysym), tolower(keysym), toupper(keysym), tolower(keysym), toupper(keysym), 0, 0, 0, 0, ...
Run Code Online (Sandbox Code Playgroud)

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

KeySym keysym_list[2] = { sym, sym  };
XChangeKeyboardMapping(dpy, scratch_keycode, 2, keysym_list, 1);
Run Code Online (Sandbox Code Playgroud)

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