]> git.eshelyaron.com Git - emacs.git/commitdiff
Register ``pinch to zoom'' touch screen gestures
authorPo Lu <luangruo@yahoo.com>
Wed, 15 Nov 2023 12:58:46 +0000 (20:58 +0800)
committerPo Lu <luangruo@yahoo.com>
Wed, 15 Nov 2023 13:07:14 +0000 (21:07 +0800)
* doc/emacs/input.texi (Touchscreens): Address pinch gestures.

* doc/lispref/commands.texi (Touchscreen Events): Address touch
screen pinch events and the process by which they are produced.

* java/org/gnu/emacs/EmacsWindow.java (figureChange)
<ACTION_POINTER_DOWN>: Supply pointer index to getX and getY,
correcting a mistake where the first touch point's coordinate
was saved here in lieu of the pointer that was pressed's.

* lisp/touch-screen.el (touch-screen-current-tool): Revise doc
string.
(touch-screen-aux-tool): New variable.
(touch-screen-scroll-point-to-y, touch-screen-pinch): New
functions.
(global-map): Bind [touchscreen-pinch] to touch-screen-pinch.
(touch-screen-handle-point-update): Revise doc string; set new
tenth field of t-s-c-t to POINT relative to its window, without
regard to whether an event has been sent.
(touch-screen-distance, touch-screen-centrum): New functions.
(touch-screen-handle-aux-point-update): New function; generate
and send touchscreen-pinch if need be.
(touch-screen-handle-point-up): If an ancillary tool exists,
transfer the information there into touch-screen-current-tool
and clear t-s-a-t.
(touch-screen-handle-touch): Call t-s-a-p-u as is proper; set
t-s-a-t if a touchscreen-down event arrives and t-s-c-t is set.

* src/androidterm.c (handle_one_android_event): Properly save
the event's X and Y when a new touch point is registered.

doc/emacs/input.texi
doc/lispref/commands.texi
java/org/gnu/emacs/EmacsWindow.java
lisp/touch-screen.el
src/androidterm.c

index 0dd7fca41ccc698f82634bff3bd46243a3327120..e4d595caf84c3dc08a2882e783cdb056b1a3d03e 100644 (file)
@@ -62,6 +62,13 @@ commence selecting text under the tool as it continues its motion, as
 if @code{mouse-1} were to be held down and a mouse moved analogously.
 @xref{Mouse Commands}.
 
+@item
+@cindex pinching, touchscreens
+  @dfn{Pinching}, which is placing two tools apart on the screen and
+adjusting their position such as to increase or decrease the distance
+between them will modify the text scale (@xref{Text Scale}) in
+proportion to the change in that distance.
+
 @vindex touch-screen-word-select
 @cindex word selection mode, touchscreens
   To the detriment of text selection, it can prove challenging to
index 2518740ad3b3b30c2627c73362faf4a102220026..75685ffe5dce6b18af92e2c6dddf59ac927cf14f 100644 (file)
@@ -2106,8 +2106,9 @@ When no command is bound to @code{touchscreen-begin},
 translate key sequences containing touch screen events into ordinary
 mouse events (@pxref{Mouse Events}.)  Since Emacs doesn't support
 distinguishing events originating from separate mouse devices, it
-assumes that only one touchpoint is active while translation takes
-place; breaking this assumption may lead to unexpected behavior.
+assumes that a maximum of two touchpoints are active while translation
+takes place, and does not place any guarantees on the results of event
+translation when that restriction is overstepped.
 
 Emacs applies two different strategies for translating touch events
 into mouse events, contingent on factors such as the commands bound to
@@ -2159,6 +2160,15 @@ purpose of displaying pop-up menus, Emacs additionally behaves as
 illustrated in the last paragraph if @code{down-mouse-1} is bound to a
 command whose name has the property @code{mouse-1-menu-command}.
 
+@cindex pinch-to-zoom touchscreen gesture translation
+When a second touch point is registered as a touch point is already
+being translated, gesture translation is terminated, and the distance
+from the second touch point (the @dfn{ancillary tool}) to the first is
+measured.  Subsequent motion from either of those touch points will
+yield @code{touchscreen-pinch} events incorporating the ratio formed
+by the distance between their new positions and the distance measured
+at the outset, as illustrated in the following table.
+
 @cindex touchscreen gesture events
 If touch gestures are detected during translation, one of the
 following input events may be generated:
@@ -2197,6 +2207,30 @@ This event is sent upon the start of a touch sequence resulting in the
 continuation of a ``drag-to-select'' gesture (subject to the
 aformentioned user option) with @var{posn} set to the position list of
 the initial @code{touchscreen-begin} event within that touch sequence.
+
+@cindex @code{touchscreen-pinch} event
+@item (touchscreen-pinch @var{posn} @var{ratio} @var{pan-x} @var{pan-y})
+This event is delivered upon significant changes to the positions of
+either active touch point when an ancillary tool is active.
+
+@var{posn} is a mouse position list for the midpoint of a line drawn
+from the ancillary tool to the other touch point being observed.
+
+@var{ratio} is the distance between both touch points being observed
+divided by that distance when the ancillary point was first
+registered; which is to say, the scale of the ``pinch'' gesture.
+
+@var{pan-x} and @var{pan-y} are the difference between the pixel
+position of @var{posn} and this position within the last event
+delivered appertaining to this series of touch events, or in the case
+that no such event exists, the centerpoint between both touch points
+when the ancillary tool was first registered.
+
+Such events are sent when the magnitude of the changes they represent
+will yield a @code{ratio} which differs by more than @code{0.2} from
+that in the previous event, or the sum of @var{pan-x} and @var{pan-y}
+will surpass half the frame's character width in pixels (@pxref{Frame
+Font}).
 @end table
 
 @cindex handling touch screen events
