fusuma-plugin-wmctrl 0.4.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8654418db5d6e428e786f5d8632ba00a302a2a4fc9cc78c89cbcdaca5dd12a2
4
- data.tar.gz: d12bb4124ad144c82e3c30302d60a64261e7f612e783a6cb26e50ded61f90d51
3
+ metadata.gz: 5143de56caec58bc113092172aab1b6ad257e245376ff43633fa994adc77a5d8
4
+ data.tar.gz: 9956dd5820be73525c539a6c1dc67435b4562a8bd116cd05d98db22608189518
5
5
  SHA512:
6
- metadata.gz: 902bfc1063f1a23f5a0ad03420998ef7e04b51f0e5e9f31dc1c7674d9f676ec40741311d8442642639a834ec421766d5b47b57ab206ca140470348339692eb51
7
- data.tar.gz: 931711256a8f0a853f35352b018dab803f4fe5cc81637e5796a2d0c5ba30b7274680835d2a026cacef4c2d39db4e174065d7c9f6b526301385b449d1be7ebaac
6
+ metadata.gz: fa40be6a49d1d002916280d2b41d0943cca6f555390f5d9a4dd53821a01ebc2d70984aef90b3546d4fcbe835e613c8b8913ddaeb1f59f4674a078003692e4737
7
+ data.tar.gz: c5d5f213771f20985d2fbffd8ad4d269b3b095abfe6f1ecc874281fc47657f3aabf778155bd2cfb6035db031ccefc9d0b421f47261607f87068ec7afc159d1f7
data/README.md CHANGED
@@ -29,7 +29,7 @@ sudo pacman -S wmctrl
29
29
  This plugin requires [Fusuma](https://github.com/iberianpig/fusuma#update) version 1.0 or later.
30
30
 
31
31
 
32
- **Note For Arch Based Distros:** By default in Arch Linux, when running ```gem```, gems are installed per-user (into ```~/.gem/ruby/```), instead of system-wide (into ```/usr/lib/ruby/gems/```). This is considered the best way to manage gems on Arch, because otherwise they might interfere with gems installed by Pacman. (From Arch Wiki)
32
+ **Note For Arch Based Distros:** By default in Arch Linux, when running `gem`, gems are installed per-user (into `~/.gem/ruby/`), instead of system-wide (into `/usr/lib/ruby/gems/`). This is considered the best way to manage gems on Arch, because otherwise they might interfere with gems installed by Pacman. (From Arch Wiki)
33
33
 
34
34
  To install gems system-wide, see any of the methods listed on [Arch Wiki](https://wiki.archlinux.org/index.php/ruby#Installing_gems_system-wide)
35
35
 
@@ -46,6 +46,7 @@ Values following are available for `workspace`.
46
46
 
47
47
  * `prev` is value to switch current workspace to previous workspace.
48
48
  * `next` is value to switch current workspace to next workspace.
49
+ * [**[For grid-style workspaces only](#support-grid-style-workspace)**] `up` / `down` / `left` / `right` navigate to the workspace in the direction.
49
50
 
50
51
  ### `window:` property
51
52
  Add `window:` property in `~/.config/fusuma/config.yml`.
@@ -54,6 +55,7 @@ Values following are available for `window`.
54
55
 
55
56
  * `prev` is value to move active window to previous workspace.
56
57
  * `next` is value to move active window to next workspace.
58
+ * [**[For grid-style workspaces only](#support-grid-style-workspace)**] `up` / `down` / `left` / `right` move window to the workspace in the direction.
57
59
  * `fullscreen` is value to toggle fullscreen
58
60
  * `fullscreen: toggle` toggles the active window to fullscreen.
59
61
  ```yml
@@ -111,6 +113,8 @@ swipe:
111
113
 
112
114
  ## Configuration
113
115
 
116
+ ### Wrap navigation
117
+
114
118
  The plugin allows to enable (disabled by default) circular navigation between workspaces. To enable it set the following in your config file `~/.config/fusuma/config.yml`.
115
119
 
116
120
  ```yaml
@@ -120,6 +124,56 @@ plugin:
120
124
  wrap-navigation: true
121
125
  ```
122
126
 
127
+ ### Support grid-style workspace
128
+
129
+ For grid-style workspace users, Fusuma has an option to move workspace up, down, left or right.
130
+ To enable this option, set `matrix-col-size`.
131
+
132
+ For example, for a 3x2 workspace, set `matrix-col-size: 3` to wmctrl_executor option.
133
+ ```yaml
134
+ plugin:
135
+ executors:
136
+ wmctrl_executor:
137
+ matrix-col-size: 3
138
+ ```
139
+
140
+ With this setting, the `up`/`down`/`left`/`right` properties will be enabled on `workspace:` and `window:`.
141
+
142
+ #### Example
143
+
144
+ ```yaml
145
+ swipe:
146
+ 4:
147
+ up:
148
+ workspace: down
149
+ keypress:
150
+ LEFTSHIFT:
151
+ window: down
152
+ down:
153
+ workspace: up
154
+ keypress:
155
+ LEFTSHIFT:
156
+ window: up
157
+ left:
158
+ workspace: right
159
+ keypress:
160
+ LEFTSHIFT:
161
+ window: right
162
+ right:
163
+ workspace: left
164
+ keypress:
165
+ LEFTSHIFT:
166
+ window: left
167
+
168
+ plugin:
169
+ executors:
170
+ wmctrl_executor:
171
+ matrix-col-size: 3
172
+ ```
173
+
174
+ NOTE: `keypress:` property is enabled with fusuma-plugin-keypress
175
+ https://github.com/iberianpig/fusuma-plugin-keypress
176
+
123
177
 
124
178
  ## Contributing
125
179
 
data/exe/fusuma-wmctrl ADDED
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'optparse'
5
+ require_relative '../lib/fusuma/plugin/wmctrl/window.rb'
6
+ require_relative '../lib/fusuma/plugin/wmctrl/workspace.rb'
7
+ require_relative '../lib/fusuma/plugin/wmctrl/version.rb'
8
+
9
+ option = {}
10
+ opt = OptionParser.new
11
+
12
+ opt.on('--wrap-navigation',
13
+ 'circular navigation between workspaces') do |v|
14
+ option[:wrap_navigation] = v
15
+ end
16
+
17
+ opt.on('--matrix-col-size=3',
18
+ 'For grid-style workspace, move up, down, left or right.'
19
+ ) do |v|
20
+ option[:matrix_col_size] = v.to_i
21
+ end
22
+
23
+ opt.on('--workspace=prev|next|up|down|left|right',
24
+ 'move workspace') do |v|
25
+ option[:workspace] = v
26
+ end
27
+
28
+ opt.on('--window=prev|next|up|down|left|right',
29
+ 'move window') do |v|
30
+ option[:window] = v
31
+ end
32
+
33
+ opt.on('--version', 'Show version') do |v|
34
+ option[:version] = v
35
+ end
36
+
37
+ opt.parse!(ARGV)
38
+
39
+ if option[:workspace]
40
+ workspace = Fusuma::Plugin::Wmctrl::Workspace.new(
41
+ wrap_navigation: option[:wrap_navigation], matrix_col_size: option[:matrix_col_size]
42
+ )
43
+
44
+ command = case property = option[:workspace]
45
+ when 'prev', 'next'
46
+ workspace.move_command(direction: property)
47
+ when 'left', 'right', 'up', 'down'
48
+ workspace.move_command_for_matrix(direction: property)
49
+ else
50
+ raise "#{property} is invalid key"
51
+ end
52
+ `#{command}`
53
+
54
+ return
55
+ end
56
+
57
+ if option[:window]
58
+ workspace = Fusuma::Plugin::Wmctrl::Workspace.new(
59
+ wrap_navigation: option[:wrap_navigation], matrix_col_size: option[:matrix_col_size]
60
+ )
61
+ window = Fusuma::Plugin::Wmctrl::Window.new
62
+
63
+ command = case property = option[:window]
64
+ when 'prev', 'next'
65
+ workspace.move_window_command(direction: property)
66
+ when 'left', 'right', 'up', 'down'
67
+ workspace.move_window_command_for_matrix(direction: property)
68
+ when 'maximized'
69
+ window.maximized(method: 'toggle')
70
+ when 'close'
71
+ window.close
72
+ when 'fullscreen'
73
+ window.fullscreen(method: 'toggle')
74
+ else
75
+ raise "#{property} is invalid key"
76
+ end
77
+
78
+ `#{command}`
79
+
80
+ return
81
+ end
82
+
83
+ if option[:version]
84
+ puts Fusuma::Plugin::Wmctrl::VERSION
85
+ return
86
+ end
87
+
88
+ param = ARGV.first
89
+
90
+ if param.nil?
91
+ warn 'fusuma-wmctrl require aruguments'
92
+ exit 1
93
+ end
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
24
 
25
- spec.required_ruby_version = '>= 2.3' # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all&section=main
26
- spec.add_dependency 'fusuma', '~> 2.0.0'
25
+ spec.required_ruby_version = '>= 2.5.1' # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all&section=main
26
+ # support bionic (18.04LTS) 2.5.1
27
+ spec.add_dependency 'fusuma', '~> 2.0'
27
28
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'posix/spawn'
4
- require 'singleton'
3
+ require_relative '../wmctrl/window'
4
+ require_relative '../wmctrl/workspace'
5
5
 
6
6
  module Fusuma
7
7
  module Plugin
@@ -16,13 +16,18 @@ module Fusuma
16
16
 
17
17
  def config_param_types
18
18
  {
19
- 'wrap-navigation': [TrueClass, FalseClass]
19
+ 'wrap-navigation': [TrueClass, FalseClass],
20
+ 'matrix-col-size': [Integer]
20
21
  }
21
22
  end
22
23
 
23
24
  def initialize
24
25
  super()
25
- Workspace.configure(wrap_navigation: config_params(:'wrap-navigation'))
26
+ @workspace = Wmctrl::Workspace.new(
27
+ wrap_navigation: config_params(:'wrap-navigation'),
28
+ matrix_col_size: config_params(:'matrix-col-size')
29
+ )
30
+ @window = Wmctrl::Window.new
26
31
  end
27
32
 
28
33
  # execute wmctrl command
@@ -32,7 +37,7 @@ module Fusuma
32
37
  return if search_command(event).nil?
33
38
 
34
39
  MultiLogger.info(wmctrl: search_command(event))
35
- pid = POSIX::Spawn.spawn(search_command(event))
40
+ pid = Process.spawn(search_command(event))
36
41
  Process.detach(pid)
37
42
  end
38
43
 
@@ -50,6 +55,9 @@ module Fusuma
50
55
  # @return [NilClass]
51
56
  def search_command(event)
52
57
  search_workspace_command(event) || search_window_command(event)
58
+ rescue Workspace::InvalidOption => e
59
+ MultiLogger.error(e.message)
60
+ exit 1
53
61
  end
54
62
 
55
63
  private
@@ -60,10 +68,16 @@ module Fusuma
60
68
  def search_workspace_command(event)
61
69
  index = Config::Index.new([*event.record.index.keys, :workspace])
62
70
 
63
- direction = Config.search(index)
64
- return unless direction.is_a?(String)
65
-
66
- Workspace.move_command(direction: direction)
71
+ case property = Config.search(index)
72
+ when 'prev', 'next'
73
+ @workspace.move_command(direction: property)
74
+ when 'left', 'right', 'up', 'down'
75
+ @workspace.move_command_for_matrix(direction: property)
76
+ when nil
77
+ nil
78
+ else
79
+ raise "#{property} is invalid key"
80
+ end
67
81
  end
68
82
 
69
83
  # @param event [Event]
@@ -74,113 +88,25 @@ module Fusuma
74
88
 
75
89
  case property = Config.search(index)
76
90
  when 'prev', 'next'
77
- Window.move_command(direction: property)
91
+ @workspace.move_window_command(direction: property)
92
+ when 'left', 'right', 'up', 'down'
93
+ @workspace.move_window_command_for_matrix(direction: property)
78
94
  when 'fullscreen'
79
- Window.fullscreen(method: 'toggle')
95
+ @window.fullscreen(method: 'toggle')
80
96
  when 'maximized'
81
- Window.maximized(method: 'toggle')
97
+ @window.maximized(method: 'toggle')
82
98
  when 'close'
83
- Window.close
99
+ @window.close
84
100
  when Hash
85
101
  if property[:fullscreen]
86
- Window.fullscreen(method: property[:fullscreen])
102
+ @window.fullscreen(method: property[:fullscreen])
87
103
  elsif property[:maximized]
88
- Window.maximized(method: property[:maximized])
89
- end
90
- end
91
- end
92
-
93
- # Manage workspace
94
- class Workspace
95
- include Singleton
96
-
97
- attr_accessor :wrap_navigation
98
-
99
- class << self
100
- # configure properties of the workspace switcher
101
- # @return [NilClass]
102
- def configure(wrap_navigation:)
103
- instance.wrap_navigation = wrap_navigation
104
- end
105
-
106
- # get next workspace number
107
- # @return [Integer]
108
- def next_workspace_num(step:)
109
- current_workspace_num, total_workspace_num = workspace_values
110
-
111
- next_workspace_num = current_workspace_num + step
112
-
113
- return next_workspace_num unless instance.wrap_navigation
114
-
115
- if next_workspace_num.negative?
116
- next_workspace_num = total_workspace_num - 1
117
- elsif next_workspace_num >= total_workspace_num
118
- next_workspace_num = 0
119
- else
120
- next_workspace_num
121
- end
122
- end
123
-
124
- def move_command(direction:)
125
- workspace_num = case direction
126
- when 'next'
127
- next_workspace_num(step: 1)
128
- when 'prev'
129
- next_workspace_num(step: -1)
130
- else
131
- raise "#{direction} is invalid key"
132
- end
133
- "wmctrl -s #{workspace_num}"
134
- end
135
-
136
- private
137
-
138
- # get current workspace and total workspace numbers
139
- # @return [Integer, Integer]
140
- def workspace_values
141
- wmctrl_output = `wmctrl -d`.split("\n")
142
-
143
- current_line = wmctrl_output.grep(/\*/).first
144
- # NOTE: stderror when failed to get desktop
145
- # `Cannot get current desktop properties. (_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)`
146
- return [0, 1] if current_line.nil?
147
-
148
- current_workspace_num = current_line.chars.first.to_i
149
- total_workspace_num = wmctrl_output.length
150
-
151
- [current_workspace_num, total_workspace_num]
152
- end
153
- end
154
- end
155
-
156
- # Manage Window
157
- class Window
158
- class << self
159
- # @param method [String] "toggle" or "add" or "remove"
160
- def maximized(method:)
161
- "wmctrl -r :ACTIVE: -b #{method},maximized_vert,maximized_horz"
162
- end
163
-
164
- def close
165
- 'wmctrl -c :ACTIVE:'
166
- end
167
-
168
- # @param method [String] "toggle" or "add" or "remove"
169
- def fullscreen(method:)
170
- "wmctrl -r :ACTIVE: -b #{method},fullscreen"
171
- end
172
-
173
- def move_command(direction:)
174
- workspace_num = case direction
175
- when 'next'
176
- Workspace.next_workspace_num(step: 1)
177
- when 'prev'
178
- Workspace.next_workspace_num(step: -1)
179
- else
180
- raise "#{direction} is invalid key"
181
- end
182
- "wmctrl -r :ACTIVE: -t #{workspace_num} ; wmctrl -s #{workspace_num}"
104
+ @window.maximized(method: property[:maximized])
183
105
  end
106
+ when nil
107
+ nil
108
+ else
109
+ raise "#{property} is invalid key"
184
110
  end
185
111
  end
186
112
  end
@@ -3,7 +3,7 @@
3
3
  module Fusuma
4
4
  module Plugin
5
5
  module Wmctrl
6
- VERSION = '0.4.2'
6
+ VERSION = '1.1.0'
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fusuma
4
+ module Plugin
5
+ module Wmctrl
6
+ # Manage Window
7
+ class Window
8
+ # @param method [String] "toggle" or "add" or "remove"
9
+ def maximized(method:)
10
+ "wmctrl -r :ACTIVE: -b #{method},maximized_vert,maximized_horz"
11
+ end
12
+
13
+ def close
14
+ 'wmctrl -c :ACTIVE:'
15
+ end
16
+
17
+ # @param method [String] "toggle" or "add" or "remove"
18
+ def fullscreen(method:)
19
+ "wmctrl -r :ACTIVE: -b #{method},fullscreen"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fusuma
4
+ module Plugin
5
+ module Wmctrl
6
+ # Manage workspace
7
+ class Workspace
8
+ class MissingMatrixOption < StandardError; end
9
+
10
+ def initialize(wrap_navigation: nil, matrix_col_size: nil)
11
+ @wrap_navigation = wrap_navigation
12
+ @matrix_col_size = matrix_col_size
13
+ end
14
+
15
+ # get next workspace number
16
+ # @return [Integer]
17
+ def next_workspace_num(step:)
18
+ current_workspace_num, total_workspace_num = workspace_values
19
+
20
+ next_workspace_num = current_workspace_num + step
21
+
22
+ return next_workspace_num unless @wrap_navigation
23
+
24
+ if next_workspace_num.negative?
25
+ next_workspace_num = total_workspace_num - 1
26
+ elsif next_workspace_num >= total_workspace_num
27
+ next_workspace_num = 0
28
+ else
29
+ next_workspace_num
30
+ end
31
+ next_workspace_num
32
+ end
33
+
34
+ # @return [Array<Integer>]
35
+ def matrix_size(total_workspace_num)
36
+ must_have_matrix_option!
37
+ col_size = @matrix_col_size
38
+ row_size = (total_workspace_num / col_size)
39
+ [row_size.to_i, col_size.to_i]
40
+ end
41
+
42
+ def must_have_matrix_option!
43
+ return if @matrix_col_size
44
+
45
+ # FIXME: move to executor
46
+ warn(<<~ERRRORMESSAGE)
47
+ Please set matrix-col-size to config.yml
48
+
49
+ ```config.yaml
50
+ plugin:
51
+ executors:
52
+ wmctrl_executor:
53
+ matrix-col-size: 2
54
+ ```
55
+ ERRRORMESSAGE
56
+ raise MissingMatrixOption, 'You need to set matrix option to config.yml'
57
+ end
58
+
59
+ # @return [Integer]
60
+ # @raise RuntimeError
61
+ def next_workspace_num_for_matrix(direction:)
62
+ must_have_matrix_option!
63
+ current_workspace_num, total_workspace_num = workspace_values
64
+ row_size, col_size = matrix_size(total_workspace_num)
65
+ x = current_workspace_num % col_size
66
+ y = current_workspace_num / col_size
67
+ case direction
68
+ when 'right'
69
+ if x < col_size - 1
70
+ current_workspace_num + 1
71
+ elsif @wrap_navigation
72
+ current_workspace_num - (col_size - 1)
73
+ else
74
+ current_workspace_num
75
+ end
76
+ when 'left'
77
+ if x.positive?
78
+ current_workspace_num - 1
79
+ elsif @wrap_navigation
80
+ current_workspace_num + (col_size - 1)
81
+ else
82
+ current_workspace_num
83
+ end
84
+ when 'down'
85
+ if y < row_size - 1
86
+ current_workspace_num + col_size
87
+ elsif @wrap_navigation
88
+ (current_workspace_num + col_size) - total_workspace_num
89
+ else
90
+ current_workspace_num
91
+ end
92
+ when 'up'
93
+ if y.positive?
94
+ current_workspace_num - col_size
95
+ elsif @wrap_navigation
96
+ total_workspace_num + (current_workspace_num - col_size)
97
+ else
98
+ current_workspace_num
99
+ end
100
+ else
101
+ raise "#{direction} is invalid key"
102
+ end
103
+ end
104
+
105
+ def move_command(direction:)
106
+ workspace_num = case direction
107
+ when 'next'
108
+ next_workspace_num(step: 1)
109
+ when 'prev'
110
+ next_workspace_num(step: -1)
111
+ else
112
+ raise "#{direction} is invalid key"
113
+ end
114
+ "wmctrl -s #{workspace_num}"
115
+ end
116
+
117
+ def move_command_for_matrix(direction:)
118
+ workspace_num = next_workspace_num_for_matrix(direction: direction)
119
+ "wmctrl -s #{workspace_num}"
120
+ end
121
+
122
+ def move_window_command(direction:)
123
+ workspace_num = case direction
124
+ when 'next'
125
+ next_workspace_num(step: 1)
126
+ when 'prev'
127
+ next_workspace_num(step: -1)
128
+ else
129
+ raise "#{direction} is invalid key"
130
+ end
131
+ "wmctrl -r :ACTIVE: -t #{workspace_num} ; wmctrl -s #{workspace_num}"
132
+ end
133
+
134
+ def move_window_command_for_matrix(direction:)
135
+ workspace_num = next_workspace_num_for_matrix(direction: direction)
136
+ "wmctrl -r :ACTIVE: -t #{workspace_num} ; wmctrl -s #{workspace_num}"
137
+ end
138
+
139
+ private
140
+
141
+ # get current workspace and total workspace numbers
142
+ # @return [Integer, Integer]
143
+ def workspace_values
144
+ wmctrl_output = `wmctrl -d`.split("\n")
145
+
146
+ current_line = wmctrl_output.grep(/\*/).first
147
+ # NOTE: stderror when failed to get desktop
148
+ # `Cannot get current desktop properties. \
149
+ # (_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)`
150
+ return [0, 1] if current_line.nil?
151
+
152
+ current_workspace_num = current_line.chars.first.to_i
153
+ total_workspace_num = wmctrl_output.length
154
+
155
+ [current_workspace_num, total_workspace_num]
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -13,15 +13,16 @@ module Fusuma
13
13
  module Executors
14
14
  RSpec.describe WmctrlExecutor do
15
15
  before do
16
+ @workspace = double(Wmctrl::Workspace)
17
+ allow(Wmctrl::Workspace)
18
+ .to receive(:new)
19
+ .and_return(@workspace)
20
+
16
21
  index = Config::Index.new([:dummy, 1, :direction])
17
22
  record = Events::Records::IndexRecord.new(index: index)
18
23
  @event = Events::Event.new(tag: 'dummy_detector', record: record)
19
24
  @executor = WmctrlExecutor.new
20
-
21
- @default_workspace_num = 1
22
- allow(WmctrlExecutor::Workspace)
23
- .to receive(:workspace_values)
24
- .and_return(@default_workspace_num)
25
+ allow(@executor).to receive(:search_command).and_return 'dummy command'
25
26
  end
26
27
 
27
28
  around do |example|
@@ -40,9 +41,8 @@ module Fusuma
40
41
  describe '#execute' do
41
42
  it 'detach' do
42
43
  pid = rand(20)
43
- allow(POSIX::Spawn)
44
- .to receive(:spawn).with(@executor.search_command(@event))
45
- .and_return pid
44
+ allow(Process).to receive(:spawn).with(@executor.search_command(@event))
45
+ .and_return pid
46
46
 
47
47
  expect(Process).to receive(:detach).with(pid)
48
48
 
@@ -64,6 +64,15 @@ module Fusuma
64
64
  end
65
65
 
66
66
  describe '#search_command' do
67
+ before do
68
+ allow(@executor).to receive(:search_command).and_call_original
69
+
70
+ @current_workspace = 1
71
+ @total_workspaces = 3
72
+ allow(@workspace)
73
+ .to receive(:workspace_values)
74
+ .and_return([@current_workspace, @total_workspaces])
75
+ end
67
76
  context "when workspace: 'prev'" do
68
77
  around do |example|
69
78
  ConfigHelper.load_config_yml = <<~CONFIG
@@ -79,28 +88,8 @@ module Fusuma
79
88
  end
80
89
 
81
90
  it 'should return wmctrl command' do
82
- expect(@executor.search_command(@event))
83
- .to match(/wmctrl -s #{@default_workspace_num - 1}/)
84
- end
85
- end
86
-
87
- context "when workspace: 'next'" do
88
- around do |example|
89
- ConfigHelper.load_config_yml = <<~CONFIG
90
- dummy:
91
- 1:
92
- direction:
93
- workspace: 'next'
94
- CONFIG
95
-
96
- example.run
97
-
98
- Config.custom_path = nil
99
- end
100
-
101
- it 'should return wmctrl command' do
102
- expect(@executor.search_command(@event))
103
- .to match(/wmctrl -s #{@default_workspace_num + 1}/)
91
+ expect(@workspace).to receive(:move_command).with(direction: 'prev')
92
+ @executor.search_command(@event)
104
93
  end
105
94
  end
106
95
 
@@ -119,32 +108,8 @@ module Fusuma
119
108
  end
120
109
 
121
110
  it 'should return wmctrl command' do
122
- expect(@executor.search_command(@event))
123
- .to match(/wmctrl -r :ACTIVE: -t #{@default_workspace_num - 1}/)
124
- expect(@executor.search_command(@event))
125
- .to match(/wmctrl -s #{@default_workspace_num - 1}/)
126
- end
127
- end
128
-
129
- context "when window: 'next'" do
130
- around do |example|
131
- ConfigHelper.load_config_yml = <<~CONFIG
132
- dummy:
133
- 1:
134
- direction:
135
- window: 'next'
136
- CONFIG
137
-
138
- example.run
139
-
140
- Config.custom_path = nil
141
- end
142
-
143
- it 'should return wmctrl command' do
144
- expect(@executor.search_command(@event))
145
- .to match(/wmctrl -r :ACTIVE: -t #{@default_workspace_num + 1}/)
146
- expect(@executor.search_command(@event))
147
- .to match(/wmctrl -s #{@default_workspace_num + 1}/)
111
+ expect(@workspace).to receive(:move_window_command).with(direction: 'prev')
112
+ @executor.search_command(@event)
148
113
  end
149
114
  end
150
115
 
@@ -338,112 +303,40 @@ module Fusuma
338
303
  end
339
304
 
340
305
  describe 'wrap_navigation: true' do
341
- context "when workspace: 'prev' and current workspace 0" do
342
- around do |example|
343
- ConfigHelper.load_config_yml = <<~CONFIG
344
- dummy:
345
- 1:
346
- direction:
347
- workspace: 'prev'
348
- plugin:
349
- executors:
350
- wmctrl_executor:
351
- wrap-navigation: true
352
- CONFIG
353
-
354
- example.run
355
-
356
- Config.custom_path = nil
357
- end
358
-
359
- it 'should return wmctrl command with an index last workspace' do
360
- current_workspace = 0
361
- total_workspaces = 3
362
-
363
- allow(WmctrlExecutor::Workspace)
364
- .to receive(:workspace_values)
365
- .and_return([current_workspace, total_workspaces])
366
-
367
- expect(@executor.search_command(@event))
368
- .to match(/wmctrl -s #{total_workspaces - 1}/)
369
- end
370
- end
371
-
372
- context "when workspace: 'next' and current workspace = total - 1" do
373
- around do |example|
374
- ConfigHelper.load_config_yml = <<~CONFIG
375
- dummy:
376
- 1:
377
- direction:
378
- workspace: 'next'
379
- plugin:
380
- executors:
381
- wmctrl_executor:
382
- wrap-navigation: true
383
- CONFIG
384
-
385
- example.run
386
-
387
- Config.custom_path = nil
388
- end
389
-
390
- it 'should return wmctrl command with an index of first workspace' do
391
- current_workspace = 3
392
- total_workspaces = 4
306
+ around do |example|
307
+ ConfigHelper.load_config_yml = <<~CONFIG
308
+ plugin:
309
+ executors:
310
+ wmctrl_executor:
311
+ wrap-navigation: true
312
+ CONFIG
393
313
 
394
- allow(WmctrlExecutor::Workspace)
395
- .to receive(:workspace_values)
396
- .and_return([current_workspace, total_workspaces])
314
+ example.run
397
315
 
398
- expect(@executor.search_command(@event))
399
- .to match(/wmctrl -s 0/)
400
- end
316
+ Config.custom_path = nil
401
317
  end
402
318
 
403
- context "when window: 'prev' and current workspace has index 0" do
404
- around do |example|
405
- ConfigHelper.load_config_yml = <<~CONFIG
406
- dummy:
407
- 1:
408
- direction:
409
- window: 'prev'
410
- plugin:
411
- executors:
412
- wmctrl_executor:
413
- wrap-navigation: true
414
- CONFIG
415
-
416
- example.run
417
-
418
- Config.custom_path = nil
419
- end
420
-
421
- it 'should return wmctrl command with index of last workspace' do
422
- current_workspace = 0
423
- total_workspaces = 5
424
-
425
- allow(WmctrlExecutor::Workspace)
426
- .to receive(:workspace_values)
427
- .and_return([current_workspace, total_workspaces])
428
-
429
- expect(@executor.search_command(@event))
430
- .to match(/wmctrl -r :ACTIVE: -t #{total_workspaces - 1}/)
431
- expect(@executor.search_command(@event))
432
- .to match(/wmctrl -s #{total_workspaces - 1}/)
433
- end
319
+ it 'should wrap-navigation mode' do
320
+ expect(Wmctrl::Workspace).to receive(:new).with(
321
+ wrap_navigation: true,
322
+ matrix_col_size: nil
323
+ )
324
+ WmctrlExecutor.new
434
325
  end
326
+ end
435
327
 
436
- context "when window: 'next' and current workspace is last" do
328
+ describe 'matrix-col-size' do
329
+ context "with matrix-col-size: '3', right" do
437
330
  around do |example|
438
331
  ConfigHelper.load_config_yml = <<~CONFIG
439
332
  dummy:
440
333
  1:
441
334
  direction:
442
- window: 'next'
335
+ window: 'right'
443
336
  plugin:
444
337
  executors:
445
338
  wmctrl_executor:
446
- wrap-navigation: true
339
+ matrix-col-size: 3
447
340
  CONFIG
448
341
 
449
342
  example.run
@@ -451,18 +344,10 @@ module Fusuma
451
344
  Config.custom_path = nil
452
345
  end
453
346
 
454
- it 'should return wmctrl command with index of first workspace' do
455
- current_workspace = 4
456
- total_workspaces = 5
457
-
458
- allow(WmctrlExecutor::Workspace)
459
- .to receive(:workspace_values)
460
- .and_return([current_workspace, total_workspaces])
461
-
462
- expect(@executor.search_command(@event))
463
- .to match(/wmctrl -r :ACTIVE: -t 0/)
464
- expect(@executor.search_command(@event))
465
- .to match(/wmctrl -s 0/)
347
+ it 'should return wmctrl command with index of right(next) workspace' do
348
+ expect(@workspace).to receive(:move_window_command_for_matrix)
349
+ .with(direction: 'right')
350
+ @executor.search_command(@event)
466
351
  end
467
352
  end
468
353
  end
@@ -0,0 +1,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require './lib/fusuma/plugin/wmctrl/workspace'
5
+
6
+ module Fusuma
7
+ module Plugin
8
+ module Wmctrl
9
+ RSpec.describe Workspace do
10
+ def stub_workspace_values(current:, total:)
11
+ allow(@workspace).to receive(:workspace_values).and_return([current, total])
12
+ end
13
+
14
+ before { @workspace = Workspace.new }
15
+
16
+ describe '#move_command' do
17
+ before { stub_workspace_values(current: 1, total: 3) }
18
+
19
+ context "with 'direction: next'" do
20
+ before { @direction = 'next' }
21
+ it 'returns wmctrl command to move NEXT workspace' do
22
+ expect(@workspace.move_command(direction: @direction))
23
+ .to match(/wmctrl -s 2/)
24
+ end
25
+ it 'calls next_workspace_num' do
26
+ expect(@workspace).to receive(:next_workspace_num).with(step: 1)
27
+ @workspace.move_command(direction: @direction)
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#move_command_for_matrix' do
33
+ before do
34
+ @workspace = Workspace.new(matrix_col_size: 3)
35
+ stub_workspace_values(current: 0, total: 9)
36
+ end
37
+
38
+ context "with direction: 'right'" do
39
+ before { @direction = 'right' }
40
+
41
+ it 'returns wmctrl command to move RIGHT workspace' do
42
+ expect(@workspace.move_command_for_matrix(direction: @direction))
43
+ .to match(/wmctrl -s 1/)
44
+ end
45
+ it 'calls next_workspace_num_for_matrix' do
46
+ expect(@workspace).to receive(:next_workspace_num_for_matrix)
47
+ .with(direction: @direction)
48
+ @workspace.move_command_for_matrix(direction: @direction)
49
+ end
50
+ end
51
+ context "with direction: 'down'" do
52
+ before { @direction = 'down' }
53
+
54
+ it 'returns wmctrl command to move workspace to DOWN' do
55
+ expect(@workspace.move_command_for_matrix(direction: @direction))
56
+ .to match(/wmctrl -s 3/)
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#move_window_command' do
62
+ before { stub_workspace_values(current: 1, total: 3) }
63
+
64
+ context "with direction: 'next'" do
65
+ before { @direction = 'next' }
66
+
67
+ it 'returns wmctrl command to move NEXT workspace' do
68
+ wmctrl_move_window = /wmctrl -r :ACTIVE: -t 2/
69
+ wmctrl_move_workspace = /wmctrl -s 2/
70
+ expect(@workspace.move_window_command(direction: @direction))
71
+ .to match(wmctrl_move_window)
72
+ expect(@workspace.move_window_command(direction: @direction))
73
+ .to match(wmctrl_move_workspace)
74
+ end
75
+ end
76
+
77
+ context "with direction: 'prev'" do
78
+ before { @direction = 'prev' }
79
+
80
+ it 'returns wmctrl command to move NEXT workspace' do
81
+ wmctrl_move_window = /wmctrl -r :ACTIVE: -t 0/
82
+ wmctrl_move_workspace = /wmctrl -s 0/
83
+ expect(@workspace.move_window_command(direction: @direction))
84
+ .to match(wmctrl_move_window)
85
+ expect(@workspace.move_window_command(direction: @direction))
86
+ .to match(wmctrl_move_workspace)
87
+ end
88
+ end
89
+ end
90
+
91
+ describe '#move_window_command_for_matrix' do
92
+ before do
93
+ @workspace = Workspace.new(matrix_col_size: 3)
94
+ stub_workspace_values(current: 0, total: 9)
95
+ end
96
+
97
+ context "with direction: 'right'" do
98
+ before { @direction = 'right' }
99
+
100
+ it 'returns wmctrl command to move NEXT workspace' do
101
+ wmctrl_move_window = /wmctrl -r :ACTIVE: -t 1/
102
+ wmctrl_move_workspace = /wmctrl -s 1/
103
+ expect(@workspace.move_window_command_for_matrix(direction: @direction))
104
+ .to match(wmctrl_move_window)
105
+ expect(@workspace.move_window_command_for_matrix(direction: @direction))
106
+ .to match(wmctrl_move_workspace)
107
+ end
108
+ end
109
+ context "with direction: 'down'" do
110
+ before { @direction = 'down' }
111
+
112
+ it 'returns wmctrl command to move NEXT workspace' do
113
+ wmctrl_move_window = /wmctrl -r :ACTIVE: -t 3/
114
+ wmctrl_move_workspace = /wmctrl -s 3/
115
+ expect(@workspace.move_window_command_for_matrix(direction: @direction))
116
+ .to match(wmctrl_move_window)
117
+ expect(@workspace.move_window_command_for_matrix(direction: @direction))
118
+ .to match(wmctrl_move_workspace)
119
+ end
120
+ end
121
+ end
122
+
123
+ describe '#next_workspace_num' do
124
+ before { stub_workspace_values(current: 1, total: 3) }
125
+ context 'with step: 1' do
126
+ before { @step = 1 }
127
+ it { expect(@workspace.next_workspace_num(step: @step)).to eq 1 + @step }
128
+ end
129
+ end
130
+
131
+ describe '#next_workspace_num_for_matrix' do
132
+ context 'without matrix option' do
133
+ before { @workspace = Workspace.new(matrix_col_size: nil) }
134
+
135
+ it 'raises InvalidOption' do
136
+ expect do
137
+ @workspace.next_workspace_num_for_matrix(direction: 'prev')
138
+ end.to raise_error(Wmctrl::Workspace::MissingMatrixOption)
139
+ end
140
+ end
141
+
142
+ context 'with matrix_col_size: 3' do
143
+ # +---+---+---+
144
+ # | 0 | 1 | 2 |
145
+ # +---+---+---+
146
+ # | 3 | 4 | 5 |
147
+ # +---+---+---+
148
+ # | 6 | 7 | 8 |
149
+ # +---+---+---+
150
+ before do
151
+ @workspace = Workspace.new(matrix_col_size: 3)
152
+ stub_workspace_values(current: 1, total: 9)
153
+ end
154
+
155
+ context 'with invalid direction' do
156
+ before { @direction = 'foo' }
157
+ it 'raises InvalidOption' do
158
+ expect do
159
+ @workspace.next_workspace_num_for_matrix(direction: @direction)
160
+ end.to raise_error(RuntimeError, "#{@direction} is invalid key")
161
+ end
162
+ end
163
+
164
+ context 'with direction: right' do
165
+ before { @direction = 'right' }
166
+ it 'next workspace' do
167
+ expect(
168
+ @workspace.next_workspace_num_for_matrix(direction: @direction)
169
+ ).to eq 2
170
+ end
171
+ context 'when current_workspace is right edge' do
172
+ before { stub_workspace_values(current: 2, total: 9) }
173
+ it 'same workspace' do
174
+ expect(
175
+ @workspace.next_workspace_num_for_matrix(direction: @direction)
176
+ ).to eq 2
177
+ end
178
+
179
+ context 'with wrap_navigation: true' do
180
+ before { @workspace.instance_variable_set(:@wrap_navigation, true) }
181
+ it 'same workspace' do
182
+ expect(
183
+ @workspace.next_workspace_num_for_matrix(direction: @direction)
184
+ ).to eq 0
185
+ end
186
+ end
187
+ end
188
+ end
189
+ context 'with direction: down' do
190
+ before { @direction = 'down' }
191
+ it 'next workspace' do
192
+ expect(
193
+ @workspace.next_workspace_num_for_matrix(direction: @direction)
194
+ ).to eq 4
195
+ end
196
+ context 'when current_workspace is bottom' do
197
+ before { stub_workspace_values(current: 7, total: 9) }
198
+ it 'same workspace' do
199
+ expect(
200
+ @workspace.next_workspace_num_for_matrix(direction: @direction)
201
+ ).to eq 7
202
+ end
203
+
204
+ context 'with wrap_navigation: true' do
205
+ before { @workspace.instance_variable_set(:@wrap_navigation, true) }
206
+ it 'same workspace' do
207
+ expect(
208
+ @workspace.next_workspace_num_for_matrix(direction: @direction)
209
+ ).to eq 1
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ describe '#matrix_size' do
218
+ context 'with matrix_col_size' do
219
+ before do
220
+ @workspace = Workspace.new(matrix_col_size: 3)
221
+ stub_workspace_values(current: 1, total: 3)
222
+ end
223
+ it { expect(@workspace.matrix_size(3)).to eq [1, 3] }
224
+ end
225
+ context 'with matrix_col_size' do
226
+ before do
227
+ @workspace = Workspace.new(matrix_col_size: nil)
228
+ stub_workspace_values(current: 1, total: 3)
229
+ end
230
+ it { expect { @workspace.matrix_size(3) }.to raise_error Workspace::MissingMatrixOption }
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fusuma-plugin-wmctrl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - iberianpig
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-19 00:00:00.000000000 Z
11
+ date: 2021-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fusuma
@@ -16,18 +16,19 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.0
19
+ version: '2.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.0
26
+ version: '2.0'
27
27
  description: fusuma-plugin-wmctrl is Fusuma plugin for window manager.
28
28
  email:
29
29
  - yhkyky@gmail.com
30
- executables: []
30
+ executables:
31
+ - fusuma-wmctrl
31
32
  extensions: []
32
33
  extra_rdoc_files: []
33
34
  files:
@@ -35,11 +36,15 @@ files:
35
36
  - README.md
36
37
  - bin/console
37
38
  - bin/setup
39
+ - exe/fusuma-wmctrl
38
40
  - fusuma-plugin-wmctrl.gemspec
39
41
  - lib/fusuma/plugin/executors/wmctrl_executor.rb
40
42
  - lib/fusuma/plugin/wmctrl.rb
41
43
  - lib/fusuma/plugin/wmctrl/version.rb
44
+ - lib/fusuma/plugin/wmctrl/window.rb
45
+ - lib/fusuma/plugin/wmctrl/workspace.rb
42
46
  - spec/fusuma/plugin/plugin/executors/wmctrl_executor_spec.rb
47
+ - spec/fusuma/plugin/plugin/wmctrl/workspace_spec.rb
43
48
  - spec/fusuma/plugin/wmctrl_spec.rb
44
49
  - spec/helpers/config_helper.rb
45
50
  - spec/spec_helper.rb
@@ -55,19 +60,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
55
60
  requirements:
56
61
  - - ">="
57
62
  - !ruby/object:Gem::Version
58
- version: '2.3'
63
+ version: 2.5.1
59
64
  required_rubygems_version: !ruby/object:Gem::Requirement
60
65
  requirements:
61
66
  - - ">="
62
67
  - !ruby/object:Gem::Version
63
68
  version: '0'
64
69
  requirements: []
65
- rubygems_version: 3.1.4
70
+ rubygems_version: 3.0.3.1
66
71
  signing_key:
67
72
  specification_version: 4
68
73
  summary: Wmctrl plugin for Fusuma
69
74
  test_files:
70
- - spec/fusuma/plugin/plugin/executors/wmctrl_executor_spec.rb
71
- - spec/fusuma/plugin/wmctrl_spec.rb
72
75
  - spec/helpers/config_helper.rb
73
76
  - spec/spec_helper.rb
77
+ - spec/fusuma/plugin/wmctrl_spec.rb
78
+ - spec/fusuma/plugin/plugin/executors/wmctrl_executor_spec.rb
79
+ - spec/fusuma/plugin/plugin/wmctrl/workspace_spec.rb