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/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
|