xdo 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env ruby
2
+ #Encoding: UTF-8
3
+ #--
4
+ #This file is part of XDo. Copyright © 2009 by Marvin Gülker.
5
+ #XDo is published under Ruby's license. See http://www.ruby-lang.org/en/LICENSE.txt
6
+ # Initia in potestate nostra sunt, de eventu fortuna iudicat.
7
+ #++
8
+
9
+ =begin
10
+ This is the full XDo demo. We will create a
11
+ command-line program that will be able to open
12
+ and close the default CD drive, open gedit and
13
+ type some text in tabular form and click the
14
+ "show desktop" button in the lower-left corner of
15
+ the Ubuntu desktop. Even if you don't use Ubuntu,
16
+ you may get the logic and find this demo useful.
17
+
18
+ Every OS and even every computer are different in their
19
+ user interface. If you'd like to add sample files for a
20
+ specific OS, feel free to contact me at sutniuq ät gmx Dot net.
21
+ =end
22
+
23
+ #First, require all Xdo files.
24
+ require "xdo/clipboard"
25
+ require "xdo/drive"
26
+ require "xdo/keyboard"
27
+ require "xdo/mouse"
28
+ require "xdo/xwindow"
29
+
30
+ class XXX
31
+
32
+ def keyboard
33
+ #Make sure gedit is not running, since it would open
34
+ #a new tab and not a new window if invoked again.
35
+ raise(RuntimeError, "gedit is already running; please close it before using.") unless XDo::XWindow.search("gedit").empty?
36
+ #If we would use system in the main program to open
37
+ #gedit, it would hang until gedit is closed. That's why
38
+ #we run it in a separate process.
39
+ spawn("gedit")
40
+ #Now we wait until gedit has made up its GUI.
41
+ #wait_for_window returns the ID of the found window, so
42
+ #we catch it and...
43
+ id = XDo::XWindow.wait_for_window("gedit")
44
+ #...use it to create a new reference to gedit's window.
45
+ #Note that this is a pseudo reference, not a real one.
46
+ #XDo allows you to create references even to unexisting
47
+ #windows, so the X window manager will not recognize
48
+ #the pseudo reference. You can check if the window
49
+ #you're looking for exists by calling #exists? on the
50
+ #XWindow object.
51
+ xwin = XDo::XWindow.new(id)
52
+ #Now we move it 20 pixels right and 10 down.
53
+ xwin.move(xwin.position[0] + 20, xwin.position[1] + 10)
54
+ #After having fun with it, let's do something useful.
55
+ #XDo::Keyboard.simulate fakes keystrokes and recognizes
56
+ #special escape sequences in braces { and }. For some
57
+ #special characters like tab you can use an ASCII escape
58
+ #like \t. The most special keys like [ESC] don't have
59
+ #those a shortcut, so you will have to write {ESC} in
60
+ #order to send them.
61
+ XDo::Keyboard.simulate("This will be some test text.")
62
+ XDo::Keyboard.ctrl_a
63
+ XDo::Keyboard.delete #This will send a BackSpace keypress. If you want a DEL, pass in true as a parameter.
64
+ #I promised at the beginning, we were goint to create data in tabular form,
65
+ #so here it is:
66
+ XDo::Keyboard.type("Percentage of smokers in different jobs in Germany".upcase) #type is usually faster than simulate if the text doesn't contain special characters or escape sequences
67
+ 2.times{XDo::Keyboard.return} #Return is the Enter key
68
+ #Set up the data
69
+ headings = ["Job", "Percentage"]
70
+ data = [
71
+ ["Judge", 28],
72
+ ["Architect", 29],
73
+ ["Doctor", 30],
74
+ ["Nurse", 45],
75
+ ["Teacher", 53]
76
+ ]
77
+ #Create the table headings
78
+ str =""
79
+ headings[0..-2].each{|h| str << h << "{TAB}{TAB}"}
80
+ str << headings[-1] << "\n"#No tab after the last heading
81
+ #Type the heading
82
+ XDo::Keyboard.simulate(str)
83
+ #Make a line between the heading and the data
84
+ XDo::Keyboard.type("=" * 30)
85
+ XDo::Keyboard.return
86
+ #Write the table data
87
+ data.each do |job, percentage|
88
+ #Insert tabs. If a word is longer than 8 (the normal tab size), don't append a second tab.
89
+ XDo::Keyboard.simulate("#{job}#{job.length > 8 ? "\t" : "\t\t"}#{percentage}\n") #\t will trigger the Tab key, \n the Return key.
90
+ end
91
+ #Oh yes, and save it of course. XDo::Keyboard tries to
92
+ #send every method name as a key combination if the
93
+ #method is not defined already. The method name
94
+ #will be capitalized and every underscore _ replaced
95
+ #by a + (that's internally important to combine keys).
96
+ File.delete("#{ENV["HOME"]}/testXDo.txt") if File.file? "#{ENV["HOME"]}/testXDo.txt" #This is the file we'll save to
97
+ XDo::Keyboard.ctrl_s
98
+ #Wait for the save window to exist. I can't use wait_for_window here, since
99
+ #it's named different in every language. Me as a German user have a title
100
+ #of "Speichern unter...", an english OS may show "Save as...".
101
+ sleep 1
102
+ #You really shouldn't try to simulate the ~ sign. xdotool seems to have a bug, so
103
+ #~ can't be simulated with one command. My library tries to simulate it with one
104
+ #command and it sometimes works, but you mustn't rely on it. Therefore I use
105
+ #the HOME environment variable here to get the home directory, rather than ~.
106
+ XDo::Keyboard.simulate("#{ENV["HOME"]}/testXDo.txt")
107
+ sleep 1 #gedit terminates if send [ALT]+[S] immediatly after the path
108
+ XDo::Keyboard.alt_s
109
+ #Now, let's duplicate our table. We could send all the stuff again,
110
+ #but I want to introduce you in the use of the X clipboard.
111
+ #First, we have to mark our text in order to be copied to the clipboard:
112
+ xwin.activate #After saving the window isn't active anymore
113
+ sleep 1 #Wait for the gedit window to be active again
114
+ XDo::Keyboard.ctrl_a
115
+ #Then copy it to the clipboard (you see, it's quite useful to know keyboard shortcuts)
116
+ XDo::Keyboard.ctrl_c
117
+ #gedit copies, like the most programs, it's data to the CLIPBOARD clipboard.
118
+ #There are two other clipboard, PRIMARY and SECONDARY, but we won't
119
+ #diskuss them here. If you are sure you've copied data to the clipboard and
120
+ ##read_clipboard doesn't find anything, check out #read_primary and #read_secondary.
121
+ #Also note, that XDo can only handle plain text data.
122
+ cliptext = XDo::Clipboard.read_clipboard
123
+ #Move to the end of the text
124
+ XDo::Keyboard.simulate("{DOWN}" * 9)
125
+ XDo::Keyboard.simulate("{END}")
126
+ #Since #simulate interprets \t correctly as a tab, we can simply put
127
+ #the clipboard's text in that method:
128
+ XDo::Keyboard.simulate("\n#{cliptext}") #Begin a new line before inserting the duplicate
129
+ sleep 1
130
+ XDo::Keyboard.ctrl_s
131
+ #Give some time to see the result
132
+ sleep 5
133
+ #Than close gedit. There are three methods to close a window,
134
+ ##close, #close! and #kill!. #close is like sending an [ALT]+[F4] keypress which may result in
135
+ #a dialog box asking you for confirmation. #close! is a bit stronger. First it calls #close and waits
136
+ #a few seconds (you can specify how long exactly) then shuts down the window process. What
137
+ #leads us to the third method: #kill!. Be sure to call #kill! only on windows you know -
138
+ #it kills the process of a window by sending SIGTERM first and then SIGKILL. I've not tried
139
+ #what happens if you send this to the desktop window, but if you like to...
140
+ xwin.close #We use the normal #close here since we saved our data and gedit shouldn't complain.
141
+ end
142
+
143
+ def mouse
144
+ #Assuming that the first entry of your right-click menu is "Create folder",
145
+ #you can create new folders on your desktop.
146
+ XDo::XWindow.toggle_minimize_all
147
+ sleep 2
148
+
149
+ #Mouse.click is the method for executing mouse clicks. If you call it
150
+ #without any arguments, it will execute a left click on the current cursor position.
151
+ XDo::Mouse.click(400, 400, XDo::Mouse::RIGHT)
152
+ sleep 1 #Give the menu time to appear
153
+ XDo::Mouse.click
154
+ sleep 1 #Wait for the folder to be created
155
+ #Give it a name
156
+ XDo::Keyboard.simulate("A test folder\n") # Return to confirm
157
+ sleep 1 #Wait to accept the name
158
+ #Move the folder icon a bit upwards
159
+ XDo::Mouse.drag(nil, nil, 400, 200)
160
+ sleep 2
161
+ XDo::XWindow.toggle_minimize_all #Restore all windows
162
+ end
163
+
164
+ end
165
+
166
+ USAGE =<<USAGE
167
+ USAGE:
168
+ ./full_demo.rb [methodname]
169
+
170
+ Executes the full_demo XDo demo file. You can optionally provide a method
171
+ name, if you don't, both methods will be run.
172
+ USAGE
173
+
174
+ if ARGV.include?("-h") or ARGV.include?("--help")
175
+ puts USAGE
176
+ end
177
+
178
+ xxx = XXX.new
179
+ if ARGV.empty?
180
+ xxx.keyboard
181
+ sleep 3
182
+ xxx.mouse
183
+ elsif ARGV[0] == "mouse"
184
+ xxx.mouse
185
+ elsif ARGV[0] == "keyboard"
186
+ xxx.keyboard
187
+ else
188
+ puts USAGE
189
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ #Encoding: UTF-8
3
+ #--
4
+ #This file is part of XDo. Copyright © 2009 Marvin Gülker
5
+ #XDo is published under Ruby's license. See http://www.ruby-lang.org/en/LICENSE.txt.
6
+ # Initia in potestate nostra sunt, de eventu fortuna iudicat.
7
+ #++
8
+
9
+ =begin
10
+ This is an example on how to use the mouse with XDo::Mouse.
11
+ =end
12
+
13
+ #Require the library
14
+ require "xdo/mouse"
15
+
16
+ #Get current cursor position
17
+ puts "Cursor X pos: #{XDo::Mouse.position[0]}"
18
+ puts "Cursor Y pos: #{XDo::Mouse.position[1]}"
19
+
20
+ #Move the mouse cursor
21
+ XDo::Mouse.move(10, 10)
22
+ #Click at the current position
23
+ XDo::Mouse.click
24
+ #Do a right-click at (100|100)
25
+ XDo::Mouse.click(100, 100, XDo::Mouse::RIGHT)
26
+ #Scroll down a text page
27
+ XDo::Mouse.wheel(XDo::Mouse::DOWN, 4)
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+ #Encoding: UTF-8
3
+ gem "test-unit", ">= 2.1" #Ensure we use the gem
4
+ require "test/unit"
5
+ require "xdo/clipboard.rb"
6
+
7
+ class ClipboardTest < Test::Unit::TestCase
8
+
9
+ def test_read
10
+ #Write something to the clipboard first
11
+ IO.popen("#{XDo::XSEL} -i", "w"){|io| io.write("Some primary test text")}
12
+ IO.popen("#{XDo::XSEL} -b -i", "w"){|io| io.write("Some clipboard \ntest text")}
13
+ IO.popen("#{XDo::XSEL} -s -i", "w"){|io| io.write("Some secondary test text")}
14
+
15
+ clip = XDo::Clipboard.read
16
+ assert_equal("Some primary test text", XDo::Clipboard.read_primary)
17
+ assert_equal(XDo::Clipboard.read_primary, clip[:primary])
18
+ assert_equal("Some clipboard \ntest text", XDo::Clipboard.read_clipboard)
19
+ assert_equal(XDo::Clipboard.read_clipboard, clip[:clipboard])
20
+ assert_equal("Some secondary test text", XDo::Clipboard.read_secondary)
21
+ assert_equal(XDo::Clipboard.read_secondary, clip[:secondary])
22
+ end
23
+
24
+ def test_write
25
+ XDo::Clipboard.write_primary "Primary!"
26
+ XDo::Clipboard.write_clipboard "Clipboard!\nNewline"
27
+ XDo::Clipboard.write_secondary "Secondary!"
28
+
29
+ assert_equal("Primary!", `#{XDo::XSEL}`)
30
+ assert_equal("Secondary!", `#{XDo::XSEL} -s`)
31
+ assert_equal("Clipboard!\nNewline", `#{XDo::XSEL} -b`)
32
+
33
+ XDo::Clipboard.write("XYZ", :primary, :secondary, :clipboard)
34
+ assert_equal({primary: "XYZ", secondary: "XYZ", clipboard: "XYZ"}, XDo::Clipboard.read)
35
+ end
36
+
37
+ def test_append
38
+ ["primary", "secondary", "clipboard"].each{|m| XDo::Clipboard.send(:"write_#{m}", "This is... ")}
39
+ XDo::Clipboard.append("a Test!", :primary, :secondary, :clipboard)
40
+ ["primary", "secondary", "clipboard"].each{|m| assert_equal(XDo::Clipboard.send(:"read_#{m}"), "This is... a Test!")}
41
+ end
42
+
43
+ def test_clear
44
+ XDo::Clipboard.write("ABC", :primary, :secondary, :clipboard)
45
+ ["primary", "secondary", "clipboard"].each{|m| XDo::Clipboard.send("clear_#{m}")}
46
+ ["primary", "secondary", "clipboard"].each{|m| assert_equal("", XDo::Clipboard.send("read_#{m}"))}
47
+ end
48
+
49
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ #Encoding: UTF-8
3
+ gem "test-unit", ">= 2.1" #Ensure we use the gem
4
+ require "test/unit"
5
+ require "xdo/drive.rb"
6
+
7
+ class DriveTest < Test::Unit::TestCase
8
+
9
+ def test_eject
10
+ begin
11
+ XDo::Drive.eject
12
+ sleep 2
13
+ begin
14
+ XDo::Drive.close
15
+ rescue XDo::XError #A laptop drive will raise an XDo::XError since it has to be closed manually
16
+ notify "Couldn't close your CD-ROM drive. Are you using a laptop?"
17
+ end
18
+ assert(true)
19
+ rescue
20
+ assert(false, $!.message)
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env ruby
2
+ #Encoding: UTF-8
3
+ gem "test-unit", ">= 2.1" #Ensure we use the gem
4
+ require "test/unit"
5
+ require "tempfile"
6
+ require "xdo/keyboard.rb"
7
+ require "xdo/clipboard.rb"
8
+ require "xdo/xwindow"
9
+ require "xdo/simulatable"
10
+
11
+ class TestKeyboard < Test::Unit::TestCase
12
+
13
+ #Command to start a simple text editor
14
+ EDITOR_CMD = "gedit"
15
+
16
+ TESTTEXT = "This is test\ntext."
17
+ TESTTEXT2 = "XYZ"
18
+ TESTTEXT_RAW = "ä{TAB}?b"
19
+ TESTTEXT_SPECIAL = "ab{TAB}c{TAB}{TAB}d"
20
+
21
+ def setup
22
+ @edit_pid = spawn(EDITOR_CMD)
23
+ sleep 1
24
+ end
25
+
26
+ def teardown
27
+ Process.kill("KILL", @edit_pid)
28
+ end
29
+
30
+ def test_char
31
+ Process.kill("KILL", @edit_pid) #Special file need to be opened
32
+ tempfile = Tempfile.open("XDOTEST")
33
+ tempfile.write(TESTTEXT)
34
+ tempfile.flush
35
+ sleep 1 #Wait for the buffer to be written out
36
+ @edit_pid = spawn(EDITOR_CMD, tempfile.path) #So it's automatically killed by #teardown
37
+ sleep 1
38
+ tempfile.close
39
+ 20.times{XDo::Keyboard.char("Shift+Right")}
40
+ XDo::Keyboard.ctrl_c
41
+ sleep 0.2
42
+ assert_equal(TESTTEXT, XDo::Clipboard.read_clipboard)
43
+ end
44
+
45
+ def test_simulate
46
+ XDo::Keyboard.simulate("A{BS}#{TESTTEXT2}")
47
+ XDo::Keyboard.ctrl_a
48
+ XDo::Keyboard.ctrl_c
49
+ sleep 0.2
50
+ assert_equal(TESTTEXT2, XDo::Clipboard.read_clipboard)
51
+
52
+ XDo::Keyboard.ctrl_a
53
+ XDo::Keyboard.delete
54
+ XDo::Keyboard.simulate(TESTTEXT_SPECIAL)
55
+ XDo::Keyboard.ctrl_a
56
+ XDo::Keyboard.ctrl_c
57
+ sleep 0.2
58
+ assert_equal(TESTTEXT_SPECIAL.gsub("{TAB}", "\t"), XDo::Clipboard.read_clipboard)
59
+
60
+ XDo::Keyboard.ctrl_a
61
+ XDo::Keyboard.delete
62
+ XDo::Keyboard.simulate(TESTTEXT_RAW, true)
63
+ XDo::Keyboard.ctrl_a
64
+ XDo::Keyboard.ctrl_c
65
+ sleep 0.2
66
+ assert_equal(TESTTEXT_RAW, XDo::Clipboard.read_clipboard)
67
+ end
68
+
69
+ def test_type
70
+ XDo::Keyboard.type(TESTTEXT2)
71
+ XDo::Keyboard.ctrl_a
72
+ XDo::Keyboard.ctrl_c
73
+ sleep 0.2
74
+ assert_equal(TESTTEXT2, XDo::Clipboard.read_clipboard)
75
+ end
76
+
77
+ def test_window_id
78
+ XDo::XWindow.unfocus #Ensure that the editor hasn't the input focus anymore
79
+ sleep 1
80
+ edit_id = XDo::XWindow.search(EDITOR_CMD).first
81
+ xwin = XDo::XWindow.new(edit_id)
82
+ XDo::Keyboard.simulate(TESTTEXT_SPECIAL, false, edit_id)
83
+ sleep 1
84
+ xwin.activate
85
+ XDo::Keyboard.ctrl_a
86
+ XDo::Keyboard.ctrl_c
87
+ sleep 0.2
88
+ assert_equal(TESTTEXT_SPECIAL.gsub("{TAB}", "\t"), XDo::Clipboard.read_clipboard)
89
+ end
90
+
91
+ def test_include
92
+ String.class_eval do
93
+ include XDo::Simulatable
94
+
95
+ def to_xdo
96
+ to_s
97
+ end
98
+ end
99
+
100
+ XDo::Keyboard.ctrl_a
101
+ XDo::Keyboard.delete
102
+ "Ein String".simulate
103
+ XDo::Keyboard.ctrl_a
104
+ sleep 0.2
105
+ XDo::Keyboard.ctrl_c
106
+ sleep 0.2
107
+ clip = XDo::Clipboard.read_clipboard
108
+ assert_equal("Ein String", clip)
109
+ end
110
+
111
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ #Encoding: UTF-8
3
+ gem "test-unit", ">= 2.1" #Ensure we use the gem
4
+ require "test/unit"
5
+ require "xdo/mouse.rb"
6
+
7
+ class MouseTest < Test::Unit::TestCase
8
+
9
+ def test_position
10
+ str = `#{XDo::XDOTOOL} getmouselocation`
11
+ xdpos = []
12
+ xdpos << str.match(/x:\s?(\d+)/)[1]
13
+ xdpos << str.match(/y:\s?(\d+)/)[1]
14
+ xdpos.collect!{|o| o.to_i}
15
+ assert_equal(xdpos, XDo::Mouse.position)
16
+ end
17
+
18
+ def test_move
19
+ XDo::Mouse.move(200, 200)
20
+ assert_equal([200, 200], XDo::Mouse.position)
21
+ XDo::Mouse.move(0, 0)
22
+ assert_equal([0, 0], XDo::Mouse.position)
23
+ XDo::Mouse.move(0, 200)
24
+ assert_equal([0, 200], XDo::Mouse.position)
25
+ XDo::Mouse.move(100, 100)
26
+ assert_equal([100, 100], XDo::Mouse.position)
27
+ end
28
+
29
+ end
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+ #Encoding: UTF-8
3
+ gem "test-unit", ">= 2.1" #Ensure we use the gem
4
+ require "test/unit"
5
+ require "xdo/xwindow.rb"
6
+ require "xdo/keyboard"
7
+
8
+ class WindowTest < Test::Unit::TestCase
9
+
10
+ attr_accessor :xwindow
11
+
12
+ #The command used to create new windows.
13
+ #The program MUST NOT start maximized. xdotool has no possibility of
14
+ #acting on maximized windows.
15
+ NEW_WINDOW_CMD = "gedit"
16
+
17
+ @@xwin = nil
18
+
19
+ def self.startup
20
+ fork{system(NEW_WINDOW_CMD)}
21
+ XDo::XWindow.wait_for_window(Regexp.new(Regexp.escape(NEW_WINDOW_CMD)))
22
+ @@xwin = XDo::XWindow.from_title(Regexp.new(Regexp.escape(NEW_WINDOW_CMD)))
23
+ end
24
+
25
+ def self.shutdown
26
+ @@xwin.focus
27
+ @@xwin.close!
28
+ end
29
+
30
+ def test_ewmh_active_window
31
+ begin
32
+ XDo::XWindow.from_active
33
+ rescue XDo::XError
34
+ #Standard not available
35
+ notify $!.message
36
+ end
37
+ end
38
+
39
+ def test_ewmh_wm_desktop
40
+ begin
41
+ XDo::XWindow.desktop_num
42
+ rescue XDo::XError
43
+ #Standard not available
44
+ notify $!.message
45
+ end
46
+ end
47
+
48
+ def test_ewmh_current_desktop
49
+ begin
50
+ XDo::XWindow.desktop
51
+ rescue XDo::XError
52
+ #Standard not available
53
+ notify $!.message
54
+ end
55
+ end
56
+
57
+ def test_exists
58
+ assert_equal(true, XDo::XWindow.exists?(@@xwin.title))
59
+ end
60
+
61
+ def test_unfocus
62
+ XDo::XWindow.unfocus
63
+ assert_raise(XDo::XError){XDo::XWindow.from_active} #Nothing's active anymore
64
+ end
65
+
66
+ def test_active
67
+ @@xwin.activate
68
+ assert_equal(@@xwin.id, XDo::XWindow.from_active.id)
69
+ end
70
+
71
+ def test_focused
72
+ @@xwin.unfocus
73
+ @@xwin.focus
74
+ assert_equal(@@xwin.id, XDo::XWindow.from_focused.id)
75
+ end
76
+
77
+ def test_move
78
+ @@xwin.move(57, 57)
79
+ assert_in_delta(50, 60, @@xwin.abs_position[0])
80
+ assert_in_delta(50, 50, @@xwin.abs_position[1])
81
+ assert_equal(@@xwin.abs_position, @@xwin.rel_position)
82
+ end
83
+
84
+ def test_resize
85
+ @@xwin.resize(500, 500)
86
+ assert_equal([500, 500], @@xwin.size)
87
+ end
88
+
89
+ def test_map
90
+ @@xwin.unmap
91
+ assert_equal(nil, @@xwin.visible?)
92
+ @@xwin.map
93
+ assert_block("Window is not visible."){@@xwin.visible?.kind_of?(Integer)}
94
+ end
95
+
96
+ end
97
+