simple_gui_creator 0.1.0
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.
- 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...
|