index d7a37a8d57f30f76140f6a9207031c8315f751aa..013f09cb756acd4681aaba476391fbc63739224c 100644 (file)
@@ -918,8 +918,8 @@ public final class EmacsWindow extends EmacsHandleObject
           it in the map.  */
        pointerIndex = event.getActionIndex ();
        pointerID = event.getPointerId (pointerIndex);
-       coordinate = new Coordinate ((int) event.getX (0),
-                                    (int) event.getY (0),
+       coordinate = new Coordinate ((int) event.getX (pointerIndex),
+                                    (int) event.getY (pointerIndex),
                                     buttonForEvent (event),
                                     pointerID);
        pointerMap.put (pointerID, coordinate);
index 2e5a88da071609123bd15a55fba664ba62a2dd90..605ae475257ed5bd906a1a669e53b3b484b8a7f5 100644 (file)
 
 (defvar touch-screen-current-tool nil
   "The touch point currently being tracked, or nil.
-If non-nil, this is a list of nine elements: the ID of the touch
+If non-nil, this is a list of ten elements: the ID of the touch
 point being tracked, the window where the touch began, a cons
-containing the last known position of the touch point, relative
+holding the last registered position of the touch point, relative
 to that window, a field used to store data while tracking the
-touch point, the initial position of the touchpoint, and another
-four fields to used store data while tracking the touch point.
+touch point, the initial position of the touchpoint, another four
+fields to used store data while tracking the touch point, and the
+last known position of the touch point.
+
 See `touch-screen-handle-point-update' and
 `touch-screen-handle-point-up' for the meanings of the fourth
-element.")
+element.
+
+The third and last elements differ in that the former is not
+modified until after a gesture is recognized in reaction to an
+update, whereas the latter is updated upon each apposite
+`touchscreen-update' event.")
+
+(defvar touch-screen-aux-tool nil
+  "The ancillary tool being tracked, or nil.
+If non-nil, this is a vector of eight elements: the ID of the
+touch point being tracked, the window where the touch began, a
+cons holding the initial position of the touch point, and the
+last known position of the touch point, all in the same format as
+in `touch-screen-current-tool', the distance in pixels between
+the current tool and the aformentioned initial position, the
+center of the line formed between those two points, the ratio
+between the present distance between both tools and the aforesaid
+initial distance when a pinch gesture was last sent, and an
+element into which commands can save data particular to a tool.
+
+The ancillary tool is a second tool whose movement is interpreted
+in unison with that of the current tool to recognize gestures
+comprising the motion of both such as \"pinch\" gestures, in
+which the text scale is adjusted in proportion to the distance
+between both tools.")
 
 (defvar touch-screen-set-point-commands '(mouse-set-point)
   "List of commands known to set the point.
@@ -844,6 +870,68 @@ keeping the bounds of the region intact, and set up state for
 
 \f
 
+;; Pinch gesture.
+
+(defvar text-scale-mode)
+(defvar text-scale-mode-amount)
+(defvar text-scale-mode-step)
+
+(defun touch-screen-scroll-point-to-y (target-point target-y)
+  "Move the row surrounding TARGET-POINT to TARGET-Y.
+Scroll the current window such that the position of TARGET-POINT
+within it on the Y axis approaches TARGET-Y."
+  (condition-case nil
+      (let* ((last-point (point))
+            (current-y (cadr (pos-visible-in-window-p target-point
+                                                       nil t)))
+            (direction (if (if current-y
+                                (< target-y current-y)
+                             (< (window-start) target-point))
+                           -1 1)))
+       (while (< 0 (* direction (if current-y
+                                    (- target-y current-y)
+                                  (- (window-start) target-point))))
+         (scroll-down direction)
+         (setq last-point (point))
+         (setq current-y (cadr (pos-visible-in-window-p target-point nil t))))
+       (unless (and (< direction 0) current-y)
+         (scroll-up direction)
+         (goto-char last-point)))
+    ;; Ignore BOB and EOB.
+    ((beginning-of-buffer end-of-buffer) nil)))
+
+(defun touch-screen-pinch (event)
+  "Scroll the window in the touchscreen-pinch event EVENT.
+Pan the display by the pan deltas in EVENT, and adjust the
+text scale by the ratio therein."
+  (interactive "e")
+  (require 'face-remap)
+  (let* ((posn (cadr event))
+         (window (posn-window posn))
+         (current-scale (if text-scale-mode
+                            text-scale-mode-amount
+                          0))
+         (start-scale (or (aref touch-screen-aux-tool 7)
+                          (aset touch-screen-aux-tool 7
+                                current-scale)))
+         (scale (nth 2 event)))
+    (with-selected-window window
+      ;; Set the text scale.
+      (text-scale-set (+ start-scale
+                         (round (log scale text-scale-mode-step))))
+      ;; Subsequently move the row which was at the centrum to its Y
+      ;; position.  TODO: pan by the deltas in EVENT when the text
+      ;; scale has not changed, and hscroll to the centrum as well.
+      (when (and (not (eq current-scale
+                          text-scale-mode-amount))
+                 (posn-point posn))
+        (touch-screen-scroll-point-to-y (posn-point posn)
+                                        (cdr (posn-x-y posn)))))))
+
+(define-key global-map [touchscreen-pinch] #'touch-screen-pinch)
+
+\f
+
 ;; Touch screen event translation.  The code here translates raw touch
 ;; screen events into `touchscreen-scroll' events and mouse events in
 ;; a ``DWIM'' fashion, consulting the keymaps at the position of the
@@ -886,6 +974,11 @@ Perform the editing operations or throw to the input translation
 function with an input event tied to any gesture that is
 recognized.
 
+Update the tenth element of `touch-screen-current-tool' with
+POINT relative to the window it was placed on.  Update the third
+element in like fashion, once sufficient motion has accumulated
+that an event is generated.
+
 POINT must be the touch point currently being tracked as
 `touch-screen-current-tool'.
 
@@ -899,7 +992,7 @@ has moved relative to its previous position in the X and Y axes.
 
 If the fourth element of `touchscreen-current-tool' is `scroll',
 then generate a `touchscreen-scroll' event with the window that
-qPOINT was initially placed upon, and pixel deltas describing how
+POINT was initially placed upon, and pixel deltas describing how
 much point has moved relative to its previous position in the X
 and Y axes.
 
@@ -918,16 +1011,17 @@ If the fourth element of `touch-screen-current-tool' is
 
 If the fourth element of `touch-screen-current-tool' is `drag',
 then move point to the position of POINT."
-  (let ((window (nth 1 touch-screen-current-tool))
-        (what (nth 3 touch-screen-current-tool)))
+  (let* ((window (nth 1 touch-screen-current-tool))
+         (what (nth 3 touch-screen-current-tool))
+         (posn (cdr point))
+         ;; Now get the position of X and Y relative to WINDOW.
+         (relative-xy
+         (touch-screen-relative-xy posn window)))
+    ;; Update the 10th field of the tool list with RELATIVE-XY.
+    (setcar (nthcdr 9 touch-screen-current-tool) relative-xy)
     (cond ((null what)
-           (let* ((posn (cdr point))
-                  (last-posn (nth 2 touch-screen-current-tool))
+           (let* ((last-posn (nth 2 touch-screen-current-tool))
                   (original-posn (nth 4 touch-screen-current-tool))
-                  ;; Now get the position of X and Y relative to
-                  ;; WINDOW.
-                  (relative-xy
-                   (touch-screen-relative-xy posn window))
                   (col (and (not (posn-area original-posn))
                             (car (posn-col-row original-posn
                                                (posn-window posn)))))
@@ -966,12 +1060,7 @@ then move point to the position of POINT."
            (when touch-screen-current-timer
              (cancel-timer touch-screen-current-timer)
              (setq touch-screen-current-timer nil))
-           (let* ((posn (cdr point))
-                  (last-posn (nth 2 touch-screen-current-tool))
-                  ;; Now get the position of X and Y relative to
-                  ;; WINDOW.
-                  (relative-xy
-                   (touch-screen-relative-xy posn window))
+           (let* ((last-posn (nth 2 touch-screen-current-tool))
                   (diff-x (- (car last-posn) (car relative-xy)))
                   (diff-y (- (cdr last-posn) (cdr relative-xy))))
              (setcar (nthcdr 3 touch-screen-current-tool)
@@ -1014,6 +1103,100 @@ then move point to the position of POINT."
              ;; Generate a (touchscreen-drag POSN) event.
              (throw 'input-event (list 'touchscreen-drag posn)))))))
 
+(defsubst touch-screen-distance (pos1 pos2)
+  "Compute the distance in pixels between POS1 and POS2.
+Each is a coordinate whose car and cdr are respectively its X and
+Y values."
+  (let ((v1 (- (cdr pos2) (cdr pos1)))
+        (v2 (- (car pos2) (car pos1))))
+    (abs (sqrt (+ (* v1 v1) (* v2 v2))))))
+
+(defsubst touch-screen-centrum (pos1 pos2)
+  "Compute the center of a line between the points POS1 and POS2.
+Each, and value, is a coordinate whose car and cdr are
+respectively its X and Y values."
+  (let ((v1 (+ (cdr pos2) (cdr pos1)))
+        (v2 (+ (car pos2) (car pos1))))
+    (cons (/ v2 2) (/ v1 2))))
+
+(defun touch-screen-handle-aux-point-update (point number)
+  "Notice that a point being observed has moved.
+Register motion from either the current or ancillary tool while
+an ancillary tool is present.
+
+POINT must be the cdr of an element of a `touchscreen-update'
+event's list of touch points.  NUMBER must be its touch ID.
+
+Calculate the distance between POINT's position and that of the
+other tool (which is to say the ancillary tool of POINT is the
+current tool, and vice versa).  Compare this distance to that
+between both points at the time they were placed on the screen,
+and signal a pinch event to adjust the text scale and scroll the
+window by the factor so derived.  Such events are lists formed as
+so illustrated:
+
+    (touchscreen-pinch CENTRUM RATIO PAN-X PAN-Y)
+
+in which CENTRUM is a posn representing the midpoint of a line
+between the present locations of both tools, PAN-X is the number
+of pixels on the X axis that centrum has moved since the last
+event, and PAN-Y is that on the Y axis."
+  (let (this-point-position
+        other-point-position
+        (window (cadr touch-screen-current-tool)))
+    (when (windowp window)
+      (if (eq number (aref touch-screen-aux-tool 0))
+          (progn
+            ;; The point pressed is the ancillary tool.  Set
+            ;; other-point-position to that of the current tool.
+            (setq other-point-position (nth 9 touch-screen-current-tool))
+            ;; Update the position within touch-screen-aux-tool.
+            (aset touch-screen-aux-tool 3
+                  (setq this-point-position
+                        (touch-screen-relative-xy point window))))
+        (setq other-point-position (aref touch-screen-aux-tool 3))
+        (setcar (nthcdr 2 touch-screen-current-tool)
+                (setq this-point-position
+                      (touch-screen-relative-xy point window)))
+        (setcar (nthcdr 9 touch-screen-current-tool)
+                this-point-position))
+      ;; Now compute, and take the absolute of, this distance.
+      (let ((distance (touch-screen-distance this-point-position
+                                             other-point-position))
+            (centrum (touch-screen-centrum this-point-position
+                                           other-point-position))
+            (initial-distance (aref touch-screen-aux-tool 4))
+            (initial-centrum (aref touch-screen-aux-tool 5)))
+        (let* ((ratio (/ distance initial-distance))
+               (diff (abs (- ratio (aref touch-screen-aux-tool 6))))
+               (centrum-diff (+ (abs (- (car initial-centrum)
+                                        (car centrum)))
+                                (abs (- (cdr initial-centrum)
+                                        (cdr centrum))))))
+          ;; If the difference in ratio has surpassed a threshold of
+          ;; 0.2 or the centrum difference exceeds the frame's char
+          ;; width, send a touchscreen-pinch event with this
+          ;; information and update that saved in
+          ;; touch-screen-aux-tool.
+          (when (or (> diff 0.2)
+                    (> centrum-diff
+                       (/ (frame-char-width) 2)))
+            (aset touch-screen-aux-tool 5 centrum)
+            (aset touch-screen-aux-tool 6 ratio)
+            (throw 'input-event (list 'touchscreen-pinch
+                                      (if (or (<= (car centrum) 0)
+                                              (<= (cdr centrum) 0))
+                                          (list window centrum nil nil nil
+                                                nil nil nil)
+                                        (posn-at-x-y (car centrum)
+                                                     (cdr centrum)
+                                                     window))
+                                      ratio
+                                      (- (car centrum)
+                                         (car initial-centrum))
+                                      (- (cdr centrum)
+                                         (cdr initial-centrum))))))))))
+
 (defun touch-screen-window-selection-changed (frame)
   "Notice that FRAME's selected window has changed.
 Cancel any timer that is supposed to hide the keyboard in
@@ -1037,6 +1220,13 @@ POINT should be the point currently tracked as
 PREFIX should be a virtual function key used to look up key
 bindings.
 
+If an ancillary touch point is being observed, transfer touch
+information from `touch-screen-aux-tool' to
+`touch-screen-current-tool' and set it to nil, thereby resuming
+gesture recognition with that tool replacing the tool removed.
+
+Otherwise:
+
 If the fourth element of `touch-screen-current-tool' is nil or
 `restart-drag', move point to the position of POINT, selecting
 the window under POINT as well, and deactivate the mark; if there
@@ -1061,140 +1251,161 @@ If the command being executed is listed in
 `touch-screen-set-point-commands' also display the on-screen
 keyboard if the current buffer and the character at the new point
 is not read-only."
-  (let ((what (nth 3 touch-screen-current-tool))
-        (posn (cdr point)) window point)
-    (cond ((or (null what)
-               ;; If dragging has been restarted but the touch point
-               ;; hasn't been moved, translate the sequence into a
-               ;; regular mouse click.
-               (eq what 'restart-drag))
-           (when (windowp (posn-window posn))
-             (setq point (posn-point posn)
-                   window (posn-window posn))
-             ;; Select the window that was tapped given that it isn't
-             ;; an inactive minibuffer window.
-             (when (or (not (eq window
-                                (minibuffer-window
-                                 (window-frame window))))
-                       (minibuffer-window-active-p window))
-               (select-window window))
-             ;; Now simulate a mouse click there.  If there is a link
-             ;; or a button, use mouse-2 to push it.
-             (let* ((event (list (if (or (mouse-on-link-p posn)
-                                         (and point (button-at point)))
-                                     'mouse-2
-                                   'mouse-1)
-                                 posn))
-                    ;; Look for the command bound to this event.
-                    (command (key-binding (if prefix
-                                              (vector prefix
-                                                      (car event))
-                                            (vector (car event)))
-                                          t nil posn)))
-               (deactivate-mark)
-               (when point
-                 ;; This is necessary for following links.
-                 (goto-char point))
-               ;; Figure out if the on screen keyboard needs to be
-               ;; displayed.
-               (when command
-                 (if (memq command touch-screen-set-point-commands)
-                     (if touch-screen-translate-prompt
-                         ;; When a `mouse-set-point' command is
-                         ;; encountered and
-                         ;; `touch-screen-handle-touch' is being
-                         ;; called from the keyboard command loop,
-                         ;; call it immediately so that point is set
-                         ;; prior to the on screen keyboard being
-                         ;; displayed.
-                         (call-interactively command nil
-                                             (vector event))
-                       (if (and (or (not buffer-read-only)
-                                    touch-screen-display-keyboard)
-                                ;; Detect the splash screen and avoid
-                                ;; displaying the on screen keyboard
-                                ;; there.
-                                (not (equal (buffer-name) "*GNU Emacs*")))
-                           ;; Once the on-screen keyboard has been
-                           ;; opened, add
-                           ;; `touch-screen-window-selection-changed'
-                           ;; as a window selection change function
-                           ;; This then prevents it from being hidden
-                           ;; after exiting the minibuffer.
-                           (progn
-                             (add-hook 'window-selection-change-functions
-                                       #'touch-screen-window-selection-changed)
-                             (frame-toggle-on-screen-keyboard (selected-frame)
-                                                              nil))
-                         ;; Otherwise, hide the on screen keyboard
-                         ;; now.
-                         (frame-toggle-on-screen-keyboard (selected-frame) t))
-                       ;; But if it's being called from `describe-key'
-                       ;; or some such, return it as a key sequence.
-                       (throw 'input-event event)))
-                 ;; If not, return the event.
-                 (throw 'input-event event)))))
-          ((eq what 'mouse-drag)
-           ;; Generate a corresponding `mouse-1' event.
-           (let* ((new-window (posn-window posn))
-                  (new-point (posn-point posn))
-                  (old-posn (nth 4 touch-screen-current-tool))
-                  (old-window (posn-window posn))
-                  (old-point (posn-point posn)))
+  (if touch-screen-aux-tool
+      (progn
+        (let ((posn (cdr point))
+              (window (cadr touch-screen-current-tool))
+              (point-no (aref touch-screen-aux-tool 0)))
+          ;; Replace the current position of touch-screen-current-tool
+          ;; with posn and its number with point-no, but leave other
+          ;; information (such as its starting position) intact: this
+          ;; touchpoint is meant to continue the gesture interrupted
+          ;; by the removal of the last, not to commence a new one.
+          (setcar touch-screen-current-tool point-no)
+          (setcar (nthcdr 2 touch-screen-current-tool)
+                  (touch-screen-relative-xy posn window))
+          (setcar (nthcdr 9 touch-screen-current-tool)
+                  (touch-screen-relative-xy posn window)))
+        (setq touch-screen-aux-tool nil))
+    (let ((what (nth 3 touch-screen-current-tool))
+          (posn (cdr point)) window point)
+      (cond ((or (null what)
+                 ;; If dragging has been restarted but the touch point
+                 ;; hasn't been moved, translate the sequence into a
+                 ;; regular mouse click.
+                 (eq what 'restart-drag))
+             (when (windowp (posn-window posn))
+               (setq point (posn-point posn)
+                     window (posn-window posn))
+               ;; Select the window that was tapped given that it
+               ;; isn't an inactive minibuffer window.
+               (when (or (not (eq window
+                                  (minibuffer-window
+                                   (window-frame window))))
+                         (minibuffer-window-active-p window))
+                 (select-window window))
+               ;; Now simulate a mouse click there.  If there is a
+               ;; link or a button, use mouse-2 to push it.
+               (let* ((event (list (if (or (mouse-on-link-p posn)
+                                           (and point (button-at point)))
+                                       'mouse-2
+                                     'mouse-1)
+                                   posn))
+                      ;; Look for the command bound to this event.
+                      (command (key-binding (if prefix
+                                                (vector prefix
+                                                        (car event))
+                                              (vector (car event)))
+                                            t nil posn)))
+                 (deactivate-mark)
+                 (when point
+                   ;; This is necessary for following links.
+                   (goto-char point))
+                 ;; Figure out if the on screen keyboard needs to be
+                 ;; displayed.
+                 (when command
+                   (if (memq command touch-screen-set-point-commands)
+                       (if touch-screen-translate-prompt
+                           ;; When a `mouse-set-point' command is
+                           ;; encountered and
+                           ;; `touch-screen-handle-touch' is being
+                           ;; called from the keyboard command loop,
+                           ;; call it immediately so that point is set
+                           ;; prior to the on screen keyboard being
+                           ;; displayed.
+                           (call-interactively command nil
+                                               (vector event))
+                         (if (and (or (not buffer-read-only)
+                                      touch-screen-display-keyboard)
+                                  ;; Detect the splash screen and
+                                  ;; avoid displaying the on screen
+                                  ;; keyboard there.
+                                  (not (equal (buffer-name) "*GNU Emacs*")))
+                             ;; Once the on-screen keyboard has been
+                             ;; opened, add
+                             ;; `touch-screen-window-selection-changed'
+                             ;; as a window selection change function
+                             ;; This then prevents it from being
+                             ;; hidden after exiting the minibuffer.
+                             (progn
+                               (add-hook
+                                'window-selection-change-functions
+                                #'touch-screen-window-selection-changed)
+                               (frame-toggle-on-screen-keyboard
+                                (selected-frame) nil))
+                           ;; Otherwise, hide the on screen keyboard
+                           ;; now.
+                           (frame-toggle-on-screen-keyboard (selected-frame)
+                                                            t))
+                         ;; But if it's being called from `describe-key'
+                         ;; or some such, return it as a key sequence.
+                         (throw 'input-event event)))
+                   ;; If not, return the event.
+                   (throw 'input-event event)))))
+            ((eq what 'mouse-drag)
+             ;; Generate a corresponding `mouse-1' event.
+             (let* ((new-window (posn-window posn))
+                    (new-point (posn-point posn))
+                    (old-posn (nth 4 touch-screen-current-tool))
+                    (old-window (posn-window posn))
+                    (old-point (posn-point posn)))
+               (throw 'input-event
+                      ;; If the position of the touch point hasn't
+                      ;; changed, or it doesn't start or end on a
+                      ;; window...
+                      (if (and (not old-point) (not new-point))
+                          ;; Should old-point and new-point both equal
+                          ;; nil, compare the posn areas and nominal
+                          ;; column position.  If either are
+                          ;; different, generate a drag event.
+                          (let ((new-col-row (posn-col-row posn))
+                                (new-area (posn-area posn))
+                                (old-col-row (posn-col-row old-posn))
+                                (old-area (posn-area old-posn)))
+                            (if (and (equal new-col-row old-col-row)
+                                     (eq new-area old-area))
+                                ;; ... generate a mouse-1 event...
+                                (list 'mouse-1 posn)
+                              ;; ... otherwise, generate a
+                              ;; drag-mouse-1 event.
+                              (list 'drag-mouse-1 old-posn posn)))
+                        (if (and (eq new-window old-window)
+                                 (eq new-point old-point)
+                                 (windowp new-window)
+                                 (windowp old-window))
+                            ;; ... generate a mouse-1 event...
+                            (list 'mouse-1 posn)
+                          ;; ... otherwise, generate a drag-mouse-1
+                          ;; event.
+                          (list 'drag-mouse-1 old-posn posn))))))
+            ((eq what 'mouse-1-menu)
+             ;; Generate a `down-mouse-1' event at the position the tap
+             ;; took place.
              (throw 'input-event
-                    ;; If the position of the touch point hasn't
-                    ;; changed, or it doesn't start or end on a
-                    ;; window...
-                    (if (and (not old-point) (not new-point))
-                        ;; Should old-point and new-point both equal
-                        ;; nil, compare the posn areas and nominal
-                        ;; column position.  If either are different,
-                        ;; generate a drag event.
-                        (let ((new-col-row (posn-col-row posn))
-                              (new-area (posn-area posn))
-                              (old-col-row (posn-col-row old-posn))
-                              (old-area (posn-area old-posn)))
-                          (if (and (equal new-col-row old-col-row)
-                                   (eq new-area old-area))
-                              ;; ... generate a mouse-1 event...
-                              (list 'mouse-1 posn)
-                            ;; ... otherwise, generate a drag-mouse-1 event.
-                            (list 'drag-mouse-1 old-posn posn)))
-                      (if (and (eq new-window old-window)
-                               (eq new-point old-point)
-                               (windowp new-window)
-                               (windowp old-window))
-                          ;; ... generate a mouse-1 event...
-                          (list 'mouse-1 posn)
-                        ;; ... otherwise, generate a drag-mouse-1 event.
-                        (list 'drag-mouse-1 old-posn posn))))))
-          ((eq what 'mouse-1-menu)
-           ;; Generate a `down-mouse-1' event at the position the tap
-           ;; took place.
-           (throw 'input-event
-                  (list 'down-mouse-1
-                        (nth 4 touch-screen-current-tool))))
-          ((or (eq what 'drag)
-               ;; Merely initiating a drag is sufficient to select a
-               ;; word if word selection is enabled.
-               (eq what 'held))
-           ;; Display the on screen keyboard if the region is now
-           ;; active.  Check this within the window where the tool was
-           ;; first place.
-           (setq window (nth 1 touch-screen-current-tool))
-           (when window
-             (with-selected-window window
-               (when (and (region-active-p)
-                          (not buffer-read-only))
-                 ;; Once the on-screen keyboard has been opened, add
-                 ;; `touch-screen-window-selection-changed' as a window
-                 ;; selection change function This then prevents it from
-                 ;; being hidden after exiting the minibuffer.
-                 (progn
-                   (add-hook 'window-selection-change-functions
-                             #'touch-screen-window-selection-changed)
-                   (frame-toggle-on-screen-keyboard (selected-frame)
-                                                    nil)))))))))
+                    (list 'down-mouse-1
+                          (nth 4 touch-screen-current-tool))))
+            ((or (eq what 'drag)
+                 ;; Merely initiating a drag is sufficient to select a
+                 ;; word if word selection is enabled.
+                 (eq what 'held))
+             ;; Display the on screen keyboard if the region is now
+             ;; active.  Check this within the window where the tool
+             ;; was first place.
+             (setq window (nth 1 touch-screen-current-tool))
+             (when window
+               (with-selected-window window
+                 (when (and (region-active-p)
+                            (not buffer-read-only))
+                   ;; Once the on-screen keyboard has been opened, add
+                   ;; `touch-screen-window-selection-changed' as a
+                   ;; window selection change function.  This then
+                   ;; prevents it from being hidden after exiting the
+                   ;; minibuffer.
+                   (progn
+                     (add-hook 'window-selection-change-functions
+                               #'touch-screen-window-selection-changed)
+                     (frame-toggle-on-screen-keyboard (selected-frame)
+                                                      nil))))))))))
 
 (defun touch-screen-handle-touch (event prefix &optional interactive)
   "Handle a single touch EVENT, and perform associated actions.
@@ -1234,81 +1445,126 @@ the place of EVENT within the key sequence being translated, or
         (when touch-screen-current-timer
           (cancel-timer touch-screen-current-timer)
           (setq touch-screen-current-timer nil))
-        ;; Replace any previously ongoing gesture.  If POSITION has no
-        ;; window or position, make it nil instead.
-        (setq tool-list (and (windowp window)
-                             (list touchpoint window
-                                   (posn-x-y position)
-                                   nil position
-                                   nil nil nil nil))
-              touch-screen-current-tool tool-list)
-
-        ;; Select the window underneath the event as the checks below
-        ;; will look up keymaps and markers inside its buffer.
-        (save-selected-window
-          ;; Check if `touch-screen-extend-selection' is enabled, the
-          ;; tap lies on the point or the mark, and the region is
-          ;; active.  If that's the case, set the fourth element of
-          ;; `touch-screen-current-tool' to `restart-drag', then
-          ;; generate a `touchscreen-restart-drag' event.
-          (when tool-list
-            ;; tool-list is always non-nil where the selected window
-            ;; matters.
-            (select-window window)
-            (when (and touch-screen-extend-selection
-                       (or (eq point (point))
-                           (eq point (mark)))
-                       (region-active-p)
-                       ;; Only restart drag-to-select if the tap falls
-                       ;; on the same row as the selection.  This
-                       ;; prevents dragging from starting if the tap
-                       ;; is below the last window line with text and
-                       ;; `point' is at ZV, as the user most likely
-                       ;; meant to scroll the window instead.
-                       (when-let* ((posn-point (posn-at-point point))
-                                   (posn-row (cdr (posn-col-row posn-point))))
-                         (eq (cdr (posn-col-row position)) posn-row)))
-              ;; Indicate that a drag is about to restart.
-              (setcar (nthcdr 3 tool-list) 'restart-drag)
-              ;; Generate the `restart-drag' event.
-              (throw 'input-event (list 'touchscreen-restart-drag
-                                        position))))
-          ;; Determine if there is a command bound to `down-mouse-1'
-          ;; at the position of the tap and that command is not a
-          ;; command whose functionality is replaced by the long-press
-          ;; mechanism.  If so, set the fourth element of
-          ;; `touch-screen-current-tool' to `mouse-drag' and generate
-          ;; an emulated `mouse-1' event.
-          ;;
-          ;; If the command in question is a keymap, set that element
-          ;; to `mouse-1-menu' instead of `mouse-drag', and don't
-          ;; generate a `down-mouse-1' event immediately.  Instead,
-          ;; wait for the touch point to be released.
-          (if (and tool-list
-                   (and (setq binding
-                              (key-binding (if prefix
-                                               (vector prefix
-                                                       'down-mouse-1)
-                                             [down-mouse-1])
-                                           t nil position))
-                        (not (and (symbolp binding)
-                                  (get binding 'ignored-mouse-command)))))
-              (if (or (keymapp binding)
-                      (and (symbolp binding)
-                           (get binding 'mouse-1-menu-command)))
-                  ;; binding is a keymap, or a command that does
-                  ;; almost the same thing.  If a `mouse-1' event is
-                  ;; generated after the keyboard command loop
-                  ;; displays it as a menu, that event could cause
-                  ;; unwanted commands to be run.  Set what to
-                  ;; `mouse-1-menu' instead and wait for the up event
-                  ;; to display the menu.
-                  (setcar (nthcdr 3 tool-list) 'mouse-1-menu)
-                (progn (setcar (nthcdr 3 tool-list) 'mouse-drag)
-                       (throw 'input-event (list 'down-mouse-1 position))))
-            (and point
-                 ;; Start the long-press timer.
-                 (touch-screen-handle-timeout nil))))))
+        ;; If a tool already exists...
+        (if touch-screen-current-tool
+            ;; Then record this tool as the ``auxiliary tool''.
+            ;; Updates to the auxiliary tool are considered in unison
+            ;; with those to the current tool; the distance between
+            ;; both tools is measured and compared with that when the
+            ;; auxiliary tool was first pressed, then interpreted as a
+            ;; scale by which to adjust text within the current tool's
+            ;; window.
+            (progn
+              ;; Set touch-screen-aux-tool as is proper.  Mind that
+              ;; the last field is always relative to the current
+              ;; tool's window.
+              (let* ((window (nth 1 touch-screen-current-tool))
+                     (relative-x-y (touch-screen-relative-xy position
+                                                             window))
+                     (initial-pos (nth 4 touch-screen-current-tool))
+                     (initial-x-y (touch-screen-relative-xy initial-pos
+                                                            window))
+                     computed-distance computed-centrum)
+                ;; Calculate the distance and centrum from this point
+                ;; to the initial position of the current tool.
+                (setq computed-distance (touch-screen-distance relative-x-y
+                                                               initial-x-y)
+                      computed-centrum (touch-screen-centrum relative-x-y
+                                                             initial-x-y))
+                ;; If computed-distance is zero, ignore this tap.
+                (unless (zerop computed-distance)
+                  (setq touch-screen-aux-tool (vector touchpoint window
+                                                      position relative-x-y
+                                                      computed-distance
+                                                      computed-centrum
+                                                      1.0 nil)))
+                ;; When an auxiliary tool is pressed, any gesture
+                ;; previously in progress must be terminated, so long
+                ;; as it represents a gesture recognized from the
+                ;; current tool's motion rather than ones detected by
+                ;; this function from circumstances surrounding its
+                ;; first press, such as the presence of a menu or
+                ;; down-mouse-1 button beneath its first press.
+                (unless (memq (nth 3 touch-screen-current-tool)
+                              '(mouse-drag mouse-1-menu))
+                  (setcar (nthcdr 3 touch-screen-current-tool) nil))))
+          ;; Replace any previously ongoing gesture.  If POSITION has no
+          ;; window or position, make it nil instead.
+          (setq tool-list (and (windowp window)
+                               (list touchpoint window
+                                     (posn-x-y position)
+                                     nil position
+                                     nil nil nil nil
+                                     (posn-x-y position)))
+                touch-screen-current-tool tool-list)
+          ;; Select the window underneath the event as the checks below
+          ;; will look up keymaps and markers inside its buffer.
+          (save-selected-window
+            ;; Check if `touch-screen-extend-selection' is enabled,
+            ;; the tap lies on the point or the mark, and the region
+            ;; is active.  If that's the case, set the fourth element
+            ;; of `touch-screen-current-tool' to `restart-drag', then
+            ;; generate a `touchscreen-restart-drag' event.
+            (when tool-list
+              ;; tool-list is always non-nil where the selected window
+              ;; matters.
+              (select-window window)
+              (when (and touch-screen-extend-selection
+                         (or (eq point (point))
+                             (eq point (mark)))
+                         (region-active-p)
+                         ;; Only restart drag-to-select if the tap
+                         ;; falls on the same row as the selection.
+                         ;; This prevents dragging from starting if
+                         ;; the tap is below the last window line with
+                         ;; text and `point' is at ZV, as the user
+                         ;; most likely meant to scroll the window
+                         ;; instead.
+                         (when-let* ((posn-point (posn-at-point point))
+                                     (posn-row (cdr
+                                                (posn-col-row posn-point))))
+                           (eq (cdr (posn-col-row position)) posn-row)))
+                ;; Indicate that a drag is about to restart.
+                (setcar (nthcdr 3 tool-list) 'restart-drag)
+                ;; Generate the `restart-drag' event.
+                (throw 'input-event (list 'touchscreen-restart-drag
+                                          position))))
+            ;; Determine if there is a command bound to `down-mouse-1'
+            ;; at the position of the tap and that command is not a
+            ;; command whose functionality is replaced by the
+            ;; long-press mechanism.  If so, set the fourth element of
+            ;; `touch-screen-current-tool' to `mouse-drag' and
+            ;; generate an emulated `mouse-1' event.
+            ;;
+            ;; If the command in question is a keymap, set that
+            ;; element to `mouse-1-menu' instead of `mouse-drag', and
+            ;; don't generate a `down-mouse-1' event immediately.
+            ;; Instead, wait for the touch point to be released.
+            (if (and tool-list
+                     (and (setq binding
+                                (key-binding (if prefix
+                                                 (vector prefix
+                                                         'down-mouse-1)
+                                               [down-mouse-1])
+                                             t nil position))
+                          (not (and (symbolp binding)
+                                    (get binding 'ignored-mouse-command)))))
+                (if (or (keymapp binding)
+                        (and (symbolp binding)
+                             (get binding 'mouse-1-menu-command)))
+                    ;; binding is a keymap, or a command that does
+                    ;; almost the same thing.  If a `mouse-1' event is
+                    ;; generated after the keyboard command loop
+                    ;; displays it as a menu, that event could cause
+                    ;; unwanted commands to be run.  Set what to
+                    ;; `mouse-1-menu' instead and wait for the up
+                    ;; event to display the menu.
+                    (setcar (nthcdr 3 tool-list) 'mouse-1-menu)
+                  (progn (setcar (nthcdr 3 tool-list) 'mouse-drag)
+                         (throw 'input-event (list 'down-mouse-1 position))))
+              (and point
+                   ;; Start the long-press timer.
+                   (touch-screen-handle-timeout nil)))))))
      ((eq (car event) 'touchscreen-update)
       (unless touch-screen-current-tool
         ;; If a stray touchscreen-update event arrives (most likely
@@ -1320,7 +1576,17 @@ the place of EVENT within the key sequence being translated, or
       (let ((new-point (assq (car touch-screen-current-tool)
                              (cadr event))))
         (when new-point
-          (touch-screen-handle-point-update new-point))))
+          (if touch-screen-aux-tool
+              (touch-screen-handle-aux-point-update (cdr new-point)
+                                                    (car new-point))
+            (touch-screen-handle-point-update new-point))))
+      ;; Check for updates to any ancillary point being monitored.
+      (when touch-screen-aux-tool
+        (let ((new-point (assq (aref touch-screen-aux-tool 0)
+                               (cadr event))))
+          (when new-point
+            (touch-screen-handle-aux-point-update (cdr new-point)
+                                                  (car new-point))))))
      ((eq (car event) 'touchscreen-end)
       ;; A tool has been removed from the screen.  If it is the tool
       ;; currently being tracked, clear `touch-screen-current-tool'.
@@ -1339,6 +1605,21 @@ the place of EVENT within the key sequence being translated, or
           ;; Make sure the tool list is cleared even if
           ;; `touch-screen-handle-point-up' throws.
           (setq touch-screen-current-tool nil)))
+      ;; If it is rather the ancillary tool, delete its vector.  No
+      ;; further action is required, for the next update received will
+      ;; resume regular gesture recognition.
+      ;;
+      ;; The what field in touch-screen-current-tool is cleared when
+      ;; the ancillary tool is pressed, so gesture recognition will
+      ;; commence with a clean slate, save for when the first touch
+      ;; landed atop a menu or some other area down-mouse-1 was bound.
+      ;;
+      ;; Gesture recognition will be inhibited in that case, so that
+      ;; menu bar or mouse motion events are generated in its place as
+      ;; they would be were no ancillary tool ever pressed.
+      (when (and touch-screen-aux-tool
+                 (eq (caadr event) (aref touch-screen-aux-tool 0)))
+        (setq touch-screen-aux-tool nil))
       ;; Throw to the key translation function.
       (throw 'input-event nil)))))
 
index 1593cac36ba30b1bf61910d81be340ef1885816a..cfb64cd69a052ab03a699f5e925721fd67cb7d87 100644 (file)
@@ -1377,7 +1377,7 @@ handle_one_android_event (struct android_display_info *dpyinfo,
        {
          /* Simply update the tool position and send an update.  */
          touchpoint->x = event->touch.x;
-         touchpoint->y = event->touch.x;
+         touchpoint->y = event->touch.y;
          android_update_tools (any, &inev.ie);
          inev.ie.timestamp = event->touch.time;
 
@@ -1390,7 +1390,7 @@ handle_one_android_event (struct android_display_info *dpyinfo,
       touchpoint = xmalloc (sizeof *touchpoint);
       touchpoint->tool_id = event->touch.pointer_id;
       touchpoint->x = event->touch.x;
-      touchpoint->y = event->touch.x;
+      touchpoint->y = event->touch.y;
       touchpoint->next = FRAME_OUTPUT_DATA (any)->touch_points;
       touchpoint->tool_bar_p = false;
       FRAME_OUTPUT_DATA (any)->touch_points = touchpoint;