sapristi 0.1.2 → 0.1.31

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: a6f73d47dbbde92a37f2d64ccf7cebbadbe9fb3f078e097d4c08e0eb70dd306d
4
- data.tar.gz: 28d8025def810ec571339fc7dada6ce05d7639120ecc3101870500400084b831
3
+ metadata.gz: fbe69ef3d4dc1b95146ae12ed0806a35e5248d213c2e9c25421cf70e702642bd
4
+ data.tar.gz: 61ba8b136d1dd8b7f34b55a945bb4297d046b7277bbcdcc3d6da5b72b73af415
5
5
  SHA512:
6
- metadata.gz: 9dca2f730eb0bacdac26279773efe1226f5b658a4f3b6dc03cf6bc6fccdd6528b9fec69e8a5cad7c00692c241b3f721a9533a16b759cf679d6fafa63213fb098
7
- data.tar.gz: 8eb2cf7358975e50a751867fd0ea98f84c25c7cebe90e3b45a8b62c93bd96a83ba763954af3a5087bfbf07d1088b387baa083edefd0be82b1d07b81d77db7ecd
6
+ metadata.gz: a9b2f36d57d26ec4489a88c810c6e46f465188c8b545ed22b5a034a1152fe25a703fae8cd255741584189a01b4ae7126c1eb50be63a5f0c28b8a889ec494aeca
7
+ data.tar.gz: d0e84f6ebf91c6a726da427d63e664e9a0b133650ebe9d860ec7518c5a0888a91c546ccd4ef962c6d68970e1f776ea12e8fdccbd9aab01e79851e7088118fc0f
@@ -0,0 +1,28 @@
1
+ name: Ruby
2
+
3
+ on:
4
+ push:
5
+ branches: [ $default-branch ]
6
+ pull_request:
7
+ branches: [ $default-branch ]
8
+
9
+ jobs:
10
+ test:
11
+
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Set up Ruby
17
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
18
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
19
+ # uses: ruby/setup-ruby@v1
20
+ uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
21
+ with:
22
+ bundler-cache: true
23
+ #with:
24
+ # ruby-version: 2.5
25
+ - name: Install dependencies
26
+ run: bundle install
27
+ - name: Run tests
28
+ run: bundle exec rake
data/.reek.yml CHANGED
@@ -4,4 +4,7 @@ detectors:
4
4
 
5
5
  UncommunicativeVariableName:
6
6
  accept:
7
- - e
7
+ - e
8
+
9
+ UtilityFunction:
10
+ public_methods_only: true
@@ -0,0 +1 @@
1
+ ruby-2.5.3
data/README.md CHANGED
@@ -2,35 +2,111 @@
2
2
 
