rmonitor 1.0.1 → 2.0.0

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +49 -40
  4. data/bin/rmonitor +38 -100
  5. data/lib/rmonitor.rb +17 -20
  6. data/lib/rmonitor/actions.rb +30 -0
  7. data/lib/rmonitor/actions/builder.rb +15 -0
  8. data/lib/rmonitor/actions/dsl.rb +23 -0
  9. data/lib/rmonitor/cache.rb +12 -0
  10. data/lib/rmonitor/capabilities.rb +38 -0
  11. data/lib/rmonitor/capabilities/builder.rb +15 -0
  12. data/lib/rmonitor/capabilities/dsl.rb +15 -0
  13. data/lib/rmonitor/commands/cl/base.rb +13 -0
  14. data/lib/rmonitor/commands/cl/invoke.rb +22 -0
  15. data/lib/rmonitor/commands/cl/list.rb +43 -0
  16. data/lib/rmonitor/commands/cl/update.rb +20 -0
  17. data/lib/rmonitor/commands/invoke.rb +23 -0
  18. data/lib/rmonitor/commands/list.rb +13 -0
  19. data/lib/rmonitor/commands/update.rb +18 -0
  20. data/lib/rmonitor/config.rb +27 -0
  21. data/lib/rmonitor/config/builder.rb +21 -0
  22. data/lib/rmonitor/config/dsl.rb +25 -0
  23. data/lib/rmonitor/invoker.rb +9 -0
  24. data/lib/rmonitor/matcher.rb +32 -0
  25. data/lib/rmonitor/profile/builder.rb +15 -0
  26. data/lib/rmonitor/profile/dsl.rb +15 -0
  27. data/lib/rmonitor/selector.rb +14 -0
  28. data/lib/rmonitor/strategies/base.rb +10 -0
  29. data/lib/rmonitor/strategies/optimistic.rb +15 -0
  30. data/lib/rmonitor/strategies/pessimistic.rb +17 -0
  31. data/lib/rmonitor/transformer.rb +38 -0
  32. data/lib/rmonitor/version.rb +1 -1
  33. data/lib/rmonitor/xrandr.rb +16 -0
  34. data/test/actions/builder_test.rb +27 -0
  35. data/test/actions_test.rb +60 -0
  36. data/test/cache_test.rb +30 -0
  37. data/test/capabilities/builder_test.rb +23 -0
  38. data/test/capabilities_test.rb +114 -0
  39. data/test/commands/cl/invoke_test.rb +38 -0
  40. data/test/commands/cl/list_test.rb +36 -0
  41. data/test/commands/cl/update_test.rb +24 -0
  42. data/test/commands/invoke_test.rb +86 -0
  43. data/test/commands/list_test.rb +50 -0
  44. data/test/commands/update_test.rb +67 -0
  45. data/test/config/builder_test.rb +51 -0
  46. data/test/config_test.rb +68 -0
  47. data/test/matcher_test.rb +90 -0
  48. data/test/profile/builder_test.rb +26 -0
  49. data/test/selector_test.rb +30 -0
  50. data/test/strategies/optimistic_test.rb +24 -0
  51. data/test/strategies/pessimistic_test.rb +26 -0
  52. data/test/test_helper.rb +19 -0
  53. data/test/transformer_test.rb +159 -0
  54. data/test/xrandr_test.rb +76 -0
  55. metadata +178 -28
  56. data/.rspec +0 -1
  57. data/Gemfile +0 -3
  58. data/Gemfile.lock +0 -24
  59. data/lib/rmonitor/devices.rb +0 -29
  60. data/lib/rmonitor/helpers/dsl_helpers.rb +0 -38
  61. data/lib/rmonitor/helpers/profile_helpers.rb +0 -48
  62. data/lib/rmonitor/helpers/xrandr_read_helpers.rb +0 -75
  63. data/lib/rmonitor/helpers/xrandr_write_helpers.rb +0 -55
  64. data/lib/rmonitor/profiles.rb +0 -51
  65. data/rmonitor.gemspec +0 -44
  66. data/spec/lib/helpers/dsl_helper_spec.rb +0 -29
  67. data/spec/lib/helpers/profile_helpers_spec.rb +0 -138
  68. data/spec/lib/helpers/xrandr_read_helpers_spec.rb +0 -259
  69. data/spec/lib/helpers/xrandr_write_helpers_spec.rb +0 -158
  70. data/spec/support/device_helper.rb +0 -3
  71. data/spec/support/profile_helper.rb +0 -3
  72. data/spec/support/string_helpers.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: efcffcebb400746bb0999ba82e922c65bf21d2e5
