mouse 1.1.0 → 2.0.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/ext/mouse/mouse.c CHANGED
@@ -1,14 +1,15 @@
1
1
  #include "mouser.h"
2
2
  #include "ruby.h"
3
3
 
4
- static VALUE rb_mMouse;
5
- static VALUE rb_cCGPoint;
6
- static ID sel_x;
7
- static ID sel_y;
8
- static ID sel_to_point;
9
- static ID sel_new;
10
- static ID unit_pixel;
11
- static ID unit_line;
4
+ static VALUE rb_mMouse, rb_cCGPoint;
5
+
6
+ static ID sel_x, sel_y, sel_to_point, sel_new;
7
+
8
+ static VALUE sym_pixel, sym_line,
9
+ sym_up, sym_down, sym_left, sym_right,
10
+ sym_zoom, sym_unzoom, sym_expand, sym_contract,
11
+ sym_cw, sym_clockwise, sym_clock_wise,
12
+ sym_ccw, sym_counter_clockwise, sym_counter_clock_wise;
12
13
 
13
14
  #define CURRENT_POSITION rb_mouse_wrap_point(mouse_current_position())
14
15
 
@@ -16,7 +17,6 @@ static
16
17
  VALUE
17
18
  rb_mouse_wrap_point(CGPoint point)
