fingerpoken 0.2.20101209171641 → 0.2.20101216024109

Sign up to get free protection for your applications and to get access to all the features.
data/bin/fingerpoken.rb CHANGED
@@ -26,19 +26,38 @@ class FingerPoken < Sinatra::Base
26
26
  end
27
27
 
28
28
  def main(args)
29
+ targets = []
29
30
  opts = OptionParser.new do |opts|
31
+ opts.banner = "Usage: #{$0} [options]"
32
+
33
+ opts.on("-t TARGET", "--target TARGET",
34
+ "Target a url. Can be given multiple times to target multiple things.") do |url|
35
+ target = URI.parse(url)
36
+ case target.scheme
37
+ when "xdo"
38
+ require "fingerpoken/#{target.scheme}"
39
+ targets << [:Xdo, {}]
40
+ when "vnc"
41
+ require "fingerpoken/#{target.scheme}"
42
+ targets << [:VNC, {}]
43
+ when "tivo"
44
+ require "fingerpoken/#{target.scheme}"
45
+ targets << [:Tivo, { :host => "192.168.0.134" }]
46
+ end
47
+ end
30
48
  end
49
+ opts.parse(args)
50
+
51
+ puts targets
52
+
31
53
  EventMachine::run do
32
54
  $:.unshift(File.dirname(__FILE__) + "/lib")
33
55
  channel = EventMachine::Channel.new
34
56
 
35
- # TODO(sissel): Pick up here and make command flags to choose the
36
- # target (vnc, xdo, etc)
37
- require "fingerpoken/xdo"
38
- target = FingerPoken::Target::Xdo.new :channel => channel
39
-
40
- #require "fingerpoken/vnc"
41
- #target = FingerPoken::Target::VNC.new :channel => channel
57
+ targets.each do |klass, args|
58
+ args.merge!({ :channel => channel })
59
+ puts FingerPoken::Target.const_get(klass).new(args)
60
+ end
42
61
 
43
62
  EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 5001) do |ws|
44
63
  ws.onmessage do |message|
@@ -11,6 +11,8 @@ class FingerPoken::Target
11
11
  case request["action"]
12
12
  when "mousemove_relative"
13
13
  mousemove_relative(request["rel_x"], request["rel_y"])
14
+ when "move_end"
15
+ move_end()
14
16
  when "click"
15
17
  click(request["button"])
16
18
  when "mousedown"
@@ -60,4 +62,8 @@ class FingerPoken::Target
60
62
  def keyup(key)
61
63
  @logger.info("keyup not supported")
62
64
  end
65
+
66
+ def move_end()
67
+ @logger.info("move_end not supported")
68
+ end
63
69
  end # class FingerPoken::Target
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # TODO(sissel): Refactor the protocol into an EM::Tivo module.
4
+
5
+ require "rubygems"
6
+ require "fingerpoken/target"
7
+ require "ostruct"
8
+
9
+ class FingerPoken::Target::Tivo < FingerPoken::Target
10
+ def initialize(config)
11
+ super(config)
12
+ # TODO(sissel): Make this a config
13
+ @host = config[:host]
14
+ @tivo = EventMachine::connect(@host, 31339, TivoClient, self)
15
+
16
+ @state = OpenStruct.new # TODO(sissel): Make this not an open struct...
17
+
18
+ @state.speed = 0
19
+ end
20
+
21
+ def mousemove_relative(x, y)
22
+ direction = x < 0 ? -1 : 1
23
+ want_speed = [(x.abs / 30).to_i, 3].min
24
+
25
+ want_speed *= direction
26
+ if want_speed != @state.speed
27
+ p [want_speed]
28
+ end
29
+
30
+ if want_speed > @state.speed
31
+ # increase to it
32
+ 1.upto(want_speed - @state.speed).each do
33
+ puts "UP"
34
+ @tivo.send_data("IRCODE FORWARD\r\n")
35
+ end
36
+ elsif (want_speed < @state.speed)
37
+ 1.upto( (want_speed - @state.speed).abs ).each do
38
+ @tivo.send_data("IRCODE REVERSE\r\n")
39
+ puts "DOWN"
40
+ end
41
+ # decrease to it
42
+ end
43
+ @state.speed = want_speed
44
+ end
45
+
46
+ def move_end
47
+ @tivo.send_data("IRCODE PLAY\r\n")
48
+ end
49
+
50
+ def click(button)
51
+ case button.to_i
52
+ when 1
53
+ @tivo.send_data("IRCODE PAUSE\r\n")
54
+ when 2 # 'middle' click (three fingers)
55
+ @tivo.send_data("IRCODE SELECT\r\n")
56
+ #when 2 # 'middle' click (three fingers)
57
+ #@tivo.send_data("IRCODE TIVO\r\n")
58
+ when 4 # scroll up
59
+ @tivo.send_data("IRCODE UP\r\n")
60
+ when 5 # scroll down
61
+ @tivo.send_data("IRCODE DOWN\r\n")
62
+ end
63
+ end
64
+
65
+ # Hack for now
66
+ def keypress(key)
67
+ case key
68
+ when "Home"
69
+ @tivo.send_data("IRCODE TIVO\r\n")
70
+ when "Return"
71
+ @tivo.send_data("IRCODE SELECT\r\n")
72
+ end
73
+ end
74
+
75
+ class TivoClient < EventMachine::Connection
76
+ def initialize(target)
77
+ puts "init #{self} / #{target}"
78
+ @target = target
79
+ end
80
+
81
+ def connection_completed
82
+ @target.register
83
+ puts "Ready"
84
+ end
85
+
86
+ def receive_data(data)
87
+ p "Tivo says: #{data}"
88
+ end
89
+
90
+ def send_data(data)
91
+ puts "Sending: #{data}"
92
+ super(data)
93
+ end
94
+ end # class TivoClient
95
+ end # class FingerPoken::Target::Tivo
@@ -33,7 +33,6 @@ class FingerPoken::Target::VNC < FingerPoken::Target
33
33
  end
