diff options
Diffstat (limited to 'src/androidfns.c')
-rw-r--r-- | src/androidfns.c | 3574 |
1 files changed, 3574 insertions, 0 deletions
diff --git a/src/androidfns.c b/src/androidfns.c new file mode 100644 index 00000000000..14561afe29a --- /dev/null +++ b/src/androidfns.c @@ -0,0 +1,3574 @@ +/* Communication module for Android terminals. + +Copyright (C) 2023 Free Software Foundation, Inc. + +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/>. */ + +#include <config.h> +#include <math.h> + +#include "lisp.h" +#include "android.h" +#include "androidterm.h" +#include "blockinput.h" +#include "keyboard.h" +#include "buffer.h" +#include "androidgui.h" +#include "pdumper.h" + +#ifndef ANDROID_STUBIFY + +/* Some kind of reference count for the image cache. */ +static ptrdiff_t image_cache_refcount; + +/* The frame of the currently visible tooltip, or nil if none. */ +static Lisp_Object tip_frame; + +/* The window-system window corresponding to the frame of the + currently visible tooltip. */ +static android_window tip_window; + +/* The X and Y deltas of the last call to `x-show-tip'. */ +static Lisp_Object tip_dx, tip_dy; + +/* A timer that hides or deletes the currently visible tooltip when it + fires. */ +static Lisp_Object tip_timer; + +/* STRING argument of last `x-show-tip' call. */ +static Lisp_Object tip_last_string; + +/* Normalized FRAME argument of last `x-show-tip' call. */ +static Lisp_Object tip_last_frame; + +/* PARMS argument of last `x-show-tip' call. */ +static Lisp_Object tip_last_parms; + +#endif + +static struct android_display_info * +android_display_info_for_name (Lisp_Object name) +{ + struct android_display_info *dpyinfo; + + CHECK_STRING (name); + + for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next) + { + if (!NILP (Fstring_equal (XCAR (dpyinfo->name_list_element), + name))) + return dpyinfo; + } + + error ("Cannot connect to Android if it was not initialized" + " at startup"); +} + +static struct android_display_info * +check_android_display_info (Lisp_Object object) +{ + struct android_display_info *dpyinfo; + struct frame *sf, *f; + struct terminal *t; + + if (NILP (object)) + { + sf = XFRAME (selected_frame); + + if (FRAME_ANDROID_P (sf) && FRAME_LIVE_P (sf)) + dpyinfo = FRAME_DISPLAY_INFO (sf); + else if (x_display_list) + dpyinfo = x_display_list; + else + error ("Android windows are not in use or not initialized"); + } + else if (TERMINALP (object)) + { + t = decode_live_terminal (object); + + if (t->type != output_android) + error ("Terminal %d is not an Android display", t->id); + + dpyinfo = t->display_info.android; + } + else if (STRINGP (object)) + dpyinfo = android_display_info_for_name (object); + else + { + f = decode_window_system_frame (object); + dpyinfo = FRAME_DISPLAY_INFO (f); + } + + return dpyinfo; +} + +Display_Info * +check_x_display_info (Lisp_Object object) +{ + return check_android_display_info (object); +} + + + +#ifndef ANDROID_STUBIFY + +void +gamma_correct (struct frame *f, Emacs_Color *color) +{ + if (f->gamma) + { + color->red = pow (color->red / 65535.0, f->gamma) * 65535.0 + 0.5; + color->green = pow (color->green / 65535.0, f->gamma) * 65535.0 + 0.5; + color->blue = pow (color->blue / 65535.0, f->gamma) * 65535.0 + 0.5; + } +} + +/* Decide if color named COLOR_NAME is valid for use on frame F. If + so, return the RGB values in COLOR. If ALLOC_P, allocate the + color. Value is false if COLOR_NAME is invalid, or no color could + be allocated. MAKE_INDEX is some mysterious argument used on + NS. */ + +bool +android_defined_color (struct frame *f, const char *color_name, + Emacs_Color *color, bool alloc_p, + bool make_index) +{ + bool success_p; + + success_p = false; + + block_input (); + success_p = android_parse_color (f, color_name, color); + if (success_p && alloc_p) + success_p = android_alloc_nearest_color (f, color); + unblock_input (); + + return success_p; +} + +/* Return the pixel color value for color COLOR_NAME on frame F. If F + is a monochrome frame, return MONO_COLOR regardless of what ARG + says. Signal an error if color can't be allocated. */ + +static unsigned long +android_decode_color (struct frame *f, Lisp_Object color_name, int mono_color) +{ + Emacs_Color cdef; + + CHECK_STRING (color_name); + + if (android_defined_color (f, SSDATA (color_name), &cdef, + true, false)) + return cdef.pixel; + + signal_error ("Undefined color", color_name); +} + +static void +android_set_parent_frame (struct frame *f, Lisp_Object new_value, + Lisp_Object old_value) +{ + struct frame *p; + + p = NULL; + + if (!NILP (new_value) + && (!FRAMEP (new_value) + || !FRAME_LIVE_P (p = XFRAME (new_value)) + || !FRAME_ANDROID_P (p))) + { + store_frame_param (f, Qparent_frame, old_value); + error ("Invalid specification of `parent-frame'"); + } + + if (p != FRAME_PARENT_FRAME (f)) + { + block_input (); + android_reparent_window (FRAME_ANDROID_WINDOW (f), + (p ? FRAME_ANDROID_WINDOW (p) + : FRAME_DISPLAY_INFO (f)->root_window), + f->left_pos, f->top_pos); + unblock_input (); + + fset_parent_frame (f, new_value); + } + + /* Update the fullscreen frame parameter as well. */ + FRAME_TERMINAL (f)->fullscreen_hook (f); +} + +void +android_implicitly_set_name (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + +} + +void +android_explicitly_set_name (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + +} + +/* Set the number of lines used for the tool bar of frame F to VALUE. + VALUE not an integer, or < 0 means set the lines to zero. OLDVAL + is the old number of tool bar lines. This function changes the + height of all windows on frame F to match the new tool bar height. + The frame's height doesn't change. */ + +static void +android_set_tool_bar_lines (struct frame *f, Lisp_Object value, + Lisp_Object oldval) +{ + int nlines; + + /* Treat tool bars like menu bars. */ + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + /* Use VALUE only if an int >= 0. */ + if (RANGED_FIXNUMP (0, value, INT_MAX)) + nlines = XFIXNAT (value); + else + nlines = 0; + + android_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f)); +} + +static void +android_set_tool_bar_position (struct frame *f, + Lisp_Object new_value, + Lisp_Object old_value) +{ + if (!EQ (new_value, Qtop) && !EQ (new_value, Qbottom)) + error ("Tool bar position must be either `top' or `bottom'"); + + if (EQ (new_value, old_value)) + return; + + /* Set the tool bar position. */ + fset_tool_bar_position (f, new_value); + + /* Now reconfigure frame glyphs to place the tool bar at the + bottom. While the inner height has not changed, call + `resize_frame_windows' to place each of the windows at its + new position. */ + + adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_position); + adjust_frame_glyphs (f); + SET_FRAME_GARBAGED (f); + + if (FRAME_ANDROID_WINDOW (f)) + android_clear_under_internal_border (f); +} + +void +android_change_tool_bar_height (struct frame *f, int height) +{ + int unit = FRAME_LINE_HEIGHT (f); + int old_height = FRAME_TOOL_BAR_HEIGHT (f); + int lines = (height + unit - 1) / unit; + Lisp_Object fullscreen = get_frame_param (f, Qfullscreen); + + /* Make sure we redisplay all windows in this frame. */ + fset_redisplay (f); + + FRAME_TOOL_BAR_HEIGHT (f) = height; + FRAME_TOOL_BAR_LINES (f) = lines; + store_frame_param (f, Qtool_bar_lines, make_fixnum (lines)); + + if (FRAME_ANDROID_WINDOW (f) && FRAME_TOOL_BAR_HEIGHT (f) == 0) + { + clear_frame (f); + clear_current_matrices (f); + } + + if ((height < old_height) && WINDOWP (f->tool_bar_window)) + clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix); + + if (!f->tool_bar_resized) + { + /* As long as tool_bar_resized is false, effectively try to change + F's native height. */ + if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth)) + adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), + 1, false, Qtool_bar_lines); + else + adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines); + + f->tool_bar_resized = f->tool_bar_redisplayed; + } + else + /* Any other change may leave the native size of F alone. */ + adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines); + + /* adjust_frame_size might not have done anything, garbage frame + here. */ + adjust_frame_glyphs (f); + SET_FRAME_GARBAGED (f); +} + +/* Set the number of lines used for the tab bar of frame F to VALUE. + VALUE not an integer, or < 0 means set the lines to zero. OLDVAL + is the old number of tab bar lines. This function may change the + height of all windows on frame F to match the new tab bar height. + The frame's height may change if frame_inhibit_implied_resize was + set accordingly. */ + +static void +android_set_tab_bar_lines (struct frame *f, Lisp_Object value, + Lisp_Object oldval) +{ + int olines; + int nlines; + + olines = FRAME_TAB_BAR_LINES (f); + + /* Treat tab bars like menu bars. */ + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + /* Use VALUE only if an int >= 0. */ + if (RANGED_FIXNUMP (0, value, INT_MAX)) + nlines = XFIXNAT (value); + else + nlines = 0; + + if (nlines != olines && (olines == 0 || nlines == 0)) + android_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f)); +} + +void +android_change_tab_bar_height (struct frame *f, int height) +{ + int unit, old_height, lines; + Lisp_Object fullscreen; + + unit = FRAME_LINE_HEIGHT (f); + old_height = FRAME_TAB_BAR_HEIGHT (f); + fullscreen = get_frame_param (f, Qfullscreen); + + /* This differs from the tool bar code in that the tab bar height is + not rounded up. Otherwise, if redisplay_tab_bar decides to grow + the tab bar by even 1 pixel, FRAME_TAB_BAR_LINES will be changed, + leading to the tab bar height being incorrectly set upon the next + call to android_set_font. (bug#59285) */ + + lines = height / unit; + + /* Even so, HEIGHT might be less than unit if the tab bar face is + not so tall as the frame's font height; which if true lines will + be set to 0 and the tab bar will thus vanish. */ + + if (lines == 0 && height != 0) + lines = 1; + + /* Make sure we redisplay all windows in this frame. */ + fset_redisplay (f); + + /* Recalculate tab bar and frame text sizes. */ + FRAME_TAB_BAR_HEIGHT (f) = height; + FRAME_TAB_BAR_LINES (f) = lines; + store_frame_param (f, Qtab_bar_lines, make_fixnum (lines)); + + if (FRAME_ANDROID_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0) + { + clear_frame (f); + clear_current_matrices (f); + } + + if ((height < old_height) && WINDOWP (f->tab_bar_window)) + clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix); + + if (!f->tab_bar_resized) + { + /* As long as tab_bar_resized is false, effectively try to change + F's native height. */ + if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth)) + adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), + 1, false, Qtab_bar_lines); + else + adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines); + + f->tab_bar_resized = f->tab_bar_redisplayed; + } + else + /* Any other change may leave the native size of F alone. */ + adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines); + + /* adjust_frame_size might not have done anything, garbage frame + here. */ + adjust_frame_glyphs (f); + SET_FRAME_GARBAGED (f); +} + +void +android_set_scroll_bar_default_height (struct frame *f) +{ + int height; + + height = FRAME_LINE_HEIGHT (f); + + /* The height of a non-toolkit scrollbar is 14 pixels. */ + FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height; + + /* Use all of that space (aside from required margins) for the + scroll bar. */ + FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) = 14; +} + +void +android_set_scroll_bar_default_width (struct frame *f) +{ + int unit; + + unit = FRAME_COLUMN_WIDTH (f); + + FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + unit - 1) / unit; + FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + = FRAME_CONFIG_SCROLL_BAR_COLS (f) * unit; +} + + +/* Verify that the icon position args for this window are valid. */ + +static void +android_icon_verify (struct frame *f, Lisp_Object parms) +{ + Lisp_Object icon_x, icon_y; + + /* Set the position of the icon. Note that twm groups all + icons in an icon window. */ + icon_x = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0, + RES_TYPE_NUMBER); + icon_y = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0, + RES_TYPE_NUMBER); + + if (!BASE_EQ (icon_x, Qunbound) && !BASE_EQ (icon_y, Qunbound)) + { + CHECK_FIXNUM (icon_x); + CHECK_FIXNUM (icon_y); + } + else if (!BASE_EQ (icon_x, Qunbound) || !BASE_EQ (icon_y, Qunbound)) + error ("Both left and top icon corners of icon must be specified"); +} + +/* Handle the icon stuff for this window. Perhaps later we might + want an x_set_icon_position which can be called interactively as + well. */ + +static void +android_icon (struct frame *f, Lisp_Object parms) +{ + /* Set the position of the icon. Note that twm groups all + icons in an icon window. */ + Lisp_Object icon_x + = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0, + RES_TYPE_NUMBER); + Lisp_Object icon_y + = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0, + RES_TYPE_NUMBER); + + bool xgiven = !BASE_EQ (icon_x, Qunbound); + bool ygiven = !BASE_EQ (icon_y, Qunbound); + + if (xgiven != ygiven) + error ("Both left and top icon corners of icon must be specified"); + + if (xgiven) + { + check_integer_range (icon_x, INT_MIN, INT_MAX); + check_integer_range (icon_y, INT_MIN, INT_MAX); + } + + /* Now return as this is not supported on Android. */ +} + +/* Make the GCs needed for this window, setting the background + color. */ + +static void +android_make_gc (struct frame *f) +{ + struct android_gc_values gc_values; + + block_input (); + + /* Create the GCs of this frame. + Note that many default values are used. */ + + gc_values.foreground = FRAME_FOREGROUND_PIXEL (f); + gc_values.background = FRAME_BACKGROUND_PIXEL (f); + f->output_data.android->normal_gc + = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND, + &gc_values); + + /* Reverse video style. */ + gc_values.foreground = FRAME_BACKGROUND_PIXEL (f); + gc_values.background = FRAME_FOREGROUND_PIXEL (f); + f->output_data.android->reverse_gc + = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND, + &gc_values); + + /* Cursor has cursor-color background, background-color foreground. */ + gc_values.foreground = FRAME_BACKGROUND_PIXEL (f); + gc_values.background = f->output_data.android->cursor_pixel; + f->output_data.android->cursor_gc + = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND, + &gc_values); + unblock_input (); +} + + +/* Free what was allocated in android_make_gc. */ + +void +android_free_gcs (struct frame *f) +{ + block_input (); + + if (f->output_data.android->normal_gc) + { + android_free_gc (f->output_data.android->normal_gc); + f->output_data.android->normal_gc = 0; + } + + if (f->output_data.android->reverse_gc) + { + android_free_gc (f->output_data.android->reverse_gc); + f->output_data.android->reverse_gc = 0; + } + + if (f->output_data.android->cursor_gc) + { + android_free_gc (f->output_data.android->cursor_gc); + f->output_data.android->cursor_gc = 0; + } + + unblock_input (); +} + +/* Handler for signals raised during x_create_frame and + Fx_create_tip_frame. FRAME is the frame which is partially + constructed. */ + +static Lisp_Object +unwind_create_frame (Lisp_Object frame) +{ + struct frame *f = XFRAME (frame); + + /* If frame is already dead, nothing to do. This can happen if the + display is disconnected after the frame has become official, but + before Fx_create_frame removes the unwind protect. */ + if (!FRAME_LIVE_P (f)) + return Qnil; + + /* If frame is ``official'', nothing to do. */ + if (NILP (Fmemq (frame, Vframe_list))) + { + /* If the frame's image cache refcount is still the same as our + private shadow variable, it means we are unwinding a frame + for which we didn't yet call init_frame_faces, where the + refcount is incremented. Therefore, we increment it here, so + that free_frame_faces, called in x_free_frame_resources + below, will not mistakenly decrement the counter that was not + incremented yet to account for this new frame. */ + if (FRAME_IMAGE_CACHE (f) != NULL + && FRAME_IMAGE_CACHE (f)->refcount == image_cache_refcount) + FRAME_IMAGE_CACHE (f)->refcount++; + + android_free_frame_resources (f); + free_glyphs (f); + return Qt; + } + + return Qnil; +} + +static void +do_unwind_create_frame (Lisp_Object frame) +{ + unwind_create_frame (frame); +} + +void +android_default_font_parameter (struct frame *f, Lisp_Object parms) +{ + struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); + Lisp_Object font_param = gui_display_get_arg (dpyinfo, parms, Qfont, NULL, NULL, + RES_TYPE_STRING); + Lisp_Object font = Qnil; + if (BASE_EQ (font_param, Qunbound)) + font_param = Qnil; + + if (NILP (font)) + font = (!NILP (font_param) + ? font_param + : gui_display_get_arg (dpyinfo, parms, + Qfont, "font", "Font", + RES_TYPE_STRING)); + + if (! FONTP (font) && ! STRINGP (font)) + { + const char *names[] = { + "Droid Sans Mono-12", + "Monospace-12", + "DroidSansMono-12", + NULL + }; + int i; + + for (i = 0; names[i]; i++) + { + font = font_open_by_name (f, build_unibyte_string (names[i])); + if (! NILP (font)) + break; + } + + if (NILP (font)) + error ("No suitable font was found"); + } + + gui_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING); +} + +static void +android_create_frame_window (struct frame *f) +{ + struct android_set_window_attributes attributes; + enum android_window_value_mask attribute_mask; + + attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f); + attribute_mask = ANDROID_CW_BACK_PIXEL; + + block_input (); + FRAME_ANDROID_WINDOW (f) + = android_create_window (FRAME_DISPLAY_INFO (f)->root_window, + f->left_pos, + f->top_pos, + FRAME_PIXEL_WIDTH (f), + FRAME_PIXEL_HEIGHT (f), + attribute_mask, &attributes); + unblock_input (); +} + +#endif /* ANDROID_STUBIFY */ + + + +DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, + 1, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object parms) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + struct frame *f; + Lisp_Object frame, tem; + Lisp_Object name; + bool minibuffer_only; + bool undecorated, override_redirect; + long window_prompting; + specpdl_ref count; + Lisp_Object display; + struct android_display_info *dpyinfo; + Lisp_Object parent, parent_frame; + struct kboard *kb; + + minibuffer_only = false; + undecorated = false; + override_redirect = false; + window_prompting = 0; + count = SPECPDL_INDEX (); + dpyinfo = NULL; + + /* Not actually used, but be consistent with X. */ + ((void) window_prompting); + + parms = Fcopy_alist (parms); + + /* Use this general default value to start with + until we know if this frame has a specified name. */ + Vx_resource_name = Vinvocation_name; + + display = gui_display_get_arg (dpyinfo, parms, Qterminal, 0, 0, + RES_TYPE_NUMBER); + if (BASE_EQ (display, Qunbound)) + display = gui_display_get_arg (dpyinfo, parms, Qdisplay, 0, 0, + RES_TYPE_STRING); + if (BASE_EQ (display, Qunbound)) + display = Qnil; + dpyinfo = check_android_display_info (display); + kb = dpyinfo->terminal->kboard; + + if (!dpyinfo->terminal->name) + error ("Terminal is not live, can't create new frames on it"); + + name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name", + RES_TYPE_STRING); + if (!STRINGP (name) + && ! BASE_EQ (name, Qunbound) + && ! NILP (name)) + error ("Invalid frame name--not a string or nil"); + + if (STRINGP (name)) + Vx_resource_name = name; + + /* See if parent window is specified. */ + parent = gui_display_get_arg (dpyinfo, parms, Qparent_id, NULL, NULL, + RES_TYPE_NUMBER); + if (BASE_EQ (parent, Qunbound)) + parent = Qnil; + if (! NILP (parent)) + CHECK_FIXNUM (parent); + + frame = Qnil; + tem = gui_display_get_arg (dpyinfo, + parms, Qminibuffer, "minibuffer", "Minibuffer", + RES_TYPE_SYMBOL); + if (EQ (tem, Qnone) || NILP (tem)) + f = make_frame_without_minibuffer (Qnil, kb, display); + else if (EQ (tem, Qonly)) + { + f = make_minibuffer_frame (); + minibuffer_only = true; + } + else if (WINDOWP (tem)) + f = make_frame_without_minibuffer (tem, kb, display); + else + f = make_frame (true); + + parent_frame = gui_display_get_arg (dpyinfo, + parms, + Qparent_frame, + NULL, + NULL, + RES_TYPE_SYMBOL); + /* Accept parent-frame iff parent-id was not specified. */ + if (!NILP (parent) + || BASE_EQ (parent_frame, Qunbound) + || NILP (parent_frame) + || !FRAMEP (parent_frame) + || !FRAME_LIVE_P (XFRAME (parent_frame)) + || !FRAME_ANDROID_P (XFRAME (parent_frame))) + parent_frame = Qnil; + + fset_parent_frame (f, parent_frame); + store_frame_param (f, Qparent_frame, parent_frame); + + if (!NILP (tem = (gui_display_get_arg (dpyinfo, + parms, + Qundecorated, + NULL, + NULL, + RES_TYPE_BOOLEAN))) + && !(BASE_EQ (tem, Qunbound))) + undecorated = true; + + FRAME_UNDECORATED (f) = undecorated; + store_frame_param (f, Qundecorated, undecorated ? Qt : Qnil); + + if (!NILP (tem = (gui_display_get_arg (dpyinfo, + parms, + Qoverride_redirect, + NULL, + NULL, + RES_TYPE_BOOLEAN))) + && !(BASE_EQ (tem, Qunbound))) + override_redirect = true; + + FRAME_OVERRIDE_REDIRECT (f) = override_redirect; + store_frame_param (f, Qoverride_redirect, override_redirect ? Qt : Qnil); + + XSETFRAME (frame, f); + + f->terminal = dpyinfo->terminal; + + f->output_method = output_android; + f->output_data.android = xzalloc (sizeof *f->output_data.android); + FRAME_FONTSET (f) = -1; + f->output_data.android->scroll_bar_foreground_pixel = -1; + f->output_data.android->scroll_bar_background_pixel = -1; + f->output_data.android->white_relief.pixel = -1; + f->output_data.android->black_relief.pixel = -1; + + fset_icon_name (f, gui_display_get_arg (dpyinfo, + parms, + Qicon_name, + "iconName", + "Title", + RES_TYPE_STRING)); + if (! STRINGP (f->icon_name)) + fset_icon_name (f, Qnil); + + FRAME_DISPLAY_INFO (f) = dpyinfo; + + /* With FRAME_DISPLAY_INFO set up, this unwind-protect is safe. */ + record_unwind_protect (do_unwind_create_frame, frame); + + /* These colors will be set anyway later, but it's important + to get the color reference counts right, so initialize them! + + (Not really on Android, but it's best to be consistent with + X.) */ + { + Lisp_Object black; + + /* Function x_decode_color can signal an error. Make + sure to initialize color slots so that we won't try + to free colors we haven't allocated. */ + FRAME_FOREGROUND_PIXEL (f) = -1; + FRAME_BACKGROUND_PIXEL (f) = -1; + f->output_data.android->cursor_pixel = -1; + f->output_data.android->cursor_foreground_pixel = -1; + f->output_data.android->mouse_pixel = -1; + + black = build_string ("black"); + FRAME_FOREGROUND_PIXEL (f) + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + FRAME_BACKGROUND_PIXEL (f) + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.android->cursor_pixel + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.android->cursor_foreground_pixel + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.android->mouse_pixel + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + } + + /* Set the name; the functions to which we pass f expect the name to + be set. */ + if (BASE_EQ (name, Qunbound) || NILP (name)) + { + fset_name (f, build_string ("GNU Emacs")); + f->explicit_name = false; + } + else + { + fset_name (f, name); + f->explicit_name = true; + /* Use the frame's title when getting resources for this frame. */ + specbind (Qx_resource_name, name); + } + + register_font_driver (&androidfont_driver, f); + register_font_driver (&android_sfntfont_driver, f); + + image_cache_refcount = (FRAME_IMAGE_CACHE (f) + ? FRAME_IMAGE_CACHE (f)->refcount + : 0); + + gui_default_parameter (f, parms, Qfont_backend, Qnil, + "fontBackend", "FontBackend", RES_TYPE_STRING); + + /* Extract the window parameters from the supplied values + that are needed to determine window geometry. */ + android_default_font_parameter (f, parms); + if (!FRAME_FONT (f)) + { + delete_frame (frame, Qnoelisp); + error ("Invalid frame font"); + } + + if (NILP (Fassq (Qinternal_border_width, parms))) + { + Lisp_Object value; + + value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width, + "internalBorder", "internalBorder", + RES_TYPE_NUMBER); + if (! BASE_EQ (value, Qunbound)) + parms = Fcons (Fcons (Qinternal_border_width, value), + parms); + } + + gui_default_parameter (f, parms, Qinternal_border_width, + make_fixnum (0), + "internalBorderWidth", "internalBorderWidth", + RES_TYPE_NUMBER); + + /* Same for child frames. */ + if (NILP (Fassq (Qchild_frame_border_width, parms))) + { + Lisp_Object value; + + value = gui_display_get_arg (dpyinfo, parms, Qchild_frame_border_width, + "childFrameBorder", "childFrameBorder", + RES_TYPE_NUMBER); + if (! BASE_EQ (value, Qunbound)) + parms = Fcons (Fcons (Qchild_frame_border_width, value), + parms); + } + + gui_default_parameter (f, parms, Qchild_frame_border_width, Qnil, + "childFrameBorderWidth", "childFrameBorderWidth", + RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0), + NULL, NULL, RES_TYPE_NUMBER); + + /* `vertical-scroll-bars' defaults to nil on Android as a + consequence of scroll bars not being supported at all. */ + + gui_default_parameter (f, parms, Qvertical_scroll_bars, Qnil, + "verticalScrollBars", "ScrollBars", + RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil, + "horizontalScrollBars", "ScrollBars", + RES_TYPE_SYMBOL); + + /* Also do the stuff which must be set before the window exists. */ + gui_default_parameter (f, parms, Qforeground_color, build_string ("black"), + "foreground", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qbackground_color, build_string ("white"), + "background", "Background", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qmouse_color, build_string ("black"), + "pointerColor", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qborder_color, build_string ("black"), + "borderColor", "BorderColor", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qscreen_gamma, Qnil, + "screenGamma", "ScreenGamma", RES_TYPE_FLOAT); + gui_default_parameter (f, parms, Qline_spacing, Qnil, + "lineSpacing", "LineSpacing", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qleft_fringe, Qnil, + "leftFringe", "LeftFringe", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qright_fringe, Qnil, + "rightFringe", "RightFringe", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qno_special_glyphs, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); + +#if 0 + android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_foreground, + "scrollBarForeground", + "ScrollBarForeground", true); + android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_background, + "scrollBarBackground", + "ScrollBarBackground", false); +#endif + + /* Init faces before gui_default_parameter is called for the + scroll-bar-width parameter because otherwise we end up in + init_iterator with a null face cache, which should not + happen. */ + + init_frame_faces (f); + + tem = gui_display_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL, + RES_TYPE_NUMBER); + if (FIXNUMP (tem)) + store_frame_param (f, Qmin_width, tem); + tem = gui_display_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL, + RES_TYPE_NUMBER); + if (FIXNUMP (tem)) + store_frame_param (f, Qmin_height, tem); + + adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f), + FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, true, + Qx_create_frame_1); + + /* Set the menu-bar-lines and tool-bar-lines parameters. We don't + look up the X resources controlling the menu-bar and tool-bar + here; they are processed specially at startup, and reflected in + the values of the mode variables. */ + + gui_default_parameter (f, parms, Qmenu_bar_lines, + NILP (Vmenu_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qtab_bar_lines, + NILP (Vtab_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qtool_bar_lines, + NILP (Vtool_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); + + gui_default_parameter (f, parms, Qbuffer_predicate, Qnil, + "bufferPredicate", "BufferPredicate", + RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qtitle, Qnil, + "title", "Title", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qwait_for_wm, Qt, + "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qtool_bar_position, + FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil, + "inhibitDoubleBuffering", "InhibitDoubleBuffering", + RES_TYPE_BOOLEAN); + + /* Compute the size of the X window. */ + window_prompting = gui_figure_window_size (f, parms, true, true); + + tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, + RES_TYPE_BOOLEAN); + f->no_split = minibuffer_only || EQ (tem, Qt); + + android_icon_verify (f, parms); + android_create_frame_window (f); + android_icon (f, parms); + android_make_gc (f); + + /* Now consider the frame official. */ + f->terminal->reference_count++; + Vframe_list = Fcons (frame, Vframe_list); + + /* We need to do this after creating the window, so that the + icon-creation functions can say whose icon they're + describing. */ + gui_default_parameter (f, parms, Qicon_type, Qt, + "bitmapIcon", "BitmapIcon", RES_TYPE_BOOLEAN); + + gui_default_parameter (f, parms, Qauto_raise, Qnil, + "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qauto_lower, Qnil, + "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qcursor_type, Qbox, + "cursorType", "CursorType", RES_TYPE_SYMBOL); + /* Scroll bars are not supported on Android, as they are near + useless. */ + gui_default_parameter (f, parms, Qscroll_bar_width, Qnil, + "scrollBarWidth", "ScrollBarWidth", + RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qscroll_bar_height, Qnil, + "scrollBarHeight", "ScrollBarHeight", + RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qalpha, Qnil, + "alpha", "Alpha", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qalpha_background, Qnil, + "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER); + + if (!NILP (parent_frame)) + { + struct frame *p = XFRAME (parent_frame); + + block_input (); + android_reparent_window (FRAME_ANDROID_WINDOW (f), + FRAME_ANDROID_WINDOW (p), + f->left_pos, f->top_pos); + unblock_input (); + } + + gui_default_parameter (f, parms, Qno_focus_on_map, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qno_accept_focus, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); + + /* Consider frame official, now. */ + f->can_set_window_size = true; + + adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), + 0, true, Qx_create_frame_2); + + /* Process fullscreen parameter here in the hope that normalizing a + fullheight/fullwidth frame will produce the size set by the last + adjust_frame_size call. Note that Android only supports the + `maximized' state. */ + gui_default_parameter (f, parms, Qfullscreen, Qmaximized, + "fullscreen", "Fullscreen", RES_TYPE_SYMBOL); + + /* When called from `x-create-frame-with-faces' visibility is + always explicitly nil. */ + Lisp_Object visibility + = gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0, + RES_TYPE_SYMBOL); + Lisp_Object height + = gui_display_get_arg (dpyinfo, parms, Qheight, 0, 0, RES_TYPE_NUMBER); + Lisp_Object width + = gui_display_get_arg (dpyinfo, parms, Qwidth, 0, 0, RES_TYPE_NUMBER); + + if (EQ (visibility, Qicon)) + { + f->was_invisible = true; + android_iconify_frame (f); + } + else + { + if (BASE_EQ (visibility, Qunbound)) + visibility = Qt; + + if (!NILP (visibility)) + android_make_frame_visible (f); + else + f->was_invisible = true; + } + + /* Leave f->was_invisible true only if height or width were + specified too. This takes effect only when we are not called + from `x-create-frame-with-faces' (see above comment). */ + f->was_invisible + = (f->was_invisible + && (!BASE_EQ (height, Qunbound) || !BASE_EQ (width, Qunbound))); + + store_frame_param (f, Qvisibility, visibility); + + /* Set whether or not frame synchronization is enabled. */ + gui_default_parameter (f, parms, Quse_frame_synchronization, Qt, + NULL, NULL, RES_TYPE_BOOLEAN); + + /* Works iff frame has been already mapped. */ + gui_default_parameter (f, parms, Qskip_taskbar, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); + /* The `z-group' parameter works only for visible frames. */ + gui_default_parameter (f, parms, Qz_group, Qnil, + NULL, NULL, RES_TYPE_SYMBOL); + + /* Initialize `default-minibuffer-frame' in case this is the first + frame on this terminal. */ + if (FRAME_HAS_MINIBUF_P (f) + && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame)) + || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame))))) + kset_default_minibuffer_frame (kb, frame); + + /* All remaining specified parameters, which have not been "used" by + gui_display_get_arg and friends, now go in the misc. alist of the + frame. */ + for (tem = parms; CONSP (tem); tem = XCDR (tem)) + if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem)))) + fset_param_alist (f, Fcons (XCAR (tem), f->param_alist)); + + /* Make sure windows on this frame appear in calls to next-window + and similar functions. */ + Vwindow_list = Qnil; + + return unbind_to (count, frame); +#endif +} + +DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, + 1, 2, 0, doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object color, Lisp_Object frame) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + Emacs_Color foo; + struct frame *f; + + f = decode_window_system_frame (frame); + + CHECK_STRING (color); + + if (android_defined_color (f, SSDATA (color), &foo, false, false)) + return Qt; + else + return Qnil; +#endif +} + +DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, + 0, doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object color, Lisp_Object frame) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + Emacs_Color foo; + struct frame *f; + + f = decode_window_system_frame (frame); + + CHECK_STRING (color); + + if (android_defined_color (f, SSDATA (color), &foo, false, false)) + return list3i (foo.red, foo.green, foo.blue); + else + return Qnil; +#endif +} + +DEFUN ("xw-display-color-p", Fxw_display_color_p, + Sxw_display_color_p, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ + return Qt; +} + +DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, + Sx_display_grayscale_p, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ + return Qnil; +} + +DEFUN ("x-display-pixel-width", Fx_display_pixel_width, + Sx_display_pixel_width, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + return make_fixnum (android_get_screen_width ()); +#endif +} + +DEFUN ("x-display-pixel-height", Fx_display_pixel_height, + Sx_display_pixel_height, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + return make_fixnum (android_get_screen_height ()); +#endif +} + +DEFUN ("x-display-planes", Fx_display_planes, Sx_display_planes, + 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ + struct android_display_info *dpyinfo; + + dpyinfo = check_android_display_info (terminal); + + return make_fixnum (dpyinfo->n_planes); +} + +DEFUN ("x-display-color-cells", Fx_display_color_cells, Sx_display_color_cells, + 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ + struct android_display_info *dpyinfo; + int nr_planes; + + dpyinfo = check_android_display_info (terminal); + nr_planes = dpyinfo->n_planes; + + /* Truncate nr_planes to 24 to avoid integer overflow. */ + + if (nr_planes > 24) + nr_planes = 24; + + return make_fixnum (1 << nr_planes); +} + +DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + check_android_display_info (terminal); + return Vandroid_build_manufacturer; +#endif +} + +DEFUN ("x-server-version", Fx_server_version, Sx_server_version, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + check_android_display_info (terminal); + return list3i (android_get_current_api_level (), 0, 0); +#endif +} + +DEFUN ("x-display-screens", Fx_display_screens, Sx_display_screens, + 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ + check_android_display_info (terminal); + return make_fixnum (1); +} + +DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, + 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + return make_fixnum (android_get_mm_width ()); +#endif +} + +DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, + 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + return make_fixnum (android_get_mm_height ()); +#endif +} + +DEFUN ("x-display-backing-store", Fx_display_backing_store, + Sx_display_backing_store, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ + check_android_display_info (terminal); + + /* Window contents are preserved insofar as they remain mapped, in a + fashion tantamount to WhenMapped. */ + return Qwhen_mapped; +} + +DEFUN ("x-display-visual-class", Fx_display_visual_class, + Sx_display_visual_class, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ + check_android_display_info (terminal); + + return Qtrue_color; +} + +#ifndef ANDROID_STUBIFY + +static Lisp_Object +android_make_monitor_attribute_list (struct MonitorInfo *monitors, + int n_monitors, + int primary_monitor) +{ + Lisp_Object monitor_frames; + Lisp_Object frame, rest; + struct frame *f; + + monitor_frames = make_nil_vector (n_monitors); + + FOR_EACH_FRAME (rest, frame) + { + f = XFRAME (frame); + + /* Associate all frames with the primary monitor. */ + + if (FRAME_WINDOW_P (f) + && !FRAME_TOOLTIP_P (f)) + ASET (monitor_frames, primary_monitor, + Fcons (frame, AREF (monitor_frames, + primary_monitor))); + } + + return make_monitor_attribute_list (monitors, n_monitors, + primary_monitor, + monitor_frames, NULL); +} + +#endif + +DEFUN ("android-display-monitor-attributes-list", + Fandroid_display_monitor_attributes_list, + Sandroid_display_monitor_attributes_list, + 0, 1, 0, + doc: /* Return a list of physical monitor attributes on the X display TERMINAL. + +The optional argument TERMINAL specifies which display to ask about. +TERMINAL should be a terminal object, a frame or a display name (a string). +If omitted or nil, that stands for the selected frame's display. + +Internal use only, use `display-monitor-attributes-list' instead. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + struct MonitorInfo monitor; + + memset (&monitor, 0, sizeof monitor); + monitor.geom.width = android_get_screen_width (); + monitor.geom.height = android_get_screen_height (); + monitor.mm_width = android_get_mm_width (); + monitor.mm_height = android_get_mm_height (); + monitor.work = monitor.geom; + monitor.name = (char *) "Android device monitor"; + + return android_make_monitor_attribute_list (&monitor, 1, 0); +#endif +} + +#ifndef ANDROID_STUBIFY + +static Lisp_Object +frame_geometry (Lisp_Object frame, Lisp_Object attribute) +{ + struct frame *f = decode_live_frame (frame); + android_window rootw; + unsigned int native_width, native_height, x_border_width = 0; + int x_native = 0, y_native = 0, xptr = 0, yptr = 0; + int left_off = 0, right_off = 0, top_off = 0, bottom_off = 0; + int outer_left, outer_top, outer_right, outer_bottom; + int native_left, native_top, native_right, native_bottom; + int inner_left, inner_top, inner_right, inner_bottom; + int internal_border_width; + bool menu_bar_external = false, tool_bar_external = false; + int menu_bar_height = 0, menu_bar_width = 0; + int tab_bar_height = 0, tab_bar_width = 0; + int tool_bar_height = 0, tool_bar_width = 0; + + if (FRAME_INITIAL_P (f) || !FRAME_ANDROID_P (f) + || !FRAME_ANDROID_WINDOW (f)) + return Qnil; + + block_input (); + android_get_geometry (FRAME_ANDROID_WINDOW (f), + &rootw, &x_native, &y_native, + &native_width, &native_height, &x_border_width); + unblock_input (); + + if (FRAME_PARENT_FRAME (f)) + { + Lisp_Object parent, edges; + + XSETFRAME (parent, FRAME_PARENT_FRAME (f)); + edges = Fandroid_frame_edges (parent, Qnative_edges); + if (!NILP (edges)) + { + x_native += XFIXNUM (Fnth (make_fixnum (0), edges)); + y_native += XFIXNUM (Fnth (make_fixnum (1), edges)); + } + + outer_left = x_native; + outer_top = y_native; + outer_right = outer_left + native_width + 2 * x_border_width; + outer_bottom = outer_top + native_height + 2 * x_border_width; + + native_left = x_native + x_border_width; + native_top = y_native + x_border_width; + native_right = native_left + native_width; + native_bottom = native_top + native_height; + } + else + { + outer_left = xptr; + outer_top = yptr; + outer_right = outer_left + left_off + native_width + right_off; + outer_bottom = outer_top + top_off + native_height + bottom_off; + + native_left = outer_left + left_off; + native_top = outer_top + top_off; + native_right = native_left + native_width; + native_bottom = native_top + native_height; + } + + internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); + inner_left = native_left + internal_border_width; + inner_top = native_top + internal_border_width; + inner_right = native_right - internal_border_width; + inner_bottom = native_bottom - internal_border_width; + + menu_bar_height = FRAME_MENU_BAR_HEIGHT (f); + inner_top += menu_bar_height; + menu_bar_width = menu_bar_height ? native_width : 0; + + tab_bar_height = FRAME_TAB_BAR_HEIGHT (f); + tab_bar_width = (tab_bar_height + ? native_width - 2 * internal_border_width + : 0); + inner_top += tab_bar_height; + + tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f); + tool_bar_width = (tool_bar_height + ? native_width - 2 * internal_border_width + : 0); + + /* Subtract or add to the inner dimensions based on the tool bar + position. */ + + if (EQ (FRAME_TOOL_BAR_POSITION (f), Qtop)) + inner_top += tool_bar_height; + else + inner_bottom -= tool_bar_height; + + /* Construct list. */ + if (EQ (attribute, Qouter_edges)) + return list4i (outer_left, outer_top, outer_right, outer_bottom); + else if (EQ (attribute, Qnative_edges)) + return list4i (native_left, native_top, native_right, native_bottom); + else if (EQ (attribute, Qinner_edges)) + return list4i (inner_left, inner_top, inner_right, inner_bottom); + else + return + list (Fcons (Qouter_position, + Fcons (make_fixnum (outer_left), + make_fixnum (outer_top))), + Fcons (Qouter_size, + Fcons (make_fixnum (outer_right - outer_left), + make_fixnum (outer_bottom - outer_top))), + /* Approximate. */ + Fcons (Qexternal_border_size, + Fcons (make_fixnum (right_off), + make_fixnum (bottom_off))), + Fcons (Qouter_border_width, make_fixnum (x_border_width)), + /* Approximate. */ + Fcons (Qtitle_bar_size, + Fcons (make_fixnum (0), + make_fixnum (top_off - bottom_off))), + Fcons (Qmenu_bar_external, menu_bar_external ? Qt : Qnil), + Fcons (Qmenu_bar_size, + Fcons (make_fixnum (menu_bar_width), + make_fixnum (menu_bar_height))), + Fcons (Qtab_bar_size, + Fcons (make_fixnum (tab_bar_width), + make_fixnum (tab_bar_height))), + Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil), + Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)), + Fcons (Qtool_bar_size, + Fcons (make_fixnum (tool_bar_width), + make_fixnum (tool_bar_height))), + Fcons (Qinternal_border_width, + make_fixnum (internal_border_width))); +} + +#endif + +DEFUN ("android-frame-geometry", Fandroid_frame_geometry, + Sandroid_frame_geometry, + 0, 1, 0, + doc: /* Return geometric attributes of FRAME. +FRAME must be a live frame and defaults to the selected one. The return +value is an association list of the attributes listed below. All height +and width values are in pixels. + +`outer-position' is a cons of the outer left and top edges of FRAME + relative to the origin - the position (0, 0) - of FRAME's display. + +`outer-size' is a cons of the outer width and height of FRAME. The + outer size includes the title bar and the external borders as well as + any menu and/or tool bar of frame. + +`external-border-size' is a cons of the horizontal and vertical width of + FRAME's external borders as supplied by the window manager. + +`title-bar-size' is a cons of the width and height of the title bar of + FRAME as supplied by the window manager. If both of them are zero, + FRAME has no title bar. If only the width is zero, Emacs was not + able to retrieve the width information. + +`menu-bar-external', if non-nil, means the menu bar is external (never + included in the inner edges of FRAME). + +`menu-bar-size' is a cons of the width and height of the menu bar of + FRAME. + +`tool-bar-external', if non-nil, means the tool bar is external (never + included in the inner edges of FRAME). + +`tool-bar-position' tells on which side the tool bar on FRAME is and can + be one of `left', `top', `right' or `bottom'. If this is nil, FRAME + has no tool bar. + +`tool-bar-size' is a cons of the width and height of the tool bar of + FRAME. + +`internal-border-width' is the width of the internal border of + FRAME. */) + (Lisp_Object frame) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + return frame_geometry (frame, Qnil); +#endif +} + +DEFUN ("android-frame-edges", Fandroid_frame_edges, + Sandroid_frame_edges, 0, 2, 0, + doc: /* Return edge coordinates of FRAME. +FRAME must be a live frame and defaults to the selected one. The return +value is a list of the form (LEFT, TOP, RIGHT, BOTTOM). All values are +in pixels relative to the origin - the position (0, 0) - of FRAME's +display. + +If optional argument TYPE is the symbol `outer-edges', return the outer +edges of FRAME. The outer edges comprise the decorations of the window +manager (like the title bar or external borders) as well as any external +menu or tool bar of FRAME. If optional argument TYPE is the symbol +`native-edges' or nil, return the native edges of FRAME. The native +edges exclude the decorations of the window manager and any external +menu or tool bar of FRAME. If TYPE is the symbol `inner-edges', return +the inner edges of FRAME. These edges exclude title bar, any borders, +menu bar or tool bar of FRAME. */) + (Lisp_Object frame, Lisp_Object type) +{ +#ifndef ANDROID_STUBIFY + return frame_geometry (frame, ((EQ (type, Qouter_edges) + || EQ (type, Qinner_edges)) + ? type + : Qnative_edges)); +#else + return Qnil; +#endif +} + +#ifndef ANDROID_STUBIFY + +static Lisp_Object +android_frame_list_z_order (struct android_display_info *dpyinfo, + android_window window) +{ + android_window root, parent, *children; + unsigned int nchildren; + unsigned long i; + Lisp_Object frames; + + frames = Qnil; + + if (android_query_tree (window, &root, &parent, + &children, &nchildren)) + { + for (i = 0; i < nchildren; i++) + { + Lisp_Object frame, tail; + + FOR_EACH_FRAME (tail, frame) + { + struct frame *cf = XFRAME (frame); + + if (FRAME_ANDROID_P (cf) + && (FRAME_ANDROID_WINDOW (cf) == children[i])) + frames = Fcons (frame, frames); + } + } + + if (children) + xfree (children); + } + + return frames; +} + +#endif + +DEFUN ("android-frame-list-z-order", Fandroid_frame_list_z_order, + Sandroid_frame_list_z_order, 0, 1, 0, + doc: /* Return list of Emacs' frames, in Z (stacking) order. +The optional argument TERMINAL specifies which display to ask about. +TERMINAL should be either a frame or a display name (a string). If +omitted or nil, that stands for the selected frame's display. Return +nil if TERMINAL contains no Emacs frame. + +As a special case, if TERMINAL is non-nil and specifies a live frame, +return the child frames of that frame in Z (stacking) order. + +Frames are listed from topmost (first) to bottommost (last). + +On Android, the order of the frames returned is undefined unless +TERMINAL is a frame. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + struct android_display_info *dpyinfo; + android_window window; + + dpyinfo = check_android_display_info (terminal); + + if (FRAMEP (terminal) && FRAME_LIVE_P (XFRAME (terminal))) + window = FRAME_ANDROID_WINDOW (XFRAME (terminal)); + else + window = dpyinfo->root_window; + + return android_frame_list_z_order (dpyinfo, window); +#endif +} + +#ifndef ANDROID_STUBIFY + +static void +android_frame_restack (struct frame *f1, struct frame *f2, + bool above_flag) +{ + android_window window1; + struct android_window_changes wc; + unsigned long mask; + + window1 = FRAME_ANDROID_WINDOW (f1); + wc.sibling = FRAME_ANDROID_WINDOW (f2); + wc.stack_mode = above_flag ? ANDROID_ABOVE : ANDROID_BELOW; + mask = ANDROID_CW_SIBLING | ANDROID_CW_STACK_MODE; + + block_input (); + android_reconfigure_wm_window (window1, mask, &wc); + unblock_input (); +} + +#endif /* !ANDROID_STUBIFY */ + +DEFUN ("android-frame-restack", Fandroid_frame_restack, + Sandroid_frame_restack, 2, 3, 0, + doc: /* Restack FRAME1 below FRAME2. +This means that if both frames are visible and the display areas of +these frames overlap, FRAME2 (partially) obscures FRAME1. If optional +third argument ABOVE is non-nil, restack FRAME1 above FRAME2. This +means that if both frames are visible and the display areas of these +frames overlap, FRAME1 (partially) obscures FRAME2. + +This may be thought of as an atomic action performed in two steps: The +first step removes FRAME1's window-step window from the display. The +second step reinserts FRAME1's window below (above if ABOVE is true) +that of FRAME2. Hence the position of FRAME2 in its display's Z +\(stacking) order relative to all other frames excluding FRAME1 remains +unaltered. + +Android does not facilitate restacking top-level windows managed by +its own window manager; nor is it possible to restack frames that are +children of different parents. Consequently, this function only +functions when FRAME1 and FRAME2 are both child frames subordinate to +the same parent frame. */) + (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object above) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else /* !ANDROID_STUBIFY */ + struct frame *f1 = decode_live_frame (frame1); + struct frame *f2 = decode_live_frame (frame2); + + if (!(FRAME_ANDROID_WINDOW (f1) && FRAME_ANDROID_WINDOW (f2))) + error ("Cannot restack frames"); + android_frame_restack (f1, f2, !NILP (above)); + return Qt; +#endif /* ANDROID_STUBIFY */ +} + +DEFUN ("android-mouse-absolute-pixel-position", + Fandroid_mouse_absolute_pixel_position, + Sandroid_mouse_absolute_pixel_position, 0, 0, 0, + doc: /* Return absolute position of mouse cursor in pixels. +The position is returned as a cons cell (X . Y) of the coordinates of +the mouse cursor position in pixels relative to a position (0, 0) of the +selected frame's display. This does not work on Android. */) + (void) +{ + /* This cannot be implemented on Android. */ + return Qnil; +} + +DEFUN ("android-set-mouse-absolute-pixel-position", + Fandroid_set_mouse_absolute_pixel_position, + Sandroid_set_mouse_absolute_pixel_position, 2, 2, 0, + doc: /* Move mouse pointer to a pixel position at (X, Y). The +coordinates X and Y are interpreted to start from the top-left corner +of the screen. This does not work on Android. */) + (Lisp_Object x, Lisp_Object y) +{ + /* This cannot be implemented on Android. */ + return Qnil; +} + +DEFUN ("android-get-connection", Fandroid_get_connection, + Sandroid_get_connection, 0, 0, 0, + doc: /* Get the connection to the display server. +Return the terminal if it exists, else nil. + +Emacs cannot open a connection to the display server itself under +Android, so there is no equivalent of `x-open-connection'. */) + (void) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + Lisp_Object terminal; + + terminal = Qnil; + + if (x_display_list) + XSETTERMINAL (terminal, x_display_list->terminal); + + return terminal; +#endif +} + +DEFUN ("x-display-list", Fx_display_list, Sx_display_list, 0, 0, 0, + doc: /* SKIP: real doc in xfns.c. */) + (void) +{ + Lisp_Object result; + + result = Qnil; + + if (x_display_list) + result = Fcons (XCAR (x_display_list->name_list_element), + result); + + return result; +} + +#ifndef ANDROID_STUBIFY + +static void +unwind_create_tip_frame (Lisp_Object frame) +{ + Lisp_Object deleted; + + deleted = unwind_create_frame (frame); + if (EQ (deleted, Qt)) + { + tip_window = ANDROID_NONE; + tip_frame = Qnil; + } +} + +static Lisp_Object +android_create_tip_frame (struct android_display_info *dpyinfo, + Lisp_Object parms) +{ + struct frame *f; + Lisp_Object frame; + Lisp_Object name; + specpdl_ref count = SPECPDL_INDEX (); + bool face_change_before = face_change; + + if (!dpyinfo->terminal->name) + error ("Terminal is not live, can't create new frames on it"); + + parms = Fcopy_alist (parms); + + /* Get the name of the frame to use for resource lookup. */ + name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name", + RES_TYPE_STRING); + if (!STRINGP (name) + && !BASE_EQ (name, Qunbound) + && !NILP (name)) + error ("Invalid frame name--not a string or nil"); + + frame = Qnil; + f = make_frame (false); + f->wants_modeline = false; + XSETFRAME (frame, f); + record_unwind_protect (unwind_create_tip_frame, frame); + + f->terminal = dpyinfo->terminal; + + /* By setting the output method, we're essentially saying that + the frame is live, as per FRAME_LIVE_P. If we get a signal + from this point on, x_destroy_window might screw up reference + counts etc. */ + f->output_method = output_android; + f->output_data.android = xzalloc (sizeof *f->output_data.android); + FRAME_FONTSET (f) = -1; + f->output_data.android->white_relief.pixel = -1; + f->output_data.android->black_relief.pixel = -1; + + f->tooltip = true; + fset_icon_name (f, Qnil); + FRAME_DISPLAY_INFO (f) = dpyinfo; + f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; + + /* These colors will be set anyway later, but it's important + to get the color reference counts right, so initialize them! */ + { + Lisp_Object black; + + /* Function android_decode_color can signal an error. Make sure + to initialize color slots so that we won't try to free colors + we haven't allocated. */ + FRAME_FOREGROUND_PIXEL (f) = -1; + FRAME_BACKGROUND_PIXEL (f) = -1; + f->output_data.android->cursor_pixel = -1; + f->output_data.android->cursor_foreground_pixel = -1; + f->output_data.android->mouse_pixel = -1; + + black = build_string ("black"); + FRAME_FOREGROUND_PIXEL (f) + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + FRAME_BACKGROUND_PIXEL (f) + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.android->cursor_pixel + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.android->cursor_foreground_pixel + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.android->mouse_pixel + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + } + + /* Set the name; the functions to which we pass f expect the name to + be set. */ + if (BASE_EQ (name, Qunbound) || NILP (name)) + f->explicit_name = false; + else + { + fset_name (f, name); + f->explicit_name = true; + /* use the frame's title when getting resources for this frame. */ + specbind (Qx_resource_name, name); + } + + register_font_driver (&androidfont_driver, f); + register_font_driver (&android_sfntfont_driver, f); + + image_cache_refcount + = FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0; +#ifdef GLYPH_DEBUG + dpyinfo_refcount = dpyinfo->reference_count; +#endif /* GLYPH_DEBUG */ + + gui_default_parameter (f, parms, Qfont_backend, Qnil, + "fontBackend", "FontBackend", RES_TYPE_STRING); + + /* Extract the window parameters from the supplied values that are + needed to determine window geometry. */ + android_default_font_parameter (f, parms); + + gui_default_parameter (f, parms, Qborder_width, make_fixnum (0), + "borderWidth", "BorderWidth", RES_TYPE_NUMBER); + + /* This defaults to 1 in order to match xterm. We recognize either + internalBorderWidth or internalBorder (which is what xterm calls + it). */ + if (NILP (Fassq (Qinternal_border_width, parms))) + { + Lisp_Object value; + + value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width, + "internalBorder", "internalBorder", + RES_TYPE_NUMBER); + if (! BASE_EQ (value, Qunbound)) + parms = Fcons (Fcons (Qinternal_border_width, value), + parms); + } + + gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (1), + "internalBorderWidth", "internalBorderWidth", + RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0), + NULL, NULL, RES_TYPE_NUMBER); + + /* Also do the stuff which must be set before the window exists. */ + gui_default_parameter (f, parms, Qforeground_color, build_string ("black"), + "foreground", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qbackground_color, build_string ("white"), + "background", "Background", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qmouse_color, build_string ("black"), + "pointerColor", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qcursor_color, build_string ("black"), + "cursorColor", "Foreground", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qborder_color, build_string ("black"), + "borderColor", "BorderColor", RES_TYPE_STRING); + gui_default_parameter (f, parms, Qno_special_glyphs, Qnil, + NULL, NULL, RES_TYPE_BOOLEAN); + + { + struct android_set_window_attributes attrs; + unsigned long mask; + + block_input (); + mask = ANDROID_CW_OVERRIDE_REDIRECT | ANDROID_CW_BACK_PIXEL; + + attrs.override_redirect = true; + attrs.background_pixel = FRAME_BACKGROUND_PIXEL (f); + tip_window + = FRAME_ANDROID_WINDOW (f) + = android_create_window (FRAME_DISPLAY_INFO (f)->root_window, + /* x, y, width, height, value-mask, + attrs. */ + 0, 0, 1, 1, mask, &attrs); + unblock_input (); + } + + /* Init faces before gui_default_parameter is called for the + scroll-bar-width parameter because otherwise we end up in + init_iterator with a null face cache, which should not happen. */ + init_frame_faces (f); + + gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil, + "inhibitDoubleBuffering", "InhibitDoubleBuffering", + RES_TYPE_BOOLEAN); + + gui_figure_window_size (f, parms, false, false); + + f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; + + android_make_gc (f); + + gui_default_parameter (f, parms, Qauto_raise, Qnil, + "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qauto_lower, Qnil, + "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); + gui_default_parameter (f, parms, Qcursor_type, Qbox, + "cursorType", "CursorType", RES_TYPE_SYMBOL); + gui_default_parameter (f, parms, Qalpha, Qnil, + "alpha", "Alpha", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qalpha_background, Qnil, + "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER); + + /* Add `tooltip' frame parameter's default value. */ + if (NILP (Fframe_parameter (frame, Qtooltip))) + { + AUTO_FRAME_ARG (arg, Qtooltip, Qt); + Fmodify_frame_parameters (frame, arg); + } + + /* FIXME - can this be done in a similar way to normal frames? + https://lists.gnu.org/r/emacs-devel/2007-10/msg00641.html */ + + /* Set the `display-type' frame parameter before setting up faces. */ + { + Lisp_Object disptype; + + disptype = Qcolor; + + if (NILP (Fframe_parameter (frame, Qdisplay_type))) + { + AUTO_FRAME_ARG (arg, Qdisplay_type, disptype); + Fmodify_frame_parameters (frame, arg); + } + } + + /* Set up faces after all frame parameters are known. This call + also merges in face attributes specified for new frames. */ + { + Lisp_Object bg = Fframe_parameter (frame, Qbackground_color); + + call2 (Qface_set_after_frame_default, frame, Qnil); + + if (!EQ (bg, Fframe_parameter (frame, Qbackground_color))) + { + AUTO_FRAME_ARG (arg, Qbackground_color, bg); + Fmodify_frame_parameters (frame, arg); + } + } + + f->no_split = true; + + /* Now that the frame will be official, it counts as a reference to + its display and terminal. */ + f->terminal->reference_count++; + + /* It is now ok to make the frame official even if we get an error + below. And the frame needs to be on Vframe_list or making it + visible won't work. */ + Vframe_list = Fcons (frame, Vframe_list); + f->can_set_window_size = true; + adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), + 0, true, Qtip_frame); + + /* Setting attributes of faces of the tooltip frame from resources + and similar will set face_change, which leads to the clearing of + all current matrices. Since this isn't necessary here, avoid it + by resetting face_change to the value it had before we created + the tip frame. */ + face_change = face_change_before; + + /* Discard the unwind_protect. */ + return unbind_to (count, frame); +} + +static Lisp_Object +android_hide_tip (bool delete) +{ + if (!NILP (tip_timer)) + { + call1 (Qcancel_timer, tip_timer); + tip_timer = Qnil; + } + + if (NILP (tip_frame) + || (!delete + && !NILP (tip_frame) + && FRAME_LIVE_P (XFRAME (tip_frame)) + && !FRAME_VISIBLE_P (XFRAME (tip_frame)))) + return Qnil; + else + { + Lisp_Object was_open = Qnil; + + specpdl_ref count = SPECPDL_INDEX (); + specbind (Qinhibit_redisplay, Qt); + specbind (Qinhibit_quit, Qt); + + if (!NILP (tip_frame)) + { + struct frame *f = XFRAME (tip_frame); + + if (FRAME_LIVE_P (f)) + { + if (delete) + { + delete_frame (tip_frame, Qnil); + tip_frame = Qnil; + } + else + android_make_frame_invisible (XFRAME (tip_frame)); + + was_open = Qt; + } + else + tip_frame = Qnil; + } + else + tip_frame = Qnil; + + return unbind_to (count, was_open); + } +} + +static void +compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, + Lisp_Object dy, int width, int height, int *root_x, + int *root_y) +{ + Lisp_Object left, top, right, bottom; + int min_x, min_y, max_x, max_y = -1; + android_window window; + struct frame *mouse_frame; + + /* Initialize these values in case there is no mouse frame. */ + *root_x = 0; + *root_y = 0; + + /* User-specified position? */ + left = CDR (Fassq (Qleft, parms)); + top = CDR (Fassq (Qtop, parms)); + right = CDR (Fassq (Qright, parms)); + bottom = CDR (Fassq (Qbottom, parms)); + + /* Move the tooltip window where the mouse pointer was last seen. + Resize and show it. */ + if ((!FIXNUMP (left) && !FIXNUMP (right)) + || (!FIXNUMP (top) && !FIXNUMP (bottom))) + { + if (x_display_list->last_mouse_motion_frame) + { + *root_x = x_display_list->last_mouse_motion_x; + *root_y = x_display_list->last_mouse_motion_y; + mouse_frame = x_display_list->last_mouse_motion_frame; + window = FRAME_ANDROID_WINDOW (mouse_frame); + + /* Translate the coordinates to the screen. */ + android_translate_coordinates (window, *root_x, *root_y, + root_x, root_y); + } + } + + min_x = 0; + min_y = 0; + max_x = android_get_screen_width (); + max_y = android_get_screen_height (); + + if (FIXNUMP (top)) + *root_y = XFIXNUM (top); + else if (FIXNUMP (bottom)) + *root_y = XFIXNUM (bottom) - height; + else if (*root_y + XFIXNUM (dy) <= min_y) + *root_y = min_y; /* Can happen for negative dy */ + else if (*root_y + XFIXNUM (dy) + height <= max_y) + /* It fits below the pointer */ + *root_y += XFIXNUM (dy); + else if (height + XFIXNUM (dy) + min_y <= *root_y) + /* It fits above the pointer. */ + *root_y -= height + XFIXNUM (dy); + else + /* Put it on the top. */ + *root_y = min_y; + + if (FIXNUMP (left)) + *root_x = XFIXNUM (left); + else if (FIXNUMP (right)) + *root_x = XFIXNUM (right) - width; + else if (*root_x + XFIXNUM (dx) <= min_x) + *root_x = 0; /* Can happen for negative dx */ + else if (*root_x + XFIXNUM (dx) + width <= max_x) + /* It fits to the right of the pointer. */ + *root_x += XFIXNUM (dx); + else if (width + XFIXNUM (dx) + min_x <= *root_x) + /* It fits to the left of the pointer. */ + *root_x -= width + XFIXNUM (dx); + else + /* Put it left justified on the screen -- it ought to fit that way. */ + *root_x = min_x; +} + +#endif + +DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, + Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + struct frame *f, *tip_f; + struct window *w; + int root_x, root_y; + struct buffer *old_buffer; + struct text_pos pos; + int width, height; + int old_windows_or_buffers_changed = windows_or_buffers_changed; + specpdl_ref count = SPECPDL_INDEX (); + Lisp_Object window, size, tip_buf; + bool displayed; +#ifdef ENABLE_CHECKING + struct glyph_row *row, *end; +#endif + AUTO_STRING (tip, " *tip*"); + + specbind (Qinhibit_redisplay, Qt); + + CHECK_STRING (string); + if (SCHARS (string) == 0) + string = make_unibyte_string (" ", 1); + + if (NILP (frame)) + frame = selected_frame; + f = decode_window_system_frame (frame); + + if (NILP (timeout)) + timeout = Vx_show_tooltip_timeout; + CHECK_FIXNAT (timeout); + + if (NILP (dx)) + dx = make_fixnum (5); + else + CHECK_FIXNUM (dx); + + if (NILP (dy)) + dy = make_fixnum (-10); + else + CHECK_FIXNUM (dy); + + tip_dx = dx; + tip_dy = dy; + + if (!NILP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame))) + { + if (FRAME_VISIBLE_P (XFRAME (tip_frame)) + && !NILP (Fequal_including_properties (tip_last_string, + string)) + && !NILP (Fequal (tip_last_parms, parms))) + { + /* Only DX and DY have changed. */ + tip_f = XFRAME (tip_frame); + if (!NILP (tip_timer)) + { + call1 (Qcancel_timer, tip_timer); + tip_timer = Qnil; + } + + block_input (); + compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f), + FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y); + android_move_window (FRAME_ANDROID_WINDOW (tip_f), + root_x, root_y); + unblock_input (); + + goto start_timer; + } + else + android_hide_tip (true); + } + else + android_hide_tip (true); + + tip_last_frame = frame; + tip_last_string = string; + tip_last_parms = parms; + + if (NILP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame))) + { + /* Add default values to frame parameters. */ + if (NILP (Fassq (Qname, parms))) + parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms); + if (NILP (Fassq (Qinternal_border_width, parms))) + parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)), + parms); + if (NILP (Fassq (Qborder_width, parms))) + parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms); + if (NILP (Fassq (Qborder_color, parms))) + parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), + parms); + if (NILP (Fassq (Qbackground_color, parms))) + parms = Fcons (Fcons (Qbackground_color, + build_string ("lightyellow")), + parms); + + /* Create a frame for the tooltip, and record it in the global + variable tip_frame. */ + if (NILP (tip_frame = android_create_tip_frame (FRAME_DISPLAY_INFO (f), + parms))) + /* Creating the tip frame failed. */ + return unbind_to (count, Qnil); + } + + tip_f = XFRAME (tip_frame); + window = FRAME_ROOT_WINDOW (tip_f); + tip_buf = Fget_buffer_create (tip, Qnil); + /* We will mark the tip window a "pseudo-window" below, and such + windows cannot have display margins. */ + bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0)); + bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0)); + set_window_buffer (window, tip_buf, false, false); + w = XWINDOW (window); + w->pseudo_window_p = true; + /* Try to avoid that `other-window' select us (Bug#47207). */ + Fset_window_parameter (window, Qno_other_window, Qt); + + /* Set up the frame's root window. Note: The following code does not + try to size the window or its frame correctly. Its only purpose is + to make the subsequent text size calculations work. The right + sizes should get installed when the toolkit gets back to us. */ + w->left_col = 0; + w->top_line = 0; + w->pixel_left = 0; + w->pixel_top = 0; + + if (CONSP (Vx_max_tooltip_size) + && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX) + && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX)) + { + w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size)); + w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size)); + } + else + { + w->total_cols = 80; + w->total_lines = 40; + } + + w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f); + w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f); + FRAME_TOTAL_COLS (tip_f) = w->total_cols; + adjust_frame_glyphs (tip_f); + + /* Insert STRING into root window's buffer and fit the frame to the + buffer. */ + specpdl_ref count_1 = SPECPDL_INDEX (); + old_buffer = current_buffer; + set_buffer_internal_1 (XBUFFER (w->contents)); + bset_truncate_lines (current_buffer, Qnil); + specbind (Qinhibit_read_only, Qt); + specbind (Qinhibit_modification_hooks, Qt); + specbind (Qinhibit_point_motion_hooks, Qt); + Ferase_buffer (); + Finsert (1, &string); + clear_glyph_matrix (w->desired_matrix); + clear_glyph_matrix (w->current_matrix); + SET_TEXT_POS (pos, BEGV, BEGV_BYTE); + displayed = try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); + + if (!displayed && NILP (Vx_max_tooltip_size)) + { +#ifdef ENABLE_CHECKING + row = w->desired_matrix->rows; + end = w->desired_matrix->rows + w->desired_matrix->nrows; + + while (row < end) + { + if (!row->displays_text_p + || row->ends_at_zv_p) + break; + ++row; + } + + eassert (row < end && row->ends_at_zv_p); +#endif + } + + /* Calculate size of tooltip window. */ + size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil, + make_fixnum (w->pixel_height), Qnil, + Qnil); + /* Add the frame's internal border to calculated size. */ + width = XFIXNUM (CAR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + height = XFIXNUM (CDR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + + /* Calculate position of tooltip frame. */ + compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y); + + /* Show tooltip frame. */ + block_input (); + android_move_resize_window (FRAME_ANDROID_WINDOW (tip_f), + root_x, root_y, width, + height); + android_map_raised (FRAME_ANDROID_WINDOW (tip_f)); + unblock_input (); + + /* Garbage the tip frame too. */ + SET_FRAME_GARBAGED (tip_f); + + w->must_be_updated_p = true; + update_single_window (w); + flush_frame (tip_f); + set_buffer_internal_1 (old_buffer); + unbind_to (count_1, Qnil); + windows_or_buffers_changed = old_windows_or_buffers_changed; + + /* MapNotify events are not sent on Android, so make the frame + visible. */ + + SET_FRAME_VISIBLE (tip_f, true); + + start_timer: + /* Let the tip disappear after timeout seconds. */ + tip_timer = call3 (Qrun_at_time, timeout, Qnil, + Qx_hide_tip); + + return unbind_to (count, Qnil); +#endif +} + +DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0, + doc: /* SKIP: real doc in xfns.c. */) + (void) +{ +#ifdef ANDROID_STUBIFY + /* Fx_hide_tip is called from pre-command-hook (in turn called from + the tests.) Since signaling here prevents any tests from being + run, refrain from protesting if this stub is called. */ +#if 0 + error ("Android cross-compilation stub called!"); +#endif /* 0 */ + return Qnil; +#else /* !ANDROID_STUBIFY */ + return android_hide_tip (true); +#endif /* ANDROID_STUBIFY */ +} + +DEFUN ("android-detect-mouse", Fandroid_detect_mouse, + Sandroid_detect_mouse, 0, 0, 0, + doc: /* Figure out whether or not there is a mouse. +Return non-nil if a mouse is connected to this computer, and nil if +there is no mouse. */) + (void) +{ +#ifndef ANDROID_STUBIFY + /* If no display connection is present, just return nil. */ + + if (!android_init_gui) + return Qnil; + + return android_detect_mouse () ? Qt : Qnil; +#else + return Qnil; +#endif +} + +DEFUN ("android-toggle-on-screen-keyboard", + Fandroid_toggle_on_screen_keyboard, + Sandroid_toggle_on_screen_keyboard, 2, 2, 0, + doc: /* Display or hide the on-screen keyboard. +If HIDE is non-nil, hide the on screen keyboard if it is currently +being displayed. Else, request that the system display it on behalf +of FRAME. This request may be rejected if FRAME does not have the +input focus. */) + (Lisp_Object frame, Lisp_Object hide) +{ +#ifndef ANDROID_STUBIFY + struct frame *f; + + f = decode_window_system_frame (frame); + + block_input (); + android_toggle_on_screen_keyboard (FRAME_ANDROID_WINDOW (f), + NILP (hide)); + unblock_input (); +#endif + + return Qnil; +} + + + +#ifndef ANDROID_STUBIFY + +static void +android_set_background_color (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + struct android_output *x; + unsigned long bg; + + x = f->output_data.android; + bg = android_decode_color (f, arg, WHITE_PIX_DEFAULT (f)); + FRAME_BACKGROUND_PIXEL (f) = bg; + + if (FRAME_ANDROID_WINDOW (f) != 0) + { + block_input (); + android_set_background (x->normal_gc, bg); + android_set_foreground (x->reverse_gc, bg); + android_set_window_background (FRAME_ANDROID_WINDOW (f), bg); + android_set_foreground (x->cursor_gc, bg); + unblock_input (); + + update_face_from_frame_parameter (f, Qbackground_color, arg); + + if (FRAME_VISIBLE_P (f)) + redraw_frame (f); + } +} + +static void +android_set_border_color (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + /* Left unimplemented because Android has no window borders. */ + CHECK_STRING (arg); + android_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); + update_face_from_frame_parameter (f, Qborder_color, arg); +} + +static void +android_set_cursor_color (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + unsigned long fore_pixel, pixel; + struct android_output *x; + + x = f->output_data.android; + + if (!NILP (Vx_cursor_fore_pixel)) + fore_pixel = android_decode_color (f, Vx_cursor_fore_pixel, + WHITE_PIX_DEFAULT (f)); + else + fore_pixel = FRAME_BACKGROUND_PIXEL (f); + + pixel = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); + + /* Make sure that the cursor color differs from the background color. */ + if (pixel == FRAME_BACKGROUND_PIXEL (f)) + { + pixel = FRAME_FOREGROUND_PIXEL (f); + if (pixel == fore_pixel) + fore_pixel = FRAME_BACKGROUND_PIXEL (f); + } + + x->cursor_foreground_pixel = fore_pixel; + x->cursor_pixel = pixel; + + if (FRAME_ANDROID_WINDOW (f) != 0) + { + block_input (); + android_set_background (x->cursor_gc, x->cursor_pixel); + android_set_foreground (x->cursor_gc, fore_pixel); + unblock_input (); + + if (FRAME_VISIBLE_P (f)) + { + gui_update_cursor (f, false); + gui_update_cursor (f, true); + } + } + + update_face_from_frame_parameter (f, Qcursor_color, arg); +} + +static void +android_set_cursor_type (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + set_frame_cursor_types (f, arg); +} + +static void +android_set_foreground_color (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + struct android_output *x; + unsigned long fg, old_fg; + + x = f->output_data.android; + + fg = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); + old_fg = FRAME_FOREGROUND_PIXEL (f); + FRAME_FOREGROUND_PIXEL (f) = fg; + + if (FRAME_ANDROID_WINDOW (f) != 0) + { + block_input (); + android_set_foreground (x->normal_gc, fg); + android_set_background (x->reverse_gc, fg); + + if (x->cursor_pixel == old_fg) + { + x->cursor_pixel = fg; + android_set_background (x->cursor_gc, x->cursor_pixel); + } + + unblock_input (); + + update_face_from_frame_parameter (f, Qforeground_color, arg); + + if (FRAME_VISIBLE_P (f)) + redraw_frame (f); + } +} + +static void +android_set_child_frame_border_width (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + int border; + + if (NILP (arg)) + border = -1; + else if (RANGED_FIXNUMP (0, arg, INT_MAX)) + border = XFIXNAT (arg); + else + signal_error ("Invalid child frame border width", arg); + + if (border != FRAME_CHILD_FRAME_BORDER_WIDTH (f)) + { + f->child_frame_border_width = border; + + if (FRAME_ANDROID_WINDOW (f)) + { + adjust_frame_size (f, -1, -1, 3, false, Qchild_frame_border_width); + android_clear_under_internal_border (f); + } + } +} + +static void +android_set_internal_border_width (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + int border = check_int_nonnegative (arg); + + if (border != FRAME_INTERNAL_BORDER_WIDTH (f)) + { + f->internal_border_width = border; + + if (FRAME_ANDROID_WINDOW (f)) + { + adjust_frame_size (f, -1, -1, 3, false, Qinternal_border_width); + android_clear_under_internal_border (f); + } + } +} + +static void +android_set_menu_bar_lines (struct frame *f, Lisp_Object value, + Lisp_Object oldval) +{ + int nlines; + int olines = FRAME_MENU_BAR_LINES (f); + + /* Right now, menu bars don't work properly in minibuf-only frames; + most of the commands try to apply themselves to the minibuffer + frame itself, and get an error because you can't switch buffers + in or split the minibuffer window. */ + if (FRAME_MINIBUF_ONLY_P (f) || FRAME_PARENT_FRAME (f)) + return; + + if (TYPE_RANGED_FIXNUMP (int, value)) + nlines = XFIXNUM (value); + else + nlines = 0; + + /* Make sure we redisplay all windows in this frame. */ + fset_redisplay (f); + + FRAME_MENU_BAR_LINES (f) = nlines; + FRAME_MENU_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f); + if (FRAME_ANDROID_WINDOW (f)) + android_clear_under_internal_border (f); + + /* If the menu bar height gets changed, the internal border below + the top margin has to be cleared. Also, if the menu bar gets + larger, the area for the added lines has to be cleared except for + the first menu bar line that is to be drawn later. */ + if (nlines != olines) + { + int height = FRAME_INTERNAL_BORDER_WIDTH (f); + int width = FRAME_PIXEL_WIDTH (f); + int y; + + adjust_frame_size (f, -1, -1, 3, true, Qmenu_bar_lines); + + /* height can be zero here. */ + if (FRAME_ANDROID_WINDOW (f) && height > 0 && width > 0) + { + y = FRAME_TOP_MARGIN_HEIGHT (f); + + block_input (); + android_clear_area (FRAME_ANDROID_DRAWABLE (f), + 0, y, width, height); + unblock_input (); + } + + if (nlines > 1 && nlines > olines) + { + y = (olines == 0 ? 1 : olines) * FRAME_LINE_HEIGHT (f); + height = nlines * FRAME_LINE_HEIGHT (f) - y; + + block_input (); + android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, y, + width, height); + unblock_input (); + } + + if (nlines == 0 && WINDOWP (f->menu_bar_window)) + clear_glyph_matrix (XWINDOW (f->menu_bar_window)->current_matrix); + } + + adjust_frame_glyphs (f); +} + + + +/* These enums must stay in sync with the mouse_cursor_types array + below! */ + +enum mouse_cursor + { + mouse_cursor_text, + mouse_cursor_nontext, + mouse_cursor_hourglass, + mouse_cursor_mode, + mouse_cursor_hand, + mouse_cursor_horizontal_drag, + mouse_cursor_vertical_drag, + mouse_cursor_left_edge, + mouse_cursor_top_left_corner, + mouse_cursor_top_edge, + mouse_cursor_top_right_corner, + mouse_cursor_right_edge, + mouse_cursor_bottom_right_corner, + mouse_cursor_bottom_edge, + mouse_cursor_bottom_left_corner, + mouse_cursor_max + }; + +struct mouse_cursor_types +{ + /* Printable name for error messages (optional). */ + const char *name; + + /* Lisp variable controlling the cursor shape. */ + /* FIXME: A couple of these variables are defined in the C code but + are not actually accessible from Lisp. They should probably be + made accessible or removed. */ + Lisp_Object *shape_var_ptr; + + /* The default shape. */ + int default_shape; +}; + +/* This array must stay in sync with enum mouse_cursor above! */ + +static const struct mouse_cursor_types mouse_cursor_types[] = + { + {"text", &Vx_pointer_shape, ANDROID_XC_XTERM, }, + {"nontext", &Vx_nontext_pointer_shape, ANDROID_XC_LEFT_PTR, }, + {"hourglass", &Vx_hourglass_pointer_shape, ANDROID_XC_WATCH, }, + {"modeline", &Vx_mode_pointer_shape, ANDROID_XC_XTERM, }, + {NULL, &Vx_sensitive_text_pointer_shape, ANDROID_XC_HAND2, }, + {NULL, &Vx_window_horizontal_drag_shape, ANDROID_XC_SB_H_DOUBLE_ARROW, }, + {NULL, &Vx_window_vertical_drag_shape, ANDROID_XC_SB_V_DOUBLE_ARROW, }, + {NULL, &Vx_window_left_edge_shape, ANDROID_XC_LEFT_SIDE, }, + {NULL, &Vx_window_top_left_corner_shape, ANDROID_XC_TOP_LEFT_CORNER, }, + {NULL, &Vx_window_top_edge_shape, ANDROID_XC_TOP_SIDE, }, + {NULL, &Vx_window_top_right_corner_shape, ANDROID_XC_TOP_RIGHT_CORNER, }, + {NULL, &Vx_window_right_edge_shape, ANDROID_XC_RIGHT_SIDE, }, + {NULL, &Vx_window_bottom_right_corner_shape, + ANDROID_XC_BOTTOM_RIGHT_CORNER, }, + {NULL, &Vx_window_bottom_edge_shape, ANDROID_XC_BOTTOM_SIDE, }, + {NULL, &Vx_window_bottom_left_corner_shape, + ANDROID_XC_BOTTOM_LEFT_CORNER, }, + }; + +struct mouse_cursor_data +{ + /* Cursor numbers chosen. */ + unsigned int cursor_num[mouse_cursor_max]; + + /* Allocated Cursor values, or zero for failed attempts. */ + android_cursor cursor[mouse_cursor_max]; +}; + + + +static void +android_set_mouse_color (struct frame *f, Lisp_Object arg, + Lisp_Object oldval) +{ + struct android_output *x = f->output_data.android; + struct mouse_cursor_data cursor_data = { -1, -1 }; + unsigned long pixel = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); + unsigned long mask_color = FRAME_BACKGROUND_PIXEL (f); + int i; + + /* Don't let pointers be invisible. */ + if (mask_color == pixel) + pixel = FRAME_FOREGROUND_PIXEL (f); + + x->mouse_pixel = pixel; + + for (i = 0; i < mouse_cursor_max; i++) + { + Lisp_Object shape_var = *mouse_cursor_types[i].shape_var_ptr; + cursor_data.cursor_num[i] + = (!NILP (shape_var) + ? check_uinteger_max (shape_var, UINT_MAX) + : mouse_cursor_types[i].default_shape); + } + + block_input (); + + for (i = 0; i < mouse_cursor_max; i++) + cursor_data.cursor[i] + = android_create_font_cursor (cursor_data.cursor_num[i]); + + if (FRAME_ANDROID_WINDOW (f)) + { + f->output_data.android->current_cursor + = cursor_data.cursor[mouse_cursor_text]; + android_define_cursor (FRAME_ANDROID_WINDOW (f), + f->output_data.android->current_cursor); + } + +#define INSTALL_CURSOR(FIELD, SHORT_INDEX) \ + eassert (x->FIELD \ + != cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX]); \ + if (x->FIELD != 0) \ + android_free_cursor (x->FIELD); \ + x->FIELD = cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX]; + + INSTALL_CURSOR (text_cursor, text); + INSTALL_CURSOR (nontext_cursor, nontext); + INSTALL_CURSOR (hourglass_cursor, hourglass); + INSTALL_CURSOR (modeline_cursor, mode); + INSTALL_CURSOR (hand_cursor, hand); + INSTALL_CURSOR (horizontal_drag_cursor, horizontal_drag); + INSTALL_CURSOR (vertical_drag_cursor, vertical_drag); + INSTALL_CURSOR (left_edge_cursor, left_edge); + INSTALL_CURSOR (top_left_corner_cursor, top_left_corner); + INSTALL_CURSOR (top_edge_cursor, top_edge); + INSTALL_CURSOR (top_right_corner_cursor, top_right_corner); + INSTALL_CURSOR (right_edge_cursor, right_edge); + INSTALL_CURSOR (bottom_right_corner_cursor, bottom_right_corner); + INSTALL_CURSOR (bottom_edge_cursor, bottom_edge); + INSTALL_CURSOR (bottom_left_corner_cursor, bottom_left_corner); + +#undef INSTALL_CURSOR + + unblock_input (); + + update_face_from_frame_parameter (f, Qmouse_color, arg); +} + +static void +android_set_title (struct frame *f, Lisp_Object name, + Lisp_Object old_name) +{ + /* Don't change the title if it's already NAME. */ + if (EQ (name, f->title)) + return; + + update_mode_lines = 38; + + fset_title (f, name); + + if (NILP (name)) + name = f->name; + else + CHECK_STRING (name); +} + +static void +android_set_alpha (struct frame *f, Lisp_Object arg, Lisp_Object oldval) +{ + double alpha = 1.0; + double newval[2]; + int i; + Lisp_Object item; + + /* N.B. that setting the window alpha is actually unsupported under + Android. */ + + for (i = 0; i < 2; i++) + { + newval[i] = 1.0; + if (CONSP (arg)) + { + item = CAR (arg); + arg = CDR (arg); + } + else + item = arg; + + if (NILP (item)) + alpha = - 1.0; + else if (FLOATP (item)) + { + alpha = XFLOAT_DATA (item); + if (! (0 <= alpha && alpha <= 1.0)) + args_out_of_range (make_float (0.0), make_float (1.0)); + } + else if (FIXNUMP (item)) + { + EMACS_INT ialpha = XFIXNUM (item); + if (! (0 <= ialpha && ialpha <= 100)) + args_out_of_range (make_fixnum (0), make_fixnum (100)); + alpha = ialpha / 100.0; + } + else + wrong_type_argument (Qnumberp, item); + newval[i] = alpha; + } + + for (i = 0; i < 2; i++) + f->alpha[i] = newval[i]; + + if (FRAME_TERMINAL (f)->set_frame_alpha_hook) + { + block_input (); + FRAME_TERMINAL (f)->set_frame_alpha_hook (f); + unblock_input (); + } +} + +static void +android_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, + Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { + android_set_dont_focus_on_map (FRAME_ANDROID_WINDOW (f), + !NILP (new_value)); + FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value); + } +} + +static void +android_set_no_accept_focus (struct frame *f, Lisp_Object new_value, + Lisp_Object old_value) +{ + if (!EQ (new_value, old_value)) + { + android_set_dont_accept_focus (FRAME_ANDROID_WINDOW (f), + !NILP (new_value)); + FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value); + } +} + +frame_parm_handler android_frame_parm_handlers[] = +{ + gui_set_autoraise, + gui_set_autolower, + android_set_background_color, + android_set_border_color, + gui_set_border_width, + android_set_cursor_color, + android_set_cursor_type, + gui_set_font, + android_set_foreground_color, + NULL, + NULL, + android_set_child_frame_border_width, + android_set_internal_border_width, + gui_set_right_divider_width, + gui_set_bottom_divider_width, + android_set_menu_bar_lines, + android_set_mouse_color, + android_explicitly_set_name, + gui_set_scroll_bar_width, + gui_set_scroll_bar_height, + android_set_title, + gui_set_unsplittable, + gui_set_vertical_scroll_bars, + gui_set_horizontal_scroll_bars, + gui_set_visibility, + android_set_tab_bar_lines, + android_set_tool_bar_lines, + NULL, + NULL, + gui_set_screen_gamma, + gui_set_line_spacing, + gui_set_left_fringe, + gui_set_right_fringe, + NULL, + gui_set_fullscreen, + gui_set_font_backend, + android_set_alpha, + NULL, + android_set_tool_bar_position, + NULL, + NULL, + android_set_parent_frame, + NULL, + android_set_no_focus_on_map, + android_set_no_accept_focus, + NULL, + NULL, + gui_set_no_special_glyphs, + NULL, + NULL, +}; + + + +/* Battery information support. */ + +DEFUN ("android-query-battery", Fandroid_query_battery, + Sandroid_query_battery, 0, 0, 0, + doc: /* Perform a query for battery information. +Value is nil upon failure, or a list of the form: + + (CAPACITY CHARGE-COUNTER CURRENT-AVERAGE CURRENT-NOW STATUS + REMAINING PLUGGED TEMP) + +where REMAINING, CURRENT-AVERAGE, and CURRENT-NOW are undefined prior +to Android 5.0. + +See the documentation at + + https://developer.android.com/reference/android/os/BatteryManager + +for more details about these values. */) + (void) +{ + struct android_battery_state state; + + /* Make sure the Android libraries have been initialized. */ + + if (!android_init_gui) + return Qnil; + + /* Perform the query. */ + + if (android_query_battery (&state)) + return Qnil; + + return listn (8, make_int (state.capacity), + make_fixnum (state.charge_counter), + make_int (state.current_average), + make_int (state.current_now), + make_fixnum (state.status), + make_int (state.remaining), + make_fixnum (state.plugged), + make_fixnum (state.temperature)); +} + + + +/* Directory access requests. */ + +DEFUN ("android-request-directory-access", Fandroid_request_directory_access, + Sandroid_request_directory_access, 0, 0, "", + doc: /* Request access to a directory within external storage. +On Android 5.0 and later, prompt for a directory within external or +application storage, and grant access to it; some of these directories +cannot be accessed through the regular `/sdcard' filesystem. + +If access to the directory is granted, it will eventually appear +within the directory `/content/storage'. */) + (void) +{ + if (android_get_current_api_level () < 21) + error ("Emacs can only access application storage on" + " Android 5.0 and later"); + + if (!android_init_gui) + return Qnil; + + android_request_directory_access (); + return Qnil; +} + + + +/* Functions concerning storage permissions. */ + +DEFUN ("android-external-storage-available-p", + Fandroid_external_storage_available_p, + Sandroid_external_storage_available_p, 0, 0, 0, + doc: /* Return non-nil if Emacs is entitled to access external storage. +Return nil if the requisite permissions for external storage access +have not been granted to Emacs, t otherwise. Such permissions can be +requested by means of the `android-request-storage-access' +command. + +External storage on Android encompasses the `/sdcard' and +`/storage/emulated' directories, access to which is denied to programs +absent these permissions. */) + (void) +{ + return android_external_storage_available_p () ? Qt : Qnil; +} + +DEFUN ("android-request-storage-access", Fandroid_request_storage_access, + Sandroid_request_storage_access, 0, 0, "", + doc: /* Request permissions to access external storage. + +Return nil regardless of whether access permissions are granted or not, +immediately after displaying the permissions request dialog. + +Use `android-external-storage-available-p' (which see) to verify +whether Emacs has actually received such access permissions. */) + (void) +{ + android_request_storage_access (); + return Qnil; +} + + + +/* Miscellaneous input method related stuff. */ + +/* Report X, Y, by the phys cursor width and height as the cursor + anchor rectangle for W's frame. */ + +void +android_set_preeditarea (struct window *w, int x, int y) +{ + struct frame *f; + + f = WINDOW_XFRAME (w); + + /* Convert the window coordinates to the frame's coordinate + space. */ + x = (WINDOW_TO_FRAME_PIXEL_X (w, x) + + WINDOW_LEFT_FRINGE_WIDTH (w) + + WINDOW_LEFT_MARGIN_WIDTH (w)); + y = WINDOW_TO_FRAME_PIXEL_Y (w, y); + + /* Note that calculating the baseline is too hard, so the bottom of + the cursor is used instead. */ + android_update_cursor_anchor_info (FRAME_ANDROID_WINDOW (f), x, + y, y + w->phys_cursor_height, + y + w->phys_cursor_height); +} + +#endif /* !ANDROID_STUBIFY */ + + + +#ifndef ANDROID_STUBIFY + +static void +syms_of_androidfns_for_pdumper (void) +{ + jclass locale; + jmethodID method; + jobject object; + jstring string; + Lisp_Object language, country, script, variant; + const char *data; + + /* Find the Locale class. */ + + locale = (*android_java_env)->FindClass (android_java_env, + "java/util/Locale"); + if (!locale) + emacs_abort (); + + /* And the method from which the default locale can be + extracted. */ + + method = (*android_java_env)->GetStaticMethodID (android_java_env, + locale, + "getDefault", + "()Ljava/util/Locale;"); + if (!method) + emacs_abort (); + + /* Retrieve the default locale. */ + + object = (*android_java_env)->CallStaticObjectMethod (android_java_env, + locale, method); + android_exception_check_1 (locale); + + if (!object) + emacs_abort (); + + /* Retrieve its language field. Each of these methods is liable to + return the empty string, though if language is empty, the locale + is malformed. */ + + method = (*android_java_env)->GetMethodID (android_java_env, locale, + "getLanguage", + "()Ljava/lang/String;"); + if (!method) + emacs_abort (); + + string = (*android_java_env)->CallObjectMethod (android_java_env, object, + method); + android_exception_check_2 (object, locale); + + if (!string) + language = empty_unibyte_string; + else + { + data = (*android_java_env)->GetStringUTFChars (android_java_env, + string, NULL); + android_exception_check_3 (object, locale, string); + + if (!data) + language = empty_unibyte_string; + else + { + language = build_unibyte_string (data); + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + string, data); + } + } + + /* Delete the reference to this string. */ + ANDROID_DELETE_LOCAL_REF (string); + + /* Proceed to retrieve the country code. */ + + method = (*android_java_env)->GetMethodID (android_java_env, locale, + "getCountry", + "()Ljava/lang/String;"); + if (!method) + emacs_abort (); + + string = (*android_java_env)->CallObjectMethod (android_java_env, object, + method); + android_exception_check_2 (object, locale); + + if (!string) + country = empty_unibyte_string; + else + { + data = (*android_java_env)->GetStringUTFChars (android_java_env, + string, NULL); + android_exception_check_3 (object, locale, string); + + if (!data) + country = empty_unibyte_string; + else + { + country = build_unibyte_string (data); + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + string, data); + } + } + + ANDROID_DELETE_LOCAL_REF (string); + + /* Proceed to retrieve the script. */ + + if (android_get_current_api_level () < 21) + script = empty_unibyte_string; + else + { + method = (*android_java_env)->GetMethodID (android_java_env, locale, + "getScript", + "()Ljava/lang/String;"); + if (!method) + emacs_abort (); + + string = (*android_java_env)->CallObjectMethod (android_java_env, + object, method); + android_exception_check_2 (object, locale); + + if (!string) + script = empty_unibyte_string; + else + { + data = (*android_java_env)->GetStringUTFChars (android_java_env, + string, NULL); + android_exception_check_3 (object, locale, string); + + if (!data) + script = empty_unibyte_string; + else + { + script = build_unibyte_string (data); + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + string, data); + } + } + } + + ANDROID_DELETE_LOCAL_REF (string); + + /* And variant. */ + + method = (*android_java_env)->GetMethodID (android_java_env, locale, + "getVariant", + "()Ljava/lang/String;"); + if (!method) + emacs_abort (); + + string = (*android_java_env)->CallObjectMethod (android_java_env, object, + method); + android_exception_check_2 (object, locale); + + if (!string) + variant = empty_unibyte_string; + else + { + data = (*android_java_env)->GetStringUTFChars (android_java_env, + string, NULL); + android_exception_check_3 (object, locale, string); + + if (!data) + variant = empty_unibyte_string; + else + { + variant = build_unibyte_string (data); + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + string, data); + } + } + + /* Delete the reference to this string. */ + ANDROID_DELETE_LOCAL_REF (string); + + /* And other remaining local references. */ + ANDROID_DELETE_LOCAL_REF (object); + ANDROID_DELETE_LOCAL_REF (locale); + + /* Set Vandroid_os_language. */ + Vandroid_os_language = list4 (language, country, script, variant); +} + +#endif /* ANDROID_STUBIFY */ + +void +syms_of_androidfns (void) +{ + /* Miscellaneous symbols used by some functions here. */ + DEFSYM (Qtrue_color, "true-color"); + DEFSYM (Qwhen_mapped, "when-mapped"); + + DEFVAR_LISP ("x-pointer-shape", Vx_pointer_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_pointer_shape = Qnil; + +#if false /* This doesn't really do anything. */ + DEFVAR_LISP ("x-nontext-pointer-shape", Vx_nontext_pointer_shape, + doc: /* SKIP: real doc in xfns.c. */); +#endif + Vx_nontext_pointer_shape = Qnil; + + DEFVAR_LISP ("x-hourglass-pointer-shape", Vx_hourglass_pointer_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_hourglass_pointer_shape = Qnil; + + DEFVAR_LISP ("x-sensitive-text-pointer-shape", + Vx_sensitive_text_pointer_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_sensitive_text_pointer_shape = Qnil; + + DEFVAR_LISP ("x-window-horizontal-drag-cursor", + Vx_window_horizontal_drag_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_horizontal_drag_shape = Qnil; + + DEFVAR_LISP ("x-window-vertical-drag-cursor", + Vx_window_vertical_drag_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_vertical_drag_shape = Qnil; + + DEFVAR_LISP ("x-window-left-edge-cursor", + Vx_window_left_edge_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_left_edge_shape = Qnil; + + DEFVAR_LISP ("x-window-top-left-corner-cursor", + Vx_window_top_left_corner_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_top_left_corner_shape = Qnil; + + DEFVAR_LISP ("x-window-top-edge-cursor", + Vx_window_top_edge_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_top_edge_shape = Qnil; + + DEFVAR_LISP ("x-window-top-right-corner-cursor", + Vx_window_top_right_corner_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_top_right_corner_shape = Qnil; + + DEFVAR_LISP ("x-window-right-edge-cursor", + Vx_window_right_edge_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_right_edge_shape = Qnil; + + DEFVAR_LISP ("x-window-bottom-right-corner-cursor", + Vx_window_bottom_right_corner_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_bottom_right_corner_shape = Qnil; + + DEFVAR_LISP ("x-window-bottom-edge-cursor", + Vx_window_bottom_edge_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_bottom_edge_shape = Qnil; + +#if false /* This doesn't really do anything. */ + DEFVAR_LISP ("x-mode-pointer-shape", Vx_mode_pointer_shape, + doc: /* SKIP: real doc in xfns.c. */); +#endif + Vx_mode_pointer_shape = Qnil; + + DEFVAR_LISP ("x-window-bottom-left-corner-cursor", + Vx_window_bottom_left_corner_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_bottom_left_corner_shape = Qnil; + + DEFVAR_LISP ("x-cursor-fore-pixel", Vx_cursor_fore_pixel, + doc: /* SKIP: real doc in xfns.c. */); + Vx_cursor_fore_pixel = Qnil; + + /* Used by Fx_show_tip. */ + DEFSYM (Qrun_at_time, "run-at-time"); + DEFSYM (Qx_hide_tip, "x-hide-tip"); + DEFSYM (Qcancel_timer, "cancel-timer"); + DEFSYM (Qassq_delete_all, "assq-delete-all"); + DEFSYM (Qcolor, "color"); + + DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size, + doc: /* SKIP: real doc in xfns.c. */); + Vx_max_tooltip_size = Qnil; + + DEFVAR_BOOL ("android-pass-multimedia-buttons-to-system", + android_pass_multimedia_buttons_to_system, + doc: /* Whether or not to pass volume control buttons to the system. +Generally, the `volume-up', `volume-down' and `volume-mute' keys are +processed by Emacs, but setting this to non-nil they are passed to the +operating system instead of being intercepted by Emacs. + +Note that if you set this, you will no longer be able to quit Emacs +using the volume down button. */); + android_pass_multimedia_buttons_to_system = false; + + DEFVAR_BOOL ("android-intercept-control-space", + android_intercept_control_space, + doc: /* Whether Emacs should intercept C-SPC. +When this variable is set, Emacs intercepts C-SPC events as they are +delivered to a frame before they are registered and filtered by the +input method. + +For no apparent purpose, Android input methods customarily discard SPC +events with the Ctrl modifier set without delivering them to Emacs +afterwards, which is an impediment to typing key sequences +incorporating such keys. */); + android_intercept_control_space = true; + + DEFVAR_BOOL ("android-use-exec-loader", android_use_exec_loader, + doc: /* Whether or not to bypass system restrictions on program execution. + +Android 10 and later prevent programs from executing files installed +in writable directories, such as the application data directory. + +When non-nil, Emacs will bypass this restriction by running such +executables under system call tracing, and replacing the `execve' +system call with a version which ignores the system's security +restrictions. + +This option has no effect on Android 9 and earlier. */); + android_use_exec_loader = true; + + DEFVAR_INT ("android-keyboard-bell-duration", + android_keyboard_bell_duration, + doc: /* Number of milliseconds to vibrate after ringing the keyboard bell. +The keyboard bell under Android systems takes the form of a vibrating +element that is activated for a given number of milliseconds upon the +bell being rung. */); + android_keyboard_bell_duration = 50; + + DEFVAR_LISP ("android-os-language", Vandroid_os_language, + doc: /* A list representing the configured system language on Android. +This list has four elements: LANGUAGE, COUNTRY, SCRIPT and VARIANT, where: + +LANGUAGE and COUNTRY are ISO language and country codes identical to +those found in POSIX locale specifications. + +SCRIPT is an ISO 15924 script tag, representing the script used +if available, or if required to disambiguate between distinct +writing systems for the same combination of language and country. + +VARIANT is an arbitrary string representing the variant of the +LANGUAGE or SCRIPT. + +Each of these fields might be empty or nil, but the locale is invalid +if LANGUAGE is empty. Users of this variable should consider the +language to be US English if LANGUAGE is empty. */); + Vandroid_os_language = Qnil; + + /* Functions defined. */ + defsubr (&Sx_create_frame); + defsubr (&Sxw_color_defined_p); + defsubr (&Sxw_color_values); + defsubr (&Sxw_display_color_p); + defsubr (&Sx_display_grayscale_p); + defsubr (&Sx_display_pixel_width); + defsubr (&Sx_display_pixel_height); + defsubr (&Sx_display_planes); + defsubr (&Sx_display_color_cells); + defsubr (&Sx_display_screens); + defsubr (&Sx_display_mm_width); + defsubr (&Sx_display_mm_height); + defsubr (&Sx_display_backing_store); + defsubr (&Sx_display_visual_class); + defsubr (&Sandroid_display_monitor_attributes_list); + defsubr (&Sandroid_frame_geometry); + defsubr (&Sandroid_frame_edges); + defsubr (&Sandroid_frame_list_z_order); + defsubr (&Sandroid_frame_restack); + defsubr (&Sandroid_mouse_absolute_pixel_position); + defsubr (&Sandroid_set_mouse_absolute_pixel_position); + defsubr (&Sandroid_get_connection); + defsubr (&Sx_display_list); + defsubr (&Sx_show_tip); + defsubr (&Sx_hide_tip); + defsubr (&Sandroid_detect_mouse); + defsubr (&Sandroid_toggle_on_screen_keyboard); + defsubr (&Sx_server_vendor); + defsubr (&Sx_server_version); +#ifndef ANDROID_STUBIFY + defsubr (&Sandroid_query_battery); + defsubr (&Sandroid_request_directory_access); + defsubr (&Sandroid_external_storage_available_p); + defsubr (&Sandroid_request_storage_access); + + tip_timer = Qnil; + staticpro (&tip_timer); + tip_frame = Qnil; + staticpro (&tip_frame); + tip_last_frame = Qnil; + staticpro (&tip_last_frame); + tip_last_string = Qnil; + staticpro (&tip_last_string); + tip_last_parms = Qnil; + staticpro (&tip_last_parms); + tip_dx = Qnil; + staticpro (&tip_dx); + tip_dy = Qnil; + staticpro (&tip_dy); + + pdumper_do_now_and_after_load (syms_of_androidfns_for_pdumper); +#endif /* !ANDROID_STUBIFY */ +} |