Modularize

This commit is contained in:
2025-10-03 12:45:54 +01:00
parent 049d762cbb
commit f7451f6ee1
4 changed files with 230 additions and 141 deletions

210
kutu.rb
View File

@@ -1,93 +1,6 @@
#!/usr/bin/env ruby
require "ffi"
module X
extend FFI::Library
ffi_lib File.join(__dir__, "X-kutu.so")
typedef :uint32, :window_id
typedef :uint32, :xcb_window_t
typedef :uint32, :xcb_pixmap_t
class Geometry < FFI::Struct
layout :x, :int16,
:y, :int16,
:width, :uint16,
:height, :uint16
end
class Event < FFI::Struct
layout :type, :int32,
:window, :window_id,
:override_redirect, :int8,
:btn, :uint32,
:x, :int32,
:y, :int32,
:height, :uint32,
:width, :uint32,
:state, :uint32,
:is_root, :int8
end
class SizeHints < FFI::Struct
layout :flags, :uint32,
:x, :int32,
:y, :int32,
:width, :int32,
:height, :int32,
:min_width, :int32,
:min_height, :int32,
:max_width, :int32,
:max_height, :int32,
:width_inc, :int32,
:height_inc, :int32,
:min_aspect_num, :int32,
:min_aspect_den, :int32,
:max_aspect_num, :int32,
:max_aspect_den, :int32,
:base_width, :int32,
:base_height, :int32,
:win_gravity, :uint32
end
class WMHints < FFI::Struct
layout :flags, :int32,
:input, :uint32,
:initial_state,:int32,
:icon_pixmap, :xcb_pixmap_t,
:icon_window, :xcb_window_t,
:icon_x, :int32,
:icon_y, :int32,
:icon_mask, :xcb_pixmap_t,
:window_group, :xcb_window_t
end
attach_function :deploy, [], :int
attach_function :add_keybind, [:int], :void
attach_function :add_mousebind, [:int], :void
attach_function :cleanup, [], :void
attach_function :focus, [:window_id], :void
attach_function :get_focus, [], :window_id
attach_function :subscribe, [:window_id], :void
attach_function :kill, [:window_id], :void
attach_function :show, [:window_id], :void
attach_function :hide, [:window_id], :void
attach_function :send_to_top, [:window_id], :void
attach_function :free_geometry, [:pointer], :void
attach_function :get_geometry, [:window_id], Geometry.by_value
attach_function :get_pointer, [], Geometry.by_value
attach_function :get_screen, [], Geometry.by_value
attach_function :warp_pointer, [:window_id, :int, :int], :void
attach_function :move_window, [:window_id, :int, :int], :void
attach_function :resize_window, [:window_id, :int, :int], :void
attach_function :wait_for_event, [], Event.by_value
attach_function :get_wm_name, [:window_id], :string
attach_function :get_wm_n_hints, [:window_id], SizeHints.by_value
attach_function :get_wm_hints, [:window_id], WMHints.by_value
attach_function :get_wm_transient_for, [:window_id], :uint8
attach_function :set_wm_state, [:window_id, :int], :void
end
require_relative "./lib/X-kutu"
if X.deploy < 0
raise "Failed to deploy X"
@@ -96,8 +9,15 @@ end
at_exit { X.cleanup }
$monitors = {}
$workspaces = {}
$windows = {}
def refresh_monitors
$keybind_actions = {}
$mousebind_actions = {}
$mouse_data = {}
def refresh_monitors!
$monitors.clear
xrandr_output = `xrandr --query`
connected = xrandr_output.each_line.select { |line| line.include?(" connected") }
@@ -123,13 +43,7 @@ def fixed_size_or_aspect?(hints)
same_size || fixed_aspect
end
refresh_monitors
$workspaces = {}
$windows = {}
$keybind_actions = {}
$mousebind_actions = {}
refresh_monitors!
def keybind(key, &block)
X.add_keybind key
@@ -329,7 +243,6 @@ class Workspace
end
EVENT_TYPES = {
0 => :unused,
1 => :create,
2 => :closed,
3 => :enter,
@@ -368,8 +281,37 @@ end
mousebind 1
mousebind 3
$mouse_state = -1
$mouse_window = -1
def compute_drop_targets!
targets = []
$monitors.each_value do |monitor|
margin = 40
targets << { type: :monitor_top, x: monitor[:x], y: monitor[:y], width: monitor[:width], height: margin, monitor: monitor }
targets << { type: :monitor_bottom, x: monitor[:x], y: monitor[:y] + monitor[:height] - margin, width: monitor[:width], height: margin, monitor: monitor }
targets << { type: :monitor_left, x: monitor[:x], y: monitor[:y], width: margin, height: monitor[:height], monitor: monitor }
targets << { type: :monitor_right, x: monitor[:x] + monitor[:width] - margin, y: monitor[:y], width: margin, height: monitor[:height], monitor: monitor }
end
$windows.each do |w|
# next if w.hidden?
margin = 60
targets << { type: :window_top, x: w.x, y: w.y, width: w.width, height: margin, window: w }
targets << { type: :window_bottom, x: w.x, y: w.y + w.height - margin, width: w.width, height: margin, window: w }
targets << { type: :window_left, x: w.x, y: w.y, width: margin, height: w.height, window: w }
targets << { type: :window_right, x: w.x + w.width - margin, y: w.y, width: margin, height: w.height, window: w }
end
targets
end
def drop_target(targets, cx, cy)
targets.find do |t|
cx >= t[:x] &&
cy >= t[:y] &&
cx < t[:x] + t[:width] &&
cy < t[:y] + t[:height]
end
end
loop do
event = X.wait_for_event
@@ -377,7 +319,6 @@ loop do
when :create
next unless event[:override_redirect].zero?
X.subscribe event[:window]
X.focus event[:window]
when :closed
pp "Deleting window #{event[:window]}"
$windows[event[:window]]&.delete
@@ -385,52 +326,65 @@ loop do
when :enter
X.focus event[:window]
X.send_to_top event[:window]
floating_windows = $windows.select { |_i, w| w.floating }
floating_windows.each { |_i, w| X.send_to_top w.window_id }
when :showed
next unless event[:override_redirect].zero?
X.focus event[:window]
X.send_to_top event[:window]
floating_windows = $windows.select { |_i, w| w.floating }
floating_windows.each { |_i, w| X.send_to_top w.window_id }
when :show_request
X.show event[:window]
$workspaces[:main].add event[:window] if $windows[event[:window]].nil?
$workspaces[:main].tiled_root_block.compute_geometry!
when :mouse_press
next if event[:is_root] != 0
X.send_to_top event[:window]
X.focus event[:window]
$mouse_state = event[:btn]
$mouse_window = $windows[event[:window]]
if $mouse_window.nil?
$mouse_state = -1
$mouse_window = -1
next
$mouse_data[:btn] = event[:btn]
$mouse_data[:window] = $windows[event[:window]]
X.grab_pointer event[:window]
if $mouse_data[:window].floating
$mouse_data[:mode] = :floating
$mouse_data[:pointer] = X.get_pointer
$mouse_data[:geometry] = X.get_geometry event[:window]
else
$mouse_data[:mode] = :tiled
end
$mouse_pos_start = X.get_pointer
$geom_start = X.get_geometry event[:window]
$mousebind_actions[event[:btn]]&.call(event)
when :mouse_drag
screen_bounds = $monitors[:primary]
mouse_pos = X.get_pointer
dx = mouse_pos[:x] - $mouse_pos_start[:x]
dy = mouse_pos[:y] - $mouse_pos_start[:y]
if $mouse_state == 1
new_x = [[$geom_start[:x] + dx, screen_bounds[:x]].max,
screen_bounds[:x] + screen_bounds[:width] - $geom_start[:width]].min
new_y = [[$geom_start[:y] + dy, screen_bounds[:y]].max,
screen_bounds[:y] + screen_bounds[:height] - $geom_start[:height]].min
$mouse_window.move new_x, new_y
elsif $mouse_state == 3
$mouse_window.resize [$geom_start[:width] + dx, 50].max,
[$geom_start[:height] + dy, 50].max
if $mouse_data[:mode] == :floating
dx = mouse_pos[:x] - $mouse_data[:pointer][:x]
dy = mouse_pos[:y] - $mouse_data[:pointer][:y]
if $mouse_data[:btn] == 1
$mouse_data[:window].move $mouse_data[:geometry][:x] + dx, $mouse_data[:geometry][:y] + dy
elsif $mouse_data[:btn] == 3
X.resize_window $mouse_data[:window].window_id,
[$mouse_data[:geometry][:width] + dx, 50].max,
[$mouse_data[:geometry][:height] + dy, 50].max
end
elsif $mouse_data[:mode] == :tiled
if $mouse_data[:btn] == 1
find_targets = compute_drop_targets!
target = drop_target(find_targets, mouse_pos[:x], mouse_pos[:y])
X.draw_rectangle target[:x], target[:y], target[:width], target[:height], 0x00ff00
elsif $mouse_data[:btn] == 3
# TODO: tile resize . dynamic
end
end
when :mouse_release
if [1, 3].include?($mouse_state)
X.focus $mouse_window.window_id
X.send_to_top $mouse_window.window_id
$mouse_state = -1
$mouse_window = -1
$mouse_pos_start = { x: -1, y: -1 }
$geom_start = nil
if $mouse_data[:mode] == :floating
if [1, 3].include?($mouse_data[:btn])
X.ungrab_pointer
X.focus $mouse_data[:window].window_id
X.send_to_top $mouse_data[:window].window_id
end
elsif $mouse_data[:mode] == :tiled
# TODO
end
X.focus $mouse_data[:window].window_id if $mouse_data[:window]
$mouse_data = {}
when :key_press
$keybind_actions[event[:btn]]&.call(event)
when :key_release