4
- data.tar.gz: 570c8a906f8a604302d2ef869988b8a9542fbd01
3
+ metadata.gz: 39988281a286deee26d615bb35fb4023b5bca9e0
4
+ data.tar.gz: 1b09ddd2c01ad30a7f31c75bcd2baf06624b1a90
5
5
  SHA512:
6
- metadata.gz: cb68858d52f4ee76132da86e9ea23cf3676bc9720fec28f4098c389eb26dc5914fdd014df856e61e566ad4e96a8bf7a925bc1e9d0041ab63c74acbadf388aaf4
7
- data.tar.gz: 8e0f383749c28649738cecaea38bc6a9ee5ffe241b18c39091cb1d655b3aa471235f80bb6b841d8da51ca7c6df7352cdb2ebec6cfd7b21a26478e387a641ab96
6
+ metadata.gz: 249da323f0ac72f0e52fee4f39cc55cb6d8702e7d098625362874a947dab993d71b2f299e85fbf15f732d6acc67543695de4868ab614e45f5623afeb225d2b68
7
+ data.tar.gz: a96ea8f0a0f87f286e882e1ea186b60600b9df3cfc3fee2bdf9a93004ff71f48373c48b5979d5d96ac8d8b88eedf00a1c0e8affa92e37d17804fcdc8f612600a
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2013 Jonas Amundsen
1
+ Copyright 2015 Jonas Amundsen
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  ## RMonitor
2
2
 