18
19
  {
19
- // TODO: Data_Wrap_Struct instead
20
20
  #if NOT_MACRUBY
21
21
  return rb_struct_new(rb_cCGPoint, DBL2NUM(point.x), DBL2NUM(point.y));
22
22
  #else
@@ -118,8 +118,11 @@ rb_mouse_drag_to(int argc, VALUE *argv, VALUE self)
118
118
  * Returns number of lines scrolled. A positive `amount` will scroll up
119
119
  * and a negative `amount` will scroll down.
120
120
  *
121
+ * An animation duration can also be specified.
122
+ *
121
123
  * @param amount [Number]
122
124
  * @param units [Symbol] `:pixel` or `:line` (_default_: `:line` ) (__optional__)
125
+ * @param duration [Float] (_default_: `0.2`) (__optional__)
123
126
  * @return [Number]
124
127
  */
125
128
  static
@@ -133,29 +136,71 @@ rb_mouse_scroll(int argc, VALUE *argv, VALUE self)
133
136
 
134
137
  if (argc == 1) {
135
138
  mouse_scroll(amt);
136
- return argv[0];
137
- }
138
139
 
139
- ID units = rb_to_id(argv[1]);
140
+ } else {
141
+ VALUE input_units = argv[1];
142
+ CGScrollEventUnit units;
140
143
 
141
- if (argc == 2) {
142
- if (units == unit_pixel)
143
- mouse_scroll2(amt, kCGScrollEventUnitPixel);
144
- else if (units == unit_line)
145
- mouse_scroll2(amt, kCGScrollEventUnitLine);
144
+ if (input_units == sym_pixel)
145
+ units = kCGScrollEventUnitPixel;
146
+ else if (input_units == sym_line)
147
+ units = kCGScrollEventUnitLine;
146
148
  else
147
- rb_raise(rb_eArgError, "unknown units `%s'", rb_id2name(units));
149
+ rb_raise(rb_eArgError, "unknown units `%s'", rb_id2name(input_units));
150
+
151
+ if (argc == 2)
152
+ mouse_scroll2(amt, units);
153
+ else
154
+ mouse_scroll3(amt, units, NUM2DBL(argv[2]));
148
155
  }
149
156
 
150
- if (argc == 3) {
151
- double duration = NUM2DBL(argv[2]);
157
+ return argv[0];
158
+ }
159
+
160
+ /*
161
+ * @note Scrolling by `:pixel` may not actually be by real pixels, but instead
162
+ * correspond to Cocoa co-ords (I don't have a retina display, so I haven't
163
+ * checked it out yet).
164
+ *
165
+ * Generate `amount` of horizontal scroll events at the current cursor position
166
+ *
167
+ * Returns number of lines scrolled. A positive `amount` will scroll left
168
+ * and a negative `amount` will scroll right.
169
+ *
170
+ * An animation duration can also be specified.
171
+ *
172
+ * @param amount [Number]
173
+ * @param units [Symbol] `:pixel` or `:line` (_default_: `:line` ) (__optional__)
174
+ * @param duration [Float] (_default_: `0.2`) (__optional__)
175
+ * @return [Number]
176
+ */
177
+ static
178
+ VALUE
179
+ rb_mouse_horizontal_scroll(int argc, VALUE *argv, VALUE self)
180
+ {
181
+ if (argc == 0 || argc > 3)
182
+ rb_raise(rb_eArgError, "scroll requires 1..3 arguments, you gave %d", argc);
183
+
184
+ int amt = NUM2INT(argv[0]);
152
185
 
153
- if (units == unit_pixel)
154
- mouse_scroll3(amt, kCGScrollEventUnitPixel, duration);
155
- else if (units == unit_line)
156
- mouse_scroll3(amt, kCGScrollEventUnitLine, duration);
186
+ if (argc == 1) {
187
+ mouse_horizontal_scroll(amt);
188
+
189
+ } else {
190
+ VALUE input_units = argv[1];
191
+ CGScrollEventUnit units;
192
+
193
+ if (input_units == sym_pixel)
194
+ units = kCGScrollEventUnitPixel;
195
+ else if (input_units == sym_line)
196
+ units = kCGScrollEventUnitLine;
157
197
  else
158
- rb_raise(rb_eArgError, "unknown units `%s'", rb_id2name(units));
198
+ rb_raise(rb_eArgError, "unknown units `%s'", rb_id2name(input_units));
199
+
200
+ if (argc == 2)
201
+ mouse_horizontal_scroll2(amt, units);
202
+ else
203
+ mouse_horizontal_scroll3(amt, units, NUM2DBL(argv[2]));
159
204
  }
160
205
 
161
206
  return argv[0];
@@ -574,6 +619,231 @@ rb_mouse_triple_click(int argc, VALUE *argv, VALUE self)
574
619
  return CURRENT_POSITION;
575
620
  }
576
621
 
622
+
623
+ /* @!group Gestures */
624
+
625
+ /*
626
+ * Perform a smart magnify (double tap on trackpad)
627
+ *
628
+ * You can optionally specify the point on the screen where to perform
629
+ * the smart magnification.
630
+ *
631
+ * @param point [CGPoint] (_default_: {#current_position}) (__optional__)
632
+ * @return [CGPoint]
633
+ */
634
+ static
635
+ VALUE
636
+ rb_mouse_smart_magnify(int argc, VALUE* argv, VALUE self)
637
+ {
638
+ switch (argc)
639
+ {
640
+ case 0:
641
+ mouse_smart_magnify();
642
+ break;
643
+ case 1:
644
+ default:
645
+ mouse_smart_magnify2(rb_mouse_unwrap_point(argv[0]));
646
+ }
647
+
648
+ return CURRENT_POSITION;
649
+ }
650
+
651
+ /*
652
+ * Perform a swipe gesture in the given `direction`
653
+ *
654
+ * You can optionally specify a point on screen for the mouse
655
+ * pointer to be moved to before the gesture begins. The movement will
656
+ * be instantaneous.
657
+ *
658
+ * You can also optionally specify the duration of the swipe event.
659
+ *
660
+ * @param direction [Symbol]
661
+ * @param point [CGPoint] (_default_: {#current_position}) (__optional__)
662
+ * @param duration [Float] (_default_: `0.2`) (__optional__)
663
+ * @return [CGPoint]
664
+ */
665
+ static
666
+ VALUE
667
+ rb_mouse_swipe(int argc, VALUE* argv, VALUE self)
668
+ {
669
+ if (!argc)
670
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1+)");
671
+
672
+ CGSwipeDirection direction;
673
+ VALUE direction_input = argv[0];
674
+ if (direction_input == sym_up)
675
+ direction = kCGSwipeDirectionUp;
676
+ else if (direction_input == sym_down)
677
+ direction = kCGSwipeDirectionDown;
678
+ else if (direction_input == sym_left)
679
+ direction = kCGSwipeDirectionLeft;
680
+ else if (direction_input == sym_right)
681
+ direction = kCGSwipeDirectionRight;
682
+ else
683
+ rb_raise(
684
+ rb_eArgError,
685
+ "invalid swipe direction `%s'",
686
+ rb_id2name(SYM2ID(direction_input))
687
+ );
688
+
689
+ if (argc == 1) {
690
+ mouse_swipe(direction);
691
+ return CURRENT_POSITION;
692
+ }
693
+
694
+ CGPoint point = rb_mouse_unwrap_point(argv[1]);
695
+
696
+ if (argc == 2)
697
+ mouse_swipe2(direction, point);
698
+ else
699
+ mouse_swipe3(direction, point, NUM2DBL(argv[1]));
700
+
701
+ return CURRENT_POSITION;
702
+ }
703
+
704
+ /*
705
+ * Perform a pinch gesture in given `direction`
706
+ *
707
+ * You can optionally specify the `magnification` factor and/or
708
+ * `duration` for the pinch event.
709
+ *
710
+ * Available pinch directions are:
711
+ *
712
+ * - `:zoom` or `:expand`
713
+ * - `:unzoom` or `:contract`
714
+ *
715
+ * Magnification is a relative magnification setting. A zoom value of
716
+ * `1.0` means `1.0` more than the current zoom level. `2.0` would be
717
+ * `2.0` levels higher than the current zoom.
718
+ *
719
+ * You can also optionally specify a point on screen for the mouse
720
+ * pointer to be moved to before the gesture begins. The movement will
721
+ * be instantaneous.
722
+ *
723
+ * An animation duration can also be specified.
724
+ *
725
+ * @param direction [Symbol]
726
+ * @param magnification [Float] (_default_: `1.0`) (__optional__)
727
+ * @param point [CGPoint] (_default_: {#current_position}) (__optional__)
728
+ * @param duration [Float] (_default_: `0.2`) (__optional__)
729
+ * @return [CGPoint]
730
+ */
731
+ static
732
+ VALUE
733
+ rb_mouse_pinch(int argc, VALUE* argv, VALUE self)
734
+ {
735
+ if (!argc)
736
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1+)", argc);
737
+
738
+ VALUE input_direction = argv[0];
739
+ CGPinchDirection direction = kCGPinchNone;
740
+
741
+ if (input_direction == sym_expand || input_direction == sym_zoom)
742
+ direction = kCGPinchExpand;
743
+ else if (input_direction == sym_contract || input_direction == sym_unzoom)
744
+ direction = kCGPinchContract;
745
+ else
746
+ rb_raise(
747
+ rb_eArgError,
748
+ "invalid pinch direction `%s'",
749
+ rb_id2name(SYM2ID(input_direction))
750
+ );
751
+
752
+ if (argc == 1) {
753
+ mouse_pinch(direction);
754
+ return CURRENT_POSITION;
755
+ }
756
+
757
+ double magnification = NUM2DBL(argv[1]);
758
+ if (argc == 2) {
759
+ mouse_pinch2(direction, magnification);
760
+ return CURRENT_POSITION;
761
+ }
762
+
763
+ CGPoint point = rb_mouse_unwrap_point(argv[2]);
764
+ if (argc == 3) {
765
+ mouse_pinch3(direction, magnification, point);
766
+ return CURRENT_POSITION;
767
+ }
768
+
769
+ double duration = NUM2DBL(argv[3]);
770
+ mouse_pinch4(direction, magnification, point, duration);
771
+ return CURRENT_POSITION;
772
+ }
773
+
774
+ /*
775
+ * Perform a rotation gesture in the given `direction` the given `angle` degrees
776
+ *
777
+ * Possible directions are:
778
+ *
779
+ * - `:cw`, ':clockwise`, ':clock_wise` to rotate in the clockwise
780
+ * direction
781
+ * - `:ccw`, ':counter_clockwise`, `:counter_clock_wise` to rotate in
782
+ * the the counter clockwise direction
783
+ *
784
+ * The `angle` parameter is a number of degrees to rotate. There are 360
785
+ * degrees in a full rotation, as you would expect in Euclidian geometry.
786
+ *
787
+ * You can also optionally specify a point on screen for the mouse
788
+ * pointer to be moved to before the gesture begins. The movement will
789
+ * be instantaneous.
790
+ *
791
+ * An animation duration can also be specified.
792
+ *
793
+ * @param direction [Symbol]
794
+ * @param angle [Float]
795
+ * @param point [CGPoint] (_default_: {#current_position}) (__optional__)
796
+ * @param duration [Float] (_default_: `0.2`) (__optional__)
797
+ * @return [CGPoint]
798
+ */
799
+ static
800
+ VALUE
801
+ rb_mouse_rotate(int argc, VALUE* argv, VALUE self)
802
+ {
803
+ if (argc < 2)
804
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2+)", argc);
805
+
806
+ CGRotateDirection direction = kCGRotateNone;
807
+ VALUE input_dir = argv[0];
808
+ if (
809
+ input_dir == sym_cw ||
810
+ input_dir == sym_clockwise ||
811
+ input_dir == sym_clock_wise
812
+ )
813
+ direction = kCGRotateClockwise;
814
+ else if (
815
+ input_dir == sym_ccw ||
816
+ input_dir == sym_counter_clockwise ||
817
+ input_dir == sym_counter_clock_wise
818
+ )
819
+ direction = kCGRotateCounterClockwise;
820
+ else
821
+ rb_raise(
822
+ rb_eArgError,
823
+ "invalid rotation direction `%s'",
824
+ rb_id2name(SYM2ID(input_dir))
825
+ );
826
+
827
+ double angle = NUM2DBL(argv[1]);
828
+
829
+ if (argc == 2) {
830
+ mouse_rotate(direction, angle);
831
+ return CURRENT_POSITION;
832
+ }
833
+
834
+ CGPoint point = rb_mouse_unwrap_point(argv[2]);
835
+ if (argc == 3) {
836
+ mouse_rotate2(direction, angle, point);
837
+ return CURRENT_POSITION;
838
+ }
839
+
840
+ mouse_rotate3(direction, angle, point, NUM2DBL(argv[3]));
841
+ return CURRENT_POSITION;
842
+ }
843
+
844
+ /* @!endgroup */
845
+
846
+
577
847
  void
