xdo 0.0.4

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