3
3
  [![Build Status](https://travis-ci.org/badeball/rmonitor.png)](https://travis-ci.org/badeball/rmonitor)
4
+ [![Code Climate](https://codeclimate.com/github/badeball/rmonitor/badges/gpa.svg)](https://codeclimate.com/github/badeball/rmonitor)
5
+ [![Test Coverage](https://codeclimate.com/github/badeball/rmonitor/badges/coverage.svg)](https://codeclimate.com/github/badeball/rmonitor/coverage)
4
6
 
5
- RMonitor is a tool for creating monitor profiles that are easily invoked. This
6
- is useful when you often find yourself in situations with different monitor
7
- configurations. It consists of one executable and a ruby configuration file.
8
- The following example shows a configuration containing monitor profiles
9
- representing three different monitor setups.
7
+ RMonitor is a tool for defining monitor profiles that can be invoked. This is
8
+ useful when you often find yourself in situations with different monitor
9
+ configurations. The following example shows a configuration containing monitor
10
+ profiles representing three different setups.
10
11
 
11
12
  ```ruby
12
13
  profile "docked" do
@@ -24,66 +25,59 @@ profile "default" do
24
25
  end
25
26
  ```
26
27
 
27
- Usage:
28
+ A configuration is parsed and considered from top to bottom. The first profile
29
+ that is currently invokable (ie. the requested devices with modes and rates are
30
+ available) is preferred. Profiles can also be invoked by name. `xrandr` is used
31
+ behind the scenes to read devices and perform changes.
28
32
 
29
- ```
30
- Usage: rmonitor [option]
31
- -c, --create [NAME] Create and output a profile with an optional name
32
- -i, --invoke NAME Invoke a profile with a given name
33
- -u, --update Invoke the most preferable profile
34
- -v, --verbose Verbose output
35
- -d, --dry-run Do everything except actually update
36
- --config-path PATH Specify the path to the configuration file (defaults to ~/.config/rmonitor/config.rb
37
- -h, --help Show this message
38
- --version Print the version number of rmonitor
39
- ```
40
-
41
- ### --create [NAME]
33
+ ## Installation
42
34
 
43
- Outputs a profile for your current setup.
35
+ The utility can be installed using `gem`.
44
36
 
45
37
  ```
46
- $ rmonitor --create "docked"
47
- profile "docked" do
48
- device "HDMI1", :mode => "1920x1080", :rate => "60.0", :pos => "0x0"
49
- device "HDMI2", :mode => "1920x1080", :rate => "60.0", :pos => "1920x0"
50
- end
38
+ $ gem install rmonitor
51
39
  ```
52
40
 
53
- ### --invoke NAME
41
+ It can be installed system-wide using the following options.
54
42
 
55
- Invokes a monitor profile by generating an xrandr query.
43
+ ```
44
+ $ gem install --no-user-install -i "$(ruby -e'puts Gem.default_dir')" -n /usr/local/bin rmonitor
45
+ ```
56
46
 
57
- ### --update
47
+ It is also packaged for Arch Linux.
58
48
 
59
- Parses the configuration file from top to bottom and invokes the first
60
- invokable profile based on what devices that are currently connected. This is
61
- likely the most used option and may for instance be used to automatically
62
- configure your screens upon startup.
49
+ ```
50
+ $ yaourt -S ruby-rmonitor
51
+ ```
63
52
 
64
- ## Installation
53
+ ## Usage
65
54
 
66
- The utility can be installed using `gem`, but is also packaged for Arch Linux.
55
+ The executable contains three sub-commands. `invoke` will invoke a monitor
56
+ profile by name.
67
57
 
68
58
  ```
69
- $ gem install rmonitor
59
+ $ rmonitor invoke --name "docked"
70
60
  ```
71
61
 
72
- It can be installed system-wide using the following options.
62
+ The `update` command will invoke the most preferred monitor profile. This
63
+ command can be mapped to XF86Display to achieve the functionality that one can
64
+ see in eg, windows or apple machines.
73
65
 
74
66
  ```
75
- $ gem install --no-user-install -i "$(ruby -e'puts Gem.default_dir')" -n /usr/bin rmonitor
67
+ $ rmonitor update
76
68
  ```
77
69
 
78
- ### Arch Linux
70
+ Lastly there is `list` which just list all defined monitor profiles.
79
71
 
80
72
  ```
81
- $ yaourt -Syua ruby-rmonitor
73
+ $ rmonitor list
82
74
  ```
83
75
 
84
76
  ## Configurable options
85
77
 
86
- The following examples shows how RMonitor can be configured.
78
+ The following examples shows how RMonitor can be configured. The default
79
+ configuration path is `~/.config/rmonitor/config.rb`, but can be configured
80
+ using the `--config-path` argument.
87
81
 
88
82
  ```ruby
89
83
  # Specify device location with position
@@ -136,6 +130,21 @@ end
136
130
 
137
131
  ## Changelog
138
132
 
133
+ ### 2.0.0
134
+
135
+ * Re-implementing the --config-path option.
136
+ * Re-implementing the :dpi profile option.
137
+
138
+ ### 2.0.0.rc2
139
+
140
+ * Relaxing the regular expression for matching device names. Previously it
141
+ would not match eg. `DP1-1`, but now it does.
142
+
143
+ ### 2.0.0.rc1
144
+
145
+ * Complete revamp of the codebase. Test coverage is wastly better. The code is
146
+ more maintainable and ready for change.
147
+
139
148
  ### 1.0.0
140
149
 
141
150
  * The -a option is removed.
@@ -1,112 +1,50 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'optparse'
4
- require 'rmonitor'
5
- require 'rmonitor/version'
6
-
7
- RMonitor.config_path = File.join(Dir.home, '.config', 'rmonitor', 'config.rb')
8
-
9
- class << self
10
- attr_accessor :options
3
+ require "commander/import"
4
+ require "rmonitor"
5
+ require "rmonitor/version"
6
+
7
+ program :name, "rmonitor"
8
+ program :version, RMonitor::VERSION
9
+ program :description, "A tool for creating monitor profiles that are easily invoked."
10
+
11
+ global_option "-c", "--config-path PATH", <<DESC.strip do |path|
12
+ Specify the path to the configuration file (defaults to ~/.config/rmonitor/config.rb)
13
+ DESC
14
+ RMonitor::Config.config_path = path
11
15
  end
12
16
 
13
- self.options = { :action => :create }
14
-
15
- OptionParser.new do |opts|
16
- opts.banner = "Usage: rmonitor [option]"
17
-
18
- opts.on("-c", "--create [NAME]", String, "Create and output a profile with an optional name") do |name|
19
- options[:action] = :create
20
- options[:name] = name
21
- end
22
-
23
- opts.on("-i", "--invoke NAME", String, "Invoke a profile with a given name") do |name|
24
- options[:action] = :invoke
25
- options[:name] = name
26
- end
27
-
28
- opts.on("-u", "--update", "Invoke the most preferable profile") do
29
- options[:action] = :update
30
- end
31
-
32
- opts.on("-v", "--verbose", "Verbose output") do
33
- options[:verbose] = true
34
- end
35
-
36
- opts.on("-d", "--dry-run", "Do everything except actually update (implies -v)") do
37
- options[:dry_run] = true
38
- options[:verbose] = true
39
- end
40
-
41
- opts.on("--config-path PATH", String, "Specify the path to the configuration file (defaults to ~/.config/rmonitor/config.rb") do |config_path|
42
- RMonitor.config_path = config_path
17
+ command :invoke do |c|
18
+ c.syntax = "invoke [options]"
19
+ c.description = "invoke a profile by name"
20
+ c.option "-n", "--name NAME", String
21
+ c.option "--dry-run", "don't run any destructive commands"
22
+ c.option "--verbose", "print every command ran"
23
+ c.action do |_, options|
24
+ RMonitor::Commands::CL::Invoke.new(
25
+ :verbose => options.verbose,
26
+ :dry_run => options.dry_run
27
+ ).execute(options.name)
43
28
  end
44
-
45
- opts.on_tail("-h", "--help", "Show this message") do
46
- puts opts
47
- exit
48
- end
49
-
50
- opts.on_tail("--version", "Print the version number of rmonitor") do
51
- puts "rmonitor #{RMonitor::VERSION}"
52
- exit
53
- end
54
- end.parse!
55
-
56
- def v_puts(content)
57
- if options[:verbose]
58
- puts content
59
- end
60
- end
61
-
62
- def exit_with(content)
63
- $stderr.puts content
64
- exit! 1
65
29
  end
66
30
 
67
- rm = RMonitor.load
68
-
69
- if options[:action] == :update
70
- # Find the first invokable profile
71
- profile = rm.profiles.find do |profile|
72
- RMonitor::Profiles.invokable?(rm.devices, profile)
73
- end
74
-
75
- if profile
76
- v_puts "Found #{profile[:name].inspect} that is invokable."
77
- options[:name] = profile[:name]
78
- options[:action] = :invoke
79
- else
80
- exit_with 'no invokable profile exists'
31
+ command :list do |c|
32
+ c.syntax = "list"
33
+ c.description = "lists all available profiles"
34
+ c.action do
35
+ RMonitor::Commands::CL::List.new.execute
81
36
  end
82
37
  end
83
38
 
84
- if options[:action] == :invoke
85
- profile = rm.profiles.find { |p| p[:name] == options[:name] }
86
-
87
- if profile
88
- if RMonitor::Profiles.invokable?(rm.devices, profile)
89
- command = RMonitor::Profiles.to_xrandr(rm.devices, profile)
90
- v_puts "Invoking #{profile[:name].inspect} by running #{command.inspect}."
91
- exec(command) unless options[:dry_run]
92
- else
93
- exit_with 'this profile is not invokable'
94
- end
95
- else
96
- exit_with 'no profile with that name exists'
97
- end
98
-
99
- elsif options[:action] == :create
100
- puts "profile #{(options[:name] || 'My profile').inspect} do"
101
- rm.devices.each do |device|
102
- if device[:enabled]
103
- puts ' device %s, :mode => %s, :rate => %s, :pos => %s' % [
104
- device[:name].inspect,
105
- device[:configuration][:mode].inspect,
106
- device[:configuration][:rate].inspect,
107
- device[:pos].inspect,
108
- ]
109
- end
39
+ command :update do |c|
40
+ c.syntax = "update"
41
+ c.description = "invokes the most preferred profile"
42
+ c.option "--dry-run", "don't run any destructive commands"
43
+ c.option "--verbose", "print every command ran"
44
+ c.action do |_, options|
45
+ RMonitor::Commands::CL::Update.new(
46
+ :verbose => options.verbose,
47
+ :dry_run => options.dry_run
48
+ ).execute
110
49
  end
111
- puts 'end'
112
50
  end
@@ -1,23 +1,20 @@
1
- require 'fileutils'
2
-
3
- require 'rmonitor/devices'
4
- require 'rmonitor/profiles'
1
+ require "rmonitor/commands/cl/invoke"
2
+ require "rmonitor/commands/cl/list"
3
+ require "rmonitor/commands/cl/update"
4
+ require "rmonitor/strategies/optimistic"
5
+ require "rmonitor/strategies/pessimistic"
6
+ require "rmonitor/actions"
7
+ require "rmonitor/cache"
8
+ require "rmonitor/capabilities"
9
+ require "rmonitor/config"
10
+ require "rmonitor/invoker"
11
+ require "rmonitor/matcher"
12
+ require "rmonitor/selector"
13
+ require "rmonitor/transformer"
14
+ require "rmonitor/xrandr"
5
15
 
6
16
  class RMonitor
7
- class XRandRArgumentError < ArgumentError; end
8
-
9
- class << self
10
- attr_accessor :config_path
11
- end
12
-
13
- attr_accessor :devices, :profiles
14
-
15
- def initialize(raw_devices_data, raw_profiles_data)
16
- @devices = Devices.parse(raw_devices_data)
17
- @profiles = Profiles.parse(raw_profiles_data)
18
- end
19
-
20
- def self.load
21
- self.new(`xrandr -q`, File.new(config_path).read)
22
- end
17
+ class UnknownProfileError < StandardError; end
18
+ class UninvokableProfileError < StandardError; end
19
+ class NoInvokableProfileError < StandardError; end
23
20
  end
@@ -0,0 +1,30 @@
1
+ require "rmonitor/actions/builder"
2
+
3
+ class RMonitor
4
+ class Actions
5
+ def initialize(options = {})
6
+ @capabilities = options[:capabilities] || Capabilities.new(options).parse
7
+ end
8
+
9
+ def create(profile)
10
+ actions = []
11
+
12
+ @avaialble_devices = @capabilities.map { |capability| capability[:name] }.uniq
13
+ @desired_devices = profile[:devices].map { |device| device[:name] }
14
+
15
+ (@avaialble_devices - @desired_devices).each do |device|
16
+ actions << {:action => :off, :name => device}
17
+ end
18
+
19
+ if profile[:dpi]
20
+ actions << {:action => :option, :name => :dpi, :value => profile[:dpi]}
21
+ end
22
+
23
+ profile[:devices].each do |device|
24
+ actions << device.merge(:action => :on)
25
+ end
26
+
27
+ actions
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ require "rmonitor/actions/dsl"
2
+
3
+ class RMonitor
4
+ class Actions
5
+ class Builder
6
+ class << self
7
+ def define(&block)
8
+ dsl = RMonitor::Actions::DSL.new
9
+ dsl.instance_eval(&block)
10
+ dsl.actions
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ class RMonitor
2
+ class Actions
3
+ class DSL
4
+ attr_accessor :actions
5
+
6
+ def initialize
7
+ @actions = []
8
+ end
9
+
10
+ def off(name)
11
+ @actions << {:action => :off, :name => name}
12
+ end
13
+
14
+ def on(name, options = {})
15
+ @actions << options.merge(:action => :on, :name => name)
16
+ end
17
+
18
+ def dpi(value)
19
+ @actions << {:action => :option, :name => :dpi, :value => value}
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ class RMonitor
2
+ class Cache
3
+ def initialize(options = {})
4
+ @output = {}
5
+ @invoker = options[:invoker] || Invoker.new(options)
6
+ end
7
+
8
+ def invoke(command)
9
+ @output[command] ||= @invoker.invoke(command)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ require "rmonitor/capabilities/builder"
2
+
3
+ class RMonitor
4
+ class Capabilities
5
+ def initialize(options = {})
6
+ @xrandr = options[:xrandr] || XRandR.new(options)
7
+ end
8
+
9
+ def parse(input = @xrandr.invoke)
10
+ capabilities = []
11
+ current_name = nil
12
+
13
+ input = input.split("\n").tap(&:shift).map(&:strip)
14
+
15
+ input.each do |line|
16
+ if /(?<name>[\w\d-]+) (dis)?connected/ =~ line
17
+ current_name = name
18
+ end
19
+
20
+ if /(?<mode>\d+x\d+)/ =~ line
21
+ line.scan(/\s(\d+(?:\.\d+)?)(?:( |\*)( |\+)?)?/) do |rate, native, current|
22
+ capabilities << {:name => current_name, :mode => mode, :rate => rate}
23
+
24
+ if native == "*"
25
+ capabilities.last[:native] = true
26
+ end
27
+
28
+ if current == "+"
29
+ capabilities.last[:current] = true
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ capabilities
36
+ end
37
+ end
38
+ end