34
34
  end
35
35
 
36
-
37
36
  def mousemove_relative(x, y)
38
37
  @x += x
39
38
  @y += y
@@ -41,14 +40,14 @@ class FingerPoken::Target::VNC < FingerPoken::Target
41
40
  end
42
41
 
43
42
  def mousedown(button)
44
- button = (1 << (button - 1))
43
+ button = (1 << (button.to_i - 1))
45
44
  return if @buttonmask & button != 0
46
45
  @buttonmask |= button
47
46
  update
48
47
  end
49
48
 
50
49
  def mouseup(button)
51
- button = (1 << (button - 1))
50
+ button = (1 << (button.to_i - 1))
52
51
  return if @buttonmask & button == 0
53
52
  @buttonmask &= (~button)
54
53
  update
@@ -62,7 +62,7 @@ class FingerPoken::Target::Xdo < FingerPoken::Target
62
62
  else
63
63
  # type printables, key others.
64
64
  if 32.upto(127).include?(key)
65
- LibXdo::xdo_type(@xdo, 0, request["key"].chr, 12000)
65
+ LibXdo::xdo_type(@xdo, 0, key.chr, 12000)
66
66
  else
67
67
  case key
68
68
  when 8
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -2,6 +2,19 @@
2
2
  /* TODO(sissel): This could use some serious refactoring. */
3
3
 
4
4
  $(document).ready(function() {
5
+ var status = $("#status");
6
+ var config = function (key, value, default_value) {
7
+ if (value) {
8
+ status.html("config[" + key + "] = " + value);
9
+ //alert(key + " => " + value);
10
+
11
+ window.localStorage[key] = value
12
+ return value
13
+ } else {
14
+ return window.localStorage[key] || default_value
15
+ }
16
+ };
17
+
5
18
  var state = {
6
19
  x: -1,
7
20
  y: -1,
@@ -10,8 +23,27 @@
10
23
  width: window.innerWidth,
11
24
  height: window.innerHeight,
12
25
  key: undefined,
13
- }
14
- var status = $("#status");
26
+ mouse: { },
27
+ scroll: {
28
+ y: 0,
29
+ }
30
+ };
31
+
32
+ /* Sync configuration elements */
33
+
34
+ /* Mouse movement */
35
+ console.log(config("fingerpoken/mouse/movement"));
36
+ $("input[name = \"mouse-config\"]")
37
+ .bind("change", function(event) {
38
+ config("fingerpoken/mouse/movement", event.target.value);
39
+ }).filter("[value = \"" + config("fingerpoken/mouse/movement") + "\"]")
40
+ .attr("checked", "checked").click()
41
+
42
+ $("input[name = \"mouse-acceleration\"]")
43
+ .bind("change", function(event) {
44
+ config("fingerpoken/mouse/acceleration", parseInt(event.target.value));
45
+ //status.html(event.target.value);
46
+ }).val(config("fingerpoken/mouse/acceleration")).change();
15
47
 
16
48
  /* Changing orientation sometimes leaves the viewport
17
49
  * not starting at 0,0. Fix it with this hack.
@@ -70,6 +102,8 @@
70
102
  //});
71
103
 
72
104
 
105
+ /* TODO(sissel): add mousedown/mousemove/mouseup support */
106
+ console.log($("#touchpad-surface"));
73
107
  $("#area").bind("touchstart", function(event) {
74
108
  event.preventDefault();
75
109
  var e = event.originalEvent;
@@ -95,9 +129,15 @@
95
129
  }))
