]> git.eshelyaron.com Git - emacs.git/commitdiff
Update Android port
authorPo Lu <luangruo@yahoo.com>
Mon, 16 Jan 2023 11:50:02 +0000 (19:50 +0800)
committerPo Lu <luangruo@yahoo.com>
Mon, 16 Jan 2023 11:50:02 +0000 (19:50 +0800)
* 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.

19 files changed:
doc/emacs/android.texi
doc/lispref/commands.texi
doc/lispref/display.texi
doc/lispref/frames.texi
doc/lispref/os.texi
java/org/gnu/emacs/EmacsWindow.java
lisp/loadup.el
lisp/pixel-scroll.el
lisp/term/android-win.el
lisp/touch-screen.el [new file with mode: 0644]
src/android.c
src/androidgui.h
src/androidterm.c
src/dispextern.h
src/image.c
src/keyboard.c
src/sfnt.c
src/sfntfont-android.c
src/sfntfont.c

index 57ef10de9f3385a206af43512d49ac6217c0df1b..b5a91b0f98f8dd5f7a73a4fadcb2cd88afbaaadb 100644 (file)
@@ -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.
index dc78adc4520c4cd7432af8c434c1a14fe097bccb..14702ce6efa021f737b89d4cb699a2a4b8381939 100644 (file)
@@ -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
index 5a9a9f95f7b183fb933a848c94f884ff6f22e431..097ce8991ab1df84ef21c7b087aff8c6ecf7d4fc 100644 (file)
@@ -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
index 68f31e500bb4f4cee982cfcdc8eeed3f1eaa896b..fb96b96ec8cc1712a473b86891c32317afa577a9 100644 (file)
@@ -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
index 3be7036f637b1a1c71335606389236f5a9172618..094e954e82bc016e0cb2dc18e09b8d7b7b71db90 100644 (file)
@@ -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.
index 6effa79d1a4bb7327be17c9b2c3a9920ba05a36f..7181bc89fea7bb404dc56ab48d30f8b5ab476cbe 100644 (file)
@@ -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:
index c4044a3bb9d1ecc51fe20c628c2f4c04ae8d758c..3b48d5fe1b5acaf47d064437b61b0982070c4c32 100644 (file)
 (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")
index 487144144f53e98827225ffb2a8e33f896be9c15..d3287d936bb591de0c98b944267917a37ed5a0d0 100644 (file)
@@ -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
index 8aeabee567d5b420921ca2f44dfac90a488d22de..43b75d39f506e0fd8a90bb9e45ca704c78d68e8f 100644 (file)
@@ -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 (file)
index 0000000..192a09b
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.
+
+;;; 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
index 3a9652864609a9a6330523bd1ae72bbc5f8c3159..9b15ea9f15ab866b8b8581f083abb99946c6ee3d 100644 (file)
@@ -25,9 +25,11 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include <signal.h>
 #include <semaphore.h>
 #include <dlfcn.h>
+#include <errno.h>
 
 #include <sys/stat.h>
 #include <sys/mman.h>
+#include <sys/param.h>
 
 #include <assert.h>
 
@@ -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)
     }
 }
 
+\f
+
+/* 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
index 9df5b073a7c3f0d3a1308d80c1ac51492cc5545c..1f28c18ff34590e97dacb06e5acb6e4345335e39 100644 (file)
@@ -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 *);
+
 \f
 
 /* X emulation stuff also needed while building stubs.  */
index 6f452a52d85e71a8a262502171e9aa4c7ded44d4..cc2da279bb3afbc2c781fcda0aa9d262d1bfe7f2 100644 (file)
@@ -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;
index 2ceed53813e27073ccab6fcbb2abe0101641df5e..bc3556114c572f1efb0288f9030d6ea00a49855c 100644 (file)
@@ -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
 
index 364ddd96e31ddc37febb545536b4130968bc55be..16618f49f1eedc3737ca4952585c9fe18e13a22c 100644 (file)
@@ -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)
index 78637ef4f15e86d15f59888de3dc3e5b8882b858..306fea354e2b7a43e62a97b95a4c1f4dc29a7502 100644 (file)
@@ -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;
+
 \f
 /* 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);
index 9b6c421212adaf99199bb89b815124e49ba44a12..09fda82382bdc4ecabbb348c790eb86ff5684fd7 100644 (file)
@@ -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.
index 01bfdbaaf5890f5d183dc968ad81aba1adaf2374..cddb3fd40f31db689a1b5dad7c86268bf5dd0f69 100644 (file)
@@ -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]);
            }
        }
     }
index 25cea59f6a7274904349d2db0ff23c88ea9cad99..56977622211cd5b32c58f104c620a3d5b9664895 100644 (file)
@@ -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;