Last active
September 6, 2020 12:14
-
-
Save MikuAuahDark/3e1a5b3946d8700e1a26769cc0f8e554 to your computer and use it in GitHub Desktop.
Z -> Left Click in Krita + X11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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