3
3
  [![Maintainability](https://api.codeclimate.com/v1/badges/e168b7940a847148f617/maintainability)](https://codeclimate.com/github/sapristi-tool/sapristi/maintainability)
4
4
 
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.
5
+ ![Sapristi image](/assets/images/sapristi.jpg)
6
6
 
7
- TODO: Delete this and the text above, and describe your gem
7
+ An efficient tool to control your multi-monitor, multi-workspace enviroment. Just define your favorite working arragement in ~/sapristi.csv and execute `sapristi` to load your applications and align them in your favorite fashion.
8
8
 
9
9
  ## Installation
10
10
 
11
- Add this line to your application's Gemfile:
11
+ Install build dependencies
12
12
 
13
- ```ruby
14
- gem 'sapristi'
13
+ `$ sudo apt install build-essential ruby-dev libx11-dev libglib2.0-dev libxmu-dev libgtk-3-dev`
14
+
15
+ Install gem
16
+
17
+ `$ gem install sapristi`
18
+
19
+ ## Usage
20
+
21
+ `sapristi` load definitions from default configuration file (~/.sapristi.csv) If default configuration file is not found, it will create an empty one.
22
+
23
+ > `-f FILE` load your definitions from another file, ie: sapristi -f ~/machine_learning_definitions.csv
24
+
25
+ > `-v | --verbose` verbose mode.
26
+
27
+ > `--dry-run` dry mode, show your definitions but it doesn't execute them.
28
+
29
+ > `-g|--group name` load definitions tagged with group, ie: sapristi -g social
30
+
31
+
32
+ ### Configuration example: ~/.sapristi.csv
33
+
34
+ | __Title__ | __Command__ | __Monitor__ | __X__ | __Y__ | __Width__ | __Height__ | __Workspace__ | __Group__ |
35
+ |-------|---------------------------------------------------------------------------------|---------|------------|------------|--------|--------|-----------|----------|
36
+ | | subl ~/projects/ruby/sapristi | | 0 | 0 | 60% | 100% | 0 | sapristi |
37
+ | | terminator --working-directory=~/projects/ruby/sapristi | | 60% | 0 | 40% | 50% | 0 | sapristi |
38
+ | | zeal | | 60% | 50% | 40% | 50% | 0 | sapristi |
39
+ | | subl ~/projects/python/stuff | | 0 | 0 | 60% | 100% | 1 | python |
40
+ | | terminator --working-directory=~/projects/python/stuff | | 60% | 0 | 40% | 50% | 1 | python |
41
+ | | 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 | 0 | 50% | 100% | 2 | social |
43
+ | | firefox --new-window https://www.slack.com | | 50% | 0 | 50% | 50% | 2 | social |
44
+ | | firefox --new-window https://www.twitter.com | | 50% | | 50% | 50% | 2 | social |
45
+ | | sol | DP-2 | 0 | 0 | 100% | 100% | 3 | games |
46
+
47
+ 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
+ - How choose a window from the current ones, or how to launch a program to produce the window: __Title__, __Command__
49
+ - Which monitor to place it: __Monitor__
50
+ - Which workspace: __Workspace__
51
+ - Desired window geometry: __X__, __Y__, __Width__, __Height__
52
+
53
+ The table above represents a CSV file like the one below:
54
+ ```
55
+ Title,Command,Monitor,X,Y,Width,Height,Workspace
56
+ ,subl ~/projects/ruby/sapristi,0,0,60%,100%,0,sapristi
57
+ ,terminator --working-directory=~/projects/ruby/sapristi,60%,0,40%,50%,0,sapristi
58
+ ,zeal,60%,50%,40%,50%,0,sapristi
59
+ ,subl ~/projects/python/stuff,0,0,60%,100%,1,python
60
+ ,terminator --working-directory=~/projects/python/stuff,60%,0,40%,50%,1,python
61
+ ,firefox --new-window https://docs.python.org/3/index.html,60%,50%,40%,50%,1,python
62
+ ,firefox --new-window https://www.gmail.com,0,0,50%,100%,2,social
63
+ ,firefox --new-window https://www.slack.com,50%,0,50%,50%,2,social
64
+ ,firefox --new-window https://www.twitter.com,50%,50%,50%,2,social
65
+ ,sol,DP-2,0,0,100%,100%,3,games
15
66
  ```
16
67
 
17
- And then execute:
18
68
 
19
- $ bundle
69
+ #### Fields:
20
70
 
21
- Or install it yourself as:
71
+ - __Title__(optional): Regex If defined, sapristi will try to find a window whose title matches the regular expression. Examples:
72
+ - \(sapristi\) - Sublime
73
+ - Twitter.+Firefox
74
+ - System Monitor
22
75
 
23
- $ gem install sapristi
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:
78
+ - firefox --new-window https://www.twitter.com
79
+ - terminator --working-directory=~/projects/python/stuff
80
+
24
81
 
25
- ## Usage
82
+ - __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
+ - Use monitor when specified.
84
+ - Use main monitor if monitor name is not found.
85
+ - Use main monitor if __Monitor__ is not provided.
86
+
87
+ - __X__(mandatory): Absolute or relative. Horizontal top left coordinate to place the window:
88
+ - Absolute (pixels): ie 100, 200, 250.
89
+ - Relative (monitor workarea): 10%, 20%, 50%. Percentage has to be an integer between 0 and 100.
90
+
91
+ - __Y__(mandatory): Absolute or relative. Vertical top left coordinate to place the window:
92
+ - Absolute (pixels): ie 100, 200, 250.
93
+ - Relative (monitor workarea): 10%, 20%, 50%. Percentage has to be an integer between 0 and 100.
26
94
 
27
- TODO: Write usage instructions here
95
+ - __Width__(mandatory): Absolute (pixels) or relative (workarea) Window width. Examples: 100, 50%.
28
96
 
29
- ## Development
97
+ - __Height__(mandatory): Absolute (pixels) or relative (workarea) Window height. Examples: 100, 50%.
30
98
 
31
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
99
+ - __Workspace__(optional): Workspace/desktop to place the window, current workspace if it is not defined. Examples: 0, 1, 5.
32
100
 
33
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
101
+ ## Requirements
102
+
103
+ Linux
104
+
105
+ See ruby-wmctrl (`libx11-dev libglib2.0-dev libxmu-dev`) and Ruby/GTK (`libgtk-3-dev`) gem requirements.
106
+
107
+ ## Caveats
108
+
109
+ Some programs use a main/server process to optimize their use of system resources. When you launch them, it is not possible to correlate the pid of the seed process with the pid of the window, Sapristi uses an heuristic approach to detect window instantiated under this type of strategy.
34
110
 
35
111
  ## Contributing
36
112
 
@@ -39,3 +115,6 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/sapris
39
115
  ## License
40
116
 
41
117
  Please see [LICENSE](https://github.com/sapristi-tool/sapristi/blob/master/LICENSE.txt) for personal usage and [COMM-LICENSE](https://github.com/sapristi-tool/sapristi/blob/master/COMM-LICENSE.txt) for commercial usage.
118
+
119
+ ## Credits
120
+ <span>Photo by <a href="https://unsplash.com/@danfreemanphoto?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Dan Freeman</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>
@@ -28,6 +28,7 @@ module Sapristi
28
28
  def build(options)
29
29
  @sapristi.verbose! if options.verbose
30
30
  @sapristi.dry! if options.dry
31
+ @sapristi.filter!(options.group) if options.group
31
32
  end
32
33
 
33
34
  def save_stacktrace(error)
@@ -3,16 +3,25 @@
3
3
  module Sapristi
4
4
  module Linux
5
5
  class ProcessManager
6
- def self.execute_and_detach(cmd)
6
+ def self.execute_and_detach(cmd, out, err)
7
+ write_log_headers(out, err, cmd)
7
8
  process_pid = begin
8
- Process.spawn(cmd)
9
+ Process.spawn(cmd, out: [out, 'a'], err: [err, 'a'], pgroup: Process.getpgrp)
9
10
  rescue StandardError
10
11
  raise Error, "Error executing process: #{$ERROR_INFO}"
11
12
  end
12
- ::Sapristi.logger.info "Launch #{cmd.split[0]}, process=#{process_pid}"
13
+ ::Sapristi.logger.info "Launch #{cmd.split[0]}, process=#{process_pid} pgroup=#{Process.getpgrp}"
13
14
  Process.detach process_pid
14
15
  end
15
16
 
17
+ def self.write_log_headers(out, err, header)
18
+ [out, err].each { |file_name| write_header(file_name, header) }
19
+ end
20
+
21
+ def self.write_header(file_name, header)
22
+ File.open(file_name, 'a') { |file| file.write "\n\n#{header}\n" }
23
+ end
24
+
16
25
  def self.kill(waiter)
17
26
  Process.kill 'KILL', waiter.pid
18
27
  # sleep 1 # XLIB error for op code
@@ -59,6 +59,11 @@ module Sapristi
59
59
  check_expected_geometry window, requested
60
60
  end
61
61
 
62
+ def to_workspace(window, workspace)
63
+ @display.action_window(window.id, :move_to_desktop, workspace)
64
+ sleep TIME_TO_APPLY_DIMENSIONS
65
+ end
66
+
62
67
  private
63
68
 
64
69
  EXTENDED_HINTS = %w[maximized_vert maximized_horz].freeze
@@ -20,9 +20,11 @@ module Sapristi
20
20
  end
21
21
  end
22
22
 
23
+ # This method smells of :reek:TooManyStatements
23
24
  def self.populate_options(opts, args)
24
25
  opts.banner = 'Usage: sapristi [options]'
25
26
  opts.on('-v', '--verbose', 'Verbose mode') { |value| args.verbose = value }
27
+ opts.on('-g', '--group GROUP', 'Use named group definitions') { |value| args.group = value }
26
28
  opts.on('--dry-run', 'Dry run') { |value| args.dry = value }
27
29
  opts.on('-f', '--file FILE', 'Read configuration from FILE') { |file| args.file = file }
28
30
  opts.on('-h', '--help', 'Prints this help') do
@@ -39,16 +39,16 @@ module Sapristi
39
39
  def apply_percentage
40
40
  validate_percentage_field
41
41
 
42
- (monitor_absolute * percentage).to_i + offset
42
+ (monitor_absolute * percentage - 1).to_i + offset
43
43
  end
44
44
 
45
45
  def offset
46
46
  work_area = monitor['work_area']
47
47
 
48
48
  case key
49
- when 'X-position'
49
+ when 'X'
50
50
  work_area[0]
51
- when 'Y-position'
51
+ when 'Y'
52
52
  work_area[1]
53
53
  else
54
54
  0
@@ -66,7 +66,7 @@ module Sapristi
66
66
  end
67
67
 
68
68
  def validate_percentage_field
69
- min_percentage = { 'V-size' => 0.05, 'H-size' => 0.05 }.fetch(key, 0)
69
+ min_percentage = { 'Height' => 0.05, 'Width' => 0.05 }.fetch(key, 0)
70
70
  unless Definition::TRANSLATIONS.include? key
71
71
  raise "#{key}=#{raw}, using percentage in invalid field, valid=#{Definition::TRANSLATIONS.keys.join(', ')}"
72
72
  end
@@ -3,8 +3,8 @@
3
3
  module Sapristi
4
4
  class Definition
5
5
  TRANSLATIONS = {
6
- 'H-size' => 'work_area_width', 'V-size' => 'work_area_height',
7
- 'X-position' => 'work_area_width', 'Y-position' => 'work_area_height'
6
+ 'Width' => 'work_area_width', 'Height' => 'work_area_height',
7
+ 'X' => 'work_area_width', 'Y' => 'work_area_height'
8
8
  }.freeze
9
9
  NUMERIC_FIELDS = (TRANSLATIONS.keys + %w[Workspace]).freeze
10
10
 
@@ -21,7 +21,8 @@ module Sapristi
21
21
  HEADERS.map { |key| "#{key}: #{raw_definition[key]}" }.join(', ')
22
22
  end
23
23
 
24
- attr_reader :raw_definition, :monitor, :x_position, :y_position, :v_size, :h_size, :workspace, :command, :title
24
+ attr_reader :raw_definition, :monitor, :x, :y, :height, :width,
25
+ :workspace, :command, :title, :group
25
26
 
26
27
  def hash
27
28
  state.hash
@@ -42,7 +43,7 @@ module Sapristi
42
43
  private
43
44
 
44
45
  def normalize_variables
45
- %w[Title Command X-position Y-position H-size V-size].each do |key|
46
+ %w[Title Command X Y Width Height Group].each do |key|
46
47
  name = key.downcase.gsub(/-/, '_')
47
48
  value = AttributeNormalizer.new(key, @raw_definition[key], @monitor).normalize
48
49
  instance_variable_set "@#{name}".to_sym, value
@@ -59,11 +60,11 @@ module Sapristi
59
60
  end
60
61
 
61
62
  def validate_geometry(definition)
62
- geometry_field_nil = %w[H-size V-size X-position Y-position].find { |key| definition[key].nil? }
63
+ geometry_field_nil = %w[Width Height X Y].find { |key| definition[key].nil? }
63
64
  raise Error, "No #{geometry_field_nil} specified" if geometry_field_nil
64
65
  end
65
66
 
66
- HEADERS = %w[Title Command Monitor Workspace X-position Y-position H-size V-size].freeze
67
+ HEADERS = %w[Title Command Monitor Workspace X Y Width Height Group].freeze
67
68
 
68
69
  def validate_headers(definition)
69
70
  headers = definition.keys
@@ -31,8 +31,8 @@ module Sapristi
31
31
  end
32
32
 
33
33
  def validate_monitor_dimensions(normalized, monitor_width, monitor_height)
34
- x_pos = normalized.x_position
35
- y_pos = normalized.y_position
34
+ x_pos = normalized.x
35
+ y_pos = normalized.y
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
@@ -42,10 +42,10 @@ module Sapristi
42
42
  end
43
43
 
44
44
  def validate_work_area(normalized, monitor_width, monitor_height)
45
- x_pos = normalized.x_position
46
- y_pos = normalized.y_position
47
- x_end = x_pos + normalized.h_size
48
- y_end = y_pos + normalized.v_size
45
+ x_pos = normalized.x
46
+ y_pos = normalized.y
47
+ x_end = x_pos + normalized.width
48
+ y_end = y_pos + normalized.height
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
@@ -57,8 +57,8 @@ module Sapristi
57
57
  MIN_X_SIZE = 50
58
58
  MIN_Y_SIZE = 50
59
59
  def validate_window_min_size(normalized)
60
- window_width = normalized.h_size
61
- window_height = normalized.v_size
60
+ window_width = normalized.width
61
+ window_height = normalized.height
62
62
  raise Error, "window x size=#{window_width} less than #{MIN_X_SIZE}" if window_width < MIN_X_SIZE
63
63
  raise Error, "window y size=#{window_height} less than #{MIN_Y_SIZE}" if window_height < MIN_Y_SIZE
64
64
  end
@@ -11,15 +11,17 @@ 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
- definition.h_size, definition.v_size])
14
+ [definition.x, definition.y,
15
+ definition.width, definition.height])
16
+ @window_manager.to_workspace(window, definition.workspace)
17
+ window
16
18
  end
17
19
 
18
20
  private
19
21
 
20
22
  def get_window(title, command)
21
23
  (title && find_one_by_title(title)) ||
22
- (command && @process_manager.detect_window_for_process(command)) ||
24
+ (command && @process_manager.detect_window_for_process(command, title)) ||
23
25
  raise(Error, "Couldn't produce a window for this definition")
24
26
  end
25
27
 
@@ -5,12 +5,13 @@ module Sapristi
5
5
  def initialize
6
6
  @display = OSFactory.new.window_manager
7
7
  @process_manager = OSFactory.new.process_manager
8
+ @log = ::Sapristi.logger
8
9
  end
9
10
 
10
- def detect_window_for_process(command, timeout_in_seconds = 30)
11
+ def detect_window_for_process(command, title, timeout_in_seconds = 30)
11
12
  save_pids_and_windows
12
13
 
13
- process_window = wait_for_window(command, timeout_in_seconds)
14
+ process_window = wait_for_window(command, title, timeout_in_seconds)
14
15
 
15
16
  if process_window
16
17
  ::Sapristi.logger.info " Found window title=#{process_window.title} for process=#{process_window.pid}!"
@@ -21,18 +22,18 @@ module Sapristi
21
22
 
22
23
  private
23
24
 
24
- attr_reader :previous_windows_ids, :previous_pids, :process_manager
25
+ attr_reader :previous_windows_ids, :previous_pids, :process_manager, :log
25
26
 
26
27
  def save_pids_and_windows
27
28
  @previous_windows_ids = @display.windows.map { |window| window[:id] }
28
29
  @previous_pids = process_manager.user_pids
29
30
  end
30
31
 
31
- def wait_for_window(command, timeout_in_seconds)
32
+ def wait_for_window(command, title, timeout_in_seconds)
32
33
  program = command.split[0]
33
- waiter = process_manager.execute_and_detach command
34
+ waiter = process_manager.execute_and_detach command, '/tmp/sapristi.stdout', '/tmp/sapristi.stderr'
34
35
 
35
- window = discover_window(waiter, program, timeout_in_seconds)
36
+ window = discover_window(waiter, program, title, timeout_in_seconds)
36
37
  return window if window
37
38
 
38
39
  raise Error, 'Error executing process, is dead' unless waiter.alive?
@@ -40,25 +41,59 @@ module Sapristi
40
41
  process_manager.kill waiter
41
42
  end
42
43
 
43
- def discover_window(waiter, program, timeout_in_seconds)
44
+ # This method smells of :reek:DuplicateMethodCall
45
+ # This method smells of :reek:LongParameterList
46
+ def discover_window(waiter, program, title, timeout_in_seconds)
44
47
  start_time = Time.now
48
+
45
49
  while Time.now - start_time < timeout_in_seconds # && waiter.alive?
46
- process_window = detect_new_windows.find do |window|
47
- window_for_waiter?(waiter, window) || window_for_command?(waiter, window, program)
48
- end
50
+ process_window = detect_new_windows.find { |window| window_for?(waiter, program, title, window) }
49
51
 
50
52
  return process_window if process_window
51
53
 
52
- sleep 0.5
54
+ sleep 0.2
55
+ end
56
+ end
57
+
58
+ # This method smells of :reek:DuplicateMethodCall
59
+ # This method smells of :reek:FeatureEnvy
60
+ # This method smells of :reek:LongParameterList
61
+ # This method smells of :reek:TooManyStatements
62
+ def window_for?(waiter, program, title, window)
63
+ window_pgroup = Process.getpgid(window.pid)
64
+ log.debug "Found new window: pid=#{window.pid}, ppid=#{window_pgroup}, id=#{window.id}, title=#{window.title}"
65
+ if window_for_waiter?(waiter, window)
66
+ log.debug "Found window by pid=#{waiter.pid}, id=#{window.id}, title=#{window.title}"
67
+ true
68
+ elsif window_for_process_group?(window)
69
+ log.debug "Found window by process group=#{Process.getpgid(window.pid)}, id=#{window.id}, title=#{window.title}"
70
+ true
71
+ elsif window_for_command?(window, program)
72
+ log.debug "Found window by program=#{program}, id=#{window.id}, title=#{window.title}"
73
+ true
74
+ elsif window_for_title?(window, title)
75
+ log.debug "Found window by title=#{title}, id=#{window.id}, title=#{window.title}"
76
+ true
77
+ else
78
+ log.warn "We can not be sure window '#{window.title}' with pid=#{window.pid} is related to program=#{program}, pid=#{waiter.pid}, status=#{waiter.status || 'dead'}"
79
+ false
53
80
  end
54
81
  end
55
82
 
83
+ def window_for_title?(window, title)
84
+ /#{title}/i.match(window.title)
85
+ end
86
+
56
87
  def window_for_waiter?(waiter, window)
57
- waiter.alive? && window.pid.eql?(waiter.pid)
88
+ window.pid.eql?(waiter.pid)
89
+ end
90
+
91
+ def window_for_process_group?(window)
92
+ Process.getpgid(window.pid).eql?(Process.getpgrp)
58
93
  end
59
94
 
60
- def window_for_command?(waiter, window, program)
61
- !waiter.alive? && process_manager.cmd_for_pid(window.pid).start_with?(program)
95
+ def window_for_command?(window, program)
96
+ process_manager.cmd_for_pid(window.pid).start_with?(program)
62
97
  end
63
98
 
64
99
  def detect_new_windows
@@ -14,13 +14,12 @@ module Sapristi
14
14
  check_user_configuration(conf_file)
15
15
 
16
16
  definitions = @configuration_loader.load(conf_file)
17
+ definitions = definitions.filter { |definition| definition.group.eql? @group } if @group
17
18
 
18
19
  process definitions
19
20
  end
20
21
 
21
- def verbose?
22
- @verbose
23
- end
22
+ attr_reader :dry, :verbose
24
23
 
25
24
  def verbose!
26
25
  @verbose = true
@@ -33,24 +32,28 @@ module Sapristi
33
32
  logger.level = :info if logger.level > Logger::INFO
34
33
  end
35
34
 
36
- def dry?
37
- @dry
35
+ def filter!(group)
36
+ @group = group
38
37
  end
39
38
 
40
39
  private
41
40
 
42
41
  def process(definitions)
43
42
  definitions.each_with_index do |definition, index|
44
- ::Sapristi.logger.info "Process line #{index}: #{definition}"
45
- @definition_processor.process_definition(definition) unless @dry
43
+ process_line(definition, index)
46
44
  rescue Error => e
47
45
  raise Error, "#{e.message}, line=#{index}"
48
46
  end
49
47
  end
50
48
 
49
+ def process_line(definition, index)
50
+ ::Sapristi.logger.info "Process line #{index}: #{definition}"
51
+ @definition_processor.process_definition(definition) unless dry
52
+ end
53
+
51
54
  def check_user_configuration(conf_file)
52
55
  return unless conf_file.eql?(Sapristi.user_default_configuration_file) && !File.exist?(conf_file)
53
-
56
+
54
57
  @configuration_loader.create_empty_configuration conf_file
55
58
  end
56
59
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sapristi
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.31'
5
5
  end
@@ -10,7 +10,7 @@ module Sapristi
10
10
  @display = OSFactory.new.window_manager
11
11
  end
12
12
 
13
- def_delegators :@display, :windows, :close, :workspaces, :move_resize, :resize, :move
13
+ def_delegators :@display, :windows, :close, :workspaces, :move_resize, :resize, :move, :to_workspace
14
14
 
15
15
  def find_window(title_regex)
16
16
  @display.windows title: title_regex
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.2
4
+ version: 0.1.31
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-18 00:00:00.000000000 Z
11
+ date: 2020-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -270,10 +270,12 @@ executables:
270
270
  extensions: []
271
271
  extra_rdoc_files: []
272
272
  files:
273
+ - ".github/workflows/ruby.yml"
273
274
  - ".gitignore"
274
275
  - ".reek.yml"
275
276
  - ".rspec"
276
277
  - ".rubocop.yml"
278
+ - ".ruby-version"
277
279
  - ".travis.yml"
278
280
  - COMM-LICENSE.txt
279
281
  - Gemfile
@@ -281,6 +283,7 @@ files:
281
283
  - LICENSE.txt
282
284
  - README.md
283
285
  - Rakefile
286
+ - assets/images/sapristi.jpg
284
287
  - bin/console
285
288
  - bin/sapristi
286
289
  - bin/setup