有没有办法为uinput虚拟设备(Wayland)设置绝对光标位置?

问题描述 投票:0回答:1

我按照 linux uinput 文档上的代码示例来模拟虚拟定点设备。我有以下几行代码可以成功地将光标向下和向右移动。

#include <linux/uinput.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

void emit(int fd, int type, int code, int val)
{
   struct input_event ie;
   struct input_event ie;

   ie.type = type;
   ie.code = code;
   ie.value = val;
   /* timestamp values below are ignored */
   ie.time.tv_sec = 0;
   ie.time.tv_usec = 0;

   write(fd, &ie, sizeof(ie));
}
/* emit function is identical to of the first example */

void emit(int fd, int type, int code, int val)
{
   struct input_event ie;

   ie.type = type;
   ie.code = code;
   ie.value = val;
   /* timestamp values below are ignored */
   ie.time.tv_sec = 0;
   ie.time.tv_usec = 0;

   write(fd, &ie, sizeof(ie));
}

int main(void)
{
   struct uinput_setup usetup;
   int i = 50;

   int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);

   /* enable mouse button left and relative events */
   ioctl(fd, UI_SET_EVBIT, EV_KEY);
   ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);

   ioctl(fd, UI_SET_EVBIT, EV_REL);
   ioctl(fd, UI_SET_RELBIT, REL_X);
   ioctl(fd, UI_SET_RELBIT, REL_Y);

   memset(&usetup, 0, sizeof(usetup));
   usetup.id.bustype = BUS_USB;
   usetup.id.vendor = 0x1234; /* sample vendor */
   usetup.id.product = 0x5678; /* sample product */
   strcpy(usetup.name, "Example device");

   ioctl(fd, UI_DEV_SETUP, &usetup);
   ioctl(fd, UI_DEV_CREATE);

   /*
    * On UI_DEV_CREATE the kernel will create the device node for this
    * device. We are inserting a pause here so that userspace has time
    * to detect, initialize the new device, and can start listening to
    * the event, otherwise it will not notice the event we are about
    * to send. This pause is only needed in our example code!
    */
   sleep(1);

   /* Move the mouse diagonally, 5 units per axis */
   while (i--) {
      emit(fd, EV_REL, REL_X, 5);
      emit(fd, EV_REL, REL_Y, 5);
      emit(fd, EV_SYN, SYN_REPORT, 0);
      usleep(15000);
   }

   /*
    * Give userspace some time to read the events before we destroy the
    * device with UI_DEV_DESTROY.
    */
   sleep(1);

   ioctl(fd, UI_DEV_DESTROY);
   close(fd);

   return 0;
}

现在,我想将光标的位置设置为屏幕上的 x=500,y=500,例如。 将代码中的“REL”实例更改为“ABS”似乎并没有达到目的(仍然相对移动)。由于我找不到此功能的文档,我想知道在哪里可以找到它,或者是否可以在 Wayland 上使用 uinput 设置绝对光标位置(Gnome 嘀咕),或者是否有其他我可以使用的东西。

我在“libinput replay”中看到了这种行为(设置光标的绝对位置),并且想知道我是否可以复制它。干杯。

c linux io driver wayland
1个回答
0
投票
ioctl

要求使用的是

ioctl(fd, UI_SET_EVBIT, EV_ABS)

和(一个或多个)

ioctl(fd, UI_ABS_SETUP, &abs)

其中 
abs

的类型为

struct uinput_abs_setup
(包裹 
struct input_absinfo
)。
这是一个修改后的示例,应该可以工作。

