xdo 0.0.1-x86-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|