x_do 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.
@@ -0,0 +1,62 @@
1
+ # Extensions to the structures in ffi_lib.
2
+
3
+ # :nodoc: namespace
4
+ class XDo
5
+
6
+ # :nodoc: namespace
7
+ module FFILib
8
+
9
+ # :nodoc: extending with better constructor
10
+ class XDoSearch
11
+ # Creates an XDoSearch structure from options passed to XDo#find_window.
12
+ def self.from_options(options = {})
13
+ search = self.new
14
+ search[:max_depth] = (options[:depth] || -1).to_i
15
+
16
+ # SEARCH_ANY vs SEARCH_ALL in xdo.h
17
+ search[:require] = (options[:require] == :any) ? 0 : 1
18
+
19
+ search[:searchmask] = 0
20
+ [
21
+ [:title, :title, XDo::FFILib::Consts::SEARCH_TITLE],
22
+ [:name, :winname, XDo::FFILib::Consts::SEARCH_NAME],
23
+ [:class, :winclass, XDo::FFILib::Consts::SEARCH_CLASS],
24
+ [:class_name, :winclassname, XDo::FFILib::Consts::SEARCH_CLASSNAME]
25
+ ].each do |option, struct_key, bit|
26
+ if options[option]
27
+ search[:searchmask] |= bit
28
+
29
+ c_string = FFI::MemoryPointer.new options[option].length + 1
30
+ c_string.put_string 0, options[option]
31
+ search[struct_key] = c_string
32
+ else
33
+ search[struct_key] = nil
34
+ end
35
+ end
36
+
37
+ if options[:visible]
38
+ search[:searchmask] |= XDo::FFILib::Consts::SEARCH_ONLYVISIBLE
39
+ search[:only_visible] = 1
40
+ else
41
+ search[:only_visible] = 0
42
+ end
43
+
44
+ [
45
+ [:pid, :pid, XDo::FFILib::Consts::SEARCH_PID],
46
+ [:screen, :screen, XDo::FFILib::Consts::SEARCH_SCREEN]
47
+ ].each do |option, struct_key, bit|
48
+ if options[option]
49
+ search[:searchmask] |= bit
50
+ search[struct_key] = options[option].to_i
51
+ else
52
+ search[struct_key] = 0
53
+ end
54
+ end
55
+
56
+ search
57
+ end
58
+ end # class XDo::FFILib::XDoSearch
59
+
60
+ end # module XDo::FFILib
61
+
62
+ end # namespace XDo
@@ -0,0 +1,51 @@
1
+ # :nodoc: namespace
2
+ class XDo
3
+
4
+ # The keyboard state for a libxdo context.
5
+ class Keyboard
6
+ # Creates a keyboard state wrapper for an XDo context.
7
+ #
8
+ # This constructor is called internally by XDo#keyboard and client code
9
+ # should not need to call it directly.
10
+ #
11
+ # Args:
12
+ # xdo:: the XDo wrapping a libxdo context
13
+ def initialize(xdo)
14
+ @xdo = xdo
15
+ @_xdo_pointer = xdo._pointer
16
+ end
17
+
18
+ # The XDo context that produced the window.
19
+ attr_accessor :xdo
20
+
21
+ # Types a string into the current window.
22
+ def type_string(string, delay = 0.12)
23
+ XDo::FFILib.xdo_type @_xdo_pointer, 0, string, (delay * 100_000).to_i
24
+ end
25
+
26
+ # Sends a keysequence to this window.
27
+ #
28
+ # Examples: "alt+Return", "Alt_L+Tab", "l", "semicolon"
29
+ def type_keysequence(keysequence, delay = 0.12)
30
+ XDo::FFILib.xdo_keysequence @_xdo_pointer, 0, keysequence,
31
+ (delay * 100_000).to_i
32
+ end
33
+
34
+ # Presses a keysequence in this window.
35
+ #
36
+ # Examples: "alt+Return", "Alt_L+Tab", "l", "semicolon"
37
+ def press_keysequence(keysequence, delay = 0.12)
38
+ XDo::FFILib.xdo_keysequence_down @_xdo_pointer, 0, keysequence,
39
+ (delay * 100_000).to_i
40
+ end
41
+
42
+ # Releases a keysequence in this window.
43
+ #
44
+ # Examples: "alt+Return", "Alt_L+Tab", "l", "semicolon"
45
+ def release_keysequence(keysequence, delay = 0.12)
46
+ XDo::FFILib.xdo_keysequence_up @_xdo_pointer, 0, keysequence,
47
+ (delay * 100_000).to_i
48
+ end
49
+ end # class XDo::Keyboard
50
+
51
+ end # namespace XDo
data/lib/x_do/mouse.rb ADDED
@@ -0,0 +1,85 @@
1
+ # :nodoc: namespace
2
+ class XDo
3
+
4
+ # The mouse state for a libxdo context.
5
+ class Mouse
6
+ # Creates a mouse state wrapper for an XDo context.
7
+ #
8
+ # This constructor is called internally by XDo#mouse and client code
9
+ # should not need to call it directly.
10
+ #
11
+ # Args:
12
+ # xdo:: the XDo wrapping a libxdo context
13
+ def initialize(xdo)
14
+ @xdo = xdo
15
+ @_xdo_pointer = xdo._pointer
16
+ end
17
+
18
+ # The XDo context that produced the window.
19
+ attr_accessor :xdo
20
+
21
+ # [x, y, screen] array of mouse coordinates.
22
+ def location
23
+ x_pointer = FFI::MemoryPointer.new :int, 1
24
+ y_pointer = FFI::MemoryPointer.new :int, 1
25
+ screen_pointer = FFI::MemoryPointer.new :int, 1
26
+ XDo::FFILib.xdo_mouselocation @_xdo_pointer, x_pointer, y_pointer,
27
+ screen_pointer
28
+ [x_pointer.read_int, y_pointer.read_int, screen_pointer.read_int]
29
+ end
30
+
31
+ # Moves the mouse to a new position.
32
+ def move(x, y, screen)
33
+ old_location = self.location
34
+ move_async x, y, screen
35
+ unless old_location[0, 2] == [x, y]
36
+ wait_for_move_from old_location[0], old_location[1]
37
+ end
38
+ end
39
+
40
+ # Queues a mouse move request to the X server.
41
+ def move_async(x, y, screen)
42
+ XDo::FFILib.xdo_mousemove @_xdo_pointer, x, y, screen
43
+ end
44
+
45
+ # Moves the mouse relatively to its current position.
46
+ def move_relative(dx, dy)
47
+ old_location = self.location
48
+ move_relative_async dx, dy
49
+ unless dx == 0 && dy == 0
50
+ wait_for_move_from old_location[0], old_location[1]
51
+ end
52
+ end
53
+
54
+ # Queues a mouse move request to the X server.
55
+ def move_relative_async(dx, dy)
56
+ XDo::FFILib.xdo_mousemove_relative @_xdo_pointer, dx, dy
57
+ end
58
+
59
+ # Blocks until the mouse moves away from a position on screen.
60
+ def wait_for_move_from(x, y)
61
+ XDo::FFILib.xdo_mouse_wait_for_move_from @_xdo_pointer, x, y
62
+ end
63
+
64
+ # Blocks until the mouse moves to a position on screen.
65
+ def wait_for_move_to(x, y)
66
+ XDo::FFILib.xdo_mouse_wait_for_move_to @_xdo_pointer, x, y
67
+ end
68
+
69
+ # Clicks a mouse button.
70
+ def click(button)
71
+ XDo::FFILib.xdo_click @_xdo_pointer, 0, button
72
+ end
73
+
74
+ # Presses a mouse button.
75
+ def press(button)
76
+ XDo::FFILib.xdo_mousedown @_xdo_pointer, 0, button
77
+ end
78
+
79
+ # Releases a mouse button.
80
+ def release(button)
81
+ XDo::FFILib.xdo_mouseup @_xdo_pointer, 0, button
82
+ end
83
+ end # class XDo::Mouse
84
+
85
+ end # namespace XDo
@@ -0,0 +1,182 @@
1
+ # :nodoc: namespace
2
+ class XDo
3
+
4
+ # Wraps an xdolib Window pointer.
5
+ class Window
6
+ # Brings a window forward and gives it focus.
7
+ def activate
8
+ XDo::FFILib.xdo_window_activate @_xdo_pointer, @_window
9
+ end
10
+
11
+ # Gives the input focus to a window
12
+ def focus
13
+ XDo::FFILib.xdo_window_focus @_xdo_pointer, @_window
14
+ end
15
+
16
+ # Moves the window at the top of the stack, making it visible.
17
+ def raise
18
+ XDo::FFILib.xdo_window_raise @_xdo_pointer, @_window
19
+ end
20
+
21
+ # The PID of the process owning the window.
22
+ def pid
23
+ XDo::FFILib.xdo_window_get_pid @_xdo_pointer, @_window
24
+ end
25
+
26
+ # [x, y] array containing the window's coordinates.
27
+ def location
28
+ x_pointer = FFI::MemoryPointer.new :int, 1
29
+ y_pointer = FFI::MemoryPointer.new :int, 1
30
+ XDo::FFILib.xdo_get_window_location @_xdo_pointer, @_window, x_pointer,
31
+ y_pointer, nil
32
+ [x_pointer.read_int, y_pointer.read_int]
33
+ end
34
+
35
+ # [width, height] array containing the window's size.
36
+ def size
37
+ width_pointer = FFI::MemoryPointer.new :int, 1
38
+ height_pointer = FFI::MemoryPointer.new :int, 1
39
+ XDo::FFILib.xdo_get_window_size @_xdo_pointer, @_window, width_pointer,
40
+ height_pointer
41
+ [width_pointer.read_int, height_pointer.read_int]
42
+ end
43
+
44
+ def move(x, y)
45
+ move_raw x, y
46
+ glitched_location = self.location
47
+ x_decoration = glitched_location.first - x
48
+ y_decoration = glitched_location.last - y
49
+ move_raw x - x_decoration, y - y_decoration
50
+ end
51
+
52
+ # Moves this window to a new position.
53
+ #
54
+ # The position is given directly to X, and does not account for window
55
+ # decorations.
56
+ def move_raw(x, y)
57
+ old_location = self.location
58
+ return_value = move_raw_async x, y
59
+ 100.times do
60
+ break unless self.location == old_location
61
+ sleep 0.01
62
+ end
63
+ return_value
64
+ end
65
+
66
+ # Asks X to move this window to a new position.
67
+ def move_raw_async(x, y)
68
+ XDo::FFILib.xdo_window_move @_xdo_pointer, @_window, x, y
69
+ end
70
+
71
+ # Resizes this window.
72
+ #
73
+ # Args:
74
+ # width:: the new window's width
75
+ # height:: the new window's height
76
+ # use_hints:: if false, width and height are specified in pixels; otherwise,
77
+ # the unit is relative to window size hints
78
+ def resize(width, height, use_hints = false)
79
+ old_size = self.size
80
+ return_value = resize_async width, height, use_hints
81
+ 100.times do
82
+ break unless self.size == old_size
83
+ sleep 0.01
84
+ end
85
+ return_value
86
+ end
87
+
88
+ # Asks X to resize this window.
89
+ def resize_async(width, height, use_hints = false)
90
+ flags = use_hints ? XDo::FFILib::Consts::SIZE_U : 0
91
+ XDo::FFILib.xdo_window_setsize @_xdo_pointer, @_window, width, height, flags
92
+ end
93
+
94
+ # Moves the mouse in window coordinates.
95
+ def move_mouse(window_x, window_y)
96
+ old_location = @xdo.mouse.location
97
+ move_mouse_async window_x, window_y
98
+ @xdo.mouse.wait_for_move_from old_location[0], old_location[1]
99
+ end
100
+
101
+ # Moves the mouse in window coordinates.
102
+ def move_mouse_async(window_x, window_y)
103
+ XDo::FFILib.xdo_mousemove_relative_to_window @_xdo_pointer, @_window,
104
+ window_x, window_y
105
+ end
106
+
107
+ # Clicks a mouse button.
108
+ def click_mouse(button)
109
+ XDo::FFILib.xdo_click @_xdo_pointer, @_window, button
110
+ end
111
+
112
+ # Presses a mouse button.
113
+ def press_mouse(button)
114
+ XDo::FFILib.xdo_mousedown @_xdo_pointer, @_window, button
115
+ end
116
+
117
+ # Releases a mouse button.
118
+ def release_mouse(button)
119
+ XDo::FFILib.xdo_mouseup @_xdo_pointer, @_window, button
120
+ end
121
+
122
+ # Types a string into this window.
123
+ def type_string(string, delay = 0.12)
124
+ XDo::FFILib.xdo_type @_xdo_pointer, @_window, string, (delay * 100_000).to_i
125
+ end
126
+
127
+ # Sends a keysequence to this window.
128
+ #
129
+ # Examples: "alt+Return", "Alt_L+Tab", "l", "semicolon"
130
+ def type_keysequence(keysequence, delay = 0.12)
131
+ XDo::FFILib.xdo_keysequence @_xdo_pointer, @_window, keysequence,
132
+ (delay * 100_000).to_i
133
+ end
134
+
135
+ # Presses a keysequence in this window.
136
+ #
137
+ # Examples: "alt+Return", "Alt_L+Tab", "l", "semicolon"
138
+ def press_keysequence(keysequence, delay = 0.12)
139
+ XDo::FFILib.xdo_keysequence_down @_xdo_pointer, @_window, keysequence,
140
+ (delay * 100_000).to_i
141
+ end
142
+
143
+ # Releases a keysequence in this window.
144
+ #
145
+ # Examples: "alt+Return", "Alt_L+Tab", "l", "semicolon"
146
+ def release_keysequence(keysequence, delay = 0.12)
147
+ XDo::FFILib.xdo_keysequence_up @_xdo_pointer, @_window, keysequence,
148
+ (delay * 100_000).to_i
149
+ end
150
+
151
+ # Creates a wrapper for an X Window handle.
152
+ #
153
+ # This constructor is called internally by XDo#find_windows and client code
154
+ # should not need to call it directly.
155
+ #
156
+ # Args:
157
+ # xdo:: the XDo wrapping the libxdo context used to get this Window
158
+ # _window:: the X Window handle to be wrapped
159
+ def initialize(xdo, _window)
160
+ @xdo = xdo
161
+ @_xdo_pointer = xdo._pointer
162
+ @_window = _window
163
+ end
164
+
165
+ # The XDo context that produced the window.
166
+ attr_accessor :xdo
167
+
168
+ # The underlying X Window handle.
169
+ attr_accessor :_window
170
+
171
+ # :nodoc: underlying window handle should impact equality
172
+ def ==(other)
173
+ other.kind_of?(XDo::Window) && @_window == other._window
174
+ end
175
+
176
+ # :nodoc: override hash to match ==
177
+ def hash
178
+ _window.hash
179
+ end
180
+ end # class XDo::Window
181
+
182
+ end # namespace XDo
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'x_do'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,2 @@
1
+ # Additional stdlib files used in rspecs but not in code.
2
+ require 'English'
@@ -0,0 +1,127 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../spec_helper')
2
+
3
+ describe XDo do
4
+ describe 'with no display specification' do
5
+ let(:xdo) { XDo.new }
6
+
7
+ it 'should use the value of $DISPLAY' do
8
+ xdo.display_name.should == ENV['DISPLAY']
9
+ end
10
+
11
+ describe 'after close' do
12
+ before { xdo.close }
13
+
14
+ it 'should not accept method calls' do
15
+ lambda {
16
+ xdo.display_name
17
+ }.should raise_error(NoMethodError)
18
+ end
19
+
20
+ it 'should accept #close without crashing' do
21
+ lambda {
22
+ xdo.close
23
+ }.should_not raise_error
24
+ end
25
+ end
26
+ end
27
+
28
+ describe 'find_windows' do
29
+ let(:xdo) { XDo.new }
30
+
31
+ describe 'with .* name pattern' do
32
+ let(:windows) { xdo.find_windows :name => '.*' }
33
+
34
+ it 'should return at least 1 element' do
35
+ windows.should_not be_empty
36
+ end
37
+
38
+ it 'should return same number of windows as xdotool' do
39
+ windows.length.should == `xdotool search --all --name ".*"`.split.length
40
+ end
41
+
42
+ it 'should have a XDo::Window as the first element' do
43
+ windows.first.should be_kind_of(XDo::Window)
44
+ end
45
+
46
+ it 'should include the active window' do
47
+ windows.should include(xdo.active_window)
48
+ end
49
+
50
+ it 'should include the "real" focused window' do
51
+ windows.should include(xdo.real_focused_window)
52
+ end
53
+ end
54
+
55
+ describe 'with no conditions' do
56
+ let(:windows) { xdo.find_windows }
57
+
58
+ it 'should include the active window' do
59
+ windows.should include(xdo.active_window)
60
+ end
61
+
62
+ it 'should include the "real" focused window' do
63
+ windows.should include(xdo.real_focused_window)
64
+ end
65
+
66
+ it 'should include the focused window' do
67
+ windows.should include(xdo.focused_window)
68
+ end
69
+ end
70
+
71
+ describe 'with the current PID' do
72
+ let(:windows) { xdo.find_windows :pid => $PID }
73
+
74
+ it 'should not return any windows for console process' do
75
+ windows.should be_empty
76
+ end
77
+ end
78
+ end
79
+
80
+ describe 'active_window' do
81
+ let(:xdo) { XDo.new }
82
+ let(:window) { xdo.active_window }
83
+
84
+ it 'should be an XDo::Window' do
85
+ window.should be_kind_of(XDo::Window)
86
+ end
87
+ end
88
+
89
+ describe 'focused_window' do
90
+ let(:xdo) { XDo.new }
91
+ let(:window) { xdo.focused_window }
92
+
93
+ it 'should be an XDo::Window' do
94
+ window.should be_kind_of(XDo::Window)
95
+ end
96
+ end
97
+
98
+ describe 'mouse' do
99
+ let(:xdo) { XDo.new }
100
+ let(:mouse) { xdo.mouse }
101
+
102
+ it 'should be an XDo::Mouse' do
103
+ mouse.should be_kind_of(XDo::Mouse)
104
+ end
105
+ end
106
+
107
+ describe 'with :0 display name' do
108
+ let(:display) { ':0'}
109
+ let(:xdo) { XDo.new display }
110
+
111
+ it 'should use given display name' do
112
+ xdo.display_name.should == display
113
+ end
114
+ end
115
+
116
+ describe 'lib_version' do
117
+ let(:version) { XDo.lib_version }
118
+
119
+ it 'should be non-nil' do
120
+ version.should_not be_nil
121
+ end
122
+
123
+ it 'should be non-empty' do
124
+ version.should_not be_empty
125
+ end
126
+ end
127
+ end