xdo 0.0.4
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/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/drive.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
#Encoding: UTF-8
|
2
|
+
require_relative("../xdo")
|
3
|
+
|
4
|
+
module XDo
|
5
|
+
|
6
|
+
#Some methods to interact with disk drives.
|
7
|
+
#The value of the +drive+ parameter of many methods can be
|
8
|
+
#either a mount point like <tt>/media/my_cdrom</tt>, a device file like <tt>scd0</tt>
|
9
|
+
#or the default name "cdrom" for the default drive.
|
10
|
+
#
|
11
|
+
#If you don't pass in a drive name, the return value of #default will
|
12
|
+
#be used.
|
13
|
+
module Drive
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
#Opens a drive.
|
18
|
+
#===Parameters
|
19
|
+
#[drive] (<tt>default()</tt>) The drive to open.
|
20
|
+
#===Return value
|
21
|
+
# True.
|
22
|
+
#===Raises
|
23
|
+
#[XError] You're using a laptop whose drive has to be closed manually.
|
24
|
+
#[XError] +eject+ failed.
|
25
|
+
#===Example
|
26
|
+
# XDo::Drive.eject("scd0")
|
27
|
+
# XDo::Drive.eject("/media/my_cdrom")
|
28
|
+
#===Remarks
|
29
|
+
#This method may silently fail if the device is blocked by e.g. a
|
30
|
+
#CD burning program. Have a look at #release if you want to force
|
31
|
+
#it to open.
|
32
|
+
def eject(drive = default)
|
33
|
+
err = ""
|
34
|
+
Open3.popen3("#{XDo::EJECT} #{drive}"){|stdin, stdout, stderr| err << stderr.read}
|
35
|
+
raise(XDo::XError, err) unless err.empty?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
#Closes a drive.
|
40
|
+
#===Parameters
|
41
|
+
#[drive] (<tt>default()</tt>) The drive to close.
|
42
|
+
#===Return value
|
43
|
+
#Undefined.
|
44
|
+
#===Raises
|
45
|
+
#[XError] +eject+ failed.
|
46
|
+
#===Example
|
47
|
+
# XDo::Drive.eject("scd0")
|
48
|
+
# XDo::Drive.close
|
49
|
+
#
|
50
|
+
# XDo::Drive.eject("/dev/my_cdrom")
|
51
|
+
# XDo::Drive.close("scd0") #A mount point doesn't make sense here
|
52
|
+
def close(drive = default)
|
53
|
+
err = ""
|
54
|
+
Open3.popen3("eject -t #{drive}"){|stdin, stdout, stderr| err << stderr.read}
|
55
|
+
raise(XDo::XError, err) unless err.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
#Returns the mount point of the default drive.
|
59
|
+
#You can use it as a value for a +drive+ parameter.
|
60
|
+
#===Return value
|
61
|
+
#The default drive's name. Usually <tt>"cdrom"</tt>.
|
62
|
+
#===Raises
|
63
|
+
#[XError] +eject+ failed.
|
64
|
+
#===Example
|
65
|
+
# p XDo::Drive.default #=> "cdrom"
|
66
|
+
# XDo::Drive.eject(XDo::Drive.default)
|
67
|
+
def default
|
68
|
+
err = ""
|
69
|
+
out = ""
|
70
|
+
Open3.popen3("#{XDo::EJECT} -d"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
71
|
+
raise(XDo::XError, err) unless err.empty?
|
72
|
+
out.match(/`(.*)'/)[1]
|
73
|
+
end
|
74
|
+
|
75
|
+
#Locks a drive, so that it can't be opened by
|
76
|
+
#using the eject button or the +eject+ command.
|
77
|
+
#===Parameters
|
78
|
+
#[drive] (<tt>default()</tt>) The drive to lock.
|
79
|
+
#===Return value
|
80
|
+
#true.
|
81
|
+
#===Raises
|
82
|
+
#[XError] +eject+ failed.
|
83
|
+
#===Example
|
84
|
+
# XDo::Drive.lock("scd0")
|
85
|
+
# XDo::Drive.eject # fails
|
86
|
+
# XDo::Drive.release
|
87
|
+
# XDo::Drive.eject("scd0") #succeeds
|
88
|
+
#===Remarks
|
89
|
+
#Note that the lock doesn't get released if your process exits.
|
90
|
+
#You should probably register a at_exit handler to avoid confusion
|
91
|
+
#when your program exits with an exception.
|
92
|
+
def lock(drive = default)
|
93
|
+
err = ""
|
94
|
+
Open3.popen3("#{XDo::EJECT} -i on #{drive}"){|stdin, stdout, stderr| err << stderr.read}
|
95
|
+
raise(XDo::XError, err) unless err.empty?
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
#Unlocks a drive, so that it can be opened
|
100
|
+
#by neither the eject button nor the +eject+ command.
|
101
|
+
#===Parameters
|
102
|
+
#[drive] The drive to remove the lock from.
|
103
|
+
#===Return value
|
104
|
+
#true.
|
105
|
+
#===Raises
|
106
|
+
#[XError] +eject+ failed.
|
107
|
+
#===Example
|
108
|
+
# XDo::Drive.lock("scd0")
|
109
|
+
# XDo::Drive.eject # fails
|
110
|
+
# XDo::Drive.release
|
111
|
+
# XDo::Drive.eject("scd0") #succeeds
|
112
|
+
#===Remarks
|
113
|
+
#Use with caution. If a burning program locked the drive and
|
114
|
+
#you force it to open, the resulting CD-ROM is garbage.
|
115
|
+
def release(drive = default)
|
116
|
+
drive = default unless drive
|
117
|
+
err = ""
|
118
|
+
Open3.popen3("#{XDo::EJECT} -i off #{drive}"){|stdin, stdout, stderr| err << stderr.read}
|
119
|
+
raise(XDo::XError,err) unless err.empty?
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/xdo/keyboard.rb
ADDED
@@ -0,0 +1,381 @@
|
|
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_relative("../xdo")
|
6
|
+
|
7
|
+
module XDo
|
8
|
+
|
9
|
+
#A namespace encabsulating methods to simulate keyboard input. You can
|
10
|
+
#send input to special windows, just pass in the window's ID or a XWindow
|
11
|
+
#object via the +w_id+ parameter.
|
12
|
+
module Keyboard
|
13
|
+
|
14
|
+
#Aliases for key names in escape sequences.
|
15
|
+
ALIASES = {
|
16
|
+
"BS" => "BackSpace",
|
17
|
+
"BACKSPACE" => "BackSpace",
|
18
|
+
"DEL" => "Delete",
|
19
|
+
"ESC" => "Escape",
|
20
|
+
"INS" => "Insert",
|
21
|
+
"PGUP" => "Prior",
|
22
|
+
"PGDN" => "Next",
|
23
|
+
"NUM1" => "KP_End",
|
24
|
+
"NUM2" => "KP_Down",
|
25
|
+
"NUM3" => "KP_Next",
|
26
|
+
"NUM4" => "KP_Left",
|
27
|
+
"NUM5" => "KP_Begin",
|
28
|
+
"NUM6" => "KP_Right",
|
29
|
+
"NUM7" => "KP_Home",
|
30
|
+
"NUM8" => "KP_Up",
|
31
|
+
"NUM9" => "KP_Prior",
|
32
|
+
"NUM_DIV" => "KP_Divide",
|
33
|
+
"NUM_MUL" => "KP_Multiply",
|
34
|
+
"NUM_SUB" => "KP_Subtract",
|
35
|
+
"NUM_ADD" => "KP_Add",
|
36
|
+
"NUM_ENTER" => "KP_Enter",
|
37
|
+
"NUM_DEL" => "KP_Delete",
|
38
|
+
"NUM_COMMA" => "KP_Separator",
|
39
|
+
"NUM_INS" => "KP_Insert",
|
40
|
+
"NUM0" => "KP_0",
|
41
|
+
"CTRL" => "Control_L",
|
42
|
+
"ALT" => "Alt_L",
|
43
|
+
"ALT_GR" => "ISO_Level3_Shift",
|
44
|
+
"WIN" => "Super_L",
|
45
|
+
"SUPER" => "Super_L"
|
46
|
+
}.freeze
|
47
|
+
|
48
|
+
#The names of some keyboard symbols. The latest release of
|
49
|
+
#xdotool is capable of sending keysymbols directly, i.e.
|
50
|
+
# xdotool key Adiaeresis
|
51
|
+
#results in Ä being sent.
|
52
|
+
#This hash defines how those special characters can be
|
53
|
+
#sent. Feel free to add characters that are missing! You
|
54
|
+
#can use the +xev+ program to obtain their keycodes.
|
55
|
+
SPECIAL_CHARS = {
|
56
|
+
"ä" => "adiaeresis",
|
57
|
+
"Ä" => "Adiaeresis",
|
58
|
+
"ö" => "odiaeresis",
|
59
|
+
"Ö" => "Odiaeresis",
|
60
|
+
"ü" => "udiaeresis",
|
61
|
+
"Ü" => "Udiaeresis",
|
62
|
+
"ë" => "ediaeresis",
|
63
|
+
"Ë" => "Ediaeresis", #Does not work with xdotool
|
64
|
+
"ï" => "idiaeresis",
|
65
|
+
"Ï" => "Idiaeresis", #Does not work with xdotool
|
66
|
+
"ß" => "ssharp",
|
67
|
+
"\n" => "Return",
|
68
|
+
"\t" => "Tab",
|
69
|
+
"\b" => "BackSpace",
|
70
|
+
"§" => "section",
|
71
|
+
"[" => "bracketleft",
|
72
|
+
"]" => "bracketright",
|
73
|
+
"{" => "braceright",
|
74
|
+
"}" => "braceleft",
|
75
|
+
"@" => "at",
|
76
|
+
"€" => "EuroSign",
|
77
|
+
"|" => "bar",
|
78
|
+
"?" => "question"
|
79
|
+
}
|
80
|
+
|
81
|
+
class << self
|
82
|
+
|
83
|
+
#Types a character sequence, but without any special chars.
|
84
|
+
#===Parameters
|
85
|
+
#[+str+] The string to type.
|
86
|
+
#[+w_id+] (0) The ID of the window you want the input send to (or an XWindow object). 0 means the active window.
|
87
|
+
#===Return value
|
88
|
+
#nil.
|
89
|
+
#===Example
|
90
|
+
# XDo::Keyboard.type("test")
|
91
|
+
# XDo::Keyboard.type("täst") #=> I don't what key produces '�', skipping.
|
92
|
+
#===Remarks
|
93
|
+
#This function is a bit faster then #simulate.
|
94
|
+
def type(str, w_id = 0)
|
95
|
+
out = Open3.popen3("#{XDOTOOL} type #{w_id.nonzero? ? "--window #{w_id.to_i} " : ""}'#{str}'") do |stdin, stdout, stderr|
|
96
|
+
stdin.close_write
|
97
|
+
str = stderr.read
|
98
|
+
warn(str) unless str.empty?
|
99
|
+
end
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
#Types a character sequence. You can use the escape sequence {...} to send special
|
104
|
+
#keystrokes.
|
105
|
+
#===Parameters
|
106
|
+
#[+str+] The string to simulate.
|
107
|
+
#[+raw+] (false) If true, escape sequences via {...} are disabled. See _Remarks_.
|
108
|
+
#[+w_id+] (0) The ID of the window you want the input send to (or an XWindow object). 0 means the active window.
|
109
|
+
#===Return value
|
110
|
+
#The string that was simulated.
|
111
|
+
#===Raises
|
112
|
+
#[ParseError] Your string was invalid.
|
113
|
+
#===Example
|
114
|
+
# XDo::Keyboard.simulate("test")
|
115
|
+
# XDo::Keyboard.simulate("täst")
|
116
|
+
# XDo::Keyboard.simulate("tex{BS}st")
|
117
|
+
#===Remarks
|
118
|
+
#This method recognizes many special chars like ? and ä, even if you disable
|
119
|
+
#the escape syntax {..} via setting the +raw+ parameter to true (that's the only way to send the { and } chars).
|
120
|
+
#
|
121
|
+
#+str+ may contain escape sequences in braces { and }. The letters between those two indicate
|
122
|
+
#what special character to send - this way you can simulate non-letter keypresses like [ESC]!
|
123
|
+
#You may use the following escape sequences:
|
124
|
+
# Escape seq. | Keystroke | Comment
|
125
|
+
# ============+===================+=================
|
126
|
+
# ALT | [Alt_L] |
|
127
|
+
# ------------+-------------------------------------
|
128
|
+
# ALT_GR | [ISO_Level3_Shift]| Not on USA
|
129
|
+
# | | keyboard
|
130
|
+
# ------------+-------------------------------------
|
131
|
+
# BS | [BackSpace] |
|
132
|
+
# ------------+-------------------------------------
|
133
|
+
# BACKSPACE | [BackSpace] |
|
134
|
+
# ------------+-------------------------------------
|
135
|
+
# CTRL | [Control_L] |
|
136
|
+
# ------------+-------------------------------------
|
137
|
+
# DEL | [Delete] |
|
138
|
+
# ------------+-------------------------------------
|
139
|
+
# END | [End] |
|
140
|
+
# ------------+-------------------------------------
|
141
|
+
# ESC | [Escape] |
|
142
|
+
# ------------+-------------------------------------
|
143
|
+
# INS | [Insert] |
|
144
|
+
# ------------+-------------------------------------
|
145
|
+
# HOME | [Home] |
|
146
|
+
# ------------+-------------------------------------
|
147
|
+
# MENU | [Menu] | Usually right-
|
148
|
+
# | | click menu
|
149
|
+
# ------------+-------------------------------------
|
150
|
+
# NUM0..NUM9 | [KP_0]..[KP_9] | Numpad keys
|
151
|
+
# ------------+-------------------------------------
|
152
|
+
# NUM_DIV | [KP_Divide] | Numpad key
|
153
|
+
# ------------+-------------------------------------
|
154
|
+
# NUM_MUL | [KP_Multiply] | Numpad key
|
155
|
+
# ------------+-------------------------------------
|
156
|
+
# NUM_SUB | [KP_Subtract] | Numpad key
|
157
|
+
# ------------+-------------------------------------
|
158
|
+
# NUM_ADD | [KP_Add] | Numpad key
|
159
|
+
# ------------+-------------------------------------
|
160
|
+
# NUM_ENTER | [KP_Enter] | Numpad key
|
161
|
+
# ------------+-------------------------------------
|
162
|
+
# NUM_DEL | [KP_Delete] | Numpad key
|
163
|
+
# ------------+-------------------------------------
|
164
|
+
# NUM_COMMA | [KP_Separator] | Numpad key
|
165
|
+
# ------------+-------------------------------------
|
166
|
+
# NUM_INS | [KP_Insert] | Numpad key
|
167
|
+
# ------------+-------------------------------------
|
168
|
+
# PAUSE | [Pause] |
|
169
|
+
# ------------+-------------------------------------
|
170
|
+
# PGUP | [Prior] | Page up
|
171
|
+
# ------------+-------------------------------------
|
172
|
+
# PGDN | [Next] | Page down
|
173
|
+
# ------------+-------------------------------------
|
174
|
+
# PRINT | [Print] |
|
175
|
+
# ------------+-------------------------------------
|
176
|
+
# SUPER | [Super_L] | Windows key
|
177
|
+
# ------------+-------------------------------------
|
178
|
+
# TAB | [Tab] |
|
179
|
+
# ------------+-------------------------------------
|
180
|
+
# WIN | [Super_L] | Windows key
|
181
|
+
def simulate(str, raw = false, w_id = 0)
|
182
|
+
raise(XDo::XError, "Invalid number of open and close braces!") unless str.scan(/{/).size == str.scan(/}/).size
|
183
|
+
|
184
|
+
tokens = tokenize(str)
|
185
|
+
|
186
|
+
tokens.each do |sym, s|
|
187
|
+
case sym
|
188
|
+
when :plain then type(s, w_id.to_i)
|
189
|
+
when :esc then
|
190
|
+
if raw
|
191
|
+
type("{#{s}}", w_id.to_i) #The braces should be preserved when using +raw+.
|
192
|
+
else
|
193
|
+
if ALIASES.has_key?(s)
|
194
|
+
key(ALIASES[s])
|
195
|
+
else
|
196
|
+
char(s.split("_").map(&:capitalize).join("_"), w_id.to_i)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
when :special then
|
200
|
+
if SPECIAL_CHARS.has_key?(s)
|
201
|
+
char(SPECIAL_CHARS[s], w_id.to_i)
|
202
|
+
else
|
203
|
+
raise(XDo::ParseError, "No key symbol known for '#{s}'!")
|
204
|
+
end
|
205
|
+
else #Write a bug report if you get here. That really shouldn't happen.
|
206
|
+
raise(XDo::ParseError, "Invalid token named #{sym.inspect}! This is an internal error - please write a bug report at http://github.com/Quintus/Automations/issues or email me at sutniuq@@gmx@net.")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
str
|
210
|
+
end
|
211
|
+
|
212
|
+
#Simulate a single char directly via the +key+ command of +xdotool+.
|
213
|
+
#===Parameters
|
214
|
+
#[+c+] A single char like "a" or a combination like "shift+a".
|
215
|
+
#[+w_id+] (0) The ID of the window you want the input send to (or an XWindow object). 0 means the active window.
|
216
|
+
#===Return value
|
217
|
+
#The +c+ you passed in.
|
218
|
+
#===Raises
|
219
|
+
#[XError] Invalid keyname.
|
220
|
+
#===Example
|
221
|
+
# XDo::Keyboard.char("a") #=> a
|
222
|
+
# XDo::Keyboard.char("A") #=> A
|
223
|
+
# XDo::Keyboard.char("ctrl+c")
|
224
|
+
def char(c, w_id = 0)
|
225
|
+
Open3.popen3("#{XDOTOOL} key #{w_id.nonzero? ? "--window #{w_id.to_i} " : ""}#{c}") do |stdin, stdout, stderr|
|
226
|
+
stdin.close_write
|
227
|
+
raise(XDo::XError, "Invalid character '#{c}'!") if stderr.read =~ /No such key name/
|
228
|
+
end
|
229
|
+
c
|
230
|
+
end
|
231
|
+
alias key char
|
232
|
+
|
233
|
+
#Holds a key down.
|
234
|
+
#===Parameters
|
235
|
+
#[+key+] The key to hold down.
|
236
|
+
#[+w_id+] (0) The ID of the window you want the input send to (or an XWindow object). 0 means the active window.
|
237
|
+
#===Return value
|
238
|
+
#+key+.
|
239
|
+
#===Raises
|
240
|
+
#[XError] Invalid keyname.
|
241
|
+
#===Example
|
242
|
+
# XDo::Keyboard.key_down("a")
|
243
|
+
# sleep 2
|
244
|
+
# XDo::Keyboard.key_up("a")
|
245
|
+
#===Remarks
|
246
|
+
#You should release the key sometime via Keyboard.key_up.
|
247
|
+
def key_down(key, w_id = 0)
|
248
|
+
Open3.popen3("#{XDOTOOL} keydown #{w_id.nonzero? ? "--window #{w_id.to_i} " : "" }#{check_for_special_key(key)}") do |stdin, stdout, stderr|
|
249
|
+
stdin.close_write
|
250
|
+
raise(XDo::XError, "Invalid character '#{key}'!") if stderr.read =~ /No such key name/
|
251
|
+
end
|
252
|
+
key
|
253
|
+
end
|
254
|
+
|
255
|
+
#Releases a key hold down by #key_down.
|
256
|
+
#===Parameters
|
257
|
+
#[+key+] The key to release.
|
258
|
+
#[+w_id+] (0) The ID of the window you want the input send to (or an XWindow object). 0 means the active window.
|
259
|
+
#===Return value
|
260
|
+
#+key+.
|
261
|
+
#===Raises
|
262
|
+
#[XError] Invalid keyname.
|
263
|
+
#===Example
|
264
|
+
# XDo::Keyboard.key_down("a")
|
265
|
+
# sleep 2
|
266
|
+
# XDo::Keyboard.key_up("a")
|
267
|
+
#===Remarks
|
268
|
+
#This has no effect on already released keys.
|
269
|
+
def key_up(key, w_id = 0)
|
270
|
+
Open3.popen3("#{XDOTOOL} keyup #{w_id.nonzero? ? "--window #{w_id.to_i} " : "" }#{check_for_special_key(key)}") do |stdin, stdout, stderr|
|
271
|
+
stdin.close_write
|
272
|
+
raise(XDo::XError, "Invalid character '#{key}'!") if stderr.read =~ /No such key name/
|
273
|
+
end
|
274
|
+
key
|
275
|
+
end
|
276
|
+
|
277
|
+
#Deletes a char.
|
278
|
+
#===Parameters
|
279
|
+
#[right] (false) If this is true, +del_char+ uses the DEL key for deletion, otherwise the BackSpace key.
|
280
|
+
#===Return value
|
281
|
+
#nil.
|
282
|
+
#===Example
|
283
|
+
# XDo::Keyboard.delete
|
284
|
+
# XDo::Keyboard.delete(true)
|
285
|
+
def delete(right = false)
|
286
|
+
Keyboard.simulate(right ? "\b" : "{DEL}")
|
287
|
+
nil
|
288
|
+
end
|
289
|
+
|
290
|
+
#Allows you to things like this:
|
291
|
+
# XDo::Keyboard.ctrl_c
|
292
|
+
#The string will be capitalized and every _ will be replaced by a + and then passed into #char.
|
293
|
+
#You can't use this way to send whitespace or _ characters.
|
294
|
+
def method_missing(sym, *args, &block)
|
295
|
+
super if args.size > 1 or block
|
296
|
+
char(sym.to_s.capitalize.gsub("_", "+"), args[0].nil? ? 0 : args[0])
|
297
|
+
end
|
298
|
+
|
299
|
+
private
|
300
|
+
|
301
|
+
#Tokenizes a string into an array of form
|
302
|
+
# [[:plain, "nonspecial"], [:special, "a"], [:esc, "INS"], ...]
|
303
|
+
def tokenize(str)
|
304
|
+
tokens = []
|
305
|
+
#We need a binary version of our string as StringScanner isn't able to work
|
306
|
+
#with encodings.
|
307
|
+
ss = StringScanner.new(str.dup.force_encoding("BINARY")) #String#force_encoding always returns self
|
308
|
+
until ss.eos?
|
309
|
+
pos = ss.pos
|
310
|
+
if ss.scan_until(/{/)
|
311
|
+
#Get the string between the last and the recent match. We have to subtract 2 here,
|
312
|
+
#since a StringScanner position is always ahead of the string character by 1 (since 0 in
|
313
|
+
#a SmallScanner means "before the first character") and the matched brace shouldn't be
|
314
|
+
#included.
|
315
|
+
tokens << [:plain, ss.string[Range.new(pos, ss.pos - 2)]] unless ss.pos == 1 #This means, the escape sequence is at the beginning of the string - no :plain text before.
|
316
|
+
pos = ss.pos
|
317
|
+
ss.scan_until(/}/)
|
318
|
+
tokens << [:esc, ss.string[Range.new(pos, ss.pos - 2)]] #See above for comment on -2
|
319
|
+
else #We're behind the last escape sequence now - there must be some characters left, otherwise this wouldn't be triggered.
|
320
|
+
tokens << [:plain, ss.rest]
|
321
|
+
ss.terminate
|
322
|
+
end
|
323
|
+
end
|
324
|
+
#Now hunt for special character like ä which can't be send using xdotool's type command.
|
325
|
+
regexp = Regexp.union(*SPECIAL_CHARS.keys.map{|st| st}) #Regexp.union escapes automatically, no need for Regexp.escape
|
326
|
+
tokens.map! do |ary|
|
327
|
+
#But first, we have to remedy from that insane forced encoding for StringScanner.
|
328
|
+
#Force every string's encoding back to the original encoding.
|
329
|
+
ary[1].force_encoding(str.encoding)
|
330
|
+
next([ary]) unless ary[0] == :plain #Extra array since we flatten(1) it afterwards
|
331
|
+
tokens2 = []
|
332
|
+
ss = StringScanner.new(ary[1])
|
333
|
+
until ss.eos?
|
334
|
+
pos = ss.pos
|
335
|
+
if ss.scan_until(regexp)
|
336
|
+
#Same as for the first StringScanner encoding problem goes here, but since I now have to use a UTF-8 regexp
|
337
|
+
#I have to put the string into the StringScanner as UTF-8, but because the StringScanner returns positions for
|
338
|
+
#a BINARY-encoded string I have to get the string, grep the position from the BINARY version and then reforce
|
339
|
+
#it to the correct encoding.
|
340
|
+
tokens2 << [:plain, ss.string.dup.force_encoding("BINARY")[Range.new(pos, ss.pos - 2)].force_encoding(str.encoding)] unless ss.pos == 1
|
341
|
+
tokens2 << [:special, ss.matched]
|
342
|
+
pos = ss.pos
|
343
|
+
else
|
344
|
+
tokens2 << [:plain, ss.rest]
|
345
|
+
ss.terminate
|
346
|
+
end
|
347
|
+
end
|
348
|
+
tokens2
|
349
|
+
end
|
350
|
+
#Make the token sequence 1-dimensional
|
351
|
+
tokens.flatten!(1)
|
352
|
+
#Now delete empty :plain tokens, they don't have to be handled.
|
353
|
+
#They are created by strings like "abc{ESC}{ESC}", where they are
|
354
|
+
#recognized between the two escapes.
|
355
|
+
#Empty escape sequences are an error in any case.
|
356
|
+
tokens.delete_if do |sym, st|
|
357
|
+
if st.empty?
|
358
|
+
if sym == :esc
|
359
|
+
raise(XDo::ParseError, "Empty escape sequence found!")
|
360
|
+
else
|
361
|
+
true
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
#Return the tokens array.
|
367
|
+
tokens
|
368
|
+
end
|
369
|
+
|
370
|
+
#Checks wheather +key+ is a special character (i.e. contained
|
371
|
+
#in the SPECIAL_CHARS hash) and returns the key symbol for it if so,
|
372
|
+
#otherwise returns +key+.
|
373
|
+
def check_for_special_key(key)
|
374
|
+
SPECIAL_CHARS.has_key?(key) ? SPECIAL_CHARS[key] : key
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
378
|
+
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|