x_do 0.1.0

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