96
130
  state.dragging = true;
97
131
  }
98
- }).bind("touchend", function(event) { /* $("#area").bind("touchend" ... */
132
+ }).bind("touchend", function(event) { /* $("#touchpadsurface").bind("touchend" ... */
99
133
  var e = event.originalEvent;
100
134
  var touches = e.touches;
135
+
136
+ if (state.mouse.vectorTimer) {
137
+ clearInterval(state.mouse.vectorTimer);
138
+ state.mouse.vectorTimer = null;
139
+ }
140
+
101
141
  if (state.dragging) {
102
142
  state.websocket.send(JSON.stringify({
103
143
  action: "mouseup",
@@ -105,7 +145,7 @@
105
145
  }));
106
146
  state.dragging = false;
107
147
  } else {
108
- if (state.moving) {
148
+ if (state.moving && !state.scrolling) {
109
149
  var e = state.last_move;
110
150
  var r = e.rotation;
111
151
  if (r < 0) {
@@ -114,7 +154,7 @@
114
154
 
115
155
  status.html(r);
116
156
  if (r > 75 && r < 105) {
117
- /* Activate the keyboard when there's a 90-dgree rotation*/
157
+ /* Activate the keyboard when there's a ~90-degree rotation*/
118
158
  var keyboard = $("<textarea id='keyboard' rows='10'></textarea>");
119
159
  keyboard.css("width", "100%");
120
160
  keyboard.css("height", "100%");
@@ -184,7 +224,14 @@
184
224
  });
185
225
  //status.html("<textarea id='keyboard'></textarea>");
186
226
  //$("#keyboard").focus();
227
+ } else { /* Otherwise, we didn't rotate */
228
+ state.websocket.send(JSON.stringify({
229
+ action: "move_end",
230
+ now: (new Date()),
231
+ }));
187
232
  }
233
+ } else if (state.scrolling) {
234
+ /* nothing for now */
188
235
  } else {
189
236
  /* No movement, click! */
190
237
  status.html("Click!");
@@ -196,16 +243,17 @@
196
243
  }
197
244
  }
198
245
  state.moving = false;
246
+ state.scrolling = false;
199
247
  event.preventDefault();
200
- }).bind("touchmove", function(event) { /* $("#area").bind("touchmove" ... */
248
+ }).bind("touchmove", function(event) { /* $("#touchpadsurface").bind("touchmove" ... */
201
249
  var e = event.originalEvent;
202
250
  var touches = e.touches;
203
251
  event.preventDefault();
204
252
  if (!state.moving) {
205
253
  /* Start calculating delta offsets now */
206
254
  state.moving = true;
207
- state.x = touches[0].clientX;
208
- state.y = touches[0].clientY;
255
+ state.start_x = state.x = touches[0].clientX;
256
+ state.start_y = state.y = touches[0].clientY;
209
257
  /* Skip this event */
210
258
  return;
211
259
  }
@@ -224,28 +272,33 @@
224
272
  output += "rotation: " + r + "\n";
225
273
  output += "scale: " + e.scale + "\n";
226
274
 
227
- x = touches[0].clientX;
228
- y = touches[0].clientY;
229
- delta_x = (x - state.x);
230
- delta_y = (y - state.y);
275
+ var x = touches[0].clientX;
276
+ var y = touches[0].clientY;
277
+ var delta_x = (x - state.x);
278
+ var delta_y = (y - state.y);
231
279
 
232
280
  /* Apply acceleration */
233
- sign_x = (delta_x < 0 ? -1 : 1);
234
- sign_y = (delta_y < 0 ? -1 : 1);
235
- delta_x = Math.ceil(Math.pow(Math.abs(delta_x), 1.5) * sign_x);
236
- delta_y = Math.ceil(Math.pow(Math.abs(delta_y), 1.5) * sign_y);
281
+ var sign_x = (delta_x < 0 ? -1 : 1);
282
+ var sign_y = (delta_y < 0 ? -1 : 1);
237
283
 
284
+ /* jQuery Mobile or HTML 'range' inputs don't support floating point.
285
+ * Hack around it by using larger numbers and compensating. */
286
+ var accel = config("fingerpoken/mouse/acceleration", null, 150) / 100.0;
287
+ output += "Accel: " + accel + "\n";
238
288
 
289
+ var delta_x = Math.ceil(Math.pow(Math.abs(delta_x), accel) * sign_x);
290
+ var delta_y = Math.ceil(Math.pow(Math.abs(delta_y), accel) * sign_y);
239
291
  output += "Delta: " + delta_x + ", " + delta_y + "\n";
240
- status.html(output);
241
292
 
242
293
  state.x = x;
243
294
  state.y = y;
244
295
 
296
+ /* TODO(sissel): Make this a config option */
245
297
  if (e.rotation < -10 || e.rotation > 10) {
246
298
  /* Skip rotations that are probably not mouse-cursor-wanting movements */
247
299
  return;
248
300
  }
301
+ /* TODO(sissel): Make this a config option */
249
302
  if (e.scale < 0.9 || e.scale > 1.1) {
250
303
  /* Skip scales that are probably not mouse-cursor-wanting movements */
251
304
  return;
@@ -253,22 +306,63 @@
253
306
 
254
307
  if (touches.length > 1 && !state.dragging) {
255
308
  /* Multifinger movement, probably should scroll? */
256
- if (delta_y < 0 || delta_y > 0) {
309
+ if (Math.abs(delta_y) > 0) {
257
310
  /* Scroll */
258
- state.websocket.send(JSON.stringify({
259
- action: "click",
260
- button: (delta_y < 0) ? 4 : 5,
261
- }))
262
- }
263
-
311
+ state.scroll.y += delta_y;
312
+
313
+ /* Don't scroll every time we move, wait until we move enough
314
+ * that it is more than 10 pixels. */
315
+ /* TODO(sissel): Make this a config option */
316
+ if (Math.abs(state.scroll.y) > 10) {
317
+ state.scrolling = true;
318
+ state.moving = false;
319
+ state.scroll.y = 0;
320
+ state.websocket.send(JSON.stringify({
321
+ action: "click",
322
+ button: (delta_y < 0) ? 4 : 5,
323
+ }))
324
+ }
325
+ } /* if (Math.abs(delta_y) > 0) */
264
326
  } else {
265
- state.websocket.send(JSON.stringify({
266
- action: "mousemove_relative",
267
- rel_x: delta_x,
268
- rel_y: delta_y
269
- }));
270
- }
271
- }); /* $("#area").bind( ... )*/
327
+ /* Only 1 finger, and we aren't dragging. So let's move! */
328
+ /* TODO(sissel): Refactor these in to fumctions */
329
+ var movement = config("fingerpoken/mouse/movement");
330
+ if (movement == "relative") {
331
+ status.html(output);
332
+ state.websocket.send(JSON.stringify({
333
+ action: "mousemove_relative",
334
+ rel_x: delta_x,
335
+ rel_y: delta_y
336
+ }));
337
+ } else if (movement == "vector") {
338
+ if (!state.mouse.vectorTimer) {
339
+ state.mouse.vectorTimer = setInterval(function() {
340
+ var rx = state.x - state.start_x;
341
+ var ry = state.y - state.start_y;
342
+ if (rx == 0 || ry == 0) {
343
+ return;
344
+ }
345
+
346
+ var sign_rx = (rx < 0 ? -1 : 1);
347
+ var sign_ry = (ry < 0 ? -1 : 1);
348
+ var vector_accel = accel / 1.7 /* feels like the right ratio */
349
+
350
+ output = "rx,ry = " + rx + ", " + ry + "\n";
351
+ rx = Math.ceil(Math.pow(Math.abs(rx), vector_accel) * sign_rx);
352
+ ry = Math.ceil(Math.pow(Math.abs(ry), vector_accel) * sign_ry);
353
+ output += "rx2,ry2 = " + rx + ", " + ry + "\n";
354
+ status.html(output)
355
+
356
+ state.websocket.send(JSON.stringify({
357
+ action: "mousemove_relative",
358
+ rel_x: rx,
359
+ rel_y: ry
360
+ }));
361
+ }, 15);
362
+ } /* if (!state.mouse.vectorTimer) */
363
+ } /* mouse vector movement */
364
+ } /* finger movement */
365
+ }); /* $("#touchpadsurface").bind( ... )*/
272
366
 
