Updates
This commit is contained in:
10
X-kutu.c
10
X-kutu.c
@@ -14,8 +14,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
// XCB headers
|
// XCB header
|
||||||
#include <xcb/randr.h>
|
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
@@ -97,13 +96,6 @@ int deploy(void) {
|
|||||||
// Free a geometry structure
|
// Free a geometry structure
|
||||||
void free_geometry(Geometry *g) { free(g); }
|
void free_geometry(Geometry *g) { free(g); }
|
||||||
|
|
||||||
// Get monitor geometries using XRandR, returns an array of Geometry structures
|
|
||||||
// The caller is responsible for freeing the returned array
|
|
||||||
Geometry *xrandr_get_monitors(void) {
|
|
||||||
// TODO: Loop through monitors and return their geometries
|
|
||||||
// return all;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set input focus to a window
|
// Set input focus to a window
|
||||||
void focus(xcb_window_t win) {
|
void focus(xcb_window_t win) {
|
||||||
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, win,
|
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, win,
|
||||||
|
|||||||
23
compile.sh
Executable file
23
compile.sh
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DIR="$(cd -- "$(dirname -- "$0")" && pwd)"
|
||||||
|
|
||||||
|
if ! XCB=$(pkg-config --cflags --libs xcb 2>/dev/null); then
|
||||||
|
echo "Error: lib-xcb not found. Please install lib-xcb." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! xrandr --version >/dev/null; then
|
||||||
|
echo "Error: xrandr not found. Please install xrandr." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
gcc -shared -fPIC -Wall -Wextra -o "$DIR/X-kutu.so" "$DIR/X-kutu.c" $XCB
|
||||||
|
|
||||||
|
if [ ! -f "$DIR/X-kutu.so" ]; then
|
||||||
|
echo "Error: compilation failed." >&2
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Success: $DIR/X-kutu.so is compiled."
|
||||||
|
fi
|
||||||
135
kutu.rb
135
kutu.rb
@@ -4,10 +4,9 @@ require "ffi"
|
|||||||
|
|
||||||
module X
|
module X
|
||||||
extend FFI::Library
|
extend FFI::Library
|
||||||
ffi_lib "/home/syed/main/kutu/X-kutu.so"
|
ffi_lib File.join(__dir__, "X-kutu.so")
|
||||||
|
|
||||||
typedef :uint32, :window_id
|
typedef :uint32, :window_id
|
||||||
typedef :pointer, :goemetry_array
|
|
||||||
|
|
||||||
class Geometry < FFI::Struct
|
class Geometry < FFI::Struct
|
||||||
layout :x, :int16,
|
layout :x, :int16,
|
||||||
@@ -40,8 +39,7 @@ module X
|
|||||||
attach_function :show, [:window_id], :void
|
attach_function :show, [:window_id], :void
|
||||||
attach_function :hide, [:window_id], :void
|
attach_function :hide, [:window_id], :void
|
||||||
attach_function :send_to_top, [:window_id], :void
|
attach_function :send_to_top, [:window_id], :void
|
||||||
attach_function :xrandr_get_monitors, [], :goemetry_array
|
attach_function :free_geometry, [:pointer], :void
|
||||||
attach_function :free_geometry, [:goemetry_array], :void
|
|
||||||
attach_function :get_geometry, [:window_id], Geometry.by_value
|
attach_function :get_geometry, [:window_id], Geometry.by_value
|
||||||
attach_function :get_pointer, [], Geometry.by_value
|
attach_function :get_pointer, [], Geometry.by_value
|
||||||
attach_function :get_screen, [], Geometry.by_value
|
attach_function :get_screen, [], Geometry.by_value
|
||||||
@@ -49,20 +47,6 @@ module X
|
|||||||
attach_function :move_window, [: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 :resize_window, [:window_id, :int, :int], :void
|
||||||
attach_function :wait_for_event, [], Event.by_value
|
attach_function :wait_for_event, [], Event.by_value
|
||||||
|
|
||||||
def self.get_monitors
|
|
||||||
ptr = xrandr_get_monitors
|
|
||||||
return [] if ptr.null?
|
|
||||||
monitors = []
|
|
||||||
2.times do |i|
|
|
||||||
geom_ptr = ptr + i * Geometry.size
|
|
||||||
geom = Geometry.new(geom_ptr)
|
|
||||||
break if geom[:width] == 0 || geom[:height] == 0
|
|
||||||
monitors << geom
|
|
||||||
end
|
|
||||||
free_geometry(ptr)
|
|
||||||
monitors
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if X.deploy < 0
|
if X.deploy < 0
|
||||||
@@ -71,7 +55,17 @@ end
|
|||||||
|
|
||||||
at_exit { X.cleanup }
|
at_exit { X.cleanup }
|
||||||
|
|
||||||
$monitors = X.get_monitors
|
$monitors = {}
|
||||||
|
|
||||||
|
xrandr_output = `xrandr --query`
|
||||||
|
connected = xrandr_output.each_line.select { |line| line.include?(" connected") }
|
||||||
|
connected.sort_by! { |line| line.include?(" primary") ? 0 : 1 }
|
||||||
|
connected.first(2).each_with_index do |line, index|
|
||||||
|
next unless line =~ /(\d+)x(\d+)\+(\d+)\+(\d+)/
|
||||||
|
w, h, x, y = $1.to_i, $2.to_i, $3.to_i, $4.to_i
|
||||||
|
key = index.zero? ? :primary : :secondary
|
||||||
|
$monitors[key] = { x: x, y: y, width: w, height: h }
|
||||||
|
end
|
||||||
|
|
||||||
$workspaces = {}
|
$workspaces = {}
|
||||||
$windows = {}
|
$windows = {}
|
||||||
@@ -98,24 +92,30 @@ class Node
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Window < Node
|
class Window < Node
|
||||||
attr_accessor :window_id
|
attr_accessor :window_id, :x, :y, :width, :height
|
||||||
|
|
||||||
def initialize(window_id)
|
def initialize(window_id)
|
||||||
@window_id = window_id
|
@window_id = window_id
|
||||||
super
|
super()
|
||||||
end
|
end
|
||||||
|
|
||||||
def each_leaf(&block)
|
def each_leaf(&block)
|
||||||
block.call(self)
|
block.call(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply_geometry
|
||||||
|
X.move_window @window_id, @x, @y
|
||||||
|
X.resize_window @window_id, @width, @height
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class WindowBlock < Node
|
class WindowBlock < Node
|
||||||
attr_accessor :children, :direction, :size
|
attr_accessor :children, :direction, :size, :x, :y, :width, :height
|
||||||
|
|
||||||
def initialize()
|
def initialize(direction = :horizontal)
|
||||||
@children = []
|
@children = []
|
||||||
super
|
@direction = direction
|
||||||
|
super()
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_node(node)
|
def add_node(node)
|
||||||
@@ -123,33 +123,68 @@ class WindowBlock < Node
|
|||||||
end
|
end
|
||||||
|
|
||||||
def each_leaf(&block)
|
def each_leaf(&block)
|
||||||
node.children.each do |child|
|
@children.each { |child| child.each_leaf(&block) }
|
||||||
child.each_leaf(&block)
|
end
|
||||||
|
|
||||||
|
def each_node(&block)
|
||||||
|
block.call(self)
|
||||||
|
@children.each { |child| child.each_node(&block) if child.is_a? WindowBlock }
|
||||||
|
end
|
||||||
|
|
||||||
|
def compute_geometry!
|
||||||
|
return if @children.empty?
|
||||||
|
|
||||||
|
horizontal = @direction == :horizontal
|
||||||
|
total_fixed = @children.map { |c| c.size || 0 }.sum
|
||||||
|
flex_count = @children.count { |c| c.size.to_i <= 0 }
|
||||||
|
total_space = horizontal ? @width : @height
|
||||||
|
remaining_space = total_space - total_fixed
|
||||||
|
remaining_space = 0 if remaining_space < 0
|
||||||
|
flex_size = flex_count > 0 ? remaining_space / flex_count : 0
|
||||||
|
pos = horizontal ? @x : @y
|
||||||
|
|
||||||
|
@children.each do |child|
|
||||||
|
child_size = child.size.to_i > 0 ? child.size.to_i : flex_size
|
||||||
|
if horizontal
|
||||||
|
child.x = pos
|
||||||
|
child.y = @y
|
||||||
|
child.width = child_size
|
||||||
|
child.height = @height
|
||||||
|
else
|
||||||
|
child.x = @x
|
||||||
|
child.y = pos
|
||||||
|
child.width = @width
|
||||||
|
child.height = child_size
|
||||||
|
end
|
||||||
|
child.compute_geometry! if child.is_a?(WindowBlock)
|
||||||
|
child.apply_geometry if child.is_a?(Window)
|
||||||
|
pos += child_size
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class RootWindowBlock < WindowBlock
|
class RootWindowBlock < WindowBlock
|
||||||
attr_accessor :x, :y, :width, :height
|
|
||||||
|
|
||||||
def initialize(workspace)
|
def initialize(workspace)
|
||||||
super
|
super(:horizontal)
|
||||||
@x = workspace.x
|
@x = workspace.x
|
||||||
@y = workspace.y
|
@y = workspace.y
|
||||||
@width = workspace.width
|
@width = workspace.width
|
||||||
@height = workspace.height
|
@height = workspace.height
|
||||||
@direction = :vertical
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Workspace
|
class Workspace
|
||||||
attr_reader :name, :monitor, :x, :y, :width, :height
|
attr_reader :name, :monitor, :x, :y, :width, :height, :tiled_root_block
|
||||||
|
|
||||||
def initialize(name, monitor = 0)
|
def initialize(name, monitor = :primary)
|
||||||
|
@monitor = monitor
|
||||||
|
@x = $monitors[monitor][:x]
|
||||||
|
@y = $monitors[monitor][:y]
|
||||||
|
@width = $monitors[monitor][:width]
|
||||||
|
@height = $monitors[monitor][:height]
|
||||||
@tiled_root_block = RootWindowBlock.new(self)
|
@tiled_root_block = RootWindowBlock.new(self)
|
||||||
@untiled_windows = []
|
@untiled_windows = []
|
||||||
@name = name
|
@name = name
|
||||||
@monitor = monitor
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def windows
|
def windows
|
||||||
@@ -160,32 +195,29 @@ class Workspace
|
|||||||
end
|
end
|
||||||
|
|
||||||
def add(window_id)
|
def add(window_id)
|
||||||
@windows[window_id] = Window.new(window_id)
|
@tiled_root_block.add_node Window.new(window_id)
|
||||||
$windows[window_id] = self
|
$windows[window_id] = self
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove(window_id)
|
def remove(window_id)
|
||||||
@windows.delete window_id
|
@untiled_windows.delete window_id
|
||||||
|
@tiled_root_block.each_node { |node| node.children.delete window_id }
|
||||||
$windows.delete window_id
|
$windows.delete window_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def close_all
|
def close_all
|
||||||
@windows.each_key do |window|
|
self.windows.each do |window|
|
||||||
X.kill window
|
X.kill window.window_id
|
||||||
remove window
|
remove window.window_id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hide
|
def hide
|
||||||
@windows.each_key do |window|
|
self.windows.each { |window| X.hide window.window_id }
|
||||||
X.hide window
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@windows.each_key do |window|
|
self.windows.each { |window| X.show window.window_id }
|
||||||
X.show window
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -219,8 +251,12 @@ keybind(26) do |_event|
|
|||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
mousebind(1)
|
keybind(27) do |_event|
|
||||||
mousebind(3)
|
pp $workspaces[:main].windows
|
||||||
|
end
|
||||||
|
|
||||||
|
mousebind 1
|
||||||
|
mousebind 3
|
||||||
|
|
||||||
$mouse_state = -1
|
$mouse_state = -1
|
||||||
$mouse_window = -1
|
$mouse_window = -1
|
||||||
@@ -232,17 +268,18 @@ loop do
|
|||||||
if event[:override_redirect].zero?
|
if event[:override_redirect].zero?
|
||||||
X.subscribe event[:window]
|
X.subscribe event[:window]
|
||||||
X.focus event[:window]
|
X.focus event[:window]
|
||||||
$workspaces[:main].add event[:window]
|
|
||||||
end
|
end
|
||||||
when :close
|
when :close
|
||||||
X.kill event[:window]
|
X.kill event[:window]
|
||||||
$windows[event[:window]].remove event[:window]
|
$windows[event[:window]]&.remove event[:window]
|
||||||
when :enter
|
when :enter
|
||||||
X.focus event[:window]
|
X.focus event[:window]
|
||||||
X.send_to_top event[:window]
|
X.send_to_top event[:window]
|
||||||
when :show
|
when :show
|
||||||
X.show event[:window]
|
X.show event[:window]
|
||||||
X.focus event[:window]
|
X.focus event[:window]
|
||||||
|
$workspaces[:main].add event[:window] if $windows[event[:window]].nil?
|
||||||
|
$workspaces[:main].tiled_root_block.compute_geometry!
|
||||||
when :mouse_press
|
when :mouse_press
|
||||||
next if event[:is_root] != 0
|
next if event[:is_root] != 0
|
||||||
X.send_to_top event[:window]
|
X.send_to_top event[:window]
|
||||||
@@ -252,7 +289,7 @@ loop do
|
|||||||
$geom_start = X.get_geometry event[:window]
|
$geom_start = X.get_geometry event[:window]
|
||||||
$mousebind_actions[event[:btn]]&.call(event)
|
$mousebind_actions[event[:btn]]&.call(event)
|
||||||
when :mouse_drag
|
when :mouse_drag
|
||||||
screen_bounds = X.get_screen # TODO: use monitor
|
screen_bounds = $monitors[:primary]
|
||||||
mouse_pos = X.get_pointer
|
mouse_pos = X.get_pointer
|
||||||
dx = mouse_pos[:x] - $mouse_pos_start[:x]
|
dx = mouse_pos[:x] - $mouse_pos_start[:x]
|
||||||
dy = mouse_pos[:y] - $mouse_pos_start[:y]
|
dy = mouse_pos[:y] - $mouse_pos_start[:y]
|
||||||
|
|||||||
Reference in New Issue
Block a user