rmonitor 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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