xrandr 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 22d9b48ee5fbeaf0c16d9d36826f9fd83bcaf292
4
+ data.tar.gz: d21fc1b476c1cd301cdc0fb3d1ecc0d090c799d8
5
+ SHA512:
6
+ metadata.gz: 8b2b346c588b1d3bc3dc254cb9eccd844ef2ffd84f971cf2202aae51ddeb59ef7c0839632be9c2924269a6f3554244dd27c102ce165d17091ff7e74ea0e1aa39
7
+ data.tar.gz: c7a03d27ff658f5739c86c88b0fab7bbaaa9597604dac087cedfc1ccc6643103bf5572d4676ec889665a97010ec57d75a1a86262ba4aac86435535734c77bf98
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .ruby-version
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in xrandr.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Fernando Martinez
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,87 @@
1
+ # xrandr
2
+
3
+ A ruby interface to [xrandr](http://www.x.org/wiki/Projects/XRandR/)
4
+
5
+ ## Install
6
+
7
+ `gem install xrandr`
8
+
9
+ (or use your preferred gemset/dependency management tool)
10
+
11
+ ## Usage
12
+
13
+ ### Outputs
14
+
15
+ Gives you the status of each of the outputs
16
+
17
+ ```ruby
18
+
19
+ Xrandr.new.outputs #=> [ <Xrandr::Output{ id: 1, name: 'LVDS1', connected: true, mode: '1920x1080' }>, <Xrandr::Output { id: 2, name: 'VGA1', connected: false }> ]
20
+
21
+ ```
22
+
23
+ Access specific output parameters
24
+
25
+ ```ruby
26
+ randr = Xrandr.new
27
+ vga = randr.output(1)
28
+
29
+ vga.connected #=> true
30
+ vga.name #=> 'VGA1'
31
+ vga.mode #=> '1920x1080'
32
+
33
+ ```
34
+
35
+ ```ruby
36
+ randr = Xrandr.new
37
+ vga = randr.output('VGA1')
38
+
39
+ vga.connected #=> true
40
+ vga.name #=> 'VGA1'
41
+ vga.mode #=> '1920x1080'
42
+
43
+ ```
44
+
45
+ ### Configure
46
+
47
+
48
+ ```ruby
49
+ xrandr = Xrandr.new
50
+
51
+ # setting global options
52
+ xrandr.configure(fb: '1920x1080', no_primary: true)
53
+
54
+
55
+ # setting per output options
56
+ xrandr.configure('VGA1', auto: true)
57
+
58
+ # also, can specify the output by index
59
+ xrandr.configure('VGA2', mode: '1920x1080', right_of: 'VGA1')
60
+
61
+
62
+ ```
63
+
64
+ After all outputs are configured, call `#apply!` to execute the command.
65
+
66
+ ```ruby
67
+
68
+ xrandr.apply!
69
+
70
+ ```
71
+
72
+ or call `#command`, to get the command line string that would be run
73
+ ```ruby
74
+
75
+ xrandr.command #=> "xrandr --fb 1920x1080 --no_primary --output VGA1 --auto --output LVDS1 --mode 1920x1080 --pos 1921x0"
76
+
77
+ ```
78
+
79
+ For all available configuration parameter see you can `man xrandr`
80
+
81
+ ## Contributing
82
+
83
+ Open an issue, lets talk.
84
+
85
+ ## License
86
+
87
+ See UNLICENSE
@@ -0,0 +1,147 @@
1
+ module Xrandr
2
+ VERSION = '0.0.2'
3
+
4
+ class Control
5
+ attr_reader :screens, :outputs, :command
6
+
7
+ def initialize(parser = Parser.new)
8
+ @screens, @outputs = * parser.parse
9
+ @command = 'xrandr'
10
+ end
11
+
12
+ def configure(output = nil, **options)
13
+ output = find_output(output) if output
14
+
15
+ command << " --output #{output.name}" if output
16
+ command << options.map do |option, value|
17
+ value = nil if value == true
18
+ " --#{option} #{value}".rstrip
19
+ end.join(' ')
20
+ end
21
+
22
+ def find_output(output)
23
+ if output.kind_of? Output
24
+ output
25
+ elsif output.kind_of? String
26
+ outputs.find {|o| o.name == output}
27
+ elsif output.kind_of? Integer
28
+ outputs[output]
29
+ else
30
+ raise ArgumentError, "Expecting a string, an integer or an Xrandr::Output instance"
31
+ end
32
+ end
33
+ alias_method :[], :find_output
34
+
35
+ def apply!
36
+ Kernel.system(command)
37
+ initialize
38
+ end
39
+ end
40
+
41
+ class Parser
42
+ attr_reader :data
43
+ def initialize(data = `xrandr --query`)
44
+ @data = data
45
+ end
46
+
47
+ def parse
48
+ screens_data, outputs_data = data.split("\n").group_by {|line| line.start_with?('Screen') }.values
49
+
50
+ [ parse_screens(screens_data), parse_outputs(outputs_data) ]
51
+ end
52
+
53
+ def parse_screens(data); []; end # TODO
54
+
55
+ def parse_outputs(data)
56
+ # join output info line with each of its modes
57
+ data = data.slice_when {|before, after| !after.start_with?(' ')}
58
+
59
+ data.map do |output_data|
60
+ parse_output(output_data)
61
+ end
62
+ end
63
+
64
+ def parse_output(data)
65
+ data, *modes = data
66
+
67
+ args = {
68
+ name: /[a-zA-Z0-9\-\_]+/,
69
+ connected: /connected|disconnected/,
70
+ primary: /primary/,
71
+ resolution: /\d+x\d+\+\d+\+\d+/,
72
+ info: /\([^\)]+\)/,
73
+ dimensions: /[0-9+]+mm x [0-9]+mm/,
74
+ }
75
+ .map {|token, regex| [token, data.scan(regex).first] }
76
+ .to_h
77
+
78
+ # split resolution and position values split all values, coherce to integers, split the array in halfs, assign each half)
79
+ args[:resolution], args[:position] = args[:resolution].split(/x|\+/).each_slice(2).map {|v| v.join('x') } if args[:resolution]
80
+
81
+ # Coherce parameters
82
+ args[:connected] = args[:connected] == 'connected'
83
+ args[:primary] = args[:primary] == 'primary'
84
+
85
+ # Parse modes
86
+ args[:modes] = parse_modes(modes)
87
+
88
+ Output.new args
89
+ end
90
+
91
+ def parse_modes(modes)
92
+ modes.map do |data|
93
+ parse_mode data
94
+ end
95
+ end
96
+
97
+ def parse_mode(data)
98
+ matches = data.lstrip.match(/^(?<resolution>\d+x\d+i?) +(?<rate>[\d\.]+)(?<current>[\* ])(?<preferred>[\+ ]).*/)
99
+
100
+ resolution = matches[:resolution].gsub 'i', '' if matches[:resolution]
101
+ args = {
102
+ resolution: resolution,
103
+ rate: matches[:rate],
104
+ current: matches[:current] == '*',
105
+ preferred: matches[:preferred] == '+',
106
+ }
107
+
108
+ Mode.new args
109
+ end
110
+ end
111
+
112
+ class Output
113
+ attr_reader :name, :connected, :primary, :resolution, :position, :info, :dimensions, :modes
114
+
115
+ def initialize(name:, connected:, primary: false, resolution: nil, position: nil, info: '', dimensions: '', modes: [])
116
+ raise ArgumentError, "must provide a name for the output" unless name
117
+ raise ArgumentError, "connected cant be nil" unless connected == true || connected == false
118
+ @name = name
119
+ @connected = connected
120
+ @primary = primary
121
+ @resolution = resolution
122
+ @position = position
123
+ @info = info
124
+ @dimensions = dimensions
125
+ @modes = modes
126
+ end
127
+
128
+ def current
129
+ modes.detect(&:current)
130
+ end
131
+
132
+ def preferred
133
+ modes.detect(&:preferred)
134
+ end
135
+ end
136
+
137
+ class Mode
138
+ attr_reader :resolution, :rate, :current, :preferred
139
+
140
+ def initialize(args={})
141
+ @resolution = args.fetch :resolution
142
+ @rate = args.fetch :rate
143
+ @current = args.fetch :current
144
+ @preferred = args.fetch :preferred
145
+ end
146
+ end
147
+ end
@@ -0,0 +1 @@
1
+ Dir['**/*_test.rb'].each &method(:load)
@@ -0,0 +1,82 @@
1
+ require_relative 'helper'
2
+ require_relative '../lib/xrandr'
3
+
4
+ module Xrandr
5
+ class XrandrTest < Minitest::Test
6
+ def outputs
7
+ [ Output.new(name: 'VGA1', connected: true), Output.new(name: 'VGA2', connected: true), Output.new(name: 'VGA3', connected: true) ]
8
+ end
9
+ end
10
+
11
+ class ControlTest < XrandrTest
12
+ def test_find_ouput_finds_an_output_by_name
13
+ parser = MockParser.new([], outputs)
14
+ xrandr = Control.new(parser)
15
+ output = outputs[2]
16
+
17
+ assert_equal outputs[2].name, xrandr.find_output(output).name
18
+ end
19
+
20
+ def test_find_ouput_finds_an_output_by_name
21
+ parser = MockParser.new([], outputs)
22
+ xrandr = Control.new(parser)
23
+
24
+ assert_equal outputs[1].name, xrandr.find_output('VGA2').name
25
+ end
26
+
27
+ def test_find_ouput_finds_an_output_by_index
28
+ parser = MockParser.new([], outputs)
29
+
30
+ xrandr = Control.new(parser)
31
+
32
+ assert_equal outputs[1].name, xrandr.find_output(1).name
33
+ end
34
+ end
35
+
36
+ class XrandrConfigureGlobalParametersTest < XrandrTest
37
+ def test_param_with_arguments
38
+ xrandr = Control.new MockParser.new([], [])
39
+ xrandr.configure fb: '2000x2000'
40
+
41
+ assert_equal 'xrandr --fb 2000x2000', xrandr.command
42
+ end
43
+
44
+ def test_switch_param
45
+ xrandr = Control.new MockParser.new([], [])
46
+ xrandr.configure noprimary: true
47
+
48
+ assert_equal 'xrandr --noprimary', xrandr.command
49
+ end
50
+ end
51
+
52
+ class XrandrConfigureOutputParametersTest < XrandrTest
53
+ def test_configure_output_by_name
54
+ xrandr = Control.new MockParser.new([], outputs)
55
+
56
+ xrandr.configure 'VGA1', mode: '2000x2000'
57
+
58
+ assert_equal 'xrandr --output VGA1 --mode 2000x2000', xrandr.command
59
+ end
60
+
61
+ def test_configure_output_by_index
62
+ xrandr = Control.new MockParser.new([], outputs)
63
+
64
+ xrandr.configure 0, primary: true
65
+
66
+ assert_equal 'xrandr --output VGA1 --primary', xrandr.command
67
+ end
68
+
69
+ def test_configure_output_param_with_a_switch_argument
70
+ xrandr = Control.new MockParser.new([], outputs)
71
+ xrandr.configure 'VGA1', primary: true
72
+
73
+ assert_equal 'xrandr --output VGA1 --primary', xrandr.command
74
+ end
75
+ end
76
+
77
+ MockParser = Struct.new(:screens, :outputs) do
78
+ def parse
79
+ [screens, outputs]
80
+ end
81
+ end
82
+ end
@@ -0,0 +1 @@
1
+ require 'minitest/autorun'
@@ -0,0 +1,18 @@
1
+ require_relative 'helper'
2
+ require_relative '../lib/xrandr'
3
+
4
+ module Xrandr
5
+ class OutputTest < Minitest::Test
6
+ def test_new_raises_if_no_name
7
+ assert_raises ArgumentError do
8
+ Output.new connected: false
9
+ end
10
+ end
11
+
12
+ def test_new_raises_if_no_connection_info
13
+ assert_raises ArgumentError do
14
+ Output.new name: 'o'
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,132 @@
1
+ require_relative 'helper'
2
+ require_relative '../lib/xrandr'
3
+
4
+ module Xrandr
5
+
6
+ class Parser::ParseOutputTest < Minitest::Test
7
+ def test_parse_outputs_returns_an_array_of_outputs
8
+ o = [
9
+ "LVDS1 connected primary 1366x768+0+0 (normal left inverted right x axis y axis) 309mm x 174mm",
10
+ " 1366x768 60.07*+"," 1024x768 60.00 ",
11
+ " 800x600 60.32 56.25 ",
12
+ " 640x480 59.94 ",
13
+ "VGA1 connected 1920x1080+1366+0 (normal left inverted right x axis y axis) 309mm x 174mm",
14
+ " 1920x1080 60.07*+",
15
+ " 1024x768 60.00 ",
16
+ " 800x600 60.32 56.25 ",
17
+ " 640x480 59.94 ",
18
+ "DP1 disconnected (normal left inverted right x axis y axis)",
19
+ "DP2 disconnected (normal left inverted right x axis y axis)"
20
+ ]
21
+
22
+ outputs = Parser.new.parse_outputs o
23
+
24
+ assert 4, outputs.size
25
+ assert outputs[0].connected
26
+ assert outputs[1].connected
27
+ refute outputs[2].connected
28
+ refute outputs[3].connected
29
+ assert 3, outputs[0].modes.size
30
+ assert 4, outputs[1].modes.size
31
+ end
32
+
33
+ def test_parses_connected_output_lines
34
+ session = Parser.new
35
+ o = ["LVDS1 connected 1366x768+0+0 (normal left inverted right x axis y axis) 309mm x 174mm"," 1366x768 60.07*+"," 1024x768 60.00 "," 800x600 60.32 56.25 "," 640x480 59.94 "]
36
+
37
+ output = session.parse_output o
38
+
39
+ assert_equal "LVDS1", output.name
40
+ assert output.connected
41
+ assert_equal '1366x768', output.resolution
42
+ assert_equal '0x0', output.position
43
+ assert_equal '(normal left inverted right x axis y axis)', output.info
44
+ assert_equal '309mm x 174mm', output.dimensions
45
+ assert_equal 4, output.modes.size
46
+ refute output.primary
47
+ end
48
+
49
+ def test_parses_primary
50
+ session = Parser.new
51
+ o = ['LVDS1 connected primary 1366x768+0+0 (normal left inverted right x axis y axis) 309mm x 174mm'," 1366x768 60.07*+"," 1024x768 60.00 "," 800x600 60.32 56.25 "," 640x480 59.94 "]
52
+
53
+ output = session.parse_output o
54
+
55
+ assert_equal "LVDS1", output.name
56
+ assert output.connected
57
+ assert_equal '1366x768', output.resolution
58
+ assert_equal '0x0', output.position
59
+ assert_equal '(normal left inverted right x axis y axis)', output.info
60
+ assert_equal '309mm x 174mm', output.dimensions
61
+ assert output.primary
62
+ assert_equal 4, output.modes.size
63
+ end
64
+
65
+ def test_parses_disconnected_output_lines
66
+ session = Parser.new
67
+ output = session.parse_output ["DP1 disconnected (normal left inverted right x axis y axis)"]
68
+
69
+ refute output.connected
70
+ assert_equal 'DP1', output.name
71
+ assert_equal '(normal left inverted right x axis y axis)', output.info
72
+ assert_nil output.dimensions
73
+ assert_nil output.resolution
74
+ assert_nil output.position
75
+ assert_empty output.modes
76
+ refute output.primary
77
+ end
78
+ end
79
+
80
+ class Parser::ParseModeTest < Minitest::Test
81
+ def test_returns_a_mode
82
+ mode = Parser.new.parse_mode(" 1366x768 60.07*+")
83
+ assert_equal '1366x768', mode.resolution
84
+ assert_equal '60.07', mode.rate
85
+ assert mode.current
86
+ assert mode.preferred
87
+ end
88
+
89
+ def test_parses_preferred_attribute_correctly
90
+ mode = Parser.new.parse_mode(" 1366x768 60.07 +")
91
+ assert mode.preferred
92
+ mode = Parser.new.parse_mode(" 1366x768 60.07 ")
93
+ refute mode.preferred
94
+
95
+ mode = Parser.new.parse_mode(" 1366x768 60.07*+")
96
+ assert mode.preferred
97
+ mode = Parser.new.parse_mode(" 1366x768 60.07* ")
98
+ refute mode.preferred
99
+ end
100
+
101
+ def test_parses_current_attribute_correctly
102
+ mode = Parser.new.parse_mode(" 1366x768 60.07* ")
103
+ assert mode.current
104
+ mode = Parser.new.parse_mode(" 1366x768 60.07 ")
105
+ refute mode.current
106
+
107
+ mode = Parser.new.parse_mode(" 1366x768 60.07*+")
108
+ assert mode.current
109
+ mode = Parser.new.parse_mode(" 1366x768 60.07 +")
110
+ refute mode.current
111
+ end
112
+
113
+ def test_parse_modes_returns_a_collection_of_modes
114
+ session = Parser.new
115
+ modes = session.parse_modes [" 1024x768 60.00 +", " 1920x1080 60.00 ", " 1366x768 60.07* "]
116
+
117
+ assert_equal "1024x768", modes[0].resolution
118
+ assert_equal "1920x1080", modes[1].resolution
119
+ assert_equal "1366x768", modes[2].resolution
120
+ end
121
+
122
+ # some systems names the modes like 1920x1080i
123
+ def test_parse_modes_allows_i_suffix
124
+ session = Parser.new
125
+ modes = session.parse_modes [" 1024x768i 60.00 +", " 1920x1080i 60.00 ", " 1366x768i 60.07* "]
126
+
127
+ assert_equal "1024x768", modes[0].resolution
128
+ assert_equal "1920x1080", modes[1].resolution
129
+ assert_equal "1366x768", modes[2].resolution
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'xrandr'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "xrandr"
8
+ spec.version = Xrandr::VERSION
9
+ spec.authors = ["Fernando Martínez"]
10
+ spec.email = ["fernando.martinez@live.com.ar"]
11
+ spec.summary = %q{ A ruby wrapper for Xrandr }
12
+ spec.description = %q{ A ruby wrapper for Xrandr }
13
+ spec.homepage = "https://github.com/f-3r/xrandr"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "minitest", "~> 5.7"
23
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xrandr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Fernando Martínez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.7'
41
+ description: " A ruby wrapper for Xrandr "
42
+ email:
43
+ - fernando.martinez@live.com.ar
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".ruby-version"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - lib/xrandr.rb
54
+ - test/all.rb
55
+ - test/control_test.rb
56
+ - test/helper.rb
57
+ - test/output_test.rb
58
+ - test/parser_test.rb
59
+ - xrandr.gemspec
60
+ homepage: https://github.com/f-3r/xrandr
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.4.5
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: A ruby wrapper for Xrandr
84
+ test_files:
85
+ - test/all.rb
86
+ - test/control_test.rb
87
+ - test/helper.rb
88
+ - test/output_test.rb
89
+ - test/parser_test.rb