#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <linux/uinput.h> #include <sys/ioctl.h> static void fatal(const char *msg) { fprintf(stderr, "fatal: "); if (errno) perror(msg); else fprintf(stderr, "%s\n", msg); exit(EXIT_FAILURE); } static void setup_abs(int fd, int type, int min, int max, int res) { struct uinput_abs_setup abs = { .code = type, .absinfo = { .minimum = min, .maximum = max, .resolution = res } }; if (-1 == ioctl(fd, UI_ABS_SETUP, &abs)) fatal("ioctl UI_ABS_SETUP"); } static void init(int fd, int width, int height, int dpi) { if (-1 == ioctl(fd, UI_SET_EVBIT, EV_SYN)) fatal("ioctl UI_SET_EVBIT EV_SYN"); if (-1 == ioctl(fd, UI_SET_EVBIT, EV_KEY)) fatal("ioctl UI_SET_EVBIT EV_KEY"); if (-1 == ioctl(fd, UI_SET_KEYBIT, BTN_LEFT)) fatal("ioctl UI_SET_KEYBIT BTN_LEFT"); if (-1 == ioctl(fd, UI_SET_EVBIT, EV_ABS)) fatal("ioctl UI_SET_EVBIT EV_ABS"); /* the ioctl UI_ABS_SETUP enables these automatically, when appropriate: ioctl(fd, UI_SET_ABSBIT, ABS_X); ioctl(fd, UI_SET_ABSBIT, ABS_Y); */ struct uinput_setup device = { .id = { .bustype = BUS_USB }, .name = "Emulated Absolute Positioning Device" }; if (-1 == ioctl(fd, UI_DEV_SETUP, &device)) fatal("ioctl UI_DEV_SETUP"); setup_abs(fd, ABS_X, 0, width, dpi); setup_abs(fd, ABS_Y, 0, height, dpi); if (-1 == ioctl(fd, UI_DEV_CREATE)) fatal("ioctl UI_DEV_CREATE"); /* give time for device creation */ sleep(1); } static void emit(int fd, int type, int code, int value) { struct input_event ie = { .type = type, .code = code, .value = value }; write(fd, &ie, sizeof ie); } int main(int argc, char **argv) { /* These values are very device specific */ int w = argc > 1 ? atoi(argv[1]) : 1920; int h = argc > 2 ? atoi(argv[2]) : 1080; int d = argc > 3 ? atoi(argv[3]) : 96; if (w < 1 || h < 1 || d < 1) fatal("Bad initial value(s)."); int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); if (-1 == fd) fatal("open"); printf("Initializing device screen map as %dx%d @ %ddpi\n", w, h, d); init(fd, w, h, d); while (1) { printf("Enter x & y: "); fflush(stdout); char input[128]; if (!fgets(input, sizeof input, stdin) || 0 == strncmp(".exit", input, 5)) break; int x, y; if (2 != sscanf(input, "%d%d", &x, &y) || x < 0 || y < 0) { fprintf(stderr, "Invalid input.\n"); continue; } printf("Moving cursor to %d,%d\n", x, y); /* input is zero-based, but event positions are one-based */ emit(fd, EV_ABS, ABS_X, 1 + x); emit(fd, EV_ABS, ABS_Y, 1 + y); emit(fd, EV_SYN, SYN_REPORT, 0); } puts("Cleaning up..."); /* give time for events to finish */ sleep(1); if (-1 == ioctl(fd, UI_DEV_DESTROY)) fatal("ioctl UI_DEV_DESTROY"); close(fd); puts("Goodbye."); }

./a.out 4640 1080 96
Initializing device screen map as 4640x1080 @ 96dpi
Enter x & y: 10 10
Moving cursor to 10,10
Enter x & y: 123 321
Moving cursor to 123,321
Enter x & y: .exit
Cleaning up...
Goodbye
如果上述方法不起作用,我遇到的一个
uinput

解决方法(黑客)是在两个轴上相对移动鼠标一个极负的量,这应该将光标放在

0,0
处。然后,您进行第二次相对移动到预期位置。这里的问题是鼠标加速度会影响最终位置。
这与

ydotool

使用的黑客相同。
void move_cursor_to_xy(int fd, int x, int y) { emit(fd, EV_REL, REL_X, INT_MIN); emit(fd, EV_REL, REL_Y, INT_MIN); emit(fd, EV_SYN, SYN_REPORT, 0); emit(fd, EV_REL, REL_X, x); emit(fd, EV_REL, REL_Y, y); emit(fd, EV_SYN, SYN_REPORT, 0); }

另请参阅:

在 Wayland 和 X11 上模拟鼠标和键盘输入

注:X11下有

XWarpPointer,但Wayland似乎不支持通过用户代码直接控制鼠标(潜在的安全风险?)。

© www.soinside.com 2019 - 2024. All rights reserved.