fingerpoken 0.2.20110101195735 → 0.2.20110102034531

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.
@@ -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