sensible-cinema 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/FAQ +12 -0
  2. data/LICENSE +5 -0
  3. data/README +7 -0
  4. data/Rakefile +30 -0
  5. data/TODO +106 -0
  6. data/VERSION +1 -0
  7. data/bin/bad_digit10.bmp +0 -0
  8. data/bin/bad_digit11.bmp +0 -0
  9. data/bin/bad_digit12.bmp +0 -0
  10. data/bin/bad_digit13.bmp +0 -0
  11. data/bin/bad_digit14.bmp +0 -0
  12. data/bin/bad_digit15.bmp +0 -0
  13. data/bin/bad_digit16.bmp +0 -0
  14. data/bin/bad_digit17.bmp +0 -0
  15. data/bin/bad_digit18.bmp +0 -0
  16. data/bin/bad_digit19.bmp +0 -0
  17. data/bin/bad_digit2.bmp +0 -0
  18. data/bin/bad_digit20.bmp +0 -0
  19. data/bin/bad_digit21.bmp +0 -0
  20. data/bin/bad_digit22.bmp +0 -0
  21. data/bin/bad_digit3.bmp +0 -0
  22. data/bin/bad_digit4.bmp +0 -0
  23. data/bin/bad_digit5.bmp +0 -0
  24. data/bin/bad_digit6.bmp +0 -0
  25. data/bin/bad_digit7.bmp +0 -0
  26. data/bin/bad_digit8.bmp +0 -0
  27. data/bin/bad_digit9.bmp +0 -0
  28. data/bin/hours.bmp +0 -0
  29. data/bin/minute_ones.bmp +0 -0
  30. data/bin/minute_tens.bmp +0 -0
  31. data/bin/scene-skipper +58 -0
  32. data/bin/second_ones.bmp +0 -0
  33. data/bin/second_tens.bmp +0 -0
  34. data/ext/mkrf_conf.rb +5 -0
  35. data/lib/blanker.rb +54 -0
  36. data/lib/keyboard_input.rb +45 -0
  37. data/lib/mouse.rb +60 -0
  38. data/lib/muter.rb +48 -0
  39. data/lib/ocr.rb +57 -0
  40. data/lib/overlayer.rb +341 -0
  41. data/lib/screen_tracker.rb +160 -0
  42. data/spec/common.rb +43 -0
  43. data/spec/convert_image.rb +8 -0
  44. data/spec/images/4.bmp +0 -0
  45. data/spec/images/black.bmp +0 -0
  46. data/spec/images/colon.bmp +0 -0
  47. data/spec/images/hulu_0.bmp +0 -0
  48. data/spec/images/hulu_2.bmp +0 -0
  49. data/spec/images/hulu_2_4.bmp +0 -0
  50. data/spec/images/hulu_3.bmp +0 -0
  51. data/spec/images/hulu_3_4.bmp +0 -0
  52. data/spec/images/hulu_4.bmp +0 -0
  53. data/spec/images/hulu_4_4.bmp +0 -0
  54. data/spec/images/hulu_5.bmp +0 -0
  55. data/spec/images/hulu_7.bmp +0 -0
  56. data/spec/images/hulu_slash.bmp +0 -0
  57. data/spec/images/vlc_0.bmp +0 -0
  58. data/spec/images/vlc_2_6.bmp +0 -0
  59. data/spec/images/vlc_4.bmp +0 -0
  60. data/spec/images/vlc_5.bmp +0 -0
  61. data/spec/images/vlc_6.bmp +0 -0
  62. data/spec/images/vlc_9.bmp +0 -0
  63. data/spec/images/vlc_colon.bmp +0 -0
  64. data/spec/mouse_forever.rb +2 -0
  65. data/spec/open.bat +1 -0
  66. data/spec/silence.wav +0 -0
  67. data/spec/spec.blanker.rb +35 -0
  68. data/spec/spec.keyboard_input.rb +56 -0
  69. data/spec/spec.mouse.rb +11 -0
  70. data/spec/spec.muter.rb +33 -0
  71. data/spec/spec.ocr.rb +33 -0
  72. data/spec/spec.overlayer.rb +331 -0
  73. data/spec/spec.screen_tracker.rb +211 -0
  74. data/spec/test_yaml.yml +4 -0
  75. data/vendor/gocr048.exe +0 -0
  76. data/zamples/players/captures/hulu full screen non hour.jpg +0 -0
  77. data/zamples/players/captures/hulu full screen over hour.jpg +0 -0
  78. data/zamples/players/captures/silence.bmp +0 -0
  79. data/zamples/players/captures/vlc grab over one hour file over one hour play.bmp +0 -0
  80. data/zamples/players/captures/vlc2.bmp +0 -0
  81. data/zamples/players/captures/vlc_full_screen_slider under one hour.bmp +0 -0
  82. data/zamples/players/captures/youtube full screen big screen.jpg +0 -0
  83. data/zamples/players/how_to +21 -0
  84. data/zamples/players/hulu_full_screened_over_an_hour.yml +23 -0
  85. data/zamples/players/vlc_full_screened.yml +6 -0
  86. data/zamples/players/vlc_full_screened_over_hour.yml +23 -0
  87. data/zamples/players/vlc_non_full_screened.yml +19 -0
  88. data/zamples/players/vlc_non_full_screened_under_an_hour.yml +19 -0
  89. data/zamples/scene_lists/disney_cars.yml +13 -0
  90. data/zamples/scene_lists/happy_feet.yml +7 -0
  91. data/zamples/scene_lists/mute_list.yml +15 -0
  92. metadata +286 -0
