xlib-objects 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d9c4da9ee32958e2f1e4e43b8835fdd448296e97
4
+ data.tar.gz: 785ff3eebe82631886e79e7dbb557c32b61bc6d0
5
+ SHA512:
6
+ metadata.gz: 14374fec2bf80b0107b7c6caff07e1c9a0d532a9378cd85a70a9bf41491526043f48dc82b6d9203467e93d1ff449f2d3ea002d77a040e1512d74887413110482
7
+ data.tar.gz: 2acfd879198ce45c68c55230eff53d429d4c40fe128fa7272c910b5ad59a1485dc1f2673f445db33ec3cec06f56ad44341c12566a120bbec464eca6bdb151e81
data/lib/atom.rb ADDED
@@ -0,0 +1,34 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ module XlibObj
10
+ class Atom
11
+ def initialize(display, atom)
12
+ @display = display
13
+ @to_native = if atom.is_a? Integer
14
+ atom
15
+ else
16
+ Xlib.XInternAtom(@display.to_native, atom.to_s, false)
17
+ end
18
+ end
19
+
20
+ attr_reader :to_native
21
+
22
+ def name
23
+ Xlib.XGetAtomName(@display.to_native, @to_native).to_sym
24
+ end
25
+
26
+ def exists?
27
+ @to_native != 0
28
+ end
29
+
30
+ def to_s
31
+ name.to_s
32
+ end
33
+ end
34
+ end
data/lib/display.rb ADDED
@@ -0,0 +1,73 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ require 'socket'
10
+
11
+ module XlibObj
12
+ class Display
13
+ class << self
14
+ def names
15
+ Dir['/tmp/.X11-unix/*'].map do |file_name|
16
+ match = file_name.match(/X(\d+)$/)
17
+ ":#{match[1]}" if match
18
+ end.compact
19
+ end
20
+ end
21
+
22
+ def initialize(name)
23
+ display_pointer = Xlib.XOpenDisplay(name)
24
+ raise ArgumentError, "Unknown display #{name}" if display_pointer.null?
25
+ @struct = Xlib::Display.new(display_pointer)
26
+ end
27
+
28
+ def to_native
29
+ @struct.pointer
30
+ end
31
+
32
+ def name
33
+ @struct[:display_name]
34
+ end
35
+
36
+ def socket
37
+ UNIXSocket.for_fd(Xlib.XConnectionNumber(to_native))
38
+ end
39
+
40
+ def screens
41
+ (0..@struct[:nscreens]-1).map{ |number| screen(number) }
42
+ end
43
+
44
+ def handle_events
45
+ handle_event(next_event) while pending_events > 0
46
+ end
47
+
48
+ private
49
+ def pending_events
50
+ Xlib.XPending(to_native)
51
+ end
52
+
53
+ def next_event
54
+ x_event = Xlib::XEvent.new
55
+ Xlib.XNextEvent(to_native, x_event) # blocks
56
+ Event.new(x_event)
57
+ end
58
+
59
+ def handle_event(event)
60
+ handling_window_id = event.event || event.parent || event.window
61
+ handling_window = Window.new(self, handling_window_id)
62
+ handling_window.handle(event)
63
+ end
64
+
65
+ def screen_pointer(number)
66
+ @struct[:screens] + number*Xlib::Screen.size
67
+ end
68
+
69
+ def screen(number)
70
+ Screen.new(self, screen_pointer(number))
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,63 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ module XlibObj
10
+ class Event
11
+ class ClientMessage
12
+ def initialize(type=nil, data=nil, subject=nil)
13
+ self.type = type
14
+ self.data = data
15
+ self.subject = subject
16
+ end
17
+
18
+ attr_writer :subject, :data, :type
19
+
20
+ def send_to(receiver)
21
+ raise "The client message needs a type at least." unless @type
22
+ @receiver = receiver
23
+ Xlib.XSendEvent(@receiver.display.to_native, @receiver.to_native, false,
24
+ Xlib::SubstructureNotifyMask | Xlib::SubstructureRedirectMask,
25
+ to_native)
26
+ Xlib.XFlush(@receiver.display.to_native)
27
+ end
28
+
29
+ private
30
+ def to_native
31
+ event = Xlib::XEvent.new
32
+ event[:xclient][:type] = Xlib::ClientMessage
33
+ event[:xclient][:message_type] = message_type
34
+ event[:xclient][:window] = (@subject || @receiver).to_native
35
+
36
+ if @data
37
+ event[:xclient][:format] = format
38
+ @data.each_with_index do |item, idx|
39
+ event[:xclient][:data][data_member][idx] = item
40
+ end
41
+ end
42
+
43
+ event.pointer
44
+ end
45
+
46
+ def message_type
47
+ Atom.new(@receiver.display, @type).to_native
48
+ end
49
+
50
+ def format
51
+ 160/@data.size
52
+ end
53
+
54
+ def data_member
55
+ case @data.size
56
+ when 5 then :l
57
+ when 10 then :s
58
+ else :b
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
data/lib/event.rb ADDED
@@ -0,0 +1,248 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ module XlibObj
10
+ class Event
11
+ def initialize(event)
12
+ @event = event
13
+ struct && (struct.members-[:type]).each do |key|
14
+ define_singleton_method(key) { struct[key] }
15
+ end
16
+ end
17
+
18
+ def name
19
+ @name ||= (x_name || xrr_name)
20
+ end
21
+
22
+ def method_missing(name)
23
+ nil
24
+ end
25
+
26
+ private
27
+ def type
28
+ @event[:type]
29
+ end
30
+
31
+ def struct
32
+ @struct ||= (xrr_struct || x_struct)
33
+ end
34
+
35
+ def union_member
36
+ self.class::TYPE_TO_UNION_MEMBER[type]
37
+ end
38
+
39
+ def x_struct
40
+ @event[union_member]
41
+ end
42
+
43
+ def xrr_type
44
+ type-self.class.xrr_type_offset(@event[:xany][:display])
45
+ end
46
+
47
+ def xrr_struct
48
+ xrr_subtype_struct || xrr_type_struct
49
+ end
50
+
51
+ def xrr_type_struct
52
+ struct = self.class::RR_TYPE_TO_STRUCT[xrr_type]
53
+ struct.new(@event.pointer) if struct
54
+ end
55
+
56
+ def xrr_subtype_struct
57
+ if xrr_type == Xlib::RRNotify
58
+ struct = self.class::RR_SUBTYPE_TO_STRUCT[xrr_type_struct[:subtype]]
59
+ struct.new(@event.pointer) if struct
60
+ end
61
+ end
62
+
63
+ def x_name
64
+ self.class::TYPE.key(type)
65
+ end
66
+
67
+ def xrr_name
68
+ xrr_subtype_name || xrr_type_name
69
+ end
70
+
71
+ def xrr_type_name
72
+ self.class::RR_TYPE.key(xrr_type)
73
+ end
74
+
75
+ def xrr_subtype_name
76
+ if xrr_type == Xlib::RRNotify
77
+ self.class::RR_SUBTYPE.key(xrr_type_struct[:subtype])
78
+ end
79
+ end
80
+ end
81
+
82
+ class Event
83
+ class << self
84
+ def xrr_type_offset(display)
85
+ @xrr_type_offset ||= {}
86
+ @xrr_type_offset[display] ||= (
87
+ rr_event_offset = FFI::MemoryPointer.new :int
88
+ rr_error_offset = FFI::MemoryPointer.new :int
89
+ Xlib.XRRQueryExtension(display, rr_event_offset, rr_error_offset)
90
+ rr_event_offset.read_int
91
+ )
92
+ end
93
+
94
+ def valid_name?(name)
95
+ TYPE[name] || RR_SUBTYPE[name] || RR_TYPE[name]
96
+ end
97
+ end
98
+
99
+ MASK = {
100
+ no_event: Xlib::NoEventMask,
101
+ key_press: Xlib::KeyPressMask,
102
+ key_release: Xlib::KeyReleaseMask,
103
+ button_press: Xlib::ButtonPressMask,
104
+ button_release: Xlib::ButtonReleaseMask,
105
+ enter_window: Xlib::EnterWindowMask,
106
+ leave_window: Xlib::LeaveWindowMask,
107
+ pointer_motion: Xlib::PointerMotionMask,
108
+ pointer_motion_hint: Xlib::PointerMotionHintMask,
109
+ button1_motion: Xlib::Button1MotionMask,
110
+ button2_motion: Xlib::Button2MotionMask,
111
+ button3_motion: Xlib::Button3MotionMask,
112
+ button4_motion: Xlib::Button4MotionMask,
113
+ button5_motion: Xlib::Button5MotionMask,
114
+ button_motion: Xlib::ButtonMotionMask,
115
+ keymap_state: Xlib::KeymapStateMask,
116
+ exposure: Xlib::ExposureMask,
117
+ visibility_change: Xlib::VisibilityChangeMask,
118
+ structure_notify: Xlib::StructureNotifyMask,
119
+ resize_redirect: Xlib::ResizeRedirectMask,
120
+ substructure_notify: Xlib::SubstructureNotifyMask,
121
+ substructure_redirect: Xlib::SubstructureRedirectMask,
122
+ focus_change: Xlib::FocusChangeMask,
123
+ property_change: Xlib::PropertyChangeMask,
124
+ colormap_change: Xlib::ColormapChangeMask,
125
+ owner_grab_button: Xlib::OwnerGrabButtonMask,
126
+ screen_change_notify: Xlib::RRScreenChangeNotifyMask,
127
+ crtc_change_notify: Xlib::RRCrtcChangeNotifyMask,
128
+ output_change_notify: Xlib::RROutputChangeNotifyMask,
129
+ output_property_notify: Xlib::RROutputPropertyNotifyMask,
130
+ provider_change_notify: Xlib::RRProviderChangeNotifyMask,
131
+ provider_property_notify: Xlib::RRProviderPropertyNotifyMask,
132
+ resource_change_notify: Xlib::RRResourceChangeNotifyMask
133
+ }
134
+
135
+ RR_MASK = {
136
+ screen_change_notify: Xlib::RRScreenChangeNotifyMask,
137
+ crtc_change_notify: Xlib::RRCrtcChangeNotifyMask,
138
+ output_change_notify: Xlib::RROutputChangeNotifyMask,
139
+ output_property_notify: Xlib::RROutputPropertyNotifyMask,
140
+ provider_change_notify: Xlib::RRProviderChangeNotifyMask,
141
+ provider_property_notify: Xlib::RRProviderPropertyNotifyMask,
142
+ resource_change_notify: Xlib::RRResourceChangeNotifyMask
143
+ }
144
+
145
+ TYPE = {
146
+ key_press: Xlib::KeyPress,
147
+ key_release: Xlib::KeyRelease,
148
+ button_press: Xlib::ButtonPress,
149
+ button_release: Xlib::ButtonRelease,
150
+ motion_notify: Xlib::MotionNotify,
151
+ enter_notify: Xlib::EnterNotify,
152
+ leave_notify: Xlib::LeaveNotify,
153
+ focus_in: Xlib::FocusIn,
154
+ focus_out: Xlib::FocusOut,
155
+ keymap_notify: Xlib::KeymapNotify,
156
+ expose: Xlib::Expose,
157
+ graphics_expose: Xlib::GraphicsExpose,
158
+ no_expose: Xlib::NoExpose,
159
+ visibility_notify: Xlib::VisibilityNotify,
160
+ create_notify: Xlib::CreateNotify,
161
+ destroy_notify: Xlib::DestroyNotify,
162
+ unmap_notify: Xlib::UnmapNotify,
163
+ map_notify: Xlib::MapNotify,
164
+ map_request: Xlib::MapRequest,
165
+ reparent_notify: Xlib::ReparentNotify,
166
+ configure_notify: Xlib::ConfigureNotify,
167
+ configure_request: Xlib::ConfigureRequest,
168
+ gravity_notify: Xlib::GravityNotify,
169
+ resize_request: Xlib::ResizeRequest,
170
+ circulate_notify: Xlib::CirculateNotify,
171
+ circulate_request: Xlib::CirculateRequest,
172
+ property_notify: Xlib::PropertyNotify,
173
+ selection_clear: Xlib::SelectionClear,
174
+ selection_request: Xlib::SelectionRequest,
175
+ selection_notify: Xlib::SelectionNotify,
176
+ colormap_notify: Xlib::ColormapNotify,
177
+ client_message: Xlib::ClientMessage,
178
+ mapping_notify: Xlib::MappingNotify,
179
+ generic_event: Xlib::GenericEvent,
180
+ last_event: Xlib::LASTEvent
181
+ }
182
+
183
+ RR_TYPE = {
184
+ screen_change_notify: Xlib::RRScreenChangeNotify,
185
+ notify: Xlib::RRNotify
186
+ }
187
+
188
+ RR_SUBTYPE = {
189
+ crtc_change_notify: Xlib::RRNotify_CrtcChange,
190
+ output_change_notify: Xlib::RRNotify_OutputChange,
191
+ output_property_notify: Xlib::RRNotify_OutputProperty,
192
+ provider_change_notify: Xlib::RRNotify_ProviderChange,
193
+ provider_property_notify: Xlib::RRNotify_ProviderProperty,
194
+ resource_change_notify: Xlib::RRNotify_ResourceChange
195
+ }
196
+
197
+ TYPE_TO_UNION_MEMBER = {
198
+ Xlib::KeyPress => :xkey,
199
+ Xlib::KeyRelease => :xkey,
200
+ Xlib::ButtonPress => :xbutton,
201
+ Xlib::ButtonRelease => :xbutton,
202
+ Xlib::MotionNotify => :xmotion,
203
+ Xlib::EnterNotify => :xcrossing,
204
+ Xlib::LeaveNotify => :xcrossing,
205
+ Xlib::FocusIn => :xfocus,
206
+ Xlib::FocusOut => :xfocus,
207
+ Xlib::KeymapNotify => :xkeymap,
208
+ Xlib::Expose => :xexpose,
209
+ Xlib::GraphicsExpose => :xgraphicsexpose,
210
+ Xlib::NoExpose => :xnoexpose,
211
+ Xlib::VisibilityNotify => :xvisibility,
212
+ Xlib::CreateNotify => :xcreatewindow,
213
+ Xlib::DestroyNotify => :xdestroywindow,
214
+ Xlib::UnmapNotify => :xunmap,
215
+ Xlib::MapNotify => :xmap,
216
+ Xlib::MapRequest => :xmaprequest,
217
+ Xlib::ReparentNotify => :xreparent,
218
+ Xlib::ConfigureNotify => :xconfigure,
219
+ Xlib::ConfigureRequest => :xconfigurerequest,
220
+ Xlib::GravityNotify => :xgravity,
221
+ Xlib::ResizeRequest => :xresizerequest,
222
+ Xlib::CirculateNotify => :xcirculate,
223
+ Xlib::CirculateRequest => :xcirculaterequest,
224
+ Xlib::PropertyNotify => :xproperty,
225
+ Xlib::SelectionClear => :xselectionclear,
226
+ Xlib::SelectionRequest => :xselectionrequest,
227
+ Xlib::SelectionNotify => :xselection,
228
+ Xlib::ColormapNotify => :xcolormap,
229
+ Xlib::ClientMessage => :xclient,
230
+ Xlib::MappingNotify => :xmapping,
231
+ Xlib::GenericEvent => :xgeneric
232
+ }
233
+
234
+ RR_TYPE_TO_STRUCT = {
235
+ Xlib::RRScreenChangeNotify => Xlib::XRRScreenChangeNotifyEvent,
236
+ Xlib::RRNotify => Xlib::XRRNotifyEvent
237
+ }
238
+
239
+ RR_SUBTYPE_TO_STRUCT = {
240
+ Xlib::RRNotify_CrtcChange => Xlib::XRRCrtcChangeNotifyEvent,
241
+ Xlib::RRNotify_OutputChange => Xlib::XRROutputChangeNotifyEvent,
242
+ Xlib::RRNotify_OutputProperty => Xlib::XRROutputPropertyNotifyEvent,
243
+ Xlib::RRNotify_ProviderChange => Xlib::XRRProviderChangeNotifyEvent,
244
+ Xlib::RRNotify_ProviderProperty => Xlib::XRRProviderPropertyNotifyEvent,
245
+ Xlib::RRNotify_ResourceChange => Xlib::XRRResourceChangeNotifyEvent
246
+ }
247
+ end
248
+ end
@@ -0,0 +1,49 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ module XlibObj
10
+ class Screen
11
+ class Crtc
12
+ class Output
13
+ def initialize(crtc, id)
14
+ @crtc = crtc
15
+ @id = id
16
+
17
+ ObjectSpace.define_finalizer(self) do
18
+ Xlib.XRRFreeOutputInfo(@attributes.pointer) if @attributes
19
+ end
20
+ end
21
+
22
+ attr_reader :crtc, :id
23
+
24
+ def attribute(attribute)
25
+ return unless attributes.layout.members.include? attribute.to_sym
26
+ attributes[attribute.to_sym]
27
+ end
28
+
29
+ def method_missing(name)
30
+ attribute(name)
31
+ end
32
+
33
+ private
34
+ def attributes
35
+ unless @attributes
36
+ screen_resources_ptr = Xlib.XRRGetScreenResources(@crtc.screen.
37
+ display.to_native, @crtc.screen.root_window.to_native)
38
+ output_info_ptr = Xlib.XRRGetOutputInfo(@crtc.screen.display.
39
+ to_native, screen_resources_ptr, @id)
40
+ @attributes = Xlib::XRROutputInfo.new(output_info_ptr)
41
+ Xlib.XRRFreeScreenResources(screen_resources_ptr)
42
+ end
43
+
44
+ @attributes
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,63 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ module XlibObj
10
+ class Screen
11
+ class Crtc
12
+ def initialize(screen, id)
13
+ @screen = screen
14
+ @id = id
15
+
16
+ ObjectSpace.define_finalizer(self) do
17
+ Xlib.XRRFreeCrtcInfo(@attributes.pointer) if @attributes
18
+ end
19
+ end
20
+
21
+ attr_reader :screen, :id
22
+
23
+ def attribute(attribute)
24
+ return unless attributes.layout.members.include? attribute.to_sym
25
+ attributes[attribute.to_sym]
26
+ end
27
+
28
+ def method_missing(name)
29
+ attribute(name)
30
+ end
31
+
32
+ def outputs
33
+ (0..attribute(:noutput)-1).map do |output_number|
34
+ output_id(attribute(:outputs), output_number)
35
+ end.map do |output_id|
36
+ Output.new(self, output_id)
37
+ end
38
+ end
39
+
40
+ private
41
+ def attributes
42
+ unless @attributes
43
+ screen_resources_ptr = Xlib.XRRGetScreenResources(@screen.
44
+ display.to_native, @screen.root_window.to_native)
45
+ crtc_info_ptr = Xlib.XRRGetCrtcInfo(@screen.display.to_native,
46
+ screen_resources_ptr, @id)
47
+ @attributes = Xlib::XRRCrtcInfo.new(crtc_info_ptr)
48
+ Xlib.XRRFreeScreenResources(screen_resources_ptr)
49
+ end
50
+
51
+ @attributes
52
+ end
53
+
54
+ def read_item(pointer, item_pos, item_size)
55
+ (pointer + item_pos*item_size).read_ulong
56
+ end
57
+
58
+ def output_id(pointer, number)
59
+ read_item(pointer, number, FFI.type_size(:RROutput))
60
+ end
61
+ end
62
+ end
63
+ end
data/lib/screen.rb ADDED
@@ -0,0 +1,70 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ module XlibObj
10
+ class Screen
11
+ def initialize(display, screen_pointer)
12
+ @display = display
13
+ @struct = Xlib::Screen.new(screen_pointer)
14
+
15
+ ObjectSpace.define_finalizer(self) do
16
+ Xlib.XRRFreeScreenResources(@resources.pointer) if @resources
17
+ end
18
+ end
19
+
20
+ attr_reader :display
21
+
22
+ def to_native
23
+ @struct.pointer
24
+ end
25
+
26
+ def attribute(name)
27
+ return unless @struct.layout.members.include? name.to_sym
28
+ @struct[name]
29
+ end
30
+
31
+ def method_missing(name)
32
+ attribute(name)
33
+ end
34
+
35
+ def number
36
+ Xlib.XScreenNumberOfScreen(to_native)
37
+ end
38
+
39
+ def root_window
40
+ @root_window ||= Window.new(@display, Xlib.XRootWindowOfScreen(to_native))
41
+ end
42
+
43
+ def crtcs
44
+ (0..resources[:ncrtc]-1).map do |crtc_number|
45
+ crtc_id(resources[:crtcs], crtc_number)
46
+ end.map do |crtc_id|
47
+ Crtc.new(self, crtc_id)
48
+ end
49
+ end
50
+
51
+ private
52
+ def resources
53
+ unless @resources
54
+ resources_ptr = Xlib.XRRGetScreenResources(@display.to_native,
55
+ root_window.to_native)
56
+ @resources = Xlib::XRRScreenResources.new(resources_ptr)
57
+ end
58
+
59
+ @resources
60
+ end
61
+
62
+ def read_item(pointer, item_pos, item_size)
63
+ (pointer + item_pos*item_size).read_ulong
64
+ end
65
+
66
+ def crtc_id(pointer, number)
67
+ read_item(pointer, number, FFI.type_size(:RRCrtc))
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,118 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ module XlibObj
10
+ class Window
11
+ class EventHandler
12
+ class << self
13
+ def singleton(display, window_id)
14
+ @handlers ||= {}
15
+ @handlers[display] ||= {}
16
+ @handlers[display][window_id] ||= new(display, window_id)
17
+ end
18
+ end
19
+
20
+ def initialize(display, window_id)
21
+ @display = display
22
+ @window_id = window_id
23
+ @event_handlers = {}
24
+ @event_mask = 0
25
+ @rr_event_mask = 0
26
+ end
27
+
28
+ def on(mask, event, &handler)
29
+ add_event_mask(mask)
30
+ add_event_handler(mask, event, &handler)
31
+ end
32
+
33
+ def off(mask, type, handler)
34
+ remove_event_handler(mask, type, handler)
35
+ remove_event_mask(mask)
36
+ end
37
+
38
+ def handle(event)
39
+ if @event_handlers[event.name]
40
+ @event_handlers[event.name].each do |_, handlers|
41
+ handlers.each{ |handler| handler.call(event) }
42
+ end
43
+ true
44
+ else
45
+ false
46
+ end
47
+ end
48
+
49
+ private
50
+ def add_event_mask(mask)
51
+ check_mask(mask)
52
+ return if mask_in_use?(mask)
53
+ @event_mask |= normalize_mask(mask)
54
+ @rr_event_mask |= normalize_rr_mask(mask)
55
+ select_events
56
+ end
57
+
58
+ def remove_event_mask(mask)
59
+ check_mask(mask)
60
+ return if mask_in_use?(mask)
61
+ return unless mask_selected?(mask)
62
+ @event_mask &= ~normalize_mask(mask)
63
+ @rr_event_mask &= ~normalize_rr_mask(mask)
64
+ select_events
65
+ end
66
+
67
+ def add_event_handler(mask, event, &handler)
68
+ check_event(event)
69
+ @event_handlers[event] ||= {}
70
+ @event_handlers[event][mask] ||= []
71
+ @event_handlers[event][mask] << handler
72
+ handler
73
+ end
74
+
75
+ def remove_event_handler(mask, event, handler)
76
+ check_event(event)
77
+ return unless mask_in_use?(mask)
78
+ return unless @event_handlers[event]
79
+ @event_handlers[event][mask].delete(handler)
80
+ @event_handlers[event].delete(mask) if @event_handlers[event][mask].empty?
81
+ @event_handlers.delete(event) if @event_handlers[event].empty?
82
+ end
83
+
84
+ def mask_in_use?(mask)
85
+ @event_handlers.select{ |_, handlers| handlers.has_key?(mask) }.any?
86
+ end
87
+
88
+ def mask_selected?(mask)
89
+ (@event_mask & ~normalize_mask(mask) != @event_mask) or
90
+ (@rr_event_mask & ~normalize_rr_mask(mask) != @rr_event_mask)
91
+ end
92
+
93
+ def check_mask(mask)
94
+ if normalize_mask(mask).zero? && normalize_rr_mask(mask).zero?
95
+ raise("Unknown event mask #{mask}.")
96
+ end
97
+ end
98
+
99
+ def normalize_mask(mask)
100
+ XlibObj::Event::MASK[mask] || 0
101
+ end
102
+
103
+ def normalize_rr_mask(mask)
104
+ XlibObj::Event::RR_MASK[mask] || 0
105
+ end
106
+
107
+ def check_event(event)
108
+ XlibObj::Event.valid_name?(event) || raise("Unknown event #{event}.")
109
+ end
110
+
111
+ def select_events
112
+ Xlib.XSelectInput(@display.to_native, @window_id, @event_mask)
113
+ Xlib.XRRSelectInput(@display.to_native, @window_id, @rr_event_mask)
114
+ Xlib.XFlush(@display.to_native)
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,177 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ module XlibObj
10
+ class Window
11
+ class Property
12
+ def initialize(window, name)
13
+ @window = window
14
+ @name = name
15
+ @atom = Atom.new(window.display, name)
16
+ end
17
+
18
+ def get
19
+ return unless @atom.exists?
20
+
21
+ data_offset = 0
22
+ data_max_length = 2**16 # multiple of 32 bit
23
+ allow_deleted = false # don't retrieve deleted properties
24
+ requested_type = Xlib::AnyPropertyType
25
+
26
+ # response data
27
+ pointer = FFI::MemoryPointer.new :pointer
28
+ item_type = FFI::MemoryPointer.new :Atom
29
+ item_width = FFI::MemoryPointer.new :int
30
+ item_count = FFI::MemoryPointer.new :ulong
31
+ cutoff_data = FFI::MemoryPointer.new :ulong
32
+
33
+ # do and validate the request
34
+ status = Xlib.XGetWindowProperty(
35
+ @window.display.to_native, @window.to_native, @atom.to_native,
36
+ data_offset, data_max_length, allow_deleted, requested_type,
37
+ item_type, item_width, item_count, cutoff_data, pointer
38
+ )
39
+
40
+ return unless status == 0
41
+
42
+ return if pointer.read_pointer.null? # property is not set for the window
43
+
44
+ # extract response data
45
+ item_type = Atom.new(@window.display, item_type.read_int).name
46
+ item_width = item_width.read_int
47
+ item_count = item_count.read_int
48
+
49
+ return if item_count == 0
50
+
51
+ # get the property's value
52
+ bytes = read_bytes(pointer, item_width, item_count)
53
+ items = bytes_to_items(bytes, item_type, item_count)
54
+
55
+ return if items.empty?
56
+
57
+ items_to_objects(items, item_type)
58
+ end
59
+
60
+ def set(value, type = nil)
61
+ objects = value.is_a?(Array) ? value : [value]
62
+ item_type = type || type_from_objects(objects)
63
+ items = objects_to_items(objects, item_type)
64
+ item_width = width_from_type(item_type)
65
+ bytes = items_to_bytes(items, item_type)
66
+ item_count = item_type[-6..-1] == 'STRING' ? bytes.size : items.size
67
+
68
+ Xlib.XChangeProperty(
69
+ @window.display.to_native, @window.to_native, @atom.to_native,
70
+ Atom.new(@window.display, item_type).to_native, item_width,
71
+ Xlib::PropModeReplace, bytes, item_count)
72
+ Xlib.XFlush(@window.display.to_native)
73
+ self
74
+ end
75
+
76
+ private
77
+ def read_bytes(pointer, width, count)
78
+ pointer.read_pointer.read_string(count*native_width(width))
79
+ end
80
+
81
+ def native_width(width)
82
+ # translate word size to platform dependent bit count
83
+ FFI.type_size({ 8 => :char, 16 => :short, 32 => :long }[width])
84
+ end
85
+
86
+ def width(native_width)
87
+ { char: 8, short: 16, long: 32 }[native_width]
88
+ end
89
+
90
+ def bytes_to_items(bytes, type, item_count)
91
+ if [:STRING, :UTF8_STRING].include? type
92
+ bytes.split("\0")
93
+ else
94
+ bytes.unpack(format(type) * item_count)
95
+ end
96
+ end
97
+
98
+ def items_to_bytes(items, type)
99
+ items.pack(format(type) * items.size)
100
+ end
101
+
102
+ def format(type)
103
+ case type
104
+ when :INTEGER then 'i!'
105
+ when :CARDINAL then 'L!'
106
+ when :ATOM then 'L!'
107
+ when :WINDOW then 'L!'
108
+ when :STRING then 'Z*'
109
+ when :UTF8_STRING then 'Z*'
110
+ else 'a'
111
+ end
112
+ end
113
+
114
+ def items_to_objects(items, type)
115
+ transform = case type
116
+ when :UTF8_STRING
117
+ Proc.new{ |s| s.force_encoding('UTF-8') }
118
+ when :ATOM
119
+ Proc.new{ |a| Atom.new(@window.display, a) }
120
+ when :WINDOW
121
+ Proc.new{ |w| Window.new(@window.display, w) }
122
+ else
123
+ Proc.new{ |a| a }
124
+ end
125
+
126
+ items.map(&transform)
127
+ end
128
+
129
+ def objects_to_items(objects, type)
130
+ transform = case type
131
+ when :ATOM
132
+ Proc.new{ |a| a.to_native }
133
+ when :WINDOW
134
+ Proc.new{ |w| w.to_native }
135
+ else
136
+ Proc.new{ |a| a }
137
+ end
138
+
139
+ objects.map(&transform)
140
+ end
141
+
142
+ def type_from_objects(objects)
143
+ if objects.all? { |o| o.is_a? Window }
144
+ :WINDOW
145
+ elsif objects.all? { |o| o.is_a? Atom }
146
+ :ATOM
147
+ elsif objects.all? { |o| o.is_a? Integer }
148
+ if objects.any? { |o| o <= 0 }
149
+ :INTEGER
150
+ else
151
+ :CARDINAL
152
+ end
153
+ elsif objects.all? { |o| o.is_a? String }
154
+ if objects.any? { |o| o.encoding == Encoding::UTF_8 }
155
+ :UTF8_STRING
156
+ else
157
+ :STRING
158
+ end
159
+ else
160
+ :BYTES
161
+ end
162
+ end
163
+
164
+ def width_from_type(type)
165
+ case type
166
+ when :INTEGER then 16
167
+ when :CARDINAL then 32
168
+ when :ATOM then 32
169
+ when :WINDOW then 32
170
+ when :STRING then 8
171
+ when :UTF8_STRING then 8
172
+ else 8
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
data/lib/window.rb ADDED
@@ -0,0 +1,108 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ module XlibObj
10
+ class Window
11
+ def initialize(display, window_id)
12
+ @display = display
13
+ @to_native = window_id
14
+ @event_handler = EventHandler.singleton(display, window_id)
15
+ end
16
+
17
+ # Queries
18
+ attr_reader :display, :to_native
19
+ alias_method :id, :to_native
20
+
21
+ def attribute(name)
22
+ attributes = Xlib::WindowAttributes.new
23
+ Xlib.XGetWindowAttributes(@display.to_native, @to_native, attributes.
24
+ pointer)
25
+ attributes[name.to_sym]
26
+ rescue
27
+ nil
28
+ end
29
+
30
+ def method_missing(name)
31
+ attribute(name)
32
+ end
33
+
34
+ def screen
35
+ Screen.new(@display, attribute(:screen))
36
+ end
37
+
38
+ def property(name)
39
+ Property.new(self, name).get
40
+ end
41
+
42
+ def set_property(name, value)
43
+ Property.new(self, name).set(value)
44
+ end
45
+
46
+ def absolute_position
47
+ x_abs = FFI::MemoryPointer.new :int
48
+ y_abs = FFI::MemoryPointer.new :int
49
+ child = FFI::MemoryPointer.new :Window
50
+ root_win = screen.root_window
51
+
52
+ Xlib.XTranslateCoordinates(@display.to_native, @to_native,
53
+ root_win.to_native, 0, 0, x_abs, y_abs, child)
54
+
55
+ { x: x_abs.read_int, y: y_abs.read_int }
56
+ end
57
+
58
+ # Commands
59
+ def move_resize(x, y, width, height)
60
+ Xlib.XMoveResizeWindow(@display.to_native, @to_native, x, y, width,
61
+ height)
62
+ Xlib.XFlush(@display.to_native)
63
+ self
64
+ end
65
+
66
+ def map
67
+ Xlib.XMapWindow(@display.to_native, @to_native)
68
+ Xlib.XFlush(@display.to_native)
69
+ self
70
+ end
71
+
72
+ def unmap
73
+ Xlib.XUnmapWindow(@display.to_native, @to_native)
74
+ Xlib.XFlush(@display.to_native)
75
+ self
76
+ end
77
+
78
+ def iconify
79
+ Xlib.XIconifyWindow(@display.to_native, @to_native, screen.number)
80
+ Xlib.XFlush(@display.to_native)
81
+ self
82
+ end
83
+
84
+ def raise
85
+ Xlib.XRaiseWindow(@display.to_native, @to_native)
86
+ Xlib.XFlush(@display.to_native)
87
+ self
88
+ end
89
+
90
+ def on(mask, type, &callback)
91
+ @event_handler.on(mask, type, &callback)
92
+ end
93
+
94
+ def off(mask, type, callback)
95
+ @event_handler.off(mask, type, callback)
96
+ self
97
+ end
98
+
99
+ def handle(event)
100
+ @event_handler.handle(event)
101
+ self
102
+ end
103
+
104
+ def send_to_itself(type, data = nil, subject = nil)
105
+ Event::ClientMessage.new(type, data, subject).send_to(self)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,22 @@
1
+ #
2
+ # Copyright (c) 2014 Christopher Aue <mail@christopheraue.net>
3
+ #
4
+ # This file is part of the ruby xlib-objects gem. It is subject to the license
5
+ # terms in the LICENSE file found in the top-level directory of this
6
+ # distribution and at http://github.com/christopheraue/ruby-xlib-objects.
7
+ #
8
+
9
+ require 'xlib'
10
+
11
+ module XlibObj; end
12
+
13
+ require_relative 'atom'
14
+ require_relative 'display'
15
+ require_relative 'event'
16
+ require_relative 'event/client_message'
17
+ require_relative 'screen'
18
+ require_relative 'screen/crtc'
19
+ require_relative 'screen/crtc/output'
20
+ require_relative 'window'
21
+ require_relative 'window/property'
22
+ require_relative 'window/event_handler'
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xlib-objects
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Christopher Aue
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: xlib
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-its
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-mocks-matchers-send_message
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.1'
69
+ description: Ruby bindings for X11
70
+ email: mail@christopheraue.net
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - lib/atom.rb
76
+ - lib/display.rb
77
+ - lib/event.rb
78
+ - lib/event/client_message.rb
79
+ - lib/screen.rb
80
+ - lib/screen/crtc.rb
81
+ - lib/screen/crtc/output.rb
82
+ - lib/window.rb
83
+ - lib/window/event_handler.rb
84
+ - lib/window/property.rb
85
+ - lib/xlib-objects.rb
86
+ homepage: https://github.com/christopheraue/ruby-xlib-objects
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.2.2
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: A light object wrapper around xlib
110
+ test_files: []