fingerpoken 0.2.20110101195735 → 0.2.20110102034531

Sign up to get free protection for your applications and to get access to all the features.
@@ -15,6 +15,8 @@ class FingerPoken::Target
15
15
  response = case request["action"]
16
16
  when "mousemove_relative"
17
17
  mousemove_relative(request["rel_x"], request["rel_y"])
18
+ when "mousemove_absolute"
19
+ mousemove_absolute(request["percent_x"], request["percent_y"])
18
20
  when "move_end"
19
21
  move_end()
20
22
  when "click"
@@ -5,6 +5,8 @@ require "fingerpoken/target"
5
5
  class FingerPoken::Target::VNC < FingerPoken::Target
6
6
  attr_accessor :x
7
7
  attr_accessor :y
8
+ attr_accessor :screen_x
9
+ attr_accessor :screen_y
8
10
  attr_accessor :buttonmask
9
11
 
10
12
  def initialize(config)
@@ -48,6 +50,16 @@ class FingerPoken::Target::VNC < FingerPoken::Target
48
50
  return nil
49
51
  end
50
52
 
53
+ def mousemove_absolute(px, py)
54
+ # Edges may be hard to hit on some devices, so inflate things a bit.
55
+ xbuf = @screen_x * 0.1
56
+ ybuf = @screen_y * 0.1
57
+ @x = (((@screen_x + xbuf) * px) - (xbuf / 2)).to_i
58
+ @y = (((@screen_y + ybuf) * py) - (ybuf / 2)).to_i
59
+ update
60
+ return nil
61
+ end
62
+
51
63
  def mousedown(button)
52
64
  button = (1 << (button.to_i - 1))
53
65
  return if @buttonmask & button != 0
@@ -72,10 +84,12 @@ class FingerPoken::Target::VNC < FingerPoken::Target
72
84
  end
73
85
 
74
86
  def ready
75
- @target.register
87
+ @target.screen_x = @screen_width
88
+ @target.screen_y = @screen_height
89
+ @target.buttonmask = 0
76
90
  @target.x = (@screen_width / 2).to_i
77
91
  @target.y = (@screen_height / 2).to_i
78
- @target.buttonmask = 0
92
+ @target.register
79
93
  end
80
94
  end # class VNCClient
81
95
  end
@@ -17,20 +17,64 @@ class FingerPoken::Target::Xdo < FingerPoken::Target
17
17
  attach_function :xdo_mouseup, [:pointer, :long, :int], :int
18
18
  attach_function :xdo_type, [:pointer, :long, :string, :long], :int
19
19
  attach_function :xdo_keysequence, [:pointer, :long, :string, :long], :int
20
+ attach_function :xdo_get_window_size, [:pointer, :long, :pointer, :pointer], :int
21
+ attach_function :xdo_window_search, [:pointer, :pointer, :pointer, :pointer], :int
20
22
  end
21
23
 
24
+ class XdoSearch < FFI::Struct
25
+ layout :title, :pointer,
26
+ :winclass, :pointer,
27
+ :winclassname, :pointer,
28
+ :winname, :pointer,
29
+ :pid, :int,
30
+ :max_depth, :long,
31
+ :only_visible, :int,
32
+ :screen, :int,
33
+ :require, :int,
34
+ :searchmask, :uint,
35
+ :desktop, :long
36
+ end # class XdoSearch
37
+
22
38
  def initialize(config)
23
39
  super(config)
24
40
  @xdo = LibXdo::xdo_new(nil)
25
41
  if @xdo.null?
26
42
  raise "xdo_new failed"
27
43
  end