data/lib/overlayer.rb ADDED
@@ -0,0 +1,341 @@
1
+ require 'sane'
2
+ require 'thread'
3
+ require 'timeout'
4
+ require 'yaml'
5
+ require_relative 'muter'
6
+ require_relative 'blanker'
7
+ require 'pp' # pretty_inspect
8
+
9
+ class Time
10
+ def self.now_f
11
+ now.to_f
12
+ end
13
+ end
14
+
15
+ class OverLayer
16
+
17
+ def muted?
18
+ @am_muted
19
+ end
20
+
21
+ def blank?
22
+ @am_blanked
23
+ end
24
+
25
+ def mute!
26
+ @am_muted = true
27
+ puts 'muting!' if $VERBOSE
28
+ Muter.mute! unless $DEBUG
29
+ end
30
+
31
+ def unmute!
32
+ @am_muted = false
33
+ puts 'unmuting!' if $VERBOSE
34
+ Muter.unmute! unless $DEBUG
35
+ end
36
+
37
+ def blank!
38
+ @am_blanked = true
39
+ Blanker.blank_full_screen! unless $DEBUG
40
+ end
41
+
42
+ def unblank!
43
+ @am_blanked = false
44
+ Blanker.unblank_full_screen! unless $DEBUG
45
+ end
46
+
47
+ def reload_yaml!
48
+ if @file_mtime != (new_time = File.stat(@filename).mtime)
49
+ @all_sequences = OverLayer.translate_yaml(File.read(@filename))
50
+ # LTODO... @all_sequences = @all_sequences.map{|k, v| v.sort!} etc.
51
+ puts '(re) loaded mute sequences as', pretty_sequences.pretty_inspect, ""
52
+ pps 'because old time', @file_mtime.to_f, '!= new time', new_time.to_f if $VERBOSE
53
+ @file_mtime = new_time # save 0.0002!
54
+ else
55
+ p 'matching time:', new_time if $VERBOSE
56
+ end
57
+ end
58
+
59
+ def pretty_sequences
60
+ new_sequences = {}
61
+ @all_sequences.each{|type, values|
62
+ if values.is_a? Array
63
+ new_sequences[type] = values.map{|s, f|
64
+ [translate_time_to_human_readable(s), translate_time_to_human_readable(f)]
65
+ }
66
+ else
67
+ new_sequences[type] = values
68
+ end
69
+ }
70
+ new_sequences
71
+ end
72
+
73
+ def self.new_raw ruby_hash
74
+ File.write 'temp.yml', YAML.dump(ruby_hash)
75
+ OverLayer.new('temp.yml')
76
+ end
77
+
78
+ def initialize filename, minutes = nil
79
+ @filename = filename
80
+ @am_muted = false
81
+ @am_blanked = false
82
+ @mutex = Mutex.new
83
+ @cv = ConditionVariable.new
84
+ @file_mtime = nil
85
+ reload_yaml!
86
+ @start_time = Time.now_f # assume they want to start immediately...
87
+ if minutes
88
+ self.set_seconds self.class.translate_string_to_seconds(minutes)
89
+ end
90
+ end
91
+
92
+ def self.translate_yaml raw_yaml
93
+ all = YAML.load(raw_yaml)
94
+ # now it's like {:mutes => {"1:02.0" => "1:3.0"}}
95
+ # translate to floats like 62.0 => 63.0
96
+ for type in [:mutes, :blank_outs]
97
+ maps = all[type] || all[type.to_s] || {}
98
+ new = {}
99
+ maps.each{|s,e|
100
+ # both are like 1:02.0
101
+ new[translate_string_to_seconds(s)] = translate_string_to_seconds(e)
102
+ }
103
+ all.delete(type.to_s)
104
+ all[type] = new.sort
105
+ end
106
+ all
107
+ end
108
+
109
+ def translate_time_to_human_readable seconds
110
+ # 3600 => "1:00:00"
111
+ out = ''
112
+ hours = seconds.to_i / 3600
113
+ out << "%d" % hours
114
+ out << ":"
115
+ seconds = seconds - hours*3600
116
+ minutes = seconds.to_i / 60
117
+ out << "%02d" % minutes
118
+ seconds = seconds - minutes * 60
119
+ out << ":"
120
+ out << "%04.1f" % seconds
121
+ end
122
+
123
+ # make it optional...for now muhaha [lodo take out if never useful]
124
+ def timestamp_changed to_this_exact_string, delta
125
+ if to_this_exact_string
126
+ set_seconds OverLayer.translate_string_to_seconds(to_this_exact_string) + delta
127
+ else
128
+ round_current_time_to_nearest_second
129
+ end
130
+ end
131
+
132
+ def round_current_time_to_nearest_second
133
+ current_time = cur_time
134
+ better_time = current_time.round
135
+ set_seconds better_time
136
+ puts 'screen snapshot diff with time we thought it was was:' + (current_time - better_time).to_s if $VERBOSE
137
+ end
138
+
139
+ def self.translate_string_to_seconds s
140
+ # might actually already be a float...
141
+ if s.is_a? Float
142
+ return s
143
+ end
144
+
145
+ # s is like 1:01:02.0
146
+ total = 0.0
147
+ seconds = s.split(":")[-1]
148
+ total += seconds.to_f
149
+ minutes = s.split(":")[-2] || "0"
150
+ total += 60 * minutes.to_i
151
+ hours = s.split(":")[-3] || "0"
152
+ total += 60* 60 * hours.to_i
153
+ total
154
+ end
155
+
156
+ # returns seconds it's at currently...
157
+ def cur_time
158
+ return Time.now_f - @start_time
159
+ end
160
+
161
+ def status
162
+ time = "Current time: " + translate_time_to_human_readable(cur_time)
163
+ begin
164
+ mute, blank, next_sig = get_current_state
165
+ if next_sig == :done
166
+ state = " no more actions after this point..."
167
+ else
168
+ state = " next action at #{translate_time_to_human_readable next_sig}s (#{mute ? "muted" : '' } #{blank ? "blanked" : '' })"
169
+ end
170
+ end
171
+ time + state + " (HhMmSsTtdvq): "
172
+ end
173
+
174
+ def keyboard_input char
175
+ delta = case char
176
+ when 'h' then 60*60
177
+ when 'H' then -60*60
178
+ when 'm' then 60
179
+ when 'M' then -60
180
+ when 's' then 1
181
+ when 'S' then -1
182
+ when 't' then 0.1
183
+ when 'T' then -0.1
184
+ when 'v' then
185
+ $VERBOSE = !$VERBOSE
186
+ p 'set verbose to ', $VERBOSE
187
+ return
188
+ when 'd'
189
+ $DEBUG = !$DEBUG
190
+ p 'set debug to', $DEBUG
191
+ return
192
+ when ' ' then
193
+ puts cur_time
194
+ return
195
+ else nil
196
+ end
197
+ if delta
198
+ reload_yaml!
199
+ set_seconds(cur_time + delta)
200
+ else
201
+ puts 'invalid char: [' + char + ']'
202
+ end
203
+ end
204
+
205
+ # sets it to a new set of seconds...
206
+ def set_seconds seconds
207
+ seconds = [seconds, 0].max
208
+ @mutex.synchronize {
209
+ @start_time = Time.now_f - seconds
210
+ # tell the driver thread to continue onward. Cheery-o. We're not super thread friendly but good enough for having two contact each other...
211
+ @cv.signal
212
+ }
213
+ end
214
+
215
+ # we have a single scheduler thread, that is notified when the time may have changed
216
+ # like
217
+ # def restart new_time
218
+ # @current_time = xxx
219
+ # broadcast # things have changed
220
+ # end
221
+
222
+ def start_thread continue_forever = false
223
+ Thread.new { continue_until_past_all continue_forever }
224
+ end
225
+
226
+ # returns [start, end, active|:done]
227
+ def discover_state type, cur_time
228
+ for start, endy in @all_sequences[type]
229
+ if cur_time < endy
230
+ # first one that we haven't passed the *end* of yet
231
+ if(cur_time >= start)
232
+ return [start, endy, true]
233
+ else
234
+ return [start, endy, false]
235
+ end
236
+ end
237
+
238
+ end
239
+ return [nil, nil, :done]
240
+ end
241
+
242
+ # returns [true, false, next_moment_of_importance|:done]
243
+ def get_current_state
244
+ all = []
245
+ time = cur_time
246
+ for type in [:mutes, :blank_outs] do
247
+ all << discover_state(type, time)
248
+ end
249
+ output = []
250
+ # all is [[start, end, active]...] or [:done, :done]
251
+ # so create [true, false, next_moment]
252
+ earliest_moment = 1_000_000
253
+ all.each{|start, endy, active|
254
+ if active == :done
255
+ output << false
256
+ next
257
+ else
258
+ output << active
259
+ end
260
+ if active
261
+ earliest_moment = [earliest_moment, endy].min
262
+ else
263
+ earliest_moment = [earliest_moment, start].min
264
+ end
265
+ }
266
+ if earliest_moment == 1_000_000
267
+ output << :done
268
+ else
269
+ output << earliest_moment
270
+ end
271
+ output
272
+ end
273
+
274
+ def continue_until_past_all continue_forever
275
+ if RUBY_VERSION < '1.9.2'
276
+ raise 'need 1.9.2+ for MRI' unless RUBY_PLATFORM =~ /java/
277
+ end
278
+
279
+ @mutex.synchronize {
280
+ loop {
281
+ muted, blanked, next_point = get_current_state
282
+ if next_point == :done
283
+ unless continue_forever
284
+ return # done!
285
+ else
286
+ time_till_next_mute_starts = 1_000_000
287
+ end
288
+ else
289
+ time_till_next_mute_starts = next_point - cur_time
290
+ end
291
+
292
+ pps 'sleeping until next action (%s) begins in %fs (%f) %f' % [next_point, time_till_next_mute_starts, Time.now_f, cur_time] if $VERBOSE
293
+
294
+ @cv.wait(@mutex, time_till_next_mute_starts) if time_till_next_mute_starts > 0
295
+ pps 'just woke up from pre-mute wait at', Time.now_f if $VERBOSE
296
+ something_has_possibly_changed
297
+ }
298
+ }
299
+ end
300
+
301
+ def set_states!
302
+ should_be_muted, should_be_blank, next_point = get_current_state
303
+
304
+ if should_be_muted && !muted?
305
+ mute!
306
+ end
307
+
308
+ if !should_be_muted && muted?
309
+ unmute!
310
+ end
311
+
312
+ if should_be_blank && !blank?
313
+ blank!
314
+ end
315
+
316
+ if !should_be_blank && blank?
317
+ unblank!
318
+ end
319
+
320
+ end
321
+
322
+ def something_has_possibly_changed
323
+ current = cur_time
324
+ muted, blanked, next_point = get_current_state
325
+ @muted = muted
326
+ @blanked = blanked
327
+ @endy = next_point
328
+ return if next_point == :done
329
+ if(current < next_point)
330
+ set_states!
331
+ duration_left = @endy - current
332
+ pps 'just muted it at', Time.now_f, current, 'for interval:', 'which is', duration_left, 'more s' if $VERBOSE
333
+ if duration_left > 0
334
+ @cv.wait(@mutex, duration_left) if duration_left > 0
335
+ end
336
+ pps 'done sleeping', duration_left, 'was muted unmuting now', Time.now_f if $VERBOSE
337
+ set_states!
338
+ end
339
+ end
340
+
341
+ end
@@ -0,0 +1,160 @@
1
+ require 'win32/screenshot'
2
+ require 'sane'
3
+ require 'yaml'
4
+ require_relative 'ocr'
5
+
6
+ class ScreenTracker
7
+
8
+ def self.new_from_yaml yaml, callback
9
+ settings = YAML.load yaml
10
+ # heigth is shared...
11
+ height = settings["height"]
12
+ digits = settings["digits"]
13
+ return new(settings["name"], settings["x"], settings["y"], settings["width"], settings["height"], digits, callback)
14
+ end
15
+
16
+ # digits like {:hours => [100,5], :minute_tens, :minute_ones, :second_tens, :second_ones}
17
+ # digits share the height...
18
+ def initialize name_or_regex,x,y,width,height,digits=nil,callback=nil
19
+ # cache to save us 0.00445136 per time LOL
20
+ if name_or_regex.to_s.downcase == 'desktop'
21
+ # full screen option
22
+ @hwnd = hwnd = Win32::Screenshot::BitmapMaker.desktop_window
23
+ else
24
+ @hwnd = Win32::Screenshot::BitmapMaker.hwnd(name_or_regex)
25
+ end
26
+ unless @hwnd
27
+ print 'perhaps not running yet? [%s] START IT QUICKLY' % name_or_regex
28
+ until @hwnd
29
+ sleep 2
30
+ print ' trying again .'
31
+ STDOUT.flush
32
+ @hwnd = Win32::Screenshot::BitmapMaker.hwnd(name_or_regex)
33
+ end
34
+ puts 'found window'
35
+ end
36
+ pps 'height', height, 'width', width if $VERBOSE
37
+ raise 'poor dimentia' if width <= 0 || height <= 0
38
+ always_zero, always_zero, max_x, max_y = Win32::Screenshot::BitmapMaker.dimensions_for(@hwnd)
39
+ if(x < 0 || y < 0)
40
+ if x < 0
41
+ x = max_x + x
42
+ end
43
+ if y < 0
44
+ y = max_y + y
45
+ end
46
+ end
47
+ @height = height
48
+ @x = x; @y = y; @x2 = x+width; @y2 = y+height; @callback = callback
49
+ @max_x = max_x
50
+ raise 'poor width or wrong window' if @x2 > max_x || @x2 == x
51
+ raise 'poor height or wrong window' if @y2 > max_y || @y2 == y
52
+ @digits = digits
53
+ pps 'using x',@x, 'from x', x, 'y', @y, 'from y', y,'x2',@x2,'y2',@y2,'digits', @digits if $VERBOSE
54
+ end
55
+
56
+ def get_bmp
57
+ # Note: we no longer bring the window to the front tho...which it needs to be in both XP and Vista to work...sigh.
58
+ Win32::Screenshot::BitmapMaker.capture_area(@hwnd,@x,@y,@x2,@y2) {|h,w,bmp| return bmp}
59
+ end
60
+
61
+ def get_full_bmp
62
+ Win32::Screenshot.hwnd(@hwnd, 0) {|h,w,bmp| return bmp}
63
+ end
64
+
65
+ def dump_bmp filename = 'dump.bmp'
66
+ File.binwrite filename, get_bmp
67
+ File.binwrite 'all.' + filename, get_full_bmp
68
+ dump_digits
69
+ end
70
+
71
+ def dump_digits
72
+ if @digits
73
+ for type, bitmap in get_digits_as_bitmaps
74
+ File.binwrite type.to_s + '.bmp', bitmap
75
+ end
76
+ end
77
+ end
78
+
79
+ DIGIT_TYPES = [:hours, :minute_tens, :minute_ones, :second_tens, :second_ones]
80
+ # returns like {:hours => nil, :minutes_tens => raw_bmp, ...
81
+ def get_digits_as_bitmaps
82
+ # @digits are like {:hours => [100,5], :minute_tens => [x, width], :minute_ones, :second_tens, :second_ones}
83
+ out = {}
84
+ for type in DIGIT_TYPES
85
+ assert @digits.key?(type)
86
+ if @digits[type]
87
+ x,w = @digits[type]
88
+ if(x < 0)
89
+ x = @max_x + x
90
+ end
91
+ out[type] = Win32::Screenshot::BitmapMaker.capture_area(@hwnd, x, @y, x+w, @y2) {|h,w,bmp| bmp}
92
+ end
93
+ end
94
+ out
95
+ end
96
+
97
+ def get_relative_coords
98
+ [@x,@y,@x2,@y2]
99
+ end
100
+
101
+ # split out for unit testing purposes
102
+ def identify_digit bitmap
103
+ require 'ruby-debug'
104
+ # debugger
105
+ OCR.identify_digit(bitmap, @digits)
106
+ end
107
+
108
+ def wait_till_next_change
109
+ original = get_bmp
110
+ loop {
111
+ current = get_bmp
112
+ if current != original
113
+ if @digits
114
+ out = {}
115
+ dump_digits if $DEBUG
116
+ digits = get_digits_as_bitmaps # 0.08s [!] not too accurate...
117
+ start = Time.now
118
+ DIGIT_TYPES.each{|type|
119
+ if digits[type]
120
+ digit = identify_digit(digits[type])
121
+ unless digit
122
+ if $DEBUG || $VERBOSE
123
+ @a ||= 1
124
+ @a += 1
125
+ p 'unable to identify digit!' + type.to_s + @a.to_s
126
+ File.binwrite("bad_digit#{@a}.bmp", digits[type])
127
+ end
128
+ # early return
129
+ return
130
+ end
131
+ out[type] = digit
132
+ else
133
+ # there isn't one on screen, so probably zero...
134
+ out[type] = 0
135
+ end
136
+ }
137
+ out = "%d:%d%d:%d%d" % DIGIT_TYPES.map{|type| out[type]}
138
+ p 'got new screen time ' + out + " delta:" + (Time.now - start).to_s if $VERBOSE
139
+ # if the window was in the background it will be all zeroes, so nil it out
140
+ out = nil unless out =~ /[1-9]/
141
+ return out, Time.now - start
142
+ else
143
+ puts 'screen time change only detected...' if $VERBOSE
144
+ end
145
+ return
146
+ end
147
+ sleep 0.02
148
+ }
149
+ end
150
+
151
+ def process_forever_in_thread
152
+ Thread.new {
153
+ loop {
154
+ out_time, delta = wait_till_next_change
155
+ @callback.timestamp_changed out_time, delta
156
+ }
157
+ }
158
+ end
159
+
160
+ end
data/spec/common.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ begin
3
+ require 'rspec' # rspec2
4
+ rescue LoadError
5
+ require 'spec' # rspec1
6
+ require 'spec/autorun'
7
+ end
8
+
9
+ # some useful utilities...
10
+
11
+ require 'sane'
12
+ require 'benchmark'
13
+ Thread.abort_on_exception = true
14
+ require 'timeout'
15
+ require 'fileutils'
16
+
17
+ begin
18
+ require 'hitimes'
19
+ Benchmark.module_eval {
20
+ def self.realtime
21
+ Hitimes::Interval.measure { yield }
22
+ end
23
+ }
24
+ rescue LoadError
25
+ if RUBY_PLATFORM =~ /java/
26
+ require 'java'
27
+ Benchmark.module_eval {
28
+ def self.realtime
29
+ beginy = java.lang.System.nano_time
30
+ yield
31
+ (java.lang.System.nano_time - beginy)/1000000000.0
32
+ end
33
+ }
34
+ else
35
+ puts 'no hitimes available...'
36
+ end
37
+
38
+ end
39
+
40
+ #for file in Dir[File.dirname(__FILE__) + "/../lib/*"] do
41
+ # don't load them here in case one or other fails...
42
+ # require file
43
+ #end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'sane'
3
+ require 'RMagick'
4
+
5
+ to = ARGV[1] || 'pnm'
6
+ img = Magick::Image.from_blob(File.binread ARGV[0])
7
+ png = img[0].to_blob {self.format = to } # TIFF
8
+ File.open("picture10." + to, "wb") {|file| file.puts png}
data/spec/images/4.bmp ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,2 @@
1
+ require './../lib/mouse.rb'
2
+ Mouse::jitter_forever_in_own_thread.join
data/spec/open.bat ADDED
@@ -0,0 +1 @@
1
+ "/program files/VideoLan/VLC/vlc.exe" silence.wav
data/spec/silence.wav ADDED
Binary file
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/common'
2
+ require_relative '../lib/blanker.rb'
3
+
4
+ describe Blanker do
5
+
6
+ it "should be able to blank then unblank" do
7
+ Blanker.blank_full_screen!
8
+ sleep 3
9
+ Blanker.unblank_full_screen!
10
+ end
11
+
12
+ it "should be able to blank several times" do
13
+ 3.times {
14
+ Blanker.blank_full_screen!
15
+ Blanker.unblank_full_screen!
16
+ }
17
+ end
18
+
19
+ it "should be able to unblank several times I suppose" do
20
+ 3.times {
21
+ Blanker.unblank_full_screen!
22
+ }
23
+ end
24
+
25
+ describe "future work", :pending => true do
26
+
27
+ it "should be able to blank certain coords"
28
+
29
+ it "should have a color optionally"
30
+
31
+ it "should have a picture optionally"
32
+
33
+ end
34
+
35
+ end