xlib-objects 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/display.rb +34 -23
- data/lib/error.rb +29 -57
- data/lib/event.rb +18 -226
- data/lib/event/mask.rb +17 -0
- data/lib/extension.rb +85 -0
- data/lib/extension/core.rb +33 -0
- data/lib/extension/core/event.rb +139 -0
- data/lib/extension/xi.rb +9 -0
- data/lib/extension/xi/event.rb +109 -0
- data/lib/extension/xrr.rb +17 -0
- data/lib/extension/xrr/event.rb +76 -0
- data/lib/input_device.rb +89 -0
- data/lib/window.rb +8 -1
- data/lib/window/event_handler.rb +32 -74
- data/lib/xlib-objects.rb +5 -12
- data/lib/xlib/x.rb +57 -0
- data/lib/xlib/xi.rb +60 -0
- data/lib/xlib/xi/event_mask.rb +32 -0
- data/lib/xlib/xrr.rb +10 -0
- metadata +31 -4
data/lib/input_device.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 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 InputDevice
|
11
|
+
def initialize(display, device_id)
|
12
|
+
@display = display
|
13
|
+
@device_id = device_id
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :display
|
17
|
+
|
18
|
+
def to_native
|
19
|
+
@device_id
|
20
|
+
end
|
21
|
+
alias_method :id, :to_native
|
22
|
+
|
23
|
+
def name
|
24
|
+
device_info(:name, &:read_string)
|
25
|
+
end
|
26
|
+
|
27
|
+
def master?
|
28
|
+
[Xlib::XIMasterPointer, Xlib::XIMasterKeyboard].include? device_info(:use)
|
29
|
+
end
|
30
|
+
|
31
|
+
def slave?
|
32
|
+
[Xlib::XISlavePointer, Xlib::XISlaveKeyboard].include? device_info(:use)
|
33
|
+
end
|
34
|
+
|
35
|
+
def floating?
|
36
|
+
device_info(:use) == Xlib::XIFloatingSlave
|
37
|
+
end
|
38
|
+
|
39
|
+
def pointer?
|
40
|
+
[Xlib::XIMasterPointer, Xlib::XISlavePointer].include? device_info(:use)
|
41
|
+
end
|
42
|
+
|
43
|
+
def keyboard?
|
44
|
+
[Xlib::XIMasterKeyboard, Xlib::XISlaveKeyboard].include? device_info(:use)
|
45
|
+
end
|
46
|
+
|
47
|
+
def master
|
48
|
+
self.class.new(@display, device_info(:attachment)) if slave?
|
49
|
+
end
|
50
|
+
|
51
|
+
def slaves
|
52
|
+
@display.input_devices.select{ |device| device.master == self }
|
53
|
+
end
|
54
|
+
|
55
|
+
def enabled?
|
56
|
+
device_info(:enabled)
|
57
|
+
end
|
58
|
+
|
59
|
+
def disabled?
|
60
|
+
not enabled?
|
61
|
+
end
|
62
|
+
|
63
|
+
def focus(window = nil)
|
64
|
+
if window
|
65
|
+
Xlib::XI.set_focus(@display, self, window, Xlib::CurrentTime)
|
66
|
+
else
|
67
|
+
Xlib::XI.get_focus(@display, self) if keyboard?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def grab(report_to:, cursor: Xlib::None, mode: Xlib::GrabModeAsync, pair_mode: Xlib::GrabModeAsync,
|
72
|
+
owner_events: true, event_mask: 0)
|
73
|
+
Xlib::XI.grab_device(@display, self, report_to, Xlib::CurrentTime, cursor, mode, pair_mode, owner_events, event_mask)
|
74
|
+
end
|
75
|
+
|
76
|
+
def ungrab
|
77
|
+
Xlib::XI.ungrab_device(@display, self, Xlib::CurrentTime)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def device_info(member, &do_with_member)
|
83
|
+
device_info = Xlib::XI.query_device(@display, @device_id).first
|
84
|
+
do_with_member ? yield(device_info[member]) : device_info[member]
|
85
|
+
ensure
|
86
|
+
Xlib::XI.free_device_info(device_info)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/window.rb
CHANGED
@@ -11,7 +11,7 @@ module XlibObj
|
|
11
11
|
def initialize(display, window_id)
|
12
12
|
@display = display
|
13
13
|
@to_native = window_id
|
14
|
-
@event_handler = EventHandler.singleton(display,
|
14
|
+
@event_handler = EventHandler.singleton(display, self)
|
15
15
|
end
|
16
16
|
|
17
17
|
# Queries
|
@@ -206,6 +206,13 @@ module XlibObj
|
|
206
206
|
Window.new(@display, win_id)
|
207
207
|
end
|
208
208
|
|
209
|
+
def create_input_window
|
210
|
+
attributes = Xlib::SetWindowAttributes.new
|
211
|
+
win_id = Xlib.XCreateWindow(@display.to_native, to_native, 0, 0, 1, 1, 0, Xlib::CopyFromParent,
|
212
|
+
Xlib::InputOnly, nil, 0, attributes.pointer)
|
213
|
+
Window.new(@display, win_id)
|
214
|
+
end
|
215
|
+
|
209
216
|
def destroy
|
210
217
|
@event_handler.destroy
|
211
218
|
Xlib.XDestroyWindow(@display.to_native, to_native)
|
data/lib/window/event_handler.rb
CHANGED
@@ -9,117 +9,75 @@
|
|
9
9
|
module XlibObj
|
10
10
|
class Window
|
11
11
|
class EventHandler
|
12
|
+
@instances = {}
|
13
|
+
|
12
14
|
class << self
|
13
|
-
def singleton(display,
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@handlers[display][window_id] ||= new(display, window_id)
|
15
|
+
def singleton(display, window)
|
16
|
+
@instances[display] ||= {}
|
17
|
+
@instances[display][window.id] ||= new(display, window)
|
17
18
|
end
|
18
19
|
|
19
|
-
def remove(display,
|
20
|
-
@
|
20
|
+
def remove(display, window)
|
21
|
+
@instances[display].delete(window.id) if @instances[display]
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
|
-
def initialize(display,
|
25
|
+
def initialize(display, window)
|
25
26
|
@display = display
|
26
|
-
@
|
27
|
+
@window = window
|
27
28
|
@event_handlers = {}
|
28
|
-
@event_mask = 0
|
29
|
-
@rr_event_mask = 0
|
30
29
|
end
|
31
30
|
|
31
|
+
attr_reader :display, :window
|
32
|
+
|
32
33
|
def on(mask, event, &handler)
|
33
34
|
add_event_mask(mask)
|
34
35
|
add_event_handler(mask, event, &handler)
|
35
36
|
end
|
36
37
|
|
37
|
-
def off(mask,
|
38
|
-
remove_event_handler(mask,
|
38
|
+
def off(mask, event, handler = nil)
|
39
|
+
remove_event_handler(mask, event, handler)
|
39
40
|
remove_event_mask(mask)
|
40
41
|
end
|
41
42
|
|
42
43
|
def handle(event)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
true
|
48
|
-
else
|
49
|
-
false
|
44
|
+
@event_handlers.each do |mask, handlers|
|
45
|
+
next unless handlers[event.name]
|
46
|
+
handlers[event.name].each{ |handler| handler.call(event) }
|
50
47
|
end
|
51
48
|
end
|
52
49
|
|
53
50
|
def destroy
|
54
|
-
self.class.remove(@display, @
|
51
|
+
self.class.remove(@display, @window)
|
55
52
|
end
|
56
53
|
|
57
54
|
private
|
55
|
+
|
58
56
|
def add_event_mask(mask)
|
59
|
-
|
60
|
-
|
61
|
-
@
|
62
|
-
@
|
63
|
-
|
57
|
+
return if @event_handlers[mask]
|
58
|
+
|
59
|
+
@event_handlers[mask] = {}
|
60
|
+
extension = @display.extensions.find{ |ext| ext.handles_event_mask?(mask) }
|
61
|
+
extension.select_mask(@window, mask)
|
64
62
|
end
|
65
63
|
|
66
64
|
def remove_event_mask(mask)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@
|
71
|
-
@
|
72
|
-
select_events
|
65
|
+
return unless @event_handlers[mask].empty?
|
66
|
+
|
67
|
+
extension = @display.extensions.find{ |ext| ext.handles_event_mask?(mask) }
|
68
|
+
extension.deselect_mask(@window, mask)
|
69
|
+
@event_handlers.delete(mask)
|
73
70
|
end
|
74
71
|
|
75
72
|
def add_event_handler(mask, event, &handler)
|
76
|
-
|
77
|
-
@event_handlers[event]
|
78
|
-
@event_handlers[event][mask] ||= []
|
79
|
-
@event_handlers[event][mask] << handler
|
73
|
+
@event_handlers[mask][event] ||= []
|
74
|
+
@event_handlers[mask][event] << handler
|
80
75
|
handler
|
81
76
|
end
|
82
77
|
|
83
78
|
def remove_event_handler(mask, event, handler)
|
84
|
-
|
85
|
-
|
86
|
-
return unless @event_handlers[event]
|
87
|
-
@event_handlers[event][mask].delete(handler) if handler
|
88
|
-
@event_handlers[event].delete(mask) if @event_handlers[event][mask].empty? or handler.nil?
|
89
|
-
@event_handlers.delete(event) if @event_handlers[event].empty?
|
90
|
-
end
|
91
|
-
|
92
|
-
def mask_in_use?(mask)
|
93
|
-
@event_handlers.select{ |_, handlers| handlers.has_key?(mask) }.any?
|
94
|
-
end
|
95
|
-
|
96
|
-
def mask_selected?(mask)
|
97
|
-
(@event_mask & ~normalize_mask(mask) != @event_mask) or
|
98
|
-
(@rr_event_mask & ~normalize_rr_mask(mask) != @rr_event_mask)
|
99
|
-
end
|
100
|
-
|
101
|
-
def check_mask(mask)
|
102
|
-
if XlibObj::Event::MASK[mask].nil? && XlibObj::Event::RR_MASK[mask].nil?
|
103
|
-
raise("Unknown event mask #{mask}.")
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def normalize_mask(mask)
|
108
|
-
XlibObj::Event::MASK[mask] || 0
|
109
|
-
end
|
110
|
-
|
111
|
-
def normalize_rr_mask(mask)
|
112
|
-
XlibObj::Event::RR_MASK[mask] || 0
|
113
|
-
end
|
114
|
-
|
115
|
-
def check_event(event)
|
116
|
-
XlibObj::Event.valid_name?(event) || raise("Unknown event #{event}.")
|
117
|
-
end
|
118
|
-
|
119
|
-
def select_events
|
120
|
-
Xlib.XSelectInput(@display.to_native, @window_id, @event_mask)
|
121
|
-
Xlib.XRRSelectInput(@display.to_native, @window_id, @rr_event_mask)
|
122
|
-
@display.flush
|
79
|
+
@event_handlers[mask][event].delete(handler)
|
80
|
+
@event_handlers[mask].delete(event) if @event_handlers[mask][event].empty?
|
123
81
|
end
|
124
82
|
end
|
125
83
|
end
|
data/lib/xlib-objects.rb
CHANGED
@@ -7,18 +7,11 @@
|
|
7
7
|
#
|
8
8
|
|
9
9
|
require 'xlib'
|
10
|
+
require 'xlib/xinput2'
|
10
11
|
|
11
12
|
module XlibObj; end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
require_relative 'event/client_message'
|
18
|
-
require_relative 'event/selection_notify'
|
19
|
-
require_relative 'screen'
|
20
|
-
require_relative 'screen/crtc'
|
21
|
-
require_relative 'screen/crtc/output'
|
22
|
-
require_relative 'window'
|
23
|
-
require_relative 'window/property'
|
24
|
-
require_relative 'window/event_handler'
|
14
|
+
lib_dir = File.dirname __FILE__
|
15
|
+
lib_files = File.join(lib_dir, '**/*.rb')
|
16
|
+
|
17
|
+
Dir[lib_files].sort.each { |f| require f }
|
data/lib/xlib/x.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Xlib
|
2
|
+
module X; end
|
3
|
+
class << X
|
4
|
+
def open_display(name)
|
5
|
+
display_pointer = Xlib.XOpenDisplay(name)
|
6
|
+
raise ArgumentError, "Unknown display #{name}" if display_pointer.null?
|
7
|
+
Xlib::Display.new(display_pointer)
|
8
|
+
end
|
9
|
+
|
10
|
+
def close_display(display)
|
11
|
+
Xlib.XCloseDisplay(display.to_native)
|
12
|
+
end
|
13
|
+
|
14
|
+
def list_extensions(display)
|
15
|
+
nextensions_ptr = FFI::MemoryPointer.new :pointer
|
16
|
+
extensions_ptr = Xlib.XListExtensions(display.to_native, nextensions_ptr)
|
17
|
+
nextensions = nextensions_ptr.read_int
|
18
|
+
extensions = extensions_ptr.get_array_of_string(0, nextensions)
|
19
|
+
Xlib.XFreeExtensionList(extensions_ptr)
|
20
|
+
extensions
|
21
|
+
end
|
22
|
+
|
23
|
+
def query_extension(display, name)
|
24
|
+
opcode_ptr = FFI::MemoryPointer.new :int
|
25
|
+
evcode_ptr = FFI::MemoryPointer.new :int
|
26
|
+
errcode_ptr = FFI::MemoryPointer.new :int
|
27
|
+
if Xlib.XQueryExtension(display.to_native, name, opcode_ptr, evcode_ptr, errcode_ptr)
|
28
|
+
{ opcode: opcode_ptr.read_int, first_event: evcode_ptr.read_int, first_error: errcode_ptr.read_int}
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_event_data(display, event_cookie)
|
35
|
+
Xlib::XGetEventData(display.to_native, event_cookie.pointer)
|
36
|
+
end
|
37
|
+
|
38
|
+
def select_input(display, window, mask)
|
39
|
+
Xlib.XSelectInput(display.to_native, window.to_native, mask)
|
40
|
+
flush(display)
|
41
|
+
end
|
42
|
+
|
43
|
+
def next_event(display)
|
44
|
+
xevent = Xlib::XEvent.new
|
45
|
+
Xlib.XNextEvent(display.to_native, xevent) # blocks
|
46
|
+
xevent
|
47
|
+
end
|
48
|
+
|
49
|
+
def pending(display)
|
50
|
+
Xlib.XPending(display.to_native)
|
51
|
+
end
|
52
|
+
|
53
|
+
def flush(display)
|
54
|
+
Xlib.XFlush(display.to_native)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/xlib/xi.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Xlib
|
2
|
+
module XI
|
3
|
+
class << self
|
4
|
+
def query_device(display, device_id)
|
5
|
+
ndevices_ptr = FFI::MemoryPointer.new :int
|
6
|
+
device_infos_ptr = Xlib::XIQueryDevice(display.to_native, device_id, ndevices_ptr)
|
7
|
+
ndevices = ndevices_ptr.read_int
|
8
|
+
|
9
|
+
0.upto(ndevices-1).map do |position|
|
10
|
+
device_info_ptr = device_infos_ptr + position * Xlib::XIDeviceInfo.size
|
11
|
+
Xlib::XIDeviceInfo.new(device_info_ptr)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def free_device_info(device_info)
|
16
|
+
Xlib.XIFreeDeviceInfo(device_info.pointer)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_focus(display, device, window, time)
|
21
|
+
0 == Xlib::XISetFocus(display.to_native, device.to_native, window.to_native, time).tap do
|
22
|
+
Xlib::X.flush(display)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_focus(display, device)
|
27
|
+
window_id_ptr = FFI::MemoryPointer.new :Window
|
28
|
+
if 0 == Xlib::XIGetFocus(display.to_native, device.to_native, window_id_ptr)
|
29
|
+
case window_id = window_id_ptr.read_int
|
30
|
+
when Xlib::PointerRoot
|
31
|
+
display.screens.first.root_window
|
32
|
+
when Xlib::None
|
33
|
+
nil
|
34
|
+
else
|
35
|
+
XlibObj::Window.new(display, window_id)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def grab_device(display, device, window, time, cursor, mode, pair_mode, owner_events, event_mask)
|
41
|
+
event_mask = EventMask.new(device, event_mask)
|
42
|
+
0 == Xlib::XIGrabDevice(display.to_native, device.to_native, window.to_native, time, cursor,
|
43
|
+
mode, pair_mode, owner_events, event_mask.to_native)
|
44
|
+
end
|
45
|
+
|
46
|
+
def ungrab_device(display, device, time)
|
47
|
+
0 == Xlib::XIUngrabDevice(display.to_native, device.to_native, time).tap do
|
48
|
+
Xlib::X.flush(display)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def select_events(display, devices, window, event_mask)
|
53
|
+
event_mask = EventMask.new(devices, event_mask)
|
54
|
+
Xlib.XISelectEvents(display.to_native, window.to_native, event_mask.to_native, devices.size)
|
55
|
+
Xlib::X.flush(display)
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Xlib
|
2
|
+
module XI
|
3
|
+
class EventMask
|
4
|
+
def initialize(devices, mask)
|
5
|
+
@devices = [*devices]
|
6
|
+
@mask = mask
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :devices, :mask
|
10
|
+
|
11
|
+
def to_native
|
12
|
+
@structs ||= begin
|
13
|
+
masks_ptr = FFI::MemoryPointer.new(Xlib::XIEventMask.size, @devices.count)
|
14
|
+
|
15
|
+
@devices.each.with_index do |device, idx|
|
16
|
+
byte_length = (@mask.bit_length/8.0).ceil
|
17
|
+
mask_ptr = FFI::MemoryPointer.new :int
|
18
|
+
mask_ptr.write_int @mask
|
19
|
+
|
20
|
+
Xlib::XIEventMask.new(masks_ptr[idx]).tap do |event_mask|
|
21
|
+
event_mask[:deviceid] = device.is_a?(XlibObj::InputDevice) ? device.id : device
|
22
|
+
event_mask[:mask_len] = FFI.type_size(:int)
|
23
|
+
event_mask[:mask] = mask_ptr
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Xlib::XIEventMask.new(masks_ptr[0]).pointer
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|