rmonitor 1.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.
@@ -0,0 +1,48 @@
1
+ class RMonitor
2
+ module ProfileHelpers
3
+ def invokable?(devices, profile, verbose = false)
4
+ necessary_devices_present = necessary_devices_present?(devices, profile)
5
+ user_defined_rules_satisfied = user_defined_rules_satisfied?(profile)
6
+
7
+ if necessary_devices_present && !user_defined_rules_satisfied && verbose
8
+ method = (profile[:options][:not_if] || profile[:options][:only_if]).name
9
+
10
+ puts "#{profile[:name].inspect} deemed not invokable due to #{method.inspect}."
11
+ end
12
+
13
+ necessary_devices_present && user_defined_rules_satisfied
14
+ end
15
+
16
+ def necessary_devices_present?(devices, profile)
17
+ profile[:devices].all? do |wanted_device|
18
+ device = devices.find { |d| d[:name] == wanted_device[:name] }
19
+
20
+ device and
21
+ has_matching_configuration(device,
22
+ wanted_device[:mode],
23
+ wanted_device[:rate])
24
+ end
25
+ end
26
+
27
+ def user_defined_rules_satisfied?(profile)
28
+ if profile[:options][:only_if]
29
+ profile[:options][:only_if].call
30
+ elsif profile[:options][:not_if]
31
+ !profile[:options][:not_if].call
32
+ else
33
+ true
34
+ end
35
+ end
36
+
37
+ def best_matching_configuration(device, mode, rate)
38
+ device[:configurations].find do |configuration|
39
+ (!mode or configuration[:mode] == mode) &&
40
+ (!rate or configuration[:rate] == rate)
41
+ end
42
+ end
43
+
44
+ def has_matching_configuration(device, mode, rate)
45
+ !best_matching_configuration(device, mode, rate).nil?
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,75 @@
1
+ class RMonitor
2
+ module XRandRReadHelpers
3
+ def split_blocks(devices_data)
4
+ block = /
5
+ ^[^\s] .+? \n (?=[^\s]|\Z)
6
+ /mx
7
+
8
+ devices_data.scan(block)
9
+ end
10
+
11
+ def collect_devices(blocks)
12
+ blocks.reject do |block|
13
+ block.match(/\AScreen/)
14
+ end
15
+ end
16
+
17
+ def extract_name(block)
18
+ block.split.first
19
+ end
20
+
21
+ def extract_pos(block)
22
+ if /\+(?<x_pos>\d+)\+(?<y_pos>\d+)/ =~ block
23
+ "#{x_pos}x#{y_pos}"
24
+ end
25
+ end
26
+
27
+ def extract_enabled(block)
28
+ block.match(/\d+x\d+\+\d+\+\d+/)
29
+ end
30
+
31
+ def extract_connected(block)
32
+ block.match(/(?<!dis)connected/)
33
+ end
34
+
35
+ def extract_configuration(block)
36
+ # Consider each line except the first
37
+ block.split("\n")[1..-1].each do |configuration_line|
38
+
39
+ # See if it contains any current configurations
40
+ if /(?<rate>\d+\.\d+)\*/ =~ configuration_line
41
+
42
+ # Extract the mode (resolution)
43
+ /(?<mode>\d+x\d+)/ =~ configuration_line
44
+ return {
45
+ :mode => mode,
46
+ :rate => rate,
47
+ }
48
+ end
49
+ end
50
+
51
+ nil
52
+ end
53
+
54
+ def extract_configurations(block)
55
+ configurations = []
56
+
57
+ # Consider each line except the first
58
+ block.split("\n")[1..-1].each do |configuration_block|
59
+
60
+ # Extract the mode (resolution)
61
+ /(?<mode>\d+x\d+)/ =~ configuration_block
62
+
63
+ # Extract each supported frame rate of that mode (resolution)
64
+ configuration_block.scan(/\d+\.\d+/).each do |rate|
65
+ configurations << {
66
+ :mode => mode,
67
+ :rate => rate,
68
+ }
69
+ end
70
+ end
71
+
72
+ configurations
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,55 @@
1
+ class RMonitor
2
+ module XRandRWriteHelpers
3
+ def turn_off(device)
4
+ "--output #{device} --off"
5
+ end
6
+
7
+ def turn_on(device, options)
8
+ on = '--output %s --mode %s --rate %s' % [
9
+ device,
10
+ options[:mode],
11
+ options[:rate],
12
+ ]
13
+
14
+ if options[:pos]
15
+ on << ' --pos ' << options[:pos]
16
+ elsif options[:left_of]
17
+ on << ' --left-of ' << options[:left_of]
18
+ elsif options[:right_of]
19
+ on << ' --right-of ' << options[:right_of]
20
+ elsif options[:above]
21
+ on << ' --above ' << options[:above]
22
+ elsif options[:below]
23
+ on << ' --below ' << options[:below]
24
+ elsif options[:same_as]
25
+ on << ' --same-as ' << options[:same_as]
26
+ end
27
+
28
+ if options[:rotate]
29
+ allowed_options = %w(normal inverted left right)
30
+
31
+ if allowed_options.include?(options[:rotate])
32
+ on << ' --rotate ' << options[:rotate]
33
+ else
34
+ raise XRandRArgumentError.new("Invalid argument for --rotate")
35
+ end
36
+ end
37
+
38
+ if options[:reflect]
39
+ allowed_options = %w(normal x y xy)
40
+
41
+ if allowed_options.include?(options[:reflect])
42
+ on << ' --reflect ' << options[:reflect]
43
+ else
44
+ raise XRandRArgumentError.new("Invalid argument for --reflect")
45
+ end
46
+ end
47
+
48
+ if options[:primary]
49
+ on << ' --primary'
50
+ end
51
+
52
+ on
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,51 @@
1
+ require 'rmonitor/helpers/dsl_helpers'
2
+ require 'rmonitor/helpers/profile_helpers'
3
+ require 'rmonitor/helpers/xrandr_write_helpers'
4
+
5
+ class RMonitor
6
+ class Profiles
7
+ include DSLHelpers
8
+ extend ProfileHelpers
9
+ extend XRandRWriteHelpers
10
+
11
+ def self.parse(config_data)
12
+ profile_builder = ProfileBuilder.new
13
+ profile_builder.instance_eval(config_data)
14
+ profile_builder.profiles
15
+ end
16
+
17
+ def self.to_xrandr(devices, profile)
18
+ xrandr = 'xrandr'
19
+
20
+ # Devices that are currently enabled, but not contained in the profile
21
+ to_disable = devices.select { |d| d[:enabled ]}.map { |d| d[:name] } -
22
+ profile[:devices].map { |d| d[:name] }
23
+
24
+ unless to_disable.empty?
25
+ to_disable.each do |name|
26
+ xrandr << ' ' << turn_off(name)
27
+ end
28
+
29
+ xrandr << ' && xrandr'
30
+ end
31
+
32
+ if profile[:options] and profile[:options][:dpi]
33
+ xrandr << ' --dpi ' << profile[:options][:dpi].to_s
34
+ end
35
+
36
+ # The devices contained in the profile are to be turned on and configured
37
+ profile[:devices].each do |wanted_device|
38
+ device = devices.find { |d| d[:name] == wanted_device[:name] }
39
+
40
+ configuration = best_matching_configuration(device,
41
+ wanted_device[:mode],
42
+ wanted_device[:rate])
43
+
44
+ xrandr << ' ' << turn_on(device[:name],
45
+ configuration.merge(wanted_device))
46
+ end
47
+
48
+ xrandr
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ class RMonitor
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,43 @@
1
+ require File.join(File.dirname(__FILE__), 'lib', 'rmonitor', 'version')
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'rmonitor'
5
+ s.version = RMonitor::VERSION
6
+ s.license = 'MIT'
7
+ s.date = '2014-08-14'
8
+
9
+ s.summary = 'A tool for creating monitor profiles that are easily invoked.'
10
+ s.description = 'RMonitor is a tool for creating monitor profiles that are easily invoked. This is useful when you often find yourself in situations with different monitor configurations.'
11
+
12
+ s.authors = ['Jonas Amundsen']
13
+ s.email = ['jonasba+gem@gmail.com']
14
+
15
+ s.executables = 'rmonitor'
16
+
17
+ s.files = %w[
18
+ bin/rmonitor
19
+ lib/rmonitor/helpers/dsl_helpers.rb
20
+ lib/rmonitor/helpers/profile_helpers.rb
21
+ lib/rmonitor/helpers/xrandr_read_helpers.rb
22
+ lib/rmonitor/helpers/xrandr_write_helpers.rb
23
+ lib/rmonitor/devices.rb
24
+ lib/rmonitor/profiles.rb
25
+ lib/rmonitor/version.rb
26
+ lib/rmonitor.rb
27
+ spec/lib/helpers/dsl_helper_spec.rb
28
+ spec/lib/helpers/profile_helpers_spec.rb
29
+ spec/lib/helpers/xrandr_read_helpers_spec.rb
30
+ spec/lib/helpers/xrandr_write_helpers_spec.rb
31
+ spec/support/device_helper.rb
32
+ spec/support/profile_helper.rb
33
+ spec/support/string_helpers.rb
34
+ .rspec
35
+ Gemfile
36
+ Gemfile.lock
37
+ LICENSE
38
+ README.md
39
+ rmonitor.gemspec
40
+ ]
41
+
42
+ s.add_development_dependency('rspec')
43
+ end
@@ -0,0 +1,29 @@
1
+ require 'rmonitor/helpers/dsl_helpers'
2
+
3
+ describe RMonitor::DSLHelpers::ProfileBuilder do
4
+ context "with :only_if being present" do
5
+ it "should replace the symbol with the respective method proc" do
6
+ profile_parser = RMonitor::DSLHelpers::ProfileBuilder.new
7
+
8
+ profile_parser.define_singleton_method(:user_defined_rule) do
9
+ # No operation
10
+ end
11
+
12
+ profile_parser.profile("dummy profile", :only_if => :user_defined_rule) do end
13
+ profile_parser.profiles.first[:options][:only_if].is_a?(Method).should be_true
14
+ end
15
+ end
16
+
17
+ context "with :not_if being present" do
18
+ it "should replace the symbol with the respective method proc" do
19
+ profile_parser = RMonitor::DSLHelpers::ProfileBuilder.new
20
+
21
+ profile_parser.define_singleton_method(:user_defined_rule) do
22
+ # No operation
23
+ end
24
+
25
+ profile_parser.profile("dummy profile", :not_if => :user_defined_rule) do end
26
+ profile_parser.profiles.first[:options][:not_if].is_a?(Method).should be_true
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,138 @@
1
+ require 'rmonitor/devices'
2
+ require 'rmonitor/helpers/profile_helpers'
3
+
4
+ describe RMonitor::ProfileHelpers do
5
+ include RMonitor::ProfileHelpers
6
+
7
+ describe :invokable? do
8
+ after do
9
+ rspec_reset
10
+ end
11
+
12
+ context "with necessary devices not being present" do
13
+ it "should return false" do
14
+ stub!(:necessary_devices_present?).and_return(false)
15
+ stub!(:user_defined_rules_satisfied?).and_return(true)
16
+
17
+ invokable?(nil, nil).should be_false
18
+ end
19
+ end
20
+
21
+ context "with user defined rules not being satisfied" do
22
+ it "should return false" do
23
+ stub!(:necessary_devices_present?).and_return(true)
24
+ stub!(:user_defined_rules_satisfied?).and_return(false)
25
+
26
+ invokable?(nil, nil).should be_false
27
+ end
28
+ end
29
+
30
+ context "with necessary devices being present and user defined rules being satisfied" do
31
+ it "should return true" do
32
+ stub!(:necessary_devices_present?).and_return(true)
33
+ stub!(:user_defined_rules_satisfied?).and_return(true)
34
+
35
+ invokable?(nil, nil).should be_true
36
+ end
37
+ end
38
+ end
39
+
40
+ describe :user_defined_rules_satisfied? do
41
+ context "with :only_if being present" do
42
+ it "should return what the user defined rule is returning" do
43
+ profile = {
44
+ :options => {
45
+ :only_if => Proc.new { true }
46
+ }
47
+ }
48
+
49
+ user_defined_rules_satisfied?(profile).should be_true
50
+ end
51
+ end
52
+
53
+ context "with :not_if being present" do
54
+ it "should return the opposite of what the user defined rule is returning" do
55
+ profile = {
56
+ :options => {
57
+ :not_if => Proc.new { true }
58
+ }
59
+ }
60
+
61
+ user_defined_rules_satisfied?(profile).should be_false
62
+ end
63
+ end
64
+ end
65
+
66
+ describe :best_matching_configuration do
67
+ context "no matching configuration" do
68
+ it "should return nil" do
69
+ device = parse_device <<-D.strip_heredoc
70
+ HDMI1 connected 1920x1080+0+0 (normal left inverted right x axis y axis) 477mm x 268mm
71
+ 1920x1080 60.0*+
72
+ 1280x1024 75.0 60.0
73
+ 1152x864 75.0
74
+ 1024x768 75.1 60.0
75
+ 800x600 75.0 60.3
76
+ 640x480 75.0 60.0
77
+ 720x400 70.1
78
+ D
79
+
80
+ best_matching_configuration(device, "1280x768", nil).should == nil
81
+ end
82
+ end
83
+
84
+ context "one matching configuration" do
85
+ it "should return it" do
86
+ device = parse_device <<-D.strip_heredoc
87
+ HDMI1 connected 1920x1080+0+0 (normal left inverted right x axis y axis) 477mm x 268mm
88
+ 1920x1080 60.0*+
89
+ 1280x1024 75.0 60.0
90
+ 1152x864 75.0
91
+ 1024x768 75.1 60.0
92
+ 800x600 75.0 60.3
93
+ 640x480 75.0 60.0
94
+ 720x400 70.1
95
+ D
96
+
97
+ best_matching_configuration(device, "1920x1080", nil).should == { :mode => "1920x1080",
98
+ :rate => "60.0" }
99
+ end
100
+ end
101
+
102
+ context "more matching configurations due to no rate specified" do
103
+ it "should return the 'best' configuration" do
104
+ device = parse_device <<-D.strip_heredoc
105
+ HDMI1 connected 1920x1080+0+0 (normal left inverted right x axis y axis) 477mm x 268mm
106
+ 1920x1080 60.0*+
107
+ 1280x1024 75.0 60.0
108
+ 1152x864 75.0
109
+ 1024x768 75.1 60.0
110
+ 800x600 75.0 60.3
111
+ 640x480 75.0 60.0
112
+ 720x400 70.1
113
+ D
114
+
115
+ best_matching_configuration(device, "1280x1024", nil).should == { :mode => "1280x1024",
116
+ :rate => "75.0" }
117
+ end
118
+ end
119
+
120
+ context "more matching configurations due to no mode specified" do
121
+ it "should return the 'best' configuration" do
122
+ device = parse_device <<-D.strip_heredoc
123
+ HDMI1 connected 1920x1080+0+0 (normal left inverted right x axis y axis) 477mm x 268mm
124
+ 1920x1080 60.0*+
125
+ 1280x1024 75.0 60.0
126
+ 1152x864 75.0
127
+ 1024x768 75.1 60.0
128
+ 800x600 75.0 60.3
129
+ 640x480 75.0 60.0
130
+ 720x400 70.1
131
+ D
132
+
133
+ best_matching_configuration(device, nil, "60.0").should == { :mode => "1920x1080",
134
+ :rate => "60.0" }
135
+ end
136
+ end
137
+ end
138
+ end