xdo 0.0.1-x86-linux
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +46 -0
- data/bin/xinfo.rb +136 -0
- data/lib/README.rdoc +55 -0
- data/lib/xdo.rb +28 -0
- data/lib/xdo/clipboard.rb +109 -0
- data/lib/xdo/drive.rb +66 -0
- data/lib/xdo/keyboard.rb +274 -0
- data/lib/xdo/mouse.rb +143 -0
- data/lib/xdo/wxaliases.rb +57 -0
- data/lib/xdo/xwindow.rb +416 -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 +23 -0
- data/test/test_keyboard.rb +60 -0
- data/test/test_mouse.rb +28 -0
- data/test/test_xwindow.rb +104 -0
- metadata +94 -0
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
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 "rake/gempackagetask"
|
6
|
+
require "rake/rdoctask"
|
7
|
+
require "rake/testtask"
|
8
|
+
|
9
|
+
spec = Gem::Specification.new do |s|
|
10
|
+
s.name = "xdo"
|
11
|
+
s.summary = "Simulate keyboard and mouse input via a ruby interface to xdotool and other console programs."
|
12
|
+
s.description =<<DESCRIPTION
|
13
|
+
XDo is a library to automate your mouse, fake keyboard input and
|
14
|
+
manipulate windows in a Linux X server environment. It's wrapped
|
15
|
+
around a lot of command line tools (see requirements) of which xdotool
|
16
|
+
is the main one, the others are usually installed.
|
17
|
+
DESCRIPTION
|
18
|
+
s.add_dependency("test-unit", ">= 2.0")
|
19
|
+
s.requirements = ["The xdotool command-line tool.", "xwininfo (usually installed)", "The xsel command-line tool.", "eject (usually installed)", "xkill (usually installed)"]
|
20
|
+
s.requirements << "The unit-test gem (will be installed if you don't have it)"
|
21
|
+
s.version = "0.0.1"
|
22
|
+
s.author = "Marvin Gülker"
|
23
|
+
s.email = "sutniuq@gmx.net"
|
24
|
+
s.platform = Gem::Platform::CURRENT
|
25
|
+
s.required_ruby_version = ">=1.9"
|
26
|
+
s.files = ["bin/xinfo.rb", Dir["lib/**/*.rb"], Dir["test/*.rb"], Dir["samples/*.rb"], "Rakefile", "lib/README.rdoc"].flatten
|
27
|
+
s.executables = ["xinfo.rb"]
|
28
|
+
s.has_rdoc = true
|
29
|
+
s.test_files = Dir["test/test_*.rb"]
|
30
|
+
s.rubyforge_project = "Automations"
|
31
|
+
end
|
32
|
+
Rake::GemPackageTask.new(spec).define
|
33
|
+
|
34
|
+
Rake::RDocTask.new do |rd|
|
35
|
+
rd.rdoc_files.include("lib/**/*.rb", "lib/README.rdoc")
|
36
|
+
rd.title = "xdo RDocs"
|
37
|
+
rd.main = "lib/README.rdoc"
|
38
|
+
end
|
39
|
+
|
40
|
+
Rake::TestTask.new("test") do |t|
|
41
|
+
t.pattern = "test/test_*.rb"
|
42
|
+
t.warning = true
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Tests XDo and then builds the gem file."
|
46
|
+
task :full_gem => [:test, :gem]
|
data/bin/xinfo.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#Encoding: UTF-8
|
3
|
+
#This file is part of Xdo.
|
4
|
+
#Copyright © 2009 Marvin Gülker
|
5
|
+
# Initia in potestate nostra sunt, de eventu fortuna iudicat.
|
6
|
+
#
|
7
|
+
#This program displays information about the currently selected window
|
8
|
+
#and the mouse. The displayed infos are updated every 1/2 second,
|
9
|
+
#but set XInfo::UPDATE_TIME to another value if you'd like to change that.
|
10
|
+
require_relative("../lib/xdo/xwindow")
|
11
|
+
require_relative("../lib/xdo/mouse")
|
12
|
+
require "wx"
|
13
|
+
require_relative("../lib/xdo/wxaliases")
|
14
|
+
|
15
|
+
puts "=" * 80
|
16
|
+
puts "Started: #{Time.now}"
|
17
|
+
at_exit{puts "Finished: #{Time.now}"}
|
18
|
+
|
19
|
+
|
20
|
+
#--
|
21
|
+
#=================================================
|
22
|
+
#Backend
|
23
|
+
#=================================================
|
24
|
+
#++
|
25
|
+
|
26
|
+
#Class that retrieves the information of windows.
|
27
|
+
class InfoGetter
|
28
|
+
|
29
|
+
attr_reader :act_win
|
30
|
+
attr_reader :cursorpos
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@act_win = XDo::XWindow.from_active
|
34
|
+
@cursorpos = XDo::Mouse.position
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
@act_win = XDo::XWindow.from_active
|
39
|
+
@cursorpos = XDo::Mouse.position
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
#--
|
45
|
+
#=================================================
|
46
|
+
#Frontend
|
47
|
+
#=================================================
|
48
|
+
#++
|
49
|
+
include Wx
|
50
|
+
|
51
|
+
#Frontend of this program.
|
52
|
+
class XInfo < App
|
53
|
+
|
54
|
+
#Time intervall of updating the infos, in milliseconds.
|
55
|
+
#Default is 500, which is 1/2 second.
|
56
|
+
UPDATE_TIME = 500
|
57
|
+
|
58
|
+
def on_init
|
59
|
+
puts "Creating main window"
|
60
|
+
@mainwindow = Frame.new(nil, -1, "XInfo", DEFAULT_POSITION, Size.new(300, 400), DEFAULT_FRAME_STYLE | STAY_ON_TOP)
|
61
|
+
@mainwindow.background_colour = NULL_COLOUR
|
62
|
+
puts "Creating controls"
|
63
|
+
create_controls
|
64
|
+
puts "Setting up timer"
|
65
|
+
create_updater
|
66
|
+
|
67
|
+
puts "Display GUI"
|
68
|
+
@mainwindow.show
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_controls
|
72
|
+
StaticText.new(@mainwindow, -1, "Title of active window: ", Point.new(20, 20))
|
73
|
+
@title = TextCtrl.new(@mainwindow, -1, "", Point.new(20, 50), Size.new(260, 24), TE_READONLY)
|
74
|
+
StaticText.new(@mainwindow, -1, "ID of active window: ", Point.new(20, 80))
|
75
|
+
@id = TextCtrl.new(@mainwindow, -1, "", Point.new(20, 110), Size.new(260, 24), TE_READONLY)
|
76
|
+
StaticText.new(@mainwindow, -1, "Absoloute and relative upper-left coords: ", Point.new(20,140))
|
77
|
+
StaticText.new(@mainwindow, -1, "Abs: ", Point.new(20, 173))
|
78
|
+
@abs_xy = TextCtrl.new(@mainwindow, -1, "", Point.new(60, 170), Size.new(70, 24), TE_READONLY)
|
79
|
+
StaticText.new(@mainwindow, -1, "Rel: ", Point.new(150, 173))
|
80
|
+
@rel_xy = TextCtrl.new(@mainwindow, -1, "", Point.new(190, 170), Size.new(70, 24), TE_READONLY)
|
81
|
+
StaticText.new(@mainwindow, -1, "Size: ", Point.new(20, 200))
|
82
|
+
StaticText.new(@mainwindow, -1, "Width: ", Point.new(20, 233))
|
83
|
+
@width = TextCtrl.new(@mainwindow, -1, "", Point.new(65, 230), Size.new(70, 24), TE_READONLY)
|
84
|
+
StaticText.new(@mainwindow, -1, "Height: ", Point.new(150, 233))
|
85
|
+
@height = TextCtrl.new(@mainwindow, -1, "", Point.new(200, 230), Size.new(70, 24), TE_READONLY)
|
86
|
+
|
87
|
+
StaticText.new(@mainwindow, -1, "Mouse position: ", Point.new(20, 300))
|
88
|
+
StaticText.new(@mainwindow, -1, "X: ", Point.new(20, 333))
|
89
|
+
@x = TextCtrl.new(@mainwindow, -1, "", Point.new(60, 330), Size.new(70, 24), TE_READONLY)
|
90
|
+
StaticText.new(@mainwindow, -1, "Y: ", Point.new(150, 333))
|
91
|
+
@y = TextCtrl.new(@mainwindow, -1, "", Point.new(190, 330), Size.new(70, 24), TE_READONLY)
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
def create_updater
|
96
|
+
@updater = InfoGetter.new
|
97
|
+
Timer.every(UPDATE_TIME) do
|
98
|
+
begin
|
99
|
+
@updater.update
|
100
|
+
@title.text = @updater.act_win.title
|
101
|
+
@id.text = @updater.act_win.id.inspect
|
102
|
+
@abs_xy.text = @updater.act_win.abs_position.join(", ")
|
103
|
+
@rel_xy.text = @updater.act_win.rel_position.join(", ")
|
104
|
+
ary = @updater.act_win.size
|
105
|
+
@width.text = ary[0].inspect
|
106
|
+
@height.text = ary[1].inspect
|
107
|
+
rescue NoMethodError
|
108
|
+
puts "Window closed. Skipping NoMethodError. "
|
109
|
+
#Das aktive Fenster wird wohl gerade gelöscht.
|
110
|
+
#Daher nichts machen.
|
111
|
+
end
|
112
|
+
|
113
|
+
curpos = @updater.cursorpos
|
114
|
+
@x.text = curpos[0].inspect
|
115
|
+
@y.text = curpos[1].inspect
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
begin
|
124
|
+
puts "Creating GUI"
|
125
|
+
x = XInfo.new
|
126
|
+
puts "Starting main loop"
|
127
|
+
x.main_loop
|
128
|
+
rescue
|
129
|
+
message = "An #{$!.class} occured. Backtrace: \n\n#{$@.join("\n")}"
|
130
|
+
message << "\n\nThe error message will be printed in the ~/.xinfo.rb.log file. "
|
131
|
+
message << "If you want to contact me about the error, send an email to sutniuq ät gmx Dot net and "
|
132
|
+
message << "attach the Err.log file."
|
133
|
+
msgbox = Wx::MessageDialog.new(nil, message, $!.class.to_s, OK | ICON_ERROR)
|
134
|
+
msgbox.show_modal
|
135
|
+
raise #for logging
|
136
|
+
end
|
data/lib/README.rdoc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--
|
2
|
+
This file is part of Xdo.
|
3
|
+
Copyright © 2009 Marvin Gülker
|
4
|
+
Initia in potestate nostra sunt, de eventu fortuna iudicat.
|
5
|
+
++
|
6
|
+
=XDo
|
7
|
+
XDo is a library to simmulate keyboard and mouse input and manipulating windows on the X server.
|
8
|
+
It's wrapped around the command-line tools xdotool[http://www.semicomplete.com/projects/xdotool/],
|
9
|
+
xsel[http://linux.die.net/man/1/xsel], xwininfo[http://linux.die.net/man/1/xwininfo], ecject[http://linux.die.net/man/1/eject] and xkill[http://linux.die.net/man/1/xkill],
|
10
|
+
so you will need to have them installed if you want to use Xdo (even if xsel, xwininfo, eject and xkill are usually already installed).
|
11
|
+
If not, as an Ubuntu or Debian user try to install them via
|
12
|
+
sudo apt-get install xdotool xsel xwininfo eject xkill
|
13
|
+
After they're installed, install XDo via RubyGems:
|
14
|
+
sudo gem install xdo
|
15
|
+
If you want to run the tests, change to the gem's installation directory and type "rake test".
|
16
|
+
Keep in mind that, while the tests run, you won't be able to use your computer, since they take control
|
17
|
+
of all things XDo is able to.
|
18
|
+
==Usage
|
19
|
+
#Require some of XDo's files
|
20
|
+
require "xdo/keyboard"
|
21
|
+
require "xdo/mouse"
|
22
|
+
require "xdo/xwindow"
|
23
|
+
#Move the cursor
|
24
|
+
XDo::Mouse.move(100, 100)
|
25
|
+
#Simulate text (with special escape sequences!)
|
26
|
+
XDo::Keyboard.simulate("This is{TAB}text.")
|
27
|
+
#And this will move a window containing the string "gedit",
|
28
|
+
#unless it's maximized.
|
29
|
+
win = XDo::XWindow.from_name("gedit")
|
30
|
+
win.move(200, 200)
|
31
|
+
==Files
|
32
|
+
You can require the following files in your projects:
|
33
|
+
* xdo/clipboard: Clipboard access
|
34
|
+
* xdo/drive: Get control of CD/DVD devices
|
35
|
+
* xdo/keyboard: Pretty self-explaining
|
36
|
+
* xdo/mouse: Automate the mouse
|
37
|
+
* xdo/xwindow: Manipulate windows in various ways
|
38
|
+
As an helpful extra, I created an executable ruby file "xinfo.rb". Thanks to RubyGems,
|
39
|
+
you can start this GUI tool right from the command line by typing:
|
40
|
+
xinfo.rb
|
41
|
+
It's by far not perfect, maybe not even good, but I think it can be useful sometimes
|
42
|
+
(you will need to have wxRuby installed, try <tt>sudo gem install wxruby-ruby19</tt>).
|
43
|
+
If you're looking for a more professional program, try the "X window information" tool.
|
44
|
+
==Notes
|
45
|
+
* +xdotool+ rejects to accept the --window option although it's documented, so you can't use the +w_id+ parameter of many methods in XDo::Keyboard.
|
46
|
+
* I recommand the "X window information" tool to get infos about your windows if you aren't satisfied by the xinfo.rb shipped with this package.
|
47
|
+
==Fairly incomplete
|
48
|
+
* I'm sure there are several things I didn't notice that can be automated somehow. If you know about, email me! Please add a description of the possibilities and a sample script (but don't expect that I will understand it - I only speak Ruby fluently, a bit C and a bit sh ;-) )
|
49
|
+
* Another interesting thing are the samples. There are many Linux distrubitions out there, and even many of them rely on X. I cannot test with another than a recent Ubuntu machine, but if you want to contribute and send samples for another OS, I want to encourage you to - I surely won't reject your work. :-)
|
50
|
+
==License/Copyright
|
51
|
+
Copyright © 2009 Marvin Gülker
|
52
|
+
This library is free software; you may redistribute it and/or modify it
|
53
|
+
under the terms of Ruby's license (see http://www.ruby-lang.org/en/LICENSE.txt).
|
54
|
+
You can contact me at sutniuq ät gmx Dot net.
|
55
|
+
Initia in potestate nostra sunt, de eventu fortuna iudicat.
|
data/lib/xdo.rb
ADDED
@@ -0,0 +1,28 @@
|
|
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
|
+
|
6
|
+
#The namespace of this library.
|
7
|
+
module XDo
|
8
|
+
|
9
|
+
#The command to start xdotool.
|
10
|
+
XDOTOOL = "xdotool"
|
11
|
+
|
12
|
+
#The command to start xsel.
|
13
|
+
XSEL = "xsel"
|
14
|
+
|
15
|
+
#The command to start xwininfo.
|
16
|
+
XWININFO = "xwininfo"
|
17
|
+
|
18
|
+
#The command to start xkill.
|
19
|
+
XKILL = "xkill"
|
20
|
+
|
21
|
+
#The command to start eject.
|
22
|
+
EJECT = "eject"
|
23
|
+
|
24
|
+
#Class for errors in this library.
|
25
|
+
class XError < StandardError
|
26
|
+
end
|
27
|
+
|
28
|
+
end #module XDo
|
@@ -0,0 +1,109 @@
|
|
1
|
+
#Encoding: UTF-8
|
2
|
+
require_relative("../xdo")
|
3
|
+
|
4
|
+
module XDo
|
5
|
+
|
6
|
+
#A module for interaction with the X clipboard. Please note, that the X clipboard
|
7
|
+
#consists of three parts: The PRIMARY clipboard, the CLIPBOARD clipboard, and
|
8
|
+
#the SECONDARY clipboard. The clipboard you access normally via [CTRL]+[C]
|
9
|
+
#or by right-clicking and selecting "copy", is usually the CLIPBOARD clipboard (but that
|
10
|
+
#depends on the application you use). The three main methods of this module (#read, #write
|
11
|
+
#and #clear) take a hash with the symbols of the clipboards to interact with. If you don't want to
|
12
|
+
#pass in the symbols, use the predefined read_xy, write_xy and clear_xy methods. They cannot
|
13
|
+
#access more than one clipboard at a time.
|
14
|
+
#The symbols for the clipboards are:
|
15
|
+
#[PRIMARY] :primary
|
16
|
+
#[SECONDARY] :secondary
|
17
|
+
#[CLIPBOARD] :clipboard
|
18
|
+
module Clipboard
|
19
|
+
|
20
|
+
class << self
|
21
|
+
|
22
|
+
##
|
23
|
+
# :singleton-method: read_primary
|
24
|
+
|
25
|
+
##
|
26
|
+
# :singleton-method: read_clipboard
|
27
|
+
|
28
|
+
##
|
29
|
+
# :singleton-method: read_secondary
|
30
|
+
|
31
|
+
##
|
32
|
+
# :singleton-method: write_primary
|
33
|
+
|
34
|
+
##
|
35
|
+
# :singleton-method: write_clipboard
|
36
|
+
|
37
|
+
##
|
38
|
+
# :singleton-method: write_secondary
|
39
|
+
|
40
|
+
##
|
41
|
+
# :singleton-method: clear_primary
|
42
|
+
|
43
|
+
##
|
44
|
+
# :singleton-method: clear_clipboard
|
45
|
+
|
46
|
+
##
|
47
|
+
# :singleton-method: clear_secondary
|
48
|
+
|
49
|
+
[:primary, :clipboard, :secondary].each do |sym|
|
50
|
+
|
51
|
+
define_method(:"read_#{sym}") do
|
52
|
+
read({sym => true})[sym]
|
53
|
+
end
|
54
|
+
|
55
|
+
define_method(:"write_#{sym}") do |text|
|
56
|
+
write(text, {sym => true})
|
57
|
+
text
|
58
|
+
end
|
59
|
+
|
60
|
+
define_method(:"clear_#{sym}") do
|
61
|
+
clear({sym => true})
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
#Reads text from the X clipboard. The +from+ argument specifies
|
68
|
+
#from what clipboard you want to read (in 70% of all cases you
|
69
|
+
#want to read from :clipboard). Return value is a hash with the
|
70
|
+
#clipboards you specified as the keys. The contents of the clipboards
|
71
|
+
#are the values. You can also use one of the read_xy methods directly.
|
72
|
+
def read(from = {:clipboard => true})
|
73
|
+
hsh = {}
|
74
|
+
hsh[:primary] = `#{XSEL}` if from[:primary]
|
75
|
+
hsh[:clipboard] = `#{XSEL} -b` if from[:clipboard]
|
76
|
+
hsh[:secondary] = `#{XSEL} -s` if from[:secondary]
|
77
|
+
hsh
|
78
|
+
end
|
79
|
+
|
80
|
+
#Writes data to the X clipboard. The +to+ argument soecifies
|
81
|
+
#the clipboard you want to write to. If you want to be able to paste
|
82
|
+
#your text via [CTRL]+[v], use :clipboard. You can also use the
|
83
|
+
#write_xy methods if you don't want to pass in the hash.
|
84
|
+
def write(text, to = {:primary => true, :clipboard => true})
|
85
|
+
IO.popen("xsel -i", "w"){|io| io.write(text)} if to[:primary]
|
86
|
+
IO.popen("xsel -b -i", "w"){|io| io.write(text)} if to[:clipboard]
|
87
|
+
IO.popen("xsel -s -i", "w"){|io| io.write(text)} if to[:secondary]
|
88
|
+
text
|
89
|
+
end
|
90
|
+
|
91
|
+
#Append data to text already written to the X clipboard. As in #write,
|
92
|
+
#you can specify the clipboard you want to append text to.
|
93
|
+
def append(text, to = {:primary => true, :clipboard => true})
|
94
|
+
IO.popen("xsel -a -i", "w"){|io| io.write(text)} if to[:primary]
|
95
|
+
IO.popen("xsel -b -a -i", "w"){|io| io.write(text)} if to[:clipboard]
|
96
|
+
IO.popen("xsel -s -a -i", "w"){|io| io.write(text)} if to[:secondary]
|
97
|
+
end
|
98
|
+
|
99
|
+
#Clears the specified clipboards.
|
100
|
+
def clear(clips = {:primary => true, :clipboard => true})
|
101
|
+
`#{XSEL} -c` if clips[:primary]
|
102
|
+
`#{XSEL} -b -c` if clips[:clipboard]
|
103
|
+
`#{XSEL} -s -c` if clips[:secondary]
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/xdo/drive.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#Encoding: UTF-8
|
2
|
+
require "open3"
|
3
|
+
require_relative("../xdo")
|
4
|
+
|
5
|
+
module XDo
|
6
|
+
|
7
|
+
#Some methods to interact with CD (and DVD, of course) drives.
|
8
|
+
#The value of the +drive+ parameter of many methods can be
|
9
|
+
#either a mount point or a device file like scd0.
|
10
|
+
#
|
11
|
+
#If you don't pass in a drive name, the returnvalue of #default will
|
12
|
+
#be used.
|
13
|
+
module Drive
|
14
|
+
|
15
|
+
class << self
|
16
|
+
include Open3
|
17
|
+
#Opens a drive.
|
18
|
+
def eject(drive = nil)
|
19
|
+
err = ""
|
20
|
+
drive = default unless drive
|
21
|
+
popen3("#{XDo::EJECT} #{drive}"){|stdin, stdout, stderr| err << stderr.read}
|
22
|
+
raise(XDo::XError, err) unless err.empty?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
#Closes a drive.
|
27
|
+
def close(drive = nil)
|
28
|
+
drive = default unless drive
|
29
|
+
err = ""
|
30
|
+
popen3("eject -t #{drive}"){|stdin, stdout, stderr| err << stderr.read}
|
31
|
+
raise(XDo::XError, err) unless err.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
#Returns the mount point of the default drive.
|
35
|
+
#You can use it as a value for a +drive+ parameter.
|
36
|
+
def default
|
37
|
+
err = ""
|
38
|
+
out = ""
|
39
|
+
popen3("#{XDo::EJECT} -d"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read}
|
40
|
+
raise(XDo::XError, err) unless err.empty?
|
41
|
+
out.match(/`(.*)'/)[1]
|
42
|
+
end
|
43
|
+
|
44
|
+
#Locks a drive, so that it can't be opened by
|
45
|
+
#using the eject button.
|
46
|
+
def lock(drive = nil)
|
47
|
+
drive = default unless drive
|
48
|
+
err = ""
|
49
|
+
popen3("#{XDo::EJECT} -i on #{drive}"){|stdin, stdout, stderr| err << stderr.read}
|
50
|
+
raise(XDo::XError, err) unless err.empty?
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
#Unlocks a drive, so that it can be opened
|
55
|
+
#by the eject button.
|
56
|
+
def release(drive = nil)
|
57
|
+
drive = default unless drive
|
58
|
+
err = ""
|
59
|
+
popen3("#{XDo::EJECT} -i off #{drive}")
|
60
|
+
raise(XDo::XError,err) unless err.empty?
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|