Z -> Left Click in Krita + X11
// You can use part or all of this code without my permission | |
// $ clang++ main.cpp -lX11 -lXtst -lpthread | |
#include <csignal> | |
#include <chrono> | |
#include <iostream> | |
#include <string> | |
#include <thread> | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
#include <X11/XKBlib.h> | |
#include <X11/extensions/XTest.h> | |
Window getWindowByClass(Display *disp, Window cur, const std::string &name, XClassHint *ch = nullptr) | |
{ | |
bool allocClassHint = false; | |
Window w = 0; | |
// Allocate class hint struct, for top level function only | |
if (ch == nullptr) | |
{ | |
ch = XAllocClassHint(); | |
allocClassHint = true; | |
} | |
// Find class | |
if (XGetClassHint(disp, cur, ch)) | |
{ | |
if (ch->res_class && name == ch->res_class) | |
w = cur; | |
// Free class hint | |
XFree(ch->res_name); | |
ch->res_name = nullptr; | |
XFree(ch->res_class); | |
ch->res_class = nullptr; | |
} | |
if (!w) | |
{ | |
// Enumerate all childs | |
Window retval, root, parent, *child; | |
unsigned int childCount; | |
if (XQueryTree(disp, cur, &root, &parent, &child, &childCount)) | |
{ | |
for (unsigned int i = 0; i < childCount; i++) | |
{ | |
Window candidate = getWindowByClass(disp, child[i], name, ch); | |
if (candidate) | |
{ | |
w = candidate; | |
break; | |
} | |
} | |
XFree(child); | |
} | |
} | |
if (allocClassHint) | |
XFree(ch); | |
return w; | |
} | |
static bool stop = false; | |
static bool closed = false; | |
// Ctrl+C handling | |
void interruptHandler(int _) | |
{ | |
stop = true; | |
} | |
int main() | |
{ | |
Display *display = XOpenDisplay(nullptr); | |
Window root = DefaultRootWindow(display); | |
signal(SIGINT, interruptHandler); | |
XkbSetDetectableAutoRepeat(display, True, nullptr); | |
std::cout << "looking for Krita" << std::endl; | |
Window krita = getWindowByClass(display, root, "krita"); | |
while (!stop && krita == 0) | |
{ | |
std::this_thread::sleep_for(std::chrono::milliseconds(100)); | |
krita = getWindowByClass(display, root, "krita"); | |
} | |
if (!stop) | |
{ | |
// If X11 is not thread safe, then it should be fine to | |
// have another thread its own "display", right? | |
std::thread kritaChecker([]() | |
{ | |
Display *display = XOpenDisplay(nullptr); | |
Window root = DefaultRootWindow(display); | |
while (!stop) | |
{ | |
if (getWindowByClass(display, root, "krita") == 0) | |
{ | |
stop = true; | |
closed = true; | |
std::cout << "krita closed" << std::endl; | |
break; | |
} | |
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | |
} | |
XCloseDisplay(display); | |
}); | |
static int keycodeZ = XKeysymToKeycode(display, XK_Z); | |
bool isPressed = false; | |
XGrabKey(display, keycodeZ, 0, krita, 0, GrabModeAsync, GrabModeAsync); | |
XSelectInput(display, krita, KeyPressMask | KeyReleaseMask); | |
std::cout << "Starting event loop" << std::endl; | |
while (!stop || isPressed) | |
{ | |
int events = XPending(display); | |
while (events > 0) | |
{ | |
XEvent ev; | |
XNextEvent(display, &ev); | |
events--; | |
if (ev.xkey.keycode == keycodeZ) | |
{ | |
switch (ev.type) | |
{ | |
case KeyPress: | |
{ | |
if (!isPressed && ev.xkey.state == 0) | |
{ | |
std::cout << "press left mouse button " << ev.xkey.serial << std::endl; | |
XTestFakeButtonEvent(display, 1, 1, CurrentTime); | |
isPressed = true; | |
} | |
break; | |
} | |
case KeyRelease: | |
{ | |
if (isPressed) | |
{ | |
XTestFakeButtonEvent(display, 1, 0, CurrentTime); | |
std::cout << "release left mouse button " << ev.xkey.serial << std::endl; | |
isPressed = false; | |
} | |
} | |
} | |
} | |
} | |
std::this_thread::sleep_for(std::chrono::milliseconds(10)); | |
} | |
// The kritaChecker thread sets closed = true if Krita is closed | |
// which invalidates the krita window | |
if (!closed) | |
XUngrabKey(display, keycodeZ, 0, krita); | |
// stop = true which means the thread should terminate. | |
kritaChecker.join(); | |
} | |
XCloseDisplay(display); | |
return 0; | |
} |
; Windows? fine, use AutoHotkey | |
#NoEnv | |
#IfWinActive ahk_exe krita.exe | |
z:: | |
Click down | |
Keywait z | |
Click up | |
return | |
#IfWinActive |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment