xdo 0.0.1-x86-linux
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +46 -0
- data/bin/xinfo.rb +136 -0
- data/lib/README.rdoc +55 -0
- data/lib/xdo.rb +28 -0
- data/lib/xdo/clipboard.rb +109 -0
- data/lib/xdo/drive.rb +66 -0
- data/lib/xdo/keyboard.rb +274 -0
- data/lib/xdo/mouse.rb +143 -0
- data/lib/xdo/wxaliases.rb +57 -0
- data/lib/xdo/xwindow.rb +416 -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 +23 -0
- data/test/test_keyboard.rb +60 -0
- data/test/test_mouse.rb +28 -0
- data/test/test_xwindow.rb +104 -0
- metadata +94 -0
data/lib/xdo/xwindow.rb
ADDED
@@ -0,0 +1,416 @@
|
|
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 "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 Jaunty) 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
|
+
#Many methods accept a +name+ parameter; be aware that this name is not the whole
|
24
|
+
#name of a window, but a pattern to match. So, if you pass in "edit", it will even match "gedit".
|
25
|
+
#+xdotool+ handles that parameter with C style regexps.
|
26
|
+
#
|
27
|
+
#The +opt+ parameter of many methods is a hash that can have the following keys:
|
28
|
+
# Key | Effect if true
|
29
|
+
# =============+====================================
|
30
|
+
# :title | Window titles are searched.
|
31
|
+
# -------------+------------------------------------
|
32
|
+
# :name | Window names are searched.
|
33
|
+
# -------------+------------------------------------
|
34
|
+
# :class | Window classes are searched.
|
35
|
+
# -------------+------------------------------------
|
36
|
+
# :onlyvisible | Only visible windows are searched.
|
37
|
+
#The default values for them depend on the method you want to use. See the method's
|
38
|
+
#argument list to find out if a parameter is set to true or, if it isn't mentioned, to nil.
|
39
|
+
#
|
40
|
+
#Be <i>very careful</i> with the methods that are part of the two desktop EWMH standards.
|
41
|
+
#After I set the number of desktops and changed the current desktop, I had to reboot my
|
42
|
+
#system to get the original configuration back. I don't know if I'm not using +xdotool+ correct,
|
43
|
+
#but neither my library nor +xdotool+ itself could rescue my desktop settings. Btw, that's the
|
44
|
+
#reason why it's not in XDo's unit tests (but it should work; at least in one way...).
|
45
|
+
class XWindow
|
46
|
+
include Open3
|
47
|
+
#The internal ID of the window.
|
48
|
+
attr_reader :id
|
49
|
+
|
50
|
+
class << self
|
51
|
+
include Open3
|
52
|
+
#Checks if a window whose name matches +name+ exists.
|
53
|
+
#Think about passing :onlyvisible in the +opt+ hash.
|
54
|
+
def exists?(name, opts = {title: true, name: true, :class => true})
|
55
|
+
begin
|
56
|
+
!search(name, opts).empty?
|
57
|
+
rescue
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#Returns true if the given window ID exists, false otherwise.
|
63
|
+
def id_exists?(id)
|
64
|
+
err = ""
|
65
|
+
popen3("#{XDo::XWININFO} -id #{id}"){|stdin, stdout, stderr| err << stderr.read}
|
66
|
+
return false unless err.empty?
|
67
|
+
return true
|
68
|
+
end
|
69
|
+
|
70
|
+
#Waits for a window name to exist and returns the ID of that window.
|
71
|
+
#Returns immediately if the window does already exist.
|
72
|
+
def wait_for_window(name, opts = {title: true, name: true, :class => true, :onlyvisible => true})
|
73
|
+
loop{break if exists?(name, opts);sleep(0.5)}
|
74
|
+
sleep 1 #To ensure it's really there
|
75
|
+
search(name, opts).first
|
76
|
+
end
|
77
|
+
|
78
|
+
#Waits for a window to close. If the window does not exists when calling +wait_for_close+,
|
79
|
+
#the method returns immediately.
|
80
|
+
def wait_for_close(name, opts = {title: true, name: true, :class => true, :onlyvisible => true})
|
81
|
+
loop{break if !exists?(name, opts);sleep(0.5)}
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
#Search for a window name to get the internal ID of a window.
|
86
|
+
#Return value is an array containing all found IDs or an
|
87
|
+
#empty array if none is found.
|
88
|
+
def search(name, opts = {title: true, name: true, :class => true})
|
89
|
+
cmd = "#{XDo::XDOTOOL} search "
|
90
|
+
opts.each_pair{|key, value| cmd << "--#{key} " if value}
|
91
|
+
cmd << '"' << name << '"'
|
92
|
+
#Error wird nicht behandelt, weil im Fehlerfall einfach nur ein leeres Array zurückkommen soll
|
93
|
+
out = `#{cmd}`
|
94
|
+
out.lines.to_a.collect{|l| l.strip.to_i}
|
95
|
+
end
|
96
|
+
|
97
|
+
#Returns the internal ID of the currently focused window.
|
98
|
+
#If the +notice_childs+ parameter is true, also childwindows
|
99
|
+
#are noticed. This method may find an invisible window, see
|
100
|
+
#active_window for a more reliable method.
|
101
|
+
def focused_window(notice_childs = false)
|
102
|
+
err = ""
|
103
|
+
out = ""
|
104
|
+
popen3("#{XDo::XDOTOOL} getwindowfocus #{notice_childs ? "-f" : ""}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
105
|
+
raise(XDo::XError, err) unless err.empty?
|
106
|
+
return out.to_i
|
107
|
+
end
|
108
|
+
|
109
|
+
#Returns the internal ID of the currently focused window.
|
110
|
+
#This method is more reliable than focused_window.
|
111
|
+
#Part of the EWMH standard ACTIVE_WINDOW.
|
112
|
+
def active_window
|
113
|
+
err = ""
|
114
|
+
out = ""
|
115
|
+
popen3("#{XDo::XDOTOOL} getactivewindow"){|stdin, stdout, stderr| out = stdout.read; err = stderr.read}
|
116
|
+
raise(XDo::XError, err) unless err.empty?
|
117
|
+
return Integer(out)
|
118
|
+
end
|
119
|
+
|
120
|
+
#Set the number of working desktops.
|
121
|
+
#Part of the EWMH standard WM_DESKTOP.
|
122
|
+
def desktop_num=(num)
|
123
|
+
err = ""
|
124
|
+
popen3("#{XDo::XDOTOOL} set_num_desktops #{num}"){|stdin, stdout, stderr| err << stderr.read}
|
125
|
+
raise(XDo::Error, err) unless err.empty?
|
126
|
+
num
|
127
|
+
end
|
128
|
+
|
129
|
+
#Get the number of working desktops.
|
130
|
+
#Part of the EWMH standard WM_DESKTOP.
|
131
|
+
def desktop_num
|
132
|
+
err = ""
|
133
|
+
out = ""
|
134
|
+
popen3("#{XDo::XDOTOOL} get_num_desktops"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
135
|
+
raise(XDo::XError, err) unless err.empty?
|
136
|
+
Integer(out)
|
137
|
+
end
|
138
|
+
|
139
|
+
#Change the view to desktop +num+.
|
140
|
+
#Part of the EWMH standard CURRENT_DESKTOP.
|
141
|
+
def desktop=(num)
|
142
|
+
err = ""
|
143
|
+
popen3("#{XDo::XDOTOOL} set_desktop #{num}"){|stdin, stdout, stderr| err << stderr.read}
|
144
|
+
raise(XDo::XError, err) unless err.empty?
|
145
|
+
num
|
146
|
+
end
|
147
|
+
|
148
|
+
#Output the number of the active desktop.
|
149
|
+
#Part of the EWMH standard CURRENT_DESKTOP.
|
150
|
+
def desktop
|
151
|
+
err = ""
|
152
|
+
out = ""
|
153
|
+
popen3("#{XDo::XDOTOOL} get_desktop"){|stdin, stdout, stderr| out = stdout.read; err = stderr.read}
|
154
|
+
raise(XDo::XError, err) unless err.empty?
|
155
|
+
Integer(out)
|
156
|
+
end
|
157
|
+
|
158
|
+
#Creates a XWindow by calling search with the given parameters.
|
159
|
+
#The window is created from the first ID found.
|
160
|
+
def from_name(name, opts = {title: true, name: true, :class => true})
|
161
|
+
ids = search(name, opts)
|
162
|
+
raise(XDo::XError, "The window '#{name}' wasn't found!") if ids.empty?
|
163
|
+
new(ids.first)
|
164
|
+
end
|
165
|
+
|
166
|
+
#Creates a XWindow by calling focused_window with the given parameter.
|
167
|
+
def from_focused(notice_childs = false)
|
168
|
+
new(focused_window(notice_childs))
|
169
|
+
end
|
170
|
+
|
171
|
+
#Creates a XWindow by calling active_window.
|
172
|
+
def from_active
|
173
|
+
new(active_window)
|
174
|
+
end
|
175
|
+
|
176
|
+
#Set this to the name of your desktop window.
|
177
|
+
def desktop_name=(name)
|
178
|
+
@desktop_name = name
|
179
|
+
end
|
180
|
+
|
181
|
+
#Name of the desktop window. Default is "x-nautilus-desktop".
|
182
|
+
def desktop_name
|
183
|
+
@desktop_name ||= "x-nautilus-desktop"
|
184
|
+
end
|
185
|
+
|
186
|
+
#Activate the desktop
|
187
|
+
def focus_desktop
|
188
|
+
desktop = from_name(desktop_name)
|
189
|
+
desktop.focus
|
190
|
+
desktop.activate
|
191
|
+
end
|
192
|
+
alias activate_desktop focus_desktop
|
193
|
+
|
194
|
+
#Minimize all windows (or restore, if already) by sending [CTRL]+[ALT]+[D].
|
195
|
+
#Available after requireing "xdo/keyboard".
|
196
|
+
def toggle_minimize_all
|
197
|
+
raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
198
|
+
XDo::Keyboard.ctrl_alt_d
|
199
|
+
end
|
200
|
+
|
201
|
+
#Minimizes the active window. There's no way to restore a specific minimized window.
|
202
|
+
#Available after requireing "xdo/keyboard".
|
203
|
+
def minimize
|
204
|
+
raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
205
|
+
XDo::Keyboard.key("Alt+F9")
|
206
|
+
end
|
207
|
+
|
208
|
+
#Maximize or normalize the active window if already maximized.
|
209
|
+
#Available after requireing "xdo/keyboard".
|
210
|
+
def toggle_maximize
|
211
|
+
raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
212
|
+
XDo::Keyboard.key("Alt+F10")
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
#Creates a new XWindow object from an internal ID. See the XWindow class methods.
|
218
|
+
def initialize(id)
|
219
|
+
@id = id.to_i
|
220
|
+
end
|
221
|
+
|
222
|
+
#Human-readable output of form
|
223
|
+
# <XDo::XWindow: "title" (window_id)>
|
224
|
+
def inspect
|
225
|
+
%Q|<XDo::XWindow: "#{title}" (#{id})>|
|
226
|
+
end
|
227
|
+
|
228
|
+
#Set the size of a window. This has no effect on maximized winwows.
|
229
|
+
def resize(width, height, use_hints = false)
|
230
|
+
err = ""
|
231
|
+
cmd = "#{XDo::XDOTOOL} windowsize #{use_hints ? "--usehints " : ""}#{@id} #{width} #{height}"
|
232
|
+
popen3("#{cmd}"){|stdin, stdout, stderr| err << stderr.read}
|
233
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
234
|
+
end
|
235
|
+
|
236
|
+
#Moves a window. +xdotool+ is not really exact with the coordinates,
|
237
|
+
#the window will be within a range of +-10 pixels.
|
238
|
+
def move(x, y)
|
239
|
+
err = ""
|
240
|
+
popen3("#{XDo::XDOTOOL} windowmove #{@id} #{x} #{y}"){|stdin, stdout, stderr| err << stderr.read}
|
241
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
242
|
+
end
|
243
|
+
|
244
|
+
#Set the input focus to the window (but don't bring it to the front).
|
245
|
+
def focus
|
246
|
+
err = ""
|
247
|
+
popen3("#{XDo::XDOTOOL} windowfocus #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
248
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
249
|
+
end
|
250
|
+
|
251
|
+
#The window loses the input focus by setting it to the desktop.
|
252
|
+
def unfocus
|
253
|
+
XDo::XWindow.focus_desktop
|
254
|
+
end
|
255
|
+
|
256
|
+
#Map a window to the screen (make it visible).
|
257
|
+
def map
|
258
|
+
err = ""
|
259
|
+
popen3("#{XDo::XDOTOOL} windowmap #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
260
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
261
|
+
end
|
262
|
+
|
263
|
+
#Unmap a window from the screen (make it invisible).
|
264
|
+
def unmap
|
265
|
+
err = ""
|
266
|
+
popen3("#{XDo::XDOTOOL} windowunmap #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
267
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
268
|
+
end
|
269
|
+
|
270
|
+
#Bring a window to the front (but don't give it the input focus).
|
271
|
+
#Not implemented in all window managers.
|
272
|
+
def raise
|
273
|
+
err = ""
|
274
|
+
popen3("#{XDo::XDOTOOL} windowraise #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
275
|
+
raise(XDo::XError, err) unless err.empty?
|
276
|
+
end
|
277
|
+
|
278
|
+
#Activate a window. That is, bring it to top and give it the input focus.
|
279
|
+
#Part of the EWMH standard ACTIVE_WINDOW.
|
280
|
+
def activate
|
281
|
+
err = ""
|
282
|
+
popen3("#{XDo::XDOTOOL} windowactivate #{@id}"){|stdin, stdout, stderr| err << stderr.read}
|
283
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
284
|
+
end
|
285
|
+
|
286
|
+
#Move a window to a desktop.
|
287
|
+
#Part of the EWMH standard CURRENT_DESKTOP.
|
288
|
+
def desktop=(num)
|
289
|
+
err = ""
|
290
|
+
popen3("#{XDo::XDOTOOL} set_desktop_for_window #{@id} #{num}"){|stdin, stdout, stderr| err << stderr.read}
|
291
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
292
|
+
end
|
293
|
+
|
294
|
+
#Get the desktop the window is on.
|
295
|
+
#Part of the EWMH standard CURRENT_DESKTOP.
|
296
|
+
def desktop
|
297
|
+
err = ""
|
298
|
+
out = ""
|
299
|
+
popen3("#{XDo::XDOTOOL} get_desktop_for_window #{@id}"){|stdin, stdout, stderr| out = stdout.read; err << stderr.read}
|
300
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
301
|
+
Integer(out)
|
302
|
+
end
|
303
|
+
|
304
|
+
#The title of the window or nil if it doesn't have a title.
|
305
|
+
def title
|
306
|
+
err = ""
|
307
|
+
out = ""
|
308
|
+
popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
309
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
310
|
+
title = out.strip.lines.to_a[0].match(/"(.*)"/)[1] rescue Kernel.raise(XDo::XError, "No window with ID #{@id} found!")
|
311
|
+
return title #Kann auch nil sein, dann ist das Fenster namenlos.
|
312
|
+
end
|
313
|
+
|
314
|
+
#The absolute position of the window on the screen.
|
315
|
+
def abs_position
|
316
|
+
out = ""
|
317
|
+
err = ""
|
318
|
+
popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
319
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
320
|
+
out = out.strip.lines.to_a
|
321
|
+
x = out[2].match(/:\s+(\d+)/)[1]
|
322
|
+
y = out[3].match(/:\s+(\d+)/)[1]
|
323
|
+
[x.to_i, y.to_i]
|
324
|
+
end
|
325
|
+
alias position abs_position
|
326
|
+
|
327
|
+
#The position of the window relative to it's parent window.
|
328
|
+
def rel_position
|
329
|
+
out = ""
|
330
|
+
err = ""
|
331
|
+
popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
332
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
333
|
+
out = out.strip.lines.to_a
|
334
|
+
x = out[4].match(/:\s+(\d+)/)[1]
|
335
|
+
y = out[5].match(/:\s+(\d+)/)[1]
|
336
|
+
[x.to_i, y.to_i]
|
337
|
+
end
|
338
|
+
|
339
|
+
#The size of the window.
|
340
|
+
def size
|
341
|
+
out = ""
|
342
|
+
err = ""
|
343
|
+
popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
344
|
+
out = out.strip.lines.to_a
|
345
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
346
|
+
width = out[6].match(/:\s+(\d+)/)[1]
|
347
|
+
height = out[7].match(/:\s+(\d+)/)[1]
|
348
|
+
[width.to_i, height.to_i]
|
349
|
+
end
|
350
|
+
|
351
|
+
#true if the window is mapped to the screen.
|
352
|
+
def visible?
|
353
|
+
err = ""
|
354
|
+
out = ""
|
355
|
+
popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
356
|
+
out = out.strip.lines.to_a
|
357
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
358
|
+
return out[17].match(/:\s+(\w+)/)[1] == "IsViewable" ? true : false
|
359
|
+
end
|
360
|
+
|
361
|
+
#Returns true if the window exists.
|
362
|
+
def exists?
|
363
|
+
XDo::XWindow.exists?(@id)
|
364
|
+
end
|
365
|
+
|
366
|
+
#Closes a window by activating it and then sending [ALT] + [F4].
|
367
|
+
#A program could ask to save data.
|
368
|
+
#Use #kill! to kill the process running the window.
|
369
|
+
#Available after requireing "xdo/keyboard"
|
370
|
+
def close
|
371
|
+
raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
372
|
+
activate
|
373
|
+
sleep 0.5
|
374
|
+
XDo::Keyboard.char("Alt+F4")
|
375
|
+
sleep 0.5
|
376
|
+
nil
|
377
|
+
end
|
378
|
+
|
379
|
+
#More aggressive variant of #close. Think of +close!+ as
|
380
|
+
#the middle between #close and #kill!. It first tries
|
381
|
+
#to close the window by calling #close and if that
|
382
|
+
#does not succeed (within +timeout+ seconds), it will call #kill!.
|
383
|
+
#Available after requireing "xdo/keyboard".
|
384
|
+
def close!(timeout = 2)
|
385
|
+
raise(NotImplementedError, "You have to require 'xdo/keyboard' before you can use #{__method__}!") unless defined? XDo::Keyboard
|
386
|
+
#Try to close normally
|
387
|
+
close
|
388
|
+
#Check if it's deleted
|
389
|
+
if exists?
|
390
|
+
#If not, wait some seconds and then check again
|
391
|
+
sleep timeout
|
392
|
+
if exists?
|
393
|
+
#If it's not deleted after some time, force it to close.
|
394
|
+
kill!
|
395
|
+
else
|
396
|
+
return
|
397
|
+
end
|
398
|
+
else
|
399
|
+
return
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
#Kills the process that runs a window. The window will be
|
404
|
+
#terminated immediatly, if that isn't what you want, have
|
405
|
+
#a look at #close.
|
406
|
+
def kill!
|
407
|
+
out = ""
|
408
|
+
err = ""
|
409
|
+
popen3("#{XDo::XKILL} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
410
|
+
Kernel.raise(XDo::XError, err) unless err.empty?
|
411
|
+
nil
|
412
|
+
end
|
413
|
+
|
414
|
+
end
|
415
|
+
|
416
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#Encoding: UTF-8
|
3
|
+
#--
|
4
|
+
#This file is part of XDo. Copyright © 2009 by Marvin Gülker.
|
5
|
+
#XDo is published under Ruby's license. See http://www.ruby-lang.org/en/LICENSE.txt
|
6
|
+
# Initia in potestate nostra sunt, de eventu fortuna iudicat.
|
7
|
+
#++
|
8
|
+
|
9
|
+
=begin
|
10
|
+
This is the full XDo demo. We will create a
|
11
|
+
command-line program that will be able to open
|
12
|
+
and close the default CD drive, open gedit and
|
13
|
+
type some text in tabular form and click the
|
14
|
+
"show desktop" button in the lower-left corner of
|
15
|
+
the Ubuntu desktop. Even if you don't use Ubuntu,
|
16
|
+
you may get the logic and find this demo useful.
|
17
|
+
|
18
|
+
Every OS and even every computer are different in their
|
19
|
+
user interface. If you'd like to add sample files for a
|
20
|
+
specific OS, feel free to contact me at sutniuq ät gmx Dot net.
|
21
|
+
=end
|
22
|
+
|
23
|
+
#First, require all Xdo files.
|
24
|
+
require "xdo/clipboard"
|
25
|
+
require "xdo/drive"
|
26
|
+
require "xdo/keyboard"
|
27
|
+
require "xdo/mouse"
|
28
|
+
require "xdo/xwindow"
|
29
|
+
|
30
|
+
class XXX
|
31
|
+
|
32
|
+
def keyboard
|
33
|
+
#Make sure gedit is not running, since it would open
|
34
|
+
#a new tab and not a new window if invoked again.
|
35
|
+
raise(RuntimeError, "gedit is already running; please close it before using.") unless XDo::XWindow.search("gedit").empty?
|
36
|
+
#If we would use system in the main program to open
|
37
|
+
#gedit, it would hang until gedit is closed. That's why
|
38
|
+
#we run it in a separate process.
|
39
|
+
fork{system("gedit")}
|
40
|
+
#Now we wait until gedit has made up its GUI.
|
41
|
+
#wait_for_window returns the ID of the found window, so
|
42
|
+
#we catch it and...
|
43
|
+
id = XDo::XWindow.wait_for_window("gedit")
|
44
|
+
#...use it to create a new reference to gedit's window.
|
45
|
+
#Note that this is a pseudo reference, not a real one.
|
46
|
+
#XDo allows you to create references even to unexisting
|
47
|
+
#windows, so the X window manager will not recognize
|
48
|
+
#the pseudo reference. You can check if the window
|
49
|
+
#you're looking for exists by calling #exists? on the
|
50
|
+
#XWindow object.
|
51
|
+
xwin = XDo::XWindow.new(id)
|
52
|
+
#Now we move it 20 pixels right and 10 down.
|
53
|
+
xwin.move(xwin.position[0] + 20, xwin.position[1] + 10)
|
54
|
+
#After having fun with it, let's do something useful.
|
55
|
+
#XDo::Keyboard.simulate fakes keystrokes and recognizes
|
56
|
+
#special escape sequences in braces { and }. For some
|
57
|
+
#special characters like tab you can use an ASCII escape
|
58
|
+
#like \t. The most special keys like [ESC] don't have
|
59
|
+
#those a shortcut, so you will have to write {ESC} in
|
60
|
+
#order to send them.
|
61
|
+
XDo::Keyboard.simulate("This will be some test text.")
|
62
|
+
XDo::Keyboard.ctrl_a
|
63
|
+
XDo::Keyboard.delete #This will send a BackSpace keypress. If you want a DEL, pass in true as a parameter.
|
64
|
+
#I promised at the beginning, we were goint to create data in tabular form,
|
65
|
+
#so here it is:
|
66
|
+
XDo::Keyboard.type("Percentage of smokers in different jobs in Germany".upcase) #type is usually faster than simulate if the text doesn't contain special characters or escape sequences
|
67
|
+
2.times{XDo::Keyboard.return} #Return is the Enter key
|
68
|
+
#Set up the data
|
69
|
+
headings = ["Job", "Percentage"]
|
70
|
+
data = [
|
71
|
+
["Judge", 28],
|
72
|
+
["Architect", 29],
|
73
|
+
["Doctor", 30],
|
74
|
+
["Nurse", 45],
|
75
|
+
["Teacher", 53]
|
76
|
+
]
|
77
|
+
#Create the table headings
|
78
|
+
str =""
|
79
|
+
headings[0..-2].each{|h| str << h << "{TAB}{TAB}"}
|
80
|
+
str << headings[-1] << "\n"#No tab after the last heading
|
81
|
+
#Type the heading
|
82
|
+
XDo::Keyboard.simulate(str)
|
83
|
+
#Make a line between the heading and the data
|
84
|
+
XDo::Keyboard.type("=" * 30)
|
85
|
+
XDo::Keyboard.return
|
86
|
+
#Write the table data
|
87
|
+
data.each do |job, percentage|
|
88
|
+
#Insert tabs. If a word is longer than 8 (the normal tab size), don't append a second tab.
|
89
|
+
XDo::Keyboard.simulate("#{job}#{job.length > 8 ? "\t" : "\t\t"}#{percentage}\n") #\t will trigger the Tab key, \n the Return key.
|
90
|
+
end
|
91
|
+
#Oh yes, and save it of course. XDo::Keyboard tries to
|
92
|
+
#send every method name as a key combination if the
|
93
|
+
#method is not defined already. The method name
|
94
|
+
#will be capitalized and every underscore _ replaced
|
95
|
+
#by a + (that's internally important to combine keys).
|
96
|
+
File.delete("#{ENV["HOME"]}/testXDo.txt") if File.file? "#{ENV["HOME"]}/testXDo.txt" #This is the file we'll save to
|
97
|
+
XDo::Keyboard.ctrl_s
|
98
|
+
#Wait for the save window to exist. I can't use wait_for_window here, since
|
99
|
+
#it's named different in every language. Me as a German user have a title
|
100
|
+
#of "Speichern unter...", an english OS may show "Save as...".
|
101
|
+
sleep 1
|
102
|
+
#You really shouldn't try to simulate the ~ sign. xdotool seems to have a bug, so
|
103
|
+
#~ can't be simulated with one command. My library tries to simulate it with one
|
104
|
+
#command and it sometimes works, but you mustn't rely on it. Therefore I use
|
105
|
+
#the HOME environment variable here to get the home directory, rather than ~.
|
106
|
+
XDo::Keyboard.simulate("#{ENV["HOME"]}/testXDo.txt")
|
107
|
+
sleep 1 #gedit terminates if send [ALT]+[A] immediatly after the path
|
108
|
+
XDo::Keyboard.alt_s
|
109
|
+
#Now, let's duplicate our table. We could send all the stuff again,
|
110
|
+
#but I want to introduce you in the use of the X clipboard.
|
111
|
+
#First, we have to mark our text in order to be copied to the clipboard:
|
112
|
+
xwin.activate #After saving the window isn't active anymore
|
113
|
+
sleep 1 #Wait for the gedit window to be active again
|
114
|
+
XDo::Keyboard.ctrl_a
|
115
|
+
#Then copy it to the clipboard (you see, it's quite useful to know keyboard shortcuts)
|
116
|
+
XDo::Keyboard.ctrl_c
|
117
|
+
#gedit copies, like the most programs, it's data to the CLIPBOARD clipboard.
|
118
|
+
#There are two other clipboard, PRIMARY and SECONDARY, but we won't
|
119
|
+
#diskuss them here. If you are sure you've copied data to the clipboard and
|
120
|
+
##read_clipboard doesn't find anything, check out #read_primary and #read_secondary.
|
121
|
+
#Also note, that XDo can only handle plain text data.
|
122
|
+
cliptext = XDo::Clipboard.read_clipboard
|
123
|
+
#Move to the end of the text
|
124
|
+
XDo::Keyboard.simulate("{DOWN}" * 9)
|
125
|
+
XDo::Keyboard.simulate("{END}")
|
126
|
+
#Since #simulate interprets \t correctly as a tab, we can simply put
|
127
|
+
#the clipboard's text in that method:
|
128
|
+
XDo::Keyboard.simulate("\n#{cliptext}") #Begin a new line before inserting the duplicate
|
129
|
+
sleep 1
|
130
|
+
XDo::Keyboard.ctrl_s
|
131
|
+
#Give some time to see the result
|
132
|
+
sleep 5
|
133
|
+
#Than close gedit. There are three methods to close a window,
|
134
|
+
##close, #close! and #kill!. #close is like sending an [ALT]+[F4] keypress which may result in
|
135
|
+
#a dialog box asking you for confimation. #close! is a bit stronger. First it calls #close and waits
|
136
|
+
#a few seconds (you can specify how long exactly) then shuts down the window process. What
|
137
|
+
#leads us to the third method: #kill!. Be sure to call #kill! only on windows you know -
|
138
|
+
#it kills the process of a window by sending SIGTERM first and then SIGKILL. I've not tried
|
139
|
+
#what happens if you send this to the desktop window, but if you like to...
|
140
|
+
xwin.close #We use the normal #close here since we saved our data and gedit shouldn't complain.
|
141
|
+
end
|
142
|
+
|
143
|
+
def mouse
|
144
|
+
#Assuming that the first entry of your right-click menu is "Create folder",
|
145
|
+
#you can create new folders on your desktop.
|
146
|
+
XDo::XWindow.toggle_minimize_all
|
147
|
+
sleep 2
|
148
|
+
|
149
|
+
#Mouse.click is the method for executing mouse clicks. If you call it
|
150
|
+
#without any arguments, it will execute a left click on the current cursor position.
|
151
|
+
XDo::Mouse.click(400, 400, XDo::Mouse::RIGHT)
|
152
|
+
sleep 1 #Give the menu time to appear
|
153
|
+
XDo::Mouse.click
|
154
|
+
sleep 1 #Wait for the folder to be created
|
155
|
+
#Give it a name
|
156
|
+
XDo::Keyboard.simulate("A test folder\n") # Return to confirm
|
157
|
+
sleep 1 #Wait to accept the name
|
158
|
+
#Move the folder icon a bit upwards
|
159
|
+
XDo::Mouse.drag(nil, nil, 400, 200)
|
160
|
+
sleep 2
|
161
|
+
XDo::XWindow.toggle_minimize_all #Restore all windows
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
USAGE =<<USAGE
|
167
|
+
USAGE:
|
168
|
+
./full_demo.rb [methodname]
|
169
|
+
|
170
|
+
Executes the full_demo XDo demo file. You can optionally provide a method
|
171
|
+
name, if you don't, both methods will be run.
|
172
|
+
USAGE
|
173
|
+
|
174
|
+
if ARGV.include?("-h") or ARGV.include?("--help")
|
175
|
+
puts USAGE
|
176
|
+
end
|
177
|
+
|
178
|
+
xxx = XXX.new
|
179
|
+
if ARGV.empty?
|
180
|
+
xxx.keyboard
|
181
|
+
sleep 3
|
182
|
+
xxx.mouse
|
183
|
+
elsif ARGV[0] == "mouse"
|
184
|
+
xxx.mouse
|
185
|
+
elsif ARGV[0] == "keyboard"
|
186
|
+
xxx.keyboard
|
187
|
+
else
|
188
|
+
puts USAGE
|
189
|
+
end
|