sapristi 0.1.31 → 0.1.32
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Maintainability](https://api.codeclimate.com/v1/badges/e168b7940a847148f617/maintainability)](https://codeclimate.com/github/sapristi-tool/sapristi/maintainability)
|
3
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/e168b7940a847148f617/maintainability)](https://codeclimate.com/github/sapristi-tool/sapristi/maintainability) ![Gem](https://img.shields.io/gem/v/sapristi?style=plastic)
|
4
4
|
|
5
5
|
![Sapristi image](/assets/images/sapristi.jpg)
|
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
|
+
![workarea image](/assets/images/workarea.jpg)
|
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
|