/* Communication module for Android terminals.

Copyright (C) 2023-2025 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 <stdlib.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

/* 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);
}

/* Set the WM name to NAME for frame F. Also set the icon name.
   If the frame already has an icon name, use that, otherwise set the
   icon name to NAME.  */

static void
android_set_name_internal (struct frame *f, Lisp_Object name)
{
  jstring java_name;

  if (FRAME_ANDROID_WINDOW (f))
    {
      java_name = android_build_string (name, NULL);
      android_set_wm_name (FRAME_ANDROID_WINDOW (f), java_name);
      ANDROID_DELETE_LOCAL_REF (java_name);
    }
}

/* Change the name of frame F to NAME.  If NAME is nil, set F's name to
       x_id_name.

   If EXPLICIT is true, that indicates that lisp code is setting the
       name; if NAME is a string, set F's name to NAME and set
       F->explicit_name; if NAME is Qnil, then clear F->explicit_name.

   If EXPLICIT is false, that indicates that Emacs redisplay code is
       suggesting a new name, which lisp code should override; if
       F->explicit_name is set, ignore the new name; otherwise, set it.  */

static void
android_set_name (struct frame *f, Lisp_Object name, bool explicit)
{
  /* Make sure that requests from lisp code override requests from
     Emacs redisplay code.  */
  if (explicit)
    {
      /* If we're switching from explicit to implicit, we had better
	 update the mode lines and thereby update the title.  */
      if (f->explicit_name && NILP (name))
	update_mode_lines = 37;

      f->explicit_name = ! NILP (name);
    }
  else if (f->explicit_name)
    return;

  /* If NAME is nil, set the name to the x_id_name.  */
  if (NILP (name))
    {
      /* Check for no change needed in this very common case
	 before we do any consing.  */
      if (!strcmp (FRAME_DISPLAY_INFO (f)->x_id_name,
		   SSDATA (f->name)))
	return;
      name = build_string (FRAME_DISPLAY_INFO (f)->x_id_name);
    }
  else
    CHECK_STRING (name);

  /* Don't change the name if it's already NAME.  */
  if (! NILP (Fstring_equal (name, f->name)))
    return;

  fset_name (f, name);

  /* For setting the frame title, the title parameter should override
     the name parameter.  */
  if (! NILP (f->title))
    name = f->title;

  android_set_name_internal (f, name);
}

void
android_implicitly_set_name (struct frame *f, Lisp_Object arg,
			     Lisp_Object oldval)
{
  android_set_name (f, arg, false);
}

void
android_explicitly_set_name (struct frame *f, Lisp_Object arg,
			     Lisp_Object oldval)
{
  android_set_name (f, arg, true);
}

