Merge branch 'window-unmap-handling'

Handle unmap notify event in the manager, generate :unmanage events
with the client, and forward them to the layout.
This commit is contained in:
Thibault Jouan 2015-04-20 10:24:51 +00:00
commit a15af6f0ab
8 changed files with 132 additions and 15 deletions

View File

@ -0,0 +1,10 @@
Feature: layout client unmanagement
Background:
Given uhwm is running
And a first window is mapped
And a second window is mapped
Scenario: maps another window
When the second window requests to be unmapped
Then the first window must be mapped

View File

@ -1,3 +1,7 @@
Given /^a (\w+) window is mapped$/ do |ident|
x_window_map ident: ident
end
When /^I press the ([^ ]+) keys?$/ do |keys| When /^I press the ([^ ]+) keys?$/ do |keys|
x_key keys x_key keys
end end
@ -6,6 +10,10 @@ When /^a window requests to be mapped$/ do
x_window_map x_window_map
end end
When /^the (\w+) window requests to be unmapped$/ do |ident|
x_window_unmap ident: ident
end
When /^a window requests to be mapped (\d+) times$/ do |times| When /^a window requests to be mapped (\d+) times$/ do |times|
x_window_map times: times.to_i x_window_map times: times.to_i
end end
@ -19,6 +27,10 @@ Then /^the window must be mapped$/ do
expect(x_window_map_state).to eq 'IsViewable' expect(x_window_map_state).to eq 'IsViewable'
end end
Then /^the (\w+) window must be mapped$/ do |ident|
expect(x_window_map_state ident: ident).to eq 'IsViewable'
end
Then /^the window must be focused$/ do Then /^the window must be focused$/ do
expect(x_focused_window_id).to eq x_window_id expect(x_focused_window_id).to eq x_window_id
end end

View File

@ -1,8 +1,8 @@
module Uh module Uh
module WM module WM
class Client class Client
attr_reader :window, :unmap_count attr_reader :window
attr_accessor :geo attr_accessor :geo, :unmap_count
def initialize window, geo = nil def initialize window, geo = nil
@window = window @window = window

View File

@ -61,6 +61,16 @@ module Uh
@events.emit :manage, args: client @events.emit :manage, args: client
end end
def unmap window
return unless client = client_for(window)
if client.unmap_count > 0
client.unmap_count -= 1
else
@clients.delete client
@events.emit :unmanage, args: client
end
end
def handle_next_event def handle_next_event
handle @display.next_event handle @display.next_event
end end
@ -97,6 +107,10 @@ module Uh
map event.window map event.window
end end
def handle_unmap_notify event
unmap event.window
end
def client_for window def client_for window
@clients.find { |e| e.window == window } @clients.find { |e| e.window == window }
end end

View File

@ -115,6 +115,10 @@ module Uh
log "Managing client #{client}" log "Managing client #{client}"
layout << client layout << client
end end
@events.on :unmanage do |client|
log "Unmanaging client #{client}"
layout.remove client
end
end end
def register_keybinds_hooks def register_keybinds_hooks

View File

@ -56,8 +56,9 @@ module Uh
@other_wm @other_wm
end end
def x_client def x_client ident: :default
@x_client ||= XClient.new @x_clients ||= {}
@x_clients[ident] ||= XClient.new(ident)
end end
def x_focused_window_id def x_focused_window_id
@ -81,25 +82,30 @@ module Uh
end.any? end.any?
end end
def x_window_id def x_window_id **options
@x_client.window_id x_client(options).window_id
end end
def x_window_name def x_window_name
@x_client.window_name x_client.window_name
end end
def x_window_map times: 1 def x_window_map times: 1, **options
times.times { x_client.map } times.times { x_client(options).map }
x_client.sync x_client(options).sync
end end
def x_window_map_state def x_window_map_state **options
`xwininfo -id #{x_window_id}`[/Map State: (\w+)/, 1] `xwininfo -id #{x_window_id options}`[/Map State: (\w+)/, 1]
end
def x_window_unmap options
x_client(options).unmap
x_client(options).sync
end end
def x_clients_ensure_stop def x_clients_ensure_stop
@x_client and @x_client.terminate @x_clients and @x_clients.any? and @x_clients.values.each &:terminate
end end
@ -132,8 +138,8 @@ module Uh
class XClient class XClient
attr_reader :name attr_reader :name
def initialize def initialize name = object_id
@name = "#{self.class.name.split('::').last}/#{object_id}" @name = "#{self.class.name.split('::').last}/#{name}"
@geo = Geo.new(0, 0, 640, 480) @geo = Geo.new(0, 0, 640, 480)
@display = Display.new.tap { |o| o.open } @display = Display.new.tap { |o| o.open }
end end
@ -164,6 +170,11 @@ module Uh
window.map window.map
self self
end end
def unmap
window.unmap
self
end
end end
end end
end end

View File

@ -164,6 +164,56 @@ module Uh
end end
end end
describe '#unmap' do
before { manager.map window }
context 'when client unmap count is 0 or less' do
it 'preserves the client unmap count' do
client = manager.clients[0]
expect { manager.unmap window }
.not_to change { client.unmap_count }
end
it 'unregisters the client' do
expect { manager.unmap window }
.to change { manager.clients.size }.from(1).to 0
end
it 'emits :unmanage event with the client' do
events.on :unmanage, &block
expect(block).to receive(:call).with manager.clients[0]
manager.unmap window
end
end
context 'when client unmap count is strictly positive' do
before { manager.clients[0].unmap_count += 1 }
it 'does not unregister the client' do
expect { manager.unmap window }.not_to change { manager.clients }
end
it 'decrements the unmap count' do
manager.unmap window
expect(manager.clients[0].unmap_count).to eq 0
end
end
context 'with unknown window' do
let(:unknown_window) { window.dup }
it 'does not change registered clients' do
expect { manager.unmap unknown_window }
.not_to change { manager.clients }
end
it 'does not emit any event' do
expect(events).not_to receive :emit
manager.unmap unknown_window
end
end
end
describe '#handle_next_event' do describe '#handle_next_event' do
it 'handles the next available event on display' do it 'handles the next available event on display' do
event = double 'event' event = double 'event'
@ -252,6 +302,15 @@ module Uh
manager.handle event manager.handle event
end end
end end
context 'when unmap_notify event is given' do
let(:event) { double 'event', type: :unmap_notify, window: :window }
it 'unmap the event window' do
expect(manager).to receive(:unmap).with :window
manager.handle event
end
end
end end
end end
end end

View File

@ -2,6 +2,7 @@ SomeLayout = Class.new do
define_method(:register) { |*args| } define_method(:register) { |*args| }
define_method(:suggest_geo) { Uh::Geo.new(0, 0, 42, 42) } define_method(:suggest_geo) { Uh::Geo.new(0, 0, 42, 42) }
define_method(:<<) { |*args| } define_method(:<<) { |*args| }
define_method(:remove) { |*args| }
end end
module Uh module Uh
@ -93,6 +94,12 @@ module Uh
expect(env.layout).to receive(:<<).with :client expect(env.layout).to receive(:<<).with :client
runner.events.emit :manage, args: :client runner.events.emit :manage, args: :client
end end
it 'registers for :unmanage event' do
runner.register_event_hooks
expect(env.layout).to receive(:remove).with :client
runner.events.emit :unmanage, args: :client
end
end end
context 'keys hooks' do context 'keys hooks' do