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 +26 -7
- data/lib/fingerpoken/target.rb +6 -0
- data/lib/fingerpoken/tivo.rb +95 -0
- data/lib/fingerpoken/vnc.rb +2 -3
- data/lib/fingerpoken/xdo.rb +1 -1
- data/public/images/ajax-loader.png +0 -0
- data/public/images/form-check-off.png +0 -0
- data/public/images/form-check-on.png +0 -0
- data/public/images/form-radio-off.png +0 -0
- data/public/images/form-radio-on.png +0 -0
- data/public/images/icon-search-black.png +0 -0
- data/public/images/icons-18-black.png +0 -0
- data/public/images/icons-18-white.png +0 -0
- data/public/images/icons-36-black.png +0 -0
- data/public/images/icons-36-white.png +0 -0
- data/public/js/fingerpoken.js +132 -39
- data/views/index.haml +28 -3
- data/views/style.sass +2 -22
- metadata +15 -4
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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|
|
data/lib/fingerpoken/target.rb
CHANGED
@@ -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
|
data/lib/fingerpoken/vnc.rb
CHANGED
@@ -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
|
data/lib/fingerpoken/xdo.rb
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/public/js/fingerpoken.js
CHANGED
@@ -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
|
-
|
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) { /* $("#
|
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-
|
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) { /* $("#
|
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
|
309
|
+
if (Math.abs(delta_y) > 0) {
|
257
310
|
/* Scroll */
|
258
|
-
state.
|
259
|
-
|
260
|
-
|
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
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
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("
|
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-
|
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
|
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
|
-
|
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:
|
4
|
+
hash: 40202432048205
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
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-
|
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
|