Skip to content

Instantly share code, notes, and snippets.

@MikuAuahDark
Last active September 6, 2020 12:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MikuAuahDark/3e1a5b3946d8700e1a26769cc0f8e554 to your computer and use it in GitHub Desktop.
Save MikuAuahDark/3e1a5b3946d8700e1a26769cc0f8e554 to your computer and use it in GitHub Desktop.
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