Skip to content

Instantly share code, notes, and snippets.

@gandalf3
Created August 3, 2022 08:55
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 gandalf3/67285f8e4d5e7f740890889f230738a9 to your computer and use it in GitHub Desktop.
Save gandalf3/67285f8e4d5e7f740890889f230738a9 to your computer and use it in GitHub Desktop.
Minimal demo of a working passive keyboard grab in X11 from a kitten for kitty terminal emulator
#!/usr/bin/env python3
import os
import sys
from Xlib import X
from typing import List
from kitty.constants import is_macos
# Installation: place minimal_popup.py in your ~/.config/kitty
# Usage: kitty +kitten minimal_popup <command>
def setup_x11_window(win_id: int) -> None:
import ctypes
from kitty.fast_data_types import x11_display
X11 = ctypes.CDLL("libX11.so")
class Display(ctypes.Structure):
""" opaque struct """
# The Pixmap, Colormap, and Cursor types in Xlib are all defined as "XID".
# If Xlib has been built with _XSERVER64 defined (see X.h +59), then XID is
# defined as "unsigned long" (which on a typical "64 bit" system is 8
# bytes)
# Otherwise, XID is defined as a CARD32 (see Xmd.h +115) which, on a *non*
# "64 bit" system (one where an unsigned long is 4 bytes), is also defined
# as an unsigned long. All that to say, it *should* be safe to consider
# XID to be always an ulong except if we are using a 32 bit xserver in a 64
# bit environment, such that ctypes defines c_ulong as 8 bytes but Xlib's
# ulong is 4. I'm not sure what a reliable way to detect that would be, so
# for now we let sleeping dragons lie~
XID_type = ctypes.c_ulong
override_redirect_mask = ctypes.c_long(1<<9)
# Xlib.h +298
class XSetWindowAttributes(ctypes.Structure):
_fields_ = [
('background_pixmap', XID_type), #Pixmap
('background_pixel', ctypes.c_ulong),
('border_pixmap', XID_type), #Pixmap
('border_pixel', ctypes.c_ulong),
('bit_gravity', ctypes.c_int),
('win_gravity', ctypes.c_int),
('backing_store', ctypes.c_int),
('backing_planes', ctypes.c_ulong),
('backing_pixel', ctypes.c_ulong),
('save_under', ctypes.c_bool),
('event_mask', ctypes.c_long),
('do_not_propagate_mask', ctypes.c_long),
('override_redirect', ctypes.c_bool),
('colormap', XID_type), #Colormap
('cursor', XID_type), #Cursor
]
dpy = x11_display()
dpy = ctypes.cast(dpy, ctypes.POINTER(Display))
X11.XMapWindow.restype = ctypes.c_int
window_attributes = XSetWindowAttributes()
window_attributes.override_redirect = True
X11.XChangeWindowAttributes(dpy, win_id,
override_redirect_mask,
ctypes.pointer(window_attributes)
)
X11.XMapWindow(dpy, win_id)
X11.XGrabKeyboard(dpy, win_id, False, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
def main(sys_args: List[str]) -> None:
global args
if is_macos or not os.environ.get('DISPLAY'):
raise SystemExit('Currently the popup kitten is supported only on X11 desktops')
items = sys_args[1:]
if not items:
raise SystemExit('You must specify the program to run')
sys.argv = ['kitty']
sys.argv.extend(items)
from kitty.main import main as real_main, run_app
run_app.first_window_callback = setup_x11_window
real_main()
if __name__ == '__main__':
main(sys.argv)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment