From ad59d8986aee4498c0427449e024341d1a195a2d Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 16 Jan 2023 19:50:02 +0800 Subject: [PATCH] Update Android port * doc/emacs/android.texi (Android, Android Environment): Improve documentation. * doc/lispref/commands.texi (Touchscreen Events): Document changes to touchscreen support. * doc/lispref/display.texi (Defining Faces, Window Systems): * doc/lispref/frames.texi (Frame Layout, Font and Color Parameters): * doc/lispref/os.texi (System Environment): Document Android in various places. * java/org/gnu/emacs/EmacsWindow.java (figureChange): Fix crash. * lisp/loadup.el: ("touch-screen"): Load touch-screen.el. * lisp/pixel-scroll.el: Autoload two functions. * lisp/term/android-win.el: Add require 'touch-screen. * lisp/touch-screen.el (touch-screen-current-tool) (touch-screen-current-timer, touch-screen-delay) (touch-screen-relative-xy, touch-screen-handle-scroll) (touch-screen-handle-timeout, touch-screen-handle-point-update) (touch-screen-handle-point-up, touch-screen-handle-touch) (global-map, touch-screen): New file. * src/android.c (android_run_debug_thread): Fix build on 64 bit systems. (JNICALL, android_put_pixel): Likewise. (android_transform_coordinates, android_four_corners_bilinear) (android_fetch_pixel_bilinear, android_project_image_bilinear) (android_fetch_pixel_nearest_24, android_fetch_pixel_nearest_1) (android_project_image_nearest): New functions. * src/androidgui.h (struct android_transform): New structure. * src/androidterm.c (android_note_mouse_movement): Remove obsolete TODO. (android_get_scale_factor): New function. (android_draw_underwave): Scale underwave correctly. * src/dispextern.h: Support native image transforms on Android. * src/image.c (matrix_identity, matrix_rotate) (matrix_mirror_horizontal, matrix_translate): New functions. (image_set_transform): Implement native image transforms on Android. (Fimage_transforms_p): Implement on Android. * src/keyboard.c (make_lispy_event, syms_of_keyboard): Handle touch screen- menu bar events. * src/sfnt.c: Fix typo in comment. * src/sfntfont-android.c (sfntfont_android_blend, U255TO256) (sfntfont_android_put_glyphs): Avoid redundant swizzling. * src/sfntfont.c (sfntfont_lookup_char): Fix build on 64 bit systems. --- doc/emacs/android.texi | 56 ++++- doc/lispref/commands.texi | 5 + doc/lispref/display.texi | 7 +- doc/lispref/frames.texi | 3 +- doc/lispref/os.texi | 3 + java/org/gnu/emacs/EmacsWindow.java | 4 +- lisp/loadup.el | 4 + lisp/pixel-scroll.el | 2 + lisp/term/android-win.el | 1 + lisp/touch-screen.el | 322 ++++++++++++++++++++++++++++ src/android.c | 282 +++++++++++++++++++++++- src/androidgui.h | 19 ++ src/androidterm.c | 28 ++- src/dispextern.h | 5 +- src/image.c | 310 ++++++++++++++++++++++++-- src/keyboard.c | 70 ++++++ src/sfnt.c | 2 +- src/sfntfont-android.c | 55 ++--- src/sfntfont.c | 2 +- 19 files changed, 1115 insertions(+), 65 deletions(-) create mode 100644 lisp/touch-screen.el diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 57ef10de9f3..b5a91b0f98f 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -14,6 +14,7 @@ an Android device running Android 2.2 or later. * Android Startup:: Starting up Emacs on Android. * Android File System:: The Android file system. * Android Environment:: Running Emacs under Android. +* Android Fonts:: Font selection under Android. @end menu @node What is Android? @@ -293,5 +294,56 @@ This strategy works as long as one window is in the foreground. Otherwise, Emacs can only run in the background for a limited amount of time before the process is killed completely. -@c TODO: write more documentation here about what is supported and -@c what is not, and fonts. +@cindex windowing limitations, android +@cindex frame parameters, android +Due to the unusual nature of the Android windowing environment, Emacs +only supports a limited subset of GUI features. Here is a list of +known limitations, and features which are not implemented: + +@itemize @bullet +@item +The functions @code{raise-frame} and @code{lower-frame} are +non-functional, because of bugs in the window system. + +@item +Scroll bars are not supported, as they are close to useless on Android +devices. + +@item +The @code{alpha}, @code{alpha-background}, @code{z-group}, +@code{override-redirect}, @code{mouse-color}, @code{cursor-color}, +@code{cursor-type}, @code{title}, @code{wait-for-wm}, @code{sticky}, +@code{undecorated} and @code{tool-bar-position} frame parameters +(@pxref{Frame Parameters,,, elisp, the Emacs Lisp Reference Manual}) +are unsupported. + +@item +The @code{fullscreen} frame parameter is always @code{maximized} for +top-level frames. +@end itemize + +@node Android Fonts +@section Font backends and selection under Android +@cindex fonts, android + + Emacs supports two font backends under Android: they are respectively +named @code{sfnt-android} and @code{android}. + +Upon startup, Emacs enumerates all the TrueType format fonts in the +directory @file{/system/fonts}; this is where the Android system +places fonts. Emacs assumes there will always be a font named ``Droid +Sans Mono'', and then defaults to using this font. These fonts are +then rendered by the @code{sfnt-android} font driver. + +When running on Android, Emacs currently lacks support for TrueType +Container and OpenType fonts. This means that only a subset of the +fonts installed on the system are currently available to Emacs. If +you are interested in raising this limitation, please contact +@email{emacs-devel@@gnu.org}. + +If the @code{sfnt-android} font driver fails to find any fonts at all, +Emacs falls back to the @code{android} font driver. This is a very +lousy font driver, because of limitations and inaccuracies in the font +metrics provided by the Android platform. In that case, Emacs uses +the ``Monospace'' typeface configured on your system; this should +always be Droid Sans Mono. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index dc78adc4520..14702ce6efa 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2018,6 +2018,11 @@ display, because another program took the grab, or because the user raised the finger from the touchscreen. @end table +If a touchpoint is pressed against the menu bar, then Emacs will not +generate any corresponding @code{touchscreen-begin} or +@code{touchscreen-end} events; instead, the menu bar may be displayed +when @code{touchscreen-end} should have been delivered. + @node Focus Events @subsection Focus Events @cindex focus event diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 5a9a9f95f7b..097ce8991ab 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -2846,8 +2846,9 @@ apply to. Here are the possible values of @var{characteristic}: The kind of window system the terminal uses---either @code{graphic} (any graphics-capable display), @code{x}, @code{pc} (for the MS-DOS console), @code{w32} (for MS Windows 9X/NT/2K/XP), @code{haiku} (for -Haiku), @code{pgtk} (for pure GTK), or @code{tty} (a non-graphics-capable -display). @xref{Window Systems, window-system}. +Haiku), @code{pgtk} (for pure GTK), @code{android} (for Android), or +@code{tty} (a non-graphics-capable display). @xref{Window Systems, +window-system}. @item class What kinds of colors the terminal supports---either @code{color}, @@ -8734,6 +8735,8 @@ Emacs is displaying the frame using MS-DOS direct screen writes. Emacs is displaying the frame using the Application Kit on Haiku. @item pgtk Emacs is displaying the frame using pure GTK facilities. +@item android +Emacs is displaying the frame on Android. @item nil Emacs is displaying the frame on a character-based terminal. @end table diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 68f31e500bb..fb96b96ec8c 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -695,7 +695,7 @@ The position of the top left corner of the native frame specifies the indicate that position for the various builds: @itemize @w{} -@item (1) non-toolkit, Haiku, and terminal frames +@item (1) non-toolkit, Android, Haiku, and terminal frames @item (2) Lucid, Motif, and MS-Windows frames @@ -2389,6 +2389,7 @@ engine), and @code{harfbuzz} (font driver for OTF and TTF fonts with HarfBuzz text shaping) (@pxref{Windows Fonts,,, emacs, The GNU Emacs Manual}). The @code{harfbuzz} driver is similarly recommended. On Haiku, there can be several font drivers (@pxref{Haiku Fonts,,, emacs, +The GNU Emacs Manual}), as on Android (@pxref{Android Fonts,,, emacs, The GNU Emacs Manual}). On other systems, there is only one available font backend, so it does diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 3be7036f637..094e954e82b 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -970,6 +970,9 @@ Hewlett-Packard HPUX operating system. @item nacl Google Native Client (@acronym{NaCl}) sandboxing system. +@item android +The Open Handset Alliance's Android operating system. + @item ms-dos Microsoft's DOS@. Emacs compiled with DJGPP for MS-DOS binds @code{system-type} to @code{ms-dos} even when you run it on MS-Windows. diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 6effa79d1a4..7181bc89fea 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -637,8 +637,8 @@ public class EmacsWindow extends EmacsHandleObject pointerIndex = event.getActionIndex (); pointerID = event.getPointerId (pointerIndex); pointerMap.put (pointerID, - new Coordinate ((int) event.getX (pointerID), - (int) event.getY (pointerID))); + new Coordinate ((int) event.getX (pointerIndex), + (int) event.getY (pointerIndex))); break; case MotionEvent.ACTION_POINTER_UP: diff --git a/lisp/loadup.el b/lisp/loadup.el index c4044a3bb9d..3b48d5fe1b5 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -295,6 +295,10 @@ (if (featurep 'dynamic-setting) (load "dynamic-setting")) +;; touch-screen.el is tiny and is used liberally throughout the button +;; code etc, so it may as well be preloaded everywhere. +(load "touch-screen") + (if (featurep 'x) (progn (load "x-dnd") diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el index 487144144f5..d3287d936bb 100644 --- a/lisp/pixel-scroll.el +++ b/lisp/pixel-scroll.el @@ -500,6 +500,7 @@ Otherwise, redisplay will reset the window's vscroll." (set-window-start nil (pixel-point-at-unseen-line) t) (set-window-vscroll nil vscroll t)) +;;;###autoload (defun pixel-scroll-precision-scroll-down-page (delta) "Scroll the current window down by DELTA pixels. Note that this function doesn't work if DELTA is larger than @@ -556,6 +557,7 @@ the height of the current window." (setq delta (- delta max-height))) (pixel-scroll-precision-scroll-down-page delta))) +;;;###autoload (defun pixel-scroll-precision-scroll-up-page (delta) "Scroll the current window up by DELTA pixels. Note that this function doesn't work if DELTA is larger than diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el index 8aeabee567d..43b75d39f50 100644 --- a/lisp/term/android-win.el +++ b/lisp/term/android-win.el @@ -37,6 +37,7 @@ (require 'mouse) (require 'fontset) (require 'dnd) +(require 'touch-screen) (add-to-list 'display-format-alist '(".*" . android)) diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el new file mode 100644 index 00000000000..192a09b3a29 --- /dev/null +++ b/lisp/touch-screen.el @@ -0,0 +1,322 @@ +;;; touch-screen.el --- touch screen support for X and Android -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; Maintainer: emacs-devel@gnu.org +;; Package: emacs + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This file provides code to recognize simple touch screen gestures. +;; It is used on X and Android, where the platform cannot recognize +;; them for us. + +;;; Code: + +(defvar touch-screen-current-tool nil + "The touch point currently being tracked, or nil. +If non-nil, this is a list of five 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 +to that window, a field used to store data while tracking the +touch point, and the initial position of the touchpoint. See +`touch-screen-handle-point-update' for the meanings of the fourth +element.") + +(defvar touch-screen-current-timer nil + "Timer used to track long-presses. +This is always cleared upon any significant state change.") + +(defcustom touch-screen-delay 0.7 + "Delay in seconds before Emacs considers a touch to be a long-press." + :type 'number + :group 'mouse + :version "30.1") + +(defun touch-screen-relative-xy (posn window) + "Return the coordinates of POSN, a mouse position list. +However, return the coordinates relative to WINDOW. + +If (posn-window posn) is the same as window, simply return the +coordinates in POSN. Otherwise, convert them to the frame, and +then back again." + (if (eq (posn-window posn) window) + (posn-x-y posn) + (let ((xy (posn-x-y posn)) + (edges (window-inside-pixel-edges window))) + ;; Make the X and Y positions frame relative. + (when (windowp (posn-window posn)) + (let ((edges (window-inside-pixel-edges + (posn-window posn)))) + (setq xy (cons (+ (car xy) (car edges)) + (+ (cdr xy) (cadr edges)))))) + ;; Make the X and Y positions window relative again. + (cons (- (car xy) (car edges)) + (- (cdr xy) (cadr edges)))))) + +(defun touch-screen-handle-scroll (dx dy) + "Scroll the display assuming that a touch point has moved by DX and DY." + (ignore dx) + ;; This only looks good with precision pixel scrolling. + (if (> dy 0) + (pixel-scroll-precision-scroll-down-page dy) + (pixel-scroll-precision-scroll-up-page (- dy)))) + +(defun touch-screen-handle-timeout (arg) + "Start the touch screen timeout or handle it depending on ARG. +When ARG is nil, start the `touch-screen-current-timer' to go off +in `touch-screen-delay' seconds, and call this function with ARG +t. + +When ARG is t, beep. Then, set the fourth element of +touch-screen-current-tool to `held', and the mark to the last +known position of the tool." + (if (not arg) + ;; Cancel the touch screen long-press timer, if it is still + ;; there by any chance. + (progn + (when touch-screen-current-timer + (cancel-timer touch-screen-current-timer)) + (setq touch-screen-current-timer + (run-at-time touch-screen-delay nil + #'touch-screen-handle-timeout + t))) + ;; Beep. + (beep) + ;; Set touch-screen-current-timer to nil. + (setq touch-screen-current-timer nil) + (when touch-screen-current-tool + ;; Set the state to `held'. + (setcar (nthcdr 3 touch-screen-current-tool) 'held) + ;; Go to the initial position of the touchpoint and activate the + ;; mark. + (with-selected-window (cadr touch-screen-current-tool) + (set-mark (posn-point (nth 4 touch-screen-current-tool))) + (goto-char (mark)) + (activate-mark))))) + +(defun touch-screen-handle-point-update (point) + "Notice that the touch point POINT has changed position. +POINT must be the touch point currently being tracked as +`touch-screen-current-tool'. + +If the fourth element of `touch-screen-current-tool' is nil, then +the touch has just begun. Determine how much POINT has moved. +If POINT has moved upwards or downwards by a significant amount, +then set the fourth element to `scroll'. Then, call +`touch-screen-handle-scroll' to scroll the display by that +amount. + +If the fourth element of `touch-screen-current-tool' is `scroll', +then scroll the display by how much POINT has moved in the Y +axis. + +If the fourth element of `touch-screen-current-tool' is `held', +then the touch has been held down for some time. If motion +happens, cancel `touch-screen-current-timer', and set the field +to `drag'. Then, activate the mark and start dragging. + +If the fourth element of `touch-screen-current-tool' is `drag', +then move point to the position of POINT. + +Set `touch-screen-current-tool' to nil should any error occur." + (let ((window (nth 1 touch-screen-current-tool)) + (what (nth 3 touch-screen-current-tool))) + (cond ((null what) + (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)) + (diff-x (- (car last-posn) (car relative-xy))) + (diff-y (- (cdr last-posn) (cdr relative-xy)))) + ;; Decide whether or not to start scrolling. + (when (or (> diff-y 10) (> diff-x 10) + (< diff-y -10) (< diff-x -10)) + (setcar (nthcdr 3 touch-screen-current-tool) + 'scroll) + (setcar (nthcdr 2 touch-screen-current-tool) + relative-xy) + (with-selected-window window + (touch-screen-handle-scroll diff-x diff-y)) + ;; Cancel the touch screen long-press timer, if it is + ;; still there by any chance. + (when touch-screen-current-timer + (cancel-timer touch-screen-current-timer) + (setq touch-screen-current-timer nil))))) + ((eq what 'scroll) + ;; Cancel the touch screen long-press timer, if it is still + ;; there by any chance. + (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)) + (diff-x (- (car last-posn) (car relative-xy))) + (diff-y (- (cdr last-posn) (cdr relative-xy)))) + (setcar (nthcdr 3 touch-screen-current-tool) + 'scroll) + (setcar (nthcdr 2 touch-screen-current-tool) + relative-xy) + (unless (and (zerop diff-x) (zerop diff-y)) + (with-selected-window window + (touch-screen-handle-scroll diff-x diff-y))))) + ((eq what 'held) + (let* ((posn (cdr point)) + (relative-xy + (touch-screen-relative-xy posn window))) + (when touch-screen-current-timer + (cancel-timer touch-screen-current-timer) + (setq touch-screen-current-timer nil)) + ;; Now start dragging. + (setcar (nthcdr 3 touch-screen-current-tool) + 'drag) + (setcar (nthcdr 2 touch-screen-current-tool) + relative-xy) + (with-selected-window window + ;; Activate the mark. It should have been set by the + ;; time `touch-screen-timeout' was called. + (activate-mark) + + ;; Figure out what character to go to. If this posn is + ;; in the window, go to (posn-point posn). If not, + ;; then go to the line before either window start or + ;; window end. + (if (and (eq (posn-window posn) window) + (posn-point posn)) + (goto-char (posn-point posn)) + (let ((relative-xy + (touch-screen-relative-xy posn window))) + (let ((scroll-conservatively 101)) + (cond + ((< (cdr relative-xy) 0) + (ignore-errors + (goto-char (1- (window-start)))) + (redisplay)) + ((> (cdr relative-xy) + (let ((edges (window-inside-pixel-edges))) + (- (nth 3 edges) (cadr edges)))) + (ignore-errors + (goto-char (1+ (window-end nil t)))) + (redisplay))))))))) + ((eq what 'drag) + (let* ((posn (cdr point))) + ;; Keep dragging. + (with-selected-window window + ;; Figure out what character to go to. If this posn is + ;; in the window, go to (posn-point posn). If not, + ;; then go to the line before either window start or + ;; window end. + (if (and (eq (posn-window posn) window) + (posn-point posn)) + (goto-char (posn-point posn)) + (let ((relative-xy + (touch-screen-relative-xy posn window))) + (let ((scroll-conservatively 101)) + (cond + ((< (cdr relative-xy) 0) + (ignore-errors + (goto-char (1- (window-start)))) + (redisplay)) + ((> (cdr relative-xy) + (let ((edges (window-inside-pixel-edges))) + (- (nth 3 edges) (cadr edges)))) + (ignore-errors + (goto-char (1+ (window-end nil t)))) + (redisplay)))))))))))) + +(defun touch-screen-handle-point-up (point) + "Notice that POINT has been removed from the screen. +POINT should be the point currently tracked as +`touch-screen-current-tool'. + +If the fourth argument of `touch-screen-current-tool' is nil, +move point to the position of POINT, selecting the window under +POINT as well; if there is a button at POINT, then activate the +button there. Otherwise, deactivate the mark. Then, display the +on-screen keyboard." + (let ((what (nth 3 touch-screen-current-tool))) + (cond ((null what) + (when (windowp (posn-window (cdr point))) + ;; Select the window that was tapped. + (select-window (posn-window (cdr point))) + (let ((button (button-at (posn-point (cdr point))))) + (when button + (button-activate button t)) + (goto-char (posn-point (cdr point))) + (deactivate-mark))))))) + +(defun touch-screen-handle-touch (event) + "Handle a single touch EVENT, and perform associated actions. +EVENT can either be a touchscreen-begin, touchscreen-update or +touchscreen-end event." + (interactive "e") + (cond + ((eq (car event) 'touchscreen-begin) + ;; A tool was just pressed against the screen. Figure out the + ;; window where it is and make it the tool being tracked on the + ;; window. + (let ((touchpoint (caadr event)) + (position (cdadr event))) + ;; Cancel the touch screen timer, if it is still there by any + ;; chance. + (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 touch-screen-current-tool (and (windowp (posn-window position)) + (posn-point position) + (list touchpoint + (posn-window position) + (posn-x-y position) + nil position))) + ;; Start the long-press timer. + (touch-screen-handle-timeout nil))) + ((eq (car event) 'touchscreen-update) + ;; The positions of tools currently pressed against the screen + ;; have changed. If there is a tool being tracked as part of a + ;; gesture, look it up in the list of tools. + (let ((new-point (assq (car touch-screen-current-tool) + (cadr event)))) + (when new-point + (touch-screen-handle-point-update 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'. + (when (eq (caadr event) (car touch-screen-current-tool)) + ;; Cancel the touch screen long-press timer, if it is still there + ;; by any chance. + (when touch-screen-current-timer + (cancel-timer touch-screen-current-timer) + (setq touch-screen-current-timer nil)) + (touch-screen-handle-point-up (cadr event)) + (setq touch-screen-current-tool nil))))) + +(define-key global-map [touchscreen-begin] #'touch-screen-handle-touch) +(define-key global-map [touchscreen-update] #'touch-screen-handle-touch) +(define-key global-map [touchscreen-end] #'touch-screen-handle-touch) + +(provide 'touch-screen) + +;;; touch-screen ends here diff --git a/src/android.c b/src/android.c index 3a965286460..9b15ea9f15a 100644 --- a/src/android.c +++ b/src/android.c @@ -25,9 +25,11 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include #include +#include #include @@ -513,7 +515,7 @@ android_run_debug_thread (void *data) char *line; size_t n; - fd = (int) data; + fd = (int) (intptr_t) data; file = fdopen (fd, "r"); if (!file) @@ -958,7 +960,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, close (pipefd[1]); if (pthread_create (&thread, NULL, android_run_debug_thread, - (void *) pipefd[0])) + (void *) (intptr_t) pipefd[0])) emacs_abort (); /* Now set the path to the site load directory. */ @@ -2829,6 +2831,7 @@ android_put_pixel (struct android_image *ximg, int x, int y, { char *byte, *word; unsigned int r, g, b; + unsigned int pixel_int; /* Ignore out-of-bounds accesses. */ @@ -2859,7 +2862,8 @@ android_put_pixel (struct android_image *ximg, int x, int y, b = pixel & 0x000000ff; pixel = (r >> 16) | g | (b << 16) | 0xff000000; - memcpy (word, &pixel, sizeof pixel); + pixel_int = pixel; + memcpy (word, &pixel_int, sizeof pixel_int); break; } } @@ -3734,6 +3738,262 @@ android_exception_check (void) } } + + +/* Native image transforms. */ + +/* Transform the coordinates X and Y by the specified affine + transformation MATRIX. Place the result in *XOUT and *YOUT. */ + +static void +android_transform_coordinates (int x, int y, + struct android_transform *transform, + float *xout, float *yout) +{ + /* Apply the specified affine transformation. + A transform looks like: + + M1 M2 M3 X + M4 M5 M6 * Y + + = + + M1*X + M2*Y + M3*1 = X1 + M4*X + M5*Y + M6*1 = Y1 + + (In most transforms, there is another row at the bottom for + mathematical reasons. Since Z1 is always 1.0, the row is simply + implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 = 1.0. See + the definition of matrix3x3 in image.c for some more explanations + about this.) */ + + *xout = transform->m1 * x + transform->m2 * y + transform->m3; + *yout = transform->m4 * x + transform->m5 * y + transform->m6; +} + +/* Return the interpolation of the four pixels TL, TR, BL, and BR, + according to the weights DISTX and DISTY. */ + +static unsigned int +android_four_corners_bilinear (unsigned int tl, unsigned int tr, + unsigned int bl, unsigned int br, + int distx, int disty) +{ + int distxy, distxiy, distixy, distixiy; + uint32_t f, r; + + distxy = distx * disty; + distxiy = (distx << 8) - distxy; + distixy = (disty << 8) - distxy; + distixiy = (256 * 256 - (disty << 8) + - (distx << 8) + distxy); + + /* Red */ + r = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); + + /* Green */ + f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); + r |= f & 0xff000000; + + /* Now do the upper two components. */ + tl >>= 16; + tr >>= 16; + bl >>= 16; + br >>= 16; + r >>= 16; + + /* Blue */ + f = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); + r |= f & 0x00ff0000; + + /* Alpha */ + f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); + r |= f & 0xff000000; + + return r; +} + +/* Return the interpolation of the four pixels closest to at X, Y in + IMAGE, according to weights in both axes computed from X and Y. + IMAGE must be depth 24, or the behavior is undefined. */ + +static unsigned int +android_fetch_pixel_bilinear (struct android_image *image, + float x, float y) +{ + int x1, y1, x2, y2; + float distx, disty; + unsigned int top_left, top_right; + unsigned int bottom_left, bottom_right; + char *word; + + /* Compute the four closest corners to X and Y. */ + x1 = (int) x; + x2 = x1 + 1; + y1 = (int) y; + y2 = y1 + 1; + + /* Make sure all four corners are within range. */ + x1 = MAX (0, MIN (image->width - 1, x1)); + y1 = MAX (0, MIN (image->height - 1, y1)); + x2 = MAX (0, MIN (image->width - 1, x2)); + y2 = MAX (0, MIN (image->height - 1, y2)); + + /* Compute the X and Y biases. These are numbers between 0f and + 1f. */ + distx = x - x1; + disty = y - y1; + + /* Fetch the four closest pixels. */ + word = image->data + y1 * image->bytes_per_line + x1 * 4; + memcpy (&top_left, word, sizeof top_left); + word = image->data + y1 * image->bytes_per_line + x2 * 4; + memcpy (&top_right, word, sizeof top_right); + word = image->data + y2 * image->bytes_per_line + x1 * 4; + memcpy (&bottom_left, word, sizeof bottom_left); + word = image->data + y2 * image->bytes_per_line + x2 * 4; + memcpy (&bottom_right, word, sizeof bottom_right); + + /* Do the interpolation. */ + return android_four_corners_bilinear (top_left, top_right, bottom_left, + bottom_right, distx * 256, + disty * 256); +} + +/* Transform the depth 24 image IMAGE by the 3x2 affine transformation + matrix MATRIX utilizing a bilinear filter. Place the result in + OUT. The matrix maps from the coordinate space of OUT to + IMAGE. */ + +void +android_project_image_bilinear (struct android_image *image, + struct android_image *out, + struct android_transform *transform) +{ + int x, y; + unsigned int pixel; + float xout, yout; + char *word; + + /* Loop through each pixel in OUT. Transform it by TRANSFORM, then + interpolate it to IMAGE, and place the result back in OUT. */ + + for (y = 0; y < out->height; ++y) + { + for (x = 0; x < out->width; ++x) + { + /* Transform the coordinates by TRANSFORM. */ + android_transform_coordinates (x, y, transform, + &xout, &yout); + + /* Interpolate back to IMAGE. */ + pixel = android_fetch_pixel_bilinear (image, xout, yout); + + /* Put the pixel back in OUT. */ + word = out->data + y * out->bytes_per_line + x * 4; + memcpy (word, &pixel, sizeof pixel); + } + } +} + +/* Return the interpolation of X, Y to IMAGE, a depth 24 image. */ + +static unsigned int +android_fetch_pixel_nearest_24 (struct android_image *image, float x, + float y) +{ + int x1, y1; + char *word; + unsigned int pixel; + + x1 = MAX (0, MIN (image->width - 1, (int) roundf (x))); + y1 = MAX (0, MIN (image->height - 1, (int) roundf (y))); + + word = image->data + y1 * image->bytes_per_line + x1 * 4; + memcpy (&pixel, word, sizeof pixel); + + return pixel; +} + +/* Return the interpolation of X, Y to IMAGE, a depth 1 image. */ + +static unsigned int +android_fetch_pixel_nearest_1 (struct android_image *image, float x, + float y) +{ + int x1, y1; + char *byte; + + x1 = MAX (0, MIN (image->width - 1, (int) roundf (x))); + y1 = MAX (0, MIN (image->height - 1, (int) roundf (y))); + + byte = image->data + y1 * image->bytes_per_line; + return (byte[x1 / 8] & (1 << x1 % 8)) ? 1 : 0; +} + +/* Transform the depth 24 or 1 image IMAGE by the 3x2 affine + transformation matrix MATRIX. Place the result in OUT. The matrix + maps from the coordinate space of OUT to IMAGE. Use a + nearest-neighbor filter. */ + +void +android_project_image_nearest (struct android_image *image, + struct android_image *out, + struct android_transform *transform) +{ + int x, y; + unsigned int pixel; + float xout, yout; + char *word, *byte; + + if (image->depth == 1) + { + for (y = 0; y < out->height; ++y) + { + for (x = 0; x < out->width; ++x) + { + /* Transform the coordinates by TRANSFORM. */ + android_transform_coordinates (x, y, transform, + &xout, &yout); + + /* Interpolate back to IMAGE. */ + pixel = android_fetch_pixel_nearest_1 (image, xout, yout); + + /* Put the pixel back in OUT. */ + byte = out->data + y * out->bytes_per_line + x / 8; + + if (pixel) + *byte |= (1 << x % 8); + else + *byte &= ~(1 << x % 8); + } + } + + return; + } + + for (y = 0; y < out->height; ++y) + { + for (x = 0; x < out->width; ++x) + { + /* Transform the coordinates by TRANSFORM. */ + android_transform_coordinates (x, y, transform, + &xout, &yout); + + /* Interpolate back to IMAGE. */ + pixel = android_fetch_pixel_nearest_24 (image, xout, yout); + + /* Put the pixel back in OUT. */ + word = out->data + y * out->bytes_per_line + x * 4; + memcpy (word, &pixel, sizeof pixel); + } + } +} + #else /* ANDROID_STUBIFY */ /* X emulation functions for Android. */ @@ -3793,4 +4053,20 @@ android_put_image (android_pixmap pixmap, emacs_abort (); } +void +android_project_image_bilinear (struct android_image *image, + struct android_image *out, + struct android_transform *transform) +{ + emacs_abort (); +} + +void +android_project_image_nearest (struct android_image *image, + struct android_image *out, + struct android_transform *transform) +{ + emacs_abort (); +} + #endif diff --git a/src/androidgui.h b/src/androidgui.h index 9df5b073a7c..1f28c18ff34 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -542,6 +542,25 @@ extern struct android_image *android_get_image (android_drawable, enum android_image_format); extern void android_put_image (android_pixmap, struct android_image *); + +/* Native image transforms. */ + +/* 3x2 matrix describing a projective transform. See + android_transform_coordinates for details. */ + +struct android_transform +{ + float m1, m2, m3; + float m4, m5, m6; +}; + +extern void android_project_image_bilinear (struct android_image *, + struct android_image *, + struct android_transform *); +extern void android_project_image_nearest (struct android_image *, + struct android_image *, + struct android_transform *); + /* X emulation stuff also needed while building stubs. */ diff --git a/src/androidterm.c b/src/androidterm.c index 6f452a52d85..cc2da279bb3 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -417,8 +417,6 @@ android_note_mouse_movement (struct frame *frame, || event->y < r->y || event->y >= r->y + r->height) { frame->mouse_moved = true; - /* TODO - dpyinfo->last_mouse_scroll_bar = NULL; */ note_mouse_highlight (frame, event->x, event->y); /* Remember which glyph we're now on. */ remember_mouse_glyph (frame, event->x, event->y, r); @@ -2959,10 +2957,34 @@ android_draw_stretch_glyph_string (struct glyph_string *s) s->background_filled_p = true; } +static void +android_get_scale_factor (int *scale_x, int *scale_y) +{ + /* This is 96 everywhere else, but 160 on Android. */ + const int base_res = 160; + struct android_display_info *dpyinfo; + + dpyinfo = x_display_list; + *scale_x = *scale_y = 1; + + if (dpyinfo) + { + if (dpyinfo->resx > base_res) + *scale_x = floor (dpyinfo->resx / base_res); + if (dpyinfo->resy > base_res) + *scale_y = floor (dpyinfo->resy / base_res); + } +} + static void android_draw_underwave (struct glyph_string *s, int decoration_width) { - int wave_height = 3, wave_length = 2; + int scale_x, scale_y; + + android_get_scale_factor (&scale_x, &scale_y); + + int wave_height = 3 * scale_y, wave_length = 2 * scale_x; + int dx, dy, x0, y0, width, x1, y1, x2, y2, xmax; bool odd; struct android_rectangle wave_clip, string_clip, final_clip; diff --git a/src/dispextern.h b/src/dispextern.h index 2ceed53813e..bc3556114c5 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -3098,8 +3098,9 @@ struct redisplay_interface #ifdef HAVE_WINDOW_SYSTEM -# if (defined USE_CAIRO || defined HAVE_XRENDER \ - || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU) +# if (defined USE_CAIRO || defined HAVE_XRENDER \ + || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU \ + || defined HAVE_ANDROID) # define HAVE_NATIVE_TRANSFORMS # endif diff --git a/src/image.c b/src/image.c index 364ddd96e31..16618f49f1e 100644 --- a/src/image.c +++ b/src/image.c @@ -2631,11 +2631,11 @@ compute_image_size (double width, double height, finally move the origin back to the top left of the image, which may now be a different corner. - Note that different GUI backends (X, Cairo, w32, NS, Haiku) want - the transform matrix defined as transform from the original image - to the transformed image, while others want the matrix to describe - the transform of the space, which boils down to inverting the - matrix. + Note that different GUI backends (X, Cairo, w32, NS, Haiku, + Android) want the transform matrix defined as transform from the + original image to the transformed image, while others want the + matrix to describe the transform of the space, which boils down to + inverting the matrix. It's possible to pre-calculate the matrix multiplications and just generate one transform matrix that will do everything we need in a @@ -2677,6 +2677,96 @@ compute_image_rotation (struct image *img, double *rotation) *rotation = XFIXNUM (reduced_angle); } +#ifdef HAVE_ANDROID + +static void +matrix_identity (matrix3x3 matrix) +{ + memset (matrix, 0, sizeof (matrix3x3)); + + matrix[0][0] = 1.0; + matrix[1][1] = 1.0; + matrix[2][2] = 1.0; +} + +/* Translate the matrix TRANSFORM to X, Y, and then perform clockwise + rotation by the given angle THETA in radians and translate back. + As the transform is being performed in a coordinate system where Y + grows downwards, the given angle describes a clockwise + rotation. */ + +static void +matrix_rotate (matrix3x3 transform, double theta, double x, double y) +{ + matrix3x3 temp, copy; + + /* 1. Translate the matrix so X and Y are in the center. */ + + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + temp[0][2] = x; + temp[1][2] = y; + + matrix3x3_mult (copy, temp, transform); + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + /* 2. Rotate the matrix counter-clockwise, assuming a coordinate + system where Y grows downwards. */ + + temp[0][0] = cos (theta); + temp[0][1] = -sin (theta); + temp[1][0] = sinf (theta); + temp[1][1] = cosf (theta); + + matrix3x3_mult (copy, temp, transform); + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + /* 3. Translate back. */ + + temp[0][2] = -x; + temp[1][2] = -y; + + matrix3x3_mult (copy, temp, transform); +} + +/* Scale the matrix TRANSFORM by -1, and then apply a TX of width, in + effect flipping the image horizontally. */ + +static void +matrix_mirror_horizontal (matrix3x3 transform, double width) +{ + matrix3x3 temp, copy; + + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + temp[0][0] = -1.0f; + temp[0][2] = width; + + matrix3x3_mult (copy, temp, transform); +} + +static void +matrix_translate (matrix3x3 transform, float tx, float ty) +{ + matrix3x3 temp, copy; + + matrix_identity (temp); + memcpy (copy, transform, sizeof copy); + + /* Set the tx and ty. */ + temp[0][2] = tx; + temp[1][2] = ty; + + /* Multiply it with the transform. */ + matrix3x3_mult (copy, temp, transform); +} + +#endif + static void image_set_transform (struct frame *f, struct image *img) { @@ -2696,6 +2786,14 @@ image_set_transform (struct frame *f, struct image *img) memcpy (&img->transform, identity, sizeof identity); #endif +#if defined HAVE_ANDROID + matrix3x3 identity = { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, + }; +#endif + # if (defined HAVE_IMAGEMAGICK \ && !defined DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE) /* ImageMagick images already have the correct transform. */ @@ -2733,7 +2831,8 @@ image_set_transform (struct frame *f, struct image *img) /* Determine flipping. */ flip = !NILP (image_spec_value (img->spec, QCflip, NULL)); -# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU +# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU \ + || defined HAVE_ANDROID /* We want scale up operations to use a nearest neighbor filter to show real pixels instead of munging them, but scale down operations to use a blended filter, to avoid aliasing and the like. @@ -2755,7 +2854,7 @@ image_set_transform (struct frame *f, struct image *img) matrix3x3 matrix = { -# if defined USE_CAIRO || defined HAVE_XRENDER +# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID [0][0] = (!IEEE_FLOATING_POINT && width == 0 ? DBL_MAX : img->width / (double) width), [1][1] = (!IEEE_FLOATING_POINT && height == 0 ? DBL_MAX @@ -2778,7 +2877,7 @@ image_set_transform (struct frame *f, struct image *img) /* Haiku needs this, since the transformation is done on the basis of the view, and not the image. */ -#ifdef HAVE_HAIKU +#if defined HAVE_HAIKU int extra_tx, extra_ty; extra_tx = 0; @@ -2789,8 +2888,9 @@ image_set_transform (struct frame *f, struct image *img) rotate_flag = 0; else { -# if (defined USE_CAIRO || defined HAVE_XRENDER \ - || defined HAVE_NTGUI || defined HAVE_NS \ +#ifndef HAVE_ANDROID +# if (defined USE_CAIRO || defined HAVE_XRENDER \ + || defined HAVE_NTGUI || defined HAVE_NS \ || defined HAVE_HAIKU) int cos_r, sin_r; if (rotation == 0) @@ -2817,7 +2917,7 @@ image_set_transform (struct frame *f, struct image *img) sin_r = 1; rotate_flag = 1; -#ifdef HAVE_HAIKU +#if defined HAVE_HAIKU if (!flip) extra_ty = height; extra_tx = 0; @@ -2853,7 +2953,7 @@ image_set_transform (struct frame *f, struct image *img) if (0 < rotate_flag) { -# if defined USE_CAIRO || defined HAVE_XRENDER +# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID /* 1. Translate so (0, 0) is in the center of the image. */ matrix3x3 t = { [0][0] = 1, @@ -2904,6 +3004,93 @@ image_set_transform (struct frame *f, struct image *img) img->height = height; } # endif +#else + /* Calculate the inverse transform from the destination to the + source. The matrix is currently identity with scale + applied. + + This code makes more sense to me than what lies above. But + I'm not touching what works. */ + + if (rotation != 0 && rotation != 90 + && rotation != 180 && rotation != 270) + { + rotate_flag = 0; + goto bail; + } + + rotate_flag = 1; + + switch ((int) rotation + (flip ? 1 : 0)) + { + case 0: + break; + + case 90: + /* Rotate the image 90 degrees clockwise. IOW, rotate the + destination by 90 degrees counterclockwise, which is 270 + degrees clockwise. */ + matrix_rotate (matrix, M_PI * 1.5, 0, 0); + matrix_translate (matrix, -height, 0); + break; + + case 180: + /* Apply clockwise 180 degree rotation around the + center. */ + matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0); + break; + + case 270: + /* Apply 270 degree counterclockwise rotation to the + destination, which is 90 degrees clockwise. */ + matrix_rotate (matrix, M_PI * 0.5, 0, 0); + matrix_translate (matrix, 0, -width); + break; + + case 1: + /* Flipped. Apply horizontal flip. */ + matrix_mirror_horizontal (matrix, width); + break; + + case 91: + /* Apply a flip but otherwise treat this the same as 90. */ + matrix_rotate (matrix, M_PI * 1.5, 0, 0); + matrix_translate (matrix, -height, 0); + matrix_mirror_horizontal (matrix, height); + break; + + case 181: + /* Flipped 180 degrees. Apply a flip and treat this the + same as 180. */ + matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0); + matrix_mirror_horizontal (matrix, width); + break; + + case 271: + /* Flipped 270 degrees. Apply a flip and treat this the + same as 270. */ + matrix_rotate (matrix, M_PI * 0.5, 0, 0); + matrix_translate (matrix, 0, -width); + matrix_mirror_horizontal (matrix, height); + break; + } + + /* Now set img->width and img->height. Flip them if the + rotation being applied requires so. */ + + if (rotation != 270 && rotation != 90) + { + img->width = width; + img->height = height; + } + else + { + img->height = width; + img->width = height; + } + bail: + ; +#endif } if (rotate_flag < 0) @@ -2968,6 +3155,103 @@ image_set_transform (struct frame *f, struct image *img) img->transform[0][2] = extra_tx; img->transform[1][2] = extra_ty; } +# elif defined HAVE_ANDROID + /* Create a new image of the right size, then turn it into a pixmap + and set that as img->pixmap. Destroy img->mask for now (this is + not right.) */ + + struct android_image *transformed_image, *image; + struct android_transform transform; + + /* If there is no transform, simply return. */ + if (!memcmp (&matrix, &identity, sizeof matrix)) + return; + + /* First, get the source image. */ + image = image_get_x_image (f, img, false); + + /* Make the transformed image. */ + transformed_image = android_create_image (image->depth, + ANDROID_Z_PIXMAP, + NULL, img->width, + img->height); + + /* Allocate memory for that image. */ + transformed_image->data + = xmalloc (transformed_image->bytes_per_line + * transformed_image->height); + + /* Do the transform. */ + transform.m1 = matrix[0][0]; + transform.m2 = matrix[0][1]; + transform.m3 = matrix[0][2]; + transform.m4 = matrix[1][0]; + transform.m5 = matrix[1][1]; + transform.m6 = matrix[1][2]; + + if (image->depth == 24 && smoothing) + android_project_image_bilinear (image, transformed_image, + &transform); + else + android_project_image_nearest (image, transformed_image, + &transform); + + image_unget_x_image (img, false, image); + + /* Now replace the image. */ + + if (img->ximg) + image_destroy_x_image (img->ximg); + + img->ximg = transformed_image; + +#ifndef ANDROID_STUBIFY + /* Then replace the pixmap. */ + android_free_pixmap (img->pixmap); + + /* In case android_create_pixmap signals. */ + img->pixmap = ANDROID_NONE; + img->pixmap = android_create_pixmap (img->width, img->height, + transformed_image->depth); + android_put_image (img->pixmap, transformed_image); +#else + emacs_abort (); +#endif + + /* Now, transform the mask. The mask should be depth 1, and is + always transformed using a nearest neighbor filter. */ + + if (img->mask_img || img->mask) + { + image = image_get_x_image (f, img, true); + transformed_image = android_create_image (1, ANDROID_Z_PIXMAP, + NULL, img->width, + img->height); + transformed_image->data + = xmalloc (transformed_image->bytes_per_line + * transformed_image->height); + android_project_image_nearest (image, transformed_image, + &transform); + image_unget_x_image (img, false, image); + + /* Now replace the image. */ + + if (img->mask_img) + image_destroy_x_image (img->mask_img); + + img->mask_img = transformed_image; + +#ifndef ANDROID_STUBIFY + if (img->mask) + android_free_pixmap (img->mask); + + img->mask = ANDROID_NONE; + img->mask = android_create_pixmap (img->width, img->height, 1); + android_put_image (img->mask, transformed_image); +#endif + } + + /* Done! */ #endif } @@ -12087,7 +12371,7 @@ The list of capabilities can include one or more of the following: { #ifdef HAVE_NATIVE_TRANSFORMS # if defined HAVE_IMAGEMAGICK || defined (USE_CAIRO) || defined (HAVE_NS) \ - || defined (HAVE_HAIKU) + || defined (HAVE_HAIKU) | defined HAVE_ANDROID return list2 (Qscale, Qrotate90); # elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER) if (FRAME_DISPLAY_INFO (f)->xrender_supported_p) diff --git a/src/keyboard.c b/src/keyboard.c index 78637ef4f15..306fea354e2 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -339,6 +339,10 @@ static struct timespec timer_last_idleness_start_time; static Lisp_Object virtual_core_pointer_name; static Lisp_Object virtual_core_keyboard_name; +/* If not nil, ID of the last TOUCHSCREEN_END_EVENT to land on the + menu bar. */ +static Lisp_Object menu_bar_touch_id; + /* Global variable declarations. */ @@ -6445,11 +6449,74 @@ make_lispy_event (struct input_event *event) { Lisp_Object x, y, id, position; struct frame *f = XFRAME (event->frame_or_window); +#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR + int column, row, dummy; +#endif id = event->arg; x = event->x; y = event->y; +#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR + if (event->kind == TOUCHSCREEN_BEGIN_EVENT + && coords_in_menu_bar_window (f, XFIXNUM (x), XFIXNUM (y))) + { + /* If the tap began in the menu bar window, then save the + id. */ + menu_bar_touch_id = id; + return Qnil; + } + else if (event->kind == TOUCHSCREEN_END_EVENT + && EQ (menu_bar_touch_id, id)) + { + /* This touch should activate the menu bar. Generate the + menu bar event. */ + menu_bar_touch_id = Qnil; + + if (f->menu_bar_window) + { + x_y_to_hpos_vpos (XWINDOW (f->menu_bar_window), XFIXNUM (x), + XFIXNUM (y), &column, &row, NULL, NULL, + &dummy); + + if (row >= 0 && row < FRAME_MENU_BAR_LINES (f)) + { + Lisp_Object items, item; + + /* Find the menu bar item under `column'. */ + item = Qnil; + items = FRAME_MENU_BAR_ITEMS (f); + for (i = 0; i < ASIZE (items); i += 4) + { + Lisp_Object pos, string; + string = AREF (items, i + 1); + pos = AREF (items, i + 3); + if (NILP (string)) + break; + if (column >= XFIXNUM (pos) + && column < XFIXNUM (pos) + SCHARS (string)) + { + item = AREF (items, i); + break; + } + } + + /* ELisp manual 2.4b says (x y) are window + relative but code says they are + frame-relative. */ + position = list4 (event->frame_or_window, + Qmenu_bar, + Fcons (event->x, event->y), + INT_TO_INTEGER (event->timestamp)); + + return list2 (item, position); + } + } + + return Qnil; + } +#endif + position = make_lispy_position (f, x, y, event->timestamp); return list2 (((event->kind @@ -12462,6 +12529,9 @@ syms_of_keyboard (void) virtual_core_keyboard_name = Qnil; staticpro (&virtual_core_keyboard_name); + menu_bar_touch_id = Qnil; + staticpro (&menu_bar_touch_id); + defsubr (&Scurrent_idle_time); defsubr (&Sevent_symbol_parse_modifiers); defsubr (&Sevent_convert_list); diff --git a/src/sfnt.c b/src/sfnt.c index 9b6c421212a..09fda82382b 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -3436,7 +3436,7 @@ sfnt_compare_edges (const void *a, const void *b) that now overlap with Y, keeping them sorted by X. Poly those edges through SPAN_FUNC. Then, move upwards by SFNT_POLY_STEP, remove edges that no longer apply, and interpolate the remaining - edge's X coordinates. Repeat until all the edges have been polyed. + edges' X coordinates. Repeat until all the edges have been polyed. Or alternatively, think of this as such: each edge is actually a vector from its bottom position towards its top most position. diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index 01bfdbaaf58..cddb3fd40f3 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -73,48 +73,22 @@ sfntfont_android_mul8x2 (unsigned int a8, unsigned int b32) return (i + ((i >> 8) & 0xff00ff)) >> 8 & 0xff00ff; } -/* Blend two pixels SRC and DST without utilizing any control flow. - SRC must be in premultiplied ARGB8888 format, and DST must be in - premultiplied ABGR8888 format. Value is in premultiplied ABGR8888 - format. */ - -static unsigned int -sfntfont_android_blend (unsigned int src, unsigned int dst) -{ - unsigned int a, br_part, ag_part, src_rb, both; - - a = (src >> 24); - br_part = sfntfont_android_mul8x2 (255 - a, dst); - ag_part = sfntfont_android_mul8x2 (255 - a, dst >> 8) << 8; - - both = ag_part | br_part; - - /* Swizzle src. */ - src_rb = src & 0x00ff00ff; - src = src & ~0x00ff00ff; - src |= (src_rb >> 16 | src_rb << 16); - - /* This addition need not be saturating because both has already - been multiplied by 255 - a. */ - return both + src; -} - #define U255TO256(x) ((unsigned short) (x) + ((x) >> 7)) /* Blend two pixels SRC and DST without utilizing any control flow. - Both SRC and DST are expected to be in premultiplied ARGB8888 + Both SRC and DST are expected to be in premultiplied ABGB8888 format. Value is returned in premultiplied ARGB8888 format. */ static unsigned int -sfntfont_android_blendrgb (unsigned int src, unsigned int dst) +sfntfont_android_blend (unsigned int src, unsigned int dst) { - unsigned int a, rb_part, ag_part, both; + unsigned int a, br_part, ag_part, both; a = (src >> 24); - rb_part = sfntfont_android_mul8x2 (255 - a, dst); + br_part = sfntfont_android_mul8x2 (255 - a, dst); ag_part = sfntfont_android_mul8x2 (255 - a, dst >> 8) << 8; - both = ag_part | rb_part; + both = ag_part | br_part; /* This addition need not be saturating because both has already been multiplied by 255 - a. */ @@ -210,6 +184,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from, jobject bitmap; int left, top, temp_y; unsigned int prod, raster_y; + unsigned long foreground, back_pixel, rb; if (!s->gc->num_clip_rects) /* Clip region is empty. */ @@ -219,6 +194,17 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from, /* Nothing to draw. */ return; + /* Swizzle the foreground and background in s->gc into BGR, then add + an alpha channel. */ + foreground = s->gc->foreground; + back_pixel = s->gc->background; + rb = foreground & 0x00ff00ff; + foreground &= ~0x00ff00ff; + foreground |= rb >> 16 | rb << 16 | 0xff000000; + rb = back_pixel & 0x00ff00ff; + back_pixel &= ~0x00ff00ff; + back_pixel |= rb >> 16 | rb << 16 | 0xff000000; + USE_SAFE_ALLOCA; prepare_face_for_display (s->f, s->face); @@ -294,7 +280,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from, + stride * temp_y); for (x = background.x; x < background.x + background.width; ++x) - row[x] = s->gc->background | 0xff000000; + row[x] = back_pixel; } } @@ -327,10 +313,9 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int from, { prod = sfntfont_android_scale32 (U255TO256 (raster_row[x]), - (s->gc->foreground - | 0xff000000)); + foreground); row[left + x] - = sfntfont_android_blendrgb (prod, row[left + x]); + = sfntfont_android_blend (prod, row[left + x]); } } } diff --git a/src/sfntfont.c b/src/sfntfont.c index 25cea59f6a7..56977622211 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -860,7 +860,7 @@ sfntfont_lookup_char (struct sfnt_font_desc *desc, Lisp_Object character, /* Emacs missing charsets? */ return false; - font_character = ENCODE_CHAR (charset, XFIXNUM (character)); + font_character = ENCODE_CHAR (charset, (int) XFIXNUM (character)); if (font_character == CHARSET_INVALID_CODE (charset)) return false; -- 2.39.5