From: Po Lu Date: Wed, 15 Nov 2023 12:58:46 +0000 (+0800) Subject: Register ``pinch to zoom'' touch screen gestures X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=a9a8d5e95992ab63a63305e2a0b2d2b36bb2c698;p=emacs.git Register ``pinch to zoom'' touch screen gestures * 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) : 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. --- diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi index 0dd7fca41cc..e4d595caf84 100644 --- a/doc/emacs/input.texi +++ b/doc/emacs/input.texi @@ -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 diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 2518740ad3b..75685ffe5dc 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -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 diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index d7a37a8d57f..013f09cb756 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -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); diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 2e5a88da071..605ae475257 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -33,15 +33,41 @@ (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 +;; 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) + + + ;; 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))))) diff --git a/src/androidterm.c b/src/androidterm.c index 1593cac36ba..cfb64cd69a0 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -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;