Merge branch 'workers'

This commit is contained in:
Thibault Jouan 2015-04-18 02:27:35 +00:00
commit c21600f500
20 changed files with 242 additions and 7 deletions

View File

@ -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
```

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -7,6 +7,7 @@ module Uh
KEYBINDS = {
q: proc { quit }
}.freeze
WORKER = :block
LOGGER_LEVEL = Logger::WARN
LOGGER_LEVEL_VERBOSE = Logger::INFO
@ -20,13 +21,15 @@ 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
@rc_path = RC_PATH
@modifier = MODIFIER
@keybinds = KEYBINDS.dup
@worker = :block
end
def verbose?

View File

@ -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
@ -31,12 +35,20 @@ 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
@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

View File

@ -32,6 +32,10 @@ module Uh
@env.keybinds[translate_keysym keysym] = block
end
def worker type, **options
@env.worker = [type, options]
end
private

View File

@ -54,8 +54,28 @@ 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
worker.watch @manager
@env.log "Working events with `#{worker.class}'"
worker.work_events until block.call
end
def terminate

View File

@ -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

21
lib/uh/wm/workers.rb Normal file
View File

@ -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

31
lib/uh/wm/workers/base.rb Normal file
View File

@ -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

View File

@ -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

18
lib/uh/wm/workers/mux.rb Normal file
View File

@ -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

View File

@ -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] }

View File

@ -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 }

View File

@ -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
@ -70,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)
@ -87,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' }

View File

@ -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

View File

@ -122,11 +122,37 @@ 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
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