Cleanups/refactoring

This commit is contained in:
Vidar Hokstad 2024-01-23 17:56:53 +00:00
parent d4fdee3b57
commit aa14a824cf
7 changed files with 112 additions and 116 deletions

View file

@ -1,4 +1,5 @@
class Desktop
attr_accessor :name, :id, :wm, :layout, :window
@ -9,7 +10,7 @@ class Desktop
def show = children.each(&:show)
def hide = children.each(&:hide)
def inspect = "<Desktop id=#{id} window=#{@window}>"
def update_layout = (layout&.call if active?)
def update_layout = (layout&.call(@wm.focus) if active?)
end

28
floating.rb Normal file
View file

@ -0,0 +1,28 @@
#
# Apart from the very limited "place" the main purpose of this
# class is to ensure there *always* is a layout
#
class FloatingLayout
def initialize(rootgeom)
@rootgeom = rootgeom
end
def find(w) = nil
def place(w, focus)
attr = w.get_geometry
return if attr.is_a?(X11::Form::Error)
x = attr.x
y = attr.y
width = attr.width
height = attr.height
width = @rootgeom.width / 2 if width < 10
height = @rootgeom.height - 100 if height < 10
x = (@rootgeom.width - width) /2 if x == 0
y = (@rootgeom.height - height)/2 if y == 0
w.configure(x:, y:, width:, height:)
end
def call = nil
end

33
geom.rb
View file

@ -5,7 +5,8 @@ module X11
def inspect = "<Geometry x=#{x.to_i} y=#{y.to_i} width=#{width.to_i} height=#{height.to_i}>"
end
end
end
end
def gap(geom,g)
geom = geom.dup
geom.x += g
@ -39,3 +40,33 @@ def split_geom(geom, dir, node, gap, ratio)
else raise "Invalid direction"
end
end
# FIXME: This needs tweaks. Especially for floating windows, where
# what we really want is to e.g. treat partially overlapping windows
# so that the one closest to *overlapping* the correct border is picked
def find_closest(w, dir, from)
return nil if from.empty?
g = w.get_geometry rescue nil # FIXME
return nil if g.nil?
predicate = case dir
when :left then predicate = ->(g2) { g.x - (g2.x + g2.width) }
when :right then predicate = ->(g2) { g2.x - (g.x + g.width) }
when :up then predicate = ->(g2) { g.y - (g2.y + g2.height) }
when :down then predicate = ->(g2) { g2.y - (g.y + g.height) }
end
secondary = case dir
when :left, :right then ->(g2) { (g.y - g2.y).abs }
else ->(g2) { (g.x - g2.x).abs }
end
return from.map do |win|
g2 = win.get_geometry rescue nil
next if g2.nil?
[predicate.call(g2), secondary.call(g2), win]
end.reject{|d1,d2, win| d1 < 0}
.min_by{|d1,d2, win| [d1,d2] }&.last
end

View file

@ -25,6 +25,8 @@ class Leaf
if !k.member?(window)
return nil if !@iclass
@window = nil
else
k.delete(window)
end
self
end

12
node.rb
View file

@ -30,15 +30,19 @@ class Node
@nodes.length <= 1 ? @nodes.first : self
end
def append(window) = @nodes << Leaf.new(window, parent: self)
def append(window)
@nodes << Leaf.new(window, parent: self)
end
def place_adjacent(window, focus, dir)
l = (self.parent || self).find(window)
raise if l
if nodes.length == 2
i = nodes.index(focus)
dir ||= Node.swapdir(@dir)
i = nodes.index(focus) || 1
dir ||= Node.swapdir(@dir) || :lr
@nodes[i] = Node.new([focus, Leaf.new(window)], parent: self, dir: dir)
else
@dir = dir if dir
#@dir ||= dir if dir
append(window)
end
end

View file

@ -27,14 +27,18 @@ class TiledLayout
return if apply_placements(window)
return @root.place(window) if !focus
leaf = self.find(focus)
leaf&.parent&.place_adjacent(window, leaf, dir) || @root.place(window)
if leaf && leaf.parent
leaf.parent.place_adjacent(window, leaf, dir)
else @root.place(window)
end
call
true
end
def call
def call(focus=nil)
new_windows = windows - @root.children
cleanup
new_windows.each { place(_1) }
new_windows.each { place(_1,focus) }
g = GAP/(1.3 ** @root.children.length)
@root.layout(gap(@geom,g), g)
end

