From 18667e2492a507e55f8a2bd90bebfbc9d8ff657b Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Fri, 17 Apr 2015 15:13:18 +0000 Subject: [PATCH 01/10] Implement Manager#to_io --- lib/uh/wm/manager.rb | 4 ++++ spec/uh/wm/manager_spec.rb | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/uh/wm/manager.rb b/lib/uh/wm/manager.rb index 5fb9089..5dc6edb 100644 --- a/lib/uh/wm/manager.rb +++ b/lib/uh/wm/manager.rb @@ -16,6 +16,10 @@ module Uh @clients = [] end + def to_io + IO.new(@display.fileno) + end + def connect @events.emit :connecting, args: @display @display.open diff --git a/spec/uh/wm/manager_spec.rb b/spec/uh/wm/manager_spec.rb index 4c498b3..a18f918 100644 --- a/spec/uh/wm/manager_spec.rb +++ b/spec/uh/wm/manager_spec.rb @@ -15,6 +15,18 @@ module Uh expect(manager.clients).to be_empty end + describe '#to_io', :xvfb do + context 'when connected' do + before { manager.connect } + + it 'returns an IO object wrapping the display file descriptor' do + expect(manager.to_io) + .to be_an(IO) + .and have_attributes(fileno: display.fileno) + end + end + end + describe '#connect', :xvfb do it 'opens the display' do expect(manager.display).to receive(:open).and_call_original From 0164b5246501b9d349da909bc27067f0f92fdf57 Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Fri, 17 Apr 2015 15:14:48 +0000 Subject: [PATCH 02/10] Implement Manager#flush --- lib/uh/wm/manager.rb | 4 ++++ spec/uh/wm/manager_spec.rb | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/lib/uh/wm/manager.rb b/lib/uh/wm/manager.rb index 5dc6edb..1b992b9 100644 --- a/lib/uh/wm/manager.rb +++ b/lib/uh/wm/manager.rb @@ -35,6 +35,10 @@ module Uh @events.emit :disconnected end + def flush + @display.flush + end + def grab_key keysym, mod = nil mod_mask = KEY_MODIFIERS[@modifier] mod_mask |= KEY_MODIFIERS[mod] if mod diff --git a/spec/uh/wm/manager_spec.rb b/spec/uh/wm/manager_spec.rb index a18f918..4cfbe5b 100644 --- a/spec/uh/wm/manager_spec.rb +++ b/spec/uh/wm/manager_spec.rb @@ -82,6 +82,13 @@ module Uh end end + describe '#flush' do + it 'flushes the display' do + expect(display).to receive :flush + manager.flush + end + end + describe '#grab_key' do it 'grabs given key on the display' do expect(manager.display) From 0176ab0010a6fa8fc91b26562bc70ab50c9237c6 Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Fri, 17 Apr 2015 21:25:00 +0000 Subject: [PATCH 03/10] Prototype two workers: blocking and multiplexing --- lib/uh/wm.rb | 4 ++++ lib/uh/wm/workers.rb | 21 +++++++++++++++++++++ lib/uh/wm/workers/base.rb | 31 +++++++++++++++++++++++++++++++ lib/uh/wm/workers/blocking.rb | 15 +++++++++++++++ lib/uh/wm/workers/mux.rb | 18 ++++++++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 lib/uh/wm/workers.rb create mode 100644 lib/uh/wm/workers/base.rb create mode 100644 lib/uh/wm/workers/blocking.rb create mode 100644 lib/uh/wm/workers/mux.rb diff --git a/lib/uh/wm.rb b/lib/uh/wm.rb index c3c6f0c..0614a84 100644 --- a/lib/uh/wm.rb +++ b/lib/uh/wm.rb @@ -12,6 +12,10 @@ require 'uh/wm/logger_formatter' require 'uh/wm/manager' require 'uh/wm/run_control' require 'uh/wm/runner' +require 'uh/wm/workers' +require 'uh/wm/workers/base' +require 'uh/wm/workers/blocking' +require 'uh/wm/workers/mux' module Uh module WM diff --git a/lib/uh/wm/workers.rb b/lib/uh/wm/workers.rb new file mode 100644 index 0000000..c065293 --- /dev/null +++ b/lib/uh/wm/workers.rb @@ -0,0 +1,21 @@ +module Uh + module WM + module Workers + FACTORIES = { + block: ->(options) { Blocking.new(options) }, + mux: ->(options) { Mux.new(options) } + }.freeze + + class << self + def types + FACTORIES.keys + end + + def build type, **options + (FACTORIES[type] or fail ArgumentError, "unknown worker: `#{type}'") + .call options + end + end + end + end +end diff --git a/lib/uh/wm/workers/base.rb b/lib/uh/wm/workers/base.rb new file mode 100644 index 0000000..2c2e63b --- /dev/null +++ b/lib/uh/wm/workers/base.rb @@ -0,0 +1,31 @@ +module Uh + module WM + module Workers + class Base + def initialize **options + @ios = [] + end + + def watch io + @ios << io + end + + def before_wait &block + if block_given? then @before_wait = block else @before_wait end + end + + def on_timeout &block + if block_given? then @on_timeout = block else @on_timeout end + end + + def on_read &block + if block_given? then @on_read = block else @on_read end + end + + def on_read_next &block + if block_given? then @on_read_next = block else @on_read_next end + end + end + end + end +end diff --git a/lib/uh/wm/workers/blocking.rb b/lib/uh/wm/workers/blocking.rb new file mode 100644 index 0000000..d6aefc1 --- /dev/null +++ b/lib/uh/wm/workers/blocking.rb @@ -0,0 +1,15 @@ +module Uh + module WM + module Workers + class Blocking < Base + def work_events + #until yield + # @on_events_read_bang.call + #end + #@on_events_read_bang.call until yield + @on_read_next.call + end + end + end + end +end diff --git a/lib/uh/wm/workers/mux.rb b/lib/uh/wm/workers/mux.rb new file mode 100644 index 0000000..e940f4e --- /dev/null +++ b/lib/uh/wm/workers/mux.rb @@ -0,0 +1,18 @@ +module Uh + module WM + module Workers + class Mux < Base + def initialize timeout: 1 + super + @timeout = timeout + end + + def work_events + @before_wait.call if @before_wait + if res = select(@ios, [], [], @timeout) then @on_read.call res + else @on_timeout.call if @on_timeout end + end + end + end + end +end From cb0c5f6ca28c974f4100dc26aeea181cd356acfa Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Fri, 17 Apr 2015 19:55:23 +0000 Subject: [PATCH 04/10] Add Env#worker attribute accessor --- lib/uh/wm/env.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/uh/wm/env.rb b/lib/uh/wm/env.rb index 2bb63aa..86454fb 100644 --- a/lib/uh/wm/env.rb +++ b/lib/uh/wm/env.rb @@ -20,7 +20,8 @@ module Uh def_delegator :@output, :print attr_reader :output, :keybinds - attr_accessor :verbose, :debug, :rc_path, :layout_class, :modifier + attr_accessor :verbose, :debug, :rc_path, :layout_class, :modifier, + :worker def initialize output @output = output From 231e7cd67a0dce4aa9d4d41e4da592c27933d122 Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Fri, 17 Apr 2015 20:04:07 +0000 Subject: [PATCH 05/10] Configure default worker settings --- lib/uh/wm/env.rb | 6 ++++-- spec/uh/wm/env_spec.rb | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/uh/wm/env.rb b/lib/uh/wm/env.rb index 86454fb..7417647 100644 --- a/lib/uh/wm/env.rb +++ b/lib/uh/wm/env.rb @@ -3,10 +3,11 @@ module Uh class Env RC_PATH = '~/.uhwmrc.rb'.freeze - MODIFIER = :mod1 - KEYBINDS = { + MODIFIER = :mod1 + KEYBINDS = { q: proc { quit } }.freeze + WORKER = :block LOGGER_LEVEL = Logger::WARN LOGGER_LEVEL_VERBOSE = Logger::INFO @@ -28,6 +29,7 @@ module Uh @rc_path = RC_PATH @modifier = MODIFIER @keybinds = KEYBINDS.dup + @worker = :block end def verbose? diff --git a/spec/uh/wm/env_spec.rb b/spec/uh/wm/env_spec.rb index 1d157dd..03336a6 100644 --- a/spec/uh/wm/env_spec.rb +++ b/spec/uh/wm/env_spec.rb @@ -29,6 +29,10 @@ module Uh expect(env.keybinds.keys).to eq %i[q] end + it 'has the blocking worker by default' do + expect(env.worker).to eq :block + end + describe '#verbose?' do context 'when verbose mode is disabled' do before { env.verbose = false } From 887c66a6f7745ded9b28484d77389680853ddc87 Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Fri, 17 Apr 2015 20:07:44 +0000 Subject: [PATCH 06/10] Implement Manager#handle_next_event --- lib/uh/wm/manager.rb | 4 ++++ spec/uh/wm/manager_spec.rb | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/uh/wm/manager.rb b/lib/uh/wm/manager.rb index 1b992b9..d5c6b0b 100644 --- a/lib/uh/wm/manager.rb +++ b/lib/uh/wm/manager.rb @@ -45,6 +45,10 @@ module Uh @display.grab_key keysym.to_s, mod_mask end + def handle_next_event + handle @display.next_event + end + def handle_pending_events handle @display.next_event while @display.pending? end diff --git a/spec/uh/wm/manager_spec.rb b/spec/uh/wm/manager_spec.rb index 4cfbe5b..f45d839 100644 --- a/spec/uh/wm/manager_spec.rb +++ b/spec/uh/wm/manager_spec.rb @@ -106,6 +106,15 @@ module Uh end end + describe '#handle_next_event' do + it 'handles the next available event on display' do + event = double 'event' + allow(display).to receive(:next_event) { event } + expect(manager).to receive(:handle).with(event).once + manager.handle_next_event + end + end + describe '#handle_pending_events' do let(:event) { double 'event' } From fa03cd736a310c1006a026c42ba828aa849f7fb3 Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Fri, 17 Apr 2015 21:23:10 +0000 Subject: [PATCH 07/10] Implement Runner#worker --- lib/uh/wm/runner.rb | 18 ++++++++++++++++++ spec/uh/wm/runner_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/uh/wm/runner.rb b/lib/uh/wm/runner.rb index 2a36744..8bee78b 100644 --- a/lib/uh/wm/runner.rb +++ b/lib/uh/wm/runner.rb @@ -54,6 +54,24 @@ module Uh end end + def worker + @worker ||= Workers.build(*(@env.worker)).tap do |w| + w.on_read do + @env.log_debug 'Processing pending events' + @manager.handle_pending_events + end + w.on_read_next do + @env.log_debug 'Processing next event' + @manager.handle_next_event + end + w.on_timeout do |*args| + @env.log_debug "Worker timeout: #{args.inspect}" + @env.log_debug 'Flushing X output buffer' + @manager.flush + end + end + end + def run_until &block manager.handle_pending_events until block.call end diff --git a/spec/uh/wm/runner_spec.rb b/spec/uh/wm/runner_spec.rb index a2f82aa..863abba 100644 --- a/spec/uh/wm/runner_spec.rb +++ b/spec/uh/wm/runner_spec.rb @@ -122,6 +122,27 @@ module Uh end end + describe '#worker' do + it 'returns a worker' do + expect(runner.worker).to respond_to :work_events + end + + it 'setups the read callback to tell manager to handle pending events' do + expect(runner.manager).to receive :handle_pending_events + runner.worker.on_read.call + end + + it 'setups the read_next callback to tell manager to handle next event' do + expect(runner.manager).to receive :handle_next_event + runner.worker.on_read_next.call + end + + it 'setups the timeout callback to tell manager to flush the output' do + expect(runner.manager).to receive :flush + runner.worker.on_timeout.call + end + end + describe '#run_until' do it 'tells the manager to handle events until given block is true' do block = proc { } From 9ea43ee69aa6e4b7f072e1325741a0d0886339d9 Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Fri, 17 Apr 2015 21:25:43 +0000 Subject: [PATCH 08/10] Integrate blocking and multiplexing workers usage --- README.md | 1 + features/cli/worker.feature | 9 +++++++++ features/steps/output_steps.rb | 1 + lib/uh/wm/cli.rb | 5 +++++ lib/uh/wm/runner.rb | 4 +++- spec/uh/wm/cli_spec.rb | 9 +++++++++ spec/uh/wm/runner_spec.rb | 9 +++++++-- 7 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 features/cli/worker.feature diff --git a/README.md b/README.md index baf3091..436a421 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ options: -f, --run-control PATH specify alternate run control file -r, --require PATH require ruby feature -l, --layout LAYOUT specify layout + -w, --worker WORKER specify worker ``` diff --git a/features/cli/worker.feature b/features/cli/worker.feature new file mode 100644 index 0000000..94162e7 --- /dev/null +++ b/features/cli/worker.feature @@ -0,0 +1,9 @@ +Feature: worker CLI option + + Scenario: uses the blocking worker when `block' is given + When I run uhwm with option -v -w block + Then the output must match /work.+event.+block/i + + Scenario: uses the multiplexing worker when `mux' is given + When I run uhwm with option -v -w mux + Then the output must match /work.+event.+mux/i diff --git a/features/steps/output_steps.rb b/features/steps/output_steps.rb index 73cd67f..875bf1a 100644 --- a/features/steps/output_steps.rb +++ b/features/steps/output_steps.rb @@ -9,6 +9,7 @@ options: -f, --run-control PATH specify alternate run control file -r, --require PATH require ruby feature -l, --layout LAYOUT specify layout + -w, --worker WORKER specify worker eoh end diff --git a/lib/uh/wm/cli.rb b/lib/uh/wm/cli.rb index 8853582..12631d9 100644 --- a/lib/uh/wm/cli.rb +++ b/lib/uh/wm/cli.rb @@ -77,6 +77,11 @@ module Uh opts.on '-l', '--layout LAYOUT', 'specify layout' do |layout| @env.layout_class = self.class.const_get layout.to_sym end + + opts.on '-w', Workers.types, '--worker WORKER', + 'specify worker' do |worker| + @env.worker = worker.to_sym + end end end end diff --git a/lib/uh/wm/runner.rb b/lib/uh/wm/runner.rb index 8bee78b..582869a 100644 --- a/lib/uh/wm/runner.rb +++ b/lib/uh/wm/runner.rb @@ -73,7 +73,9 @@ module Uh end def run_until &block - manager.handle_pending_events until block.call + worker.watch @manager + @env.log "Working events with `#{worker.class}'" + worker.work_events until block.call end def terminate diff --git a/spec/uh/wm/cli_spec.rb b/spec/uh/wm/cli_spec.rb index 6bcf551..027546c 100644 --- a/spec/uh/wm/cli_spec.rb +++ b/spec/uh/wm/cli_spec.rb @@ -176,6 +176,15 @@ module Uh end end + context 'with worker option' do + let(:arguments) { %w[-w mux] } + + it 'assigns the worker type in the env' do + cli.parse_arguments! + expect(cli.env.worker).to eq :mux + end + end + context 'with invalid option' do let(:arguments) { %w[--unknown-option] } diff --git a/spec/uh/wm/runner_spec.rb b/spec/uh/wm/runner_spec.rb index 863abba..bda4bef 100644 --- a/spec/uh/wm/runner_spec.rb +++ b/spec/uh/wm/runner_spec.rb @@ -144,10 +144,15 @@ module Uh end describe '#run_until' do - it 'tells the manager to handle events until given block is true' do + it 'tells the worker to watch the manager' do + expect(runner.worker).to receive(:watch).with runner.manager + runner.run_until { true } + end + + it 'tells the worker to work events until given block is true' do block = proc { } allow(block).to receive(:call).and_return(false, false, false, true) - expect(runner.manager).to receive(:handle_pending_events).exactly(3).times + expect(runner.worker).to receive(:work_events).exactly(3).times runner.run_until &block end end From 5ab463cde56d016b77e9f12eb8760223260a9423 Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Fri, 17 Apr 2015 20:20:18 +0000 Subject: [PATCH 09/10] Allow worker configuration with run control file --- features/run_control/worker.feature | 9 +++++++++ lib/uh/wm/run_control.rb | 4 ++++ spec/uh/wm/run_control_spec.rb | 12 ++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 features/run_control/worker.feature diff --git a/features/run_control/worker.feature b/features/run_control/worker.feature new file mode 100644 index 0000000..a586c5a --- /dev/null +++ b/features/run_control/worker.feature @@ -0,0 +1,9 @@ +Feature: `worker' run control keyword + + Scenario: configures the modifier key + Given a run control file with: + """ + worker :mux + """ + And I start uhwm + Then the output must match /work.+event.+mux/i diff --git a/lib/uh/wm/run_control.rb b/lib/uh/wm/run_control.rb index 2d511be..b3fc618 100644 --- a/lib/uh/wm/run_control.rb +++ b/lib/uh/wm/run_control.rb @@ -32,6 +32,10 @@ module Uh @env.keybinds[translate_keysym keysym] = block end + def worker type, **options + @env.worker = [type, options] + end + private diff --git a/spec/uh/wm/run_control_spec.rb b/spec/uh/wm/run_control_spec.rb index 7684b8e..1afca27 100644 --- a/spec/uh/wm/run_control_spec.rb +++ b/spec/uh/wm/run_control_spec.rb @@ -80,6 +80,18 @@ module Uh expect(env.keybinds.keys).to include %i[f shift] end end + + describe '#worker' do + it 'sets the worker type in the env' do + rc.worker :some_worker + expect(env.worker[0]).to eq :some_worker + end + + it 'sets the worker options in the env' do + rc.worker :some_worker, some: :option + expect(env.worker[1]).to eq({ some: :option }) + end + end end end end From 17de52f8af05c0549fe6b0685edb13fd09190b27 Mon Sep 17 00:00:00 2001 From: Thibault Jouan Date: Sat, 18 Apr 2015 02:02:21 +0000 Subject: [PATCH 10/10] Modify test helpers to quit gracefully in cucumber --- lib/uh/wm/testing/acceptance_helpers.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/uh/wm/testing/acceptance_helpers.rb b/lib/uh/wm/testing/acceptance_helpers.rb index bf5d05b..44612aa 100644 --- a/lib/uh/wm/testing/acceptance_helpers.rb +++ b/lib/uh/wm/testing/acceptance_helpers.rb @@ -11,7 +11,10 @@ module Uh end def uhwm_ensure_stop - @process and @process.terminate + if @process + x_key 'alt+q' + @process.terminate + end end def uhwm_pid