xdo 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/DEPRECATE.rdoc +11 -0
- data/HISTORY.rdoc +33 -0
- data/README.rdoc +53 -0
- data/VERSION +1 -0
- data/bin/xinfo.rb +155 -0
- data/lib/xdo.rb +37 -0
- data/lib/xdo/clipboard.rb +208 -0
- data/lib/xdo/drive.rb +125 -0
- data/lib/xdo/keyboard.rb +381 -0
- data/lib/xdo/mouse.rb +251 -0
- data/lib/xdo/simulatable.rb +89 -0
- data/lib/xdo/xwindow.rb +1006 -0
- data/samples/full_demo.rb +189 -0
- data/samples/mouse.rb +27 -0
- data/test/test_clipboard.rb +49 -0
- data/test/test_drive.rb +24 -0
- data/test/test_keyboard.rb +111 -0
- data/test/test_mouse.rb +29 -0
- data/test/test_xwindow.rb +97 -0
- metadata +115 -0
data/lib/xdo/mouse.rb
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
#Encoding: UTF-8
|
2
|
+
#This file is part of Xdo.
|
3
|
+
#Copyright © 2009 Marvin Gülker
|
4
|
+
# Initia in potestate nostra sunt, de eventu fortuna iudicat.
|
5
|
+
require_relative("../xdo")
|
6
|
+
module XDo
|
7
|
+
|
8
|
+
#Automate your mouse! You can simulate every click you can do with
|
9
|
+
#your fingers - it's kind of funny, but don't forget to create USEFUL
|
10
|
+
#applications, not ones annoying your users (e. g. you could make
|
11
|
+
#his/her mouse unusable).
|
12
|
+
module Mouse
|
13
|
+
|
14
|
+
#Left mouse button.
|
15
|
+
LEFT = 1
|
16
|
+
#Middle mouse button (as if you click your mouse wheel).
|
17
|
+
MIDDLE = 2
|
18
|
+
#Right mouse button.
|
19
|
+
RIGHT = 3
|
20
|
+
#Mouse wheel up.
|
21
|
+
UP = 4
|
22
|
+
#Mouse wheel down.
|
23
|
+
DOWN = 5
|
24
|
+
|
25
|
+
#Maps the button's symbols to the numbers xdotool uses.
|
26
|
+
BUTTON2XDOTOOL = {
|
27
|
+
:left => 1,
|
28
|
+
:middle => 2,
|
29
|
+
:right => 3,
|
30
|
+
:up => 4,
|
31
|
+
:down => 5
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
class << self
|
35
|
+
|
36
|
+
#Gets the current cursor position.
|
37
|
+
#===Return value
|
38
|
+
#A two-element array of form <code>[x, y]</code>.
|
39
|
+
#===Example
|
40
|
+
# p XDo::Mouse.position #=> [12, 326]
|
41
|
+
def position
|
42
|
+
out = `#{XDOTOOL} getmouselocation`.match(/x:(\d+) y:(\d+)/)
|
43
|
+
[$1.to_i, $2.to_i]
|
44
|
+
end
|
45
|
+
|
46
|
+
#Moves the mouse cursor to the given position.
|
47
|
+
#===Parameters
|
48
|
+
#[+x+] The goal X coordinate.
|
49
|
+
#[+x+] The goal Y coordinate.
|
50
|
+
#[+speed+] (2) Cursor move speed, in pixels per iteration. The higher the value, the more inaccurate the movement (but you can be sure the cursor is always at the position you specified at the end).
|
51
|
+
#[+set+] (false) If true, the +speed+ parameter is ignored and the cursor is directly set to the given position.
|
52
|
+
#[+sync+] (true) If true, this method blocks until the cursor has reached the given position.
|
53
|
+
#===Return value
|
54
|
+
#The position you specified, as a two-dimensional array of form <tt>[x, y]</tt>.
|
55
|
+
#===Raises
|
56
|
+
#[ArgumentError] The value of +speed+ was lower or equal to zero.
|
57
|
+
#===Example
|
58
|
+
# #Move to (10|10)
|
59
|
+
# XDo::Mouse.move(10, 10)
|
60
|
+
# #Move fast to (10|10)
|
61
|
+
# XDo::Mouse.move(10, 10, 10)
|
62
|
+
# #Directly set the cursor to (10|10) without any movement
|
63
|
+
# XDo::Mouse.move(10, 10, 1, true)
|
64
|
+
def move(x, y, speed = 2, set = false, sync = true)
|
65
|
+
if set
|
66
|
+
opts = []
|
67
|
+
opts << "--sync" if sync
|
68
|
+
`#{XDOTOOL} mousemove #{opts.join(" ")} #{x} #{y}`
|
69
|
+
return [x, y]
|
70
|
+
else
|
71
|
+
raise(ArgumentError, "speed has to be > 0 (default is 2), was #{speed}!") if speed <= 0
|
72
|
+
pos = position #Current cursor position
|
73
|
+
act_x = pos[0]
|
74
|
+
act_y = pos[1]
|
75
|
+
aim_x = x
|
76
|
+
aim_y = y
|
77
|
+
#Create the illusion of a fluent movement (hey, that statement sounds better in German, really! ;-))
|
78
|
+
loop do
|
79
|
+
#Change position as indiciated by +speed+
|
80
|
+
if act_x > aim_x
|
81
|
+
act_x -= speed
|
82
|
+
elsif act_x < aim_x
|
83
|
+
act_x += speed
|
84
|
+
end
|
85
|
+
if act_y > aim_y
|
86
|
+
act_y -= speed
|
87
|
+
elsif act_y < aim_y
|
88
|
+
act_y += speed
|
89
|
+
end
|
90
|
+
#Move to computed position
|
91
|
+
move(act_x, act_y, speed, true)
|
92
|
+
#Check wheather the cursor's current position is inside an
|
93
|
+
#acceptable area around the goal position. The size of this
|
94
|
+
#area is defined by +speed+; this check ensures we don't get
|
95
|
+
#an infinite loop for unusual conditions.
|
96
|
+
if ((aim_x - speed)..(aim_x + speed)).include? act_x
|
97
|
+
if ((aim_y - speed)..(aim_y + speed)).include? act_y
|
98
|
+
break
|
99
|
+
end #if in Y-Toleranz
|
100
|
+
end #if in X-Toleranz
|
101
|
+
end #loop
|
102
|
+
#Correct the cursor position to point to the exact point specified.
|
103
|
+
#This is for the case the "acceptable area" condition above triggers.
|
104
|
+
if position != [x, y]
|
105
|
+
move(x, y, 1, true)
|
106
|
+
end #if position != [x, y]
|
107
|
+
|
108
|
+
end #if set
|
109
|
+
[x, y]
|
110
|
+
end #def move
|
111
|
+
|
112
|
+
#Simulates a mouse click. If you don't specify a X AND a Y position,
|
113
|
+
#the click will happen at the current cursor position.
|
114
|
+
#===Parameters
|
115
|
+
#[+x+] (nil) The goal X position. Specify together with +y+.
|
116
|
+
#[+y+] (nil) The goal Y position.
|
117
|
+
#[+button+] (:left) The button to click with. One of the following symbols: <tt>:left</tt>, <tt>:right</tt>, <tt>:middle</tt>.
|
118
|
+
#The other arguments are the same as for #move.
|
119
|
+
#===Return value
|
120
|
+
#Undefined.
|
121
|
+
#===Example
|
122
|
+
# #Click at the current position
|
123
|
+
# XDo::Mouse.click
|
124
|
+
# #Click at (10|10)
|
125
|
+
# XDo::Mouse.click(10, 10)
|
126
|
+
# #Click at the current position with the right mouse button
|
127
|
+
# XDo::Mouse.click(nil, nil, :right)
|
128
|
+
# #Move fast to (10|10) and click with the right mouse button
|
129
|
+
# XDo::Mouse.click(10, 10, :right, 10)
|
130
|
+
# #Directly set the cursor to (10|10) and click with the middle mouse button
|
131
|
+
# XDo::Mouse.click(10, 10, :middle, 1, true)
|
132
|
+
def click(x = nil, y = nil, button = :left, speed = 1, set = false)
|
133
|
+
if button.kind_of?(Numeric)
|
134
|
+
warn("#{caller.first}: Deprecation warning: Use symbols such as :left for the button parameter.")
|
135
|
+
button = BUTTON2XDOTOOL.keys[button - 1] #indices are 0-based
|
136
|
+
end
|
137
|
+
if x and y
|
138
|
+
move(x, y, speed, set)
|
139
|
+
end
|
140
|
+
`#{XDOTOOL} click #{BUTTON2XDOTOOL[button]}`
|
141
|
+
end
|
142
|
+
|
143
|
+
#Scroll with the mouse wheel. +amount+ is the time of steps to scroll.
|
144
|
+
#===Parameters
|
145
|
+
#[+dir+] The direction to scroll into. Either :up or :down.
|
146
|
+
#[+amount+] The number of steps to scroll. These are *not* meant to be full wheel rounds.
|
147
|
+
#===Return value
|
148
|
+
#Undefined.
|
149
|
+
#===Example
|
150
|
+
# #Scroll up
|
151
|
+
# XDo::Mouse.wheel(:up, 4)
|
152
|
+
# #Scroll down 4 steps
|
153
|
+
# XDo::Mouse.wheel(:down, 4)
|
154
|
+
#===Remarks
|
155
|
+
#The X Server handles wheel up and wheel down events like mouse click
|
156
|
+
#events and if you take a look in the source code of this function you will see, that
|
157
|
+
#it calls #click passing through the +dir+ parameter. So, if you want to be funny,
|
158
|
+
#write something like
|
159
|
+
# XDo::Mouse.click(nil, nil, :up)
|
160
|
+
#.
|
161
|
+
def wheel(dir, amount)
|
162
|
+
if button.kind_of?(Numeric)
|
163
|
+
warn("#{caller.first}: Deprecation warning: Use symbols such as :up for the dir parameter.")
|
164
|
+
button = BUTTON2XDOTOOL.keys[button - 1] #indices are 0-based
|
165
|
+
end
|
166
|
+
amount.times{click(nil, nil, dir)}
|
167
|
+
end
|
168
|
+
|
169
|
+
#Holds a mouse button down. Don't forget to release it some time.
|
170
|
+
#===Parameters
|
171
|
+
#[+button+] (:left) The button to hold down.
|
172
|
+
#===Return value
|
173
|
+
#Undefined.
|
174
|
+
#===Example
|
175
|
+
# #Hold down the left mouse button for a second
|
176
|
+
# XDo::Mouse.down
|
177
|
+
# sleep 1
|
178
|
+
# XDo::Mouse.up
|
179
|
+
# #Hold down the right mouse button for a second
|
180
|
+
# XDo::Mouse.down(:right)
|
181
|
+
# sleep 1
|
182
|
+
# XDo::Mouse.up(:right)
|
183
|
+
def down(button = :left)
|
184
|
+
if button.kind_of?(Numeric)
|
185
|
+
warn("#{caller.first}: Deprecation warning: Use symbols such as :left for the button parameter.")
|
186
|
+
button = BUTTON2XDOTOOL.keys[button - 1] #indices are 0-based
|
187
|
+
end
|
188
|
+
`#{XDOTOOL} mousedown #{BUTTON2XDOTOOL[button]}`
|
189
|
+
end
|
190
|
+
|
191
|
+
#Releases a mouse button. Probably it's a good idea to call #down first?
|
192
|
+
#===Parameters
|
193
|
+
#[+button+] (:left) The button to release.
|
194
|
+
#===Return value
|
195
|
+
#Undefined.
|
196
|
+
#===Example
|
197
|
+
# #Hold down the left mouse button for a second
|
198
|
+
# XDo::Mouse.down
|
199
|
+
# sleep 1
|
200
|
+
# XDo::Mouse.up
|
201
|
+
# #Hold down the right mouse button for a second
|
202
|
+
# XDo::Mouse.down(:right)
|
203
|
+
# sleep 1
|
204
|
+
# XDo::Mouse.up(:right)
|
205
|
+
def up(button = :left)
|
206
|
+
if button.kind_of?(Numeric)
|
207
|
+
warn("#{caller.first}: Deprecation warning: Use symbols such as :left for the button parameter.")
|
208
|
+
button = BUTTON2XDOTOOL.keys[button - 1] #indices are 0-based
|
209
|
+
end
|
210
|
+
`#{XDOTOOL} mouseup #{BUTTON2XDOTOOL[button]}`
|
211
|
+
end
|
212
|
+
|
213
|
+
#Executs a drag&drop operation.
|
214
|
+
#===Parameters
|
215
|
+
#[+x1+] Start X coordinate. Set to the current cursor X coordinate if set to nil. Pass together with +y1+.
|
216
|
+
#[+y1+] Start Y coordinate. Set to the current cursor Y coordinate if set to nil.
|
217
|
+
#[+x2+] Goal X coordinate.
|
218
|
+
#[+y2+] Goal Y coordinate.
|
219
|
+
#[+button+] (:left) The button to hold down.
|
220
|
+
#The rest of the parameters is the same as for #move.
|
221
|
+
#===Return value
|
222
|
+
#nil.
|
223
|
+
#===Example
|
224
|
+
# #Drag from (10|10) to (37|56)
|
225
|
+
# XDo::Mouse.drag(10, 10, 37, 56)
|
226
|
+
# #Drag from (10|10) to (37|56) holding the right mouse button down
|
227
|
+
# XDo::Mouse.drag(10, 10, 37, 56, :right)
|
228
|
+
def drag(x1, y1, x2, y2, button = :left, speed = 2, set = false)
|
229
|
+
if button.kind_of?(Numeric)
|
230
|
+
warn("#{caller.first}: Deprecation warning: Use symbols such as :left for the button parameter.")
|
231
|
+
button = BUTTON2XDOTOOL.keys[button - 1] #indices are 0-based
|
232
|
+
end
|
233
|
+
#If x1 and y1 aren't passed, assume the current position
|
234
|
+
if x1.nil? and y1.nil?
|
235
|
+
x1, y1 = position
|
236
|
+
end
|
237
|
+
#Go to the given start position
|
238
|
+
move(x1, y1, speed, set)
|
239
|
+
#Hold button down
|
240
|
+
down(button)
|
241
|
+
#Move to the goal position
|
242
|
+
move(x2, y2, speed, set)
|
243
|
+
#Release button
|
244
|
+
up(button)
|
245
|
+
nil
|
246
|
+
end
|
247
|
+
|
248
|
+
end #class << self
|
249
|
+
|
250
|
+
end #module Mouse
|
251
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#Encoding: UTF-8
|
3
|
+
#This file is part of Xdo.
|
4
|
+
#Copyright © 2009, 2010 Marvin Gülker
|
5
|
+
# Initia in potestate nostra sunt, de eventu fortuna iudicat.
|
6
|
+
require_relative("../xdo")
|
7
|
+
require_relative("./keyboard")
|
8
|
+
|
9
|
+
module XDo
|
10
|
+
|
11
|
+
#Mixin that allows String-like objects to be directly
|
12
|
+
#simulated. You can use it with Ruby's String class:
|
13
|
+
# require "xdo/simulatable"
|
14
|
+
#
|
15
|
+
# class String
|
16
|
+
# include XDo::Simulatable
|
17
|
+
# def to_xdo
|
18
|
+
# to_s
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# "abc".simulate
|
23
|
+
#Every method in this module calls #to_xdo on +self+
|
24
|
+
#first, so make sure this method returns a xdo-usable
|
25
|
+
#String (i.e. no invisible characters except newline, tab and space).
|
26
|
+
module Simulatable
|
27
|
+
|
28
|
+
#Simulates +self+ as keystrokes. Escape sequences are allowed.
|
29
|
+
#===Parameters
|
30
|
+
#[raw] (+false+) If true, escape sequences are ignored.
|
31
|
+
#[w_id] (+nil+) The window's ID to send the keystrokes to. Either an integer or a XWindow object.
|
32
|
+
#===Return value
|
33
|
+
#self.
|
34
|
+
#===Raises
|
35
|
+
#[ParseError] Your string was invalid.
|
36
|
+
#===Example
|
37
|
+
# "This is an{BS} test".simulate
|
38
|
+
# win = XDo::XWindow.from_title(/gedit/)
|
39
|
+
# "This is an{BS} test".simulate(false, win)
|
40
|
+
def simulate(raw = false, w_id = nil)
|
41
|
+
XDo::Keyboard.simulate(to_xdo, raw, w_id)
|
42
|
+
end
|
43
|
+
|
44
|
+
#Types +self+ as keystrokes. Ignores escape sequences.
|
45
|
+
#===Parameters
|
46
|
+
#[w_id] The window's ID to send the keystrokes to. Either an integer or a XWindow object.
|
47
|
+
#===Return value
|
48
|
+
#nil.
|
49
|
+
#===Example
|
50
|
+
# "Some test text".type
|
51
|
+
# win = XDo::XWindow.from_title(/gedit/)
|
52
|
+
# "Some test text".type(win)
|
53
|
+
def type(w_id = nil)
|
54
|
+
XDo::Keyboard.type(to_xdo, w_id)
|
55
|
+
end
|
56
|
+
|
57
|
+
#Holds the the key corresponding to the first character in +self+ down.
|
58
|
+
#===Parameters
|
59
|
+
#[w_id] The window in which to hold a key down. Either an integer ID or a XWindow object.
|
60
|
+
#===Return value
|
61
|
+
#The character of the key hold down.
|
62
|
+
#===Raises
|
63
|
+
#[XError] Invalid keyname.
|
64
|
+
#===Example
|
65
|
+
# "a".down
|
66
|
+
# win = XDo::XWindow.from_title(/gedit/)
|
67
|
+
# "a".down(win)
|
68
|
+
def down(w_id = nil)
|
69
|
+
XDo::Keyboard.key_down(to_xdo[0], w_id)
|
70
|
+
end
|
71
|
+
|
72
|
+
#Releases the key corresponding to the first character in +self+.
|
73
|
+
#===Parameters
|
74
|
+
#[w_id] The window in which to release a key. Either an integer ID or a XWindow object.
|
75
|
+
#===Return value
|
76
|
+
#The character of the key released.
|
77
|
+
#===Raises
|
78
|
+
#[XError] Invalid keyname.
|
79
|
+
#===Example
|
80
|
+
# "a".up
|
81
|
+
# win = XDo::XWindow.from_title(/gedit/)
|
82
|
+
# "a".up(win)
|
83
|
+
def up(w_id = nil)
|
84
|
+
XDo::Keyboard.key_up(to_xdo[0], w_id)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
data/lib/xdo/xwindow.rb
ADDED
@@ -0,0 +1,1006 @@
|
|
1
|
+
#Encoding: UTF-8
|
2
|
+
#This file is part of Xdo.
|
3
|
+
#Copyright © 2009, 2010 Marvin Gülker
|
4
|
+
# Initia in potestate nostra sunt, de eventu fortuna iudicat.
|
5
|
+
require "open3"
|
6
|
+
require_relative("../xdo")
|
7
|
+
module XDo
|
8
|
+
|
9
|
+
#This class represents a window on the screen. Each window is uniquely identified by
|
10
|
+
#an internal ID; before you can create a reference to a window (a XWindow object) you
|
11
|
+
#have to obtain the internal ID of that window and pass it into XWindow.new.
|
12
|
+
#Or you use the class methods of this class, notably XWindow.active_window.
|
13
|
+
#
|
14
|
+
#Via the XWindow object you get you can manipulate a window in serveral ways, e.g.
|
15
|
+
#you can move or resize it. Some methods are not available on every window
|
16
|
+
#manager: XWindow.active_window, XWindow.desktop_num, XWindow.desktop_num=, XWindow.desktop,
|
17
|
+
#XWindow.desktop=, XWindow.from_active, #raise, #activate, #desktop, #desktop=.
|
18
|
+
#Some of them may be available, some not. On my machine (an Ubuntu Lucid) for
|
19
|
+
#example I can use active_window, desktop_num and #activate, but not #raise or #desktop=.
|
20
|
+
#Those methods are tagged with the sentence "Part of the EWMH standard XY". Not all
|
21
|
+
#parts of the EWMH standard are provided by every window manager.
|
22
|
+
#
|
23
|
+
#As of version 0.0.4 the way to search for window is about to change. The old version
|
24
|
+
#where you passed a hash with symbols has been deprecated (and you get warnings
|
25
|
+
#about this if you use it) in favor of passing those symbols as a rest argument. See
|
26
|
+
#XWindow.search for more details.
|
27
|
+
#
|
28
|
+
#You should also be aware of the fact that XDo is about to support Regexp objects
|
29
|
+
#in XWindow.search. In future versions (i.e. after the next minor release) strings
|
30
|
+
#*always* mean an exact title/class/whatever match. For parts, you have to use
|
31
|
+
#Regular Expressions. There is a culprit, though. +xdotool+ doesn't use Ruby's
|
32
|
+
#Regular Expressions engine Oniguruma and expects C-style regexps. I don't know
|
33
|
+
#about the differences - but if you're absolutely sure your window title matches
|
34
|
+
#that wonderful three-line extended regexp and +xdotool+ doesn't find it, you
|
35
|
+
#may email me at sutniuq@@gmx@net explaining which construct defeats +xdotool+.
|
36
|
+
#I will then setup a list over time which states which constructs don't work.
|
37
|
+
#
|
38
|
+
#Be <i>very careful</i> with the methods that are part of the two desktop EWMH standards.
|
39
|
+
#After I set the number of desktops and changed the current desktop, I had to reboot my
|
40
|
+
#system to get the original configuration back. I don't know if I'm not using +xdotool+ correct,
|
41
|
+
#but neither my library nor +xdotool+ itself could rescue my desktop settings. Btw, that's the
|
42
|
+
#reason why it's not in XDo's unit tests (but it should work; at least in one way...).
|
43
|
+
class XWindow
|
44
|
+
#The internal ID of the window.
|
45
|
+
attr_reader :id
|
46
|
+
|
47
|
+
class << self
|
48
|
+
|
49
|
+
#Checks if a window exists.
|
50
|
+
#===Parameters
|
51
|
+
#[+name+] The name of the window to look for. Either a string or a Regular Expression; however, there's no guaranty that +xdotool+ gets the regexp right. Simple ones should work, though.
|
52
|
+
#[<tt>*opts</tt> (<tt>[:name, :class, :classname]</tt>) Search parameters. See XWindow.search.
|
53
|
+
#===Return value
|
54
|
+
#true or false.
|
55
|
+
#===Example
|
56
|
+
# p XWindow.exists?("gedit") #=> true
|
57
|
+
# p XWindow.exists?(/^gedit/) #=> false
|
58
|
+
#===Remarks
|
59
|
+
#It may be a good idea to pass :onlyvisible as a search parameter.
|
60
|
+
def exists?(name, *opts)
|
61
|
+
if opts.first.kind_of?(Hash)
|
62
|
+
warn("#{caller.first}: Deprecation Warning: Using a hash as further arguments is deprecated. Pass the symbols directly.")
|
63
|
+
opts = opts.first.keys
|
64
|
+
end
|
65
|
+
|
66
|
+
!search(name, *opts).empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
#Checks wheather the given ID exists or not.
|
70
|
+
#===Parameters
|
71
|
+
#[+id+] The ID to check for.
|
72
|
+
#===Return value
|
73
|
+
#true or false.
|
74
|
+
#===Example
|
75
|
+
# p XWindow.id_exits?(29360674) #=> true
|
76
|
+
# p XWindow.id_exists?(123456) #=> false
|
77
|
+
def id_exists?(id)
|
78
|
+
err = ""
|
79
|
+
Open3.popen3("#{XDo::XWININFO} -id #{id}"){|stdin, stdout, stderr| err << stderr.read}
|
80
|
+
return false unless err.empty?
|
81
|
+
return true
|
82
|
+
end
|
83
|
+
|
84
|
+
#Waits for a window name to exist.
|
85
|
+
#===Parameters
|
86
|
+
#[+name+] The name of the window to look for. Either a string or a Regular Expression; however, there's no guaranty that +xdotool+ gets the regexp right. Simple ones should work, though.
|
87
|
+
#[<tt>*opts</tt> (<tt>[:name, :class, :classname]</tt>) Search parameters. See XWindow.search.
|
88
|
+
#===Return value
|
89
|
+
#The ID of the newly appeared window.
|
90
|
+
#===Example
|
91
|
+
# #Wait for a window with "gedit" somewhere in it's title:
|
92
|
+
# XDo::XWindow.wait_for_window("gedit")
|
93
|
+
# #Wait for a window that ends with "ends_with_this":
|
94
|
+
# XDo::XWindow.wait_for_window(/ends_with_this$/)
|
95
|
+
# #It's useful to combine this method with the Timeout module:
|
96
|
+
# require "timeout"
|
97
|
+
# Timeout.timeout(3){XDo::XWindow.wait_for_window("gedit")}
|
98
|
+
#===Remarks
|
99
|
+
#Returns immediately if the window does already exist.
|
100
|
+
def wait_for_window(name, *opts)
|
101
|
+
if opts.first.kind_of?(Hash)
|
102
|
+
warn("#{caller.first}: Deprecation Warning: Using a hash as further arguments is deprecated. Pass the symbols directly.")
|
103
|
+
opts = opts.first.keys
|
104
|
+
end
|
105
|
+
|
106
|
+
loop{break if exists?(name, *opts);sleep(0.5)}
|
107
|
+
search(name, *opts).first
|
108
|
+
end
|
109
|
+
|
110
|
+
#Waits for a window to close.
|
111
|
+
#===Parameters
|
112
|
+
#[+name+] The name of the window to look for. Either a string or a Regular Expression; however, there's no guaranty that +xdotool+ gets the regexp right. Simple ones should work, though.
|
113
|
+
#[<tt>*opts</tt> (<tt>[:name, :class, :classname]</tt>) Search parameters. See XWindow.search.
|
114
|
+
#===Return value
|
115
|
+
#nil.
|
116
|
+
#===Example
|
117
|
+
# #Wait for a window with "gedit" somewhere in it's title
|
118
|
+
# XDo::XWindow.wait_for_close("gedit")
|
119
|
+
# #Waits for a window whose title ends with "ends_with_this":
|
120
|
+
# XDo::XWindow.wait_for_close(/ends_with_this$/)
|
121
|
+
# #It's quite useful to combine this method with the Timeout module:
|
122
|
+
# require "timeout"
|
123
|
+
# Timeout.timeout(3){XDo::XWindow.wait_for_close("gedit")}
|
124
|
+
def wait_for_close(name, *opts)
|
125
|
+
if opts.first.kind_of?(Hash)
|
126
|
+
warn("#{caller.first}: Deprecation Warning: Using a hash as further arguments is deprecated. Pass the symbols directly.")
|
127
|
+
opts = opts.first.keys
|
128
|
+
end
|
129
|
+
|
130
|
+
loop{break if !exists?(name, *opts);sleep(0.5)}
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
|
134
|
+
#Search for a window name to get the internal ID of a window.
|
135
|
+
#===Parameters
|
136
|
+
#[+str+] The name of the window to look for. Either a string or a Regular Expression; however, there's no guaranty that +xdotool+ gets the regexp right. Simple ones should work, though.
|
137
|
+
#[<tt>*opts</tt> (<tt>[:name, :class, :classname]</tt>) Search parameters.
|
138
|
+
#====Possible search parameters
|
139
|
+
#Copied from the +xdotool+ manpage:
|
140
|
+
#[class] Match against the window class.
|
141
|
+
#[classname] Match against the window classname.
|
142
|
+
#[name] Match against the window name. This is the same string that is displayed in the window titlebar.
|
143
|
+
#[onlyvisible] Show only visible windows in the results. This means ones with map state IsViewable.
|
144
|
+
#===Return value
|
145
|
+
#An array containing the IDs of all found windows or an empty array
|
146
|
+
#if none was found.
|
147
|
+
#===Example
|
148
|
+
# #Look for every window with "gedit" in it's title, class or classname
|
149
|
+
# XDo::XWindow.search("gedit")
|
150
|
+
# #Look for every window whose title, class or classname ends with "SciTE"
|
151
|
+
# XDo::XWindow.search(/SciTE$/)
|
152
|
+
# #Explicitly only search the titles of visible windows
|
153
|
+
# XDo::XWindow.search("gedit", :name, :onlyvisible)
|
154
|
+
def search(str, *opts)
|
155
|
+
if opts.first.kind_of?(Hash)
|
156
|
+
warn("#{caller.first}: Deprecation Warning: Using a hash as further arguments is deprecated. Pass the symbols directly.")
|
157
|
+
opts = opts.first.keys
|
158
|
+
end
|
159
|
+
opts = [:name, :class, :classname] if opts.empty?
|
160
|
+
|
161
|
+
#Allow Regular Expressions. Since I can't pass them directly to the command line,
|
162
|
+
#I need to get their source. Otherwise we want an exact match, therefore the line
|
163
|
+
#begin and line end anchors need to be set around the given string.
|
164
|
+
str = str.source if str.kind_of?(Regexp)
|
165
|
+
#TODO
|
166
|
+
#The following is the new behaviour that will be activated with the next minor version.
|
167
|
+
#See DEPRECATE.rdoc.
|
168
|
+
#str = if str.kind_of?(Regexp)
|
169
|
+
#str.source
|
170
|
+
#else
|
171
|
+
#"^#{str.to_str}$"
|
172
|
+
#end
|
173
|
+
|
174
|
+
cmd = "#{XDo::XDOTOOL} search "
|
175
|
+
opts.each{|sym| cmd << "--#{sym} "}
|
176
|
+
cmd << "'" << str << "'"
|
177
|
+
#Don't handle errors since we want an empty array in case of an error
|
178
|
+
Open3.popen3(cmd){|stdin, stdout, stderr| stdin.close_write; stdout.read}.lines.to_a.collect{|l| l.strip.to_i}
|
179
|
+
end
|
180
|
+
|
181
|
+
#Returns the internal ID of the currently focused window.
|
182
|
+
#===Parameters
|
183
|
+
#[+notice_children+] (false) If true, childwindows are noticed and you may get a child window instead of a toplevel window.
|
184
|
+
#===Return value
|
185
|
+
#The internal ID of the found window.
|
186
|
+
#===Raises
|
187
|
+
#[XError] Error invoking +xdotool+.
|
188
|
+
#===Example
|
189
|
+
# p XDo::XWindow.focused_window #=> 41943073
|
190
|
+
# p XDo::XWindow.focused_window(true) #=> 41943074
|
191
|
+
#===Remarks
|
192
|
+
#This method may find an invisible window, see active_window for a more reliable method.
|
193
|
+
def focused_window(notice_children = false)
|
194
|
+
err = ""
|
195
|
+
out = ""
|
196
|
+
Open3.popen3("#{XDo::XDOTOOL} getwindowfocus #{notice_children ? "-f" : ""}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
197
|
+
raise(XDo::XError, err) unless err.empty?
|
198
|
+
return out.to_i
|
199
|
+
end
|
200
|
+
|
201
|
+
#Returns the internal ID of the currently focused window.
|
202
|
+
#===Return value
|
203
|
+
#The ID of the found window.
|
204
|
+
#===Raises
|
205
|
+
#[XError] Error invoking +xdotool+.
|
206
|
+
#===Example
|
207
|
+
# p XDo::XWindow.active_window #=> 41943073
|
208
|
+
#===Remarks
|
209
|
+
#This method is more reliable than #focused_window, but never finds an invisible window.
|
210
|
+
#
|
211
|
+
#Part of the EWMH standard ACTIVE_WINDOW.
|
212
|
+
def active_window
|
213
|
+
err = ""
|
214
|
+
out = ""
|
215
|
+
Open3.popen3("#{XDo::XDOTOOL} getactivewindow"){|stdin, stdout, stderr| out = stdout.read; err = stderr.read}
|
216
|
+
raise(XDo::XError, err) unless err.empty?
|
217
|
+
return Integer(out)
|
218
|
+
end
|
219
|
+
|
220
|
+
#Set the number of working desktops.
|
221
|
+
#===Parameters
|
222
|
+
#[+num+] The number of desktops you want to exist.
|
223
|
+
#===Return value
|
224
|
+
#+num+.
|
225
|
+
#===Raises
|
226
|
+
#[XError] Error invoking +xdotool+.
|
227
|
+
#===Example
|
228
|
+
# XDo::XWindow.desktop_num = 2
|
229
|
+
#===Remarks
|
230
|
+
#Although Ubuntu systems seem to have several desktops, that isn't completely true. An usual Ubuntu system only
|
231
|
+
#has a single working desktop, on which Ubuntu sets up an arbitrary number of other "desktop views" (usually 4).
|
232
|
+
#That's kind of cheating, but I have not yet find out why it is like that. Maybe it's due to the nice cube rotating effect?
|
233
|
+
#That's the reason, why the desktop-related methods don't work with Ubuntu.
|
234
|
+
#
|
235
|
+
#Part of the EWMH standard WM_DESKTOP.
|
236
|
+
def desktop_num=(num)
|
237
|
+
err = ""
|
238
|
+
Open3.popen3("#{XDo::XDOTOOL} set_num_desktops #{num}"){|stdin, stdout, stderr| err << stderr.read}
|
239
|
+
raise(XDo::Error, err) unless err.empty?
|
240
|
+
num
|
241
|
+
end
|
242
|
+
|
243
|
+
#Get the number of working desktops.
|
244
|
+
#===Return value
|
245
|
+
#The number of desktops.
|
246
|
+
#===Raises
|
247
|
+
#[XError] Error invoking +xdotool+.
|
248
|
+
#===Example
|
249
|
+
# p XDo::XWindow.desktop_num = 1
|
250
|
+
#===Remarks
|
251
|
+
#Although Ubuntu systems seem to have several desktops, that isn't completely true. An usual Ubuntu system only
|
252
|
+
#has a single working desktop, on which Ubuntu sets up an arbitrary number of other "desktop views" (usually 4).
|
253
|
+
#That's kind of cheating, but I have not yet find out why it is like that. Maybe it's due to the nice cube rotating effect?
|
254
|
+
#That's the reason, why the desktop-related methods don't work with Ubuntu.
|
255
|
+
#
|
256
|
+
#Part of the EWMH standard WM_DESKTOP.
|
257
|
+
def desktop_num
|
258
|
+
err = ""
|
259
|
+
out = ""
|
260
|
+
Open3.popen3("#{XDo::XDOTOOL} get_num_desktops"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
261
|
+
raise(XDo::XError, err) unless err.empty?
|
262
|
+
Integer(out)
|
263
|
+
end
|
264
|
+
|
265
|
+
#Change the view to desktop +num+.
|
266
|
+
#===Parameters
|
267
|
+
#[+num+] The 0-based index of the desktop you want to switch to.
|
268
|
+
#===Return value
|
269
|
+
#+num+.
|
270
|
+
#===Raises
|
271
|
+
#[XError] Error invoking +xdotool+.
|
272
|
+
#===Example
|
273
|
+
# XDo::XWindow.desktop = 1
|
274
|
+
#===Remarks
|
275
|
+
#Although Ubuntu systems seem to have several desktops, that isn't completely true. An usual Ubuntu system only
|
276
|
+
#has a single working desktop, on which Ubuntu sets up an arbitrary number of other "desktop views" (usually 4).
|
277
|
+
#That's kind of cheating, but I have not yet find out why it is like that. Maybe it's due to the nice cube rotating effect?
|
278
|
+
#That's the reason, why the desktop-related methods don't work with Ubuntu.
|
279
|
+
#
|
280
|
+
#Part of the EWMH standard CURRENT_DESKTOP.
|
281
|
+
def desktop=(num)
|
282
|
+
err = ""
|
283
|
+
Open3.popen3("#{XDo::XDOTOOL} set_desktop #{num}"){|stdin, stdout, stderr| err << stderr.read}
|
284
|
+
raise(XDo::XError, err) unless err.empty?
|
285
|
+
num
|
286
|
+
end
|
287
|
+
|
288
|
+
#Returns the number of the active desktop.
|
289
|
+
#===Return value
|
290
|
+
#The number of the currently shown desktop.
|
291
|
+
#===Raises
|
292
|
+
#[XError] Error invoking +xdotool+.
|
293
|
+
#===Example
|
294
|
+
# p XDo::XWindow.desktop #=> 0
|
295
|
+
#===Remarks
|
296
|
+
#Although Ubuntu systems seem to have several desktops, that isn't completely true. An usual Ubuntu system only
|
297
|
+
#has a single working desktop, on which Ubuntu sets up an arbitrary number of other "desktop views" (usually 4).
|
298
|
+
#That's kind of cheating, but I have not yet find out why it is like that. Maybe it's due to the nice cube rotating effect?
|
299
|
+
#That's the reason, why the desktop-related methods don't work with Ubuntu.
|
300
|
+
#
|
301
|
+
#Part of the EWMH standard CURRENT_DESKTOP.
|
302
|
+
def desktop
|
303
|
+
err = ""
|
304
|
+
out = ""
|
305
|
+
Open3.popen3("#{XDo::XDOTOOL} get_desktop"){|stdin, stdout, stderr| out = stdout.read; err = stderr.read}
|
306
|
+
raise(XDo::XError, err) unless err.empty?
|
307
|
+
Integer(out)
|
308
|
+
end
|
309
|
+
|
310
|
+
#Creates a XWindow by calling search with the given parameters.
|
311
|
+
#The window is created from the first ID found.
|
312
|
+
#===Parameters
|
313
|
+
#[+name+] The name of the window to look for. Either a string or a Regular Expression; however, there's no guaranty that +xdotool+ gets the regexp right. Simple ones should work, though.
|
314
|
+
#[<tt>*opts</tt> (<tt>[:name, :class, :classname]</tt>) Search parameters. See XWindow.search.
|
315
|
+
#===Return value
|
316
|
+
#The created XWindow object.
|
317
|
+
#===Raises
|
318
|
+
#[XError] Error invoking +xdotool+.
|
319
|
+
#===Example
|
320
|
+
# #Exact title/class/classname match
|
321
|
+
# xwin = XDo::XWindow.from_search("xwindow.rb - SciTE")
|
322
|
+
# #Part match via regexp
|
323
|
+
# xwin = XDo::XWindow.from_search(/SciTE/)
|
324
|
+
# #Part match via string - DEPRECATED.
|
325
|
+
# xwin = XDo::XWindow.from_search("SciTE")
|
326
|
+
# #Only search the window classes
|
327
|
+
# xwin = XDo::XWindow.from_search(/SciTE/, :class)
|
328
|
+
def from_search(name, *opts)
|
329
|
+
if opts.first.kind_of?(Hash)
|
330
|
+
warn("#{caller.first}: Deprecation Warning: Using a hash as further arguments is deprecated. Pass the symbols directly.")
|
331
|
+
opts = opts.first.keys
|
332
|
+
end
|
333
|
+
|
334
|
+
ids = search(name, *opts)
|
335
|
+
raise(XDo::XError, "The window '#{name}' wasn't found!") if ids.empty?
|
336
|
+
new(ids.first)
|
337
|
+
end
|
338
|
+
|
339
|
+
#_Deprecated_. Use XWindow.from_search or XWindow.from_title instead.
|
340
|
+
def from_name(name, *opts)
|
341
|
+
warn("#{caller.first}: Deprecation Warning: ::from_name is deprecated. Use ::from_search if you want the old behaviour with the ability to specify all search parameters, or ::from_title if you just want to look through the window titles.")
|
342
|
+
from_search(name, *opts)
|
343
|
+
end
|
344
|
+
|
345
|
+
#Same as XWindow.from_search, but only looks for the window's titles to match.
|
346
|
+
#===Parameters
|
347
|
+
#[+title+] The title of the window to look for. Either a string or a Regular Expression; however, there's no guaranty that +xdotool+ gets the regexp right. Simple ones should work, though.
|
348
|
+
#===Return value
|
349
|
+
#A XWindow object made up from the first window ID found.
|
350
|
+
#===Raises
|
351
|
+
#[XError] Error invoking +xdotool+.
|
352
|
+
#===Example
|
353
|
+
# #Exact string match
|
354
|
+
# xwin = XDo::XWindow.from_title("xwindow.rb - SciTE")
|
355
|
+
# #Part match via regexp
|
356
|
+
# xwin = XDo::XWindow.from_title(/SciTE/)
|
357
|
+
# #Part match via string - DEPRECATED.
|
358
|
+
# xwin = XDo::XWindow.from_title("SciTE")
|
359
|
+
def from_title(title)
|
360
|
+
from_search(title, :name)
|
361
|
+
end
|
362
|
+
|
363
|
+
#Creates a XWindow by calling XWindow.focused_window with the given parameter.
|
364
|
+
#===Parameters
|
365
|
+
#[+notice_children+] (false) If true, you may get a child window as the active window.
|
366
|
+
#===Return value
|
367
|
+
#The newly created XWindow objects.
|
368
|
+
#===Example
|
369
|
+
# xwin = XDo::XWindow.from_focused
|
370
|
+
#===Remarks
|
371
|
+
#The XWindow.focused_window method is a bit dangerous, since it may
|
372
|
+
#find an invisible window. Use XWindow.from_active if you don't want that.
|
373
|
+
def from_focused(notice_childs = false)
|
374
|
+
new(focused_window(notice_childs))
|
375
|
+
end
|
376
|
+
|
377
|
+
#Creates a XWindow by calling active_window.
|
378
|
+
#===Return value
|
379
|
+
#The newly created XWindow object.
|
380
|
+
#===Example
|
381
|
+
# xwin = XDo::XWindow.from_active
|
382
|
+
#===Remarks
|
383
|
+
#This method does not find invisible nor child windows; if you want that,
|
384
|
+
#you should take a look at XWindow.from_focused.
|
385
|
+
def from_active
|
386
|
+
new(active_window)
|
387
|
+
end
|
388
|
+
|
389
|
+
#Returns the ID of the root window.
|
390
|
+
#===Return value
|
391
|
+
#The ID of the root window.
|
392
|
+
#===Example
|
393
|
+
# p XDo::XWindow.root_id #=> 346
|
394
|
+
def root_id
|
395
|
+
out = ""
|
396
|
+
err = ""
|
397
|
+
Open3.popen3("#{XDo::XWININFO} -root"){|stdin, stdout, stderr| out << stdout.read.strip; err << stderr.read.strip}
|
398
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
399
|
+
Integer(out.lines.to_a[0].match(/Window id:(.*?)\(/)[1].strip)
|
400
|
+
end
|
401
|
+
|
402
|
+
#Creates a XWindow refering to the root window.
|
403
|
+
#===Return value
|
404
|
+
#The newly created XWindow object.
|
405
|
+
#===Example
|
406
|
+
# rwin = XDo::XWindow.from_root
|
407
|
+
def from_root
|
408
|
+
new(root_id)
|
409
|
+
end
|
410
|
+
|
411
|
+
#Creates a invalid XWindow.
|
412
|
+
#===Return value
|
413
|
+
#The newly created XWindow object.
|
414
|
+
#===Example
|
415
|
+
# nwin = XDo::XWindow.from_null
|
416
|
+
#===Remarks
|
417
|
+
#The handle the returned XWindow object uses is zero and
|
418
|
+
#therefore invalid. You can't call #move, #resize or other
|
419
|
+
#methods on it, but it may be useful for unsetting focus.
|
420
|
+
#See also the XWindow.unfocus method.
|
421
|
+
def from_null
|
422
|
+
new(0) #Zero never is a valid window ID. Even the root window has another ID.
|
423
|
+
end
|
424
|
+
|
425
|
+
#Unsets the input focus by setting it to the invalid
|
426
|
+
#NULL window.
|
427
|
+
#===Parameters
|
428
|
+
#[+sync+] (true) If true, this method blocks until the input focus has been unset.
|
429
|
+
#===Return value
|
430
|
+
#nil.
|
431
|
+
#===Example
|
432
|
+
# win = XDo::XWindow.from_active
|
433
|
+
# win.focus
|
434
|
+
# XDo::XWindow.unfocus
|
435
|
+
def unfocus(sync = true)
|
436
|
+
from_null.focus(sync)
|
437
|
+
end
|
438
|
+
|
439
|
+
#Deprecated.
|
440
|
+
def desktop_name=(name)
|
441
|
+
warn("#{caller.first}: Deprecation warning: XWindow.desktop_name= doesn't do anything anymore.")
|
442
|
+
end
|
443
|
+
|
444
|
+
#Deprecated.
|
445
|
+
def desktop_name
|
446
|
+
warn("#{caller.first}: Deprecation warning: XWindow.desktop_name doesn't do anything anymore.")
|
447
|
+
"x-nautilus-desktop"
|
448
|
+
end
|
449
|
+
|
450
|
+
#Deprecated. Just calls XWindow.unfocus internally.
|
451
|
+
def focus_desktop
|
452
|
+
warn("#{caller.first}: Deprecation warning: XWindow.focus_desktop is deprecated. Use XWindow.unfocus instead.")
|
453
|
+
unfocus
|
454
|
+
end
|
455
|
+
alias activate_desktop focus_desktop
|
456
|
+
|
457
|
+
#Minimize all windows (or restore, if already) by sending [CTRL]+[ALT]+[D].
|
458
|
+
#Available after requireing "xdo/keyboard".
|
459
|
+
#===Return value
|
460
|
+
#Undefined.
|
461
|
+
#===Raises
|
462
|
+
#[NotImplementedError] You didn't require 'xdo/keyboard'.
|
463
|
+
#===Example
|
464
|
+
# #Everything will be minimized:
|
465
|
+
# XDo::XWindow.toggle_minimize_all
|
466
|
+
# #And now we'll restore everything.
|
467
|
+
# XDo::XWindow.toggle_minimize_all
|
468
|
+
def toggle_minimize_all
|
469
|
+
raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
470
|
+
XDo::Keyboard.ctrl_alt_d
|
471
|
+
end
|
472
|
+
|
473
|
+
#Minimizes the active window. There's no way to restore a specific minimized window.
|
474
|
+
#Available after requireing "xdo/keyboard".
|
475
|
+
#===Return value
|
476
|
+
#Undefined.
|
477
|
+
#===Raises
|
478
|
+
#[NotImplementedError] You didn't require 'xdo/keyboard'.
|
479
|
+
#===Example
|
480
|
+
# XDo::XWindow.minimize
|
481
|
+
def minimize
|
482
|
+
raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
483
|
+
XDo::Keyboard.key("Alt+F9")
|
484
|
+
end
|
485
|
+
|
486
|
+
#Maximize or normalize the active window if already maximized.
|
487
|
+
#Available after requireing "xdo/keyboard".
|
488
|
+
#===Return value
|
489
|
+
#Undefined.
|
490
|
+
#===Raises
|
491
|
+
#[NotImplementedError] You didn't require 'xdo/keyboard'.
|
492
|
+
#===Example
|
493
|
+
# XDo::XWindow.minimize
|
494
|
+
# XDo::XWindow.toggle_maximize
|
495
|
+
def toggle_maximize
|
496
|
+
raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
497
|
+
XDo::Keyboard.key("Alt+F10")
|
498
|
+
end
|
499
|
+
|
500
|
+
end
|
501
|
+
|
502
|
+
##
|
503
|
+
# :method: title=
|
504
|
+
#call-seq:
|
505
|
+
# title = str
|
506
|
+
# name = str
|
507
|
+
#
|
508
|
+
#Changes a window's title.
|
509
|
+
#===Parameters
|
510
|
+
#[+str+] The new title.
|
511
|
+
#===Return value
|
512
|
+
#+str+.
|
513
|
+
#===Raises
|
514
|
+
#[XError] Error invoking +xdotool+.
|
515
|
+
#===Example
|
516
|
+
# xwin.title = "Ruby is awesome!"
|
517
|
+
|
518
|
+
##
|
519
|
+
# :method: icon_title=
|
520
|
+
#call-seq:
|
521
|
+
# icon_title = str
|
522
|
+
# icon_name = str
|
523
|
+
#
|
524
|
+
#Changes the window's icon title, i.e. the string that is displayed in
|
525
|
+
#the task bar or panel where all open windows show up.
|
526
|
+
#===Parameters
|
527
|
+
#[+str+] The string you want to set.
|
528
|
+
#===Return value
|
529
|
+
#+str+.
|
530
|
+
#===Raises
|
531
|
+
#[XError] Error invoking +xdotool+.
|
532
|
+
#===Example
|
533
|
+
# xwin.icon_title = "This is minimized."
|
534
|
+
|
535
|
+
##
|
536
|
+
# :method: classname=
|
537
|
+
#call-seq:
|
538
|
+
# classname = str
|
539
|
+
#
|
540
|
+
#Sets a window's classname.
|
541
|
+
#===Parameters
|
542
|
+
#[+str+] The window's new classname.
|
543
|
+
#===Return value
|
544
|
+
#+str+.
|
545
|
+
#===Raises
|
546
|
+
#[XError] Error invoking +xdotool+.
|
547
|
+
#===Example
|
548
|
+
# xwin.classname = "MyNewClass"
|
549
|
+
|
550
|
+
#Creates a new XWindow object from an internal ID.
|
551
|
+
#===Parameters
|
552
|
+
#[+id+] The internal ID to create the window from.
|
553
|
+
#===Return value
|
554
|
+
#The newly created XWindow object.
|
555
|
+
#===Example
|
556
|
+
# id = XWindow.search(/edit/)[1]
|
557
|
+
# xwin = XWindow.new(id)
|
558
|
+
#===Remarks
|
559
|
+
#See also many class methods of the XWindow class which allow
|
560
|
+
#you to forget about the internal ID of a window.
|
561
|
+
def initialize(id)
|
562
|
+
@id = id.to_i
|
563
|
+
end
|
564
|
+
|
565
|
+
#Human-readable output of form
|
566
|
+
# <XDo::XWindow: "title" (window_id)>
|
567
|
+
def inspect
|
568
|
+
%Q|<#{self.class}: "#{title}" (#{id})>|
|
569
|
+
end
|
570
|
+
|
571
|
+
#Set the size of a window.
|
572
|
+
#===Parameters
|
573
|
+
#[+width+] The new width, usually in pixels.
|
574
|
+
#[+height+] The new height, usually in pixels.
|
575
|
+
#[+use_hints+] (false) If true, window sizing hints are used if they're available. This is usually done when resizing terminal windows to a specific number of rows and columns.
|
576
|
+
#[+sync+] (true) If true, this method blocks until the window has finished resizing.
|
577
|
+
#===Return value
|
578
|
+
#Undefined.
|
579
|
+
#===Raises
|
580
|
+
#[XError] Error executing +xdotool+.
|
581
|
+
#===Example
|
582
|
+
# #Resize a window to 400x300px
|
583
|
+
# xwin.resize(400, 300)
|
584
|
+
# #Resize a terminal window to 100 rows and 100 columns
|
585
|
+
# xtermwin.resize(100, 100, true)
|
586
|
+
#===Remarks
|
587
|
+
#This has no effect on maximized winwows.
|
588
|
+
def resize(width, height, use_hints = false, sync = true)
|
589
|
+
err = ""
|
590
|
+
opts = []
|
591
|
+
opts << "--usehints" if use_hints
|
592
|
+
opts << "--sync" if sync
|
593
|
+
Open3.popen3("#{XDo::XDOTOOL} windowsize #{opts.join(" ")} #{@id} #{width} #{height}"){|stdin, stdout, stderr| err << stderr.read}
|
594
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
595
|
+
end
|
596
|
+
|
597
|
+
#Moves a window. +xdotool+ is not really exact with the coordinates,
|
598
|
+
#special windows like Ubuntu's panels make it placing wrong.
|
599
|
+
#===Parameters
|
600
|
+
#[+x+] The goal X coordinate.
|
601
|
+
#[+y+] The goal Y coordinate.
|
602
|
+
#[+sync+] (true) If true, this method blocks until the window has finished moving.
|
603
|
+
#===Return value
|
604
|
+
#Undefined.
|
605
|
+
#===Raises
|
606
|
+
#[XError] Error executing +xdotool+.
|
607
|
+
#===Example
|
608
|
+
# xwin.move(100, 100)
|
609
|
+
# p xwin.abs_position #=> [101, 101]
|
610
|
+
def move(x, y, sync = true)
|
611
|
+
err = ""
|
612
|
+
opts = []
|
613
|
+
opts << "--sync" if sync
|
614
|
+
Open3.popen3("#{XDo::XDOTOOL} windowmove #{opts.join(" ")} #{@id} #{x} #{y}"){|stdin, stdout, stderr| err << stderr.read}
|
615
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
616
|
+
end
|
617
|
+
|
618
|
+
#Set the input focus to the window (but don't bring it to the front).
|
619
|
+
#===Parameters
|
620
|
+
#[+sync+] (true) If true, this method blocks until the window got the input focus.
|
621
|
+
#===Return value
|
622
|
+
#Undefined.
|
623
|
+
#===Raises
|
624
|
+
#[XError] Error invoking +xdotool+.
|
625
|
+
#===Example
|
626
|
+
# xwin.focus
|
627
|
+
#===Remarks
|
628
|
+
#This method may not work on every window manager. You should use
|
629
|
+
##activate, which is supported by more window managers.
|
630
|
+
def focus(sync = true)
|
631
|
+
err = ""
|
632
|
+
opts = []
|
633
|
+
opts << "--sync" if sync
|
634
|
+
Open3.popen3("#{XDo::XDOTOOL} windowfocus #{opts.join(" ")} #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
635
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
636
|
+
end
|
637
|
+
|
638
|
+
#The window loses the input focus by setting it to an invalid window.
|
639
|
+
#Parameters
|
640
|
+
#[+sync+] (true) If true, this method blocks until the focus has been set to nothing.
|
641
|
+
#===Return value
|
642
|
+
#Undefined.
|
643
|
+
#===Example
|
644
|
+
# xwin.focus
|
645
|
+
# xwin.unfocus
|
646
|
+
def unfocus(sync = true)
|
647
|
+
XDo::XWindow.unfocus(sync)
|
648
|
+
end
|
649
|
+
|
650
|
+
#Maps a window to the screen (makes it visible).
|
651
|
+
#===Parameters
|
652
|
+
#[+sync+] (true) If true, this method blocks until the window has been mapped.
|
653
|
+
#===Return value
|
654
|
+
#Undefined.
|
655
|
+
#===Raises
|
656
|
+
#[XError] Error invoking +xdotool+.
|
657
|
+
#===Example
|
658
|
+
# xwin.unmap #Windows are usually mapped
|
659
|
+
# xwin.map
|
660
|
+
def map(sync = true)
|
661
|
+
err = ""
|
662
|
+
opts = []
|
663
|
+
opts << "--sync" if sync
|
664
|
+
Open3.popen3("#{XDo::XDOTOOL} windowmap #{opts.join(" ")} #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
665
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
666
|
+
end
|
667
|
+
|
668
|
+
#Unmap a window from the screen (make it invisible).
|
669
|
+
#===Parameters
|
670
|
+
#[+sync+] (true) If true, this method blocks until the window has been unmapped.
|
671
|
+
#===Return value
|
672
|
+
#Undefined.
|
673
|
+
#===Raises
|
674
|
+
#[XError] Error executing +xdotool+.
|
675
|
+
#===Example
|
676
|
+
# xwin.unmap
|
677
|
+
def unmap(sync = true)
|
678
|
+
err = ""
|
679
|
+
opts = []
|
680
|
+
opts << "--sync" if sync
|
681
|
+
Open3.popen3("#{XDo::XDOTOOL} windowunmap #{opts.join(" ")} #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
682
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
683
|
+
end
|
684
|
+
|
685
|
+
#Bring a window to the front (but don't give it the input focus).
|
686
|
+
#Not implemented in all window managers.
|
687
|
+
#===Return value
|
688
|
+
#Undefined.
|
689
|
+
#===Raises
|
690
|
+
#[XError] Error executing +xdotool+.
|
691
|
+
#===Example
|
692
|
+
# xwin.raise
|
693
|
+
def raise
|
694
|
+
err = ""
|
695
|
+
Open3.popen3("#{XDo::XDOTOOL} windowraise #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
696
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
697
|
+
end
|
698
|
+
|
699
|
+
#Activate a window. That is, bring it to top and give it the input focus.
|
700
|
+
#===Parameters
|
701
|
+
#[+sync+] (true) If true, this method blocks until the window has been activated.
|
702
|
+
#===Return value
|
703
|
+
#Undefined.
|
704
|
+
#===Raises
|
705
|
+
#[XError] Error executing +xdotool+.
|
706
|
+
#===Example
|
707
|
+
# xwin.activate
|
708
|
+
#===Remarks
|
709
|
+
#This is the recommanded method to give a window the input focus, since
|
710
|
+
#it works on more window managers than #focus and also works across
|
711
|
+
#desktops.
|
712
|
+
#
|
713
|
+
#Part of the EWMH standard ACTIVE_WINDOW.
|
714
|
+
def activate(sync = true)
|
715
|
+
tried_focus = false
|
716
|
+
begin
|
717
|
+
err = ""
|
718
|
+
opts = []
|
719
|
+
opts << "--sync" if sync
|
720
|
+
Open3.popen3("#{XDo::XDOTOOL} windowactivate #{opts.join(" ")} #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
721
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
722
|
+
rescue XDo::XError
|
723
|
+
#If no window is active, xdotool's windowactivate fails,
|
724
|
+
#because it tries to determine which is the currently active window.
|
725
|
+
unless tried_focus
|
726
|
+
tried_focus = true
|
727
|
+
focus
|
728
|
+
retry
|
729
|
+
else
|
730
|
+
raise
|
731
|
+
end
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
#Move a window to a desktop.
|
736
|
+
#===Parameters
|
737
|
+
#[+num+] The 0-based index of the desktop you want the window to move to.
|
738
|
+
#===Return value
|
739
|
+
#Undefined.
|
740
|
+
#===Raises
|
741
|
+
#[XError] Error executing +xdotool+.
|
742
|
+
#===Example
|
743
|
+
# xwin.desktop = 3
|
744
|
+
#===Remarks
|
745
|
+
#Although Ubuntu systems seem to have several desktops, that isn't completely true. An usual Ubuntu system only
|
746
|
+
#has a single working desktop, on which Ubuntu sets up an arbitrary number of other "desktop views" (usually 4).
|
747
|
+
#That's kind of cheating, but I have not yet find out why it is like that. Maybe it's due to the nice cube rotating effect?
|
748
|
+
#That's the reason, why the desktop-related methods don't work with Ubuntu.
|
749
|
+
#
|
750
|
+
#Part of the EWMH standard CURRENT_DESKTOP.
|
751
|
+
def desktop=(num)
|
752
|
+
err = ""
|
753
|
+
Open3.popen3("#{XDo::XDOTOOL} set_desktop_for_window #{@id} #{num}"){|stdin, stdout, stderr| err << stderr.read}
|
754
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
755
|
+
end
|
756
|
+
|
757
|
+
#Get the desktop the window is on.
|
758
|
+
#===Return value
|
759
|
+
#The 0-based index of the desktop this window resides on.
|
760
|
+
#===Raises
|
761
|
+
#[XError] Error executing +xdotool+.
|
762
|
+
#===Example
|
763
|
+
# p xwin.desktop #=> 0
|
764
|
+
#===Remarks
|
765
|
+
#Although Ubuntu systems seem to have several desktops, that isn't completely true. An usual Ubuntu system only
|
766
|
+
#has a single working desktop, on which Ubuntu sets up an arbitrary number of other "desktop views" (usually 4).
|
767
|
+
#That's kind of cheating, but I have not yet find out why it is like that. Maybe it's due to the nice cube rotating effect?
|
768
|
+
#That's the reason, why the desktop-related methods don't work with Ubuntu.
|
769
|
+
#
|
770
|
+
#Part of the EWMH standard CURRENT_DESKTOP.
|
771
|
+
def desktop
|
772
|
+
err = ""
|
773
|
+
out = ""
|
774
|
+
Open3.popen3("#{XDo::XDOTOOL} get_desktop_for_window #{@id}"){|stdin, stdout, stderr| out = stdout.read; err << stderr.read}
|
775
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
776
|
+
Integer(out)
|
777
|
+
end
|
778
|
+
|
779
|
+
#The title of the window or nil if it doesn't have a title.
|
780
|
+
#===Return value
|
781
|
+
#The window's title, encoded as UTF-8, or nil if the window doesn't have a title.
|
782
|
+
#===Raises
|
783
|
+
#[XError] Error executing +xwininfo+.
|
784
|
+
#===Example
|
785
|
+
# p xwin.title #=> "xwindow.rb SciTE"
|
786
|
+
def title
|
787
|
+
err = ""
|
788
|
+
out = ""
|
789
|
+
if @id == XWindow.root_id #This is the root window
|
790
|
+
return "(the root window)"
|
791
|
+
elsif @id.zero?
|
792
|
+
return "(NULL window)"
|
793
|
+
else
|
794
|
+
Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
795
|
+
end
|
796
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
797
|
+
title = out.strip.lines.to_a[0].match(/"(.*)"/)[1] rescue Kernel.raise(XDo::XError, "No window with ID #{@id} found!")
|
798
|
+
return title #Kann auch nil sein, dann ist das Fenster namenlos.
|
799
|
+
end
|
800
|
+
|
801
|
+
#The absolute position of the window on the screen.
|
802
|
+
#===Return value
|
803
|
+
#A two-element array of form <tt>[x, y]</tt>.
|
804
|
+
#===Raises
|
805
|
+
#[XError] Error executing +xwininfo+.
|
806
|
+
#===Example
|
807
|
+
# p xwin.abs_position #=> [0, 51]
|
808
|
+
def abs_position
|
809
|
+
out = ""
|
810
|
+
err = ""
|
811
|
+
Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
812
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
813
|
+
out = out.strip.lines.to_a
|
814
|
+
x = out[2].match(/:\s+(\d+)/)[1]
|
815
|
+
y = out[3].match(/:\s+(\d+)/)[1]
|
816
|
+
[x.to_i, y.to_i]
|
817
|
+
end
|
818
|
+
alias position abs_position
|
819
|
+
|
820
|
+
#The position of the window relative to it's parent window.
|
821
|
+
#===Return value
|
822
|
+
#A two-element array of form <tt>[x, y]</tt>.
|
823
|
+
#===Raises
|
824
|
+
#[XError] Error executing +xdotool+.
|
825
|
+
#===Example
|
826
|
+
# p xwin.rel_position => [0, 51]
|
827
|
+
def rel_position
|
828
|
+
out = ""
|
829
|
+
err = ""
|
830
|
+
Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
831
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
832
|
+
out = out.strip.lines.to_a
|
833
|
+
x = out[4].match(/:\s+(\d+)/)[1]
|
834
|
+
y = out[5].match(/:\s+(\d+)/)[1]
|
835
|
+
[x.to_i, y.to_i]
|
836
|
+
end
|
837
|
+
|
838
|
+
#The size of the window.
|
839
|
+
#===Return value
|
840
|
+
#A two-element array of form <tt>[width, height]</tt>.
|
841
|
+
#===Raises
|
842
|
+
#[XError] Error executing +xwininfo+.
|
843
|
+
#===Example
|
844
|
+
# p xwin.size #=> [1280, 948]
|
845
|
+
def size
|
846
|
+
out = ""
|
847
|
+
err = ""
|
848
|
+
Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
849
|
+
out = out.strip.lines.to_a
|
850
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
851
|
+
width = out[6].match(/:\s+(\d+)/)[1]
|
852
|
+
height = out[7].match(/:\s+(\d+)/)[1]
|
853
|
+
[width.to_i, height.to_i]
|
854
|
+
end
|
855
|
+
|
856
|
+
#true if the window is mapped to the screen.
|
857
|
+
#===Return value
|
858
|
+
#nil if the window is not mapped, an integer value otherwise.
|
859
|
+
#===Raises
|
860
|
+
#[XError] Error executing +xwininfo+.
|
861
|
+
#===Example
|
862
|
+
# p xwin.visible? #=> 470
|
863
|
+
# xwin.unmap
|
864
|
+
# p xwin.visible? #=> nil
|
865
|
+
def visible?
|
866
|
+
err = ""
|
867
|
+
out = ""
|
868
|
+
Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
869
|
+
out = out.strip
|
870
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
871
|
+
return out =~ /IsViewable/
|
872
|
+
end
|
873
|
+
|
874
|
+
#Returns true if the window exists.
|
875
|
+
#===Return value
|
876
|
+
#true or false.
|
877
|
+
#===Example
|
878
|
+
# p xwin.exists? #=> true
|
879
|
+
def exists?
|
880
|
+
XDo::XWindow.id_exists?(@id)
|
881
|
+
end
|
882
|
+
|
883
|
+
#Closes a window by activating it and then sending [ALT] + [F4].
|
884
|
+
#===Return value
|
885
|
+
#nil.
|
886
|
+
#===Raises
|
887
|
+
#[NotImplementedError] You didn't require "xdo/keyboard".
|
888
|
+
#===Example
|
889
|
+
# xwin.close
|
890
|
+
#===Remarks
|
891
|
+
#A program could ask to save data.
|
892
|
+
#
|
893
|
+
#Use #kill! to kill the process running the window.
|
894
|
+
#
|
895
|
+
#Available after requireing "xdo/keyboard".
|
896
|
+
def close
|
897
|
+
Kernel.raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
898
|
+
activate
|
899
|
+
XDo::Keyboard.char("Alt+F4")
|
900
|
+
sleep 0.5
|
901
|
+
nil
|
902
|
+
end
|
903
|
+
|
904
|
+
#More aggressive variant of #close. Think of +close!+ as
|
905
|
+
#the middle between #close and #kill!. It first tries
|
906
|
+
#to close the window by calling #close and if that
|
907
|
+
#does not succeed (within +timeout+ seconds), it will call #kill!.
|
908
|
+
#===Paramters
|
909
|
+
#[+timeout+] (2) The time to wait before using #kill!, in seconds.
|
910
|
+
#===Return value
|
911
|
+
#Undefined.
|
912
|
+
#===Raises
|
913
|
+
#[XError] Error executing +xkill+.
|
914
|
+
#===Example
|
915
|
+
# xwin.close!
|
916
|
+
#===Remarks
|
917
|
+
#Available after requireing "xdo/keyboard".
|
918
|
+
def close!(timeout = 2)
|
919
|
+
Kernel.raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
920
|
+
#Try to close normally
|
921
|
+
close
|
922
|
+
#Check if it's deleted
|
923
|
+
if exists?
|
924
|
+
#If not, wait some seconds and then check again
|
925
|
+
sleep timeout
|
926
|
+
if exists?
|
927
|
+
#If it's not deleted after some time, force it to close.
|
928
|
+
kill!
|
929
|
+
end
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
#Kills the process that runs a window. The window will be
|
934
|
+
#terminated immediatly, if that isn't what you want, have
|
935
|
+
#a look at #close.
|
936
|
+
#===Return value
|
937
|
+
#nil.
|
938
|
+
#===Raises
|
939
|
+
#[XError] Error executing +xkill+.
|
940
|
+
#===Example
|
941
|
+
# xwin.kill!
|
942
|
+
def kill!
|
943
|
+
out = ""
|
944
|
+
err = ""
|
945
|
+
Open3.popen3("#{XDo::XKILL} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
946
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
947
|
+
nil
|
948
|
+
end
|
949
|
+
|
950
|
+
#Returns the window's internal ID.
|
951
|
+
#===Return value
|
952
|
+
#An integer describing the window's internal ID.
|
953
|
+
#===Example
|
954
|
+
# p xwin.to_i #=> 29361095
|
955
|
+
def to_i
|
956
|
+
@id
|
957
|
+
end
|
958
|
+
|
959
|
+
#Returns a window's title.
|
960
|
+
#===Return value
|
961
|
+
#The window's title.
|
962
|
+
#===Example
|
963
|
+
# p xwin.to_s #=> "xwindow.rb * SciTE"
|
964
|
+
def to_s
|
965
|
+
title
|
966
|
+
end
|
967
|
+
|
968
|
+
#true if the internal ID is zero.
|
969
|
+
#===Return value
|
970
|
+
#true or false.
|
971
|
+
#===Example
|
972
|
+
# p xwin.zero? #=> false
|
973
|
+
def zero?
|
974
|
+
@id.zero?
|
975
|
+
end
|
976
|
+
|
977
|
+
#true if the internal ID is not zero.
|
978
|
+
#===Return value
|
979
|
+
#nil or the internal ID.
|
980
|
+
#===Example
|
981
|
+
# p xwin.nonzero? #=> 29361095
|
982
|
+
def nonzero?
|
983
|
+
@id.nonzero?
|
984
|
+
end
|
985
|
+
|
986
|
+
[:"name=", :"icon_name=", :"classname="].each do |sym|
|
987
|
+
define_method(sym) do |str|
|
988
|
+
set_window(sym.to_s[0..-2].gsub("_", "-"), str.encode("UTF-8"))
|
989
|
+
str
|
990
|
+
end
|
991
|
+
end
|
992
|
+
alias title= name=
|
993
|
+
alias icon_title= icon_name=
|
994
|
+
|
995
|
+
private
|
996
|
+
|
997
|
+
#Calls +xdotool+'s set_window command with the given options.
|
998
|
+
def set_window(option, value)
|
999
|
+
err = ""
|
1000
|
+
Open3.popen3("#{XDOTOOL} set_window --#{option} '#{value}' #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
1001
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
end
|