140
wm.rb
View file

@ -1,4 +1,6 @@
require_relative 'floating'
class WindowManager
attr_reader :dpy, :desktops, :windows, :focus
@ -11,6 +13,8 @@ class WindowManager
@border_normal = 0x88666666
@border_focus = 0xffff66ff
@floating = FloatingLayout.new(rootgeom)
@desktops ||= num_desktops.times.map do |num|
Desktop.new(self, num, name: (num+1).to_s[-1])
end
@ -69,8 +73,10 @@ class WindowManager
def current_desktop = desktops[current_desktop_id] || desktops[0]
def root_id = (@root_id ||= @dpy.screens.first.root)
def root = (@root ||= X11::Window.new(@dpy, root_id))
def update_layout = current_desktop&.update_layout
def layout = current_desktop&.layout || @floating
def layout_for(w) = (w.floating? ? @floating : layout)
def update_layout = layout.call(@focus)
# FIXME: Does not take into account panels
def rootgeom = (@rootgeom ||= root.get_geometry)
def window(wid)
@ -88,7 +94,6 @@ class WindowManager
return w if w
w = Window.new(self, wid)
begin
STDERR.puts "\e[35madopt6\e[0m: #{wid.to_s(16)}; type=#{w.type.inspect}"
# FIXME: At least some of these ought to "adopted" but set as
# floating/non-layout so they stay on a single desktop.
#
@ -102,26 +107,18 @@ class WindowManager
w.type == dpy.atom(:_NET_WM_WINDOW_TYPE_SPLASH) ||
w.type == dpy.atom(:_NET_WM_WINDOW_TYPE_UTILITY)
w.floating = true
p [:ignoring, w.inspect]
w.stack
return w
end
if w.desktop?
w.floating = true
end
STDERR.puts "\e[35madopt5\e[0m: #{wid.to_s(16)}"
attr = w.get_window_attributes
if attr.wclass == 2 # InputOnly
# We don't want to adopt inputonly windows, as they're
# for event handling only
return w
end
return w if attr.wclass == 2 # InputOnly
return w if attr.override_redirect
w.mapped = attr.map_state != 0
geom = w.get_geometry
return w if geom.is_a?(X11::Form::Error)
return w if geom.width < 2 || geom.height < 2
return w if geom.is_a?(X11::Form::Error) || geom.width < 2 || geom.height < 2
@windows[wid] = w
wms = w.get_property(:_NET_WM_STATE, :atom)&.value
@ -152,23 +149,8 @@ class WindowManager
def map_window(wid)
w = window(wid)
attr = w.get_geometry
return if attr.is_a?(X11::Form::Error)
x = attr.x
y = attr.y
width = attr.width
height = attr.height
width = rootgeom.width / 2 if width < 10
height = rootgeom.height - 100 if height < 10
w.mapped = true
if w.floating?
x = (rootgeom.width - width) /2 if x == 0
y = (rootgeom.height - height)/2 if y == 0
w.configure(x:, y:, width:, height:)
else
current_desktop.layout&.place(w, @focus)
end
layout_for(w).place(w, @focus) unless layout_for(w).find(w)
w.map
set_focus(wid) unless w.special?
end
@ -204,7 +186,6 @@ class WindowManager
def set_focus(wid)
return if wid == root_id
w = window(wid)
p [:set_focus, wid, w]
# FIXME: This may be a bit brutal, in that it prevents keyboard control of the desktop or dock.
return if w.special?
@ -216,52 +197,6 @@ class WindowManager
change_property(:_NET_ACTIVE_WINDOW, :window, wid)
end
# FIXME: This needs tweaks. Especially for floating windows, where
# what we really want is to e.g. treat partially overlapping windows
# so that the one closest to *overlapping* the correct border is picked
def find_closest(w, dir, from)
g = w.get_geometry
case dir
when :left then predicate = ->(g2) { g.x - (g2.x + g2.width) }
when :right then predicate = ->(g2) { g2.x - (g.x + g.width) }
when :up then predicate = ->(g2) { g.y - (g2.y + g2.height) }
when :down then predicate = ->(g2) { g2.y - (g.y + g.height) }
end
min = 10000
list = []
p [:here]
from.each do |win|
p [:checking, win, dir]
g2 = win.get_geometry rescue nil
next if g2.nil?
dist = predicate.call(g2).abs
if dist <= min
if dist == min
list << win
else
list = [win]
min = dist
end
end
p [dist, min, list]
end
p [min, list]
return nil if list.empty?
return list.first if list.length == 1
# More than one in the same direction,
# FIXME: For now we just pick the first.
# Ideally I'd probably want to request the pointer location
# and find the closest along the other axis.
# May also want to check which window had focus last,
# and track last direction, so that e.g. left->right->left
# will go back to the same window
return list.first
end
def destroy_window(wid)
if w = @windows[wid]
@windows.delete(wid)
@ -308,7 +243,6 @@ class WindowManager
rescue # FIXME: Why is this here?
end
#p w
if @start.detail == 1 # Move
if w.floating?
w.configure(x: @attr.x + xdiff, y: @attr.y + ydiff)
@ -326,39 +260,33 @@ class WindowManager
@attr.height = @attr.height+ (tb ? -ydiff : ydiff)
w.configure(x: @attr.x, y: @attr.y, width: @attr.width, height: @attr.height)
else
layout = current_desktop&.layout
if layout
ancestors = ->(first,dir,flag, &block) do
first&.ancestors&.each_cons(2) do |prev, node|
if node.dir == dir &&
((node.nodes[0] == prev && !flag) ||
(node.nodes[1] == prev))
node.ratio += node.geom ? block.call(prev,node,flag) : 0.0
node.ratio = node.ratio.clamp(0.1,0.9)
return
end
ancestors = ->(first,dir,flag, &block) do
first&.ancestors&.each_cons(2) do |prev, node|
if node.dir == dir &&
((node.nodes[0] == prev && !flag) ||
(node.nodes[1] == prev))
node.ratio += node.geom ? block.call(prev,node,flag) : 0.0
node.ratio = node.ratio.clamp(0.1,0.9)
return
end
end
ancestors.call(w.layout_leaf,:lr,lr) do |prev, node, flag|
(((node.geom.width * node.ratio) + xdiff)/node.geom.width) - node.ratio
end
ancestors.call(w.layout_leaf, :tb, tb) do |prev,node, flag|
(((node.geom.height * node.ratio) + ydiff)/node.geom.height) - node.ratio
end
update_layout
end
ancestors.call(w.layout_leaf,:lr,lr) do |prev, node, flag|
(((node.geom.width * node.ratio) + xdiff)/node.geom.width) - node.ratio
end
ancestors.call(w.layout_leaf, :tb, tb) do |prev,node, flag|
(((node.geom.height * node.ratio) + ydiff)/node.geom.height) - node.ratio
end
update_layout
end
@start.root_x = ev.root_x
@start.root_y = ev.root_y
end
end
def on_button_release(ev)
@start.child = nil if @start
end
def on_button_release(ev) = (@start.child = nil if @start)
def on_focus_in(ev) = focus || set_focus(ev.event)
def on_enter_notify(ev) = set_focus(ev.event)
def on_unmap_notify(ev) = window(ev.window)&.desktop&.update_layout
@ -414,7 +342,6 @@ class WindowManager
dir = dpy.get_atom_name(dir).downcase.to_sym
return if !@focus || @focus.special?
w = find_closest(@focus, dir, @focus.desktop.mapped_regular_children)
set_focus(w.wid) if w
end
@ -422,8 +349,7 @@ class WindowManager
def on_rwm_shift_direction(_,dir)
# FIXME: Respect the window passed instead of doing it to @focus
return if !@focus || @focus.special?
node = current_desktop&.layout&.find(@focus)
if node
if node = layout.find(@focus)
node = node.parent if node.is_a?(Leaf)
node.dir = node.dir == :lr ? :tb : :lr
current_desktop&.update_layout
@ -435,13 +361,13 @@ class WindowManager
# FIXME: Respect the window passed instead of doing it to @focus
# no matter what
return if !@focus || @focus.special?
node = current_desktop&.layout&.find(@focus)
if node
# FIXME: Move to layout?
if node = layout.find(@focus)
node = node.parent if node.is_a?(Leaf)
tmp = node.nodes[0]
node.nodes[0] = node.nodes[1]
node.nodes[1] = tmp
current_desktop&.update_layout
update_layout
end
end