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.
@@ -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
+