Created
November 21, 2023 20:16
-
-
Save vidarh/1cdbfcdf3cfd8d25a247243963e55a66 to your computer and use it in GitHub Desktop.
rubywm.rb - a minimalist WM using pure-x11 and based on TinyWM
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
# Based on TinyWM by Nick Welch | |
require 'X11' | |
dpy = X11::Display.new | |
root = dpy.screens.first.root | |
# OK, so pure-x11 *really* badly needs built in keysym lookup, | |
# but I don't want to add that without doing it properly, so this | |
# is a temporary hack. | |
reply = dpy.get_keyboard_mapping | |
$kmap = reply.keysyms.map { |c| X11::KeySyms[c] }.each_slice(reply.keysyms_per_keycode).to_a | |
$key_to_sym = {} | |
$kmap.each_with_index{|sym, key| $key_to_sym[sym.first] = key+dpy.display_info.min_keycode } | |
f1,f2,f3,f4 = *[:f1,:f2,:f3,:f4].map {|sym| $key_to_sym[sym] } | |
[f1,f2,f3,f4].each {|key| dpy.grab_key(true, root, X11::Form::Mod1, key , :async, :async) } | |
mask = X11::Form::ButtonPressMask|X11::Form::ButtonReleaseMask|X11::Form::PointerMotionMask | |
dpy.grab_button(true, root, mask, :async, :async, 0, 0, 1, X11::Form::Mod1) | |
dpy.grab_button(true, root, mask, :async, :async, 0, 0, 3, X11::Form::Mod1) | |
# FIXME: This is *NOT* the right way to find the maximum window size | |
# as it does not take into account panels etc., but as a crude start it's fine. | |
rootgeom = dpy.get_geometry(root) | |
old_geom = {} | |
start = nil | |
attr = nil | |
loop do | |
ev = dpy.next_packet | |
p ev | |
case ev | |
when X11::Form::KeyPress | |
if ev.child | |
case ev.detail | |
when f1 # Alt + F1: Send to top | |
dpy.configure_window(ev.child, stack_mode: :above) | |
when f2 # Alt + F2: Maximize or revert | |
geom = dpy.get_geometry(ev.child) | |
# If maximized, and we know the old size, we revert the size. | |
if (og = old_geom[ev.child]) && | |
geom.x == 0 && geom.y == 0 && | |
geom.width == rootgeom.width && | |
geom.height == rootgeom.height | |
dpy.configure_window(ev.child, x: og.x, y: og.y, width: og.width, height: og.height) | |
else | |
# Otherwise we maximize | |
old_geom[ev.child] = geom | |
dpy.configure_window(ev.child, x: 0, y: 0, width: rootgeom.width, height: rootgeom.height, stack_mode: :above) | |
end | |
when f3 # Alt + F3: Send to bottom | |
dpy.configure_window(ev.child, stack_mode: :below) | |
when f4 # Alt + F4: Close | |
dpy.destroy_window(ev.child) | |
end | |
end | |
when X11::Form::ButtonPress | |
if ev.child # Whichever button, we want to know more about this window | |
attr = dpy.get_geometry(ev.child) | |
start = ev | |
end | |
when X11::Form::MotionNotify # if start.button == 1 we move; if 3 we resize, all with the same request: | |
if start&.child | |
xdiff = ev.root_x - start.root_x; | |
ydiff = ev.root_y - start.root_y; | |
dpy.configure_window(start.child, | |
x: attr.x + (start.detail==1 ? xdiff : 0), | |
y: attr.y + (start.detail==1 ? ydiff : 0), | |
width: [1, attr.width + (start.detail==3 ? xdiff : 0)].max, | |
height: [1, attr.height + (start.detail==3 ? ydiff : 0)].max | |
) | |
end | |
when X11::Form::ButtonRelease | |
# Make sure we don't accidentally operate on another window. | |
start.child = nil if start | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment