xinerama 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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +183 -0
- data/lib/xinerama/display.rb +153 -0
- data/lib/xinerama/extension.rb +55 -0
- data/lib/xinerama/ffi.rb +49 -0
- data/lib/xinerama/screen_info.rb +94 -0
- data/lib/xinerama/version.rb +5 -0
- data/lib/xinerama.rb +31 -0
- metadata +65 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b933357913f6fed409fde25826b23f96b615be6d22856960c796a6edb2e8655e
|
|
4
|
+
data.tar.gz: 5019b91d24b032ae78b5e2fe6ee1c5ec60855bb82a36a7a5a30658e6ef30ac54
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 10d929bfe5dda72bca4d05b796c08db422c5f29951b6a4cd863e34000fe8c2ce67c0c53f942499b0eee9045c78b8f036b882309c03414f58c5551d73f5b74b0c
|
|
7
|
+
data.tar.gz: 12d2672635c3c30987bee19d26185a7aa207e1f2d58903d9e95b486d488040f88e3e08de8f85b975a40697df523b3eabd52edba0084e66c49884f46a661e4395
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Xinerama
|
|
2
|
+
|
|
3
|
+
Ruby FFI bindings for the Xinerama X11 extension. Query multi-monitor screen configurations with a clean, idiomatic Ruby API.
|
|
4
|
+
|
|
5
|
+
Official libXinerama repo: https://gitlab.freedesktop.org/xorg/lib/libxinerama
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add to your Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'xinerama'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or install directly:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
gem install xinerama
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Dependencies:** Requires `libX11` and `libXinerama` to be installed on your system.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Debian/Ubuntu
|
|
25
|
+
apt-get install libx11-6 libxinerama1
|
|
26
|
+
|
|
27
|
+
# Fedora/RHEL
|
|
28
|
+
dnf install libX11 libXinerama
|
|
29
|
+
|
|
30
|
+
# Arch
|
|
31
|
+
pacman -S libx11 libxinerama
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Quick Access
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
require 'xinerama'
|
|
40
|
+
|
|
41
|
+
# Get all screens (lazy enumerator)
|
|
42
|
+
Xinerama.screens.each do |screen|
|
|
43
|
+
puts "Screen #{screen.screen_number}: #{screen.width}x#{screen.height}+#{screen.x}+#{screen.y}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Check if Xinerama is active
|
|
47
|
+
Xinerama.active? # => true
|
|
48
|
+
|
|
49
|
+
# Get version
|
|
50
|
+
Xinerama.version # => #<Xinerama::Version 1.1>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Display Connection
|
|
54
|
+
|
|
55
|
+
For multiple queries, reuse the display connection:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
Xinerama.open do |display|
|
|
59
|
+
puts "Xinerama active: #{display.active?}"
|
|
60
|
+
puts "Version: #{display.version}"
|
|
61
|
+
puts "Extension event base: #{display.extension.event_base}"
|
|
62
|
+
|
|
63
|
+
display.screens.each do |screen|
|
|
64
|
+
puts screen.inspect
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Screen Information
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
Xinerama.open do |display|
|
|
73
|
+
display.screens.each do |screen|
|
|
74
|
+
screen.screen_number # => 0
|
|
75
|
+
screen.x # => 0
|
|
76
|
+
screen.y # => 0
|
|
77
|
+
screen.width # => 1920
|
|
78
|
+
screen.height # => 1080
|
|
79
|
+
screen.origin # => [0, 0]
|
|
80
|
+
screen.dimensions # => [1920, 1080]
|
|
81
|
+
screen.area # => 2073600
|
|
82
|
+
screen.aspect_ratio # => (16/9)
|
|
83
|
+
screen.to_h # => { screen_number: 0, x: 0, y: 0, width: 1920, height: 1080 }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Finding Screens
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
Xinerama.open do |display|
|
|
92
|
+
# Primary screen (first in list)
|
|
93
|
+
display.primary_screen
|
|
94
|
+
|
|
95
|
+
# Find screen containing a point
|
|
96
|
+
display.screen_at(100, 200)
|
|
97
|
+
|
|
98
|
+
# Total virtual desktop dimensions
|
|
99
|
+
display.total_dimensions # => [3840, 1080]
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Lazy Evaluation
|
|
104
|
+
|
|
105
|
+
Screen queries return lazy enumerators, efficient for large multi-monitor setups:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
# Only iterates until first matching screen is found
|
|
109
|
+
large_screen = Xinerama.screens.find { |s| s.area > 2_000_000 }
|
|
110
|
+
|
|
111
|
+
# Chain operations without intermediate arrays
|
|
112
|
+
Xinerama.screens
|
|
113
|
+
.select { |s| s.width > 1920 }
|
|
114
|
+
.map(&:to_h)
|
|
115
|
+
.first(2)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Connecting to Remote Displays
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
Xinerama.open("remote:0.0") do |display|
|
|
122
|
+
display.screens.each { |s| puts s }
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Error Handling
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
begin
|
|
130
|
+
Xinerama.open do |display|
|
|
131
|
+
display.extension # Raises if Xinerama unavailable
|
|
132
|
+
end
|
|
133
|
+
rescue Xinerama::DisplayError => e
|
|
134
|
+
puts "Cannot connect to X server"
|
|
135
|
+
rescue Xinerama::NotAvailableError => e
|
|
136
|
+
puts "Xinerama extension not available"
|
|
137
|
+
end
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## API Reference
|
|
141
|
+
|
|
142
|
+
### Module Methods
|
|
143
|
+
|
|
144
|
+
| Method | Description |
|
|
145
|
+
|--------|-------------|
|
|
146
|
+
| `Xinerama.open(display_name = nil, &block)` | Open display connection |
|
|
147
|
+
| `Xinerama.screens(display_name = nil)` | Get lazy enumerator of screens |
|
|
148
|
+
| `Xinerama.active?(display_name = nil)` | Check if Xinerama is active |
|
|
149
|
+
| `Xinerama.version(display_name = nil)` | Get Xinerama version |
|
|
150
|
+
|
|
151
|
+
### Display
|
|
152
|
+
|
|
153
|
+
| Method | Description |
|
|
154
|
+
|--------|-------------|
|
|
155
|
+
| `#active?` | Boolean indicating Xinerama activation |
|
|
156
|
+
| `#available?` | Boolean indicating extension availability |
|
|
157
|
+
| `#screens` | Lazy enumerator of ScreenInfo objects |
|
|
158
|
+
| `#primary_screen` | First screen |
|
|
159
|
+
| `#screen_at(x, y)` | Screen containing the given point |
|
|
160
|
+
| `#screen_count` | Number of screens |
|
|
161
|
+
| `#total_dimensions` | `[width, height]` of virtual desktop |
|
|
162
|
+
| `#version` | Version object |
|
|
163
|
+
| `#extension` | Extension object with event/error bases |
|
|
164
|
+
| `#close` | Close display connection |
|
|
165
|
+
| `#closed?` | Check if connection is closed |
|
|
166
|
+
|
|
167
|
+
### ScreenInfo
|
|
168
|
+
|
|
169
|
+
| Method | Description |
|
|
170
|
+
|--------|-------------|
|
|
171
|
+
| `#screen_number` | Screen index |
|
|
172
|
+
| `#x`, `#y` | Origin coordinates |
|
|
173
|
+
| `#width`, `#height` | Dimensions |
|
|
174
|
+
| `#origin` | `[x, y]` |
|
|
175
|
+
| `#dimensions` | `[width, height]` |
|
|
176
|
+
| `#area` | `width * height` |
|
|
177
|
+
| `#aspect_ratio` | Rational width/height |
|
|
178
|
+
| `#contains?(x, y)` | Point containment check |
|
|
179
|
+
| `#to_h`, `#to_a` | Conversion methods |
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT License. See LICENSE.txt.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Xinerama
|
|
4
|
+
class Display
|
|
5
|
+
attr_reader :display_name
|
|
6
|
+
|
|
7
|
+
def initialize(display_pointer, display_name = nil)
|
|
8
|
+
@pointer = display_pointer
|
|
9
|
+
@display_name = display_name
|
|
10
|
+
@closed = false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def close
|
|
14
|
+
return if @closed
|
|
15
|
+
|
|
16
|
+
FFI.XCloseDisplay(@pointer)
|
|
17
|
+
@closed = true
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def closed?
|
|
22
|
+
@closed
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def extension
|
|
26
|
+
ensure_open!
|
|
27
|
+
@extension ||= query_extension
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def available?
|
|
31
|
+
ensure_open!
|
|
32
|
+
@available ||= check_availability
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def version
|
|
36
|
+
ensure_open!
|
|
37
|
+
@version ||= query_version
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def active?
|
|
41
|
+
ensure_open!
|
|
42
|
+
FFI.XineramaIsActive(@pointer)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def screens
|
|
46
|
+
ensure_open!
|
|
47
|
+
query_screens
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def primary_screen
|
|
51
|
+
screens.first
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def screen_count
|
|
55
|
+
screens.count
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def screen_at(x, y)
|
|
59
|
+
screens.find { |screen| screen.contains?(x, y) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def total_dimensions
|
|
63
|
+
all_screens = screens.to_a
|
|
64
|
+
return [0, 0] if all_screens.empty?
|
|
65
|
+
|
|
66
|
+
max_x = all_screens.map { |s| s.x + s.width }.max
|
|
67
|
+
max_y = all_screens.map { |s| s.y + s.height }.max
|
|
68
|
+
[max_x, max_y]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class << self
|
|
72
|
+
def open(display_name = nil)
|
|
73
|
+
display = connect(display_name)
|
|
74
|
+
return display unless block_given?
|
|
75
|
+
|
|
76
|
+
begin
|
|
77
|
+
yield display
|
|
78
|
+
ensure
|
|
79
|
+
display.close
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def connect(display_name)
|
|
86
|
+
pointer = FFI.XOpenDisplay(display_name)
|
|
87
|
+
raise DisplayError, "Cannot open display: #{display_name || '$DISPLAY'}" if pointer.null?
|
|
88
|
+
|
|
89
|
+
new(pointer, display_name)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def ensure_open!
|
|
96
|
+
raise DisplayError, "Display connection closed" if @closed
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def check_availability
|
|
100
|
+
event_base = ::FFI::MemoryPointer.new(:int)
|
|
101
|
+
error_base = ::FFI::MemoryPointer.new(:int)
|
|
102
|
+
FFI.XineramaQueryExtension(@pointer, event_base, error_base)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def query_extension
|
|
106
|
+
event_base = ::FFI::MemoryPointer.new(:int)
|
|
107
|
+
error_base = ::FFI::MemoryPointer.new(:int)
|
|
108
|
+
|
|
109
|
+
available = FFI.XineramaQueryExtension(@pointer, event_base, error_base)
|
|
110
|
+
raise NotAvailableError, "Xinerama extension not available" unless available
|
|
111
|
+
|
|
112
|
+
Extension.new(
|
|
113
|
+
event_base: event_base.read_int,
|
|
114
|
+
error_base: error_base.read_int
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def query_version
|
|
119
|
+
major = ::FFI::MemoryPointer.new(:int)
|
|
120
|
+
minor = ::FFI::MemoryPointer.new(:int)
|
|
121
|
+
|
|
122
|
+
status = FFI.XineramaQueryVersion(@pointer, major, minor)
|
|
123
|
+
raise NotAvailableError, "Cannot query Xinerama version" if status.zero?
|
|
124
|
+
|
|
125
|
+
Version.new(
|
|
126
|
+
major: major.read_int,
|
|
127
|
+
minor: minor.read_int
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def query_screens
|
|
132
|
+
count_ptr = ::FFI::MemoryPointer.new(:int)
|
|
133
|
+
screens_ptr = FFI.XineramaQueryScreens(@pointer, count_ptr)
|
|
134
|
+
count = count_ptr.read_int
|
|
135
|
+
|
|
136
|
+
return Enumerator.new { }.lazy if screens_ptr.null? || count.zero?
|
|
137
|
+
|
|
138
|
+
build_screens_enumerator(screens_ptr, count)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def build_screens_enumerator(screens_ptr, count)
|
|
142
|
+
Enumerator.new do |yielder|
|
|
143
|
+
begin
|
|
144
|
+
ScreenInfo.from_pointer(screens_ptr, count).each do |screen|
|
|
145
|
+
yielder << screen
|
|
146
|
+
end
|
|
147
|
+
ensure
|
|
148
|
+
FFI.XFree(screens_ptr) unless screens_ptr.null?
|
|
149
|
+
end
|
|
150
|
+
end.lazy
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Xinerama
|
|
4
|
+
class Extension
|
|
5
|
+
attr_reader :event_base, :error_base
|
|
6
|
+
|
|
7
|
+
def initialize(event_base:, error_base:)
|
|
8
|
+
@event_base = event_base
|
|
9
|
+
@error_base = error_base
|
|
10
|
+
freeze
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_h
|
|
14
|
+
{ event_base: event_base, error_base: error_base }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def inspect
|
|
18
|
+
"#<#{self.class} event_base=#{event_base} error_base=#{error_base}>"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
alias_method :to_s, :inspect
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Version
|
|
25
|
+
attr_reader :major, :minor
|
|
26
|
+
|
|
27
|
+
def initialize(major:, minor:)
|
|
28
|
+
@major = major
|
|
29
|
+
@minor = minor
|
|
30
|
+
freeze
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_s
|
|
34
|
+
"#{major}.#{minor}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_a
|
|
38
|
+
[major, minor]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_h
|
|
42
|
+
{ major: major, minor: minor }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def inspect
|
|
46
|
+
"#<#{self.class} #{self}>"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def <=>(other)
|
|
50
|
+
[major, minor] <=> [other.major, other.minor]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
include Comparable
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/xinerama/ffi.rb
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ffi"
|
|
4
|
+
|
|
5
|
+
module Xinerama
|
|
6
|
+
module FFI
|
|
7
|
+
extend ::FFI::Library
|
|
8
|
+
|
|
9
|
+
ffi_lib "X11"
|
|
10
|
+
ffi_lib "Xinerama"
|
|
11
|
+
|
|
12
|
+
# XineramaScreenInfo struct
|
|
13
|
+
# typedef struct {
|
|
14
|
+
# int screen_number;
|
|
15
|
+
# short x_org;
|
|
16
|
+
# short y_org;
|
|
17
|
+
# short width;
|
|
18
|
+
# short height;
|
|
19
|
+
# } XineramaScreenInfo;
|
|
20
|
+
class XineramaScreenInfoStruct < ::FFI::Struct
|
|
21
|
+
layout :screen_number, :int,
|
|
22
|
+
:x_org, :short,
|
|
23
|
+
:y_org, :short,
|
|
24
|
+
:width, :short,
|
|
25
|
+
:height, :short
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Display* XOpenDisplay(const char *display_name)
|
|
29
|
+
attach_function :XOpenDisplay, [:string], :pointer
|
|
30
|
+
|
|
31
|
+
# int XCloseDisplay(Display *display)
|
|
32
|
+
attach_function :XCloseDisplay, [:pointer], :int
|
|
33
|
+
|
|
34
|
+
# int XFree(void *data)
|
|
35
|
+
attach_function :XFree, [:pointer], :int
|
|
36
|
+
|
|
37
|
+
# Bool XineramaQueryExtension(Display *dpy, int *event_base_return, int *error_base_return)
|
|
38
|
+
attach_function :XineramaQueryExtension, [:pointer, :pointer, :pointer], :bool
|
|
39
|
+
|
|
40
|
+
# Status XineramaQueryVersion(Display *dpy, int *major_version_return, int *minor_version_return)
|
|
41
|
+
attach_function :XineramaQueryVersion, [:pointer, :pointer, :pointer], :int
|
|
42
|
+
|
|
43
|
+
# Bool XineramaIsActive(Display *dpy)
|
|
44
|
+
attach_function :XineramaIsActive, [:pointer], :bool
|
|
45
|
+
|
|
46
|
+
# XineramaScreenInfo* XineramaQueryScreens(Display *dpy, int *number)
|
|
47
|
+
attach_function :XineramaQueryScreens, [:pointer, :pointer], :pointer
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Xinerama
|
|
4
|
+
class ScreenInfo
|
|
5
|
+
attr_reader :screen_number, :x, :y, :width, :height
|
|
6
|
+
|
|
7
|
+
def initialize(screen_number:, x:, y:, width:, height:)
|
|
8
|
+
@screen_number = screen_number
|
|
9
|
+
@x = x
|
|
10
|
+
@y = y
|
|
11
|
+
@width = width
|
|
12
|
+
@height = height
|
|
13
|
+
freeze
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def origin
|
|
17
|
+
[x, y]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def dimensions
|
|
21
|
+
[width, height]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def area
|
|
25
|
+
width * height
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def aspect_ratio
|
|
29
|
+
Rational(width, height)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def contains?(point_x, point_y)
|
|
33
|
+
point_x >= x && point_x < (x + width) &&
|
|
34
|
+
point_y >= y && point_y < (y + height)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_h
|
|
38
|
+
{
|
|
39
|
+
screen_number: screen_number,
|
|
40
|
+
x: x,
|
|
41
|
+
y: y,
|
|
42
|
+
width: width,
|
|
43
|
+
height: height
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_a
|
|
48
|
+
[screen_number, x, y, width, height]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def inspect
|
|
52
|
+
"#<#{self.class} screen=#{screen_number} geometry=#{width}x#{height}+#{x}+#{y}>"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
alias_method :to_s, :inspect
|
|
56
|
+
|
|
57
|
+
def ==(other)
|
|
58
|
+
other.is_a?(ScreenInfo) &&
|
|
59
|
+
screen_number == other.screen_number &&
|
|
60
|
+
x == other.x &&
|
|
61
|
+
y == other.y &&
|
|
62
|
+
width == other.width &&
|
|
63
|
+
height == other.height
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
alias_method :eql?, :==
|
|
67
|
+
|
|
68
|
+
def hash
|
|
69
|
+
to_a.hash
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class << self
|
|
73
|
+
def from_struct(struct)
|
|
74
|
+
new(
|
|
75
|
+
screen_number: struct[:screen_number],
|
|
76
|
+
x: struct[:x_org],
|
|
77
|
+
y: struct[:y_org],
|
|
78
|
+
width: struct[:width],
|
|
79
|
+
height: struct[:height]
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def from_pointer(pointer, count)
|
|
84
|
+
Enumerator.new do |yielder|
|
|
85
|
+
count.times do |index|
|
|
86
|
+
offset = index * FFI::XineramaScreenInfoStruct.size
|
|
87
|
+
struct = FFI::XineramaScreenInfoStruct.new(pointer + offset)
|
|
88
|
+
yielder << from_struct(struct)
|
|
89
|
+
end
|
|
90
|
+
end.lazy
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/xinerama.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "xinerama/version"
|
|
4
|
+
require_relative "xinerama/ffi"
|
|
5
|
+
require_relative "xinerama/screen_info"
|
|
6
|
+
require_relative "xinerama/display"
|
|
7
|
+
require_relative "xinerama/extension"
|
|
8
|
+
|
|
9
|
+
module Xinerama
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
class NotAvailableError < Error; end
|
|
12
|
+
class DisplayError < Error; end
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
def open(display_name = nil, &block)
|
|
16
|
+
Display.open(display_name, &block)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def screens(display_name = nil)
|
|
20
|
+
Display.open(display_name, &:screens)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def active?(display_name = nil)
|
|
24
|
+
Display.open(display_name, &:active?)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def version(display_name = nil)
|
|
28
|
+
Display.open(display_name, &:version)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: xinerama
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nathan
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ffi
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.15'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.15'
|
|
26
|
+
description: Provides a clean Ruby interface to query Xinerama screen information
|
|
27
|
+
for multi-monitor X11 setups
|
|
28
|
+
email:
|
|
29
|
+
- nathankidd@hey.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- LICENSE.txt
|
|
35
|
+
- README.md
|
|
36
|
+
- lib/xinerama.rb
|
|
37
|
+
- lib/xinerama/display.rb
|
|
38
|
+
- lib/xinerama/extension.rb
|
|
39
|
+
- lib/xinerama/ffi.rb
|
|
40
|
+
- lib/xinerama/screen_info.rb
|
|
41
|
+
- lib/xinerama/version.rb
|
|
42
|
+
homepage: https://github.com/n-at-han-k/ruby-xinerama
|
|
43
|
+
licenses:
|
|
44
|
+
- MIT
|
|
45
|
+
metadata:
|
|
46
|
+
homepage_uri: https://github.com/n-at-han-k/ruby-xinerama
|
|
47
|
+
source_code_uri: https://github.com/n-at-han-k/ruby-xinerama
|
|
48
|
+
rdoc_options: []
|
|
49
|
+
require_paths:
|
|
50
|
+
- lib
|
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: 3.0.0
|
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
requirements: []
|
|
62
|
+
rubygems_version: 3.6.7
|
|
63
|
+
specification_version: 4
|
|
64
|
+
summary: Ruby FFI bindings for the Xinerama X11 extension
|
|
65
|
+
test_files: []
|