273
367
 
274
368
  /* Take commands like this:
@@ -280,22 +374,21 @@
280
374
  * <a class="command" data-action="click" data-button="button to click">
281
375
  */
282
376
  $("a.command").bind("touchstart", function(event) {
283
- //event.preventDefault();
284
377
  state.touchelement = this;
285
- }).bind("touchmove", function(event) {
378
+ }).bind("mousedown", function(event) {
379
+ state.touchelement = this;
380
+ event.preventDefault();
381
+ }).bind("touchmove mousemove", function(event) {
286
382
  event.preventDefault();
287
- }).bind("touchend", function(event) {
288
- //event.preventDefault();
289
- console.log(this)
383
+ }).bind("touchend mouseup", function(event) {
290
384
  if (state.touchelement == this) {
291
- console.log(this)
292
385
  state.websocket.send(JSON.stringify({
293
386
  action: $(this).attr("data-action"),
294
387
  key: $(this).attr("data-key"),
295
- button: $(this).attr("data-button"),
388
+ button: parseInt($(this).attr("data-button")),
296
389
  }));
297
390
  }
298
391
  });
299
-
392
+
300
393
  }); /* $(document).ready */
301
394
  })();
data/views/index.haml CHANGED
@@ -16,10 +16,11 @@
16
16
  :type => "text/javascript" }
