simple_gui_creator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +70 -0
- data/Rakefile +13 -0
- data/TODO +4 -0
- data/VERSION +1 -0
- data/bin/simple_gui_creator +88 -0
- data/examples/absolute_positioning.rb +12 -0
- data/ext/jl1.0.1.jar +0 -0
- data/lib/simple_gui_creator/drive_info.rb +162 -0
- data/lib/simple_gui_creator/mouse_control.rb +149 -0
- data/lib/simple_gui_creator/parse_template.rb +207 -0
- data/lib/simple_gui_creator/play_audio.rb +79 -0
- data/lib/simple_gui_creator/play_mp3_audio.rb +42 -0
- data/lib/simple_gui_creator/ruby_clip.rb +33 -0
- data/lib/simple_gui_creator/simple_gui_creator.rb +542 -0
- data/lib/simple_gui_creator/storage.rb +124 -0
- data/lib/simple_gui_creator.rb +18 -0
- data/spec/common.rb +5 -0
- data/spec/diesel.mp3 +0 -0
- data/spec/drive_info.spec.rb +75 -0
- data/spec/mouse.spec.rb +67 -0
- data/spec/parse_template.spec.rb +179 -0
- data/spec/play_mp3_audio.spec.rb +31 -0
- data/spec/ruby_clip.spec.rb +11 -0
- data/spec/run_drive_info.rb +4 -0
- data/spec/static.wav +0 -0
- data/spec/swing_helpers.spec.rb +140 -0
- data/vendor/dvdid.exe +0 -0
- data/vendor/mac_dvdid/bin/dvdid +0 -0
- data/vendor/mac_dvdid/include/dvdid/dvdid.h +67 -0
- data/vendor/mac_dvdid/include/dvdid/dvdid2.h +131 -0
- data/vendor/mac_dvdid/include/dvdid/export.h +32 -0
- data/vendor/mac_dvdid/lib/libdvdid.0.dylib +0 -0
- data/vendor/mac_dvdid/lib/libdvdid.a +0 -0
- data/vendor/mac_dvdid/lib/libdvdid.dylib +0 -0
- data/vendor/mac_dvdid/lib/libdvdid.la +41 -0
- metadata +110 -0
data/README
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
This gem is meant to make GUI development in Ruby easy, and even fun.
|
2
|
+
|
3
|
+
You can specify "easy window layout" in ASCII text, example, if you have a layout like this:
|
4
|
+
|
5
|
+
---------- A Title ---------------------------
|
6
|
+
| [a button:button1] [button text:button2] |
|
7
|
+
| "some text2:text1" |
|
8
|
+
----------------------------------------------
|
9
|
+
|
10
|
+
It will create a window that has buttons and text "like that," with reasonable spacing.
|
11
|
+
|
12
|
+
Here's how:
|
13
|
+
>> frame = ParseTemplate::JFramer.new # or optionally subclass this instead
|
14
|
+
>> frame.parse_setup_filename 'some_filename'
|
15
|
+
|
16
|
+
You can program behavior, like this:
|
17
|
+
|
18
|
+
>> frame.elements['button1'].on_clicked {
|
19
|
+
SimpleGui.show_blocking_message_dialog "you clicked button1!"
|
20
|
+
}
|
21
|
+
|
22
|
+
This has the effect of separating your view from your controller, in this case, because you can store the layouts in
|
23
|
+
an entirely separate file, or embedded in the code. "Normal humans" can edit the design layout files, for instance,
|
24
|
+
and the separation allows for easy peace of mind.
|
25
|
+
|
26
|
+
It also has helper methods for common GUI tasks, like the above show_blocking_message_dialog:
|
27
|
+
|
28
|
+
SimpleGui.new_nonexisting_filechooser_and_go # select file or filename for a "new file" (not yet existing)
|
29
|
+
SimpleGui.new_existing_dir_chooser_and_go # select pre-existing directory
|
30
|
+
SimpleGui.show_in_explorer(filename) # reveals file in Explorer for windows, Finder for OS X
|
31
|
+
text_from_user = SimpleGui.get_user_input "Input your name:" # these raise an exception if the user cancels the dialog,
|
32
|
+
|
33
|
+
A select-button prompt dialog:
|
34
|
+
|
35
|
+
if(SimpleGui.show_select_buttons_prompt("message title", :yes => 'text for the yes button', :no => 'text for the no button', :cancel => 'text for the cancel button') == :yes)
|
36
|
+
# they chose the "yes" equivalent button...
|
37
|
+
end
|
38
|
+
|
39
|
+
etc. ...
|
40
|
+
|
41
|
+
It provies a few helper methods to the ParseTemplate::JFramer (and JFrame) class, like:
|
42
|
+
|
43
|
+
#bring_to_front
|
44
|
+
#minimize
|
45
|
+
#restore
|
46
|
+
#after_closed { ... }
|
47
|
+
#after_minimized { ... }
|
48
|
+
|
49
|
+
It has helpers to control/playback audio, like mp3's or wave's, starting/stopping asynchronously (see the files in the 'lib/ruby-easy-gui-creator' directory.
|
50
|
+
It has helpers to set/get system clipboard contents.
|
51
|
+
It has helpers to control/query the mouse (I use this, but don't know why anybody else ever would want to LOL).
|
52
|
+
It has helpers to query the current system for its DVD drives, be notified when disks are inserted/changed, etc.
|
53
|
+
|
54
|
+
Feedback/feature requests welcome.
|
55
|
+
http://groups.google.com/group/roger-projects, roger-projects@googlegroups.com
|
56
|
+
I'd even be happy to wrap other gui frameworks (currently jruby/swing only) with the same helper functions, if anybody wanted it.
|
57
|
+
|
58
|
+
Enjoy!
|
59
|
+
|
60
|
+
To install/use:
|
61
|
+
|
62
|
+
$ gem install simple-ruby-gui-creator
|
63
|
+
|
64
|
+
>> require 'simple_gui'
|
65
|
+
>> ... [see above]
|
66
|
+
|
67
|
+
== Known problems ==
|
68
|
+
|
69
|
+
Only allows single element types per line currently, like "all text" or "all buttons", but I'm working on it :)
|
70
|
+
Only Jruby today, which can make loading time painful (hint: use splash screen).
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'sane'
|
2
|
+
require 'jeweler2'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "simple_gui_creator"
|
5
|
+
gem.summary = %Q{Framework to ease in creation of ruby GUI apps. Makes designing windows a snap.}
|
6
|
+
gem.description = gem.summary # %Q{TODO: longer description of your gem}
|
7
|
+
gem.email = "rogerdpack@gmail.com"
|
8
|
+
gem.homepage = "http://github.com/rdp/simple-ruby-gui-creator"
|
9
|
+
gem.authors = ["rogerdpack"]
|
10
|
+
gem.add_dependency 'sane'
|
11
|
+
end
|
12
|
+
|
13
|
+
Jeweler::RubygemsDotOrgTasks.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# assumes has rubygems loaded...since we need the sane gem
|
3
|
+
require 'os'
|
4
|
+
if !OS.jruby?
|
5
|
+
$stderr.puts 'jruby only for now, you can request a change to this, exiting...'
|
6
|
+
exit 1
|
7
|
+
end
|
8
|
+
require File.dirname(__FILE__) + "/../lib/simple_gui_creator.rb"
|
9
|
+
|
10
|
+
class TestWindow < ParseTemplate::JFramer
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super
|
14
|
+
string = <<-EOL
|
15
|
+
---------- Simple Ruby Gui Creator Test Window ---------------------------
|
16
|
+
| "Edit this, then..." |
|
17
|
+
| [Test it out! :create_button] |
|
18
|
+
| [ :text_area_to_use, width=70chars, height=500, font=fixed_width] |
|
19
|
+
| [ ] |
|
20
|
+
| [ ] |
|
21
|
+
| [ ] |
|
22
|
+
| [ ] |
|
23
|
+
| [ ] |
|
24
|
+
| [ ] |
|
25
|
+
| [ ] |
|
26
|
+
| |
|
27
|
+
| [Show code snippet :create_snippet] |
|
28
|
+
--------------------------------------------------------------------------
|
29
|
+
EOL
|
30
|
+
Kernel.print string
|
31
|
+
parse_setup_string string
|
32
|
+
elements[:text_area_to_use].text=string
|
33
|
+
elements[:create_button].on_clicked {
|
34
|
+
ParseTemplate::JFramer.new.parse_setup_string elements[:text_area_to_use].text
|
35
|
+
}
|
36
|
+
elements[:create_snippet].on_clicked {
|
37
|
+
frame = ParseTemplate::JFramer.new.parse_setup_string elements[:text_area_to_use].text
|
38
|
+
frame.close
|
39
|
+
|
40
|
+
element_code = ""
|
41
|
+
frame.elements.each{|e|
|
42
|
+
if e[1].is_a? Java::JavaxSwing::JButton
|
43
|
+
element_code += " elements[:#{e[0]}].on_clicked { puts 'clicked #{e[0]}' }\n"
|
44
|
+
end
|
45
|
+
}
|
46
|
+
|
47
|
+
code=<<-EOL
|
48
|
+
|
49
|
+
require 'simple-ruby-gui-creator.rb'
|
50
|
+
|
51
|
+
class MyWindow < ParseTemplate::JFramer
|
52
|
+
def initialize
|
53
|
+
super
|
54
|
+
parse_setup_filename 'my_window.template' # create this file first
|
55
|
+
|
56
|
+
#{element_code}
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
MyWindow.new
|
61
|
+
EOL
|
62
|
+
frame2 = ParseTemplate::JFramer.new.parse_setup_string <<-EOL
|
63
|
+
"Here's your snippet!"
|
64
|
+
[:code,width=500,height=400]
|
65
|
+
[ ]
|
66
|
+
[ ]
|
67
|
+
[Save Code Snippet:snippet_save]
|
68
|
+
"Here's your template:"
|
69
|
+
[:template_text,width=500,height=400,font=fixed_width]
|
70
|
+
[ ]
|
71
|
+
[Save Template:template_save]
|
72
|
+
EOL
|
73
|
+
frame2.elements[:code].text = code
|
74
|
+
frame2.elements[:snippet_save].on_clicked { save_file 'simple-gui-demo.rb', code}
|
75
|
+
template = elements[:text_area_to_use].text
|
76
|
+
frame2.elements[:template_text].text = template
|
77
|
+
frame2.elements[:template_save].on_clicked { save_file 'template.simple-gui-demo', template}
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def save_file default_filename, text
|
82
|
+
location = SimpleGuiCreator.new_nonexisting_or_existing_filechooser_and_go "Choose where to save:", nil, default_filename
|
83
|
+
File.write(location, text)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
TestWindow.new
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sane'
|
3
|
+
require __DIR__ + '/../lib/simple-ruby-gui-creator.rb'
|
4
|
+
|
5
|
+
a = ParseTemplate::JFramer.new
|
6
|
+
a.parse_setup_string <<EOL
|
7
|
+
|
8
|
+
| [a button:button_name,width=100,height=100,abs_x=50,abs_y=50] [a third button]
|
9
|
+
| [another button:button_name2] [another button:button_name4]|
|
10
|
+
| [another button:button_name3] |
|
11
|
+
|
12
|
+
EOL
|
data/ext/jl1.0.1.jar
ADDED
Binary file
|
@@ -0,0 +1,162 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright 2010, Roger Pack
|
3
|
+
This file is part of Sensible Cinema.
|
4
|
+
|
5
|
+
Sensible Cinema is free software: you can redistribute it and/or modify
|
6
|
+
it under the terms of the GNU General Public License as published by
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
8
|
+
(at your option) any later version.
|
9
|
+
|
10
|
+
Sensible Cinema is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
GNU General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU General Public License
|
16
|
+
along with Sensible Cinema. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
=end
|
18
|
+
|
19
|
+
require 'os'
|
20
|
+
require 'ostruct'
|
21
|
+
require 'thread'
|
22
|
+
|
23
|
+
class DriveInfo
|
24
|
+
|
25
|
+
def self.md5sum_disk(dir)
|
26
|
+
if OS.mac?
|
27
|
+
exe = "#{__DIR__}/../../vendor/mac_dvdid/bin/dvdid"
|
28
|
+
else
|
29
|
+
exe = "#{__DIR__}/../../vendor/dvdid.exe"
|
30
|
+
end
|
31
|
+
raise exe + '--exe doesnt exist?' unless File.exist? exe
|
32
|
+
command = "#{exe} \"#{dir}\""
|
33
|
+
output = `#{command}` # can take like 2.2s to spin up the disk...
|
34
|
+
raise 'dvdid command failed?' + command unless $?.exitstatus == 0
|
35
|
+
output.strip
|
36
|
+
end
|
37
|
+
|
38
|
+
@@drive_cache = nil
|
39
|
+
@@drive_cache_mutex = Mutex.new
|
40
|
+
@@drive_changed_notifies = []
|
41
|
+
def self.create_looping_drive_cacher
|
42
|
+
# has to be in its own thread or wmi will choke...
|
43
|
+
looped_at_least_once = false
|
44
|
+
if @caching_thread
|
45
|
+
looped_at_least_once = true
|
46
|
+
else
|
47
|
+
@caching_thread = Thread.new {
|
48
|
+
# use a dir glob to avoid having to use wmi too frequently (or accessing disks too often, which we might still be doing accidentally anyway)
|
49
|
+
if OS.doze?
|
50
|
+
old_drive_glob = '{' + DriveInfo.get_dvd_drives_even_if_no_disc_present.map{|dr| dr.MountPoint[0..0]}.join(',') + '}:/.' # must be in the thread for wmi
|
51
|
+
else
|
52
|
+
old_drive_glob = '/Volumes/*'
|
53
|
+
end
|
54
|
+
previously_known_about_discs = nil
|
55
|
+
loop {
|
56
|
+
should_update = false
|
57
|
+
@@drive_cache_mutex.synchronize { # in case the first update takes too long, basically, so they miss it LODO still a tiny race condition in here...might be useless...
|
58
|
+
looped_at_least_once = true # let the spawning waiting thread exit quickly
|
59
|
+
cur_disks = Dir[old_drive_glob]
|
60
|
+
if cur_disks != previously_known_about_discs
|
61
|
+
p 'updating disk lists...'
|
62
|
+
should_update = true
|
63
|
+
@@drive_cache = get_all_drives_as_ostructs_internal
|
64
|
+
previously_known_about_discs = cur_disks
|
65
|
+
end
|
66
|
+
}
|
67
|
+
notify_all_drive_blocks_that_change_has_occured if should_update
|
68
|
+
sleep 0.5
|
69
|
+
}
|
70
|
+
}
|
71
|
+
end
|
72
|
+
# maintain some thread startup sanity :P
|
73
|
+
while(!looped_at_least_once)
|
74
|
+
sleep 0.01
|
75
|
+
end
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
@@updating_mutex = Mutex.new # theoretically 2 threads could call this at once...so synchronize it...
|
80
|
+
def self.notify_all_drive_blocks_that_change_has_occured
|
81
|
+
@@updating_mutex.synchronize {
|
82
|
+
@@drive_changed_notifies.each{|block| block.call}
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.add_notify_on_changed_disks &block
|
87
|
+
raise unless block
|
88
|
+
@@drive_changed_notifies << block
|
89
|
+
should_call = false
|
90
|
+
@@drive_cache_mutex.synchronize { should_call = true if @@drive_cache }
|
91
|
+
block.call if should_call # should be called at least once, right, on init?
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.get_dvd_drives_as_openstruct
|
96
|
+
disks = get_all_drives_as_ostructs
|
97
|
+
disks.select{|d| d.Description =~ /CD-ROM/ && File.exist?(d.Name + "/VIDEO_TS")}
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
def self.get_drive_with_most_space_with_slash
|
102
|
+
disks = get_all_drives_as_ostructs
|
103
|
+
most_space = disks.sort_by{|d| d.FreeSpace}[-1]
|
104
|
+
most_space.MountPoint + "/"
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.get_all_drives_as_ostructs # gets all drives not just DVD drives...
|
108
|
+
@@drive_cache_mutex.synchronize {
|
109
|
+
if @caching_thread
|
110
|
+
@@drive_cache
|
111
|
+
else
|
112
|
+
get_all_drives_as_ostructs_internal # first time through for the startup thread goes here too
|
113
|
+
end
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def self.get_dvd_drives_even_if_no_disc_present # private since it uses internal
|
120
|
+
raise unless OS.doze? # no idea how to do this in mac :P
|
121
|
+
disks = get_all_drives_as_ostructs_internal
|
122
|
+
disks.select{|d| d.Description =~ /CD-ROM/}
|
123
|
+
end
|
124
|
+
|
125
|
+
# DevicePoint is like "where to point mplayer at this succer"
|
126
|
+
def self.get_all_drives_as_ostructs_internal
|
127
|
+
if OS.mac?
|
128
|
+
require 'plist'
|
129
|
+
Dir['/Volumes/*'].map{|dir|
|
130
|
+
parsed = Plist.parse_xml(`diskutil info -plist "#{dir}"`)
|
131
|
+
d2 = OpenStruct.new
|
132
|
+
d2.VolumeName = parsed["VolumeName"]
|
133
|
+
d2.Name = dir # DevNode?
|
134
|
+
d2.FreeSpace = parsed["FreeSpace"].to_i
|
135
|
+
d2.Description = parsed['OpticalDeviceType']
|
136
|
+
d2.MountPoint = parsed['MountPoint']
|
137
|
+
if d2.MountPoint == '/'
|
138
|
+
# try to guess a more writable default location...this works I guess?
|
139
|
+
d2.MountPoint = File.expand_path '~'
|
140
|
+
end
|
141
|
+
d2.DevicePoint = parsed['DeviceNode'].sub('disk', 'rdisk') # I've heard using rdisk is better/faster...
|
142
|
+
d2
|
143
|
+
}
|
144
|
+
else
|
145
|
+
require 'ruby-wmi'
|
146
|
+
disks = WMI::Win32_LogicalDisk.find(:all)
|
147
|
+
disks.map{|d| d2 = OpenStruct.new
|
148
|
+
d2.Description = d.Description
|
149
|
+
d2.VolumeName = d.VolumeName
|
150
|
+
d2.Name = d.Name
|
151
|
+
d2.FreeSpace = d.FreeSpace.to_i
|
152
|
+
d2.MountPoint = d.Name[0..2] # like f:\
|
153
|
+
d2.DevicePoint = d2.MountPoint
|
154
|
+
d2
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
if $0 == __FILE__
|
161
|
+
p DriveInfo.get_dvd_drives_as_openstruct
|
162
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright 2010, Roger Pack
|
3
|
+
This file is part of Sensible Cinema.
|
4
|
+
|
5
|
+
Sensible Cinema is free software: you can redistribute it and/or modify
|
6
|
+
it under the terms of the GNU General Public License as published by
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
8
|
+
(at your option) any later version.
|
9
|
+
|
10
|
+
Sensible Cinema is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
GNU General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU General Public License
|
16
|
+
along with Sensible Cinema. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
=end
|
18
|
+
require 'rubygems'
|
19
|
+
require 'ffi'
|
20
|
+
require 'java'
|
21
|
+
|
22
|
+
module MouseControl
|
23
|
+
extend FFI::Library
|
24
|
+
MouseInfo = java.awt.MouseInfo
|
25
|
+
|
26
|
+
ffi_lib 'user32'
|
27
|
+
ffi_convention :stdcall
|
28
|
+
|
29
|
+
MOUSEEVENTF_MOVE = 1
|
30
|
+
INPUT_MOUSE = 0
|
31
|
+
MOUSEEVENTF_ABSOLUTE = 0x8000
|
32
|
+
MOUSEEVENTF_LEFTDOWN = 0x0002
|
33
|
+
MOUSEEVENTF_LEFTUP = 0x0004
|
34
|
+
|
35
|
+
|
36
|
+
class MouseInput < FFI::Struct
|
37
|
+
layout :dx, :long,
|
38
|
+
:dy, :long,
|
39
|
+
:mouse_data, :ulong,
|
40
|
+
:flags, :ulong,
|
41
|
+
:time, :ulong,
|
42
|
+
:extra, :ulong
|
43
|
+
end
|
44
|
+
|
45
|
+
class InputEvent < FFI::Union
|
46
|
+
layout :mi, MouseInput
|
47
|
+
end
|
48
|
+
|
49
|
+
class Input < FFI::Struct
|
50
|
+
layout :type, :ulong,
|
51
|
+
:evt, InputEvent
|
52
|
+
end
|
53
|
+
|
54
|
+
# UINT SendInput(UINT nInputs, LPINPUT pInputs, int cbSize);
|
55
|
+
attach_function :SendInput, [ :uint, :pointer, :int ], :uint
|
56
|
+
|
57
|
+
# poller...
|
58
|
+
attach_function :GetAsyncKeyState, [:int], :uint
|
59
|
+
|
60
|
+
class << self
|
61
|
+
|
62
|
+
def jitter_forever_in_own_thread
|
63
|
+
|
64
|
+
old_x, old_y = get_mouse_location
|
65
|
+
Thread.new {
|
66
|
+
loop {
|
67
|
+
move_y = 8 # just enough for VLC when full screened...
|
68
|
+
cur_x, cur_y = get_mouse_location
|
69
|
+
if(cur_x == old_x && cur_y == old_y)
|
70
|
+
@total_movements += 1
|
71
|
+
# blit it up
|
72
|
+
move_mouse_relative(0, move_y)
|
73
|
+
move_mouse_relative(0, move_y * -1)
|
74
|
+
# let it move it back
|
75
|
+
sleep 0.05
|
76
|
+
old_x, old_y = get_mouse_location
|
77
|
+
sleep 0.75
|
78
|
+
else
|
79
|
+
# user has been moving the mouse around, so we don't need to, to not annoy them
|
80
|
+
old_x, old_y = get_mouse_location
|
81
|
+
sleep 3
|
82
|
+
end
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
def move_mouse_relative dx, dy
|
89
|
+
myinput = MouseControl::Input.new
|
90
|
+
myinput[:type] = MouseControl::INPUT_MOUSE
|
91
|
+
in_evt = myinput[:evt][:mi]
|
92
|
+
in_evt[:mouse_data] = 0 # null it out
|
93
|
+
in_evt[:flags] = MouseControl::MOUSEEVENTF_MOVE
|
94
|
+
in_evt[:time] = 0
|
95
|
+
in_evt[:extra] = 0
|
96
|
+
in_evt[:dx] = dx
|
97
|
+
in_evt[:dy] = dy
|
98
|
+
SendInput(1, myinput, MouseControl::Input.size)
|
99
|
+
end
|
100
|
+
|
101
|
+
def single_click_left_mouse_button
|
102
|
+
left_mouse_down!
|
103
|
+
left_mouse_up!
|
104
|
+
p "CLICKED LEFT MOUSE BUTTON"
|
105
|
+
end
|
106
|
+
|
107
|
+
def left_mouse_down!
|
108
|
+
send_left_mouse_button MOUSEEVENTF_LEFTDOWN
|
109
|
+
end
|
110
|
+
|
111
|
+
def left_mouse_up!
|
112
|
+
send_left_mouse_button MOUSEEVENTF_LEFTUP
|
113
|
+
end
|
114
|
+
|
115
|
+
VK_LBUTTON = 0x01 # mouse left button for GetAsyncKeyState (seeing if mouse down currently or not)
|
116
|
+
|
117
|
+
def left_mouse_button_state
|
118
|
+
GetAsyncKeyState(VK_LBUTTON) # ignore a first response, which also tells us if it has changed at all since last call
|
119
|
+
if GetAsyncKeyState(VK_LBUTTON) == 0 # zero means up
|
120
|
+
:up
|
121
|
+
else
|
122
|
+
:down
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# [x, y]
|
127
|
+
def get_mouse_location
|
128
|
+
loc = MouseInfo.getPointerInfo.getLocation # pure java!
|
129
|
+
[loc.x, loc.y]
|
130
|
+
end
|
131
|
+
|
132
|
+
attr_accessor :total_movements
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def send_left_mouse_button action_type
|
137
|
+
myinput = MouseControl::Input.new
|
138
|
+
myinput[:type] = MouseControl::INPUT_MOUSE
|
139
|
+
in_evt = myinput[:evt][:mi]
|
140
|
+
in_evt[:flags] = action_type
|
141
|
+
SendInput(1, myinput, MouseControl::Input.size)
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
MouseControl.total_movements=0 # ruby is a bit freaky with these...
|