diff --git a/features/layout/manage.feature b/features/layout/manage.feature new file mode 100644 index 0000000..d485e57 --- /dev/null +++ b/features/layout/manage.feature @@ -0,0 +1,16 @@ +Feature: layout client management + + Scenario: sends the #<< message when telling the layout to manage a client + Given a file named layout.rb with: + """ + class Layout + def register *_; end + + def << client + puts client + end + end + """ + And uhwm is running with options -v -r./layout -l Layout + When a window requests to be mapped + Then the output must contain the window name diff --git a/features/steps/output_steps.rb b/features/steps/output_steps.rb index e55109e..73cd67f 100644 --- a/features/steps/output_steps.rb +++ b/features/steps/output_steps.rb @@ -27,3 +27,7 @@ end Then /^the output must contain current display$/ do uhwm_wait_output ENV['DISPLAY'] end + +Then /^the output must contain the window name$/ do + uhwm_wait_output x_window_name +end diff --git a/features/steps/run_steps.rb b/features/steps/run_steps.rb index d72b716..790e57d 100644 --- a/features/steps/run_steps.rb +++ b/features/steps/run_steps.rb @@ -6,6 +6,10 @@ Given /^uhwm is running$/ do uhwm_run_wait_ready end +Given /^uhwm is running with options? (-.+)$/ do |options| + uhwm_run_wait_ready options +end + Given /^uhwm is running with this run control file:$/ do |rc| write_file '.uhwmrc.rb', rc uhwm_run_wait_ready diff --git a/features/steps/x_steps.rb b/features/steps/x_steps.rb index c2950e7..e7a1544 100644 --- a/features/steps/x_steps.rb +++ b/features/steps/x_steps.rb @@ -2,6 +2,10 @@ When /^I press the ([^ ]+) keys?$/ do |keys| x_key keys end +When /^a window requests to be mapped$/ do + x_window_map +end + Then /^it must connect to X display$/ do uhwm_wait_output 'Connected to' expect(x_socket_check uhwm_pid).to be true diff --git a/lib/uh/wm.rb b/lib/uh/wm.rb index 2bd157a..d259520 100644 --- a/lib/uh/wm.rb +++ b/lib/uh/wm.rb @@ -5,6 +5,7 @@ require 'uh' require 'uh/wm/actions_handler' require 'uh/wm/cli' +require 'uh/wm/client' require 'uh/wm/dispatcher' require 'uh/wm/env' require 'uh/wm/manager' diff --git a/lib/uh/wm/client.rb b/lib/uh/wm/client.rb new file mode 100644 index 0000000..546288d --- /dev/null +++ b/lib/uh/wm/client.rb @@ -0,0 +1,24 @@ +module Uh + module WM + class Client + attr_reader :window + + def initialize window, geo = nil + @window = window + @geo = geo + end + + def to_s + "<#{wname}> (#{wclass}) #{@geo} win: #{@window}" + end + + def wname + @wname ||= @window.name + end + + def wclass + @wclass ||= @window.wclass + end + end + end +end diff --git a/lib/uh/wm/manager.rb b/lib/uh/wm/manager.rb index 6f8c232..ce163b6 100644 --- a/lib/uh/wm/manager.rb +++ b/lib/uh/wm/manager.rb @@ -1,14 +1,19 @@ module Uh module WM class Manager - INPUT_MASK = Events::SUBSTRUCTURE_REDIRECT_MASK + INPUT_MASK = Events::SUBSTRUCTURE_REDIRECT_MASK + ROOT_MASK = Events::PROPERTY_CHANGE_MASK | + Events::SUBSTRUCTURE_REDIRECT_MASK | + Events::SUBSTRUCTURE_NOTIFY_MASK | + Events::STRUCTURE_NOTIFY_MASK - attr_reader :modifier, :display + attr_reader :modifier, :display, :clients def initialize events, modifier, display = Display.new @events = events @modifier = modifier @display = display + @clients = [] end def connect @@ -20,6 +25,7 @@ module Uh Display.on_error { |*args| handle_error *args } @display.sync false @events.emit :connected, args: @display + @display.root.mask = ROOT_MASK end def disconnect @@ -44,6 +50,9 @@ module Uh [event.key.to_sym, :shift] : event.key.to_sym @events.emit :key, *key_selector + when :map_request + @clients << client = Client.new(event.window) + @events.emit :manage, args: client end end diff --git a/lib/uh/wm/runner.rb b/lib/uh/wm/runner.rb index 1afd7a0..181ecd4 100644 --- a/lib/uh/wm/runner.rb +++ b/lib/uh/wm/runner.rb @@ -84,6 +84,9 @@ module Uh @events.on :connected do |display| layout.register display end + @events.on :manage do |client| + layout << client + end end def register_keybinds_hooks diff --git a/lib/uh/wm/testing/acceptance_helpers.rb b/lib/uh/wm/testing/acceptance_helpers.rb index d28f6fc..27a0bfb 100644 --- a/lib/uh/wm/testing/acceptance_helpers.rb +++ b/lib/uh/wm/testing/acceptance_helpers.rb @@ -66,6 +66,14 @@ module Uh `sockstat -u`.lines.grep /\s+ruby.+\s+#{pid}/ end.any? end + + def x_window_name + 'Event Tester' + end + + def x_window_map + spawn 'xev -event owner_grab_button' + end end end end diff --git a/spec/uh/wm/client_spec.rb b/spec/uh/wm/client_spec.rb new file mode 100644 index 0000000..62cf8ef --- /dev/null +++ b/spec/uh/wm/client_spec.rb @@ -0,0 +1,39 @@ +module Uh + module WM + RSpec.describe Client do + let(:geo) { Geo.new(0, 0, 640, 480) } + let(:window) { double 'window', to_s: 'wid', name: 'wname', wclass: 'wclass' } + subject(:client) { described_class.new window, geo } + + describe '#to_s' do + it 'includes window name' do + expect(client.to_s).to include 'wname' + end + + it 'includes window class' do + expect(client.to_s).to include 'wclass' + end + + it 'includes geo' do + expect(client.to_s).to include geo.to_s + end + + it 'includes window id' do + expect(client.to_s).to include 'wid' + end + end + + describe '#wname' do + it 'returns the window name' do + expect(client.wname).to eq window.name + end + end + + describe '#wclass' do + it 'returns the window class' do + expect(client.wclass).to eq window.wclass + end + end + end + end +end diff --git a/spec/uh/wm/manager_spec.rb b/spec/uh/wm/manager_spec.rb index 1ece020..6fe3a61 100644 --- a/spec/uh/wm/manager_spec.rb +++ b/spec/uh/wm/manager_spec.rb @@ -11,6 +11,10 @@ module Uh expect(manager.display).to be_a Display end + it 'has no clients' do + expect(manager.clients).to be_empty + end + describe '#connect', :xvfb do it 'opens the display' do expect(manager.display).to receive(:open).and_call_original @@ -33,6 +37,14 @@ module Uh manager.connect end + it 'updates the root window mask in order to manage windows' do + manager.connect + expect(display.root.mask).to eq Events::PROPERTY_CHANGE_MASK | + Events::SUBSTRUCTURE_REDIRECT_MASK | + Events::SUBSTRUCTURE_NOTIFY_MASK | + Events::STRUCTURE_NOTIFY_MASK + end + context 'when connection fails' do before { allow(display).to receive(:open) { fail } } @@ -127,6 +139,27 @@ module Uh end end end + + context 'when map_request event is given' do + let(:event) { double 'event', type: :map_request, window: :window } + + it 'registers a new client wrapping the event window' do + manager.handle event + expect(manager.clients[0]) + .to be_a(Client) + .and have_attributes(window: :window) + end + + it 'emits :manage event with the registered client' do + events.on :manage, &block + expect(block).to receive :call do |client| + expect(client) + .to be_a(Client) + .and have_attributes(window: :window) + end + manager.handle event + end + end end end end diff --git a/spec/uh/wm/runner_spec.rb b/spec/uh/wm/runner_spec.rb index fe65c11..978d31f 100644 --- a/spec/uh/wm/runner_spec.rb +++ b/spec/uh/wm/runner_spec.rb @@ -1,5 +1,6 @@ SomeLayout = Class.new do - define_method(:register) { |*args| } + define_method(:register) { |*args| } + define_method(:<<) { |*args| } end module Uh @@ -85,6 +86,12 @@ module Uh runner.events.emit :connected, args: :display end + it 'registers layout hook for :manage event' do + runner.register_event_hooks + expect(env.layout).to receive(:<<).with :window + runner.events.emit :manage, args: :window + end + it 'registers key bindings event hooks' do env.keybinds[:f] = -> { } runner.register_event_hooks