Skip to content

Instantly share code, notes, and snippets.

@vidarh
Created November 21, 2023 20:16
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 vidarh/1cdbfcdf3cfd8d25a247243963e55a66 to your computer and use it in GitHub Desktop.
Save vidarh/1cdbfcdf3cfd8d25a247243963e55a66 to your computer and use it in GitHub Desktop.
rubywm.rb - a minimalist WM using pure-x11 and based on TinyWM
# 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