578
848
  Init_mouse()
579
849
  {
@@ -585,8 +855,26 @@ Init_mouse()
585
855
  sel_to_point = rb_intern("to_point");
586
856
  sel_new = rb_intern("new");
587
857
 
588
- unit_pixel = rb_intern("pixel");
589
- unit_line = rb_intern("line");
858
+ sym_pixel = ID2SYM(rb_intern("pixel"));
859
+ sym_line = ID2SYM(rb_intern("line"));
860
+
861
+ sym_up = ID2SYM(rb_intern("up"));
862
+ sym_down = ID2SYM(rb_intern("down"));
863
+ sym_left = ID2SYM(rb_intern("left"));
864
+ sym_right = ID2SYM(rb_intern("right"));
865
+
866
+ sym_zoom = ID2SYM(rb_intern("zoom"));
867
+ sym_unzoom = ID2SYM(rb_intern("unzoom"));
868
+ sym_expand = ID2SYM(rb_intern("expand"));
869
+ sym_contract = ID2SYM(rb_intern("contract"));
870
+
871
+ sym_cw = ID2SYM(rb_intern("cw"));
872
+ sym_clockwise = ID2SYM(rb_intern("clockwise"));
873
+ sym_clock_wise = ID2SYM(rb_intern("clock_wise"));
874
+ sym_ccw = ID2SYM(rb_intern("ccw"));
875
+ sym_counter_clockwise = ID2SYM(rb_intern("counter_clockwise"));
876
+ sym_counter_clock_wise = ID2SYM(rb_intern("counter_clock_wise"));
877
+
590
878
 
591
879
  /*
592
880
  * Document-module: Mouse
@@ -611,6 +899,7 @@ Init_mouse()
611
899
  rb_define_method(rb_mMouse, "move_to", rb_mouse_move_to, -1);
612
900
  rb_define_method(rb_mMouse, "drag_to", rb_mouse_drag_to, -1);
613
901
  rb_define_method(rb_mMouse, "scroll", rb_mouse_scroll, -1);
902
+ rb_define_method(rb_mMouse, "horizontal_scroll", rb_mouse_horizontal_scroll, -1);
614
903
  rb_define_method(rb_mMouse, "click_down", rb_mouse_click_down, -1);
615
904
  rb_define_method(rb_mMouse, "click_up", rb_mouse_click_up, -1);
616
905
  rb_define_method(rb_mMouse, "click", rb_mouse_click, -1);
@@ -625,7 +914,14 @@ Init_mouse()
625
914
  rb_define_method(rb_mMouse, "double_click", rb_mouse_double_click, -1);
626
915
  rb_define_method(rb_mMouse, "triple_click", rb_mouse_triple_click, -1);
627
916
 
628
- rb_define_alias(rb_mMouse, "right_click_down", "secondary_click_down");
629
- rb_define_alias(rb_mMouse, "right_click_up", "secondary_click_up");
630
- rb_define_alias(rb_mMouse, "right_click", "secondary_click");
917
+ rb_define_method(rb_mMouse, "smart_magnify", rb_mouse_smart_magnify, -1);
918
+ rb_define_method(rb_mMouse, "swipe", rb_mouse_swipe, -1);
919
+ rb_define_method(rb_mMouse, "pinch", rb_mouse_pinch, -1);
920
+ rb_define_method(rb_mMouse, "rotate", rb_mouse_rotate, -1);
921
+
922
+ rb_define_alias(rb_mMouse, "hscroll", "horizontal_scroll");
923
+ rb_define_alias(rb_mMouse, "right_click_down", "secondary_click_down");
924
+ rb_define_alias(rb_mMouse, "right_click_up", "secondary_click_up");
925
+ rb_define_alias(rb_mMouse, "right_click", "secondary_click");
926
+ rb_define_alias(rb_mMouse, "two_finger_double_tap", "smart_magnify");
631
927
  }
data/ext/mouse/mouser.c CHANGED
@@ -9,10 +9,12 @@
9
9
  #include <ApplicationServices/ApplicationServices.h>
10
10
  #include "mouser.h"
11
11
 
12
- static const uint_t FPS = 240;
13
- static const uint_t QUANTUM = 1000000 / 240; // should be FPS, but GCC sucks
14
- static const double DEFAULT_DURATION = 0.2; // seconds
12
+ static const uint_t FPS = 240;
13
+ static const uint_t QUANTUM = 1000000 / 240; // should be FPS, but GCC sucks
14
+ static const double DEFAULT_DURATION = 0.2; // seconds
15
+ static const double DEFAULT_MAGNIFICATION = 1.0; // factor
15
16
 
17
+ #define NEW_GESTURE(name) CGEventRef name = CGEventCreate(nil); CHANGE(name, kCGEventGesture);
16
18
  #define NEW_EVENT(type,point,button) CGEventCreateMouseEvent(nil,type,point,button)
17
19
  #define POST(event) CGEventPost(kCGHIDEventTap, event)
18
20
  #define CHANGE(event,type) CGEventSetType(event, type)
@@ -27,9 +29,9 @@ static const double DEFAULT_DURATION = 0.2; // seconds
27
29
  #endif
28
30
 
29
31
  #define POSTRELEASE(x) do { \
30
- CGEventRef event = x; \
31
- POST(event); \
32
- RELEASE(event); \
32
+ CGEventRef _event = x; \
33
+ POST(_event); \
34
+ RELEASE(_event); \
33
35
  } while(false);
34
36
 
35
37
 
@@ -147,21 +149,25 @@ mouse_drag_to(CGPoint point)
147
149
  }
148
150
 
149
151
 
152
+ #define SCROLL(vval, hval) do { \
153
+ size_t steps = round(FPS * duration); \
154
+ double current = 0.0; \
155
+ double done = 0.0; \
156
+ int32_t scroll = 0; \
157
+ \
158
+ for (size_t step = 0; step < steps; step++) { \
159
+ done = (double)(step+1) / (double)steps; \
160
+ scroll = round((done - current) * amount); \
161
+ POSTRELEASE(CGEventCreateScrollWheelEvent(nil, units, 2, vval, hval)); \
162
+ mouse_sleep(2); \
163
+ current += (double)scroll / (double)amount; \
164
+ } \
165
+ } while (false);
166
+
150
167
  void
151
168
  mouse_scroll3(int amount, CGScrollEventUnit units, double duration)
152
169
  {
153
- size_t steps = round(FPS * duration);
154
- double current = 0.0;
155
- double done = 0.0;
156
- int32_t scroll = 0;
157
-
158
- for (size_t step = 0; step < steps; step++) {
159
- done = (double)(step+1) / (double)steps;
160
- scroll = round((done - current) * amount);
161
- POSTRELEASE(CGEventCreateScrollWheelEvent(nil, units, 1, scroll));
162
- mouse_sleep(2);
163
- current += (double)scroll / (double)amount;
164
- }
170
+ SCROLL(scroll, 0);
165
171
  }
166
172
 
167
173
  void
@@ -176,6 +182,23 @@ mouse_scroll(int amount)
176
182
  mouse_scroll2(amount, kCGScrollEventUnitLine);
177
183
  }
178
184
 
185
+ void
186
+ mouse_horizontal_scroll3(int amount, CGScrollEventUnit units, double duration)
187
+ {
188
+ SCROLL(0, scroll);
189
+ }
190
+
191
+ void
192
+ mouse_horizontal_scroll2(int amount, CGScrollEventUnit units)
193
+ {
194
+ mouse_horizontal_scroll3(amount, units, DEFAULT_DURATION);
195
+ }
196
+
197
+ void
198
+ mouse_horizontal_scroll(int amount)
199
+ {
200
+ mouse_horizontal_scroll2(amount, kCGScrollEventUnitLine);
201
+ }
179
202
 
180
203
  void
181
204
  mouse_click_down3(CGPoint point, uint_t sleep_quanta)
@@ -423,3 +446,213 @@ mouse_triple_click()
423
446
  {
424
447
  mouse_triple_click2(mouse_current_position());
425
448
  }
449
+
450
+
451
+ static
452
+ void
453
+ mouse_gesture(CGPoint point, uint_t sleep_quanta, void (^gesture_block)(void))
454
+ {
455
+ POSTRELEASE(NEW_EVENT(kCGEventMouseMoved, point, kCGMouseButtonLeft));
456
+
457
+ NEW_GESTURE(gesture);
458
+ CGEventSetIntegerValueField(
459
+ gesture,
460
+ kCGEventGestureType,
461
+ kCGGestureTypeGestureStarted
462
+ );
463
+ POST(gesture);
464
+
465
+ gesture_block();
466
+
467
+ CGEventSetIntegerValueField(gesture, kCGEventGestureType, kCGGestureTypeGestureEnded);
468
+ POSTRELEASE(gesture);
469
+
470
+ mouse_sleep(sleep_quanta);
471
+ }
472
+
473
+ void
474
+ mouse_smart_magnify2(CGPoint point)
475
+ {
476
+ mouse_gesture(point, (FPS / 2), ^(void) {
477
+ NEW_GESTURE(event);
478
+ CGEventSetIntegerValueField(event, kCGEventGestureType, kCGGestureTypeSmartMagnify);
479
+ POSTRELEASE(event);
480
+ });
481
+ }
482
+
483
+ void
484
+ mouse_smart_magnify()
485
+ {
486
+ mouse_smart_magnify2(mouse_current_position());
487
+ }
488
+
489
+ void
490
+ mouse_swipe3(
491
+ CGSwipeDirection direction,
492
+ CGPoint point,
493
+ double duration
494
+ )
495
+ {
496
+ uint16_t axis = 0;
497
+ CGFloat distance = 1.0;
498
+ CGGestureMotion motion = kCGGestureMotionNone;
499
+
500
+ switch (direction)
501
+ {
502
+ case kCGSwipeDirectionUp:
503
+ axis = kCGEventGestureSwipePositionY;
504
+ motion = kCGGestureMotionVertical;
505
+ distance = -(distance);
506
+ break;
507
+ case kCGSwipeDirectionDown:
508
+ axis = kCGEventGestureSwipePositionY;
509
+ motion = kCGGestureMotionVertical;
510
+ break;
511
+ case kCGSwipeDirectionLeft:
512
+ axis = kCGEventGestureSwipePositionX;
513
+ motion = kCGGestureMotionHorizontal;
514
+ break;
515
+ case kCGSwipeDirectionRight:
516
+ axis = kCGEventGestureSwipePositionX;
517
+ motion = kCGGestureMotionHorizontal;
518
+ distance = -(distance);
519
+ break;
520
+ default:
521
+ return;
522
+ }
523
+
524
+ mouse_gesture(point, (FPS / 10), ^(void) {
525
+ NEW_GESTURE(swipe);
526
+
527
+ CGEventSetIntegerValueField(swipe, kCGEventGestureType, kCGGestureTypeSwipe);
528
+ CGEventSetIntegerValueField(swipe, kCGEventGestureSwipeMotion, motion);
529
+ CGEventSetIntegerValueField(swipe, kCGEventGestureSwipeDirection, direction);
530
+ CGEventSetIntegerValueField(swipe, kCGEventGesturePhase, kCGGesturePhaseBegan);
531
+ CGEventSetDoubleValueField( swipe, kCGEventGestureSwipeProgress, distance);
532
+ CGEventSetDoubleValueField( swipe, axis, distance);
533
+
534
+ // TODO: animation steps don't seem to do anything...
535
+ // kCGGesturePhaseChanged
536
+ // kCGGesturePhaseEnded
537
+
538
+ POSTRELEASE(swipe);
539
+ });
540
+ }
541
+
542
+ void
543
+ mouse_swipe2(CGSwipeDirection direction, CGPoint point)
544
+ {
545
+ mouse_swipe3(direction, point, DEFAULT_DURATION);
546
+ }
547
+
548
+ void
549
+ mouse_swipe(CGSwipeDirection direction)
550
+ {
551
+ mouse_swipe2(direction, mouse_current_position());
552
+ }
553
+
554
+ void
555
+ mouse_pinch4(
556
+ CGPinchDirection direction,
557
+ double magnification,
558
+ CGPoint point,
559
+ double duration
560
+ )
561
+ {
562
+ switch (direction)
563
+ {
564
+ case kCGPinchExpand:
565
+ break;
566
+ case kCGPinchContract:
567
+ magnification = -(magnification);
568
+ break;
569
+ default:
570
+ return;
571
+ }
572
+
573
+ mouse_gesture(point, FPS / 10, ^(void) {
574
+ NEW_GESTURE(pinch);
575
+ CGEventSetIntegerValueField(pinch, kCGEventGestureType, kCGGestureTypePinch);
576
+
577
+ size_t steps = FPS / duration;
578
+ double step_size = magnification / steps;
579
+ double step_period = (duration / steps) * 1000000;
580
+
581
+ CGEventSetDoubleValueField(pinch, kCGEventGesturePinchValue, step_size);
582
+
583
+ for (size_t i = 0; i < steps; i++) {
584
+ POST(pinch);
585
+ usleep(step_period);
586
+ }
587
+
588
+ RELEASE(pinch);
589
+ });
590
+ }
591
+
592
+ void
593
+ mouse_pinch3(CGPinchDirection direction, double magnification, CGPoint point)
594
+ {
595
+ mouse_pinch4(direction, magnification, point, DEFAULT_DURATION);
596
+ }
597
+
598
+ void
599
+ mouse_pinch2(CGPinchDirection direction, double magnification)
600
+ {
601
+ mouse_pinch3(direction, magnification, mouse_current_position());
602
+ }
603
+
604
+ void
605
+ mouse_pinch(CGPinchDirection direction)
606
+ {
607
+ mouse_pinch2(direction, DEFAULT_MAGNIFICATION);
608
+ }
609
+
610
+ void
611
+ mouse_rotate3(
612
+ CGRotateDirection direction,
613
+ double angle,
614
+ CGPoint point,
615
+ double duration
616
+ )
617
+ {
618
+ switch (direction)
619
+ {
620
+ case kCGRotateClockwise:
621
+ angle = -(angle);
622
+ break;
623
+ case kCGRotateCounterClockwise:
624
+ break;
625
+ default:
626
+ return;
627
+ }
628
+
629
+ mouse_gesture(point, (FPS / 10), ^(void) {
630
+ NEW_GESTURE(rotation);
631
+ CGEventSetIntegerValueField(rotation, kCGEventGestureType, kCGGestureTypeRotation);
632
+
633
+ size_t steps = FPS / duration;
634
+ double step_size = angle / steps;
635
+ double step_period = (duration / steps) * 1000000;
636
+
637
+ CGEventSetDoubleValueField(rotation, kCGEventGestureRotationValue, step_size);
638
+
639
+ for (size_t i = 0; i < steps; i++) {
640
+ POST(rotation);
641
+ usleep(step_period);
642
+ }
643
+
644
+ RELEASE(rotation);
645
+ });
646
+ }
647
+
648
+ void
649
+ mouse_rotate2(CGRotateDirection direction, double angle, CGPoint point)
650
+ {
651
+ mouse_rotate3(direction, angle, point, DEFAULT_DURATION);
652
+ }
653
+
654
+ void
655
+ mouse_rotate(CGRotateDirection direction, double angle)
656
+ {
657
+ mouse_rotate2(direction, angle, mouse_current_position());
658
+ }