* java/Makefile.in (clean): Fix distclean and bootstrap-clean rules.
* java/debug.sh (jdb_port):
(attach_existing):
(num_pids):
(line): Add new options to upload a gdbserver binary to the device.
* java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): Make
focusedActivities public.
* java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
New class.
* java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Fix
bounds computation.
* java/org/gnu/emacs/EmacsGC.java (markDirty): Set stroke width
explicitly.
* java/org/gnu/emacs/EmacsService.java (EmacsService)
(getLocationOnScreen, nameKeysym): New functions.
* java/org/gnu/emacs/EmacsView.java (EmacsView): Disable focus
highlight.
(onCreateContextMenu, popupMenu, cancelPopupMenu): New
functions.
* java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): Implement a
kind of ``override redirect'' window for tooltips.
* src/android.c (struct android_emacs_service): New method
`name_keysym'.
(android_run_select_thread, android_init_events):
(android_select): Release select thread on semaphores instead of
signals to avoid one nasty race on SIGUSR2 delivery.
(android_init_emacs_service): Initialize new method.
(android_create_window): Handle CW_OVERRIDE_REDIRECT.
(android_move_resize_window, android_map_raised)
(android_translate_coordinates, android_get_keysym_name)
(android_build_string, android_exception_check): New functions.
* src/android.h: Update prototypes.
* src/androidfns.c (android_set_parent_frame, Fx_create_frame)
(unwind_create_tip_frame, android_create_tip_frame)
(android_hide_tip, compute_tip_xy, Fx_show_tip, Fx_hide_tip)
(syms_of_androidfns): Implement tooltips and iconification
reporting.
* src/androidgui.h (enum android_window_value_mask): Add
CWOverrideRedirect.
(struct android_set_window_attributes): Add `override_redirect'.
(ANDROID_IS_MODIFIER_KEY): Recognize Caps Lock.
* src/androidmenu.c (struct android_emacs_context_menu): New
struct.
(android_init_emacs_context_menu, android_unwind_local_frame)
(android_push_local_frame, android_menu_show, init_androidmenu):
New functions.
* src/androidterm.c (handle_one_android_event): Fix NULL pointer
dereference.
(android_fullscreen_hook): Handle fullscreen correctly.
(android_draw_box_rect): Fix top line.
(get_keysym_name): Implement function.
(android_create_terminal): Remove scroll bar stubs and add menu
hook.
* src/androidterm.h: Update prototypes.
* src/emacs.c (android_emacs_init): Initialize androidmenu.c.
* xcompile/Makefile.in: Fix clean rules.
rm -rf install-temp
find . -name '*.class' -delete
-maintainer-clean: clean
+maintainer-clean distclean bootstrap-clean: clean
jdb_port=64013
jdb=no
attach_existing=no
+gdbserver=
while [ $# -gt 0 ]; do
case "$1" in
echo "You must specify an argument to --device"
exit 1
fi
+ shift
;;
"--help" )
echo "Usage: $progname [options] -- [gdb options]"
echo " --jdb-port PORT run the JDB server on a specific port"
echo " --jdb run JDB instead of GDB"
echo " --attach-existing attach to an existing process"
+ echo " --gdbserver BINARY upload and use the specified gdbserver binary"
echo " --help print this message"
echo ""
echo "Available devices:"
"--jdb" )
jdb=yes
;;
+ "--gdbserver" )
+ shift
+ gdbserver=$1
+ ;;
"--port" )
+ shift
gdb_port=$1
;;
+ "--jdb-port" )
+ shift
+ jdb_port=$1
+ ;;
"--attach-existing" )
attach_existing=yes
;;
exit 1
fi
-# Start JDB to make the wait dialog disappear.
-echo "Attaching JDB to unblock the application."
-adb -s $device forward --remove-all
-adb -s $device forward "tcp:$jdb_port" "jdwp:$pid"
+# This isn't necessary when attaching gdb to an existing process.
+if [ "$jdb" = "yes" ] || [ "$attach_existing" != yes ]; then
+ # Start JDB to make the wait dialog disappear.
+ echo "Attaching JDB to unblock the application."
+ adb -s $device forward --remove-all
+ adb -s $device forward "tcp:$jdb_port" "jdwp:$pid"
-if [ ! $? ]; then
- echo "Failed to forward jdwp:$pid to $jdb_port!"
- echo "Perhaps you need to specify a different port with --port?"
- exit 1;
-fi
+ if [ ! $? ]; then
+ echo "Failed to forward jdwp:$pid to $jdb_port!"
+ echo "Perhaps you need to specify a different port with --port?"
+ exit 1;
+ fi
-jdb_command="jdb -connect \
+ jdb_command="jdb -connect \
com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port"
-if [ $jdb = "yes" ]; then
- # Just start JDB and then exit
- $jdb_command
- exit 1
-fi
+ if [ $jdb = "yes" ]; then
+ # Just start JDB and then exit
+ $jdb_command
+ exit 1
+ fi
-exec 4<> /tmp/file-descriptor-stamp
+ exec 4<> /tmp/file-descriptor-stamp
-# Now run JDB with IO redirected to file descriptor 4 in a subprocess.
-$jdb_command <&4 >&4 &
+ # Now run JDB with IO redirected to file descriptor 4 in a subprocess.
+ $jdb_command <&4 >&4 &
-character=
-# Next, wait until the prompt is found.
-while read -n1 -u 4 character; do
- if [ "$character" = ">" ]; then
- echo "JDB attached successfully"
- break;
- fi
-done
+ character=
+ # Next, wait until the prompt is found.
+ while read -n1 -u 4 character; do
+ if [ "$character" = ">" ]; then
+ echo "JDB attached successfully"
+ break;
+ fi
+ done
+fi
+
+# See if gdbserver has to be uploaded
+if [ -z "$gdbserver" ]; then
+ gdbserver_bin=/system/bin/gdbserver
+else
+ gdbserver_bin=/data/local/tmp/gdbserver
+
+ # Upload the specified gdbserver binary to the device.
+ adb -s $device push "$gdbserver" "$gdbserver_bin"
+ adb -s $device shell chmod +x "$gdbserver_bin"
+fi
# Now start gdbserver on the device asynchronously.
echo "Attaching gdbserver to $pid on $device..."
exec 5<> /tmp/file-descriptor-stamp
-adb -s $device shell run-as $package /system/bin/gdbserver --once \
- "+debug.$package_uid.socket" --attach $pid >&5 &
+
+if [ -z "$gdbserver" ]; then
+ adb -s $device shell run-as $package $gdbserver_bin --once \
+ "+debug.$package_uid.socket" --attach $pid >&5 &
+ gdb_socket="localfilesystem:$app_data_dir/debug.$package_uid.socket"
+else
+ # Normally the program cannot access $gdbserver_bin when it is
+ # placed in /data/local/tmp.
+ adb -s $device shell $gdbserver_bin --once \
+ "+/data/local/tmp/debug.$package_uid.socket" \
+ --attach $pid >&5 &
+ gdb_socket="localfilesystem:/data/local/tmp/debug.$package_uid.socket"
+fi
# Wait until gdbserver successfully runs.
line=
esac
done
-# Send EOF to JDB to make it go away. This will also cause Android to
-# allow Emacs to continue executing.
-echo "Making JDB go away..."
-echo "exit" >&4
-read -u 4 line
-echo "JDB has gone away with $line"
+if [ "$attach_existing" != "yes" ]; then
+ # Send EOF to JDB to make it go away. This will also cause
+ # Android to allow Emacs to continue executing.
+ echo "Making JDB go away..."
+ echo "exit" >&4
+ read -u 4 line
+ echo "JDB has gone away with $line"
+fi
# Forward the gdb server port here.
-adb -s $device forward "tcp:$gdb_port" \
- "localfilesystem:$app_data_dir/debug.$package_uid.socket"
+adb -s $device forward "tcp:$gdb_port" $gdb_socket
if [ ! $? ]; then
echo "Failed to forward $app_data_dir/debug.$package_uid.socket"
echo "to $gdb_port! Perhaps you need to specify a different port"
private FrameLayout layout;
/* List of activities with focus. */
- private static List<EmacsActivity> focusedActivities;
+ public static List<EmacsActivity> focusedActivities;
/* The currently focused window. */
public static EmacsWindow focusedWindow;
--- /dev/null
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+package org.gnu.emacs;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.content.Intent;
+
+import android.os.Bundle;
+
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+import android.widget.PopupMenu;
+
+/* Context menu implementation. This object is built from JNI and
+ describes a menu hiearchy. Then, `inflate' can turn it into an
+ Android menu, which can be turned into a popup (or other kind of)
+ menu. */
+
+public class EmacsContextMenu
+{
+ private class Item
+ {
+ public int itemID;
+ public String itemName;
+ public EmacsContextMenu subMenu;
+ public boolean isEnabled;
+ };
+
+ public List<Item> menuItems;
+ public String title;
+ private EmacsContextMenu parent;
+
+ /* Create a context menu with no items inside and the title TITLE,
+ which may be NULL. */
+
+ public static EmacsContextMenu
+ createContextMenu (String title)
+ {
+ EmacsContextMenu menu;
+
+ menu = new EmacsContextMenu ();
+ menu.menuItems = new ArrayList<Item> ();
+ menu.title = title;
+
+ return menu;
+ }
+
+ /* Add a normal menu item to the context menu with the id ITEMID and
+ the name ITEMNAME. Enable it if ISENABLED, else keep it
+ disabled. */
+
+ public void
+ addItem (int itemID, String itemName, boolean isEnabled)
+ {
+ Item item;
+
+ item = new Item ();
+ item.itemID = itemID;
+ item.itemName = itemName;
+ item.isEnabled = isEnabled;
+
+ menuItems.add (item);
+ }
+
+ /* Create a disabled menu item with the name ITEMNAME. */
+
+ public void
+ addPane (String itemName)
+ {
+ Item item;
+
+ item = new Item ();
+ item.itemName = itemName;
+
+ menuItems.add (item);
+ }
+
+ /* Add a submenu to the context menu with the specified title and
+ item name. */
+
+ public EmacsContextMenu
+ addSubmenu (String itemName, String title)
+ {
+ EmacsContextMenu submenu;
+ Item item;
+
+ item = new Item ();
+ item.itemID = 0;
+ item.itemName = itemName;
+ item.subMenu = createContextMenu (title);
+ item.subMenu.parent = this;
+
+ menuItems.add (item);
+ return item.subMenu;
+ }
+
+ /* Add the contents of this menu to MENU. */
+
+ private void
+ inflateMenuItems (Menu menu)
+ {
+ Intent intent;
+ MenuItem menuItem;
+ Menu submenu;
+
+ for (Item item : menuItems)
+ {
+ if (item.subMenu != null)
+ {
+ /* This is a submenu. Create the submenu and add the
+ contents of the menu to it. */
+ submenu = menu.addSubMenu (item.itemName);
+ inflateMenuItems (submenu);
+ }
+ else
+ {
+ menuItem = menu.add (item.itemName);
+
+ /* If the item ID is zero, then disable the item. */
+ if (item.itemID == 0 || !item.isEnabled)
+ menuItem.setEnabled (false);
+ }
+ }
+ }
+
+ /* Enter the items in this context menu to MENU. Create each menu
+ item with an Intent containing a Bundle, where the key
+ "emacs:menu_item_hi" maps to the high 16 bits of the
+ corresponding item ID, and the key "emacs:menu_item_low" maps to
+ the low 16 bits of the item ID. */
+
+ public void
+ expandTo (Menu menu)
+ {
+ inflateMenuItems (menu);
+ }
+
+ /* Return the parent or NULL. */
+
+ public EmacsContextMenu
+ parent ()
+ {
+ return parent;
+ }
+
+ /* Like display, but does the actual work and runs in the main
+ thread. */
+
+ private boolean
+ display1 (EmacsWindow window, int xPosition, int yPosition)
+ {
+ return window.view.popupMenu (this, xPosition, yPosition);
+ }
+
+ /* Display this context menu on WINDOW, at xPosition and
+ yPosition. */
+
+ public boolean
+ display (final EmacsWindow window, final int xPosition,
+ final int yPosition)
+ {
+ Runnable runnable;
+ final Holder<Boolean> rc;
+
+ rc = new Holder<Boolean> ();
+
+ runnable = new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ synchronized (this)
+ {
+ rc.thing = display1 (window, xPosition, yPosition);
+ notify ();
+ }
+ }
+ };
+
+ try
+ {
+ runnable.wait ();
+ }
+ catch (InterruptedException e)
+ {
+ EmacsNative.emacsAbort ();
+ }
+
+ return rc.thing;
+ }
+};
}
paint = gc.gcPaint;
- rect = new Rect (x, y, x + width, y + height);
+ rect = new Rect (x + 1, y + 1, x + width, y + height);
paint.setStyle (Paint.Style.STROKE);
else
real_clip_rects = clip_rects;
+ gcPaint.setStrokeWidth (1f);
gcPaint.setColor (foreground | 0xff000000);
gcPaint.setXfermode (function == GC_XOR
? xorAlu : srcInAlu);
import android.view.View;
import android.view.InputDevice;
+import android.view.KeyEvent;
import android.annotation.TargetApi;
import android.app.Service;
/* Functions from here on must only be called from the Emacs
thread. */
- void
+ public void
runOnUiThread (Runnable runnable)
{
handler.post (runnable);
}
- EmacsView
+ public EmacsView
getEmacsView (final EmacsWindow window, final int visibility,
final boolean isFocusedByDefault)
{
return view.thing;
}
+ public void
+ getLocationOnScreen (final EmacsView view, final int[] coordinates)
+ {
+ Runnable runnable;
+
+ runnable = new Runnable () {
+ public void
+ run ()
+ {
+ synchronized (this)
+ {
+ view.getLocationOnScreen (coordinates);
+ notify ();
+ }
+ }
+ };
+
+ synchronized (runnable)
+ {
+ runOnUiThread (runnable);
+
+ try
+ {
+ runnable.wait ();
+ }
+ catch (InterruptedException e)
+ {
+ EmacsNative.emacsAbort ();
+ }
+ }
+ }
+
public void
fillRectangle (EmacsDrawable drawable, EmacsGC gc,
int x, int y, int width, int height)
return false;
}
+
+ public String
+ nameKeysym (int keysym)
+ {
+ return KeyEvent.keyCodeToString (keysym);
+ }
};
import android.content.res.ColorStateList;
+import android.view.ContextMenu;
import android.view.View;
import android.view.KeyEvent;
import android.view.MotionEvent;
next call to getBitmap. */
private Rect bitmapDirty;
+ /* Whether or not a popup is active. */
+ private boolean popupActive;
+
+ /* The current context menu. */
+ private EmacsContextMenu contextMenu;
+
public
EmacsView (EmacsWindow window)
{
/* Get rid of the foreground and background tint. */
setBackgroundTintList (null);
setForegroundTintList (null);
+
+ /* Get rid of the default focus highlight. */
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
+ setDefaultFocusHighlightEnabled (false);
}
private void
removeView (surfaceView);
addView (surfaceView, 0);
}
+
+ @Override
+ protected void
+ onCreateContextMenu (ContextMenu menu)
+ {
+ if (contextMenu == null)
+ return;
+
+ contextMenu.expandTo (menu);
+ }
+
+ public boolean
+ popupMenu (EmacsContextMenu menu, int xPosition,
+ int yPosition)
+ {
+ if (popupActive)
+ return false;
+
+ contextMenu = menu;
+
+ /* On API 21 or later, use showContextMenu (float, float). */
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP)
+ return showContextMenu ((float) xPosition, (float) yPosition);
+ else
+ return showContextMenu ();
+ }
+
+ public void
+ cancelPopupMenu ()
+ {
+ if (!popupActive)
+ throw new IllegalStateException ("cancelPopupMenu called without"
+ + " popupActive set");
+
+ contextMenu = null;
+ }
};
import java.util.List;
import java.util.HashMap;
+import android.content.Context;
+
import android.graphics.Rect;
import android.graphics.Canvas;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.graphics.PixelFormat;
import android.view.View;
+import android.view.ViewManager;
import android.view.ViewGroup;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.InputDevice;
+import android.view.WindowManager;
import android.content.Intent;
import android.util.Log;
not the window should be focusable. */
private boolean dontFocusOnMap, dontAcceptFocus;
+ /* Whether or not the window is override-redirect. An
+ override-redirect window always has its own system window. */
+ private boolean overrideRedirect;
+
+ /* The window manager that is the parent of this window. NULL if
+ there is no such window manager. */
+ private WindowManager windowManager;
+
public
EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
- int width, int height)
+ int width, int height, boolean overrideRedirect)
{
super (handle);
view = EmacsService.SERVICE.getEmacsView (this, View.GONE,
parent == null);
this.parent = parent;
+ this.overrideRedirect = overrideRedirect;
/* Create the list of children. */
children = new ArrayList<EmacsWindow> ();
public void
run ()
{
- View parent;
+ ViewManager parent;
EmacsWindowAttachmentManager manager;
if (EmacsActivity.focusedWindow == EmacsWindow.this)
manager = EmacsWindowAttachmentManager.MANAGER;
view.setVisibility (View.GONE);
- parent = (View) view.getParent ();
+ /* If the window manager is set, use that instead. */
+ if (windowManager != null)
+ parent = windowManager;
+ else
+ parent = (ViewManager) view.getParent ();
+ windowManager = null;
if (parent != null)
- ((ViewGroup) parent).removeView (view);
+ parent.removeView (view);
manager.detachWindow (EmacsWindow.this);
}
public void
run ()
{
+ if (overrideRedirect)
+ /* Set the layout parameters again. */
+ view.setLayoutParams (getWindowLayoutParams ());
+
view.mustReportLayout = true;
view.requestLayout ();
}
}
}
+ private WindowManager.LayoutParams
+ getWindowLayoutParams ()
+ {
+ WindowManager.LayoutParams params;
+ int flags, type;
+ Rect rect;
+
+ flags = 0;
+ rect = getGeometry ();
+ flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+
+ params
+ = new WindowManager.LayoutParams (rect.width (), rect.height (),
+ rect.left, rect.top,
+ type, flags,
+ PixelFormat.RGBA_8888);
+ params.gravity = Gravity.TOP | Gravity.LEFT;
+ return params;
+ }
+
+ private Context
+ findSuitableActivityContext ()
+ {
+ /* Find a recently focused activity. */
+ if (!EmacsActivity.focusedActivities.isEmpty ())
+ return EmacsActivity.focusedActivities.get (0);
+
+ /* Return the service context, which probably won't work. */
+ return EmacsService.SERVICE;
+ }
+
public void
mapWindow ()
{
run ()
{
EmacsWindowAttachmentManager manager;
+ WindowManager windowManager;
+ Context ctx;
+ Object tem;
+ WindowManager.LayoutParams params;
/* Make the view visible, first of all. */
view.setVisibility (View.VISIBLE);
- manager = EmacsWindowAttachmentManager.MANAGER;
-
- /* If parent is the root window, notice that there are new
- children available for interested activites to pick
- up. */
- manager.registerWindow (EmacsWindow.this);
-
- if (!getDontFocusOnMap ())
- /* Eventually this should check no-focus-on-map. */
- view.requestFocus ();
+ if (!overrideRedirect)
+ {
+ manager = EmacsWindowAttachmentManager.MANAGER;
+
+ /* If parent is the root window, notice that there are new
+ children available for interested activites to pick
+ up. */
+ manager.registerWindow (EmacsWindow.this);
+
+ if (!getDontFocusOnMap ())
+ /* Eventually this should check no-focus-on-map. */
+ view.requestFocus ();
+ }
+ else
+ {
+ /* But if the window is an override-redirect window,
+ then:
+
+ - Find an activity that is currently active.
+
+ - Map the window as a panel on top of that
+ activity using the system window manager. */
+
+ ctx = findSuitableActivityContext ();
+ tem = ctx.getSystemService (Context.WINDOW_SERVICE);
+ windowManager = (WindowManager) tem;
+
+ /* Calculate layout parameters. */
+ params = getWindowLayoutParams ();
+ view.setLayoutParams (params);
+
+ /* Attach the view. */
+ try
+ {
+ windowManager.addView (view, params);
+
+ /* Record the window manager being used in the
+ EmacsWindow object. */
+ EmacsWindow.this.windowManager = windowManager;
+ }
+ catch (Exception e)
+ {
+ Log.w (TAG,
+ "failed to attach override-redirect window, " + e);
+ }
+ }
}
});
}
view.setVisibility (View.GONE);
+ /* Detach the view from the window manager if possible. */
+ if (windowManager != null)
+ windowManager.removeView (view);
+ windowManager = null;
+
/* Now that the window is unmapped, unregister it as
well. */
manager.detachWindow (EmacsWindow.this);
run ()
{
EmacsWindowAttachmentManager manager;
- View parent;
+ ViewManager parent;
/* First, detach this window if necessary. */
manager = EmacsWindowAttachmentManager.MANAGER;
manager.detachWindow (EmacsWindow.this);
/* Also unparent this view. */
- parent = (View) view.getParent ();
+
+ /* If the window manager is set, use that instead. */
+ if (windowManager != null)
+ parent = windowManager;
+ else
+ parent = (ViewManager) view.getParent ();
+ windowManager = null;
if (parent != null)
- ((ViewGroup) parent).removeView (view);
+ parent.removeView (view);
/* Next, either add this window as a child of the new
parent's view, or make it available again. */
{
return dontFocusOnMap;
}
+
+ public int[]
+ translateCoordinates (int x, int y)
+ {
+ int[] array;
+
+ /* This is supposed to translate coordinates to the root
+ window. */
+ array = new int[2];
+ EmacsService.SERVICE.getLocationOnScreen (view, array);
+
+ /* Now, the coordinates of the view should be in array. Offset X
+ and Y by them. */
+ array[0] += x;
+ array[1] += y;
+
+ /* Return the resulting coordinates. */
+ return array;
+ }
};
jmethodID get_screen_width;
jmethodID get_screen_height;
jmethodID detect_mouse;
+ jmethodID name_keysym;
};
struct android_emacs_pixmap
/* The global event queue. */
static struct android_event_queue event_queue;
-/* Semaphore used to signal select completion. */
-static sem_t android_pselect_sem;
+/* Semaphores used to signal select completion and start. */
+static sem_t android_pselect_sem, android_pselect_start_sem;
static void *
android_run_select_thread (void *data)
{
sigset_t signals;
- int sig, rc;
+ int rc;
sigfillset (&signals);
"pthread_sigmask: %s",
strerror (errno));
- sigemptyset (&signals);
- sigaddset (&signals, SIGUSR1);
-
- if (pthread_sigmask (SIG_UNBLOCK, &signals, NULL))
- __android_log_print (ANDROID_LOG_FATAL, __func__,
- "pthread_sigmask: %s",
- strerror (errno));
-
- sigemptyset (&signals);
- sigaddset (&signals, SIGUSR2);
-
while (true)
{
- /* Keep waiting for SIGUSR2, ignoring EINTR in the meantime. */
-
- while (sigwait (&signals, &sig))
- /* Spin. */;
+ /* Wait for the thread to be released. */
+ sem_wait (&android_pselect_start_sem);
/* Get the select lock and call pselect. */
pthread_mutex_lock (&event_queue.select_mutex);
strerror (errno));
sem_init (&android_pselect_sem, 0, 0);
+ sem_init (&android_pselect_start_sem, 0, 0);
event_queue.events.next = &event_queue.events;
event_queue.events.last = &event_queue.events;
android_pselect_sigset = sigset;
pthread_mutex_unlock (&event_queue.select_mutex);
- pthread_kill (event_queue.select_thread, SIGUSR2);
+ /* Release the select thread. */
+ sem_post (&android_pselect_start_sem);
+
pthread_cond_wait (&event_queue.read_var, &event_queue.mutex);
/* Interrupt the select thread now, in case it's still in
FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I");
FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I");
FIND_METHOD (detect_mouse, "detectMouse", "()Z");
+ FIND_METHOD (name_keysym, "nameKeysym", "(I)Ljava/lang/String;");
#undef FIND_METHOD
}
jobject object, parent_object, old;
android_window window;
android_handle prev_max_handle;
+ bool override_redirect;
parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
constructor
= (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
- "(SLorg/gnu/emacs/EmacsWindow;IIII)V");
+ "(SLorg/gnu/emacs/EmacsWindow;"
+ "IIIIZ)V");
assert (constructor != NULL);
old = class;
memory_full (0);
}
+ /* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
+ creation time. */
+ override_redirect = ((value_mask
+ & ANDROID_CW_OVERRIDE_REDIRECT)
+ && attrs->override_redirect);
+
object = (*android_java_env)->NewObject (android_java_env, class,
constructor, (jshort) window,
parent_object, (jint) x, (jint) y,
- (jint) width, (jint) height);
+ (jint) width, (jint) height,
+ (jboolean) override_redirect);
if (!object)
{
(*android_java_env)->ExceptionClear (android_java_env);
ANDROID_DELETE_LOCAL_REF (window_geometry);
}
+void
+android_move_resize_window (android_window window, int x, int y,
+ unsigned int width, unsigned int height)
+{
+ android_move_window (window, x, y);
+ android_resize_window (window, width, height);
+}
+
+void
+android_map_raised (android_window window)
+{
+ android_raise_window (window);
+ android_map_window (window);
+}
+
+void
+android_translate_coordinates (android_window src, int x,
+ int y, int *root_x, int *root_y)
+{
+ jobject window;
+ jarray coordinates;
+ jmethodID method;
+ jint *ints;
+
+ window = android_resolve_handle (src, ANDROID_HANDLE_WINDOW);
+ method = android_lookup_method ("org/gnu/emacs/EmacsWindow",
+ "translateCoordinates",
+ "(II)[I");
+ coordinates
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ window, method,
+ (jint) x, (jint) y);
+
+ if (!coordinates)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+
+ /* The array must contain two elements: X, Y translated to the root
+ window. */
+ eassert ((*android_java_env)->GetArrayLength (android_java_env,
+ coordinates)
+ == 2);
+
+ /* Obtain the coordinates from the array. */
+ ints = (*android_java_env)->GetIntArrayElements (android_java_env,
+ coordinates, NULL);
+ *root_x = ints[0];
+ *root_y = ints[1];
+
+ /* Release the coordinates. */
+ (*android_java_env)->ReleaseIntArrayElements (android_java_env,
+ coordinates, ints,
+ JNI_ABORT);
+
+ /* And free the local reference. */
+ ANDROID_DELETE_LOCAL_REF (coordinates);
+}
+
\f
/* Low level drawing primitives. */
(jboolean) no_accept_focus);
}
+void
+android_get_keysym_name (int keysym, char *name_return, size_t size)
+{
+ jobject string;
+ const char *buffer;
+
+ string = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.name_keysym,
+ (jint) keysym);
+ android_exception_check ();
+
+ buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
+ (jstring) string,
+ NULL);
+ android_exception_check ();
+ strncpy (name_return, buffer, size - 1);
+
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ (jstring) string,
+ buffer);
+ ANDROID_DELETE_LOCAL_REF (string);
+}
+
\f
#undef faccessat
abort ();
}
+\f
+
+/* Given a Lisp string TEXT, return a local reference to an equivalent
+ Java string. */
+
+jstring
+android_build_string (Lisp_Object text)
+{
+ Lisp_Object encoded;
+ jstring string;
+
+ encoded = ENCODE_UTF_8 (text);
+
+ /* Note that Java expects this string to be in ``modified UTF
+ encoding'', which is actually UTF-8, except with NUL encoded as a
+ two-byte sequence. The only consequence of passing an actual
+ UTF-8 string is that NUL bytes cannot be represented, which is
+ not really of consequence. */
+ string = (*android_java_env)->NewStringUTF (android_java_env,
+ SSDATA (encoded));
+ if (!string)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+
+ return string;
+}
+
+/* Check for JNI exceptions and call memory_full in that
+ situation. */
+
+void
+android_exception_check (void)
+{
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+}
+
#else /* ANDROID_STUBIFY */
/* X emulation functions for Android. */
#include <android/bitmap.h>
#include "androidgui.h"
+#include "lisp.h"
#endif
/* This must be used in every symbol declaration to export it to the
extern void android_set_dont_focus_on_map (android_window, bool);
extern void android_set_dont_accept_focus (android_window, bool);
+extern jstring android_build_string (Lisp_Object);
+extern void android_exception_check (void);
+
+extern void android_get_keysym_name (int, char *, size_t);
+
\f
/* Directory listing emulation. */
#include "androidterm.h"
#include "blockinput.h"
#include "keyboard.h"
+#include "buffer.h"
#ifndef ANDROID_STUBIFY
/* Some kind of reference count for the image cache. */
static ptrdiff_t image_cache_refcount;
+/* The frame of the currently visible tooltip, or nil if none. */
+static Lisp_Object tip_frame;
+
+/* The window-system window corresponding to the frame of the
+ currently visible tooltip. */
+static android_window tip_window;
+
+/* The X and Y deltas of the last call to `x-show-tip'. */
+static Lisp_Object tip_dx, tip_dy;
+
+/* A timer that hides or deletes the currently visible tooltip when it
+ fires. */
+static Lisp_Object tip_timer;
+
+/* STRING argument of last `x-show-tip' call. */
+static Lisp_Object tip_last_string;
+
+/* Normalized FRAME argument of last `x-show-tip' call. */
+static Lisp_Object tip_last_frame;
+
+/* PARMS argument of last `x-show-tip' call. */
+static Lisp_Object tip_last_parms;
+
#endif
static struct android_display_info *
fset_parent_frame (f, new_value);
}
+
+ /* Update the fullscreen frame parameter as well. */
+ FRAME_TERMINAL (f)->fullscreen_hook (f);
}
void
gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
NULL, NULL, RES_TYPE_NUMBER);
- /* gui_default_parameter (f, parms, Qvertical_scroll_bars, */
- /* Qleft, */
- /* "verticalScrollBars", "ScrollBars", */
- /* RES_TYPE_SYMBOL); */
- /* gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil, */
- /* "horizontalScrollBars", "ScrollBars", */
- /* RES_TYPE_SYMBOL); TODO */
+ gui_default_parameter (f, parms, Qvertical_scroll_bars,
+ Qleft,
+ "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"),
android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_background,
"scrollBarBackground",
"ScrollBarBackground", false);
-#endif /* TODO */
+#endif
/* Init faces before gui_default_parameter is called for the
scroll-bar-width parameter because otherwise we end up in
"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. */
+#if 0
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);
+#endif
gui_default_parameter (f, parms, Qalpha, Qnil,
"alpha", "Alpha", RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qalpha_background, Qnil,
/* 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. */
- gui_default_parameter (f, parms, Qfullscreen, Qnil,
+ 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
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;
+
+ 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));
+ }
+
+ /* Set the name; the functions to which we pass f expect the name to
+ be set. */
+ if (BASE_EQ (name, Qunbound) || NILP (name))
+ f->explicit_name = false;
+ else
+ {
+ fset_name (f, name);
+ f->explicit_name = true;
+ /* use the frame's title when getting resources for this frame. */
+ specbind (Qx_resource_name, name);
+ }
+
+ register_font_driver (&androidfont_driver, f);
+ register_font_driver (&android_sfntfont_driver, f);
+
+ image_cache_refcount
+ = FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
+#ifdef GLYPH_DEBUG
+ dpyinfo_refcount = dpyinfo->reference_count;
+#endif /* GLYPH_DEBUG */
+
+ gui_default_parameter (f, parms, Qfont_backend, Qnil,
+ "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+ /* Extract the window parameters from the supplied values that are
+ needed to determine window geometry. */
+ android_default_font_parameter (f, parms);
+
+ gui_default_parameter (f, parms, Qborder_width, make_fixnum (0),
+ "borderWidth", "BorderWidth", RES_TYPE_NUMBER);
+
+ /* This defaults to 1 in order to match xterm. We recognize either
+ internalBorderWidth or internalBorder (which is what xterm calls
+ it). */
+ if (NILP (Fassq (Qinternal_border_width, parms)))
+ {
+ Lisp_Object value;
+
+ value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
+ "internalBorder", "internalBorder",
+ RES_TYPE_NUMBER);
+ if (! BASE_EQ (value, Qunbound))
+ parms = Fcons (Fcons (Qinternal_border_width, value),
+ parms);
+ }
+
+ gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (1),
+ "internalBorderWidth", "internalBorderWidth",
+ RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+
+ /* Also do the stuff which must be set before the window exists. */
+ gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+ "foreground", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+ "background", "Background", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
+ "pointerColor", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qcursor_color, build_string ("black"),
+ "cursorColor", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
+ "borderColor", "BorderColor", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+
+ {
+ struct android_set_window_attributes attrs;
+ unsigned long mask;
+
+ block_input ();
+ mask = ANDROID_CW_OVERRIDE_REDIRECT;
+
+ attrs.override_redirect = true;
+ 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,
error ("Android cross-compilation stub called!");
return Qnil;
#else
- /* TODO tooltips */
- return Qnil;
+ struct frame *f, *tip_f;
+ struct window *w;
+ int root_x, root_y;
+ struct buffer *old_buffer;
+ struct text_pos pos;
+ int width, height;
+ int old_windows_or_buffers_changed = windows_or_buffers_changed;
+ specpdl_ref count = SPECPDL_INDEX ();
+ Lisp_Object window, size, tip_buf;
+ bool displayed;
+#ifdef ENABLE_CHECKING
+ struct glyph_row *row, *end;
+#endif
+ AUTO_STRING (tip, " *tip*");
+
+ specbind (Qinhibit_redisplay, Qt);
+
+ CHECK_STRING (string);
+ if (SCHARS (string) == 0)
+ string = make_unibyte_string (" ", 1);
+
+ if (NILP (frame))
+ frame = selected_frame;
+ f = decode_window_system_frame (frame);
+
+ if (NILP (timeout))
+ timeout = Vx_show_tooltip_timeout;
+ CHECK_FIXNAT (timeout);
+
+ if (NILP (dx))
+ dx = make_fixnum (5);
+ else
+ CHECK_FIXNUM (dx);
+
+ if (NILP (dy))
+ dy = make_fixnum (-10);
+ else
+ CHECK_FIXNUM (dy);
+
+ tip_dx = dx;
+ tip_dy = dy;
+
+ if (!NILP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
+ {
+ if (FRAME_VISIBLE_P (XFRAME (tip_frame))
+ && !NILP (Fequal_including_properties (tip_last_string,
+ string))
+ && !NILP (Fequal (tip_last_parms, parms)))
+ {
+ /* Only DX and DY have changed. */
+ tip_f = XFRAME (tip_frame);
+ if (!NILP (tip_timer))
+ {
+ call1 (Qcancel_timer, tip_timer);
+ tip_timer = Qnil;
+ }
+
+ block_input ();
+ compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
+ FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
+ android_move_window (FRAME_ANDROID_WINDOW (tip_f),
+ root_x, root_y);
+ unblock_input ();
+
+ goto start_timer;
+ }
+ else
+ android_hide_tip (true);
+ }
+ else
+ android_hide_tip (true);
+
+ tip_last_frame = frame;
+ tip_last_string = string;
+ tip_last_parms = parms;
+
+ if (NILP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
+ {
+ /* Add default values to frame parameters. */
+ if (NILP (Fassq (Qname, parms)))
+ parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
+ if (NILP (Fassq (Qinternal_border_width, parms)))
+ parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)),
+ parms);
+ if (NILP (Fassq (Qborder_width, parms)))
+ parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms);
+ if (NILP (Fassq (Qborder_color, parms)))
+ parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")),
+ parms);
+ if (NILP (Fassq (Qbackground_color, parms)))
+ parms = Fcons (Fcons (Qbackground_color,
+ build_string ("lightyellow")),
+ parms);
+
+ /* Create a frame for the tooltip, and record it in the global
+ variable tip_frame. */
+ if (NILP (tip_frame = android_create_tip_frame (FRAME_DISPLAY_INFO (f),
+ parms)))
+ /* Creating the tip frame failed. */
+ return unbind_to (count, Qnil);
+ }
+
+ tip_f = XFRAME (tip_frame);
+ window = FRAME_ROOT_WINDOW (tip_f);
+ tip_buf = Fget_buffer_create (tip, Qnil);
+ /* We will mark the tip window a "pseudo-window" below, and such
+ windows cannot have display margins. */
+ bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+ bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+ set_window_buffer (window, tip_buf, false, false);
+ w = XWINDOW (window);
+ w->pseudo_window_p = true;
+ /* Try to avoid that `other-window' select us (Bug#47207). */
+ Fset_window_parameter (window, Qno_other_window, Qt);
+
+ /* Set up the frame's root window. Note: The following code does not
+ try to size the window or its frame correctly. Its only purpose is
+ to make the subsequent text size calculations work. The right
+ sizes should get installed when the toolkit gets back to us. */
+ w->left_col = 0;
+ w->top_line = 0;
+ w->pixel_left = 0;
+ w->pixel_top = 0;
+
+ if (CONSP (Vx_max_tooltip_size)
+ && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
+ && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
+ {
+ w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size));
+ w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size));
+ }
+ else
+ {
+ w->total_cols = 80;
+ w->total_lines = 40;
+ }
+
+ w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
+ w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
+ FRAME_TOTAL_COLS (tip_f) = w->total_cols;
+ adjust_frame_glyphs (tip_f);
+
+ /* Insert STRING into root window's buffer and fit the frame to the
+ buffer. */
+ specpdl_ref count_1 = SPECPDL_INDEX ();
+ old_buffer = current_buffer;
+ set_buffer_internal_1 (XBUFFER (w->contents));
+ bset_truncate_lines (current_buffer, Qnil);
+ specbind (Qinhibit_read_only, Qt);
+ specbind (Qinhibit_modification_hooks, Qt);
+ specbind (Qinhibit_point_motion_hooks, Qt);
+ Ferase_buffer ();
+ Finsert (1, &string);
+ clear_glyph_matrix (w->desired_matrix);
+ clear_glyph_matrix (w->current_matrix);
+ SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
+ displayed = try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
+
+ if (!displayed && NILP (Vx_max_tooltip_size))
+ {
+#ifdef ENABLE_CHECKING
+ row = w->desired_matrix->rows;
+ end = w->desired_matrix->rows + w->desired_matrix->nrows;
+
+ while (row < end)
+ {
+ if (!row->displays_text_p
+ || row->ends_at_zv_p)
+ break;
+ ++row;
+ }
+
+ eassert (row < end && row->ends_at_zv_p);
+#endif
+ }
+
+ /* Calculate size of tooltip window. */
+ size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
+ make_fixnum (w->pixel_height), Qnil,
+ Qnil);
+ /* Add the frame's internal border to calculated size. */
+ width = XFIXNUM (CAR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+ height = XFIXNUM (CDR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+
+ /* Calculate position of tooltip frame. */
+ compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
+
+ /* Show tooltip frame. */
+ block_input ();
+ android_move_resize_window (FRAME_ANDROID_WINDOW (tip_f),
+ root_x, root_y, width,
+ height);
+ android_map_raised (FRAME_ANDROID_WINDOW (tip_f));
+ unblock_input ();
+
+ w->must_be_updated_p = true;
+ update_single_window (w);
+ flush_frame (tip_f);
+ set_buffer_internal_1 (old_buffer);
+ unbind_to (count_1, Qnil);
+ windows_or_buffers_changed = old_windows_or_buffers_changed;
+
+ 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
}
error ("Android cross-compilation stub called!");
return Qnil;
#else
- return Qnil;
+ return android_hide_tip (true);
#endif
}
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;
+
/* Functions defined. */
defsubr (&Sx_create_frame);
defsubr (&Sxw_color_defined_p);
defsubr (&Sx_show_tip);
defsubr (&Sx_hide_tip);
defsubr (&Sandroid_detect_mouse);
+
+#ifndef ANDROID_STUBIFY
+ 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);
+#endif
}
enum android_window_value_mask
{
- ANDROID_CW_BACK_PIXEL = (1 << 1),
+ ANDROID_CW_BACK_PIXEL = (1 << 1),
+ ANDROID_CW_OVERRIDE_REDIRECT = (1 << 2),
};
struct android_set_window_attributes
{
/* The background pixel. */
unsigned long background_pixel;
+
+ /* Whether or not the window is override redirect. This cannot be
+ set after creation on Android. */
+ bool override_redirect;
};
struct android_gc_values
((key) == 57 || (key) == 58 || (key) == 113 || (key) == 114 \
|| (key) == 119 || (key) == 117 || (key) == 118 || (key) == 78 \
|| (key) == 94 || (key) == 59 || (key) == 60 || (key) == 95 \
- || (key) == 63)
+ || (key) == 63 || (key) == 115)
struct android_configure_event
{
extern void android_get_geometry (android_window, android_window *,
int *, int *, unsigned int *,
unsigned int *, unsigned int *);
+extern void android_move_resize_window (android_window, int, int,
+ unsigned int, unsigned int);
+extern void android_map_raised (android_window);
+extern void android_translate_coordinates (android_window, int,
+ int, int *, int *);
#endif
#include "lisp.h"
#include "androidterm.h"
+#include "android.h"
+#include "blockinput.h"
+#include "keyboard.h"
+#include "menu.h"
#ifndef ANDROID_STUBIFY
return popup_activated_flag;
}
+\f
+
+/* Toolkit menu implementation. */
+
+/* Structure describing the EmacsContextMenu class. */
+
+struct android_emacs_context_menu
+{
+ jclass class;
+ jmethodID create_context_menu;
+ jmethodID add_item;
+ jmethodID add_submenu;
+ jmethodID add_pane;
+ jmethodID parent;
+ jmethodID display;
+};
+
+/* Identifiers associated with the EmacsContextMenu class. */
+static struct android_emacs_context_menu menu_class;
+
+static void
+android_init_emacs_context_menu (void)
+{
+ jclass old;
+
+ menu_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsContextMenu");
+ eassert (menu_class.class);
+
+ old = menu_class.class;
+ menu_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!menu_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ menu_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ menu_class.class, \
+ name, signature); \
+ eassert (menu_class.c_name);
+
+#define FIND_METHOD_STATIC(c_name, name, signature) \
+ menu_class.c_name \
+ = (*android_java_env)->GetStaticMethodID (android_java_env, \
+ menu_class.class, \
+ name, signature); \
+ eassert (menu_class.c_name);
+
+ FIND_METHOD_STATIC (create_context_menu, "createContextMenu",
+ "(Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;");
+
+ FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;Z)V");
+ FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;"
+ "Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;");
+ FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V");
+ FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;");
+ FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;II)Z");
+
+#undef FIND_METHOD
+#undef FIND_METHOD_STATIC
+}
+
+static void
+android_unwind_local_frame (void)
+{
+ (*android_java_env)->PopLocalFrame (android_java_env, NULL);
+}
+
+/* Push a local reference frame to the JVM stack and record it on the
+ specpdl. Release local references created within that frame when
+ the specpdl is unwound past where it is after returning. */
+
+static void
+android_push_local_frame (void)
+{
+ int rc;
+
+ rc = (*android_java_env)->PushLocalFrame (android_java_env, 30);
+
+ /* This means the JVM ran out of memory. */
+ if (rc < 1)
+ android_exception_check ();
+
+ record_unwind_protect_void (android_unwind_local_frame);
+}
+
+Lisp_Object
+android_menu_show (struct frame *f, int x, int y, int menuflags,
+ Lisp_Object title, const char **error_name)
+{
+ jobject context_menu, current_context_menu;
+ jobject title_string, temp;
+ size_t i;
+ Lisp_Object pane_name, prefix;
+ const char *pane_string;
+ specpdl_ref count, count1;
+ Lisp_Object item_name, enable, def;
+ jmethodID method;
+ jobject store;
+ bool rc;
+ jobject window;
+
+ count = SPECPDL_INDEX ();
+
+ block_input ();
+
+ /* Push the first local frame. */
+ android_push_local_frame ();
+
+ /* Push the first local frame for the context menu. */
+ title_string = (!NILP (title)
+ ? (jobject) android_build_string (title)
+ : NULL);
+ method = menu_class.create_context_menu;
+ current_context_menu = context_menu
+ = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ menu_class.class,
+ method,
+ title_string);
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ /* Push the second local frame for temporaries. */
+ count1 = SPECPDL_INDEX ();
+ android_push_local_frame ();
+
+ /* Iterate over the menu. */
+ i = 0;
+
+ while (i < menu_items_used)
+ {
+ if (NILP (AREF (menu_items, i)))
+ {
+ /* This is the start of a new submenu. However, it can be
+ ignored here. */
+ i += 1;
+ }
+ else if (EQ (AREF (menu_items, i), Qlambda))
+ {
+ /* This is the end of a submenu. Go back to the previous
+ context menu. */
+ store = current_context_menu;
+ current_context_menu
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ current_context_menu,
+ menu_class.parent);
+ android_exception_check ();
+
+ if (store != context_menu)
+ ANDROID_DELETE_LOCAL_REF (store);
+ i += 1;
+
+ eassert (current_context_menu);
+ }
+ else if (EQ (AREF (menu_items, i), Qquote))
+ i += 1;
+ else if (EQ (AREF (menu_items, i), Qt))
+ {
+ /* This is a new pane. Switch back to the topmost context
+ menu. */
+ if (current_context_menu != context_menu)
+ ANDROID_DELETE_LOCAL_REF (current_context_menu);
+ current_context_menu = context_menu;
+
+ /* Now figure out the title of this pane. */
+ pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+ prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+ pane_string = (NILP (pane_name)
+ ? "" : SSDATA (pane_name));
+ if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
+ pane_string++;
+
+ /* Add the pane. */
+ temp = (*android_java_env)->NewStringUTF (android_java_env,
+ pane_string);
+ android_exception_check ();
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_pane,
+ temp);
+ android_exception_check ();
+ ANDROID_DELETE_LOCAL_REF (temp);
+
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ else
+ {
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+ def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+
+ /* This is an actual menu item (or submenu). Add it to the
+ menu. */
+
+ if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used &&
+ NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH)))
+ {
+ /* This is a submenu. Add it. */
+ title_string = (!NILP (item_name)
+ ? android_build_string (item_name)
+ : NULL);
+ store = current_context_menu;
+ current_context_menu
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_submenu,
+ title_string);
+ android_exception_check ();
+
+ if (store != context_menu)
+ ANDROID_DELETE_LOCAL_REF (store);
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+ }
+ else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
+ /* Ignore this separator item. */
+ ;
+ else
+ {
+ /* Add this menu item with the appropriate state. */
+
+ title_string = (!NILP (item_name)
+ ? android_build_string (item_name)
+ : NULL);
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_item,
+ (jint) 1,
+ title_string,
+ (jboolean) !NILP (enable));
+ android_exception_check ();
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+ }
+
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ /* The menu has now been built. Pop the second local frame. */
+ unbind_to (count1, Qnil);
+
+ /* Now, display the context menu. */
+ window = android_resolve_handle (FRAME_ANDROID_WINDOW (f),
+ ANDROID_HANDLE_WINDOW);
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ context_menu,
+ window, (jint) x,
+ (jint) y);
+ android_exception_check ();
+
+ if (!rc)
+ /* This means displaying the menu failed. */
+ goto finish;
+
+#if 0
+ record_unwind_protect_ptr (android_dismiss_menu, &context_menu);
+
+ /* Otherwise, loop waiting for the menu event to arrive. */
+ android_process_events_for_menu (&id);
+
+ if (!id)
+ /* This means no menu item was selected. */
+ goto finish;
+
+#endif
+
+ finish:
+ unblock_input ();
+ return unbind_to (count, Qnil);
+}
+
+#endif
+
+void
+init_androidmenu (void)
+{
+#ifndef ANDROID_STUBIFY
+ android_init_emacs_context_menu ();
#endif
+}
f = android_window_to_frame (dpyinfo,
configureEvent.xconfigure.window);
+ if (!f)
+ goto OTHER;
+
int width = configureEvent.xconfigure.width;
int height = configureEvent.xconfigure.height;
inev.ie.arg = tab_bar_arg;
}
}
- else
- {
- /* TODO: scroll bars */
- }
if (event->type == ANDROID_BUTTON_PRESS)
{
static void
android_fullscreen_hook (struct frame *f)
{
- /* TODO */
+ /* Explicitly setting fullscreen is not supported on Android. */
+
+ if (!FRAME_PARENT_FRAME (f))
+ store_frame_param (f, Qfullscreen, Qmaximized);
+ else
+ store_frame_param (f, Qfullscreen, Qnil);
}
void
/* Top. */
android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
- left_x, right_x - left_x + 1, hwidth);
+ top_y, right_x - left_x + 1, hwidth);
/* Left. */
if (left_p)
char *
get_keysym_name (int keysym)
{
- return (char *) "UNKNOWN KEYSYM";
+ static char buffer[64];
+
+#ifndef ANDROID_STUBIFY
+ android_get_keysym_name (keysym, buffer, 64);
+#else
+ emacs_abort ();
+#endif
+ return buffer;
}
\f
terminal->set_new_font_hook = android_new_font;
terminal->set_bitmap_icon_hook = android_bitmap_icon;
terminal->implicit_set_name_hook = android_implicitly_set_name;
- /* terminal->menu_show_hook = android_menu_show; XXX */
+ terminal->menu_show_hook = android_menu_show;
terminal->change_tab_bar_height_hook = android_change_tab_bar_height;
terminal->change_tool_bar_height_hook = android_change_tool_bar_height;
- /* terminal->set_vertical_scroll_bar_hook */
- /* = android_set_vertical_scroll_bar; */
- /* terminal->set_horizontal_scroll_bar_hook */
- /* = android_set_horizontal_scroll_bar; */
terminal->set_scroll_bar_default_width_hook
= android_set_scroll_bar_default_width;
terminal->set_scroll_bar_default_height_hook
= android_set_scroll_bar_default_height;
- /* terminal->condemn_scroll_bars_hook = android_condemn_scroll_bars; */
- /* terminal->redeem_scroll_bars_hook = android_redeem_scroll_bars; */
- /* terminal->judge_scroll_bars_hook = android_judge_scroll_bars; */
terminal->free_pixmap = android_free_pixmap_hook;
terminal->delete_frame_hook = android_delete_frame;
terminal->delete_terminal_hook = android_delete_terminal;
extern void android_finalize_font_entity (struct font_entity *);
+/* Defined in androidmenu.c. */
+
+extern Lisp_Object android_menu_show (struct frame *, int, int, int,
+ Lisp_Object, const char **);
+extern void init_androidmenu (void);
+
/* Defined in sfntfont-android.c. */
extern const struct font_driver android_sfntfont_driver;
init_window ();
init_font ();
+#ifdef HAVE_ANDROID
+ init_androidmenu ();
+#endif
+
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
init_androidfont ();
init_sfntfont ();
+make -C lib libgnu.a
src/Makefile src/config.h &: $(top_builddir)/src/config.h.android \
- $(top_builddir)/src/Makefile.android $(PRE_BUILD_DEPS)
+ $(top_builddir)/src/Makefile.android
mkdir -p src src/deps
# Copy config.h to src/
cp -f -p $(top_builddir)/src/config.h.android src/config.h
# Finally, go into lib-src and make everything being built
+make -C lib-src $(foreach bin,$(LIBSRC_BINARIES),$(notdir $(bin)))
-.PHONY: clean maintainer-clean
+.PHONY: clean maintainer-clean distclean
clean:
rm -rf $(CLEAN_SUBDIRS) *.bak sys
if [ -e lib/Makefile ]; then \
make -C lib clean; \
fi
- rm -rf lib/gnulib.mk lib/Makefile lib/config.h
+ rm -rf lib/config.h
+
+distclean bootstrap-clean: clean
+ if [ -e lib/Makefile ]; then \
+ make -C lib distclean; \
+ fi
+# Just in case.
+ rm -rf lib/Makefile lib/gnulib.mk
maintainer-clean: clean
if [ -e lib/Makefile ]; then \