44
+
45
+ search = XdoSearch.new
46
+ search[:searchmask] = 1 << 2 # SEARCH_NAME, from xdo.h
47
+ search[:max_depth] = 0
48
+ search[:winname] = FFI::MemoryPointer.new(:char, 3)
49
+ search[:winname].put_string(0, ".*")
50
+ ptr_nwindows = FFI::MemoryPointer.new(:ulong, 1)
51
+ ptr_winlist = FFI::MemoryPointer.new(:pointer, 1)
52
+ LibXdo::xdo_window_search(@xdo, search, ptr_winlist, ptr_nwindows)
53
+ nwindows = ptr_nwindows.read_long
54
+ @rootwin = ptr_winlist.read_pointer.read_array_of_long(nwindows)[0]
55
+
56
+ ptr_x = FFI::MemoryPointer.new(:int, 1)
57
+ ptr_y = FFI::MemoryPointer.new(:int, 1)
58
+
59
+ LibXdo::xdo_get_window_size(@xdo, @rootwin, ptr_x, ptr_y)
60
+ @screen_x = ptr_x.read_int
61
+ @screen_y = ptr_y.read_int
28
62
  end
29
63
 
30
64
  def mousemove_relative(x, y)
31
65
  return LibXdo::xdo_mousemove_relative(@xdo, x, y)
32
66
  end
33
67
 
68
+ def mousemove_absolute(px, py)
69
+ # Edges may be hard to hit on some devices, so inflate things a bit.
70
+ xbuf = @screen_x * 0.1
71
+ ybuf = @screen_y * 0.1
72
+ x = (((@screen_x + xbuf) * px) - (xbuf / 2)).to_i
73
+ y = (((@screen_y + ybuf) * py) - (ybuf / 2)).to_i
74
+
75
+ return LibXdo::xdo_mousemove(@xdo, x, y, 0)
76
+ end
77
+
34
78
  def click(button)
35
79
  return LibXdo::xdo_click(@xdo, 0, button.to_i)
36
80
  end
@@ -3,6 +3,90 @@
3
3
 
