diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-28 18:04:16 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-28 18:04:16 -0400 |
| commit | 1fd8d50a8d7277148ef8a7bbe67d1c62a29429b2 (patch) | |
| tree | 78bbee4b39a8113e45b6c04b5bf1dc86af1d592e /tools/guest-diag/xtest_move.c | |
| parent | 375d627a7abd0c07b95d70395ce89b2f4c42d106 (diff) | |
| download | open-iov-1fd8d50a8d7277148ef8a7bbe67d1c62a29429b2.tar.gz open-iov-1fd8d50a8d7277148ef8a7bbe67d1c62a29429b2.zip | |
tools/guest-diag: working guest-GL capture and input for virtio-gpu virgl guests (SDL swap-hook capture; XSendEvent input - SDL ignores XTEST here; region capture, focus query, window resize)
Diffstat (limited to 'tools/guest-diag/xtest_move.c')
| -rw-r--r-- | tools/guest-diag/xtest_move.c | 171 |
1 files changed, 95 insertions, 76 deletions
diff --git a/tools/guest-diag/xtest_move.c b/tools/guest-diag/xtest_move.c index a156bf8..8331487 100644 --- a/tools/guest-diag/xtest_move.c +++ b/tools/guest-diag/xtest_move.c @@ -1,106 +1,125 @@ // SPDX-License-Identifier: BSD-3-Clause -// xtest_move.c - drive the game with XTEST: hold forward + fire and tap -// left/right periodically so the captured footage has constant motion. +// xtest_move.c - drive the game with XSendEvent-synthetic keys (SDL2 on this +// Xorg ignores XTEST-faked keys; it accepts send_event KeyPress/KeyRelease). +// Finds the game window by name, then holds forward+fire and alternates +// turning left/right so the captured footage has constant motion. // Usage: xtest_move <display> <seconds> #include <X11/Xlib.h> #include <X11/keysym.h> +#include <X11/Xutil.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <string.h> +#include <unistd.h> + +static Display *d; +static Window game; + +static Window find_named(const char *sub) +{ + Window root = DefaultRootWindow(d), r, parent, *kids = NULL; + unsigned int nk = 0; + + if (!XQueryTree(d, root, &r, &parent, &kids, &nk)) + return 0; + for (unsigned int i = 0; i < nk; i++) { + char *name = NULL; -extern int XTestFakeKeyEvent(Display *, unsigned int, int, unsigned long); + if (XFetchName(d, kids[i], &name) && name) { + if (strstr(name, sub)) { + Window w = kids[i]; + + XFree(name); + if (kids) + XFree(kids); + return w; + } + XFree(name); + } + } + if (kids) + XFree(kids); + return 0; +} + +static void send_key(KeySym ks, int down) +{ + XEvent e; + Window root = DefaultRootWindow(d); + int rx, ry, wx, wy; + unsigned int mask; + Window cr, pr; + + if (!game) + return; + memset(&e, 0, sizeof(e)); + e.xkey.type = down ? KeyPress : KeyRelease; + e.xkey.display = d; + e.xkey.window = game; + e.xkey.root = root; + e.xkey.subwindow = None; + e.xkey.time = CurrentTime; + XQueryPointer(d, root, &cr, &pr, &rx, &ry, &wx, &wy, &mask); + e.xkey.x = wx; + e.xkey.y = wy; + e.xkey.x_root = rx; + e.xkey.y_root = ry; + e.xkey.same_screen = True; + e.xkey.keycode = XKeysymToKeycode(d, ks); + e.xkey.state = 0; + XSendEvent(d, game, True, down ? KeyPressMask : KeyReleaseMask, &e); + XFlush(d); +} int main(int argc, char **argv) { - Display *d = XOpenDisplay(argc > 1 ? argv[1] : ":1"); int secs = argc > 2 ? atoi(argv[2]) : 12; - KeyCode kFwd, kFire, kLeft, kRight; int t; + d = XOpenDisplay(argc > 1 ? argv[1] : ":1"); if (!d) { fprintf(stderr, "xtest_move: no display\n"); return 1; } - /* No WM on Xvfb: give the game window input focus ourselves. */ - { - Window r, parent, *kids = NULL; - unsigned int nk = 0; - - if (XQueryTree(d, DefaultRootWindow(d), &r, &parent, &kids, &nk)) { - unsigned int i; - - for (i = 0; i < nk; i++) { - char *nm = NULL; - - if (XFetchName(d, kids[i], &nm) && nm) { - int hit = strstr(nm, "Freedoom") || strstr(nm, "Doom") || - strstr(nm, "gzdoom") || strstr(nm, "Outer"); - - XFree(nm); - if (hit) { - XSetInputFocus(d, kids[i], RevertToParent, CurrentTime); - fprintf(stderr, "xtest_move: focused 0x%lx\n", - (unsigned long)kids[i]); - break; - } - } - } - if (kids) - XFree(kids); - } - } - kFwd = XKeysymToKeycode(d, XK_Up); - kFire = XKeysymToKeycode(d, XK_space); - kLeft = XKeysymToKeycode(d, XK_Left); - kRight = XKeysymToKeycode(d, XK_Right); - fprintf(stderr, "xtest_move: fwd=%d fire=%d left=%d right=%d\n", - kFwd, kFire, kLeft, kRight); - XTestFakeKeyEvent(d, kFwd, 1, 0); - XTestFakeKeyEvent(d, kFire, 1, 0); + game = find_named("Freedoom"); + if (!game) + game = find_named("Outer Prison"); + if (!game) + game = find_named("Doom"); + fprintf(stderr, "xtest_move: game window 0x%lx\n", game); + if (!game) + return 1; + XSetInputFocus(d, game, RevertToParent, CurrentTime); XFlush(d); - /* Continuous motion: rotate steadily (hold a turn key ~0.8 s, then - * the other) while forward+fire stay held - every frame changes. */ - for (t = 0; t < secs * 10; t++) { - KeyCode turn = (t & 8) ? kLeft : kRight; - Window wr, wpar, *wk = NULL; - unsigned int wnk = 0; - /* Re-assert focus every tick: Xvfb has no WM, and any new X client - * (the capture tool) can leave the game unfocused - GZDoom pauses - * the game the moment SDL reports focus loss. */ - if (XQueryTree(d, DefaultRootWindow(d), &wr, &wpar, &wk, &wnk)) { - unsigned int i; + /* Hold forward only (no fire: fire pulls enemies and the player can + * die mid-capture; with +noclip from the command line this is a smooth + * fly-through). */ + send_key(XK_Up, 1); - for (i = 0; i < wnk; i++) { - char *nm = NULL; + for (t = 0; t < secs * 10; t++) { + static int dir; /* 0 = left held, 1 = right held */ - if (XFetchName(d, wk[i], &nm) && nm) { - int hit = strstr(nm, "Freedoom") || strstr(nm, "Outer") || - strstr(nm, "Doom"); + XSetInputFocus(d, game, RevertToParent, CurrentTime); /* keep focus */ + /* Re-assert the held forward key: SDL's held state drops between + * synthetic events, so forward motion stalls unless re-sent. */ + if ((t % 4) == 0) + send_key(XK_Up, 1); + if ((t % 9) == 8) { + KeySym off = dir ? XK_Right : XK_Left; + KeySym on = dir ? XK_Left : XK_Right; - XFree(nm); - if (hit) { - XSetInputFocus(d, wk[i], RevertToParent, - CurrentTime); - break; - } - } - } - if (wk) - XFree(wk); + send_key(off, 0); + usleep(30000); + send_key(on, 1); + dir = !dir; } - XTestFakeKeyEvent(d, kLeft, 0, 0); - XTestFakeKeyEvent(d, kRight, 0, 0); - XTestFakeKeyEvent(d, turn, 1, 0); XFlush(d); usleep(100000); } - XTestFakeKeyEvent(d, kLeft, 0, 0); - XTestFakeKeyEvent(d, kRight, 0, 0); - XTestFakeKeyEvent(d, kFwd, 0, 0); - XTestFakeKeyEvent(d, kFire, 0, 0); - XFlush(d); + send_key(XK_Left, 0); + send_key(XK_Right, 0); + send_key(XK_Up, 0); XCloseDisplay(d); fprintf(stderr, "xtest_move: done\n"); return 0; |
