xlib-objects 0.1.0

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