fingerpoken 0.2.20110104190832 → 0.3.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/bin/fingerpoken.rb +9 -2
- data/lib/fingerpoken/target.rb +53 -3
- data/lib/fingerpoken/vnc.rb +49 -6
- data/public/js/2.2.0-crypto-md5.js +11 -0
- data/public/js/2.2.0-hmac-min.js +1 -0
- data/public/js/CRYPTO_JS_LICENSE +9 -0
- data/public/js/fingerpoken.js +201 -142
- data/views/index.haml +6 -0
- metadata +76 -17
data/bin/fingerpoken.rb
CHANGED
@@ -28,6 +28,8 @@ end
|
|
28
28
|
|
29
29
|
def main(args)
|
30
30
|
targets = []
|
31
|
+
passphrase = nil
|
32
|
+
|
31
33
|
opts = OptionParser.new do |opts|
|
32
34
|
opts.banner = "Usage: #{$0} [options]"
|
33
35
|
|
@@ -49,6 +51,10 @@ def main(args)
|
|
49
51
|
targets << [:Tivo, { :host => target.host }]
|
50
52
|
end
|
51
53
|
end
|
54
|
+
|
55
|
+
opts.on("--passphrase PASSPHRASE", "Passphrase for verifying client calls") do |val|
|
56
|
+
passphrase = val
|
57
|
+
end
|
52
58
|
end
|
53
59
|
opts.parse(args)
|
54
60
|
|
@@ -62,8 +68,9 @@ def main(args)
|
|
62
68
|
channel = EventMachine::Channel.new
|
63
69
|
|
64
70
|
targets.each do |klass, args|
|
65
|
-
args.merge!({ :channel => channel })
|
71
|
+
args.merge!({ :channel => channel, :passphrase => passphrase })
|
66
72
|
target = FingerPoken::Target.const_get(klass).new(args)
|
73
|
+
|
67
74
|
target.register
|
68
75
|
end # targets.each
|
69
76
|
|
@@ -84,4 +91,4 @@ def main(args)
|
|
84
91
|
end # EventMachine::run
|
85
92
|
end
|
86
93
|
|
87
|
-
|
94
|
+
main(ARGV)
|
data/lib/fingerpoken/target.rb
CHANGED
@@ -1,17 +1,67 @@
|
|
1
|
+
require "rubygems"
|
1
2
|
require "logger"
|
3
|
+
require "hmac-md5"
|
2
4
|
|
3
5
|
class FingerPoken::Target
|
4
6
|
def initialize(config)
|
5
7
|
@channel = config[:channel]
|
6
8
|
@logger = Logger.new(STDERR)
|
7
9
|
@logger.level = ($DEBUG ? Logger::DEBUG: Logger::WARN)
|
10
|
+
@passphrase = config[:passphrase] # OK if this is nil
|
11
|
+
|
12
|
+
@last_sequence = -1
|
8
13
|
end
|
9
14
|
|
15
|
+
def verify(request, callback)
|
16
|
+
if !request["signature"]
|
17
|
+
# TODO(sissel): Send callback saying "passphrase required"
|
18
|
+
@logger.warn("Message with no signature")
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
|
22
|
+
if request["sequence"] < @last_sequence
|
23
|
+
# Reject out of sequence or replayed messages
|
24
|
+
# TODO(sissel): Report replay attack detected
|
25
|
+
@logger.warn("Sequence #{request["sequence"]} < #{@last_sequence} "\
|
26
|
+
"(last sequence). Replay attack or bug?")
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
|
30
|
+
hmac = HMAC::MD5.new(@passphrase)
|
31
|
+
hmac.update(request["sequence"].to_s)
|
32
|
+
digest_bytes = hmac.digest.bytes.to_a
|
33
|
+
if request["signature"] == digest_bytes
|
34
|
+
return true
|
35
|
+
else
|
36
|
+
# TODO(sissel): Report verification failed
|
37
|
+
@logger.warn("Signature verify failed. Server:#{digest_bytes.inspect}, Client:#{request["signature"].inspect}")
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
end # def verify
|
41
|
+
|
10
42
|
def register
|
43
|
+
if @registered
|
44
|
+
@logger.warn("Ignoring extra call to #{self.class.name}#register. Trace:\n#{caller[0..3].join("\n")}")
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
@registered = true
|
49
|
+
@logger.debug(:register => self.class.name)
|
11
50
|
@channel.subscribe do |obj|
|
12
51
|
request = obj[:request]
|
13
52
|
callback = obj[:callback]
|
14
|
-
@logger.debug(request)
|
53
|
+
@logger.debug(:request => request)
|
54
|
+
|
55
|
+
if @passphrase
|
56
|
+
verified = verify(request, callback)
|
57
|
+
if !verified
|
58
|
+
@logger.warn("Dropping corrupt/forged request")
|
59
|
+
next
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@last_sequence = request["sequence"].to_i
|
64
|
+
|
15
65
|
response = case request["action"]
|
16
66
|
when "mousemove_relative"
|
17
67
|
mousemove_relative(request["rel_x"], request["rel_y"])
|
@@ -36,8 +86,8 @@ class FingerPoken::Target
|
|
36
86
|
if response.is_a?(Hash)
|
37
87
|
callback.call(response)
|
38
88
|
end
|
39
|
-
end
|
40
|
-
end
|
89
|
+
end # @channel.subscribe
|
90
|
+
end # def register
|
41
91
|
|
42
92
|
# Subclasses should implement this.
|
43
93
|
def mousemove_relative(x, y)
|
data/lib/fingerpoken/vnc.rb
CHANGED
@@ -16,6 +16,7 @@ class FingerPoken::Target::VNC < FingerPoken::Target
|
|
16
16
|
@password = (config[:password] or config[:user])
|
17
17
|
@host = config[:host]
|
18
18
|
@port = (config[:port] or 5900)
|
19
|
+
@ready = false
|
19
20
|
@recenter = config[:recenter]
|
20
21
|
|
21
22
|
# For eventmachine-vnc
|
@@ -31,7 +32,11 @@ class FingerPoken::Target::VNC < FingerPoken::Target
|
|
31
32
|
@buttonmask = 0
|
32
33
|
end
|
33
34
|
|
34
|
-
def
|
35
|
+
def update_mouse
|
36
|
+
if !@ready
|
37
|
+
@logger.warn("VNC connection is not ready. Ignoring update.")
|
38
|
+
return { "action" => "status", "status" => "VNC connection not ready, yet" }
|
39
|
+
end
|
35
40
|
@vnc.pointerevent(@x, @y, @buttonmask)
|
36
41
|
|
37
42
|
# TODO(sissel): Hack to make it work in TF2.
|
@@ -43,10 +48,15 @@ class FingerPoken::Target::VNC < FingerPoken::Target
|
|
43
48
|
end
|
44
49
|
end
|
45
50
|
|
51
|
+
def ready
|
52
|
+
@ready = true
|
53
|
+
return { "action" => "status", "status" => "VNC READY!" }
|
54
|
+
end
|
55
|
+
|
46
56
|
def mousemove_relative(x, y)
|
47
57
|
@x += x
|
48
58
|
@y += y
|
49
|
-
|
59
|
+
update_mouse
|
50
60
|
return nil
|
51
61
|
end
|
52
62
|
|
@@ -56,7 +66,7 @@ class FingerPoken::Target::VNC < FingerPoken::Target
|
|
56
66
|
ybuf = @screen_y * 0.1
|
57
67
|
@x = (((@screen_x + xbuf) * px) - (xbuf / 2)).to_i
|
58
68
|
@y = (((@screen_y + ybuf) * py) - (ybuf / 2)).to_i
|
59
|
-
|
69
|
+
update_mouse
|
60
70
|
return nil
|
61
71
|
end
|
62
72
|
|
@@ -64,7 +74,7 @@ class FingerPoken::Target::VNC < FingerPoken::Target
|
|
64
74
|
button = (1 << (button.to_i - 1))
|
65
75
|
return if @buttonmask & button != 0
|
66
76
|
@buttonmask |= button
|
67
|
-
|
77
|
+
update_mouse
|
68
78
|
return nil
|
69
79
|
end
|
70
80
|
|
@@ -72,13 +82,46 @@ class FingerPoken::Target::VNC < FingerPoken::Target
|
|
72
82
|
button = (1 << (button.to_i - 1))
|
73
83
|
return if @buttonmask & button == 0
|
74
84
|
@buttonmask &= (~button)
|
75
|
-
|
85
|
+
update_mouse
|
76
86
|
return nil
|
77
87
|
end
|
78
88
|
|
79
89
|
# TODO(sissel): Add keyboard support.
|
80
90
|
# VNC uses the same keysym values as X11, so that's a win. We can likely
|
81
91
|
# leverage xdo's char-to-keysym magic with VNC.
|
92
|
+
def keypress(key)
|
93
|
+
puts "Got key: #{key} (#{key.class})"
|
94
|
+
if key.is_a?(String)
|
95
|
+
if key.length == 1
|
96
|
+
# Assume letter
|
97
|
+
@vnc.keyevent(key.chr, true)
|
98
|
+
@vnc.keyevent(key.chr, false)
|
99
|
+
else
|
100
|
+
# Assume keysym
|
101
|
+
puts "I don't know how to type '#{key}'"
|
102
|
+
return { :action => "status", :status => "I don't know how to type '#{key}'" }
|
103
|
+
end
|
104
|
+
else
|
105
|
+
# type printables, key others.
|
106
|
+
if 32.upto(127).include?(key)
|
107
|
+
@vnc.keyevent(key, true)
|
108
|
+
@vnc.keyevent(key, false)
|
109
|
+
else
|
110
|
+
case key
|
111
|
+
when 8
|
112
|
+
@vnc.keyevent(0xff08, true)
|
113
|
+
@vnc.keyevent(0xff08, false)
|
114
|
+
when 13
|
115
|
+
@vnc.keyevent(0xff0D, true)
|
116
|
+
@vnc.keyevent(0xff0D, false)
|
117
|
+
else
|
118
|
+
puts "I don't know how to type web keycode '#{key}'"
|
119
|
+
return { :action => "status", :status => "I don't know how to type '#{key}'" }
|
120
|
+
end # case key
|
121
|
+
end # if 32.upto(127).include?(key)
|
122
|
+
end # if key.is_a?String
|
123
|
+
return nil
|
124
|
+
end # def keypress
|
82
125
|
|
83
126
|
class VNCClient < EventMachine::Connection
|
84
127
|
include EventMachine::Protocols::VNC::Client
|
@@ -93,7 +136,7 @@ class FingerPoken::Target::VNC < FingerPoken::Target
|
|
93
136
|
@target.buttonmask = 0
|
94
137
|
@target.x = (@screen_width / 2).to_i
|
95
138
|
@target.y = (@screen_height / 2).to_i
|
96
|
-
@target.
|
139
|
+
@target.ready()
|
97
140
|
end
|
98
141
|
end # class VNCClient
|
99
142
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
if(typeof Crypto=="undefined"||!Crypto.util)(function(){var n=window.Crypto={},o=n.util={rotl:function(g,i){return g<<i|g>>>32-i},rotr:function(g,i){return g<<32-i|g>>>i},endian:function(g){if(g.constructor==Number)return o.rotl(g,8)&16711935|o.rotl(g,24)&4278255360;for(var i=0;i<g.length;i++)g[i]=o.endian(g[i]);return g},randomBytes:function(g){for(var i=[];g>0;g--)i.push(Math.floor(Math.random()*256));return i},bytesToWords:function(g){for(var i=[],h=0,a=0;h<g.length;h++,a+=8)i[a>>>5]|=g[h]<<24-
|
2
|
+
a%32;return i},wordsToBytes:function(g){for(var i=[],h=0;h<g.length*32;h+=8)i.push(g[h>>>5]>>>24-h%32&255);return i},bytesToHex:function(g){for(var i=[],h=0;h<g.length;h++){i.push((g[h]>>>4).toString(16));i.push((g[h]&15).toString(16))}return i.join("")},hexToBytes:function(g){for(var i=[],h=0;h<g.length;h+=2)i.push(parseInt(g.substr(h,2),16));return i},bytesToBase64:function(g){if(typeof btoa=="function")return btoa(p.bytesToString(g));for(var i=[],h=0;h<g.length;h+=3)for(var a=g[h]<<16|g[h+1]<<
|
3
|
+
8|g[h+2],b=0;b<4;b++)h*8+b*6<=g.length*8?i.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(a>>>6*(3-b)&63)):i.push("=");return i.join("")},base64ToBytes:function(g){if(typeof atob=="function")return p.stringToBytes(atob(g));g=g.replace(/[^A-Z0-9+\/]/ig,"");for(var i=[],h=0,a=0;h<g.length;a=++h%4)a!=0&&i.push(("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(g.charAt(h-1))&Math.pow(2,-2*a+8)-1)<<a*2|"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(g.charAt(h))>>>
|
4
|
+
6-a*2);return i}};n.mode={};n=n.charenc={};n.UTF8={stringToBytes:function(g){return p.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(p.bytesToString(g)))}};var p=n.Binary={stringToBytes:function(g){for(var i=[],h=0;h<g.length;h++)i.push(g.charCodeAt(h)&255);return i},bytesToString:function(g){for(var i=[],h=0;h<g.length;h++)i.push(String.fromCharCode(g[h]));return i.join("")}}})();
|
5
|
+
(function(){var n=Crypto,o=n.util,p=n.charenc,g=p.UTF8,i=p.Binary,h=n.MD5=function(a,b){var j=o.wordsToBytes(h._md5(a));return b&&b.asBytes?j:b&&b.asString?i.bytesToString(j):o.bytesToHex(j)};h._md5=function(a){if(a.constructor==String)a=g.stringToBytes(a);var b=o.bytesToWords(a),j=a.length*8;a=1732584193;for(var d=-271733879,e=-1732584194,c=271733878,f=0;f<b.length;f++)b[f]=(b[f]<<8|b[f]>>>24)&16711935|(b[f]<<24|b[f]>>>8)&4278255360;b[j>>>5]|=128<<j%32;b[(j+64>>>9<<4)+14]=j;j=h._ff;var k=h._gg,l=
|
6
|
+
h._hh,m=h._ii;for(f=0;f<b.length;f+=16){var q=a,r=d,s=e,t=c;a=j(a,d,e,c,b[f+0],7,-680876936);c=j(c,a,d,e,b[f+1],12,-389564586);e=j(e,c,a,d,b[f+2],17,606105819);d=j(d,e,c,a,b[f+3],22,-1044525330);a=j(a,d,e,c,b[f+4],7,-176418897);c=j(c,a,d,e,b[f+5],12,1200080426);e=j(e,c,a,d,b[f+6],17,-1473231341);d=j(d,e,c,a,b[f+7],22,-45705983);a=j(a,d,e,c,b[f+8],7,1770035416);c=j(c,a,d,e,b[f+9],12,-1958414417);e=j(e,c,a,d,b[f+10],17,-42063);d=j(d,e,c,a,b[f+11],22,-1990404162);a=j(a,d,e,c,b[f+12],7,1804603682);c=
|
7
|
+
j(c,a,d,e,b[f+13],12,-40341101);e=j(e,c,a,d,b[f+14],17,-1502002290);d=j(d,e,c,a,b[f+15],22,1236535329);a=k(a,d,e,c,b[f+1],5,-165796510);c=k(c,a,d,e,b[f+6],9,-1069501632);e=k(e,c,a,d,b[f+11],14,643717713);d=k(d,e,c,a,b[f+0],20,-373897302);a=k(a,d,e,c,b[f+5],5,-701558691);c=k(c,a,d,e,b[f+10],9,38016083);e=k(e,c,a,d,b[f+15],14,-660478335);d=k(d,e,c,a,b[f+4],20,-405537848);a=k(a,d,e,c,b[f+9],5,568446438);c=k(c,a,d,e,b[f+14],9,-1019803690);e=k(e,c,a,d,b[f+3],14,-187363961);d=k(d,e,c,a,b[f+8],20,1163531501);
|
8
|
+
a=k(a,d,e,c,b[f+13],5,-1444681467);c=k(c,a,d,e,b[f+2],9,-51403784);e=k(e,c,a,d,b[f+7],14,1735328473);d=k(d,e,c,a,b[f+12],20,-1926607734);a=l(a,d,e,c,b[f+5],4,-378558);c=l(c,a,d,e,b[f+8],11,-2022574463);e=l(e,c,a,d,b[f+11],16,1839030562);d=l(d,e,c,a,b[f+14],23,-35309556);a=l(a,d,e,c,b[f+1],4,-1530992060);c=l(c,a,d,e,b[f+4],11,1272893353);e=l(e,c,a,d,b[f+7],16,-155497632);d=l(d,e,c,a,b[f+10],23,-1094730640);a=l(a,d,e,c,b[f+13],4,681279174);c=l(c,a,d,e,b[f+0],11,-358537222);e=l(e,c,a,d,b[f+3],16,-722521979);
|
9
|
+
d=l(d,e,c,a,b[f+6],23,76029189);a=l(a,d,e,c,b[f+9],4,-640364487);c=l(c,a,d,e,b[f+12],11,-421815835);e=l(e,c,a,d,b[f+15],16,530742520);d=l(d,e,c,a,b[f+2],23,-995338651);a=m(a,d,e,c,b[f+0],6,-198630844);c=m(c,a,d,e,b[f+7],10,1126891415);e=m(e,c,a,d,b[f+14],15,-1416354905);d=m(d,e,c,a,b[f+5],21,-57434055);a=m(a,d,e,c,b[f+12],6,1700485571);c=m(c,a,d,e,b[f+3],10,-1894986606);e=m(e,c,a,d,b[f+10],15,-1051523);d=m(d,e,c,a,b[f+1],21,-2054922799);a=m(a,d,e,c,b[f+8],6,1873313359);c=m(c,a,d,e,b[f+15],10,-30611744);
|
10
|
+
e=m(e,c,a,d,b[f+6],15,-1560198380);d=m(d,e,c,a,b[f+13],21,1309151649);a=m(a,d,e,c,b[f+4],6,-145523070);c=m(c,a,d,e,b[f+11],10,-1120210379);e=m(e,c,a,d,b[f+2],15,718787259);d=m(d,e,c,a,b[f+9],21,-343485551);a=a+q>>>0;d=d+r>>>0;e=e+s>>>0;c=c+t>>>0}return o.endian([a,d,e,c])};h._ff=function(a,b,j,d,e,c,f){a=a+(b&j|~b&d)+(e>>>0)+f;return(a<<c|a>>>32-c)+b};h._gg=function(a,b,j,d,e,c,f){a=a+(b&d|j&~d)+(e>>>0)+f;return(a<<c|a>>>32-c)+b};h._hh=function(a,b,j,d,e,c,f){a=a+(b^j^d)+(e>>>0)+f;return(a<<c|a>>>
|
11
|
+
32-c)+b};h._ii=function(a,b,j,d,e,c,f){a=a+(j^(b|~d))+(e>>>0)+f;return(a<<c|a>>>32-c)+b};h._blocksize=16;h._digestsize=16})();
|
@@ -0,0 +1 @@
|
|
1
|
+
(function(){var f=Crypto,j=f.util,g=f.charenc,h=g.UTF8,k=g.Binary;f.HMAC=function(b,c,a,d){if(c.constructor==String)c=h.stringToBytes(c);if(a.constructor==String)a=h.stringToBytes(a);if(a.length>b._blocksize*4)a=b(a,{asBytes:true});var i=a.slice(0);a=a.slice(0);for(var e=0;e<b._blocksize*4;e++){i[e]^=92;a[e]^=54}b=b(i.concat(b(a.concat(c),{asBytes:true})),{asBytes:true});return d&&d.asBytes?b:d&&d.asString?k.bytesToString(b):j.bytesToHex(b)}})();
|
data/public/js/fingerpoken.js
CHANGED
@@ -1,10 +1,95 @@
|
|
1
1
|
(function() {
|
2
2
|
/* TODO(sissel): This could use some serious refactoring. */
|
3
3
|
|
4
|
+
/* From http://alastairc.ac/2010/03/detecting-touch-based-browsing/ */
|
5
|
+
function isTouchDevice() {
|
6
|
+
var el = document.createElement('div');
|
7
|
+
el.setAttribute('ongesturestart', 'return;');
|
8
|
+
if (typeof el.ongesturestart == "function"){
|
9
|
+
return true;
|
10
|
+
} else {
|
11
|
+
return false;
|
12
|
+
}
|
13
|
+
};
|
14
|
+
var TOUCH_SUPPORTED = isTouchDevice;
|
15
|
+
|
16
|
+
var Fingerpoken = function() {
|
17
|
+
this.x = -1;
|
18
|
+
this.y = -1;
|
19
|
+
this.moving = false;
|
20
|
+
this.dragging = false;
|
21
|
+
this.width = window.innerWidth;
|
22
|
+
this.height = window.innerHeight;
|
23
|
+
this.key = undefined; /* TODO(sissel) = unused? */
|
24
|
+
this.keyboard = false;
|
25
|
+
this.touchpad_active = false;
|
26
|
+
this.mouse = { };
|
27
|
+
this.scroll = {
|
28
|
+
y: 0
|
29
|
+
};
|
30
|
+
this.sequence = Math.floor(Math.random() * 10000);
|
31
|
+
};
|
32
|
+
|
33
|
+
Fingerpoken.prototype.send = function(obj) {
|
34
|
+
this.sign(obj);
|
35
|
+
this.websocket.send(JSON.stringify(obj))
|
36
|
+
};
|
37
|
+
|
38
|
+
Fingerpoken.prototype.sign = function(obj) {
|
39
|
+
var passphrase = this.config("fingerpoken/passphrase");
|
40
|
+
obj.sequence = this.sequence;
|
41
|
+
this.sequence += 1;
|
42
|
+
|
43
|
+
console.log("Passphrase: " + passphrase);
|
44
|
+
console.log("Sequence: " + this.sequence);
|
45
|
+
obj.signature = Crypto.HMAC(Crypto.MD5, "" + obj.sequence, passphrase, { asBytes: true });
|
46
|
+
};
|
47
|
+
|
48
|
+
Fingerpoken.prototype.connect = function() {
|
49
|
+
if (this.status === undefined) {
|
50
|
+
this.status = $("#status");
|
51
|
+
}
|
52
|
+
this.status.html("connecting...");
|
53
|
+
var websocket = new WebSocket("ws://" + document.location.hostname + ":5001");
|
54
|
+
var self = this;
|
55
|
+
websocket.onopen = function(event) {
|
56
|
+
self.status.html("websocket ready");
|
57
|
+
}
|
58
|
+
|
59
|
+
websocket.onclose = function(event) {
|
60
|
+
self.status.html("Closed, trying to reopen.");
|
61
|
+
setTimeout(function() {
|
62
|
+
self.connect();
|
63
|
+
}, 1000);
|
64
|
+
}
|
65
|
+
|
66
|
+
websocket.onmessage = function(event) {
|
67
|
+
var request = JSON.parse(event.data);
|
68
|
+
fingerpoken.message_callback(request)
|
69
|
+
}
|
70
|
+
|
71
|
+
this.websocket = websocket;
|
72
|
+
};
|
73
|
+
|
74
|
+
Fingerpoken.prototype.config = function(key, value, default_value) {
|
75
|
+
if (value) {
|
76
|
+
this.status.html("config[" + key + "] = " + value);
|
77
|
+
//alert(key + " => " + value);
|
78
|
+
|
79
|
+
window.localStorage[key] = value
|
80
|
+
return value
|
81
|
+
} else {
|
82
|
+
return window.localStorage[key] || default_value
|
83
|
+
}
|
84
|
+
};
|
85
|
+
|
4
86
|
$(document).ready(function() {
|
5
87
|
var status = $("#status");
|
6
88
|
var keyboard = $('#keyboard');
|
7
89
|
var keyboard_button = keyboard.prev('a');
|
90
|
+
var fingerpoken = new Fingerpoken();
|
91
|
+
fingerpoken.status = status;
|
92
|
+
|
8
93
|
keyboard.width(keyboard_button.width());
|
9
94
|
keyboard.height(keyboard_button.height());
|
10
95
|
/* TODO(sissel): get the computed width (margin, padding, width) */
|
@@ -14,12 +99,12 @@
|
|
14
99
|
keyboard.bind("focus", function() {
|
15
100
|
/* move the textarea away so we don't see the caret */
|
16
101
|
keyboard.css('margin-left', '-10000px');
|
17
|
-
|
102
|
+
fingerpoken.keyboard = true;
|
18
103
|
$(window).triggerHandler("resize");
|
19
104
|
});
|
20
105
|
keyboard.bind("blur", function(){
|
21
106
|
keyboard.css('margin-left', '-' + keyboard_button.width() + 'px');
|
22
|
-
|
107
|
+
fingerpoken.keyboard = false;
|
23
108
|
$(window).triggerHandler("resize");
|
24
109
|
});
|
25
110
|
keyboard.bind("keypress", function(event) {
|
@@ -28,14 +113,14 @@
|
|
28
113
|
if (!key) {
|
29
114
|
key = (e.keyCode ? e.keyCode : e.which);
|
30
115
|
}
|
31
|
-
|
116
|
+
fingerpoken.send(JSON.stringify({
|
32
117
|
action: "log",
|
33
118
|
shift: e.shiftKey,
|
34
119
|
char: e.charCode,
|
35
120
|
ctrl: e.ctrlKey,
|
36
121
|
meta: e.ctrlKey,
|
37
122
|
}));
|
38
|
-
|
123
|
+
fingerpoken.send(JSON.stringify({
|
39
124
|
action: "keypress",
|
40
125
|
key: key,
|
41
126
|
shift: e.shiftKey,
|
@@ -52,7 +137,7 @@
|
|
52
137
|
return;
|
53
138
|
}
|
54
139
|
|
55
|
-
|
140
|
+
fingerpoken.send(JSON.stringify({
|
56
141
|
action: "type",
|
57
142
|
string: keyboard.val(),
|
58
143
|
}));
|
@@ -63,7 +148,7 @@
|
|
63
148
|
|
64
149
|
keyboard.bind("keyup", function(event) {
|
65
150
|
var e = event.originalEvent;
|
66
|
-
|
151
|
+
fingerpoken.send(JSON.stringify({
|
67
152
|
action: "log",
|
68
153
|
shift: e.shiftKey,
|
69
154
|
char: e.charCode,
|
@@ -78,7 +163,7 @@
|
|
78
163
|
return;
|
79
164
|
}
|
80
165
|
|
81
|
-
|
166
|
+
fingerpoken.send(JSON.stringify({
|
82
167
|
action: "keypress",
|
83
168
|
key: key,
|
84
169
|
shift: e.shiftKey,
|
@@ -87,35 +172,7 @@
|
|
87
172
|
event.preventDefault();
|
88
173
|
});
|
89
174
|
|
90
|
-
|
91
|
-
if (value) {
|
92
|
-
status.html("config[" + key + "] = " + value);
|
93
|
-
//alert(key + " => " + value);
|
94
|
-
|
95
|
-
window.localStorage[key] = value
|
96
|
-
return value
|
97
|
-
} else {
|
98
|
-
return window.localStorage[key] || default_value
|
99
|
-
}
|
100
|
-
};
|
101
|
-
|
102
|
-
var state = {
|
103
|
-
x: -1,
|
104
|
-
y: -1,
|
105
|
-
moving: false,
|
106
|
-
dragging: false,
|
107
|
-
width: window.innerWidth,
|
108
|
-
height: window.innerHeight,
|
109
|
-
key: undefined, /* TODO(sissel): unused? */
|
110
|
-
keyboard: false,
|
111
|
-
touchpad_active: false,
|
112
|
-
mouse: { },
|
113
|
-
scroll: {
|
114
|
-
y: 0,
|
115
|
-
}
|
116
|
-
};
|
117
|
-
|
118
|
-
state.message_callback = function(request) {
|
175
|
+
fingerpoken.message_callback = function(request) {
|
119
176
|
action = request["action"];
|
120
177
|
switch (action) {
|
121
178
|
case "status":
|
@@ -131,17 +188,22 @@
|
|
131
188
|
/* Sync configuration elements */
|
132
189
|
|
133
190
|
/* Mouse movement */
|
134
|
-
console.log(config("fingerpoken/mouse/movement"));
|
191
|
+
console.log(fingerpoken.config("fingerpoken/mouse/movement"));
|
135
192
|
$("input[name = \"mouse-config\"]")
|
136
193
|
.bind("change", function(event) {
|
137
|
-
config("fingerpoken/mouse/movement", event.target.value);
|
138
|
-
}).filter("[value = \"" + config("fingerpoken/mouse/movement") + "\"]")
|
194
|
+
fingerpoken.config("fingerpoken/mouse/movement", event.target.value);
|
195
|
+
}).filter("[value = \"" + fingerpoken.config("fingerpoken/mouse/movement") + "\"]")
|
139
196
|
.attr("checked", "checked").click()
|
140
197
|
|
141
198
|
$("input[name = \"mouse-acceleration\"]")
|
142
199
|
.bind("change", function(event) {
|
143
|
-
config("fingerpoken/mouse/acceleration", parseInt(event.target.value));
|
144
|
-
}).val(config("fingerpoken/mouse/acceleration")).change();
|
200
|
+
fingerpoken.config("fingerpoken/mouse/acceleration", parseInt(event.target.value));
|
201
|
+
}).val(fingerpoken.config("fingerpoken/mouse/acceleration")).change();
|
202
|
+
|
203
|
+
$("input[name = \"fingerpoken-passphrase\"]")
|
204
|
+
.bind("change", function(event) {
|
205
|
+
fingerpoken.config("fingerpoken/passphrase", event.target.value);
|
206
|
+
}).val(fingerpoken.config("fingerpoken/passphrase")).change();
|
145
207
|
|
146
208
|
/* Changing orientation sometimes leaves the viewport
|
147
209
|
* not starting at 0,0. Fix it with this hack.
|
@@ -162,7 +224,7 @@
|
|
162
224
|
/* TODO(sissel): Make this special handling only for iphones.
|
163
225
|
* http://developer.apple.com/library/safari/#documentation/appleapplications/reference/safariwebcontent/UsingtheViewport/UsingtheViewport.html
|
164
226
|
*/
|
165
|
-
if (
|
227
|
+
if (fingerpoken.keyboard) {
|
166
228
|
if (window.orientation == 90 || window.orientation == -90) {
|
167
229
|
content_height -= 162; /* landscape orientation keyboard */
|
168
230
|
content_height -= 32; /* "form assistant" aka FormFill, this height is undocumented. */
|
@@ -171,57 +233,36 @@
|
|
171
233
|
content_height -= 44; /* "form assistant" aka FormFill */
|
172
234
|
}
|
173
235
|
}
|
174
|
-
status.html("Resize / " + window.orientation + " / " +
|
236
|
+
status.html("Resize / " + window.orientation + " / " + fingerpoken.keyboard + " / " + content_height);
|
175
237
|
content.height(content_height);
|
176
238
|
});
|
177
239
|
|
178
|
-
var connect = function(state) {
|
179
|
-
status.html("connecting...");
|
180
|
-
var websocket = new WebSocket("ws://" + document.location.hostname + ":5001");
|
181
|
-
websocket.onopen = function(event) {
|
182
|
-
status.html("websocket ready");
|
183
|
-
}
|
184
240
|
|
185
|
-
|
186
|
-
status.html("Closed, trying to reopen.");
|
187
|
-
setTimeout(function() {
|
188
|
-
connect(state);
|
189
|
-
}, 1000);
|
190
|
-
}
|
191
|
-
|
192
|
-
websocket.onmessage = function(event) {
|
193
|
-
var request = JSON.parse(event.data);
|
194
|
-
state.message_callback(request)
|
195
|
-
}
|
196
|
-
|
197
|
-
state.websocket = websocket;
|
198
|
-
}
|
199
|
-
|
200
|
-
connect(state);
|
241
|
+
fingerpoken.connect();
|
201
242
|
|
202
243
|
/* This will track orientation/motion changes with the accelerometer and
|
203
244
|
* gyroscope. Not sure how useful this would be... */
|
204
245
|
//$(window).bind("devicemotion", function(event) {
|
205
246
|
//var e = event.originalEvent;
|
206
|
-
//
|
247
|
+
//fingerpoken.accel = e.accelerationIncludingGravity;
|
207
248
|
|
208
249
|
/* Trim shakes */
|
209
|
-
//if (Math.abs(
|
250
|
+
//if (Math.abs(fingerpoken.accel.x) < 0.22 && Math.abs(fingerpoken.accel.y) < 0.22) {
|
210
251
|
//return;
|
211
252
|
//}
|
212
|
-
//status.html("Motion: \nx: " +
|
213
|
-
//
|
253
|
+
//status.html("Motion: \nx: " + fingerpoken.accel.x + "\ny: " + fingerpoken.accel.y + "\nz: " + fingerpoken.accel.z);
|
254
|
+
//fingerpoken.send({
|
214
255
|
//action: "move",
|
215
|
-
//rel_x: Math.ceil(
|
216
|
-
//rel_y: Math.ceil(
|
217
|
-
//})
|
256
|
+
//rel_x: Math.ceil(fingerpoken.accel.x) * -1,
|
257
|
+
//rel_y: Math.ceil(fingerpoken.accel.y) * -1,
|
258
|
+
//});
|
218
259
|
//});
|
219
260
|
|
220
261
|
|
221
262
|
/* TODO(sissel): add mousedown/mousemove/mouseup support */
|
222
263
|
$("#area").bind("touchstart mousedown", function(event) {
|
223
264
|
var e = event.originalEvent;
|
224
|
-
|
265
|
+
fingerpoken.touchpad_active = true;
|
225
266
|
/* if no 'touches', use the event itself, one finger/mouse */
|
226
267
|
var touches = e.touches || [ e ];
|
227
268
|
var output = "Start: " + touches[0].clientX + "," + touches[0].clientY + "\n";
|
@@ -229,84 +270,85 @@
|
|
229
270
|
status.html(output);
|
230
271
|
|
231
272
|
/* number of fingers == mouse button */
|
232
|
-
|
233
|
-
switch (
|
234
|
-
case 1:
|
235
|
-
case 2:
|
236
|
-
case 3:
|
273
|
+
fingerpoken.fingers = touches.length;
|
274
|
+
switch (fingerpoken.fingers) {
|
275
|
+
case 1: fingerpoken.button = 1; break;
|
276
|
+
case 2: fingerpoken.button = 3; break;
|
277
|
+
case 3: fingerpoken.button = 2; break;
|
237
278
|
}
|
238
279
|
|
239
280
|
var now = (new Date()).getTime();
|
240
|
-
if ((now -
|
281
|
+
if ((now - fingerpoken.last_click) < 170) {
|
241
282
|
/* Start dragging */
|
242
|
-
|
283
|
+
fingerpoken.send({
|
243
284
|
action: "mousedown",
|
244
|
-
button:
|
245
|
-
})
|
246
|
-
|
285
|
+
button: fingerpoken.button,
|
286
|
+
})
|
287
|
+
fingerpoken.dragging = true;
|
247
288
|
}
|
248
289
|
event.preventDefault();
|
249
290
|
}).bind("touchend mouseup", function(event) { /* $("#touchpadsurface").bind("touchend" ... */
|
250
291
|
var e = event.originalEvent;
|
251
292
|
var touches = e.touches || [ e ];
|
252
293
|
|
253
|
-
if (
|
254
|
-
clearInterval(
|
255
|
-
|
294
|
+
if (fingerpoken.mouse.vectorTimer) {
|
295
|
+
clearInterval(fingerpoken.mouse.vectorTimer);
|
296
|
+
fingerpoken.mouse.vectorTimer = null;
|
256
297
|
}
|
257
298
|
|
258
|
-
if (
|
259
|
-
|
299
|
+
if (fingerpoken.dragging) {
|
300
|
+
fingerpoken.send({
|
260
301
|
action: "mouseup",
|
261
|
-
button:
|
262
|
-
})
|
263
|
-
|
302
|
+
button: fingerpoken.button,
|
303
|
+
});
|
304
|
+
fingerpoken.dragging = false;
|
264
305
|
} else {
|
265
|
-
if (
|
266
|
-
var e =
|
306
|
+
if (fingerpoken.moving && !fingerpoken.scrolling) {
|
307
|
+
var e = fingerpoken.last_move;
|
267
308
|
var r = e.rotation;
|
268
309
|
if (r < 0) {
|
269
310
|
r += 360;
|
270
311
|
}
|
271
312
|
|
272
313
|
status.html(r);
|
273
|
-
} else if (
|
314
|
+
} else if (fingerpoken.scrolling) {
|
274
315
|
/* nothing for now */
|
275
316
|
} else {
|
276
317
|
/* No movement, click! */
|
277
318
|
status.html("Click!");
|
278
|
-
|
319
|
+
console.log("click");
|
320
|
+
fingerpoken.send({
|
279
321
|
action: "click",
|
280
|
-
button:
|
281
|
-
})
|
282
|
-
|
322
|
+
button: fingerpoken.button,
|
323
|
+
});
|
324
|
+
fingerpoken.last_click = (new Date()).getTime();
|
283
325
|
}
|
284
326
|
}
|
285
327
|
if (touches.length == 0 || !e.touches) {
|
286
|
-
|
287
|
-
|
288
|
-
|
328
|
+
fingerpoken.moving = false;
|
329
|
+
fingerpoken.scrolling = false;
|
330
|
+
fingerpoken.touchpad_active = false;
|
289
331
|
}
|
290
332
|
event.preventDefault();
|
291
333
|
}).bind("touchmove mousemove", function(event) { /* $("#touchpadsurface").bind("touchmove" ... */
|
292
334
|
var e = event.originalEvent;
|
293
335
|
var touches = e.touches || [ e ];
|
294
336
|
|
295
|
-
//if (!
|
337
|
+
//if (!fingerpoken.touchpad_active) {
|
296
338
|
//event.preventDefault();
|
297
339
|
//return;
|
298
340
|
//}
|
299
341
|
|
300
|
-
if (!
|
342
|
+
if (!fingerpoken.moving) {
|
301
343
|
/* Start calculating delta offsets now */
|
302
|
-
|
303
|
-
|
304
|
-
|
344
|
+
fingerpoken.moving = true;
|
345
|
+
fingerpoken.start_x = fingerpoken.x = touches[0].clientX;
|
346
|
+
fingerpoken.start_y = fingerpoken.y = touches[0].clientY;
|
305
347
|
/* Skip this event */
|
306
348
|
return;
|
307
349
|
}
|
308
350
|
|
309
|
-
|
351
|
+
fingerpoken.last_move = e;
|
310
352
|
|
311
353
|
var output = "";
|
312
354
|
for (var i in touches) {
|
@@ -322,8 +364,8 @@
|
|
322
364
|
|
323
365
|
var x = touches[0].clientX;
|
324
366
|
var y = touches[0].clientY;
|
325
|
-
var delta_x = (x -
|
326
|
-
var delta_y = (y -
|
367
|
+
var delta_x = (x - fingerpoken.x);
|
368
|
+
var delta_y = (y - fingerpoken.y);
|
327
369
|
|
328
370
|
/* Apply acceleration */
|
329
371
|
var sign_x = (delta_x < 0 ? -1 : 1);
|
@@ -331,15 +373,15 @@
|
|
331
373
|
|
332
374
|
/* jQuery Mobile or HTML 'range' inputs don't support floating point.
|
333
375
|
* Hack around it by using larger numbers and compensating. */
|
334
|
-
var accel = config("fingerpoken/mouse/acceleration", null, 150) / 100.0;
|
376
|
+
var accel = fingerpoken.config("fingerpoken/mouse/acceleration", null, 150) / 100.0;
|
335
377
|
output += "Accel: " + accel + "\n";
|
336
378
|
|
337
379
|
var delta_x = Math.ceil(Math.pow(Math.abs(delta_x), accel) * sign_x);
|
338
380
|
var delta_y = Math.ceil(Math.pow(Math.abs(delta_y), accel) * sign_y);
|
339
381
|
output += "Delta: " + delta_x + ", " + delta_y + "\n";
|
340
382
|
|
341
|
-
|
342
|
-
|
383
|
+
fingerpoken.x = x;
|
384
|
+
fingerpoken.y = y;
|
343
385
|
|
344
386
|
/* TODO(sissel): Make this a config option */
|
345
387
|
if (e.rotation < -10 || e.rotation > 10) {
|
@@ -352,48 +394,49 @@
|
|
352
394
|
return;
|
353
395
|
}
|
354
396
|
|
355
|
-
if (touches.length > 1 && !
|
397
|
+
if (touches.length > 1 && !fingerpoken.dragging) {
|
356
398
|
/* Multifinger movement, probably should scroll? */
|
357
399
|
if (Math.abs(delta_y) > 0) {
|
358
400
|
/* Scroll */
|
359
|
-
|
401
|
+
fingerpoken.scroll.y += delta_y;
|
360
402
|
|
361
403
|
/* Don't scroll every time we move, wait until we move enough
|
362
404
|
* that it is more than 10 pixels. */
|
363
405
|
/* TODO(sissel): Make this a config option */
|
364
|
-
if (Math.abs(
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
406
|
+
if (Math.abs(fingerpoken.scroll.y) > 10) {
|
407
|
+
fingerpoken.scrolling = true;
|
408
|
+
fingerpoken.moving = false;
|
409
|
+
/* TODO(sissel): Support horizontal scrolling (buttons 6 and 7) */
|
410
|
+
fingerpoken.scroll.y = 0;
|
411
|
+
fingerpoken.send({
|
369
412
|
action: "click",
|
370
413
|
button: (delta_y < 0) ? 4 : 5,
|
371
|
-
})
|
414
|
+
});
|
372
415
|
}
|
373
416
|
} /* if (Math.abs(delta_y) > 0) */
|
374
417
|
} else {
|
375
418
|
/* Only 1 finger, and we aren't dragging. So let's move! */
|
376
419
|
/* TODO(sissel): Refactor these in to fumctions */
|
377
|
-
var movement = config("fingerpoken/mouse/movement");
|
420
|
+
var movement = fingerpoken.config("fingerpoken/mouse/movement");
|
378
421
|
if (movement == "relative") {
|
379
|
-
|
422
|
+
fingerpoken.send({
|
380
423
|
action: "mousemove_relative",
|
381
424
|
rel_x: delta_x,
|
382
425
|
rel_y: delta_y
|
383
|
-
})
|
426
|
+
});
|
384
427
|
} else if (movement == "absolute") {
|
385
428
|
/* Send absolute in terms of percentages. */
|
386
429
|
var content = $(".content:visible");
|
387
|
-
|
430
|
+
fingerpoken.send({
|
388
431
|
action: "mousemove_absolute",
|
389
432
|
percent_x: x / content.innerWidth(),
|
390
433
|
percent_y: y / content.innerHeight(),
|
391
|
-
})
|
434
|
+
});
|
392
435
|
} else if (movement == "vector") {
|
393
|
-
if (!
|
394
|
-
|
395
|
-
var rx =
|
396
|
-
var ry =
|
436
|
+
if (!fingerpoken.mouse.vectorTimer) {
|
437
|
+
fingerpoken.mouse.vectorTimer = setInterval(function() {
|
438
|
+
var rx = fingerpoken.x - fingerpoken.start_x;
|
439
|
+
var ry = fingerpoken.y - fingerpoken.start_y;
|
397
440
|
if (rx == 0 || ry == 0) {
|
398
441
|
return;
|
399
442
|
}
|
@@ -407,13 +450,13 @@
|
|
407
450
|
ry = Math.ceil(Math.pow(Math.abs(ry), vector_accel) * sign_ry);
|
408
451
|
output += "rx2,ry2 = " + rx + ", " + ry + "\n";
|
409
452
|
|
410
|
-
|
453
|
+
fingerpoken.send({
|
411
454
|
action: "mousemove_relative",
|
412
455
|
rel_x: rx,
|
413
456
|
rel_y: ry
|
414
|
-
})
|
457
|
+
});
|
415
458
|
}, 15);
|
416
|
-
} /* if (!
|
459
|
+
} /* if (!fingerpoken.mouse.vectorTimer) */
|
417
460
|
} /* mouse vector movement */
|
418
461
|
status.html(output)
|
419
462
|
} /* finger movement */
|
@@ -428,22 +471,38 @@
|
|
428
471
|
* Mouse click
|
429
472
|
* <a class="command" data-action="click" data-button="button to click">
|
430
473
|
*/
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
}).bind("touchmove mousemove", function(event) {
|
474
|
+
|
475
|
+
var cmd = $("a.command")
|
476
|
+
cmd.bind("touchstart", function(event) {
|
477
|
+
fingerpoken.touchelement = this;
|
478
|
+
}).bind("touchmove", function(event) {
|
437
479
|
event.preventDefault();
|
438
|
-
}).bind("touchend
|
439
|
-
if (
|
440
|
-
|
480
|
+
}).bind("touchend", function(event) {
|
481
|
+
if (fingerpoken.touchelement == this) {
|
482
|
+
fingerpoken.send({
|
441
483
|
action: $(this).attr("data-action"),
|
442
484
|
key: $(this).attr("data-key"),
|
443
485
|
button: parseInt($(this).attr("data-button")),
|
444
|
-
})
|
486
|
+
});
|
445
487
|
}
|
446
488
|
});
|
447
489
|
|
490
|
+
if (!TOUCH_SUPPORTED) {
|
491
|
+
cmd.bind("mousedown", function(event) {
|
492
|
+
fingerpoken.touchelement = this;
|
493
|
+
event.preventDefault();
|
494
|
+
}).bind("mousemove", function(event) {
|
495
|
+
event.preventDefault();
|
496
|
+
}).bind("touchend", function(event) {
|
497
|
+
if (fingerpoken.touchelement == this) {
|
498
|
+
fingerpoken.send({
|
499
|
+
action: $(this).attr("data-action"),
|
500
|
+
key: $(this).attr("data-key"),
|
501
|
+
button: parseInt($(this).attr("data-button")),
|
502
|
+
});
|
503
|
+
}
|
504
|
+
});
|
505
|
+
} /* if !TOUCH_SUPPORTED */
|
506
|
+
|
448
507
|
}); /* $(document).ready */
|
449
508
|
})();
|
data/views/index.haml
CHANGED
@@ -15,6 +15,8 @@
|
|
15
15
|
%script{ :src => "/js/jquery.mobile-1.0a2.min.js",
|
16
16
|
:type => "text/javascript" }
|
17
17
|
|
18
|
+
%script{ :src => "/js/2.2.0-crypto-md5.js", :type => "text/javascript" }
|
19
|
+
%script{ :src => "/js/2.2.0-hmac-min.js", :type => "text/javascript" }
|
18
20
|
%body
|
19
21
|
/ touchpad
|
20
22
|
%div{"data-role" => "page", "data-theme" => "a", "id" => "touchpad"}
|
@@ -71,5 +73,9 @@
|
|
71
73
|
/ Mouse acceleration
|
72
74
|
%label{:for => "mouse-acceleration"} Acceleration
|
73
75
|
%input{:type => "range", :name => "mouse-acceleration", :id => "mouse-acceleration", :min => 1, :max => 300, :step => "any"}
|
76
|
+
/ Crypto passphrase
|
77
|
+
%br
|
78
|
+
%label{:for => "fingerpoken-passphrase"} Passphrase
|
79
|
+
%input{:type => "text", :name => "fingerpoken-passphrase", :id => "fingerpoken-passphrase"}
|
74
80
|
|
75
81
|
%script{ :src => "/js/fingerpoken.js" }
|
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: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jordan Sissel
|
@@ -15,11 +15,11 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-04-20 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
name:
|
22
|
+
name: ffi
|
23
23
|
prerelease: false
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
type: :runtime
|
34
34
|
version_requirements: *id001
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
|
-
name:
|
36
|
+
name: ruby-hmac
|
37
37
|
prerelease: false
|
38
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
@@ -60,6 +60,62 @@ dependencies:
|
|
60
60
|
version: "0"
|
61
61
|
type: :runtime
|
62
62
|
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: em-websocket
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :runtime
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: async_sinatra
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
type: :runtime
|
90
|
+
version_requirements: *id005
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: haml
|
93
|
+
prerelease: false
|
94
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
type: :runtime
|
104
|
+
version_requirements: *id006
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: eventmachine-vnc
|
107
|
+
prerelease: false
|
108
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
type: :runtime
|
118
|
+
version_requirements: *id007
|
63
119
|
description: fingerpoken - turns your ipad/itouch/iphone into a remote touchpad, keyboard, etc
|
64
120
|
email: jls@semicomplete.com
|
65
121
|
executables:
|
@@ -69,25 +125,28 @@ extensions: []
|
|
69
125
|
extra_rdoc_files: []
|
70
126
|
|
71
127
|
files:
|
72
|
-
- lib/fingerpoken/xdo.rb
|
73
128
|
- lib/fingerpoken/target.rb
|
74
|
-
- lib/fingerpoken/tivo.rb
|
75
129
|
- lib/fingerpoken/vnc.rb
|
76
|
-
-
|
130
|
+
- lib/fingerpoken/tivo.rb
|
131
|
+
- lib/fingerpoken/xdo.rb
|
132
|
+
- public/js/jquery.mobile-1.0a2.min.js
|
133
|
+
- public/js/CRYPTO_JS_LICENSE
|
77
134
|
- public/js/fingerpoken.js
|
78
135
|
- public/js/jquery.min.js
|
79
|
-
- public/js/
|
80
|
-
- public/
|
136
|
+
- public/js/2.2.0-hmac-min.js
|
137
|
+
- public/js/2.2.0-crypto-md5.js
|
138
|
+
- public/jquery.mobile-1.0a2.min.css
|
139
|
+
- public/reset.css
|
81
140
|
- public/images/form-check-on.png
|
82
|
-
- public/images/
|
141
|
+
- public/images/form-radio-off.png
|
83
142
|
- public/images/icons-36-black.png
|
84
|
-
- public/images/icon-search-black.png
|
85
143
|
- public/images/icons-18-white.png
|
144
|
+
- public/images/form-radio-on.png
|
86
145
|
- public/images/ajax-loader.png
|
87
|
-
- public/images/
|
88
|
-
- public/images/icons-
|
146
|
+
- public/images/icon-search-black.png
|
147
|
+
- public/images/icons-36-white.png
|
89
148
|
- public/images/form-check-off.png
|
90
|
-
- public/
|
149
|
+
- public/images/icons-18-black.png
|
91
150
|
- views/style.sass
|
92
151
|
- views/index.haml
|
93
152
|
- bin/fingerpoken.rb
|