17
17
 
18
18
  %body
19
+ / touchpad
19
20
  %div{"data-role" => "page", "data-theme" => "a", "id" => "touchpad"}
20
21
  %div.header{"data-role" => "header"}
21
22
  %h1 fingerpoken
22
- %a{:href => "config", "data-role" => "button", "data-inline" => true} Config
23
+ %a{:href => "#config", "data-role" => "button", "data-rel" => "dialog", "data-transition" => "pop"} Config
23
24
  %a{:href => "javascript:window.location.reload()", "data-role" => "button", "data-inline" => true} Reload
24
25
  %div.content{"data-role" => "content"}
25
26
  #area
@@ -27,7 +28,7 @@
27
28
 
28
29
  %div.footer{"data-role" => "footer"}
29
30
  %span.left
30
- %a#extras{:href => "#commands", "data-role" => "button", "data-rel" => "dialog", "data-transition" => "pop"} Commands
31
+ %a{:href => "#commands", "data-role" => "button", "data-rel" => "dialog", "data-transition" => "pop"} Commands
31
32
  %span.right
32
33
  %a.command{:href => "#", "data-role" => "button", "data-action" => "click", "data-button" => "1"} Left
33
34
  %a.command{:href => "#", "data-role" => "button", "data-action" => "click", "data-button" => "3"} Right
@@ -35,6 +36,7 @@
35
36
  %div{"data-role" => "page", "data-theme" => "a", "id" => "commands"}
36
37
  %div{"data-role" => "header"}
37
38
  %h1 Commands
39
+ %a{:href => "#touchpad", "data-role" => "button"} Touchpad
38
40
  %div{"data-role" => "content"}
39
41
  %a.command{:href => "#", "data-role" => "button", "data-action" => "keypress", "data-key" => "Right"} Next Slide
40
42
  %a.command{:href => "#", "data-role" => "button", "data-action" => "keypress", "data-key" => "Left"} Prev Slide
@@ -42,7 +44,30 @@
42
44
  %a.command{:href => "#", "data-role" => "button", "data-action" => "keypress", "data-key" => "ctrl+plus"} Zoom In
43
45
  %a.command{:href => "#", "data-role" => "button", "data-action" => "keypress", "data-key" => "ctrl+minus"} Zoom Out
44
46
  %a.command{:href => "#", "data-role" => "button", "data-action" => "keypress", "data-key" => "ctrl+0"} Zoom Zero
