sapristi 0.1.31 → 0.1.32
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 +36 -27
- data/assets/images/workarea.jpg +0 -0
- data/bin/sapristi +16 -8
- data/lib/sapristi/arguments_parser.rb +20 -0
- data/lib/sapristi/definition_processor.rb +4 -1
- data/lib/sapristi/monitor.rb +6 -0
- data/lib/sapristi/monitor_manager.rb +7 -0
- data/lib/sapristi/new_process_window_detector.rb +14 -2
- data/lib/sapristi/sapristi.rb +5 -0
- data/lib/sapristi/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 841eaad20944eda7c31a7e9557a2edcb1c894184b89226715d4d07a0959cc4ab
|
4
|
+
data.tar.gz: 9ca9977a9fe31b3c8bef49af27a939c0b069843dae18a6316e5eba8e35beffca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33fd1ecdde12c32c02dc8aacf1ad6dd8d0cc7af0e96d46b6e86c04551969965b72f6de0602c195f2549fa4145bf70131448f0216f34a062ae6a5c732d45800b0
|
7
|
+
data.tar.gz: d354af47aea67215e560744c36220b02af47728e06cdb1178a750c96e755578f1943c7845a556e51bae2788c15132bd7fb4628e9d715a207b928a5b2f8be77f9
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Sapristi
|
2
2
|
|
3
|
-
[](https://codeclimate.com/github/sapristi-tool/sapristi/maintainability)
|
3
|
+
[](https://codeclimate.com/github/sapristi-tool/sapristi/maintainability) 
|
4
4
|
|
5
5
|

|
6
6
|
|
@@ -22,27 +22,33 @@ Install gem
|
|
22
22
|
|
23
23
|
> `-f FILE` load your definitions from another file, ie: sapristi -f ~/machine_learning_definitions.csv
|
24
24
|
|
25
|
-
> `-v | --verbose` verbose mode
|
25
|
+
> `-v | --verbose` verbose mode
|
26
26
|
|
27
|
-
> `--dry-run` dry mode, show your definitions but it doesn't execute them
|
27
|
+
> `--dry-run` dry mode, show your definitions but it doesn't execute them
|
28
28
|
|
29
29
|
> `-g|--group name` load definitions tagged with group, ie: sapristi -g social
|
30
|
+
|
31
|
+
> `-h|--help` show help
|
32
|
+
|
33
|
+
> `-m|--monitors` show available monitors info (including work area size) and exits
|
34
|
+
|
35
|
+
> `-w|--wait-time wait-time-in-seconds` set wait time for detecting a window (default: 30)
|
30
36
|
|
31
37
|
|
32
38
|
### Configuration example: ~/.sapristi.csv
|
33
39
|
|
34
40
|
| __Title__ | __Command__ | __Monitor__ | __X__ | __Y__ | __Width__ | __Height__ | __Workspace__ | __Group__ |
|
35
41
|
|-------|---------------------------------------------------------------------------------|---------|------------|------------|--------|--------|-----------|----------|
|
36
|
-
| | subl ~/projects/ruby/sapristi | | 0
|
37
|
-
| | terminator --working-directory=~/projects/ruby/sapristi | | 60% | 0
|
42
|
+
| | subl ~/projects/ruby/sapristi | | 0% | 0% | 60% | 100% | 0 | sapristi |
|
43
|
+
| | terminator --working-directory=~/projects/ruby/sapristi | | 60% | 0% | 40% | 50% | 0 | sapristi |
|
38
44
|
| | zeal | | 60% | 50% | 40% | 50% | 0 | sapristi |
|
39
|
-
| | subl ~/projects/python/stuff | | 0
|
40
|
-
| | terminator --working-directory=~/projects/python/stuff | | 60% | 0
|
45
|
+
| | subl ~/projects/python/stuff | | 0% | 0% | 60% | 100% | 1 | python |
|
46
|
+
| | terminator --working-directory=~/projects/python/stuff | | 60% | 0% | 40% | 50% | 1 | python |
|
41
47
|
| | firefox --new-window https://docs.python.org/3/index.html | | 60% | 50% | 40% | 50% | 1 | python |
|
42
|
-
| | firefox --new-window https://www.gmail.com | | 0
|
43
|
-
| | firefox --new-window https://www.slack.com | | 50% | 0
|
44
|
-
| | firefox --new-window https://www.twitter.com | | 50% |
|
45
|
-
| | sol | DP-2 | 0
|
48
|
+
| | firefox --new-window https://www.gmail.com | | 0% | 0% | 50% | 100% | 2 | social |
|
49
|
+
| | firefox --new-window https://www.slack.com | | 50% | 0% | 50% | 50% | 2 | social |
|
50
|
+
| | firefox --new-window https://www.twitter.com | | 50% | 0% | 50% | 50% | 2 | social |
|
51
|
+
| | sol | DP-2 | 0% | 0% | 100% | 100% | 3 | games |
|
46
52
|
|
47
53
|
The configuration file is a CSV file (comma delimited, no separator). First line is the header line, next lines are definitions. Each definition prescribes:
|
48
54
|
- How choose a window from the current ones, or how to launch a program to produce the window: __Title__, __Command__
|
@@ -52,17 +58,17 @@ The configuration file is a CSV file (comma delimited, no separator). First line
|
|
52
58
|
|
53
59
|
The table above represents a CSV file like the one below:
|
54
60
|
```
|
55
|
-
Title,Command,Monitor,X,Y,Width,Height,Workspace
|
56
|
-
,subl ~/projects/ruby/sapristi
|
57
|
-
,terminator --working-directory=~/projects/ruby/sapristi
|
58
|
-
,zeal
|
59
|
-
,subl ~/projects/python/stuff
|
60
|
-
,terminator --working-directory=~/projects/python/stuff
|
61
|
-
,firefox --new-window https://docs.python.org/3/index.html
|
62
|
-
,firefox --new-window https://www.gmail.com
|
63
|
-
,firefox --new-window https://www.slack.com
|
64
|
-
,firefox --new-window https://www.twitter.com
|
65
|
-
,sol,DP-2,0
|
61
|
+
Title,Command,Monitor,X,Y,Width,Height,Workspace,Group
|
62
|
+
,subl ~/projects/ruby/sapristi,,0%,0%,60%,100%,0,sapristi
|
63
|
+
,terminator --working-directory=~/projects/ruby/sapristi,,60%,0%,40%,50%,0,sapristi
|
64
|
+
,zeal,,60%,50%,40%,50%,0,sapristi
|
65
|
+
,subl ~/projects/python/stuff,,0%,0%,60%,100%,1,python
|
66
|
+
,terminator --working-directory=~/projects/python/stuff,,60%,0%,40%,50%,1,python
|
67
|
+
,firefox --new-window https://docs.python.org/3/index.html,,60%,50%,40%,50%,1,python
|
68
|
+
,firefox --new-window https://www.gmail.com,,0%,0%,50%,100%,2,social
|
69
|
+
,firefox --new-window https://www.slack.com,,50%,0%,50%,50%,2,social
|
70
|
+
,firefox --new-window https://www.twitter.com,,50%,50%,50%,50%,2,social
|
71
|
+
,sol,DP-2,0%,0%,100%,100%,3,games
|
66
72
|
```
|
67
73
|
|
68
74
|
|
@@ -73,24 +79,27 @@ Title,Command,Monitor,X,Y,Width,Height,Workspace
|
|
73
79
|
- Twitter.+Firefox
|
74
80
|
- System Monitor
|
75
81
|
|
76
|
-
- __Command__(optional): A command. If __Title__ is not provided or there isn't a window that matches it, sapristi will execute __Command__.
|
77
|
-
Every line has to define a __Title__, a __Command__ or both. Examples:
|
82
|
+
- __Command__(optional): A command. If __Title__ is not provided or there isn't a window that matches it, sapristi will execute __Command__. Examples:
|
78
83
|
- firefox --new-window https://www.twitter.com
|
79
84
|
- terminator --working-directory=~/projects/python/stuff
|
80
85
|
|
81
|
-
|
86
|
+
(Every line has to define a __Title__, **OR** a __Command__ or both)
|
87
|
+
|
82
88
|
- __Monitor__(optional): Monitor name (check your monitor names with xrandr) If a definition specifies a monitor not present or if is empty, window will be placed in the main monitor of the actual environment.
|
83
89
|
- Use monitor when specified.
|
84
90
|
- Use main monitor if monitor name is not found.
|
85
91
|
- Use main monitor if __Monitor__ is not provided.
|
86
92
|
|
87
93
|
- __X__(mandatory): Absolute or relative. Horizontal top left coordinate to place the window:
|
88
|
-
- Absolute (
|
94
|
+
- Absolute (monitor width): ie 100, 200, 250.
|
89
95
|
- Relative (monitor workarea): 10%, 20%, 50%. Percentage has to be an integer between 0 and 100.
|
90
96
|
|
91
97
|
- __Y__(mandatory): Absolute or relative. Vertical top left coordinate to place the window:
|
92
|
-
- Absolute (
|
98
|
+
- Absolute (monitor monitor height): ie 100, 200, 250.
|
93
99
|
- Relative (monitor workarea): 10%, 20%, 50%. Percentage has to be an integer between 0 and 100.
|
100
|
+
|
101
|
+
The work area should be considered when positioning menus and similar popups, to avoid placing them below panels, docks or other desktop components.
|
102
|
+

|
94
103
|
|
95
104
|
- __Width__(mandatory): Absolute (pixels) or relative (workarea) Window width. Examples: 100, 50%.
|
96
105
|
|
Binary file
|
data/bin/sapristi
CHANGED
@@ -11,12 +11,13 @@ module Sapristi
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def run(args)
|
14
|
-
options = ArgumentsParser.new.parse args
|
15
|
-
|
16
|
-
|
17
|
-
options.file ? @sapristi.run(options.file) : @sapristi.run
|
14
|
+
@options = ArgumentsParser.new.parse args
|
15
|
+
setup
|
16
|
+
process
|
18
17
|
rescue Error => e
|
19
18
|
exit_error 1, e.message
|
19
|
+
rescue OptionParser::InvalidOption => e
|
20
|
+
exit_error 1, "Error: #{e}, check sapristi -h"
|
20
21
|
rescue StandardError => e
|
21
22
|
error_file = save_stacktrace e
|
22
23
|
|
@@ -25,10 +26,17 @@ module Sapristi
|
|
25
26
|
|
26
27
|
private
|
27
28
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
@sapristi.
|
29
|
+
def process
|
30
|
+
return MonitorManager.new.show_monitors if @options.show_monitors
|
31
|
+
|
32
|
+
@options.file ? @sapristi.run(@options.file) : @sapristi.run
|
33
|
+
end
|
34
|
+
|
35
|
+
def setup
|
36
|
+
@sapristi.verbose! if @options.verbose
|
37
|
+
@sapristi.dry! if @options.dry
|
38
|
+
@sapristi.filter!(@options.group) if @options.group
|
39
|
+
@sapristi.wait_time!(@options.wait_time) if @options.wait_time
|
32
40
|
end
|
33
41
|
|
34
42
|
def save_stacktrace(error)
|
@@ -21,16 +21,36 @@ module Sapristi
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# This method smells of :reek:TooManyStatements
|
24
|
+
# rubocop:disable Metrics/AbcSize
|
25
|
+
# rubocop:disable Metrics/MethodLength
|
24
26
|
def self.populate_options(opts, args)
|
25
27
|
opts.banner = 'Usage: sapristi [options]'
|
26
28
|
opts.on('-v', '--verbose', 'Verbose mode') { |value| args.verbose = value }
|
27
29
|
opts.on('-g', '--group GROUP', 'Use named group definitions') { |value| args.group = value }
|
30
|
+
opts.on('-w', '--wait-time NUMBER_OF_SECONDS (1-120), default=30', 'Wait time for detecting a window') do |value|
|
31
|
+
args.wait_time = parse_integer(value, 1, 120)
|
32
|
+
end
|
28
33
|
opts.on('--dry-run', 'Dry run') { |value| args.dry = value }
|
29
34
|
opts.on('-f', '--file FILE', 'Read configuration from FILE') { |file| args.file = file }
|
30
35
|
opts.on('-h', '--help', 'Prints this help') do
|
31
36
|
puts opts
|
32
37
|
exit
|
33
38
|
end
|
39
|
+
opts.on('-m', '--monitors', 'Show monitor\'s info') { args.show_monitors = true }
|
40
|
+
end
|
41
|
+
# rubocop:enable Metrics/AbcSize
|
42
|
+
# rubocop:enable Metrics/MethodLength
|
43
|
+
|
44
|
+
def self.parse_integer(value, min = nil, max = nil)
|
45
|
+
raise OptionParser::InvalidOption, "'#{value}' is not an integer" unless value.match(/^[0-9]+$/)
|
46
|
+
|
47
|
+
integer = value.to_i
|
48
|
+
raise OptionParser::InvalidOption, "requires a wait time > 0, provided=#{value}" unless min.nil? || integer >= min
|
49
|
+
unless max.nil? || integer <= max
|
50
|
+
raise OptionParser::InvalidOption, "requires a wait time <= 120 seconds, provided=#{value}"
|
51
|
+
end
|
52
|
+
|
53
|
+
integer
|
34
54
|
end
|
35
55
|
end
|
36
56
|
end
|
@@ -5,8 +5,11 @@ module Sapristi
|
|
5
5
|
def initialize(window_manager = WindowManager.new, process_manager = NewProcessWindowDetector.new)
|
6
6
|
@window_manager = window_manager
|
7
7
|
@process_manager = process_manager
|
8
|
+
@wait_time = 30
|
8
9
|
end
|
9
10
|
|
11
|
+
attr_accessor :wait_time
|
12
|
+
|
10
13
|
def process_definition(definition)
|
11
14
|
window = get_window definition.title, definition.command
|
12
15
|
|
@@ -21,7 +24,7 @@ module Sapristi
|
|
21
24
|
|
22
25
|
def get_window(title, command)
|
23
26
|
(title && find_one_by_title(title)) ||
|
24
|
-
(command && @process_manager.detect_window_for_process(command, title)) ||
|
27
|
+
(command && @process_manager.detect_window_for_process(command, title, @wait_time)) ||
|
25
28
|
raise(Error, "Couldn't produce a window for this definition")
|
26
29
|
end
|
27
30
|
|
data/lib/sapristi/monitor.rb
CHANGED
@@ -12,6 +12,12 @@ module Sapristi
|
|
12
12
|
|
13
13
|
ATTRIBUTES = %i[id main name x y offset_x offset_y work_area work_area_width work_area_height].freeze
|
14
14
|
|
15
|
+
def to_s
|
16
|
+
# rubocop:disable Layout/LineLength
|
17
|
+
"#{id} #{main ? 'main' : ' '} #{name} #{x}x#{y} workarea[x=#{work_area[0]}, y=#{work_area[1]}, width=#{work_area_width}, height=#{work_area_height}]"
|
18
|
+
# rubocop:enable Layout/LineLength
|
19
|
+
end
|
20
|
+
|
15
21
|
attr_reader(*ATTRIBUTES)
|
16
22
|
|
17
23
|
def hash
|
@@ -21,6 +21,13 @@ module Sapristi
|
|
21
21
|
@os_manager.monitors
|
22
22
|
end
|
23
23
|
|
24
|
+
def show_monitors
|
25
|
+
the_monitors = monitors
|
26
|
+
|
27
|
+
puts "Monitors: #{the_monitors.size}"
|
28
|
+
the_monitors.each_value { |monitor| puts monitor }
|
29
|
+
end
|
30
|
+
|
24
31
|
private
|
25
32
|
|
26
33
|
def monitor_present?(name)
|
@@ -47,9 +47,9 @@ module Sapristi
|
|
47
47
|
start_time = Time.now
|
48
48
|
|
49
49
|
while Time.now - start_time < timeout_in_seconds # && waiter.alive?
|
50
|
-
|
50
|
+
new_window = detect_new_windows.find { |window| window_for?(waiter, program, title, window) }
|
51
51
|
|
52
|
-
return
|
52
|
+
return new_window if new_window && !splash?(new_window)
|
53
53
|
|
54
54
|
sleep 0.2
|
55
55
|
end
|
@@ -105,5 +105,17 @@ module Sapristi
|
|
105
105
|
def new_window?(window)
|
106
106
|
!previous_windows_ids.include?(window.id)
|
107
107
|
end
|
108
|
+
|
109
|
+
def splash?(window)
|
110
|
+
skip_taskbar?(window) || skip_pager?(window)
|
111
|
+
end
|
112
|
+
|
113
|
+
def skip_taskbar?(window)
|
114
|
+
window.state.include? '_NET_WM_STATE_SKIP_TASKBAR'
|
115
|
+
end
|
116
|
+
|
117
|
+
def skip_pager?(window)
|
118
|
+
window.state.include? '_NET_WM_STATE_SKIP_PAGER'
|
119
|
+
end
|
108
120
|
end
|
109
121
|
end
|
data/lib/sapristi/sapristi.rb
CHANGED
@@ -7,6 +7,7 @@ module Sapristi
|
|
7
7
|
@definition_processor = definition_processor
|
8
8
|
@dry = false
|
9
9
|
@verbose = false
|
10
|
+
@group = nil
|
10
11
|
end
|
11
12
|
|
12
13
|
def run(conf_file = Sapristi.user_default_configuration_file)
|
@@ -32,6 +33,10 @@ module Sapristi
|
|
32
33
|
logger.level = :info if logger.level > Logger::INFO
|
33
34
|
end
|
34
35
|
|
36
|
+
def wait_time!(wait_time)
|
37
|
+
@definition_processor.wait_time = wait_time
|
38
|
+
end
|
39
|
+
|
35
40
|
def filter!(group)
|
36
41
|
@group = group
|
37
42
|
end
|
data/lib/sapristi/version.rb
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.32
|
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-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -284,6 +284,7 @@ files:
|
|
284
284
|
- README.md
|
285
285
|
- Rakefile
|
286
286
|
- assets/images/sapristi.jpg
|
287
|
+
- assets/images/workarea.jpg
|
287
288
|
- bin/console
|
288
289
|
- bin/sapristi
|
289
290
|
- bin/setup
|