mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-05 22:20:24 -08:00
Update Android port
* doc/emacs/android.texi (Android Startup, Android Environment): Document that restrictions on starting Emacs have been lifted. * java/README: Document Java for Emacs developers and how the Android port works. * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication) (findDumpFile): New function. (onCreate): Factor out dump file finding functions to there. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Update function declarations. * java/org/gnu/emacs/EmacsNoninteractive.java (EmacsNoninteractive): New class. * java/org/gnu/emacs/EmacsService.java (EmacsService, getApkFile) (onCreate): Pass classpath to setEmacsParams. * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Make run an override. * lisp/loadup.el: Don't dump on Android when noninteractive. * lisp/shell.el (shell--command-completion-data): Handle inaccessible directories. * src/Makefile.in (android-emacs): Link with gnulib. * src/android-emacs.c (main): Implement to launch app-process and then EmacsNoninteractive. * src/android.c (setEmacsParams): New argument `class_path'. Don't set stuff up when running noninteractive. * src/android.h (initEmacs): Likewise. * src/androidfont.c (init_androidfont): * src/androidselect.c (init_androidselect): Don't initialize when running noninteractive. * src/emacs.c (load_pdump): New argument `dump_file'. (android_emacs_init): Give new argument `dump_file' to `load_pdump'. * src/sfntfont-android.c (init_sfntfont_android): Don't initialize when running noninteractive.
This commit is contained in:
parent
6f9a2a8f29
commit
0900bfbcc5
17 changed files with 1306 additions and 109 deletions
824
java/README
824
java/README
|
|
@ -10,3 +10,827 @@ to install different builds of Emacs on top of each other.
|
|||
Please keep the Java code indented with tabs and formatted according
|
||||
to the rules for C code in the GNU coding standards. Always use
|
||||
C-style comments.
|
||||
|
||||
======================================================================
|
||||
|
||||
OVERVIEW OF JAVA
|
||||
|
||||
Emacs developers do not know Java, and there is no reason they should
|
||||
have to. Thus, the code in this directory is confined to what is
|
||||
strictly necessary to support Emacs, and only uses a subset of Java
|
||||
written in a way that is easily understandable to C programmers.
|
||||
|
||||
Java is required because the entire Android runtime is based around
|
||||
Java, and there is no way to write an Android program which runs
|
||||
without Java.
|
||||
|
||||
This text exists to prime other Emacs developers, already familar with
|
||||
C, on the basic architecture of the Android port, and to teach them
|
||||
how to read and write the Java code found in this directory.
|
||||
|
||||
Java is an object oriented language with automatic memory management
|
||||
compiled down to bytecode, which is then subject to interpretation by
|
||||
a Java virtual machine.
|
||||
|
||||
What that means, is that:
|
||||
|
||||
struct emacs_window
|
||||
{
|
||||
int some_fields;
|
||||
int of_emacs_window;
|
||||
};
|
||||
|
||||
static void
|
||||
do_something_with_emacs_window (struct emacs_window *a, int n)
|
||||
{
|
||||
a->some_fields = a->of_emacs_window + n;
|
||||
}
|
||||
|
||||
would be written:
|
||||
|
||||
public class EmacsWindow
|
||||
{
|
||||
public int someFields;
|
||||
public int ofEmacsWindow;
|
||||
|
||||
public void
|
||||
doSomething (int n)
|
||||
{
|
||||
someFields = ofEmacsWindow + n;
|
||||
}
|
||||
}
|
||||
|
||||
and instead of doing:
|
||||
|
||||
do_something_with_emacs_window (my_window, 1);
|
||||
|
||||
you say:
|
||||
|
||||
myWindow.doSomething (1);
|
||||
|
||||
In addition to functions associated with an object of a given class
|
||||
(such as EmacsWindow), Java also has two other kinds of functions.
|
||||
|
||||
The first are so-called ``static'' functions (the static means
|
||||
something entirely different from what it does in C.)
|
||||
|
||||
A static function, while still having to be defined within a class,
|
||||
can be called without any object. Instead of the object, you write
|
||||
the name of the Java class within which it is defined. For example,
|
||||
the following C code:
|
||||
|
||||
int
|
||||
multiply_a_with_b_and_then_add_c (int a, int b, int c)
|
||||
{
|
||||
return a * b + c;
|
||||
}
|
||||
|
||||
would be:
|
||||
|
||||
public class EmacsSomething
|
||||
{
|
||||
public static int
|
||||
multiplyAWithBAndThenAddC (int a, int b, int c)
|
||||
{
|
||||
return a * b + c;
|
||||
}
|
||||
};
|
||||
|
||||
Then, instead of calling:
|
||||
|
||||
int foo;
|
||||
|
||||
foo = multiply_a_with_b_then_add_c (1, 2, 3);
|
||||
|
||||
you say:
|
||||
|
||||
int foo;
|
||||
|
||||
foo = EmacsSomething.multiplyAWithBAndThenAddC (1, 2, 3);
|
||||
|
||||
In Java, ``static'' does not mean that the function is only used
|
||||
within its compilation unit! Instead, the ``private'' qualifier is
|
||||
used to mean more or less the same thing:
|
||||
|
||||
static void
|
||||
this_procedure_is_only_used_within_this_file (void)
|
||||
{
|
||||
do_something ();
|
||||
}
|
||||
|
||||
becomes
|
||||
|
||||
public class EmacsSomething
|
||||
{
|
||||
private static void
|
||||
thisProcedureIsOnlyUsedWithinThisClass ()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
the other kind are called ``constructors''. They are functions that
|
||||
must be called to allocate memory to hold a class:
|
||||
|
||||
public class EmacsFoo
|
||||
{
|
||||
int bar;
|
||||
|
||||
public
|
||||
EmacsFoo (int tokenA, int tokenB)
|
||||
{
|
||||
bar = tokenA + tokenB;
|
||||
}
|
||||
}
|
||||
|
||||
now, the following statement:
|
||||
|
||||
EmacsFoo foo;
|
||||
|
||||
foo = new EmacsFoo (1, 2);
|
||||
|
||||
becomes more or less equivalent to the following C code:
|
||||
|
||||
struct emacs_foo
|
||||
{
|
||||
int bar;
|
||||
};
|
||||
|
||||
struct emacs_foo *
|
||||
make_emacs_foo (int token_a, int token_b)
|
||||
{
|
||||
struct emacs_foo *foo;
|
||||
|
||||
foo = xmalloc (sizeof *foo);
|
||||
foo->bar = token_a + token_b;
|
||||
|
||||
return foo;
|
||||
}
|
||||
|
||||
/* ... */
|
||||
|
||||
struct emacs_foo *foo;
|
||||
|
||||
foo = make_emacs_foo (1, 2);
|
||||
|
||||
A class may have any number of constructors, or no constructors at
|
||||
all, in which case the compiler inserts an empty constructor.
|
||||
|
||||
|
||||
|
||||
Sometimes, you will see Java code that looks like this:
|
||||
|
||||
allFiles = filesDirectory.listFiles (new FileFilter () {
|
||||
@Override
|
||||
public boolean
|
||||
accept (File file)
|
||||
{
|
||||
return (!file.isDirectory ()
|
||||
&& file.getName ().endsWith (".pdmp"));
|
||||
}
|
||||
});
|
||||
|
||||
This is Java's version of GCC's nested function extension. The major
|
||||
difference is that the nested function may still be called even after
|
||||
it goes out of scope, and always retains a reference to the class and
|
||||
local variables around where it was called.
|
||||
|
||||
Being an object-oriented language, Java also allows defining that a
|
||||
class ``extends'' another class. The following C code:
|
||||
|
||||
struct a
|
||||
{
|
||||
long thirty_two;
|
||||
};
|
||||
|
||||
struct b
|
||||
{
|
||||
struct a a;
|
||||
long long sixty_four;
|
||||
};
|
||||
|
||||
extern void do_something (struct a *);
|
||||
|
||||
void
|
||||
my_function (struct b *b)
|
||||
{
|
||||
do_something (&b->a);
|
||||
}
|
||||
|
||||
is roughly equivalent to the following Java code, split into two
|
||||
files:
|
||||
|
||||
A.java
|
||||
|
||||
public class A
|
||||
{
|
||||
int thirtyTwo;
|
||||
|
||||
public void
|
||||
doSomething ()
|
||||
{
|
||||
etcEtcEtc ();
|
||||
}
|
||||
};
|
||||
|
||||
B.java
|
||||
|
||||
public class B extends A
|
||||
{
|
||||
long sixty_four;
|
||||
|
||||
public static void
|
||||
myFunction (B b)
|
||||
{
|
||||
b.doSomething ();
|
||||
}
|
||||
}
|
||||
|
||||
the Java runtime has transformed the call to ``b.doSomething'' to
|
||||
``((A) b).doSomething''.
|
||||
|
||||
However, Java also allows overriding this behavior, by specifying the
|
||||
@Override keyword:
|
||||
|
||||
public class B extends A
|
||||
{
|
||||
long sixty_four;
|
||||
|
||||
@Override
|
||||
public void
|
||||
doSomething ()
|
||||
{
|
||||
Something.doSomethingTwo ();
|
||||
super.doSomething ();
|
||||
}
|
||||
}
|
||||
|
||||
now, any call to ``doSomething'' on a ``B'' created using ``new B ()''
|
||||
will end up calling ``Something.doSomethingTwo'', before calling back
|
||||
to ``A.doSomething''. This override also applies in reverse; that is
|
||||
to say, even if you write:
|
||||
|
||||
((A) b).doSomething ();
|
||||
|
||||
B's version of doSomething will still be called, if ``b'' was created
|
||||
using ``new B ()''.
|
||||
|
||||
This mechanism is used extensively throughout the Java language and
|
||||
Android windowing APIs.
|
||||
|
||||
Elsewhere, you will encounter Java code that defines arrays:
|
||||
|
||||
public class EmacsFrobinicator
|
||||
{
|
||||
public static void
|
||||
emacsFrobinicate (int something)
|
||||
{
|
||||
int[] primesFromSomething;
|
||||
|
||||
primesFromSomething = new int[numberOfPrimes];
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
|
||||
Java arrays are similar to C arrays in that they can not grow. But
|
||||
they are very much unlike C arrays in that they are always references
|
||||
(as opposed to decaying into pointers in various situations), and
|
||||
contain information about their length.
|
||||
|
||||
If another function named ``frobinicate1'' takes an array as an
|
||||
argument, then it need not take the length of the array.
|
||||
|
||||
Instead, it simply iterates over the array like so:
|
||||
|
||||
int i, k;
|
||||
|
||||
for (i = 0; i < array.length; ++i)
|
||||
{
|
||||
k = array[i];
|
||||
|
||||
Whatever.doSomethingWithK (k);
|
||||
}
|
||||
|
||||
The syntax used to define arrays is also slightly different. As
|
||||
arrays are always references, there is no way for you to tell the
|
||||
runtime to allocate an array of size N in a structure (class.)
|
||||
|
||||
Instead, if you need an array of that size, you must declare a field
|
||||
with the type of the array, and allocate the array inside the class's
|
||||
constructor, like so:
|
||||
|
||||
public class EmacsArrayContainer
|
||||
{
|
||||
public int[] myArray;
|
||||
|
||||
public
|
||||
EmacsArrayContainer ()
|
||||
{
|
||||
myArray = new array[10];
|
||||
}
|
||||
}
|
||||
|
||||
while in C, you could just have written:
|
||||
|
||||
struct emacs_array_container
|
||||
{
|
||||
int my_array[10];
|
||||
};
|
||||
|
||||
or, possibly even better,
|
||||
|
||||
typedef int my_array[10];
|
||||
|
||||
Alas, Java has no equivalent of `typedef'.
|
||||
|
||||
JAVA NATIVE INTERFACE
|
||||
|
||||
Java also provides an interface for C code to interface with Java.
|
||||
|
||||
C functions exported from a shared library become static Java
|
||||
functions within a class, like so:
|
||||
|
||||
public class EmacsNative
|
||||
{
|
||||
/* Obtain the fingerprint of this build of Emacs. The fingerprint
|
||||
can be used to determine the dump file name. */
|
||||
public static native String getFingerprint ();
|
||||
|
||||
/* Set certain parameters before initializing Emacs.
|
||||
|
||||
assetManager must be the asset manager associated with the
|
||||
context that is loading Emacs. It is saved and remains for the
|
||||
remainder the lifetime of the Emacs process.
|
||||
|
||||
filesDir must be the package's data storage location for the
|
||||
current Android user.
|
||||
|
||||
libDir must be the package's data storage location for native
|
||||
libraries. It is used as PATH.
|
||||
|
||||
cacheDir must be the package's cache directory. It is used as
|
||||
the `temporary-file-directory'.
|
||||
|
||||
pixelDensityX and pixelDensityY are the DPI values that will be
|
||||
used by Emacs.
|
||||
|
||||
classPath must be the classpath of this app_process process, or
|
||||
NULL.
|
||||
|
||||
emacsService must be the EmacsService singleton, or NULL. */
|
||||
public static native void setEmacsParams (AssetManager assetManager,
|
||||
String filesDir,
|
||||
String libDir,
|
||||
String cacheDir,
|
||||
float pixelDensityX,
|
||||
float pixelDensityY,
|
||||
String classPath,
|
||||
EmacsService emacsService);
|
||||
}
|
||||
|
||||
Where the corresponding C functions are located in android.c, and
|
||||
loaded by the special invocation:
|
||||
|
||||
static
|
||||
{
|
||||
System.loadLibrary ("emacs");
|
||||
};
|
||||
|
||||
|
||||
See http://docs.oracle.com/en/java/javase/19/docs/specs/jni/intro.html
|
||||
for more details.
|
||||
|
||||
|
||||
|
||||
OVERVIEW OF ANDROID
|
||||
|
||||
When the Android system starts an application, it does not actually
|
||||
call the application's ``main'' function. It may not even start the
|
||||
application's process if one is already running.
|
||||
|
||||
Instead, Android is organized around components. When the user opens
|
||||
the ``Emacs'' icon, the Android system looks up and starts the
|
||||
component associated with the ``Emacs'' icon. In this case, the
|
||||
component is called an activity, and is declared in
|
||||
the AndroidManifest.xml in this directory:
|
||||
|
||||
<activity android:name="org.gnu.emacs.EmacsActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
This tells Android to start the activity defined in ``EmacsActivity''
|
||||
(defined in org/gnu/emacs/EmacsActivity.java), a class extending the
|
||||
Android class ``Activity''.
|
||||
|
||||
To do so, the Android system creates an instance of ``EmacsActivity''
|
||||
and the window system window associated with it, and eventually calls:
|
||||
|
||||
Activity activity;
|
||||
|
||||
activity.onCreate (...);
|
||||
|
||||
But which ``onCreate'' is really called?
|
||||
It is actually the ``onCreate'' defined in EmacsActivity.java, as
|
||||
it overrides the ``onCreate'' defined in Android's own Activity class:
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate (Bundle savedInstanceState)
|
||||
{
|
||||
FrameLayout.LayoutParams params;
|
||||
Intent intent;
|
||||
|
||||
Then, this is what happens step-by-step within the ``onCreate''
|
||||
function:
|
||||
|
||||
/* See if Emacs should be started with -Q. */
|
||||
intent = getIntent ();
|
||||
EmacsService.needDashQ
|
||||
= intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
|
||||
false);
|
||||
|
||||
Here, Emacs obtains the intent (a request to start a component) which
|
||||
was used to start Emacs, and sets a special flag if it contains a
|
||||
request for Emacs to start with the ``-Q'' command-line argument.
|
||||
|
||||
/* Set the theme to one without a title bar. */
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
setTheme (android.R.style.Theme_DeviceDefault_NoActionBar);
|
||||
else
|
||||
setTheme (android.R.style.Theme_NoTitleBar);
|
||||
|
||||
Next, Emacs sets an appropriate theme for the activity's associated
|
||||
window decorations.
|
||||
|
||||
params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT);
|
||||
|
||||
/* Make the frame layout. */
|
||||
layout = new FrameLayout (this);
|
||||
layout.setLayoutParams (params);
|
||||
|
||||
/* Set it as the content view. */
|
||||
setContentView (layout);
|
||||
|
||||
Then, Emacs creates a ``FrameLayout'', a widget that holds a single
|
||||
other widget, and makes it the activity's ``content view''.
|
||||
|
||||
The activity itself is a ``FrameLayout'', so the ``layout parameters''
|
||||
here apply to the FrameLayout itself, and not its children.
|
||||
|
||||
/* Maybe start the Emacs service if necessary. */
|
||||
EmacsService.startEmacsService (this);
|
||||
|
||||
And after that, Emacs calls the static function ``startEmacsService'',
|
||||
defined in the class ``EmacsService''. This starts the Emacs service
|
||||
component if necessary.
|
||||
|
||||
/* Add this activity to the list of available activities. */
|
||||
EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
|
||||
|
||||
super.onCreate (savedInstanceState);
|
||||
|
||||
Finally, Emacs registers that this activity is now ready to receive
|
||||
top-level frames (windows) created from Lisp.
|
||||
|
||||
Activities come and go, but Emacs has to stay running in the mean
|
||||
time. Thus, Emacs also defines a ``service'', which is a long-running
|
||||
component that the Android system allows to run in the background.
|
||||
|
||||
Let us go back and review the definition of ``startEmacsService'':
|
||||
|
||||
public static void
|
||||
startEmacsService (Context context)
|
||||
{
|
||||
if (EmacsService.SERVICE == null)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
/* Start the Emacs service now. */
|
||||
context.startService (new Intent (context,
|
||||
EmacsService.class));
|
||||
else
|
||||
/* Display the permanant notification and start Emacs as a
|
||||
foreground service. */
|
||||
context.startForegroundService (new Intent (context,
|
||||
EmacsService.class));
|
||||
}
|
||||
}
|
||||
|
||||
If ``EmacsService.SERVICE'' does not yet exist, what this does is to
|
||||
tell the ``context'' (the equivalent of an Xlib Display *) to start a
|
||||
service defined by the class ``EmacsService''. Eventually, this
|
||||
results in ``EmacsService.onCreate'' being called:
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate ()
|
||||
{
|
||||
AssetManager manager;
|
||||
Context app_context;
|
||||
String filesDir, libDir, cacheDir, classPath;
|
||||
double pixelDensityX;
|
||||
double pixelDensityY;
|
||||
|
||||
Here is what this function does, step-by-step:
|
||||
|
||||
SERVICE = this;
|
||||
|
||||
First, it sets the special static variable ``SERVICE'' to ``this'',
|
||||
which is a pointer to the ``EmacsService' object that was created.
|
||||
|
||||
handler = new Handler (Looper.getMainLooper ());
|
||||
|
||||
Next, it creates a ``Handler'' object for the ``main looper''.
|
||||
This is a helper structure which allows executing code on the Android
|
||||
user interface thread.
|
||||
|
||||
manager = getAssets ();
|
||||
app_context = getApplicationContext ();
|
||||
metrics = getResources ().getDisplayMetrics ();
|
||||
pixelDensityX = metrics.xdpi;
|
||||
pixelDensityY = metrics.ydpi;
|
||||
|
||||
Finally, it obtains:
|
||||
|
||||
- the asset manager, which is used to retrieve assets packaged
|
||||
into the Emacs application package.
|
||||
|
||||
- the application context, used to obtain application specific
|
||||
information.
|
||||
|
||||
- the display metrics, and from them, the X and Y densities in dots
|
||||
per inch.
|
||||
|
||||
Then, inside a ``try'' block:
|
||||
|
||||
try
|
||||
{
|
||||
/* Configure Emacs with the asset manager and other necessary
|
||||
parameters. */
|
||||
filesDir = app_context.getFilesDir ().getCanonicalPath ();
|
||||
libDir = getLibraryDirectory ();
|
||||
cacheDir = app_context.getCacheDir ().getCanonicalPath ();
|
||||
|
||||
It obtains the names of the Emacs home, shared library, and temporary
|
||||
file directories.
|
||||
|
||||
/* Now provide this application's apk file, so a recursive
|
||||
invocation of app_process (through android-emacs) can
|
||||
find EmacsNoninteractive. */
|
||||
classPath = getApkFile ();
|
||||
|
||||
The name of the Emacs application package.
|
||||
|
||||
Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
|
||||
+ ", libDir = " + libDir + ", and classPath = " + classPath);
|
||||
|
||||
Prints a debug message to the Android system log with this
|
||||
information.
|
||||
|
||||
EmacsNative.setEmacsParams (manager, filesDir, libDir,
|
||||
cacheDir, (float) pixelDensityX,
|
||||
(float) pixelDensityY,
|
||||
classPath, this);
|
||||
|
||||
And calls the native function ``setEmacsParams'' (defined in
|
||||
android.c) to configure Emacs with this information.
|
||||
|
||||
/* Start the thread that runs Emacs. */
|
||||
thread = new EmacsThread (this, needDashQ);
|
||||
thread.start ();
|
||||
|
||||
Then, it allocates an ``EmacsThread'' object, and starts that thread.
|
||||
Inside that thread is where Emacs's C code runs.
|
||||
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
EmacsNative.emacsAbort ();
|
||||
return;
|
||||
|
||||
And here is the purpose of the ``try'' block. Functions related to
|
||||
file names in Java will signal errors of various types upon failure.
|
||||
|
||||
This ``catch'' block means that the Java virtual machine will abort
|
||||
execution of the contents of the ``try'' block as soon as an error of
|
||||
type ``IOException'' is encountered, and begin executing the contents
|
||||
of the ``catch'' block.
|
||||
|
||||
Any failure of that type here is a crash, and
|
||||
``EmacsNative.emacsAbort'' is called to quickly abort the process to
|
||||
get a useful backtrace.
|
||||
}
|
||||
}
|
||||
|
||||
Now, let us look at the definition of the class ``EmacsThread'', found
|
||||
in org/gnu/emacs/EmacsThread.java:
|
||||
|
||||
public class EmacsThread extends Thread
|
||||
{
|
||||
/* Whether or not Emacs should be started -Q. */
|
||||
private boolean startDashQ;
|
||||
|
||||
public
|
||||
EmacsThread (EmacsService service, boolean startDashQ)
|
||||
{
|
||||
super ("Emacs main thread");
|
||||
this.startDashQ = startDashQ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void
|
||||
run ()
|
||||
{
|
||||
String args[];
|
||||
|
||||
if (!startDashQ)
|
||||
args = new String[] { "libandroid-emacs.so", };
|
||||
else
|
||||
args = new String[] { "libandroid-emacs.so", "-Q", };
|
||||
|
||||
/* Run the native code now. */
|
||||
EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
|
||||
}
|
||||
};
|
||||
|
||||
The class itself defines a single field, ``startDashQ'', a constructor
|
||||
with an unused argument of the type ``EmacsService'' (which is useful
|
||||
while debugging) and a flag ``startDashQ'', and a single function
|
||||
``run'', overriding the same function in the class ``Thread''.
|
||||
|
||||
When ``thread.start'' is called, the Java virtual machine creates a
|
||||
new thread, and then calls the function ``run'' within that thread.
|
||||
|
||||
This function then computes a suitable argument vector, and calls
|
||||
``EmacsNative.initEmacs'' (defined in android.c), which then calls a
|
||||
modified version of the regular Emacs ``main'' function.
|
||||
|
||||
At that point, Emacs initialization proceeds as usual:
|
||||
Vinitial_window_system is set, loadup.el calls `normal-top-level',
|
||||
which calls `command-line', and finally
|
||||
`window-system-initialization', which initializes the `android'
|
||||
terminal interface as usual.
|
||||
|
||||
What happens here is the same as on other platforms. Now, here is
|
||||
what happens when the initial frame is created: Fx_create_frame calls
|
||||
`android_create_frame_window' to create a top level window:
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
||||
This calls the function `android_create_window' with some arguments
|
||||
whose meanings are identical to the arguments to `XCreateWindow'.
|
||||
|
||||
Here is the definition of `android_create_window', in android.c:
|
||||
|
||||
android_window
|
||||
android_create_window (android_window parent, int x, int y,
|
||||
int width, int height,
|
||||
enum android_window_value_mask value_mask,
|
||||
struct android_set_window_attributes *attrs)
|
||||
{
|
||||
static jclass class;
|
||||
static jmethodID constructor;
|
||||
jobject object, parent_object, old;
|
||||
android_window window;
|
||||
android_handle prev_max_handle;
|
||||
bool override_redirect;
|
||||
|
||||
What does it do? First, some context:
|
||||
|
||||
At any time, there can be at most 65535 Java objects referred to by
|
||||
the rest of Emacs through the Java native interface. Each such object
|
||||
is assigned a ``handle'' (similar to an XID on X) and given a unique
|
||||
type. The function `android_resolve_handle' returns the JNI `jobject'
|
||||
associated with a given handle.
|
||||
|
||||
parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
|
||||
|
||||
Here, it is being used to look up the `jobject' associated with the
|
||||
`parent' handle.
|
||||
|
||||
prev_max_handle = max_handle;
|
||||
window = android_alloc_id ();
|
||||
|
||||
Next, `max_handle' is saved, and a new handle is allocated for
|
||||
`window'.
|
||||
|
||||
if (!window)
|
||||
error ("Out of window handles!");
|
||||
|
||||
An error is signalled if Emacs runs out of available handles.
|
||||
|
||||
if (!class)
|
||||
{
|
||||
class = (*android_java_env)->FindClass (android_java_env,
|
||||
"org/gnu/emacs/EmacsWindow");
|
||||
assert (class != NULL);
|
||||
|
||||
Then, if this initialization has not yet been completed, Emacs
|
||||
proceeds to find the Java class named ``EmacsWindow''.
|
||||
|
||||
constructor
|
||||
= (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
|
||||
"(SLorg/gnu/emacs/EmacsWindow;"
|
||||
"IIIIZ)V");
|
||||
assert (constructor != NULL);
|
||||
|
||||
And it tries to look up the constructor, which should take seven
|
||||
arguments:
|
||||
|
||||
S - a short. (the handle ID)
|
||||
Lorg/gnu/Emacs/EmacsWindow; - an instance of the EmacsWindow
|
||||
class. (the parent)
|
||||
IIII - four ints. (the window geometry.)
|
||||
Z - a boolean. (whether or not the
|
||||
window is override-redirect; see
|
||||
XChangeWindowAttributes.)
|
||||
|
||||
old = class;
|
||||
class = (*android_java_env)->NewGlobalRef (android_java_env, class);
|
||||
(*android_java_env)->ExceptionClear (android_java_env);
|
||||
ANDROID_DELETE_LOCAL_REF (old);
|
||||
|
||||
Next, it saves a global reference to the class and deletes the local
|
||||
reference. Global references will never be deallocated by the Java
|
||||
virtual machine as long as they still exist.
|
||||
|
||||
if (!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,
|
||||
(jboolean) override_redirect);
|
||||
|
||||
Then, it creates an instance of the ``EmacsWindow'' class with the
|
||||
appropriate arguments and previously determined constructor.
|
||||
|
||||
if (!object)
|
||||
{
|
||||
(*android_java_env)->ExceptionClear (android_java_env);
|
||||
|
||||
max_handle = prev_max_handle;
|
||||
memory_full (0);
|
||||
|
||||
If creating the object fails, Emacs clears the ``pending exception''
|
||||
and signals that it is out of memory.
|
||||
}
|
||||
|
||||
android_handles[window].type = ANDROID_HANDLE_WINDOW;
|
||||
android_handles[window].handle
|
||||
= (*android_java_env)->NewGlobalRef (android_java_env,
|
||||
object);
|
||||
(*android_java_env)->ExceptionClear (android_java_env);
|
||||
ANDROID_DELETE_LOCAL_REF (object);
|
||||
|
||||
Otherwise, it associates a new global reference to the object with the
|
||||
handle, and deletes the local reference returned from the JNI
|
||||
NewObject function.
|
||||
|
||||
if (!android_handles[window].handle)
|
||||
memory_full (0);
|
||||
|
||||
If allocating the global reference fails, Emacs signals that it is out
|
||||
of memory.
|
||||
|
||||
android_change_window_attributes (window, value_mask, attrs);
|
||||
return window;
|
||||
|
||||
Otherwise, it applies the specified window attributes and returns the
|
||||
handle of the new window.
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue