sapristi 0.1.1 → 0.1.2
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 +2 -0
- data/lib/sapristi.rb +1 -0
- data/lib/sapristi/adapters/linux/process_manager.rb +3 -3
- data/lib/sapristi/adapters/linux/window_manager.rb +61 -1
- data/lib/sapristi/adapters/os_factory.rb +37 -0
- data/lib/sapristi/definition_parser.rb +6 -6
- data/lib/sapristi/definition_processor.rb +2 -2
- data/lib/sapristi/monitor_manager.rb +1 -1
- data/lib/sapristi/new_process_window_detector.rb +3 -3
- data/lib/sapristi/sapristi.rb +3 -3
- data/lib/sapristi/version.rb +1 -1
- data/lib/sapristi/window_manager.rb +6 -55
- data/sapristi.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6f73d47dbbde92a37f2d64ccf7cebbadbe9fb3f078e097d4c08e0eb70dd306d
|
4
|
+
data.tar.gz: 28d8025def810ec571339fc7dada6ce05d7639120ecc3101870500400084b831
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9dca2f730eb0bacdac26279773efe1226f5b658a4f3b6dc03cf6bc6fccdd6528b9fec69e8a5cad7c00692c241b3f721a9533a16b759cf679d6fafa63213fb098
|
7
|
+
data.tar.gz: 8eb2cf7358975e50a751867fd0ea98f84c25c7cebe90e3b45a8b62c93bd96a83ba763954af3a5087bfbf07d1088b387baa083edefd0be82b1d07b81d77db7ecd
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Sapristi
|
2
2
|
|
3
|
+
[](https://codeclimate.com/github/sapristi-tool/sapristi/maintainability)
|
4
|
+
|
3
5
|
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/sapristi`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
6
|
|
5
7
|
TODO: Delete this and the text above, and describe your gem
|
data/lib/sapristi.rb
CHANGED
@@ -15,6 +15,7 @@ require 'sapristi/adapters/linux/window_manager'
|
|
15
15
|
require 'sapristi/adapters/linux/process_manager'
|
16
16
|
require 'sapristi/new_process_window_detector'
|
17
17
|
require 'sapristi/monitor'
|
18
|
+
require 'sapristi/adapters/os_factory'
|
18
19
|
require 'logger'
|
19
20
|
|
20
21
|
module Sapristi
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Sapristi
|
4
4
|
module Linux
|
5
5
|
class ProcessManager
|
6
|
-
def execute_and_detach(cmd)
|
6
|
+
def self.execute_and_detach(cmd)
|
7
7
|
process_pid = begin
|
8
8
|
Process.spawn(cmd)
|
9
9
|
rescue StandardError
|
@@ -13,7 +13,7 @@ module Sapristi
|
|
13
13
|
Process.detach process_pid
|
14
14
|
end
|
15
15
|
|
16
|
-
def kill(waiter)
|
16
|
+
def self.kill(waiter)
|
17
17
|
Process.kill 'KILL', waiter.pid
|
18
18
|
# sleep 1 # XLIB error for op code
|
19
19
|
raise Error, 'Error executing process, it didn\'t open a window'
|
@@ -24,7 +24,7 @@ module Sapristi
|
|
24
24
|
`ps -u #{user_id}`.split("\n")[1..nil].map(&:to_i)
|
25
25
|
end
|
26
26
|
|
27
|
-
def cmd_for_pid(pid)
|
27
|
+
def self.cmd_for_pid(pid)
|
28
28
|
cmd = "ps -o cmd -p #{pid}"
|
29
29
|
line = `#{cmd}`.split("\n")[1]
|
30
30
|
raise Error, "No process found pid=#{pid}" unless line
|
@@ -36,16 +36,76 @@ module Sapristi
|
|
36
36
|
GRAVITY = 0
|
37
37
|
TIME_TO_APPLY_DIMENSIONS = 0.25
|
38
38
|
|
39
|
-
def
|
39
|
+
def move(window, x_position, y_position)
|
40
|
+
geometry = complete_geometry(window.id, x_position: x_position, y_position: y_position)
|
41
|
+
move_resize(window, geometry)
|
42
|
+
end
|
43
|
+
|
44
|
+
def resize(window, width, height)
|
45
|
+
geometry = complete_geometry(window.id, width: width, height: height)
|
46
|
+
move_resize(window, geometry)
|
47
|
+
end
|
48
|
+
|
49
|
+
def move_resize(window, requested)
|
40
50
|
remove_extended_hints(window) if window.maximized_horizontally? || window.maximized_vertically?
|
51
|
+
|
52
|
+
geometry = requested.clone
|
53
|
+
left, right, top, bottom = window.frame_extents || [0, 0, 0, 0]
|
54
|
+
geometry[2] -= left + right
|
55
|
+
geometry[3] -= top + bottom
|
56
|
+
|
41
57
|
@display.action_window(window.id, :move_resize, GRAVITY, *geometry)
|
42
58
|
sleep TIME_TO_APPLY_DIMENSIONS
|
59
|
+
check_expected_geometry window, requested
|
43
60
|
end
|
44
61
|
|
62
|
+
private
|
63
|
+
|
45
64
|
EXTENDED_HINTS = %w[maximized_vert maximized_horz].freeze
|
46
65
|
|
47
66
|
def remove_extended_hints(window)
|
48
67
|
display.action_window(window.id, :change_state, 'remove', *EXTENDED_HINTS)
|
68
|
+
sleep TIME_TO_APPLY_DIMENSIONS
|
69
|
+
end
|
70
|
+
|
71
|
+
def complete_geometry(window_id, requested)
|
72
|
+
window = @display.windows(id: window_id).first
|
73
|
+
Geometry.new(window).merge(requested)
|
74
|
+
end
|
75
|
+
|
76
|
+
LABELS = %w[x y width heigth].freeze
|
77
|
+
|
78
|
+
def check_expected_geometry(window, expected)
|
79
|
+
actual_window = @display.windows(id: window.id).first
|
80
|
+
actual = actual_window.exterior_frame || actual_window.geometry
|
81
|
+
|
82
|
+
return if actual.eql? expected
|
83
|
+
|
84
|
+
# rubocop:disable Layout/LineLength
|
85
|
+
::Sapristi.logger.warn "Geometry mismatch #{WindowManager.text_diff(actual, expected)}, requested=#{expected}, window=#{window.title}"
|
86
|
+
# rubocop:enable Layout/LineLength
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.text_diff(actual, expected)
|
90
|
+
diffs = 4.times.filter { |index| !expected[index].eql? actual[index] }
|
91
|
+
diffs.map do |diff_index|
|
92
|
+
"#{LABELS[diff_index]}: expected=#{expected[diff_index]}, actual=#{actual[diff_index]}"
|
93
|
+
end.join(', ')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Geometry
|
98
|
+
def initialize(window)
|
99
|
+
@geometry = window.exterior_frame || window.geometry
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_reader :geometry
|
103
|
+
|
104
|
+
def merge(requested)
|
105
|
+
[requested.fetch(:x_position, geometry[0]),
|
106
|
+
requested.fetch(:y_position, geometry[1]),
|
107
|
+
requested.fetch(:width, geometry[2]),
|
108
|
+
requested.fetch(:height, geometry[3])]
|
49
109
|
end
|
50
110
|
end
|
51
111
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'os'
|
4
|
+
|
5
|
+
module Sapristi
|
6
|
+
class OSFactory
|
7
|
+
def initialize
|
8
|
+
@os = OS
|
9
|
+
end
|
10
|
+
|
11
|
+
def factory_module
|
12
|
+
return Linux if linux?
|
13
|
+
|
14
|
+
raise Error, "OS not implemented: #{os_name}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def window_manager
|
18
|
+
factory_module.const_get('WindowManager').new
|
19
|
+
end
|
20
|
+
|
21
|
+
def monitor_manager
|
22
|
+
factory_module.const_get('MonitorManager').new
|
23
|
+
end
|
24
|
+
|
25
|
+
def process_manager
|
26
|
+
factory_module.const_get('ProcessManager')
|
27
|
+
end
|
28
|
+
|
29
|
+
def linux?
|
30
|
+
@os.linux?
|
31
|
+
end
|
32
|
+
|
33
|
+
def os_name
|
34
|
+
@os.parse_os_release[:pretty_name]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -36,9 +36,9 @@ module Sapristi
|
|
36
36
|
unless (0...monitor_width).include? x_pos
|
37
37
|
raise Error, "x=#{x_pos} is outside of monitor width dimension=0..#{monitor_width - 1}"
|
38
38
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
return if (0...monitor_height).include? y_pos
|
40
|
+
|
41
|
+
raise Error, "y=#{y_pos} is outside of monitor height dimension=0..#{monitor_height - 1}"
|
42
42
|
end
|
43
43
|
|
44
44
|
def validate_work_area(normalized, monitor_width, monitor_height)
|
@@ -49,9 +49,9 @@ module Sapristi
|
|
49
49
|
if x_end >= monitor_width
|
50
50
|
raise Error, "window x dimensions: [#{x_pos}, #{x_end}] exceeds monitor width [0..#{monitor_width - 1}]"
|
51
51
|
end
|
52
|
-
if y_end
|
53
|
-
|
54
|
-
|
52
|
+
return if y_end < monitor_height
|
53
|
+
|
54
|
+
raise Error, "window y dimensions: [#{y_pos}, #{y_end}] exceeds monitor height [0..#{monitor_height - 1}]"
|
55
55
|
end
|
56
56
|
|
57
57
|
MIN_X_SIZE = 50
|
@@ -11,8 +11,8 @@ module Sapristi
|
|
11
11
|
window = get_window definition.title, definition.command
|
12
12
|
|
13
13
|
@window_manager.move_resize(window,
|
14
|
-
definition.x_position, definition.y_position,
|
15
|
-
|
14
|
+
[definition.x_position, definition.y_position,
|
15
|
+
definition.h_size, definition.v_size])
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
@@ -3,8 +3,8 @@
|
|
3
3
|
module Sapristi
|
4
4
|
class NewProcessWindowDetector
|
5
5
|
def initialize
|
6
|
-
@display =
|
7
|
-
@process_manager =
|
6
|
+
@display = OSFactory.new.window_manager
|
7
|
+
@process_manager = OSFactory.new.process_manager
|
8
8
|
end
|
9
9
|
|
10
10
|
def detect_window_for_process(command, timeout_in_seconds = 30)
|
@@ -25,7 +25,7 @@ module Sapristi
|
|
25
25
|
|
26
26
|
def save_pids_and_windows
|
27
27
|
@previous_windows_ids = @display.windows.map { |window| window[:id] }
|
28
|
-
@previous_pids = process_manager.
|
28
|
+
@previous_pids = process_manager.user_pids
|
29
29
|
end
|
30
30
|
|
31
31
|
def wait_for_window(command, timeout_in_seconds)
|
data/lib/sapristi/sapristi.rb
CHANGED
@@ -49,9 +49,9 @@ module Sapristi
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def check_user_configuration(conf_file)
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
return unless conf_file.eql?(Sapristi.user_default_configuration_file) && !File.exist?(conf_file)
|
53
|
+
|
54
|
+
@configuration_loader.create_empty_configuration conf_file
|
55
55
|
end
|
56
56
|
|
57
57
|
def self.user_default_configuration_file
|
data/lib/sapristi/version.rb
CHANGED
@@ -1,43 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'forwardable'
|
4
|
+
|
3
5
|
module Sapristi
|
4
6
|
class WindowManager
|
5
|
-
|
6
|
-
@display = Linux::WindowManager.new
|
7
|
-
end
|
7
|
+
extend Forwardable
|
8
8
|
|
9
|
-
def
|
10
|
-
@display.
|
9
|
+
def initialize
|
10
|
+
@display = OSFactory.new.window_manager
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
@display.close(window)
|
15
|
-
end
|
13
|
+
def_delegators :@display, :windows, :close, :workspaces, :move_resize, :resize, :move
|
16
14
|
|
17
15
|
def find_window(title_regex)
|
18
16
|
@display.windows title: title_regex
|
19
17
|
end
|
20
18
|
|
21
|
-
def move_resize(window, x_position, y_position, width, height)
|
22
|
-
call_move_resize(window, [x_position, y_position, width, height])
|
23
|
-
end
|
24
|
-
|
25
|
-
def resize(window, width, height)
|
26
|
-
actual_window = @display.windows(id: window.id).first
|
27
|
-
x_position, y_position = (actual_window.exterior_frame || actual_window.geometry)[0..1]
|
28
|
-
call_move_resize(window, [x_position, y_position, width, height])
|
29
|
-
end
|
30
|
-
|
31
|
-
def move(window, x_position, y_position)
|
32
|
-
actual_window = @display.windows(id: window.id).first
|
33
|
-
width, height = (actual_window.exterior_frame || actual_window.geometry)[2..3]
|
34
|
-
call_move_resize(window, [x_position, y_position, width, height])
|
35
|
-
end
|
36
|
-
|
37
|
-
def workspaces
|
38
|
-
@display.workspaces
|
39
|
-
end
|
40
|
-
|
41
19
|
def find_workspace_or_current(id)
|
42
20
|
return workspaces.find(&:current).id unless id
|
43
21
|
|
@@ -52,32 +30,5 @@ module Sapristi
|
|
52
30
|
def workspace?(id)
|
53
31
|
workspaces.find { |workspace| workspace.id.eql? id }
|
54
32
|
end
|
55
|
-
|
56
|
-
def call_move_resize(window, requested)
|
57
|
-
geometry = requested.clone
|
58
|
-
left, right, top, bottom = window.frame_extents || [0, 0, 0, 0]
|
59
|
-
geometry[2] -= left + right
|
60
|
-
geometry[3] -= top + bottom
|
61
|
-
|
62
|
-
@display.move_resize(window, geometry)
|
63
|
-
|
64
|
-
check_expected_geometry window, requested
|
65
|
-
end
|
66
|
-
|
67
|
-
LABELS = %w[x y width heigth].freeze
|
68
|
-
|
69
|
-
def check_expected_geometry(window, expected)
|
70
|
-
actual_window = @display.windows(id: window.id).first
|
71
|
-
actual = actual_window.exterior_frame || actual_window.geometry
|
72
|
-
|
73
|
-
unless actual.eql? expected
|
74
|
-
::Sapristi.logger.warn "Geometry mismatch #{WindowManager.text_diff(actual, expected)}, requested=#{expected}, window=#{window.title}"
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.text_diff(actual, expected)
|
79
|
-
diffs = 4.times.filter { |index| !expected[index].eql? actual[index] }
|
80
|
-
diffs.map { |diff_index| "#{LABELS[diff_index]}: expected=#{expected[diff_index]}, actual=#{actual[diff_index]}" }.join(', ')
|
81
|
-
end
|
82
33
|
end
|
83
34
|
end
|
data/sapristi.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sapristi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sapristi dev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
11
|
+
date: 2020-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -234,6 +234,20 @@ dependencies:
|
|
234
234
|
- - "~>"
|
235
235
|
- !ruby/object:Gem::Version
|
236
236
|
version: 3.4.3
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: os
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - "~>"
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: 1.1.0
|
244
|
+
type: :runtime
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - "~>"
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: 1.1.0
|
237
251
|
- !ruby/object:Gem::Dependency
|
238
252
|
name: ruby-wmctrl
|
239
253
|
requirement: !ruby/object:Gem::Requirement
|
@@ -274,6 +288,7 @@ files:
|
|
274
288
|
- lib/sapristi/adapters/linux/monitor_manager.rb
|
275
289
|
- lib/sapristi/adapters/linux/process_manager.rb
|
276
290
|
- lib/sapristi/adapters/linux/window_manager.rb
|
291
|
+
- lib/sapristi/adapters/os_factory.rb
|
277
292
|
- lib/sapristi/arguments_parser.rb
|
278
293
|
- lib/sapristi/attribute_normalizer.rb
|
279
294
|
- lib/sapristi/configuration_loader.rb
|