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.
This commit is contained in:
parent
da9ae10636
commit
ad59d8986a
19 changed files with 1115 additions and 65 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -637,8 +637,8 @@ private class Coordinate
|
|||
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:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
(require 'mouse)
|
||||
(require 'fontset)
|
||||
(require 'dnd)
|
||||
(require 'touch-screen)
|
||||
|
||||
(add-to-list 'display-format-alist '(".*" . android))
|
||||
|
||||
|
|
322
lisp/touch-screen.el
Normal file
322
lisp/touch-screen.el
Normal file
|
@ -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
|
282
src/android.c
282
src/android.c
|
@ -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)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
310
src/image.c
310
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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -73,15 +73,16 @@ sfntfont_android_mul8x2 (unsigned int a8, unsigned int b32)
|
|||
return (i + ((i >> 8) & 0xff00ff)) >> 8 & 0xff00ff;
|
||||
}
|
||||
|
||||
#define U255TO256(x) ((unsigned short) (x) + ((x) >> 7))
|
||||
|
||||
/* 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. */
|
||||
Both SRC and DST are expected to be in premultiplied ABGB8888
|
||||
format. Value is returned in premultiplied ARGB8888 format. */
|
||||
|
||||
static unsigned int
|
||||
sfntfont_android_blend (unsigned int src, unsigned int dst)
|
||||
{
|
||||
unsigned int a, br_part, ag_part, src_rb, both;
|
||||
unsigned int a, br_part, ag_part, both;
|
||||
|
||||
a = (src >> 24);
|
||||
br_part = sfntfont_android_mul8x2 (255 - a, dst);
|
||||
|
@ -89,33 +90,6 @@ sfntfont_android_blend (unsigned int src, unsigned int dst)
|
|||
|
||||
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
|
||||
format. Value is returned in premultiplied ARGB8888 format. */
|
||||
|
||||
static unsigned int
|
||||
sfntfont_android_blendrgb (unsigned int src, unsigned int dst)
|
||||
{
|
||||
unsigned int a, rb_part, ag_part, both;
|
||||
|
||||
a = (src >> 24);
|
||||
rb_part = sfntfont_android_mul8x2 (255 - a, dst);
|
||||
ag_part = sfntfont_android_mul8x2 (255 - a, dst >> 8) << 8;
|
||||
|
||||
both = ag_part | rb_part;
|
||||
|
||||
/* This addition need not be saturating because both has already
|
||||
been multiplied by 255 - a. */
|
||||
return both + src;
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue