aboutsummaryrefslogtreecommitdiffstats
path: root/src/android.c
diff options
context:
space:
mode:
authorPo Lu2022-12-31 18:04:18 +0800
committerPo Lu2022-12-31 18:04:18 +0800
commitcfbc8a5dbcd362b69b37b4e6832ae4a31834364c (patch)
treece003d03c4ae98f4e1d02186d78e5ae6e0d36e55 /src/android.c
parent785095c416f9bae43d2947849282b814e2c7942e (diff)
downloademacs-cfbc8a5dbcd362b69b37b4e6832ae4a31834364c.tar.gz
emacs-cfbc8a5dbcd362b69b37b4e6832ae4a31834364c.zip
Bring up the Android operating system and its window system
* .dir-locals.el (c-mode): Add ANDROID_EXPORT noise macro. * .gitignore: Add new files to ignore. * Makefile.in: Adjust for Android. * admin/merge-gnulib: Add new warning. * configure.ac: Detect Android. Run cross-configuration for Android when appropriate. * etc/DEBUG: Document how to debug Emacs on Android. * java/AndroidManifest.xml: * java/Makefile.in: * java/README: * java/debug.sh: * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication): * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea): * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine): * java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint): * java/org/gnu/emacs/EmacsDrawRectangle.java (EmacsDrawRectangle): * java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable): * java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon): * java/org/gnu/emacs/EmacsFillRectangle.java (EmacsFillRectangle): * java/org/gnu/emacs/EmacsFontDriver.java (EmacsFontDriver): * java/org/gnu/emacs/EmacsGC.java (EmacsGC): * java/org/gnu/emacs/EmacsHandleObject.java (EmacsHandleObject): * java/org/gnu/emacs/EmacsNative.java (EmacsNative): * java/org/gnu/emacs/EmacsPaintQueue.java (EmacsPaintQueue): * java/org/gnu/emacs/EmacsPaintReq.java (EmacsPaintReq): * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): * java/org/gnu/emacs/EmacsSdk7FontDriver.java (EmacsSdk7FontDriver): * java/org/gnu/emacs/EmacsService.java (class Holder<T>) (EmacsService): * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): * java/org/gnu/emacs/EmacsThread.java (EmacsThread): * java/org/gnu/emacs/EmacsView.java (EmacsView): * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): New files and classes. * lib-src/Makefile.in (srcdir): * lib/Makefile.in (VPATH): (HAVE_NATIVE_COMP): (libgnu_a_SOURCES): (DEPFLAGS): Configure correctly for cross-compiling. * lib/faccessat.c: * lib/fpending.c (__fpending): * lib/open.c: * lib/unistd.c (_GL_UNISTD_INLINE): Temporary adjustments to gnulib. * lisp/frame.el (display-graphic-p): (display-screens): (display-pixel-height): (display-pixel-width): (display-mm-height): (display-mm-width): (display-backing-store): (display-save-under): (display-planes): (display-color-cells): (display-visual-class): Adjust for new window system `android'. * lisp/image/wallpaper.el (x-open-connection): Add declaration. * lisp/loadup.el (featurep): Load up files for Android. * lisp/net/eww.el (eww-form-submit, eww-form-file) (eww-form-checkbox, eww-form-select): Adjust faces for android. * lisp/term/android-win.el: New file. * src/Makefile.in: Add new targets emacs.so and android-emacs, then adjust for cross compilation. * src/alloc.c (cleanup_vector): Clean up Android font entities as well. (garbage_collect): Mark androidterm. * src/android-emacs.c (main): * src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags) (struct android_emacs_service, struct android_emacs_pixmap) (struct android_graphics_point, struct android_event_container) (struct android_event_queue, android_run_select_thread) (android_handle_sigusr1, android_init_events, android_pending) (android_next_event, android_write_event, android_select) (android_run_debug_thread, android_user_full_name) (android_get_asset_name, android_fstat, android_fstatat) (android_file_access_p, android_hack_asset_fd, android_open) (android_close, JNICALL, android_init_emacs_service) (android_init_emacs_pixmap, android_init_graphics_point) (MAX_HANDLE, struct android_handle_entry, android_alloc_id) (android_destroy_handle, android_resolve_handle) (android_resolve_handle2, android_change_window_attributes) (android_create_window, android_set_window_background) (android_destroy_window, android_init_android_rect_class) (android_init_emacs_gc_class, android_create_gc, android_free_gc) (android_change_gc, android_set_clip_rectangles) (android_reparent_window, android_lookup_method) (android_clear_window, android_map_window, android_unmap_window) (android_resize_window, android_move_window, android_swap_buffers) (android_get_gc_values, android_set_foreground) (android_fill_rectangle, android_create_pixmap_from_bitmap_data) (android_set_clip_mask, android_set_fill_style, android_copy_area) (android_free_pixmap, android_set_background, android_fill_polygon) (android_draw_rectangle, android_draw_point, android_draw_line) (android_create_pixmap, android_set_ts_origin, android_clear_area): * src/android.h (ANDROID_EXPORT): * src/androidfns.c (android_display_info_for_name) (check_android_display_info, check_x_display_info, gamma_correct) (android_defined_color, android_decode_color) (android_implicitly_set_name, android_explicitly_set_name) (android_set_tool_bar_lines, android_change_tool_bar_height) (android_set_tab_bar_lines, android_change_tab_bar_height) (android_set_scroll_bar_default_height) (android_set_scroll_bar_default_width, android_icon_verify) (android_icon, android_make_gc, android_free_gcs) (unwind_create_frame, do_unwind_create_frame) (android_default_font_parameter, android_create_frame_window) (Fx_create_frame, Fxw_color_defined_p, Fxw_color_values) (Fxw_display_color_p, Fx_display_grayscale_p) (Fx_display_pixel_width, Fx_display_pixel_height) (Fx_display_planes, Fx_display_color_cells, Fx_display_screens) (Fx_display_mm_width, Fx_display_mm_height) (Fx_display_backing_store, Fx_display_visual_class) (Fx_display_monitor_attributes_list, Fx_frame_geometry) (Fx_frame_list_z_order, Fx_frame_restack) (Fx_mouse_absolute_pixel_position) (Fx_set_mouse_absolute_pixel_position, Fandroid_get_connection) (Fx_display_list, Fx_show_tip, Fx_hide_tip) (android_set_background_color, android_set_border_color) (android_set_cursor_color, android_set_cursor_type) (android_set_foreground_color) (android_set_child_frame_border_width) (android_set_internal_border_width, android_set_menu_bar_lines) (android_set_mouse_color, android_set_title, android_set_alpha) (android_frame_parm_handlers, syms_of_androidfns): * src/androidfont.c (struct android_emacs_font_driver) (struct android_emacs_font_spec, struct android_emacs_font_metrics) (struct android_emacs_font_object, struct android_integer) (struct androidfont_info, struct androidfont_entity) (android_init_font_driver, android_init_font_spec) (android_init_font_metrics, android_init_integer) (android_init_font_object, androidfont_get_cache) (androidfont_from_lisp, androidfont_from_java, androidfont_list) (androidfont_match, androidfont_draw, androidfont_open_font) (androidfont_close_font, androidfont_has_char) (androidfont_encode_char, androidfont_text_extents) (androidfont_list_family, androidfont_driver) (syms_of_androidfont_for_pdumper, syms_of_androidfont) (init_androidfont, android_finalize_font_entity): * src/androidgui.h (_ANDROID_GUI_H_, struct android_rectangle) (struct android_point, enum android_gc_function) (enum android_gc_value_mask, enum android_fill_style) (enum android_window_value_mask) (struct android_set_window_attributes, struct android_gc_values) (struct android_gc, enum android_swap_action, enum android_shape) (enum android_coord_mode, struct android_swap_info) (NativeRectangle, struct android_any_event) (struct android_key_event, struct android_configure_event) (union android_event): * src/androidterm.c (android_window_to_frame, android_clear_frame) (android_ring_bell, android_toggle_invisible_pointer) (android_update_begin, android_update_end, show_back_buffer) (android_flush_dirty_back_buffer_on, handle_one_android_event) (android_read_socket, android_frame_up_to_date) (android_buffer_flipping_unblocked_hook) (android_query_frame_background_color, android_parse_color) (android_alloc_nearest_color, android_query_colors) (android_mouse_position, android_get_focus_frame) (android_focus_frame, android_frame_rehighlight) (android_frame_raise_lower, android_make_frame_visible) (android_make_frame_invisible) (android_make_frame_visible_invisible, android_fullscreen_hook) (android_iconify_frame, android_set_window_size_1) (android_set_window_size, android_set_offset, android_set_alpha) (android_new_font, android_bitmap_icon, android_free_pixmap_hook) (android_free_frame_resources, android_delete_frame) (android_delete_terminal, android_scroll_run) (android_after_update_window_line, android_flip_and_flush) (android_clear_rectangle, android_reset_clip_rectangles) (android_clip_to_row, android_draw_fringe_bitmap) (android_set_cursor_gc, android_set_mouse_face_gc) (android_set_mode_line_face_gc, android_set_glyph_string_gc) (android_set_glyph_string_clipping) (android_set_glyph_string_clipping_exactly) (android_compute_glyph_string_overhangs) (android_clear_glyph_string_rect) (android_draw_glyph_string_background, android_fill_triangle) (android_make_point, android_inside_rect_p, android_clear_point) (android_draw_relief_rect, android_draw_box_rect) (HIGHLIGHT_COLOR_DARK_BOOST_LIMIT, android_setup_relief_color) (android_setup_relief_colors, android_draw_glyph_string_box) (android_draw_glyph_string_bg_rect, android_draw_image_relief) (android_draw_image_foreground, android_draw_image_foreground_1) (android_draw_image_glyph_string) (android_draw_stretch_glyph_string, android_draw_underwave) (android_draw_glyph_string_foreground) (android_draw_composite_glyph_string_foreground) (android_draw_glyphless_glyph_string_foreground) (android_draw_glyph_string, android_define_frame_cursor) (android_clear_frame_area, android_clear_under_internal_border) (android_draw_hollow_cursor, android_draw_bar_cursor) (android_draw_window_cursor, android_draw_vertical_window_border) (android_draw_window_divider, android_redisplay_interface) (frame_set_mouse_pixel_position, get_keysym_name) (android_create_terminal, android_term_init, syms_of_androidterm) (mark_androidterm): * src/androidterm.h (_ANDROID_TERM_H_, struct android_display_info) (struct android_output, FRAME_ANDROID_OUTPUT, XSCROLL_BAR): New files. * src/dired.c (file_attributes): Do not use openat on Android. * src/dispextern.h (No_Cursor): Define appropriately on Android. (struct glyph_string, struct face): Make gc field of type struct android_gc on Android. * src/dispnew.c (clear_current_matrices, clear_desired_matrices) (adjust_frame_glyphs_for_window_redisplay, free_glyphs) (update_frame, scrolling, char_ins_del_cost, update_frame_line) (init_display_interactive): Disable text terminal support completely on Android. Fix non-toolkit menus for non-X systems. * src/editfns.c (Fuser_full_name): Call android_user_full_name. * src/emacs.c (android_emacs_init): Make main this on Android. Prohibit argv sorting from exceeding end of argv. * src/epaths.in: Add path definitions for Android. * src/fileio.c (file_access_p): Call android_file_access_p. (file_name_directory): Avoid using openat on Android. (Fcopy_file): Adjust to call sys_fstat instead. (file_directory_p): (Finsert_file_contents): (write_region): Likewise. * src/filelock.c: * src/fns.c (Flocale_info): Pacify warning on Android. * src/font.c (font_make_entity_android): New function. * src/font.h: * src/frame.c (Fframep): (Fwindow_system): Handle new window system `android'. Update doc strings. (Fmake_terminal_frame): Disable on Android. (gui_display_get_resource): Disable get_string_resource_hook on Android. (syms_of_frame): New defsym `android'. * src/frame.h (GCALIGNED_STRUCT): Add new output data for Android. (ENUM_BF): Expand enumerator size. (FRAME_ANDROID_P, FRAME_WINDOW_P, MOUSE_HL_INFO): Add definitions for Android. * src/image.c (GET_PIXEL): (image_create_bitmap_from_file): (image_create_x_image_and_pixmap_1): (image_get_x_image): (slurp_file): (lookup_rgb_color): (image_to_emacs_colors): (image_from_emacs_colors): (image_pixmap_draw_cross): (image_disable_image): (MaskForeground): (gif_load): Add stubs for Android. * src/lisp.h: * src/lread.c (safe_to_load_version, maybe_swap_for_eln1, openp): * src/pdumper.c (pdumper_load): Call sys_fstat instead of fstat. * src/process.c (wait_reading_process_output): Use android_select instead of pselect. * src/scroll.c: Disable on Android. * src/sysdep.c (widen_foreground_group, reset_sys_modes) (init_signals, emacs_fstatat, sys_fstat): New function. (emacs_open, emacs_open_noquit, emacs_close): Implement differently on Android. (close_output_streams): Disable what is not required on Android. * src/term.c (OUTPUT1_IF, encode_terminal_code, string_cost) (string_cost_one_line, per_line_cost, calculate_costs) (struct fkey_table, tty_append_glyph, produce_glyphs) (tty_capable_p, Fsuspend_tty, Fresume_tty, device, init_tty) (maybe_fatal, syms_of_term): Disable text terminal support on Android. * src/termhooks.h (enum output_method): Add android output method. (GCALIGNED_STRUCT, TERMINAL_FONT_CACHE): Define for Android. * src/terminal.c (Fterminal_live_p): Implement for Android. * src/verbose.mk.in (AM_V_GLOBALS): Add JAVAC and DX. * src/xdisp.c (redisplay_internal): Disable text terminals on Android. (display_menu_bar): (display_tty_menu_item): (draw_row_with_mouse_face): (expose_frame): Make the non toolkit menu bar work on Android. * src/xfaces.c (GCGraphicsExposures): (x_create_gc): (x_free_gc): (Fx_load_color_file): Define for Android. * xcompile/Makefile.in (top_srcdir): (top_builddir): * xcompile/README: * xcompile/langinfo.h (nl_langinfo): New files.
Diffstat (limited to 'src/android.c')
-rw-r--r--src/android.c2335
1 files changed, 2335 insertions, 0 deletions
diff --git a/src/android.c b/src/android.c
new file mode 100644
index 00000000000..dd841cf383a
--- /dev/null
+++ b/src/android.c
@@ -0,0 +1,2335 @@
1/* Android initialization for GNU Emacs.
2
3Copyright (C) 2023 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20#include <config.h>
21#include <fcntl.h>
22#include <unistd.h>
23#include <pthread.h>
24#include <limits.h>
25#include <signal.h>
26#include <semaphore.h>
27
28#include <sys/stat.h>
29#include <sys/mman.h>
30
31#include <assert.h>
32
33#include "android.h"
34#include "androidgui.h"
35
36#include "lisp.h"
37#include "blockinput.h"
38#include "coding.h"
39#include "epaths.h"
40
41/* Whether or not Emacs is running inside the application process and
42 Android windowing should be enabled. */
43bool android_init_gui;
44
45#ifndef ANDROID_STUBIFY
46
47#include <android/asset_manager.h>
48#include <android/asset_manager_jni.h>
49#include <android/log.h>
50
51#include <linux/ashmem.h>
52
53#define ANDROID_THROW(env, class, msg) \
54 ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg))
55
56#define ANDROID_MAX_ASSET_FD 65535
57
58struct android_fd_table_entry
59{
60 /* Various flags associated with this table. */
61 short flags;
62
63 /* The stat buffer associated with this entry. */
64 struct stat statb;
65};
66
67enum android_fd_table_entry_flags
68 {
69 ANDROID_FD_TABLE_ENTRY_IS_VALID = 1,
70 };
71
72struct android_emacs_service
73{
74 jclass class;
75 jmethodID fill_rectangle;
76 jmethodID fill_polygon;
77 jmethodID draw_rectangle;
78 jmethodID draw_line;
79 jmethodID draw_point;
80 jmethodID copy_area;
81 jmethodID clear_window;
82 jmethodID clear_area;
83};
84
85struct android_emacs_pixmap
86{
87 jclass class;
88 jmethodID constructor;
89};
90
91struct android_graphics_point
92{
93 jclass class;
94 jmethodID constructor;
95};
96
97/* The asset manager being used. */
98static AAssetManager *asset_manager;
99
100/* Whether or not Emacs has been initialized. */
101static int emacs_initialized;
102
103/* The path used to store site-lisp. */
104char *android_site_load_path;
105
106/* The path used to store native libraries. */
107char *android_lib_dir;
108
109/* The Android application data directory. */
110static char *android_files_dir;
111
112/* Array of structures used to hold asset information corresponding to
113 a file descriptor. This assumes Emacs does not do funny things
114 with dup. It currently does not. */
115static struct android_fd_table_entry android_table[ANDROID_MAX_ASSET_FD];
116
117/* The Java environment being used for the main thread. */
118JNIEnv *android_java_env;
119
120/* The EmacsGC class. */
121static jclass emacs_gc_class;
122
123/* Various fields. */
124static jfieldID emacs_gc_foreground, emacs_gc_background;
125static jfieldID emacs_gc_function, emacs_gc_clip_rects;
126static jfieldID emacs_gc_clip_x_origin, emacs_gc_clip_y_origin;
127static jfieldID emacs_gc_stipple, emacs_gc_clip_mask;
128static jfieldID emacs_gc_fill_style, emacs_gc_ts_origin_x;
129static jfieldID emacs_gc_ts_origin_y;
130
131/* The constructor and one function. */
132static jmethodID emacs_gc_constructor, emacs_gc_mark_dirty;
133
134/* The Rect class. */
135static jclass android_rect_class;
136
137/* Its constructor. */
138static jmethodID android_rect_constructor;
139
140/* The EmacsService object. */
141static jobject emacs_service;
142
143/* Various methods associated with the EmacsService. */
144static struct android_emacs_service service_class;
145
146/* Various methods associated with the EmacsPixmap class. */
147static struct android_emacs_pixmap pixmap_class;
148
149/* Various methods associated with the Point class. */
150static struct android_graphics_point point_class;
151
152
153
154/* Event handling functions. Events are stored on a (circular) queue
155 that is read synchronously. The Android port replaces pselect with
156 a function android_select, which runs pselect in a separate thread,
157 but more importantly also waits for events to be available on the
158 android event queue. */
159
160struct android_event_container
161{
162 /* The next and last events in this queue. */
163 struct android_event_container *volatile next, *last;
164
165 /* The event itself. */
166 union android_event event;
167};
168
169struct android_event_queue
170{
171 /* Mutex protecting the event queue. */
172 pthread_mutex_t mutex;
173
174 /* Mutex protecting the select data. */
175 pthread_mutex_t select_mutex;
176
177 /* The thread used to run select. */
178 pthread_t select_thread;
179
180 /* Condition variable for the writing side. */
181 pthread_cond_t write_var;
182
183 /* Condition variables for the reading side. */
184 pthread_cond_t read_var;
185
186 /* The number of events in the queue. If this is greater than 1024,
187 writing will block. */
188 volatile int num_events;
189
190 /* Circular queue of events. */
191 struct android_event_container events;
192};
193
194/* Arguments to pselect used by the select thread. */
195static volatile int android_pselect_nfds;
196static fd_set *volatile android_pselect_readfds;
197static fd_set *volatile android_pselect_writefds;
198static fd_set *volatile android_pselect_exceptfds;
199static struct timespec *volatile android_pselect_timeout;
200static const sigset_t *volatile android_pselect_sigset;
201
202/* Value of pselect. */
203static int android_pselect_rc;
204
205/* Whether or not pselect finished. */
206static volatile bool android_pselect_completed;
207
208/* The global event queue. */
209static struct android_event_queue event_queue;
210
211/* Semaphore used to signal select completion. */
212static sem_t android_pselect_sem;
213
214static void *
215android_run_select_thread (void *data)
216{
217 sigset_t signals;
218 int sig, rc;
219
220 sigfillset (&signals);
221
222 if (pthread_sigmask (SIG_BLOCK, &signals, NULL))
223 __android_log_print (ANDROID_LOG_FATAL, __func__,
224 "pthread_sigmask: %s",
225 strerror (errno));
226
227 sigemptyset (&signals);
228 sigaddset (&signals, SIGUSR1);
229
230 if (pthread_sigmask (SIG_UNBLOCK, &signals, NULL))
231 __android_log_print (ANDROID_LOG_FATAL, __func__,
232 "pthread_sigmask: %s",
233 strerror (errno));
234
235 sigemptyset (&signals);
236 sigaddset (&signals, SIGUSR2);
237
238 while (true)
239 {
240 /* Keep waiting for SIGUSR2, ignoring EINTR in the meantime. */
241
242 while (sigwait (&signals, &sig))
243 /* Spin. */;
244
245 /* Get the select lock and call pselect. */
246 pthread_mutex_lock (&event_queue.select_mutex);
247 rc = pselect (android_pselect_nfds,
248 android_pselect_readfds,
249 android_pselect_writefds,
250 android_pselect_exceptfds,
251 android_pselect_timeout,
252 android_pselect_sigset);
253 android_pselect_rc = rc;
254 pthread_mutex_unlock (&event_queue.select_mutex);
255
256 /* Signal the Emacs thread that pselect is done. If read_var
257 was signaled by android_write_event, event_queue.mutex could
258 still be locked, so this must come before. */
259 sem_post (&android_pselect_sem);
260
261 pthread_mutex_lock (&event_queue.mutex);
262 android_pselect_completed = true;
263 pthread_cond_signal (&event_queue.read_var);
264 pthread_mutex_unlock (&event_queue.mutex);
265 }
266}
267
268static void
269android_handle_sigusr1 (int sig, siginfo_t *siginfo, void *arg)
270{
271 /* Nothing to do here, this signal handler is only installed to make
272 sure the disposition of SIGUSR1 is enough. */
273}
274
275/* Set up the global event queue by initializing the mutex and two
276 condition variables, and the linked list of events. This must be
277 called before starting the Emacs thread. Also, initialize the
278 thread used to run pselect.
279
280 These functions must also use the C library malloc and free,
281 because xmalloc is not thread safe. */
282
283static void
284android_init_events (void)
285{
286 struct sigaction sa;
287
288 if (pthread_mutex_init (&event_queue.mutex, NULL))
289 __android_log_print (ANDROID_LOG_FATAL, __func__,
290 "pthread_mutex_init: %s",
291 strerror (errno));
292
293 if (pthread_mutex_init (&event_queue.select_mutex, NULL))
294 __android_log_print (ANDROID_LOG_FATAL, __func__,
295 "pthread_mutex_init: %s",
296 strerror (errno));
297
298 if (pthread_cond_init (&event_queue.write_var, NULL))
299 __android_log_print (ANDROID_LOG_FATAL, __func__,
300 "pthread_cond_init: %s",
301 strerror (errno));
302
303 if (pthread_cond_init (&event_queue.read_var, NULL))
304 __android_log_print (ANDROID_LOG_FATAL, __func__,
305 "pthread_cond_init: %s",
306 strerror (errno));
307
308 sem_init (&android_pselect_sem, 0, 0);
309
310 event_queue.events.next = &event_queue.events;
311 event_queue.events.last = &event_queue.events;
312
313 /* Before starting the select thread, make sure the disposition for
314 SIGUSR1 is correct. */
315 sigfillset (&sa.sa_mask);
316 sa.sa_sigaction = android_handle_sigusr1;
317 sa.sa_flags = SA_SIGINFO;
318
319 if (sigaction (SIGUSR1, &sa, NULL))
320 __android_log_print (ANDROID_LOG_FATAL, __func__,
321 "sigaction: %s",
322 strerror (errno));
323
324 /* Start the select thread. */
325 if (pthread_create (&event_queue.select_thread, NULL,
326 android_run_select_thread, NULL))
327 __android_log_print (ANDROID_LOG_FATAL, __func__,
328 "pthread_create: %s",
329 strerror (errno));
330}
331
332int
333android_pending (void)
334{
335 int i;
336
337 pthread_mutex_lock (&event_queue.mutex);
338 i = event_queue.num_events;
339 pthread_mutex_unlock (&event_queue.mutex);
340
341 return i;
342}
343
344void
345android_next_event (union android_event *event_return)
346{
347 struct android_event_container *container;
348
349 pthread_mutex_lock (&event_queue.mutex);
350
351 /* Wait for events to appear if there are none available to
352 read. */
353 if (!event_queue.num_events)
354 pthread_cond_wait (&event_queue.read_var,
355 &event_queue.mutex);
356
357 /* Obtain the event from the end of the queue. */
358 container = event_queue.events.last;
359 eassert (container != &event_queue.events);
360
361 /* Remove the event from the queue and copy it to the caller
362 supplied buffer. */
363 container->last->next = container->next;
364 container->next->last = container->last;
365 *event_return = container->event;
366 event_queue.num_events--;
367
368 /* Free the container. */
369 free (container);
370
371 /* Signal that events can now be written. */
372 pthread_cond_signal (&event_queue.write_var);
373 pthread_mutex_unlock (&event_queue.mutex);
374}
375
376static void
377android_write_event (union android_event *event)
378{
379 struct android_event_container *container;
380
381 container = malloc (sizeof *container);
382
383 if (!container)
384 return;
385
386 pthread_mutex_lock (&event_queue.mutex);
387
388 /* The event queue is full, wait for events to be read. */
389 if (event_queue.num_events >= 1024)
390 pthread_cond_wait (&event_queue.write_var,
391 &event_queue.mutex);
392
393 container->next = event_queue.events.next;
394 container->last = &event_queue.events;
395 container->next->last = container;
396 container->last->next = container;
397 container->event = *event;
398 event_queue.num_events++;
399 pthread_cond_signal (&event_queue.read_var);
400 pthread_mutex_unlock (&event_queue.mutex);
401}
402
403int
404android_select (int nfds, fd_set *readfds, fd_set *writefds,
405 fd_set *exceptfds, struct timespec *timeout,
406 const sigset_t *sigset)
407{
408 int nfds_return;
409
410 pthread_mutex_lock (&event_queue.mutex);
411
412 if (event_queue.num_events)
413 {
414 pthread_mutex_unlock (&event_queue.mutex);
415 return 1;
416 }
417
418 nfds_return = 0;
419 android_pselect_completed = false;
420
421 pthread_mutex_lock (&event_queue.select_mutex);
422 android_pselect_nfds = nfds;
423 android_pselect_readfds = readfds;
424 android_pselect_writefds = writefds;
425 android_pselect_exceptfds = exceptfds;
426 android_pselect_timeout = timeout;
427 android_pselect_sigset = sigset;
428 pthread_mutex_unlock (&event_queue.select_mutex);
429
430 pthread_kill (event_queue.select_thread, SIGUSR2);
431 pthread_cond_wait (&event_queue.read_var, &event_queue.mutex);
432
433 /* Interrupt the select thread now, in case it's still in
434 pselect. */
435 pthread_kill (event_queue.select_thread, SIGUSR1);
436
437 /* Wait for pselect to return in any case. */
438 sem_wait (&android_pselect_sem);
439
440 /* If there are now events in the queue, return 1. */
441 if (event_queue.num_events)
442 nfds_return = 1;
443
444 /* Add the return value of pselect. */
445 if (android_pselect_rc >= 0)
446 nfds_return += android_pselect_rc;
447
448 if (!nfds_return && android_pselect_rc < 0)
449 nfds_return = android_pselect_rc;
450
451 /* Unlock the event queue mutex. */
452 pthread_mutex_unlock (&event_queue.mutex);
453
454 return nfds_return;
455}
456
457
458
459static void *
460android_run_debug_thread (void *data)
461{
462 FILE *file;
463 int fd;
464 char *line;
465 size_t n;
466
467 fd = (int) data;
468 file = fdopen (fd, "r");
469
470 if (!file)
471 return NULL;
472
473 line = NULL;
474
475 while (true)
476 {
477 if (getline (&line, &n, file) < 0)
478 {
479 free (line);
480 break;
481 }
482
483 __android_log_print (ANDROID_LOG_INFO, __func__, "%s", line);
484 }
485
486 fclose (file);
487 return NULL;
488}
489
490
491
492/* Intercept USER_FULL_NAME and return something that makes sense if
493 pw->pw_gecos is NULL. */
494
495char *
496android_user_full_name (struct passwd *pw)
497{
498 if (!pw->pw_gecos)
499 return (char *) "Android user";
500
501 return pw->pw_gecos;
502}
503
504/* Given a real file name, return the part that describes its asset
505 path, or NULL if it is not an asset. */
506
507static const char *
508android_get_asset_name (const char *filename)
509{
510 if (!strcmp (filename, "/assets") || !strcmp (filename, "/assets/"))
511 return "/";
512
513 if (!strncmp (filename, "/assets/", sizeof "/assets/" - 1))
514 return filename + (sizeof "/assets/" - 1);
515
516 return NULL;
517}
518
519/* Like fstat. However, look up the asset corresponding to the file
520 descriptor. If it exists, return the right information. */
521
522int
523android_fstat (int fd, struct stat *statb)
524{
525 if (fd < ANDROID_MAX_ASSET_FD
526 && (android_table[fd].flags
527 & ANDROID_FD_TABLE_ENTRY_IS_VALID))
528 {
529 memcpy (statb, &android_table[fd].statb,
530 sizeof *statb);
531 return 0;
532 }
533
534 return fstat (fd, statb);
535}
536
537/* Like fstatat. However, if dirfd is AT_FDCWD and PATHNAME is an
538 asset, find the information for the corresponding asset. */
539
540int
541android_fstatat (int dirfd, const char *restrict pathname,
542 struct stat *restrict statbuf, int flags)
543{
544 AAsset *asset_desc;
545 const char *asset;
546
547 if (dirfd == AT_FDCWD
548 && asset_manager
549 && (asset = android_get_asset_name (pathname)))
550 {
551 /* AASSET_MODE_STREAMING is fastest here. */
552 asset_desc = AAssetManager_open (asset_manager, asset,
553 AASSET_MODE_STREAMING);
554
555 if (!asset_desc)
556 return ENOENT;
557
558 memset (statbuf, 0, sizeof *statbuf);
559
560 /* Fill in the stat buffer. */
561 statbuf->st_mode = S_IFREG;
562 statbuf->st_size = AAsset_getLength (asset_desc);
563
564 /* Close the asset. */
565 AAsset_close (asset_desc);
566 return 0;
567 }
568
569 return fstatat (dirfd, pathname, statbuf, flags);
570}
571
572/* Return if NAME is a file that is actually an asset and is
573 accessible, as long as !(amode & W_OK). */
574
575bool
576android_file_access_p (const char *name, int amode)
577{
578 AAsset *asset;
579 AAssetDir *directory;
580
581 if (!asset_manager)
582 return false;
583
584 if (!(amode & W_OK) && (name = android_get_asset_name (name)))
585 {
586 /* Check if the asset exists by opening it. Suboptimal! */
587 asset = AAssetManager_open (asset_manager, name,
588 AASSET_MODE_UNKNOWN);
589
590 if (!asset)
591 {
592 /* See if it's a directory also. */
593 directory = AAssetManager_openDir (asset_manager, name);
594
595 if (directory)
596 {
597 AAssetDir_close (directory);
598 return true;
599 }
600
601 return false;
602 }
603
604 AAsset_close (asset);
605 return true;
606 }
607
608 return false;
609}
610
611/* Get a file descriptor backed by a temporary in-memory file for the
612 given asset. */
613
614static int
615android_hack_asset_fd (AAsset *asset)
616{
617 int fd, rc;
618 unsigned char *mem;
619 size_t size;
620
621 fd = open ("/dev/ashmem", O_RDWR);
622
623 if (fd < 0)
624 return -1;
625
626 /* Assets must be small enough to fit in size_t, if off_t is
627 larger. */
628 size = AAsset_getLength (asset);
629
630 /* An empty name means the memory area will exist until the file
631 descriptor is closed, because no other process can attach. */
632 rc = ioctl (fd, ASHMEM_SET_NAME, "");
633
634 if (rc < 0)
635 {
636 __android_log_print (ANDROID_LOG_ERROR, __func__,
637 "ioctl ASHMEM_SET_NAME: %s",
638 strerror (errno));
639 close (fd);
640 return -1;
641 }
642
643 rc = ioctl (fd, ASHMEM_SET_SIZE, size);
644
645 if (rc < 0)
646 {
647 __android_log_print (ANDROID_LOG_ERROR, __func__,
648 "ioctl ASHMEM_SET_SIZE: %s",
649 strerror (errno));
650 close (fd);
651 return -1;
652 }
653
654 if (!size)
655 return fd;
656
657 /* Now map the resource. */
658 mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
659 if (mem == MAP_FAILED)
660 {
661 __android_log_print (ANDROID_LOG_ERROR, __func__,
662 "mmap: %s", strerror (errno));
663 close (fd);
664 return -1;
665 }
666
667 if (AAsset_read (asset, mem, size) != size)
668 {
669 /* Too little was read. Close the file descriptor and report an
670 error. */
671 __android_log_print (ANDROID_LOG_ERROR, __func__,
672 "AAsset_read: %s", strerror (errno));
673 close (fd);
674 return -1;
675 }
676
677 /* Return anyway even if munmap fails. */
678 munmap (mem, size);
679 return fd;
680}
681
682/* `open' and such are modified even though they exist on Android,
683 because Emacs treats "/assets/" as a special directory that must
684 contain all assets in the application package. */
685
686int
687android_open (const char *filename, int oflag, int mode)
688{
689 const char *name;
690 AAsset *asset;
691 int fd;
692 off_t out_start, out_length;
693 bool fd_hacked;
694
695 /* This flag means whether or not fd should not be duplicated. */
696 fd_hacked = false;
697
698 if (asset_manager && (name = android_get_asset_name (filename)))
699 {
700 /* If Emacs is trying to write to the file, return NULL. */
701
702 if (oflag & O_WRONLY || oflag & O_RDWR)
703 {
704 errno = EROFS;
705 return -1;
706 }
707
708 if (oflag & O_DIRECTORY)
709 {
710 errno = EINVAL;
711 return -1;
712 }
713
714 asset = AAssetManager_open (asset_manager, name,
715 AASSET_MODE_BUFFER);
716
717 if (!asset)
718 {
719 errno = ENOENT;
720 return -1;
721 }
722
723 /* Try to obtain the file descriptor corresponding to this
724 asset. */
725 fd = AAsset_openFileDescriptor (asset, &out_start,
726 &out_length);
727
728 if (fd == -1)
729 {
730 /* The asset can't be accessed for some reason. Try to
731 create a shared memory file descriptor. */
732 fd = android_hack_asset_fd (asset);
733
734 if (fd == -1)
735 {
736 AAsset_close (asset);
737 errno = ENXIO;
738 return -1;
739 }
740
741 fd_hacked = true;
742 }
743
744 /* Duplicate the file descriptor and then close the asset,
745 which will close the original file descriptor. */
746
747 if (!fd_hacked)
748 fd = fcntl (fd, F_DUPFD_CLOEXEC);
749
750 if (fd >= ANDROID_MAX_ASSET_FD || fd < 0)
751 {
752 /* Too bad. You lose. */
753 errno = ENOMEM;
754
755 if (fd >= 0)
756 close (fd);
757
758 fd = -1;
759 }
760 else
761 {
762 assert (!(android_table[fd].flags
763 & ANDROID_FD_TABLE_ENTRY_IS_VALID));
764 android_table[fd].flags = ANDROID_FD_TABLE_ENTRY_IS_VALID;
765 memset (&android_table[fd].statb, 0,
766 sizeof android_table[fd].statb);
767
768 /* Fill in some information that will be reported to
769 callers of android_fstat, among others. */
770 android_table[fd].statb.st_mode = S_IFREG;
771
772 /* Owned by root. */
773 android_table[fd].statb.st_uid = 0;
774 android_table[fd].statb.st_gid = 0;
775
776 /* Size of the file. */
777 android_table[fd].statb.st_size
778 = AAsset_getLength (asset);
779 }
780
781 AAsset_close (asset);
782 return fd;
783 }
784
785 return open (filename, oflag, mode);
786}
787
788/* Like close. However, remove the file descriptor from the asset
789 table as well. */
790
791int
792android_close (int fd)
793{
794 if (fd < ANDROID_MAX_ASSET_FD
795 && (android_table[fd].flags & ANDROID_FD_TABLE_ENTRY_IS_VALID))
796 {
797 __android_log_print (ANDROID_LOG_INFO, __func__,
798 "closing android file descriptor %d",
799 fd);
800 android_table[fd].flags = 0;
801 }
802
803 return close (fd);
804}
805
806
807
808/* JNI functions called by Java. */
809
810#pragma clang diagnostic push
811#pragma clang diagnostic ignored "-Wmissing-prototypes"
812
813JNIEXPORT void JNICALL
814NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
815 jobject local_asset_manager,
816 jobject files_dir, jobject libs_dir,
817 jobject emacs_service_object)
818{
819 int pipefd[2];
820 pthread_t thread;
821 const char *java_string;
822
823 /* This may be called from multiple threads. setEmacsParams should
824 only ever be called once. */
825 if (__atomic_fetch_add (&emacs_initialized, -1, __ATOMIC_RELAXED))
826 {
827 ANDROID_THROW (env, "java/lang/IllegalArgumentException",
828 "Emacs was already initialized!");
829 return;
830 }
831
832 __android_log_print (ANDROID_LOG_INFO, __func__,
833 "Initializing "PACKAGE_STRING"...\nPlease report bugs to "
834 PACKAGE_BUGREPORT". Thanks.\n");
835
836 /* Set the asset manager. */
837 asset_manager = AAssetManager_fromJava (env, local_asset_manager);
838
839 /* Hold a VM reference to the asset manager to prevent the native
840 object from being deleted. */
841 (*env)->NewGlobalRef (env, local_asset_manager);
842
843 /* Create a pipe and duplicate it to stdout and stderr. Next, make
844 a thread that prints stderr to the system log. */
845
846 if (pipe2 (pipefd, O_CLOEXEC) < 0)
847 emacs_abort ();
848
849 if (dup2 (pipefd[1], 2) < 0)
850 emacs_abort ();
851 close (pipefd[1]);
852
853 if (pthread_create (&thread, NULL, android_run_debug_thread,
854 (void *) pipefd[0]))
855 emacs_abort ();
856
857 /* Now set the path to the site load directory. */
858
859 java_string = (*env)->GetStringUTFChars (env, (jstring) files_dir,
860 NULL);
861
862 if (!java_string)
863 emacs_abort ();
864
865 android_files_dir = strdup ((const char *) java_string);
866
867 if (!android_files_dir)
868 emacs_abort ();
869
870 (*env)->ReleaseStringUTFChars (env, (jstring) files_dir,
871 java_string);
872
873 java_string = (*env)->GetStringUTFChars (env, (jstring) libs_dir,
874 NULL);
875
876 if (!java_string)
877 emacs_abort ();
878
879 android_lib_dir = strdup ((const char *) java_string);
880
881 if (!android_files_dir)
882 emacs_abort ();
883
884 (*env)->ReleaseStringUTFChars (env, (jstring) libs_dir,
885 java_string);
886
887 /* Calculate the site-lisp path. */
888
889 android_site_load_path = malloc (PATH_MAX + 1);
890
891 if (!android_site_load_path)
892 emacs_abort ();
893
894 snprintf (android_site_load_path, PATH_MAX, "%s/site-lisp",
895 android_files_dir);
896 __android_log_print (ANDROID_LOG_INFO, __func__,
897 "Site-lisp directory: %s\n"
898 "Files directory: %s\n"
899 "Native code directory: %s",
900 android_site_load_path,
901 android_files_dir,
902 android_lib_dir);
903
904 /* Make a reference to the Emacs service. */
905 emacs_service = (*env)->NewGlobalRef (env, emacs_service_object);
906
907 if (!emacs_service)
908 emacs_abort ();
909
910 /* Set up events. */
911 android_init_events ();
912
913 /* OK, setup is now complete. The caller may start the Emacs thread
914 now. */
915}
916
917/* Initialize service_class, aborting if something goes wrong. */
918
919static void
920android_init_emacs_service (void)
921{
922 jclass old;
923
924 service_class.class
925 = (*android_java_env)->FindClass (android_java_env,
926 "org/gnu/emacs/EmacsService");
927 eassert (service_class.class);
928
929 old = service_class.class;
930 service_class.class
931 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
932 (jobject) old);
933 ANDROID_DELETE_LOCAL_REF (old);
934
935 if (!service_class.class)
936 emacs_abort ();
937
938#define FIND_METHOD(c_name, name, signature) \
939 service_class.c_name \
940 = (*android_java_env)->GetMethodID (android_java_env, \
941 service_class.class, \
942 name, signature); \
943 assert (service_class.c_name);
944
945 FIND_METHOD (fill_rectangle, "fillRectangle",
946 "(Lorg/gnu/emacs/EmacsDrawable;"
947 "Lorg/gnu/emacs/EmacsGC;IIII)V");
948 FIND_METHOD (fill_polygon, "fillPolygon",
949 "(Lorg/gnu/emacs/EmacsDrawable;"
950 "Lorg/gnu/emacs/EmacsGC;"
951 "[Landroid/graphics/Point;)V");
952 FIND_METHOD (draw_rectangle, "drawRectangle",
953 "(Lorg/gnu/emacs/EmacsDrawable;"
954 "Lorg/gnu/emacs/EmacsGC;IIII)V");
955 FIND_METHOD (draw_line, "drawLine",
956 "(Lorg/gnu/emacs/EmacsDrawable;"
957 "Lorg/gnu/emacs/EmacsGC;IIII)V");
958 FIND_METHOD (draw_point, "drawPoint",
959 "(Lorg/gnu/emacs/EmacsDrawable;"
960 "Lorg/gnu/emacs/EmacsGC;II)V");
961 FIND_METHOD (copy_area, "copyArea",
962 "(Lorg/gnu/emacs/EmacsDrawable;"
963 "Lorg/gnu/emacs/EmacsDrawable;"
964 "Lorg/gnu/emacs/EmacsGC;IIIIII)V");
965 FIND_METHOD (clear_window, "clearWindow",
966 "(Lorg/gnu/emacs/EmacsWindow;)V");
967 FIND_METHOD (clear_area, "clearArea",
968 "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
969
970#undef FIND_METHOD
971}
972
973static void
974android_init_emacs_pixmap (void)
975{
976 jclass old;
977
978 pixmap_class.class
979 = (*android_java_env)->FindClass (android_java_env,
980 "org/gnu/emacs/EmacsPixmap");
981 eassert (pixmap_class.class);
982
983 old = pixmap_class.class;
984 pixmap_class.class
985 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
986 (jobject) old);
987 ANDROID_DELETE_LOCAL_REF (old);
988
989 if (!pixmap_class.class)
990 emacs_abort ();
991
992#define FIND_METHOD(c_name, name, signature) \
993 pixmap_class.c_name \
994 = (*android_java_env)->GetMethodID (android_java_env, \
995 pixmap_class.class, \
996 name, signature); \
997 assert (pixmap_class.c_name);
998
999 FIND_METHOD (constructor, "<init>", "(S[IIII)V");
1000
1001#undef FIND_METHOD
1002}
1003
1004static void
1005android_init_graphics_point (void)
1006{
1007 jclass old;
1008
1009 point_class.class
1010 = (*android_java_env)->FindClass (android_java_env,
1011 "android/graphics/Point");
1012 eassert (point_class.class);
1013
1014 old = point_class.class;
1015 point_class.class
1016 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
1017 (jobject) old);
1018 ANDROID_DELETE_LOCAL_REF (old);
1019
1020 if (!point_class.class)
1021 emacs_abort ();
1022
1023#define FIND_METHOD(c_name, name, signature) \
1024 point_class.c_name \
1025 = (*android_java_env)->GetMethodID (android_java_env, \
1026 point_class.class, \
1027 name, signature); \
1028 assert (point_class.c_name);
1029
1030 FIND_METHOD (constructor, "<init>", "(II)V");
1031#undef FIND_METHOD
1032}
1033
1034extern JNIEXPORT void JNICALL
1035NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv)
1036{
1037 char **c_argv;
1038 jsize nelements, i;
1039 jobject argument;
1040 const char *c_argument;
1041
1042 android_java_env = env;
1043
1044 nelements = (*env)->GetArrayLength (env, argv);
1045 c_argv = alloca (sizeof *c_argv * nelements);
1046
1047 for (i = 0; i < nelements; ++i)
1048 {
1049 argument = (*env)->GetObjectArrayElement (env, argv, i);
1050 c_argument = (*env)->GetStringUTFChars (env, (jstring) argument,
1051 NULL);
1052
1053 if (!c_argument)
1054 emacs_abort ();
1055
1056 /* Note that c_argument is in ``modified UTF-8 encoding'', but
1057 we don't care as NUL bytes are not being specified inside. */
1058 c_argv[i] = alloca (strlen (c_argument) + 1);
1059 strcpy (c_argv[i], c_argument);
1060 (*env)->ReleaseStringUTFChars (env, (jstring) argument, c_argument);
1061 }
1062
1063 android_init_emacs_service ();
1064 android_init_emacs_pixmap ();
1065 android_init_graphics_point ();
1066
1067 /* Initialize the Android GUI. */
1068 android_init_gui = true;
1069 android_emacs_init (nelements, c_argv);
1070 /* android_emacs_init should never return. */
1071 emacs_abort ();
1072}
1073
1074extern JNIEXPORT void JNICALL
1075NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object)
1076{
1077 emacs_abort ();
1078}
1079
1080extern JNIEXPORT void JNICALL
1081NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
1082 jshort window, jlong time,
1083 jint x, jint y, jint width,
1084 jint height)
1085{
1086 union android_event event;
1087
1088 event.xconfigure.type = ANDROID_CONFIGURE_NOTIFY;
1089 event.xconfigure.window = window;
1090 event.xconfigure.time = time;
1091 event.xconfigure.x = x;
1092 event.xconfigure.y = y;
1093 event.xconfigure.width = width;
1094 event.xconfigure.height = height;
1095
1096 android_write_event (&event);
1097}
1098
1099extern JNIEXPORT void JNICALL
1100NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
1101 jshort window, jlong time,
1102 jint state, jint keycode)
1103{
1104 union android_event event;
1105
1106 event.xkey.type = ANDROID_KEY_PRESS;
1107 event.xkey.window = window;
1108 event.xkey.time = time;
1109 event.xkey.state = state;
1110 event.xkey.keycode = keycode;
1111
1112 android_write_event (&event);
1113}
1114
1115extern JNIEXPORT void JNICALL
1116NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
1117 jshort window, jlong time,
1118 jint state, jint keycode)
1119{
1120 union android_event event;
1121
1122 event.xkey.type = ANDROID_KEY_RELEASE;
1123 event.xkey.window = window;
1124 event.xkey.time = time;
1125 event.xkey.state = state;
1126 event.xkey.keycode = keycode;
1127
1128 android_write_event (&event);
1129}
1130
1131#pragma clang diagnostic pop
1132
1133
1134
1135/* Java functions called by C.
1136
1137 Because all C code runs in the native function initEmacs, ALL LOCAL
1138 REFERENCES WILL PERSIST!
1139
1140 This means that every local reference must be explicitly destroyed
1141 with DeleteLocalRef. A helper macro is provided to do this. */
1142
1143#define MAX_HANDLE 65535
1144
1145enum android_handle_type
1146 {
1147 ANDROID_HANDLE_WINDOW,
1148 ANDROID_HANDLE_GCONTEXT,
1149 ANDROID_HANDLE_PIXMAP,
1150 };
1151
1152struct android_handle_entry
1153{
1154 /* The type. */
1155 enum android_handle_type type;
1156
1157 /* The handle. */
1158 jobject handle;
1159};
1160
1161/* Table of handles MAX_HANDLE long. */
1162struct android_handle_entry android_handles[USHRT_MAX];
1163
1164/* The largest handle ID currently known, but subject to
1165 wraparound. */
1166static android_handle max_handle;
1167
1168/* Allocate a new, unused, handle identifier. If Emacs is out of
1169 identifiers, return 0. */
1170
1171static android_handle
1172android_alloc_id (void)
1173{
1174 android_handle handle;
1175
1176 /* 0 is never a valid handle ID. */
1177 if (!max_handle)
1178 max_handle++;
1179
1180 if (android_handles[max_handle].handle)
1181 {
1182 handle = max_handle + 1;
1183
1184 while (max_handle < handle)
1185 {
1186 ++max_handle;
1187
1188 if (!max_handle)
1189 ++max_handle;
1190
1191 if (!android_handles[max_handle].handle)
1192 return 0;
1193 }
1194
1195 return 0;
1196 }
1197
1198 return max_handle++;
1199}
1200
1201/* Destroy the specified handle and mark it as free on the Java side
1202 as well. */
1203
1204static void
1205android_destroy_handle (android_handle handle)
1206{
1207 static jclass old, class;
1208 static jmethodID method;
1209
1210 if (!android_handles[handle].handle)
1211 {
1212 __android_log_print (ANDROID_LOG_ERROR, __func__,
1213 "Trying to destroy free handle!");
1214 emacs_abort ();
1215 }
1216
1217 if (!class)
1218 {
1219 class
1220 = (*android_java_env)->FindClass (android_java_env,
1221 "org/gnu/emacs/EmacsHandleObject");
1222 assert (class != NULL);
1223
1224 method
1225 = (*android_java_env)->GetMethodID (android_java_env, class,
1226 "destroyHandle", "()V");
1227 assert (method != NULL);
1228
1229 old = class;
1230 class
1231 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
1232 (jobject) class);
1233 (*android_java_env)->ExceptionClear (android_java_env);
1234 ANDROID_DELETE_LOCAL_REF (old);
1235
1236 if (!class)
1237 memory_full (0);
1238 }
1239
1240 (*android_java_env)->CallVoidMethod (android_java_env,
1241 android_handles[handle].handle,
1242 method);
1243 (*android_java_env)->DeleteGlobalRef (android_java_env,
1244 android_handles[handle].handle);
1245 android_handles[handle].handle = NULL;
1246}
1247
1248static jobject
1249android_resolve_handle (android_handle handle,
1250 enum android_handle_type type)
1251{
1252 if (!handle)
1253 /* ANDROID_NONE. */
1254 return NULL;
1255
1256 if (!android_handles[handle].handle)
1257 {
1258 __android_log_print (ANDROID_LOG_ERROR, __func__,
1259 "Trying to resolve free handle!");
1260 emacs_abort ();
1261 }
1262
1263 if (android_handles[handle].type != type)
1264 {
1265 __android_log_print (ANDROID_LOG_ERROR, __func__,
1266 "Handle has wrong type!");
1267 emacs_abort ();
1268 }
1269
1270 return android_handles[handle].handle;
1271}
1272
1273static jobject
1274android_resolve_handle2 (android_handle handle,
1275 enum android_handle_type type,
1276 enum android_handle_type type2)
1277{
1278 if (!handle)
1279 return NULL;
1280
1281 if (!android_handles[handle].handle)
1282 {
1283 __android_log_print (ANDROID_LOG_ERROR, __func__,
1284 "Trying to resolve free handle!");
1285 emacs_abort ();
1286 }
1287
1288 if (android_handles[handle].type != type
1289 && android_handles[handle].type != type2)
1290 {
1291 __android_log_print (ANDROID_LOG_ERROR, __func__,
1292 "Handle has wrong type!");
1293 emacs_abort ();
1294 }
1295
1296 return android_handles[handle].handle;
1297}
1298
1299static jmethodID android_lookup_method (const char *, const char *,
1300 const char *);
1301
1302void
1303android_change_window_attributes (android_window handle,
1304 enum android_window_value_mask value_mask,
1305 struct android_set_window_attributes *attrs)
1306{
1307 jmethodID method;
1308 jobject window;
1309
1310 window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
1311
1312 if (value_mask & ANDROID_CW_BACK_PIXEL)
1313 {
1314 method = android_lookup_method ("org/gnu/emacs/EmacsWindow",
1315 "changeWindowBackground", "(I)V");
1316 (*android_java_env)->CallVoidMethod (android_java_env,
1317 window, method,
1318 (jint) attrs->background_pixel);
1319 }
1320}
1321
1322/* Create a new window with the given width, height and
1323 attributes. */
1324
1325android_window
1326android_create_window (android_window parent, int x, int y,
1327 int width, int height,
1328 enum android_window_value_mask value_mask,
1329 struct android_set_window_attributes *attrs)
1330{
1331 static jclass class;
1332 static jmethodID constructor;
1333 jobject object, parent_object, old;
1334 android_window window;
1335 android_handle prev_max_handle;
1336
1337 parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
1338
1339 prev_max_handle = max_handle;
1340 window = android_alloc_id ();
1341
1342 if (!window)
1343 error ("Out of window handles!");
1344
1345 if (!class)
1346 {
1347 class = (*android_java_env)->FindClass (android_java_env,
1348 "org/gnu/emacs/EmacsWindow");
1349 assert (class != NULL);
1350
1351 constructor
1352 = (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
1353 "(SLorg/gnu/emacs/EmacsWindow;IIII)V");
1354 assert (constructor != NULL);
1355
1356 old = class;
1357 class = (*android_java_env)->NewGlobalRef (android_java_env, class);
1358 (*android_java_env)->ExceptionClear (android_java_env);
1359 ANDROID_DELETE_LOCAL_REF (old);
1360
1361 if (!class)
1362 memory_full (0);
1363 }
1364
1365 object = (*android_java_env)->NewObject (android_java_env, class,
1366 constructor, (jshort) window,
1367 parent_object, (jint) x, (jint) y,
1368 (jint) width, (jint) height);
1369 if (!object)
1370 {
1371 (*android_java_env)->ExceptionClear (android_java_env);
1372
1373 max_handle = prev_max_handle;
1374 memory_full (0);
1375 }
1376
1377 android_handles[window].type = ANDROID_HANDLE_WINDOW;
1378 android_handles[window].handle
1379 = (*android_java_env)->NewGlobalRef (android_java_env,
1380 object);
1381 (*android_java_env)->ExceptionClear (android_java_env);
1382 ANDROID_DELETE_LOCAL_REF (object);
1383
1384 if (!android_handles[window].handle)
1385 memory_full (0);
1386
1387 android_change_window_attributes (window, value_mask, attrs);
1388 return window;
1389}
1390
1391void
1392android_set_window_background (android_window window, unsigned long pixel)
1393{
1394 struct android_set_window_attributes attrs;
1395
1396 attrs.background_pixel = pixel;
1397 android_change_window_attributes (window, ANDROID_CW_BACK_PIXEL,
1398 &attrs);
1399}
1400
1401void
1402android_destroy_window (android_window window)
1403{
1404 if (android_handles[window].type != ANDROID_HANDLE_WINDOW)
1405 {
1406 __android_log_print (ANDROID_LOG_ERROR, __func__,
1407 "Trying to destroy something not a window!");
1408 emacs_abort ();
1409 }
1410
1411 android_destroy_handle (window);
1412}
1413
1414static void
1415android_init_android_rect_class (void)
1416{
1417 jclass old;
1418
1419 if (android_rect_class)
1420 /* Already initialized. */
1421 return;
1422
1423 android_rect_class
1424 = (*android_java_env)->FindClass (android_java_env,
1425 "android/graphics/Rect");
1426 assert (android_rect_class);
1427
1428 android_rect_constructor
1429 = (*android_java_env)->GetMethodID (android_java_env, android_rect_class,
1430 "<init>", "(IIII)V");
1431 assert (emacs_gc_constructor);
1432
1433 old = android_rect_class;
1434 android_rect_class
1435 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
1436 (jobject) android_rect_class);
1437 (*android_java_env)->ExceptionClear (android_java_env);
1438 ANDROID_DELETE_LOCAL_REF (old);
1439
1440 if (!android_rect_class)
1441 memory_full (0);
1442}
1443
1444static void
1445android_init_emacs_gc_class (void)
1446{
1447 jclass old;
1448
1449 if (emacs_gc_class)
1450 /* Already initialized. */
1451 return;
1452
1453 emacs_gc_class
1454 = (*android_java_env)->FindClass (android_java_env,
1455 "org/gnu/emacs/EmacsGC");
1456 assert (emacs_gc_class);
1457
1458 emacs_gc_constructor
1459 = (*android_java_env)->GetMethodID (android_java_env,
1460 emacs_gc_class,
1461 "<init>", "(S)V");
1462 assert (emacs_gc_constructor);
1463
1464 emacs_gc_mark_dirty
1465 = (*android_java_env)->GetMethodID (android_java_env,
1466 emacs_gc_class,
1467 "markDirty", "()V");
1468 assert (emacs_gc_mark_dirty);
1469
1470 old = emacs_gc_class;
1471 emacs_gc_class
1472 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
1473 (jobject) emacs_gc_class);
1474 (*android_java_env)->ExceptionClear (android_java_env);
1475 ANDROID_DELETE_LOCAL_REF (old);
1476 if (!emacs_gc_class)
1477 memory_full (0);
1478
1479 emacs_gc_foreground
1480 = (*android_java_env)->GetFieldID (android_java_env,
1481 emacs_gc_class,
1482 "foreground", "I");
1483 emacs_gc_background
1484 = (*android_java_env)->GetFieldID (android_java_env,
1485 emacs_gc_class,
1486 "background", "I");
1487 emacs_gc_function
1488 = (*android_java_env)->GetFieldID (android_java_env,
1489 emacs_gc_class,
1490 "function", "I");
1491 emacs_gc_clip_rects
1492 = (*android_java_env)->GetFieldID (android_java_env,
1493 emacs_gc_class,
1494 "clip_rects",
1495 "[Landroid/graphics/Rect;");
1496 emacs_gc_clip_x_origin
1497 = (*android_java_env)->GetFieldID (android_java_env,
1498 emacs_gc_class,
1499 "clip_x_origin", "I");
1500 emacs_gc_clip_y_origin
1501 = (*android_java_env)->GetFieldID (android_java_env,
1502 emacs_gc_class,
1503 "clip_y_origin", "I");
1504 emacs_gc_stipple
1505 = (*android_java_env)->GetFieldID (android_java_env,
1506 emacs_gc_class,
1507 "stipple",
1508 "Lorg/gnu/emacs/EmacsPixmap;");
1509 emacs_gc_clip_mask
1510 = (*android_java_env)->GetFieldID (android_java_env,
1511 emacs_gc_class,
1512 "clip_mask",
1513 "Lorg/gnu/emacs/EmacsPixmap;");
1514 emacs_gc_fill_style
1515 = (*android_java_env)->GetFieldID (android_java_env,
1516 emacs_gc_class,
1517 "fill_style", "I");
1518 emacs_gc_ts_origin_x
1519 = (*android_java_env)->GetFieldID (android_java_env,
1520 emacs_gc_class,
1521 "ts_origin_x", "I");
1522 emacs_gc_ts_origin_y
1523 = (*android_java_env)->GetFieldID (android_java_env,
1524 emacs_gc_class,
1525 "ts_origin_y", "I");
1526}
1527
1528struct android_gc *
1529android_create_gc (enum android_gc_value_mask mask,
1530 struct android_gc_values *values)
1531{
1532 struct android_gc *gc;
1533 android_handle prev_max_handle;
1534 jobject object;
1535
1536 android_init_emacs_gc_class ();
1537
1538 gc = xmalloc (sizeof *gc);
1539 prev_max_handle = max_handle;
1540 gc->gcontext = android_alloc_id ();
1541
1542 if (!gc->gcontext)
1543 {
1544 xfree (gc);
1545 error ("Out of GContext handles!");
1546 }
1547
1548 object = (*android_java_env)->NewObject (android_java_env,
1549 emacs_gc_class,
1550 emacs_gc_constructor,
1551 (jshort) gc->gcontext);
1552
1553 if (!object)
1554 {
1555 (*android_java_env)->ExceptionClear (android_java_env);
1556
1557 max_handle = prev_max_handle;
1558 memory_full (0);
1559 }
1560
1561 android_handles[gc->gcontext].type = ANDROID_HANDLE_GCONTEXT;
1562 android_handles[gc->gcontext].handle
1563 = (*android_java_env)->NewGlobalRef (android_java_env, object);
1564 (*android_java_env)->ExceptionClear (android_java_env);
1565 ANDROID_DELETE_LOCAL_REF (object);
1566
1567 if (!android_handles[gc->gcontext].handle)
1568 memory_full (0);
1569
1570 android_change_gc (gc, mask, values);
1571 return gc;
1572}
1573
1574void
1575android_free_gc (struct android_gc *gc)
1576{
1577 android_destroy_handle (gc->gcontext);
1578 xfree (gc);
1579}
1580
1581void
1582android_change_gc (struct android_gc *gc,
1583 enum android_gc_value_mask mask,
1584 struct android_gc_values *values)
1585{
1586 jobject what, gcontext;
1587
1588 android_init_emacs_gc_class ();
1589 gcontext = android_resolve_handle (gc->gcontext,
1590 ANDROID_HANDLE_GCONTEXT);
1591
1592 if (mask & ANDROID_GC_FOREGROUND)
1593 (*android_java_env)->SetIntField (android_java_env,
1594 gcontext,
1595 emacs_gc_foreground,
1596 values->foreground);
1597
1598 if (mask & ANDROID_GC_BACKGROUND)
1599 (*android_java_env)->SetIntField (android_java_env,
1600 gcontext,
1601 emacs_gc_background,
1602 values->background);
1603
1604 if (mask & ANDROID_GC_FUNCTION)
1605 (*android_java_env)->SetIntField (android_java_env,
1606 gcontext,
1607 emacs_gc_function,
1608 values->function);
1609
1610 if (mask & ANDROID_GC_CLIP_X_ORIGIN)
1611 (*android_java_env)->SetIntField (android_java_env,
1612 gcontext,
1613 emacs_gc_clip_x_origin,
1614 values->clip_x_origin);
1615
1616 if (mask & ANDROID_GC_CLIP_Y_ORIGIN)
1617 (*android_java_env)->SetIntField (android_java_env,
1618 gcontext,
1619 emacs_gc_clip_y_origin,
1620 values->clip_y_origin);
1621
1622 if (mask & ANDROID_GC_CLIP_MASK)
1623 {
1624 what = android_resolve_handle (values->clip_mask,
1625 ANDROID_HANDLE_PIXMAP);
1626 (*android_java_env)->SetObjectField (android_java_env,
1627 gcontext,
1628 emacs_gc_stipple,
1629 what);
1630
1631 /* Clearing GCClipMask also clears the clip rectangles. */
1632 (*android_java_env)->SetObjectField (android_java_env,
1633 gcontext,
1634 emacs_gc_clip_rects,
1635 NULL);
1636 }
1637
1638 if (mask & ANDROID_GC_STIPPLE)
1639 {
1640 what = android_resolve_handle (values->stipple,
1641 ANDROID_HANDLE_PIXMAP);
1642 (*android_java_env)->SetObjectField (android_java_env,
1643 gcontext,
1644 emacs_gc_stipple,
1645 what);
1646 }
1647
1648 if (mask & ANDROID_GC_FILL_STYLE)
1649 (*android_java_env)->SetIntField (android_java_env,
1650 gcontext,
1651 emacs_gc_fill_style,
1652 values->fill_style);
1653
1654 if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN)
1655 (*android_java_env)->SetIntField (android_java_env,
1656 gcontext,
1657 emacs_gc_ts_origin_x,
1658 values->ts_x_origin);
1659
1660 if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN)
1661 (*android_java_env)->SetIntField (android_java_env,
1662 gcontext,
1663 emacs_gc_ts_origin_y,
1664 values->ts_y_origin);
1665
1666 if (mask)
1667 (*android_java_env)->CallVoidMethod (android_java_env,
1668 gcontext,
1669 emacs_gc_mark_dirty);
1670}
1671
1672void
1673android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin,
1674 int clip_y_origin,
1675 struct android_rectangle *clip_rects,
1676 int n_clip_rects)
1677{
1678 jobjectArray array;
1679 jobject rect, gcontext;
1680 int i;
1681
1682 android_init_android_rect_class ();
1683 android_init_emacs_gc_class ();
1684
1685 gcontext = android_resolve_handle (gc->gcontext,
1686 ANDROID_HANDLE_GCONTEXT);
1687
1688 array = (*android_java_env)->NewObjectArray (android_java_env,
1689 n_clip_rects,
1690 android_rect_class,
1691 NULL);
1692
1693 if (!array)
1694 {
1695 (*android_java_env)->ExceptionClear (android_java_env);
1696 memory_full (0);
1697 }
1698
1699 for (i = 0; i < n_clip_rects; ++i)
1700 {
1701 rect = (*android_java_env)->NewObject (android_java_env,
1702 android_rect_class,
1703 android_rect_constructor,
1704 (jint) clip_rects[i].x,
1705 (jint) clip_rects[i].y,
1706 (jint) (clip_rects[i].x
1707 + clip_rects[i].width),
1708 (jint) (clip_rects[i].y
1709 + clip_rects[i].height));
1710
1711 if (!rect)
1712 {
1713 (*android_java_env)->ExceptionClear (android_java_env);
1714 ANDROID_DELETE_LOCAL_REF (array);
1715 memory_full (0);
1716 }
1717
1718 (*android_java_env)->SetObjectArrayElement (android_java_env,
1719 array, i, rect);
1720 ANDROID_DELETE_LOCAL_REF (rect);
1721 }
1722
1723 (*android_java_env)->SetObjectField (android_java_env,
1724 gcontext,
1725 emacs_gc_clip_rects,
1726 (jobject) array);
1727 ANDROID_DELETE_LOCAL_REF (array);
1728
1729 (*android_java_env)->SetIntField (android_java_env,
1730 gcontext,
1731 emacs_gc_clip_x_origin,
1732 clip_x_origin);
1733 (*android_java_env)->SetIntField (android_java_env,
1734 gcontext,
1735 emacs_gc_clip_y_origin,
1736 clip_y_origin);
1737
1738 (*android_java_env)->CallVoidMethod (android_java_env,
1739 gcontext,
1740 emacs_gc_mark_dirty);
1741}
1742
1743void
1744android_reparent_window (android_window w, android_window parent,
1745 int x, int y)
1746{
1747 /* TODO */
1748}
1749
1750/* Look up the method with SIGNATURE by NAME in CLASS. Abort if it
1751 could not be found. This should be used for functions which are
1752 not called very often.
1753
1754 CLASS must never be unloaded, or the behavior is undefined. */
1755
1756static jmethodID
1757android_lookup_method (const char *class, const char *name,
1758 const char *signature)
1759{
1760 jclass java_class;
1761 jmethodID method;
1762
1763 java_class
1764 = (*android_java_env)->FindClass (android_java_env, class);
1765
1766 if (!java_class)
1767 {
1768 __android_log_print (ANDROID_LOG_ERROR, __func__,
1769 "Failed to find class %s", class);
1770 emacs_abort ();
1771 }
1772
1773 method
1774 = (*android_java_env)->GetMethodID (android_java_env,
1775 java_class, name,
1776 signature);
1777
1778 if (!method)
1779 {
1780 __android_log_print (ANDROID_LOG_ERROR, __func__,
1781 "Failed to find method %s in class %s"
1782 " with signature %s",
1783 name, class, signature);
1784 emacs_abort ();
1785 }
1786
1787 ANDROID_DELETE_LOCAL_REF (java_class);
1788 return method;
1789}
1790
1791void
1792android_clear_window (android_window handle)
1793{
1794 jobject window;
1795
1796 window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
1797
1798 (*android_java_env)->CallVoidMethod (android_java_env,
1799 emacs_service,
1800 service_class.clear_window,
1801 window);
1802}
1803
1804void
1805android_map_window (android_window handle)
1806{
1807 jobject window;
1808 jmethodID map_window;
1809
1810 window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
1811 map_window = android_lookup_method ("org/gnu/emacs/EmacsWindow",
1812 "mapWindow", "()V");
1813
1814 (*android_java_env)->CallVoidMethod (android_java_env,
1815 window, map_window);
1816}
1817
1818void
1819android_unmap_window (android_window handle)
1820{
1821 jobject window;
1822 jmethodID unmap_window;
1823
1824 window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
1825 unmap_window = android_lookup_method ("org/gnu/emacs/EmacsWindow",
1826 "unmapWindow", "()V");
1827
1828 (*android_java_env)->CallVoidMethod (android_java_env,
1829 window, unmap_window);
1830}
1831
1832void
1833android_resize_window (android_window handle, unsigned int width,
1834 unsigned int height)
1835{
1836 jobject window;
1837 jmethodID resize_window;
1838
1839 window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
1840 resize_window = android_lookup_method ("org/gnu/emacs/EmacsWindow",
1841 "resizeWindow", "(II)V");
1842
1843 (*android_java_env)->CallVoidMethod (android_java_env,
1844 window, resize_window,
1845 (jint) width, (jint) height);
1846}
1847
1848void
1849android_move_window (android_window handle, int x, int y)
1850{
1851 jobject window;
1852 jmethodID move_window;
1853
1854 window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
1855 move_window = android_lookup_method ("org/gnu/emacs/EmacsWindow",
1856 "moveWindow", "(II)V");
1857
1858 (*android_java_env)->CallVoidMethod (android_java_env,
1859 window, move_window,
1860 (jint) x, (jint) y);
1861}
1862
1863void
1864android_swap_buffers (struct android_swap_info *swap_info,
1865 int num_windows)
1866{
1867 jobject window;
1868 jmethodID swap_buffers;
1869 int i;
1870
1871 swap_buffers = android_lookup_method ("org/gnu/emacs/EmacsWindow",
1872 "swapBuffers", "()V");
1873
1874 for (i = 0; i < num_windows; ++i)
1875 {
1876 window = android_resolve_handle (swap_info[i].swap_window,
1877 ANDROID_HANDLE_WINDOW);
1878 (*android_java_env)->CallVoidMethod (android_java_env,
1879 window, swap_buffers);
1880 }
1881}
1882
1883void
1884android_get_gc_values (struct android_gc *gc,
1885 enum android_gc_value_mask mask,
1886 struct android_gc_values *values)
1887{
1888 jobject gcontext;
1889
1890 gcontext = android_resolve_handle (gc->gcontext,
1891 ANDROID_HANDLE_GCONTEXT);
1892
1893 if (mask & ANDROID_GC_FOREGROUND)
1894 /* GCs never have 32 bit colors, so we don't have to worry about
1895 sign extension here. */
1896 values->foreground
1897 = (*android_java_env)->GetIntField (android_java_env,
1898 gcontext,
1899 emacs_gc_foreground);
1900
1901 if (mask & ANDROID_GC_BACKGROUND)
1902 values->background
1903 = (*android_java_env)->GetIntField (android_java_env,
1904 gcontext,
1905 emacs_gc_background);
1906
1907 if (mask & ANDROID_GC_FUNCTION)
1908 values->function
1909 = (*android_java_env)->GetIntField (android_java_env,
1910 gcontext,
1911 emacs_gc_function);
1912
1913 if (mask & ANDROID_GC_CLIP_X_ORIGIN)
1914 values->clip_x_origin
1915 = (*android_java_env)->GetIntField (android_java_env,
1916 gcontext,
1917 emacs_gc_clip_x_origin);
1918
1919 if (mask & ANDROID_GC_CLIP_Y_ORIGIN)
1920 values->clip_y_origin
1921 = (*android_java_env)->GetIntField (android_java_env,
1922 gcontext,
1923 emacs_gc_clip_y_origin);
1924
1925 if (mask & ANDROID_GC_FILL_STYLE)
1926 values->fill_style
1927 = (*android_java_env)->GetIntField (android_java_env,
1928 gcontext,
1929 emacs_gc_fill_style);
1930
1931 if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN)
1932 values->ts_x_origin
1933 = (*android_java_env)->GetIntField (android_java_env,
1934 gcontext,
1935 emacs_gc_ts_origin_x);
1936
1937 if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN)
1938 values->ts_y_origin
1939 = (*android_java_env)->GetIntField (android_java_env,
1940 gcontext,
1941 emacs_gc_ts_origin_y);
1942
1943 /* Fields involving handles are not used by Emacs, and thus not
1944 implemented */
1945}
1946
1947void
1948android_set_foreground (struct android_gc *gc, unsigned long foreground)
1949{
1950 struct android_gc_values gcv;
1951
1952 gcv.foreground = foreground;
1953 android_change_gc (gc, ANDROID_GC_FOREGROUND, &gcv);
1954}
1955
1956void
1957android_fill_rectangle (android_drawable handle, struct android_gc *gc,
1958 int x, int y, unsigned int width,
1959 unsigned int height)
1960{
1961 jobject drawable, gcontext;
1962
1963 drawable = android_resolve_handle2 (handle,
1964 ANDROID_HANDLE_WINDOW,
1965 ANDROID_HANDLE_PIXMAP);
1966 gcontext = android_resolve_handle (gc->gcontext,
1967 ANDROID_HANDLE_GCONTEXT);
1968
1969 (*android_java_env)->CallVoidMethod (android_java_env,
1970 emacs_service,
1971 service_class.fill_rectangle,
1972 drawable,
1973 gcontext,
1974 (jint) x, (jint) y,
1975 (jint) width,
1976 (jint) height);
1977}
1978
1979android_pixmap
1980android_create_pixmap_from_bitmap_data (char *data, unsigned int width,
1981 unsigned int height,
1982 unsigned long foreground,
1983 unsigned long background,
1984 unsigned int depth)
1985{
1986 android_handle prev_max_handle;
1987 jobject object;
1988 jintArray colors;
1989 android_pixmap pixmap;
1990 unsigned int x, y;
1991 jint *region;
1992
1993 USE_SAFE_ALLOCA;
1994
1995 /* Create the color array holding the data. */
1996 colors = (*android_java_env)->NewIntArray (android_java_env,
1997 width * height);
1998
1999 if (!colors)
2000 {
2001 (*android_java_env)->ExceptionClear (android_java_env);
2002 memory_full (0);
2003 }
2004
2005 SAFE_NALLOCA (region, sizeof *region, width);
2006
2007 for (y = 0; y < height; ++y)
2008 {
2009 for (x = 0; x < width; ++x)
2010 {
2011 if (data[y / 8] & (1 << (x % 8)))
2012 region[x] = foreground;
2013 else
2014 region[x] = background;
2015 }
2016
2017 (*android_java_env)->SetIntArrayRegion (android_java_env,
2018 colors,
2019 width * y, width,
2020 region);
2021 }
2022
2023 /* First, allocate the pixmap handle. */
2024 prev_max_handle = max_handle;
2025 pixmap = android_alloc_id ();
2026
2027 if (!pixmap)
2028 {
2029 ANDROID_DELETE_LOCAL_REF ((jobject) colors);
2030 error ("Out of pixmap handles!");
2031 }
2032
2033 object = (*android_java_env)->NewObject (android_java_env,
2034 pixmap_class.class,
2035 pixmap_class.constructor,
2036 (jshort) pixmap, colors,
2037 (jint) width, (jint) height,
2038 (jint) depth);
2039 (*android_java_env)->ExceptionClear (android_java_env);
2040 ANDROID_DELETE_LOCAL_REF ((jobject) colors);
2041
2042 if (!object)
2043 {
2044 max_handle = prev_max_handle;
2045 memory_full (0);
2046 }
2047
2048 android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
2049 android_handles[pixmap].handle
2050 = (*android_java_env)->NewGlobalRef (android_java_env, object);
2051 ANDROID_DELETE_LOCAL_REF (object);
2052
2053 if (!android_handles[pixmap].handle)
2054 memory_full (0);
2055
2056 SAFE_FREE ();
2057 return pixmap;
2058}
2059
2060void
2061android_set_clip_mask (struct android_gc *gc, android_pixmap pixmap)
2062{
2063 struct android_gc_values gcv;
2064
2065 gcv.clip_mask = pixmap;
2066 android_change_gc (gc, ANDROID_GC_CLIP_MASK, &gcv);
2067}
2068
2069void
2070android_set_fill_style (struct android_gc *gc,
2071 enum android_fill_style fill_style)
2072{
2073 struct android_gc_values gcv;
2074
2075 gcv.fill_style = fill_style;
2076 android_change_gc (gc, ANDROID_GC_FILL_STYLE, &gcv);
2077}
2078
2079void
2080android_copy_area (android_drawable src, android_drawable dest,
2081 struct android_gc *gc, int src_x, int src_y,
2082 unsigned int width, unsigned int height,
2083 int dest_x, int dest_y)
2084{
2085 jobject src_object, dest_object, gcontext;
2086
2087 src_object = android_resolve_handle2 (src, ANDROID_HANDLE_WINDOW,
2088 ANDROID_HANDLE_PIXMAP);
2089 dest_object = android_resolve_handle2 (dest, ANDROID_HANDLE_WINDOW,
2090 ANDROID_HANDLE_PIXMAP);
2091 gcontext = android_resolve_handle (gc->gcontext,
2092 ANDROID_HANDLE_GCONTEXT);
2093
2094 (*android_java_env)->CallVoidMethod (android_java_env,
2095 emacs_service,
2096 service_class.copy_area,
2097 src_object,
2098 dest_object,
2099 gcontext,
2100 (jint) src_x, (jint) src_y,
2101 (jint) width, (jint) height,
2102 (jint) dest_x, (jint) dest_y);
2103}
2104
2105void
2106android_free_pixmap (android_pixmap pixmap)
2107{
2108 android_destroy_handle (pixmap);
2109}
2110
2111void
2112android_set_background (struct android_gc *gc, unsigned long background)
2113{
2114 struct android_gc_values gcv;
2115
2116 gcv.background = background;
2117 android_change_gc (gc, ANDROID_GC_BACKGROUND, &gcv);
2118}
2119
2120void
2121android_fill_polygon (android_drawable drawable, struct android_gc *gc,
2122 struct android_point *points, int npoints,
2123 enum android_shape shape, enum android_coord_mode mode)
2124{
2125 jobjectArray array;
2126 jobject point, drawable_object, gcontext;
2127 int i;
2128
2129 drawable_object = android_resolve_handle2 (drawable,
2130 ANDROID_HANDLE_WINDOW,
2131 ANDROID_HANDLE_PIXMAP);
2132 gcontext = android_resolve_handle (gc->gcontext,
2133 ANDROID_HANDLE_GCONTEXT);
2134
2135 array = (*android_java_env)->NewObjectArray (android_java_env,
2136 npoints,
2137 point_class.class,
2138 NULL);
2139
2140 if (!array)
2141 {
2142 (*android_java_env)->ExceptionClear (android_java_env);
2143 memory_full (0);
2144 }
2145
2146 for (i = 0; i < npoints; ++i)
2147 {
2148 point = (*android_java_env)->NewObject (android_java_env,
2149 point_class.class,
2150 point_class.constructor,
2151 (jint) points[i].x,
2152 (jint) points[i].y);
2153
2154 if (!point)
2155 {
2156 (*android_java_env)->ExceptionClear (android_java_env);
2157 ANDROID_DELETE_LOCAL_REF (array);
2158 memory_full (0);
2159 }
2160
2161 (*android_java_env)->SetObjectArrayElement (android_java_env,
2162 array, i, point);
2163 ANDROID_DELETE_LOCAL_REF (point);
2164 }
2165
2166 (*android_java_env)->CallVoidMethod (android_java_env,
2167 emacs_service,
2168 service_class.fill_polygon,
2169 drawable_object,
2170 gcontext, array);
2171 ANDROID_DELETE_LOCAL_REF (array);
2172}
2173
2174void
2175android_draw_rectangle (android_drawable handle, struct android_gc *gc,
2176 int x, int y, unsigned int width, unsigned int height)
2177{
2178 jobject drawable, gcontext;
2179
2180 drawable = android_resolve_handle2 (handle,
2181 ANDROID_HANDLE_WINDOW,
2182 ANDROID_HANDLE_PIXMAP);
2183 gcontext = android_resolve_handle (gc->gcontext,
2184 ANDROID_HANDLE_GCONTEXT);
2185
2186 (*android_java_env)->CallVoidMethod (android_java_env,
2187 emacs_service,
2188 service_class.draw_rectangle,
2189 drawable, gcontext,
2190 (jint) x, (jint) y,
2191 (jint) width, (jint) height);
2192}
2193
2194void
2195android_draw_point (android_drawable handle, struct android_gc *gc,
2196 int x, int y)
2197{
2198 jobject drawable, gcontext;
2199
2200 drawable = android_resolve_handle2 (handle,
2201 ANDROID_HANDLE_WINDOW,
2202 ANDROID_HANDLE_PIXMAP);
2203 gcontext = android_resolve_handle (gc->gcontext,
2204 ANDROID_HANDLE_GCONTEXT);
2205
2206 (*android_java_env)->CallVoidMethod (android_java_env,
2207 emacs_service,
2208 service_class.draw_point,
2209 drawable, gcontext,
2210 (jint) x, (jint) y);
2211}
2212
2213void
2214android_draw_line (android_drawable handle, struct android_gc *gc,
2215 int x, int y, int x2, int y2)
2216{
2217 jobject drawable, gcontext;
2218
2219 drawable = android_resolve_handle2 (handle,
2220 ANDROID_HANDLE_WINDOW,
2221 ANDROID_HANDLE_PIXMAP);
2222 gcontext = android_resolve_handle (gc->gcontext,
2223 ANDROID_HANDLE_GCONTEXT);
2224
2225 (*android_java_env)->CallVoidMethod (android_java_env,
2226 emacs_service,
2227 service_class.draw_line,
2228 drawable, gcontext,
2229 (jint) x, (jint) y,
2230 (jint) x2, (jint) y2);
2231}
2232
2233android_pixmap
2234android_create_pixmap (unsigned int width, unsigned int height,
2235 int depth)
2236{
2237 android_handle prev_max_handle;
2238 jobject object;
2239 jintArray colors;
2240 android_pixmap pixmap;
2241
2242 /* Create the color array holding the data. */
2243 colors = (*android_java_env)->NewIntArray (android_java_env,
2244 width * height);
2245
2246 if (!colors)
2247 {
2248 (*android_java_env)->ExceptionClear (android_java_env);
2249 memory_full (0);
2250 }
2251
2252 /* First, allocate the pixmap handle. */
2253 prev_max_handle = max_handle;
2254 pixmap = android_alloc_id ();
2255
2256 if (!pixmap)
2257 {
2258 ANDROID_DELETE_LOCAL_REF ((jobject) colors);
2259 error ("Out of pixmap handles!");
2260 }
2261
2262 object = (*android_java_env)->NewObject (android_java_env,
2263 pixmap_class.class,
2264 pixmap_class.constructor,
2265 (jshort) pixmap, colors,
2266 (jint) width, (jint) height,
2267 (jint) depth);
2268 ANDROID_DELETE_LOCAL_REF ((jobject) colors);
2269
2270 if (!object)
2271 {
2272 (*android_java_env)->ExceptionClear (android_java_env);
2273 max_handle = prev_max_handle;
2274 memory_full (0);
2275 }
2276
2277 android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
2278 android_handles[pixmap].handle
2279 = (*android_java_env)->NewGlobalRef (android_java_env, object);
2280 (*android_java_env)->ExceptionClear (android_java_env);
2281 ANDROID_DELETE_LOCAL_REF (object);
2282
2283 if (!android_handles[pixmap].handle)
2284 memory_full (0);
2285
2286 return pixmap;
2287}
2288
2289void
2290android_set_ts_origin (struct android_gc *gc, int x, int y)
2291{
2292 struct android_gc_values gcv;
2293
2294 gcv.ts_x_origin = x;
2295 gcv.ts_y_origin = y;
2296 android_change_gc (gc, (ANDROID_GC_TILE_STIP_X_ORIGIN
2297 | ANDROID_GC_TILE_STIP_Y_ORIGIN),
2298 &gcv);
2299}
2300
2301void
2302android_clear_area (android_window handle, int x, int y,
2303 unsigned int width, unsigned int height)
2304{
2305 jobject window;
2306
2307 window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
2308
2309 (*android_java_env)->CallVoidMethod (android_java_env,
2310 emacs_service,
2311 service_class.clear_area,
2312 window, (jint) x, (jint) y,
2313 (jint) width, (jint) height);
2314}
2315
2316#else /* ANDROID_STUBIFY */
2317
2318/* X emulation functions for Android. */
2319
2320struct android_gc *
2321android_create_gc (enum android_gc_value_mask mask,
2322 struct android_gc_values *values)
2323{
2324 /* This function should never be called when building stubs. */
2325 emacs_abort ();
2326}
2327
2328void
2329android_free_gc (struct android_gc *gc)
2330{
2331 /* This function should never be called when building stubs. */
2332 emacs_abort ();
2333}
2334
2335#endif