/* 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)))
    {
      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);

  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)
{
  struct android_display_info *dpyinfo;

  dpyinfo = check_android_display_info (terminal);
  return dpyinfo->n_planes > 8 ? Qt : Qnil;
}

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)
{
  struct android_display_info *dpyinfo;

  dpyinfo = check_android_display_info (terminal);
  return (dpyinfo->n_planes > 1 && dpyinfo->n_planes <= 8
	  ? Qt : 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
  check_android_display_info (terminal);
  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
  check_android_display_info (terminal);
  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)
{
  struct android_display_info *dpyinfo;

  dpyinfo = check_android_display_info (terminal);

  if (dpyinfo->n_planes < 24)
    return Qstatic_gray;

  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;

  check_android_display_info (terminal);
  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's 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);

      /* Update the display's bit depth from
	 `android_display_planes'.  */
      x_display_list->n_planes
	= (android_display_planes > 8
	   ? 24 : (android_display_planes > 1
		   ? android_display_planes : 1));
    }

  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);

  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 if (tooltip_reuse_hidden_frame && BASE_EQ (frame, tip_last_frame))
	{
	  bool delete = false;
	  Lisp_Object tail, elt, parm, last;

	  /* Check if every parameter in PARMS has the same value in
	     tip_last_parms.  This may destruct tip_last_parms which,
	     however, will be recreated below.  */
	  for (tail = parms; CONSP (tail); tail = XCDR (tail))
	    {
	      elt = XCAR (tail);
	      parm = CAR (elt);
	      /* The left, top, right and bottom parameters are handled
		 by compute_tip_xy so they can be ignored here.  */
	      if (!EQ (parm, Qleft) && !EQ (parm, Qtop)
		  && !EQ (parm, Qright) && !EQ (parm, Qbottom))
		{
		  last = Fassq (parm, tip_last_parms);
		  if (NILP (Fequal (CDR (elt), CDR (last))))
		    {
		      /* We lost, delete the old tooltip.  */
		      delete = true;
		      break;
		    }
		  else
		    tip_last_parms
		      = call2 (Qassq_delete_all, parm, tip_last_parms);
		}
	      else
		tip_last_parms
		  = call2 (Qassq_delete_all, parm, tip_last_parms);
	    }

	  /* Now check if every parameter in what is left of
	     tip_last_parms with a non-nil value has an association in
	     PARMS.  */
	  for (tail = tip_last_parms; CONSP (tail); tail = XCDR (tail))
	    {
	      elt = XCAR (tail);
	      parm = CAR (elt);
	      if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright)
		  && !EQ (parm, Qbottom) && !NILP (CDR (elt)))
		{
		  /* We lost, delete the old tooltip.  */
		  delete = true;
		  break;
		}
	    }

	  android_hide_tip (delete);
	}
      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);

  /* Block input around `update_single_window' and `flush_frame', lest a
     ConfigureNotify and Expose event arrive during the update, and set
     flags, e.g. garbaged_p, that are cleared once the update completes,
     leaving the requested exposure or configuration outstanding.  */
  block_input ();
  w->must_be_updated_p = true;
  update_single_window (w);
  flush_frame (tip_f);
  unblock_input ();

  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 (!tooltip_reuse_hidden_frame);
#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-detect-keyboard", Fandroid_detect_keyboard,
       Sandroid_detect_keyboard, 0, 0, 0,
       doc: /* Return whether a keyboard is connected.
Return non-nil if a key is connected to this computer, or nil
if there is no keyboard.  */)
  (void)
{
#ifndef ANDROID_STUBIFY
  /* If no display connection is present, just return nil.  */

  if (!android_init_gui)
    return Qnil;

  return android_detect_keyboard () ? Qt : Qnil;
#else /* ANDROID_STUBIFY */
  return Qt;
#endif /* ANDROID_STUBIFY */
}

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);

  android_set_name_internal (f, 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));
}



/* SAF directory access management.  */

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)
{
  /* Implement a rather undependable fallback when no GUI is
     available.  */
  if (!android_init_gui)
    return Ffile_accessible_directory_p (build_string ("/sdcard"));

  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)
{
  if (!android_init_gui)
    return Qnil;

  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);
}



/* Debugging.  */

DEFUN ("android-recreate-activity", Fandroid_recreate_activity,
       Sandroid_recreate_activity, 0, 0, "",
       doc: /* Recreate the activity attached to the current frame.
This function exists for debugging purposes and is of no interest to
users.  */)
  (void)
{
  struct frame *f;

  f = decode_window_system_frame (Qnil);
  android_recreate_activity (FRAME_ANDROID_WINDOW (f));
  return Qnil;
}

#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;
  FILE *fd;
  char *line;
  size_t size;
  long pid;

  /* 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);

  /* Detect whether Emacs is running under libloader.so or another
     process tracing mechanism, and disable `android_use_exec_loader' if
     so, leaving subprocesses started by Emacs to the care of that
     loader instance.  */

  if (android_get_current_api_level () >= 29) /* Q */
    {
      fd = fopen ("/proc/self/status", "r");
      if (!fd)
	return;

      line = NULL;
      while (getline (&line, &size, fd) != -1)
	{
	  if (strncmp (line, "TracerPid:", sizeof "TracerPid:" - 1))
	    continue;

	  pid = atol (line + sizeof "TracerPid:" - 1);

	  if (pid)
	    android_use_exec_loader = false;

	  break;
	}

      free (line);
      fclose (fd);
    }
}

#endif /* ANDROID_STUBIFY */

void
syms_of_androidfns (void)
{
  /* Miscellaneous symbols used by some functions here.  */
  DEFSYM (Qtrue_color, "true-color");
  DEFSYM (Qstatic_gray, "static-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_detect_keyboard);
  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);
  defsubr (&Sandroid_recreate_activity);

  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 */
}
