fusuma-plugin-wmctrl 0.4.0.pre2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +55 -1
- data/fusuma-plugin-wmctrl.gemspec +5 -6
- data/lib/fusuma/plugin/executors/wmctrl_executor.rb +37 -110
- data/lib/fusuma/plugin/wmctrl/version.rb +1 -1
- data/lib/fusuma/plugin/wmctrl/window.rb +18 -0
- data/lib/fusuma/plugin/wmctrl/workspace.rb +153 -0
- data/spec/fusuma/plugin/plugin/executors/wmctrl_executor_spec.rb +358 -0
- data/spec/fusuma/plugin/plugin/wmctrl/workspace_spec.rb +236 -0
- data/spec/fusuma/plugin/wmctrl_spec.rb +9 -0
- data/spec/helpers/config_helper.rb +16 -0
- data/spec/spec_helper.rb +18 -0
- metadata +20 -17
- data/.gitignore +0 -12
- data/.rspec +0 -3
- data/.rubocop.yml +0 -15
- data/.rubocop_todo.yml +0 -20
- data/.travis.yml +0 -10
- data/CHANGELOG.md +0 -33
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -17
- data/Rakefile +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 260a70ddbdd31218d0e43a7c354482b44d1cc17aaf7ece9fe3800ce7bf156c72
|
4
|
+
data.tar.gz: 0a15ae0fbe6c080fccc5e460400a3b77e062079e00f0a90dabdfb43d3c5d4dd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e07ea3be6ceed205615ba3f22e57519b3dfad5e3fba29c099fffc35afa542bb33eef7c2143962cd1c987127b13472780c45f99f03d3f07e5e0ce9afda6e63178
|
7
|
+
data.tar.gz: a0fa6b1ba117356a3062a30e11c2f3969a2382c4adbd2b0327c03dbb404f89d9ed71d4564dbde8588d798b5297af46ad429efa7f64517ec807bbc5e57babf80b
|
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
|
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
|
|
@@ -16,14 +16,13 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.license = 'MIT'
|
17
17
|
|
18
18
|
# Specify which files should be added to the gem when it is released.
|
19
|
-
|
20
|
-
spec.
|
21
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
-
end
|
19
|
+
spec.files = Dir['{bin,lib,exe}/**/*', 'LICENSE*', 'README*', '*.gemspec']
|
20
|
+
spec.test_files = Dir['{test,spec,features}/**/*']
|
23
21
|
spec.bindir = 'exe'
|
24
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
23
|
spec.require_paths = ['lib']
|
26
24
|
|
27
|
-
spec.required_ruby_version = '>= 2.
|
28
|
-
|
25
|
+
spec.required_ruby_version = '>= 2.5.1' # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all§ion=main
|
26
|
+
# support bionic (18.04LTS) 2.5.1
|
27
|
+
spec.add_dependency 'fusuma', '~> 2.0.0'
|
29
28
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative '../wmctrl/window'
|
4
|
+
require_relative '../wmctrl/workspace'
|
5
5
|
|
6
6
|
module Fusuma
|
7
7
|
module Plugin
|
@@ -11,17 +11,23 @@ module Fusuma
|
|
11
11
|
# executor properties on config.yml
|
12
12
|
# @return [Array<Symbol>]
|
13
13
|
def execute_keys
|
14
|
-
%i[
|
14
|
+
%i[workspace window]
|
15
15
|
end
|
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()
|
26
|
+
@workspace = Workspace.new(
|
27
|
+
wrap_navigation: config_params(:'wrap-navigation'),
|
28
|
+
matrix_col_size: config_params(:'matrix-col-size')
|
29
|
+
)
|
30
|
+
@window = Window.new
|
25
31
|
end
|
26
32
|
|
27
33
|
# execute wmctrl command
|
@@ -31,7 +37,7 @@ module Fusuma
|
|
31
37
|
return if search_command(event).nil?
|
32
38
|
|
33
39
|
MultiLogger.info(wmctrl: search_command(event))
|
34
|
-
pid =
|
40
|
+
pid = Process.spawn(search_command(event))
|
35
41
|
Process.detach(pid)
|
36
42
|
end
|
37
43
|
|
@@ -49,6 +55,9 @@ module Fusuma
|
|
49
55
|
# @return [NilClass]
|
50
56
|
def search_command(event)
|
51
57
|
search_workspace_command(event) || search_window_command(event)
|
58
|
+
rescue Workspace::InvalidOption => e
|
59
|
+
MultiLogger.error(e.message)
|
60
|
+
exit 1
|
52
61
|
end
|
53
62
|
|
54
63
|
private
|
@@ -59,10 +68,16 @@ module Fusuma
|
|
59
68
|
def search_workspace_command(event)
|
60
69
|
index = Config::Index.new([*event.record.index.keys, :workspace])
|
61
70
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
66
81
|
end
|
67
82
|
|
68
83
|
# @param event [Event]
|
@@ -73,113 +88,25 @@ module Fusuma
|
|
73
88
|
|
74
89
|
case property = Config.search(index)
|
75
90
|
when 'prev', 'next'
|
76
|
-
|
91
|
+
@workspace.move_window_command(direction: property)
|
92
|
+
when 'left', 'right', 'up', 'down'
|
93
|
+
@workspace.move_window_command_for_matrix(direction: property)
|
77
94
|
when 'fullscreen'
|
78
|
-
|
95
|
+
@window.fullscreen(method: 'toggle')
|
79
96
|
when 'maximized'
|
80
|
-
|
97
|
+
@window.maximized(method: 'toggle')
|
81
98
|
when 'close'
|
82
|
-
|
99
|
+
@window.close
|
83
100
|
when Hash
|
84
101
|
if property[:fullscreen]
|
85
|
-
|
102
|
+
@window.fullscreen(method: property[:fullscreen])
|
86
103
|
elsif property[:maximized]
|
87
|
-
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Manage workspace
|
93
|
-
class Workspace
|
94
|
-
include Singleton
|
95
|
-
|
96
|
-
attr_accessor :wrap_navigation
|
97
|
-
|
98
|
-
class << self
|
99
|
-
# configure properties of the workspace switcher
|
100
|
-
# @return [NilClass]
|
101
|
-
def configure(wrap_navigation:)
|
102
|
-
instance.wrap_navigation = wrap_navigation
|
103
|
-
end
|
104
|
-
|
105
|
-
# get next workspace number
|
106
|
-
# @return [Integer]
|
107
|
-
def next_workspace_num(step:)
|
108
|
-
current_workspace_num, total_workspace_num = workspace_values
|
109
|
-
|
110
|
-
next_workspace_num = current_workspace_num + step
|
111
|
-
|
112
|
-
return next_workspace_num unless instance.wrap_navigation
|
113
|
-
|
114
|
-
if next_workspace_num.negative?
|
115
|
-
next_workspace_num = total_workspace_num - 1
|
116
|
-
elsif next_workspace_num >= total_workspace_num
|
117
|
-
next_workspace_num = 0
|
118
|
-
else
|
119
|
-
next_workspace_num
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def move_command(direction:)
|
124
|
-
workspace_num = case direction
|
125
|
-
when 'next'
|
126
|
-
next_workspace_num(step: 1)
|
127
|
-
when 'prev'
|
128
|
-
next_workspace_num(step: -1)
|
129
|
-
else
|
130
|
-
raise "#{direction} is invalid key"
|
131
|
-
end
|
132
|
-
"wmctrl -s #{workspace_num}"
|
133
|
-
end
|
134
|
-
|
135
|
-
private
|
136
|
-
|
137
|
-
# get current workspace and total workspace numbers
|
138
|
-
# @return [Integer, Integer]
|
139
|
-
def workspace_values
|
140
|
-
wmctrl_output = `wmctrl -d`.split("\n")
|
141
|
-
|
142
|
-
current_line = wmctrl_output.grep(/\*/).first
|
143
|
-
# NOTE: stderror when failed to get desktop
|
144
|
-
# `Cannot get current desktop properties. (_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)`
|
145
|
-
return [0, 1] if current_line.nil? # If not found ,return desktop id as 0 and the total number as 1
|
146
|
-
|
147
|
-
current_workspace_num = current_line.chars.first.to_i
|
148
|
-
total_workspace_num = wmctrl_output.length
|
149
|
-
|
150
|
-
[current_workspace_num, total_workspace_num]
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
# Manage Window
|
156
|
-
class Window
|
157
|
-
class << self
|
158
|
-
# @param method [String] "toggle" or "add" or "remove"
|
159
|
-
def maximized(method:)
|
160
|
-
"wmctrl -r :ACTIVE: -b #{method},maximized_vert,maximized_horz"
|
161
|
-
end
|
162
|
-
|
163
|
-
def close
|
164
|
-
'wmctrl -c :ACTIVE:'
|
165
|
-
end
|
166
|
-
|
167
|
-
# @param method [String] "toggle" or "add" or "remove"
|
168
|
-
def fullscreen(method:)
|
169
|
-
"wmctrl -r :ACTIVE: -b #{method},fullscreen"
|
170
|
-
end
|
171
|
-
|
172
|
-
def move_command(direction:)
|
173
|
-
workspace_num = case direction
|
174
|
-
when 'next'
|
175
|
-
Workspace.next_workspace_num(step: 1)
|
176
|
-
when 'prev'
|
177
|
-
Workspace.next_workspace_num(step: -1)
|
178
|
-
else
|
179
|
-
raise "#{direction} is invalid key"
|
180
|
-
end
|
181
|
-
"wmctrl -r :ACTIVE: -t #{workspace_num} ; wmctrl -s #{workspace_num}"
|
104
|
+
@window.maximized(method: property[:maximized])
|
182
105
|
end
|
106
|
+
when nil
|
107
|
+
nil
|
108
|
+
else
|
109
|
+
raise "#{property} is invalid key"
|
183
110
|
end
|
184
111
|
end
|
185
112
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Manage Window
|
4
|
+
class Window
|
5
|
+
# @param method [String] "toggle" or "add" or "remove"
|
6
|
+
def maximized(method:)
|
7
|
+
"wmctrl -r :ACTIVE: -b #{method},maximized_vert,maximized_horz"
|
8
|
+
end
|
9
|
+
|
10
|
+
def close
|
11
|
+
'wmctrl -c :ACTIVE:'
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param method [String] "toggle" or "add" or "remove"
|
15
|
+
def fullscreen(method:)
|
16
|
+
"wmctrl -r :ACTIVE: -b #{method},fullscreen"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Manage workspace
|
4
|
+
class Workspace
|
5
|
+
class InvalidOption < StandardError; end
|
6
|
+
|
7
|
+
def initialize(wrap_navigation: nil, matrix_col_size: nil)
|
8
|
+
@wrap_navigation = wrap_navigation
|
9
|
+
@matrix_col_size = matrix_col_size
|
10
|
+
end
|
11
|
+
|
12
|
+
# get next workspace number
|
13
|
+
# @return [Integer]
|
14
|
+
def next_workspace_num(step:)
|
15
|
+
current_workspace_num, total_workspace_num = workspace_values
|
16
|
+
|
17
|
+
next_workspace_num = current_workspace_num + step
|
18
|
+
|
19
|
+
return next_workspace_num unless @wrap_navigation
|
20
|
+
|
21
|
+
if next_workspace_num.negative?
|
22
|
+
next_workspace_num = total_workspace_num - 1
|
23
|
+
elsif next_workspace_num >= total_workspace_num
|
24
|
+
next_workspace_num = 0
|
25
|
+
else
|
26
|
+
next_workspace_num
|
27
|
+
end
|
28
|
+
next_workspace_num
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Array<Integer>]
|
32
|
+
def matrix_size(total_workspace_num)
|
33
|
+
must_have_matrix_option!
|
34
|
+
col_size = @matrix_col_size
|
35
|
+
row_size = (total_workspace_num / col_size)
|
36
|
+
[row_size.to_i, col_size.to_i]
|
37
|
+
end
|
38
|
+
|
39
|
+
def must_have_matrix_option!
|
40
|
+
return if @matrix_col_size
|
41
|
+
|
42
|
+
warn(<<~ERRRORMESSAGE)
|
43
|
+
Please set matrix-col-size to config.yml
|
44
|
+
|
45
|
+
```config.yaml
|
46
|
+
plugin:
|
47
|
+
executors:
|
48
|
+
wmctrl_executor:
|
49
|
+
matrix-col-size: 2
|
50
|
+
```
|
51
|
+
ERRRORMESSAGE
|
52
|
+
raise InvalidOption, 'You need to set matrix option to config.yml'
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Integer]
|
56
|
+
# @raise RuntimeError
|
57
|
+
def next_workspace_num_for_matrix(direction:)
|
58
|
+
must_have_matrix_option!
|
59
|
+
current_workspace_num, total_workspace_num = workspace_values
|
60
|
+
row_size, col_size = matrix_size(total_workspace_num)
|
61
|
+
x = current_workspace_num % col_size
|
62
|
+
y = current_workspace_num / col_size
|
63
|
+
case direction
|
64
|
+
when 'right'
|
65
|
+
if x < col_size - 1
|
66
|
+
current_workspace_num + 1
|
67
|
+
elsif @wrap_navigation
|
68
|
+
current_workspace_num - (col_size - 1)
|
69
|
+
else
|
70
|
+
current_workspace_num
|
71
|
+
end
|
72
|
+
when 'left'
|
73
|
+
if x.positive?
|
74
|
+
current_workspace_num - 1
|
75
|
+
elsif @wrap_navigation
|
76
|
+
current_workspace_num + (col_size - 1)
|
77
|
+
else
|
78
|
+
current_workspace_num
|
79
|
+
end
|
80
|
+
when 'down'
|
81
|
+
if y < row_size - 1
|
82
|
+
current_workspace_num + col_size
|
83
|
+
elsif @wrap_navigation
|
84
|
+
(current_workspace_num + col_size) - total_workspace_num
|
85
|
+
else
|
86
|
+
current_workspace_num
|
87
|
+
end
|
88
|
+
when 'up'
|
89
|
+
if y.positive?
|
90
|
+
current_workspace_num - col_size
|
91
|
+
elsif @wrap_navigation
|
92
|
+
total_workspace_num + (current_workspace_num - col_size)
|
93
|
+
else
|
94
|
+
current_workspace_num
|
95
|
+
end
|
96
|
+
else
|
97
|
+
raise "#{direction} is invalid key"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def move_command(direction:)
|
102
|
+
workspace_num = case direction
|
103
|
+
when 'next'
|
104
|
+
next_workspace_num(step: 1)
|
105
|
+
when 'prev'
|
106
|
+
next_workspace_num(step: -1)
|
107
|
+
else
|
108
|
+
raise "#{direction} is invalid key"
|
109
|
+
end
|
110
|
+
"wmctrl -s #{workspace_num}"
|
111
|
+
end
|
112
|
+
|
113
|
+
def move_command_for_matrix(direction:)
|
114
|
+
workspace_num = next_workspace_num_for_matrix(direction: direction)
|
115
|
+
"wmctrl -s #{workspace_num}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def move_window_command(direction:)
|
119
|
+
workspace_num = case direction
|
120
|
+
when 'next'
|
121
|
+
next_workspace_num(step: 1)
|
122
|
+
when 'prev'
|
123
|
+
next_workspace_num(step: -1)
|
124
|
+
else
|
125
|
+
raise "#{direction} is invalid key"
|
126
|
+
end
|
127
|
+
"wmctrl -r :ACTIVE: -t #{workspace_num} ; wmctrl -s #{workspace_num}"
|
128
|
+
end
|
129
|
+
|
130
|
+
def move_window_command_for_matrix(direction:)
|
131
|
+
workspace_num = next_workspace_num_for_matrix(direction: direction)
|
132
|
+
"wmctrl -r :ACTIVE: -t #{workspace_num} ; wmctrl -s #{workspace_num}"
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
# get current workspace and total workspace numbers
|
138
|
+
# @return [Integer, Integer]
|
139
|
+
def workspace_values
|
140
|
+
wmctrl_output = `wmctrl -d`.split("\n")
|
141
|
+
|
142
|
+
current_line = wmctrl_output.grep(/\*/).first
|
143
|
+
# NOTE: stderror when failed to get desktop
|
144
|
+
# `Cannot get current desktop properties. \
|
145
|
+
# (_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
|