diff --git a/features/actions/quit.feature b/features/actions/quit.feature index 60a2e57..b989fa6 100644 --- a/features/actions/quit.feature +++ b/features/actions/quit.feature @@ -1,6 +1,9 @@ -Feature: quit action +Feature: `quit' action keyword - Scenario: quits on alt+q keys press - Given uhwm is running - When I press the alt+q keys - Then uhwm should terminate successfully + Scenario: requests quit when invoked + Given uhwm is running with this run control file: + """ + key(:f) { quit } + """ + When I press the alt+f keys + Then uhwm must terminate successfully diff --git a/features/run_control/evaluation.feature b/features/run_control/evaluation.feature index e60c40b..32c280c 100644 --- a/features/run_control/evaluation.feature +++ b/features/run_control/evaluation.feature @@ -7,3 +7,12 @@ Feature: run control file evaluation """ When I start uhwm Then the output must contain "run control evaluation" + + Scenario: reports run control code in backtrace on errors + Given a run control file with: + """ + 'no error on first line' + fail 'fails on second line' + """ + When I start uhwm + Then the output must match /\.uhwmrc\.rb:2:.+fails on second line/ diff --git a/features/x/connection.feature b/features/session/connection.feature similarity index 100% rename from features/x/connection.feature rename to features/session/connection.feature diff --git a/features/session/termination.feature b/features/session/termination.feature new file mode 100644 index 0000000..135b056 --- /dev/null +++ b/features/session/termination.feature @@ -0,0 +1,6 @@ +Feature: program termination + + Scenario: terminates when requested to quit + Given uhwm is running + When I tell uhwm to quit + Then uhwm must terminate successfully diff --git a/features/steps/run_steps.rb b/features/steps/run_steps.rb index 5e25604..d72b716 100644 --- a/features/steps/run_steps.rb +++ b/features/steps/run_steps.rb @@ -19,10 +19,14 @@ When /^I run uhwm with options? (-.+)$/ do |options| uhwm_run options end +When /^I tell uhwm to quit$/ do + x_key 'alt+q' +end + Then /^the exit status must be (\d+)$/ do |exit_status| assert_exit_status exit_status.to_i end -Then /^uhwm should terminate successfully$/ do +Then /^uhwm must terminate successfully$/ do assert_exit_status 0 end diff --git a/lib/uh/wm.rb b/lib/uh/wm.rb index fcf448c..2bd157a 100644 --- a/lib/uh/wm.rb +++ b/lib/uh/wm.rb @@ -3,6 +3,7 @@ require 'logger' require 'optparse' require 'uh' +require 'uh/wm/actions_handler' require 'uh/wm/cli' require 'uh/wm/dispatcher' require 'uh/wm/env' diff --git a/lib/uh/wm/actions_handler.rb b/lib/uh/wm/actions_handler.rb new file mode 100644 index 0000000..904a33e --- /dev/null +++ b/lib/uh/wm/actions_handler.rb @@ -0,0 +1,17 @@ +module Uh + module WM + class ActionsHandler + def initialize env, events + @env, @events = env, events + end + + def evaluate code + instance_eval &code + end + + def quit + @events.emit :quit + end + end + end +end diff --git a/lib/uh/wm/env.rb b/lib/uh/wm/env.rb index 5cf8318..ae30fe1 100644 --- a/lib/uh/wm/env.rb +++ b/lib/uh/wm/env.rb @@ -19,7 +19,9 @@ module Uh def initialize output @output = output @rc_path = RC_PATH - @keybinds = {} + @keybinds = { + q: proc { quit } + } end def verbose? diff --git a/lib/uh/wm/run_control.rb b/lib/uh/wm/run_control.rb index 9beb390..7c68a3d 100644 --- a/lib/uh/wm/run_control.rb +++ b/lib/uh/wm/run_control.rb @@ -12,7 +12,7 @@ module Uh def evaluate env rc_path = File.expand_path(env.rc_path) rc = new env - rc.evaluate File.read(rc_path) if File.exist?(rc_path) + rc.evaluate File.read(rc_path), rc_path if File.exist?(rc_path) end end @@ -20,8 +20,8 @@ module Uh @env = env end - def evaluate code - instance_eval code + def evaluate code, path + instance_eval code, path end def key keysym, &block diff --git a/lib/uh/wm/runner.rb b/lib/uh/wm/runner.rb index f81c9a3..8e3c921 100644 --- a/lib/uh/wm/runner.rb +++ b/lib/uh/wm/runner.rb @@ -14,12 +14,13 @@ module Uh extend Forwardable def_delegator :@env, :layout - attr_reader :env, :events, :manager + attr_reader :env, :events, :manager, :actions def initialize env, manager: nil, stopped: false @env = env @events = Dispatcher.new @manager = manager || Manager.new(@events) + @actions = ActionsHandler.new(@env, @events) @stopped = stopped end @@ -36,6 +37,7 @@ module Uh end def register_event_hooks + register_runner_hooks register_manager_hooks register_layout_event_hooks register_key_bindings_hooks @@ -43,7 +45,6 @@ module Uh def connect_manager @manager.connect - @manager.grab_key :q @env.keybinds.each do |keysym, _| @manager.grab_key *keysym end @@ -56,6 +57,10 @@ module Uh private + def register_runner_hooks + @events.on(:quit) { stop! } + end + def register_manager_hooks @events.on(:connecting) do |display| @env.log_debug "Connecting to X server on `#{display}'" @@ -72,9 +77,10 @@ module Uh end def register_key_bindings_hooks - @events.on(:key, :q) { stop! } @env.keybinds.each do |keysym, code| - @events.on :key, *keysym, &code + @events.on :key, *keysym do + @actions.evaluate code + end end end end diff --git a/spec/uh/wm/actions_handler_spec.rb b/spec/uh/wm/actions_handler_spec.rb new file mode 100644 index 0000000..a593c7e --- /dev/null +++ b/spec/uh/wm/actions_handler_spec.rb @@ -0,0 +1,23 @@ +module Uh + module WM + RSpec.describe ActionsHandler do + let(:env) { Env.new(StringIO.new) } + let(:events) { Dispatcher.new } + subject(:actions) { described_class.new env, events } + + describe '#evaluate' do + it 'evaluates given code' do + expect { actions.evaluate proc { throw :action_code } } + .to throw_symbol :action_code + end + end + + describe '#quit' do + it 'emits the quit event' do + expect(events).to receive(:emit).with :quit + actions.quit + end + end + end + end +end diff --git a/spec/uh/wm/env_spec.rb b/spec/uh/wm/env_spec.rb index e5280c6..c357980 100644 --- a/spec/uh/wm/env_spec.rb +++ b/spec/uh/wm/env_spec.rb @@ -21,6 +21,10 @@ module Uh expect(env.layout_class).not_to be end + it 'has defaults key bindings set' do + expect(env.keybinds.keys).to eq %i[q] + end + describe '#verbose?' do context 'when verbose mode is disabled' do before { env.verbose = false } diff --git a/spec/uh/wm/run_control_spec.rb b/spec/uh/wm/run_control_spec.rb index a0f2f97..421ccc3 100644 --- a/spec/uh/wm/run_control_spec.rb +++ b/spec/uh/wm/run_control_spec.rb @@ -23,7 +23,8 @@ module Uh end it 'tells the new instance to evaluate run control file content' do - expect(rc).to receive(:evaluate).with ':run_control_code' + expect(rc) + .to receive(:evaluate).with(':run_control_code', env.rc_path) allow(described_class).to receive(:new) { rc } described_class.evaluate env end @@ -39,12 +40,12 @@ module Uh describe '#evaluate' do it 'evaluates given code' do - expect { rc.evaluate 'throw :run_control_code' } + expect { rc.evaluate 'throw :run_control_code', 'some_path' } .to throw_symbol :run_control_code end it 'provides access to assigned env' do - expect { rc.evaluate 'fail @env.object_id.to_s' } + expect { rc.evaluate 'fail @env.object_id.to_s', 'some_path' } .to raise_error env.object_id.to_s end end diff --git a/spec/uh/wm/runner_spec.rb b/spec/uh/wm/runner_spec.rb index f4ad6b9..007335a 100644 --- a/spec/uh/wm/runner_spec.rb +++ b/spec/uh/wm/runner_spec.rb @@ -63,6 +63,12 @@ module Uh end describe '#register_event_hooks' do + it 'registers quit event hook' do + runner.register_event_hooks + expect(runner).to receive(:stop!) + runner.events.emit :quit + end + it 'registers manager event hooks for logging' do runner.register_event_hooks expect(env).to receive(:log) @@ -86,6 +92,13 @@ module Uh runner.register_event_hooks expect(runner.events[:key, :f, :shift]).not_to be_empty end + + it 'registers key bindings code evaluation with the actions handler' do + env.keybinds[:f] = code = proc { } + runner.register_event_hooks + expect(runner.actions).to receive(:evaluate).with code + runner.events.emit :key, :f + end end describe '#connect_manager' do