4
4
  $(document).ready(function() {
5
5
  var status = $("#status");
6
+ var keyboard = $('#keyboard');
7
+ var keyboard_button = keyboard.prev('a');
8
+ keyboard.width(keyboard_button.width());
9
+ keyboard.height(keyboard_button.height());
10
+ /* TODO(sissel): get the computed width (margin, padding, width) */
11
+ keyboard.css('margin-left', '-' + keyboard_button.width() + 'px');
12
+ keyboard.show();
13
+
14
+ keyboard.bind("focus", function() {
15
+ /* move the textarea away so we don't see the caret */
16
+ keyboard.css('margin-left', '-10000px');
17
+ state.keyboard = true;
18
+ $(window).triggerHandler("resize");
19
+ });
20
+ keyboard.bind("blur", function(){
21
+ keyboard.css('margin-left', '-' + keyboard_button.width() + 'px');
22
+ state.keyboard = false;
23
+ $(window).triggerHandler("resize");
24
+ });
25
+ keyboard.bind("keypress", function(event) {
26
+ var e = event.originalEvent;
27
+ var key = e.charCode;
28
+ if (!key) {
29
+ key = (e.keyCode ? e.keyCode : e.which);
30
+ }
31
+ state.websocket.send(JSON.stringify({
32
+ action: "log",
33
+ shift: e.shiftKey,
34
+ char: e.charCode,
35
+ ctrl: e.ctrlKey,
36
+ meta: e.ctrlKey,
37
+ }));
38
+ state.websocket.send(JSON.stringify({
39
+ action: "keypress",
40
+ key: key,
41
+ shift: e.shiftKey,
42
+ }));
43
+
44
+ /* Only prevent default if we're not backspace,
45
+ * this lets 'backspace' do keyrepeat. */
46
+ if (key != 8) {
47
+ event.preventDefault();
48
+ }
49
+ }).bind("change", function(event) {
50
+ /* Skip empty changes */
51
+ if (keyboard.val() == "") {
52
+ return;
53
+ }
54
+
55
+ state.websocket.send(JSON.stringify({
56
+ action: "type",
57
+ string: keyboard.val(),
58
+ }));
59
+
60
+ /* Clear the field */
61
+ keyboard.val("");
62
+ });
63
+
64
+ keyboard.bind("keyup", function(event) {
65
+ var e = event.originalEvent;
66
+ state.websocket.send(JSON.stringify({
67
+ action: "log",
68
+ shift: e.shiftKey,
69
+ char: e.charCode,
70
+ key: e.which,
71
+ ctrl: e.ctrlKey,
72
+ meta: e.ctrlKey,
73
+ }));
74
+
75
+ var key = (e.keyCode ? e.keyCode : e.which);
76
+ if (key >= 32 && key <= 127) {
77
+ /* skip printable keys (a-z, etc) */
78
+ return;
79
+ }
80
+
81
+ state.websocket.send(JSON.stringify({
82
+ action: "keypress",
83
+ key: key,
84
+ shift: e.shiftKey,
85
+ }));
86
+
87
+ event.preventDefault();
88
+ });
89
+
6
90
  var config = function (key, value, default_value) {
7
91
  if (value) {
8
92
  status.html("config[" + key + "] = " + value);
@@ -22,7 +106,9 @@
22
106
  dragging: false,
23
107
  width: window.innerWidth,
24
108
  height: window.innerHeight,
25
- key: undefined,
109
+ key: undefined, /* TODO(sissel): unused? */
110
+ keyboard: false,
111
+ touchpad_active: false,
26
112
  mouse: { },
27
113
  scroll: {
28
114
  y: 0,
@@ -55,7 +141,6 @@
55
141
  $("input[name = \"mouse-acceleration\"]")
56
142
  .bind("change", function(event) {
57
143
  config("fingerpoken/mouse/acceleration", parseInt(event.target.value));
58
- //status.html(event.target.value);
59
144
  }).val(config("fingerpoken/mouse/acceleration")).change();
60
145
 
61
146
  /* Changing orientation sometimes leaves the viewport
@@ -63,7 +148,6 @@
63
148
  * Also, we want to make the content size full height. */
64
149
  $(window).bind("orientationchange resize pageshow", function(event) {
65
150
  scroll(0, 0);
66
- console.log(window.orientation);
67
151
 
68
152
  var header = $(".header:visible");
69
153
  var footer = $(".footer:visible");
@@ -74,6 +158,20 @@
74
158
 
75
159
  /* Trim margin/border/padding height */
76
160
  content_height -= (content.outerHeight() - content.height());
161
+
162
+ /* TODO(sissel): Make this special handling only for iphones.
163
+ * http://developer.apple.com/library/safari/#documentation/appleapplications/reference/safariwebcontent/UsingtheViewport/UsingtheViewport.html
164
+ */
165
+ if (state.keyboard) {
166
+ if (window.orientation == 90 || window.orientation == -90) {
167
+ content_height -= 162; /* landscape orientation keyboard */
168
+ content_height -= 32; /* "form assistant" aka FormFill, this height is undocumented. */
169
+ } else {
170
+ content_height -= 216; /* portrait orientation keyboard */
171
+ content_height -= 44; /* "form assistant" aka FormFill */
172
+ }
173
+ }
174
+ status.html("Resize / " + window.orientation + " / " + state.keyboard + " / " + content_height);
77
175
  content.height(content_height);
78
176
  });
79
177
 
@@ -121,11 +219,11 @@
121
219
 
122
220
 
123
221
  /* TODO(sissel): add mousedown/mousemove/mouseup support */
124
- console.log($("#touchpad-surface"));
125
- $("#area").bind("touchstart", function(event) {
126
- event.preventDefault();
222
+ $("#area").bind("touchstart mousedown", function(event) {
127
223
  var e = event.originalEvent;
128
- var touches = e.touches;
224
+ state.touchpad_active = true;
225
+ /* if no 'touches', use the event itself, one finger/mouse */
226
+ var touches = e.touches || [ e ];
129
227
  var output = "Start: " + touches[0].clientX + "," + touches[0].clientY + "\n";
130
228
  output += "Fingers: " + touches.length + "\n";
131
229
  status.html(output);
@@ -147,9 +245,10 @@
147
245
  }))
148
246
  state.dragging = true;
149
247
  }
150
- }).bind("touchend", function(event) { /* $("#touchpadsurface").bind("touchend" ... */
248
+ event.preventDefault();
249
+ }).bind("touchend mouseup", function(event) { /* $("#touchpadsurface").bind("touchend" ... */
151
250
  var e = event.originalEvent;
152
- var touches = e.touches;
251
+ var touches = e.touches || [ e ];
153
252
 
154
253
  if (state.mouse.vectorTimer) {
155
254
  clearInterval(state.mouse.vectorTimer);
@@ -171,83 +270,6 @@
171
270
  }
172
271
 
173
272
  status.html(r);
174
- if (r > 75 && r < 105) {
175
- /* Activate the keyboard when there's a ~90-degree rotation*/
176
- var keyboard = $("<textarea id='keyboard' rows='10'></textarea>");
177
- keyboard.css("width", "100%");
178
- keyboard.css("height", "100%");
179
- status.html("");
180
- keyboard.appendTo(status).focus();
181
- keyboard.bind("keypress", function(event) {
182
- var e = event.originalEvent;
183
- var key = e.charCode;
184
- console.log(key);
185
- if (!key) {
186
- key = (e.keyCode ? e.keyCode : e.which);
187
- }
188
- state.websocket.send(JSON.stringify({
189
- action: "log",
190
- shift: e.shiftKey,
191
- char: e.charCode,
192
- ctrl: e.ctrlKey,
193
- meta: e.ctrlKey,
194
- }));
195
- state.websocket.send(JSON.stringify({
196
- action: "keypress",
197
- key: key,
198
- shift: e.shiftKey,
199
- }));
200
-
201
- e.preventDefault();
202
- }).bind("change", function(event) {
203
- /* Skip empty changes */
204
- if (keyboard.val() == "") {
205
- return;
206
- }
207
-
208
- state.websocket.send(JSON.stringify({
209
- action: "type",
210
- string: keyboard.val(),
211
- }));
212
-
213
- /* Clear the field */
214
- keyboard.val("");
215
- });
216
-
217
- keyboard.bind("keyup", function(event) {
218
- var e = event.originalEvent;
219
- state.websocket.send(JSON.stringify({
220
- action: "log",
221
- shift: e.shiftKey,
222
- char: e.charCode,
223
- key: e.which,
224
- ctrl: e.ctrlKey,
225
- meta: e.ctrlKey,
226
- }));
227
-
228
- console.log(key);
229
- var key = (e.keyCode ? e.keyCode : e.which);
230
- if (key >= 32 && key <= 127) {
231
- /* skip printable keys (a-z, etc) */
232
- return;
233
- }
234
-
235
- state.websocket.send(JSON.stringify({
236
- action: "keypress",
237
- key: key,
238
- shift: e.shiftKey,
239
- }));
240
-
241
- e.preventDefault();
242
- });
243
- //status.html("<textarea id='keyboard'></textarea>");
244
- //$("#keyboard").focus();
245
- } else { /* Otherwise, we didn't rotate */
246
- state.websocket.send(JSON.stringify({
247
- action: "move_end",
248
- now: (new Date()),
249
- }));
250
- }
251
273
  } else if (state.scrolling) {
252
274
  /* nothing for now */
253
275
  } else {
@@ -260,15 +282,21 @@
260
282
  state.last_click = (new Date()).getTime();
261
283
  }
262
284
  }
263
- if (touches.length == 0) {
285
+ if (touches.length == 0 || !e.touches) {
264
286
  state.moving = false;
265
287
  state.scrolling = false;
288
+ state.touchpad_active = false;
266
289
  }
267
290
  event.preventDefault();
268
- }).bind("touchmove", function(event) { /* $("#touchpadsurface").bind("touchmove" ... */
291
+ }).bind("touchmove mousemove", function(event) { /* $("#touchpadsurface").bind("touchmove" ... */
269
292
  var e = event.originalEvent;
270
- var touches = e.touches;
271
- event.preventDefault();
293
+ var touches = e.touches || [ e ];
294
+
295
+ //if (!state.touchpad_active) {
296
+ //event.preventDefault();
297
+ //return;
298
+ //}
299
+
272
300
  if (!state.moving) {
273
301
  /* Start calculating delta offsets now */
274
302
  state.moving = true;
@@ -348,12 +376,19 @@
348
376
  /* TODO(sissel): Refactor these in to fumctions */
349
377
  var movement = config("fingerpoken/mouse/movement");
350
378
  if (movement == "relative") {
351
- status.html(output);
352
379
  state.websocket.send(JSON.stringify({
353
380
  action: "mousemove_relative",
354
381
  rel_x: delta_x,
355
382
  rel_y: delta_y
356
383
  }));
384
+ } else if (movement == "absolute") {
385
+ /* Send absolute in terms of percentages. */
386
+ var content = $(".content:visible");
387
+ state.websocket.send(JSON.stringify({
388
+ action: "mousemove_absolute",
389
+ percent_x: x / content.innerWidth(),
390
+ percent_y: y / content.innerHeight(),
391
+ }));
357
392
  } else if (movement == "vector") {
358
393
  if (!state.mouse.vectorTimer) {
359
394
  state.mouse.vectorTimer = setInterval(function() {
@@ -371,7 +406,6 @@
371
406
  rx = Math.ceil(Math.pow(Math.abs(rx), vector_accel) * sign_rx);
372
407
  ry = Math.ceil(Math.pow(Math.abs(ry), vector_accel) * sign_ry);
373
408
  output += "rx2,ry2 = " + rx + ", " + ry + "\n";
374
- status.html(output)
375
409
 
376
410
  state.websocket.send(JSON.stringify({
377
411
  action: "mousemove_relative",
@@ -381,6 +415,7 @@
381
415
  }, 15);
382
416
  } /* if (!state.mouse.vectorTimer) */
383
417
  } /* mouse vector movement */
418
+ status.html(output)
384
419
  } /* finger movement */
385
420
  }); /* $("#touchpadsurface").bind( ... )*/
386
421
 
data/views/index.haml CHANGED
@@ -24,11 +24,13 @@
24
24
  %a{:href => "javascript:window.location.reload()", "data-role" => "button", "data-inline" => true} Reload
25
25
  %div.content{"data-role" => "content"}
26
26
  #area
27
- /%pre#status
27
+ %pre#status
28
28
 
29
29
  %div.footer{"data-role" => "footer"}
30
30
  %span.left
31
31
  %a{:href => "#commands", "data-role" => "button", "data-rel" => "dialog", "data-transition" => "pop"} Commands
32
+ %a{:href => "#keyboard", "data-role" => "button", "data-action" => "keyboard"} Keyboard
33
+ %textarea#keyboard{:autocapitalize => "off", :autocorrect => "off"}
32
34
  %span.right
33
35
  %a.command{:href => "#", "data-role" => "button", "data-action" => "click", "data-button" => "1"} Left
34
36
  %a.command{:href => "#", "data-role" => "button", "data-action" => "click", "data-button" => "3"} Right
data/views/style.sass CHANGED
@@ -20,6 +20,7 @@ html, body
20
20
  color: orange
21
21
 
22
22
  .content
23
+ -webkit-transition: height 0.1s linear
23
24
  h1.status
24
25
  font-size: 200%
25
26
  color: white
@@ -37,8 +38,11 @@ html, body
37
38
  padding: 10px
38
39
 
39
40
  #keyboard
40
- width: 100%
41
- height: 100%
41
+ border: 0
42
+ background: transparent
42
43
  margin: 0
43
44
  padding: 0
44
-
45
+ position: absolute
46
+ outline: none
47
+ resize: none
48
+ display: none
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: 40220202391481
4
+ hash: 40220204069073
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 20110101195735
10
- version: 0.2.20110101195735
9
+ - 20110102034531
10
+ version: 0.2.20110102034531
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: 2011-01-01 00:00:00 -08:00
18
+ date: 2011-01-02 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency