fusuma-plugin-wmctrl 0.4.0.pre2 → 1.0.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: '069aaa7f3d06e2445189455621c251823ae6b012b3aae0c7ec717f013af5c4c9'
4
- data.tar.gz: cabf1c51189097888bcc4f729ffbc985b3a953cbb65a260a517b807f234056bf
3
+ metadata.gz: 260a70ddbdd31218d0e43a7c354482b44d1cc17aaf7ece9fe3800ce7bf156c72
4
+ data.tar.gz: 0a15ae0fbe6c080fccc5e460400a3b77e062079e00f0a90dabdfb43d3c5d4dd2
5
5
  SHA512:
6
- metadata.gz: d7e2d157ca4e01b97b09bebe5354069b03a43d3fa47234c39eb5bdfd8f92fa510139c48618d3e428ebbf8a0663dcaffe628b1f77c3c422abaa84203b91fd8a40
7
- data.tar.gz: 216014d9644812c3992b7e8ba02f1816737eea4ce00f1097070c8b9acf15719ee874a4c635eaadc57711c4a5f1c427300031cc5ce5f91a60f484eda4534cd5f7
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 ```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
 
@@ -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
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
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.3' # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all&section=main
28
- spec.add_dependency 'fusuma', '~> 2.0.0.pre2'
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.0'
29
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
@@ -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[wmctrl workspace]
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
- Workspace.configure(wrap_navigation: config_params(:'wrap-navigation'))
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 = POSIX::Spawn.spawn(search_command(event))
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
- direction = Config.search(index)
63
- return unless direction.is_a?(String)
64
-
65
- 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
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
- 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)
77
94
  when 'fullscreen'
78
- Window.fullscreen(method: 'toggle')
95
+ @window.fullscreen(method: 'toggle')
79
96
  when 'maximized'
80
- Window.maximized(method: 'toggle')
97
+ @window.maximized(method: 'toggle')
81
98
  when 'close'
82
- Window.close
99
+ @window.close
83
100
  when Hash
84
101
  if property[:fullscreen]
85
- Window.fullscreen(method: property[:fullscreen])
102
+ @window.fullscreen(method: property[:fullscreen])
86
103
  elsif property[:maximized]
87
- Window.maximized(method: property[:maximized])
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
@@ -3,7 +3,7 @@
3
3
  module Fusuma
4
4
  module Plugin
5
5
  module Wmctrl
6
- VERSION = '0.4.0.pre2'
6
+ VERSION = '1.0.0'
7
7
  end
8
8
  end
9
9
  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