45
- %div{"data-role" => "footer"}
47
+ %hr
48
+ %a.command{:href => "#", "data-role" => "button", "data-action" => "keypress", "data-key" => "alt+Return"} Full Screen
49
+ %a.command{:href => "#", "data-role" => "button", "data-action" => "keypress", "data-key" => "End"} End
50
+ %a.command{:href => "#", "data-role" => "button", "data-action" => "keypress", "data-key" => "Home"} Home
51
+ %a.command{:href => "#", "data-role" => "button", "data-action" => "keypress", "data-key" => "Return"} Return
52
+
53
+ %div{"data-role" => "page", "data-theme" => "a", "id" => "config"}
54
+ %div{"data-role" => "header"}
46
55
  %a{:href => "#touchpad", "data-role" => "button"} Touchpad
56
+ %h1 Configuration
57
+ %div{"data-role" => "content"}
58
+ Mouse movement
59
+
60
+ / Mouse movement type
61
+ %fieldset{"data-role" => "controlgroup"}
62
+ %input{:type => :radio, :name => "mouse-config", :id => "mouse-relative", :value => "relative"}
63
+ %label{:for => "mouse-relative"} relative
64
+ %input{:type => :radio, :name => "mouse-config", :id => "mouse-absolute", :value => "absolute"}
65
+ %label{:for => "mouse-absolute"} absolute
66
+ %input{:type => :radio, :name => "mouse-config", :id => "mouse-vector", :value => "vector"}
67
+ %label{:for => "mouse-vector"} vector
68
+
69
+ / Mouse acceleration
70
+ %label{:for => "mouse-acceleration"} Acceleration
71
+ %input{:type => "range", :name => "mouse-acceleration", :id => "mouse-acceleration", :min => 1, :max => 300, :step => "any"}
47
72
 
48
73
  %script{ :src => "/js/fingerpoken.js" }
data/views/style.sass CHANGED
@@ -19,34 +19,14 @@ html, body
19
19
  h1
20
20
  color: orange
21
21
 
22
- #menu
23
- width: 100%
24
- color: white
25
- font-family: sans-serif
26
- font-weight: bold
27
- text-align: center
28
- vertical-align: middle
29
- padding: 0
30
- padding-bottom: 5px
31
- margin: 0
32
-
33
- .item:last-child
34
- border-right: 2px solid lightgreen
35
- .item
36
- font-size: 250%
37
- border: 2px solid lightgreen
38
- border-right: 0px
39
- height: 40px
40
- padding: 10px
41
- margin: 0
42
- text-align: center
43
- vertical-align: middle
44
22
  .content
45
23
  .header
46
24
  .footer
47
25
  font-size: 120%
48
26
  .right
49
27
  float: right
28
+ //.content > #area
29
+ //padding: 0px !important
50
30
  #area
51
31
  width: 100%
52
32
  height: 100%
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fingerpoken
3
3
  version: !ruby/object:Gem::Version
4
- hash: 40202418343269
4
+ hash: 40202432048205
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 20101209171641
10
- version: 0.2.20101209171641
9
+ - 20101216024109
10
+ version: 0.2.20101216024109
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jordan Sissel
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-09 00:00:00 -08:00
18
+ date: 2010-12-16 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -71,11 +71,22 @@ extra_rdoc_files: []
71
71
  files:
72
72
  - lib/fingerpoken/xdo.rb
73
73
  - lib/fingerpoken/target.rb
74
+ - lib/fingerpoken/tivo.rb
74
75
  - lib/fingerpoken/vnc.rb
75
76
  - public/reset.css
76
77
  - public/js/fingerpoken.js
77
78
  - public/js/jquery.min.js
78
79
  - public/js/jquery.mobile-1.0a2.min.js
80
+ - public/images/form-radio-on.png
81
+ - public/images/form-check-on.png
82
+ - public/images/icons-36-white.png
83
+ - public/images/icons-36-black.png
84
+ - public/images/icon-search-black.png
85
+ - public/images/icons-18-white.png
86
+ - public/images/ajax-loader.png
87
+ - public/images/form-radio-off.png
88
+ - public/images/icons-18-black.png
89
+ - public/images/form-check-off.png
79
90
  - public/jquery.mobile-1.0a2.min.css
80
91
  - views/style.sass
81
92
  - views/index.haml