From: Po Lu Date: Sun, 3 Sep 2023 02:04:44 +0000 (+0800) Subject: Move Android port internals documentation to admin/notes X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=4debb110d70691a405a50272b3ca5d0a264e0010;p=emacs.git Move Android port internals documentation to admin/notes * admin/notes/java: New file. Move most of its contents from README, and introduce a section on compatibility. * java/README: Move internals to admin/notes/java. --- diff --git a/admin/notes/java b/admin/notes/java new file mode 100644 index 00000000000..125ac0aad67 --- /dev/null +++ b/admin/notes/java @@ -0,0 +1,1097 @@ +Installation instructions for Android +Copyright (C) 2023 Free Software Foundation, Inc. +See the end of the file for license conditions. + + + +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 only some 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 may simply iterate 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 emacs_array_container[10]; + +Alas, Java has no equivalent of `typedef'. + +Like in C, Java string literals are delimited by double quotes. +Unlike C, however, strings are not NULL-terminated arrays of +characters, but a distinct type named ``String''. They store their +own length, characters in Java's 16-bit ``char'' type, and are capable +of holding NULL bytes. + +Instead of writing: + +wchar_t character; +extern char *s; +size_t s; + + for (/* determine n, s in a loop. */) + s += mbstowc (&character, s, n); + +or: + +const char *byte; + +for (byte = my_string; *byte; ++byte) + /* do something with *byte. */; + +or perhaps even: + +size_t length, i; +char foo; + +length = strlen (my_string); + +for (i = 0; i < length; ++i) + foo = my_string[i]; + +you write: + +char foo; +int i; + +for (i = 0; i < myString.length (); ++i) + foo = myString.charAt (0); + +Java also has stricter rules on what can be used as a truth value in a +conditional. While in C, any non-zero value is true, Java requires +that every truth value be of the boolean type ``boolean''. + +What this means is that instead of simply writing: + + if (foo || bar) + +where foo can either be 1 or 0, and bar can either be NULL or a +pointer to something, you must explicitly write: + + if (foo != 0 || bar != null) + +in Java. + +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"); + }; + +where ``static'' defines a section of code which will be run upon the +object (containing class) being loaded. This is like: + + __attribute__((constructor)) + +on systems where shared object constructors are supported. + +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: + + + + + + + + + +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, "", + "(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. +} + + + +DRAWABLES, CURSORS AND HANDLES + +Each widget created by Emacs corresponds to a single ``window'', which +has its own backing store. This arrangement is quite similar to X. + +C code does not directly refer to the EmacsView widgets that implement +the UI logic behind windows. Instead, its handles refer to +EmacsWindow structures, which contain the state necessary to interact +with the widgets in an orderly and synchronized manner. + +Like X, both pixmaps and windows are drawable resources, and the same +graphics operations can be applied to both. Thus, a separate +EmacsPixmap structure is used to wrap around Android Bitmap resources, +and the Java-level graphics operation functions are capable of +operating on them both. + +Finally, graphics contexts are maintained on both the C and Java +levels; the C state recorded in `struct android_gc' is kept in sync +with the Java state in the GContext handle's corresponding EmacsGC +structure, and cursors are used through handles that refer to +EmacsCursor structures that hold system PointerIcons. + +In all cases, the interfaces provided are identical to X. + + + +EVENT LOOP + +In a typical Android application, the event loop is managed by the +operating system, and callbacks (implemented through overriding +separate functions in widgets) are run by the event loop wherever +necessary. The thread which runs the event loop is also the only +thread capable of creating and manipulating widgets and activities, +and is referred to as the ``UI thread''. + +These callbacks are used by Emacs to write representations of X-like +events to a separate event queue, which are then read from Emacs's own +event loop running in a separate thread. This is accomplished through +replacing `select' by a function which waits for the event queue to be +occupied, in addition to any file descriptors that `select' would +normally wait for. + +Conversely, Emacs's event loop sometimes needs to send events to the +UI thread. These events are implemented as tiny fragments of code, +which are run as they are received by the main thread. + +A typical example is `displayToast', which is implemented in +EmacsService.java: + + public void + displayToast (final String string) + { + runOnUiThread (new Runnable () { + @Override + public void + run () + { + Toast toast; + + toast = Toast.makeText (getApplicationContext (), + string, Toast.LENGTH_SHORT); + toast.show (); + } + }); + } + +Here, the variable `string' is used by a nested function. This nested +function contains a copy of that variable, and is run on the main +thread using the function `runOnUiThread', in order to display a short +status message on the display. + +When Emacs needs to wait for the nested function to finish, it uses a +mechanism implemented in `syncRunnable'. This mechanism first calls a +deadlock avoidance mechanism, then runs a nested function on the UI +thread, which is expected to signal itself as a condition variable +upon completion. It is typically used to allocate resources that can +only be allocated from the UI thread, or to obtain non-thread-safe +information. The following function is an example; it returns a new +EmacsView widget corresponding to the provided window: + + public EmacsView + getEmacsView (final EmacsWindow window, final int visibility, + final boolean isFocusedByDefault) + { + Runnable runnable; + final EmacsHolder view; + + view = new EmacsHolder (); + + runnable = new Runnable () { + public void + run () + { + synchronized (this) + { + view.thing = new EmacsView (window); + view.thing.setVisibility (visibility); + + /* The following function is only present on Android 26 + or later. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + view.thing.setFocusedByDefault (isFocusedByDefault); + + notify (); + } + } + }; + + syncRunnable (runnable); + return view.thing; + } + +As no value can be directly returned from the nested function, a +separate container object is used to hold the result after the +function finishes execution. Note the type name inside the angle +brackets: this type is substituted into the class definition as it is +used; a definition such as: + +public class Foo +{ + T bar; +}; + +can not be used alone: + + Foo holder; /* Error! */ + +but must have a type specified: + + Foo holder; + +in which case the effective definition is: + +public class Foo +{ + Object bar; +}; + + + +COMPATIBILITY + +There are three variables set within every Android application that +extert influence over the set of Android systems it supports, and the +measures it must take to function faithfully on each of those systems: +the minimum API level, compile SDK version and target API level. + +The minimum API level is the earliest version of Android that is +permitted to install and run the application. For Emacs, this is +established by detecting the __ANDROID_API__ preprocessor macro +defined within the Android C compiler. + +Before Java code executes any Android API calls that are not present +within Android 2.2 (API level 8), the lowest API level supported by +Emacs as a whole, it must first check the value of the: + + Build.VERSION.SDK_INT + +variable, which is always set to the API level of the system Emacs is +presently installed within. For example, before calling +`dispatchKeyEventFromInputMethod', a function absent from Android 6.0 +(API level 23) or earlier, check: + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + view.imManager.dispatchKeyEventFromInputMethod (view, key); + else + { + +where `N' is a constant defined to 24. + +The compile SDK version is the version of the Android SDK headers Java +code is compiled against. Because Java does not provide conditional +compilation constructs, Emacs can't be compiled with any version of +these headers other than the version mentioned in `java/INSTALL', but +the headers used do not affect the set of supported systems provided +that the version checks illustrated above are performed where +necessary. + +The target API level is a number within java/AndroidManifest.xml.in +the system refers to when deciding whether to enable +backwards-incompatible modifications to the behavior of various system +APIs. For any given Android version, backwards incompatible changes +in that version will be disabled for applications whose target API +levels don't exceed its own. + +The target API should nevertheless be updated to match every major +Android update, as Google has stated their intentions to prohibit +users from installing applications targeting ``out-of-date'' versions +of Android, though this threat has hitherto been made good on. + + + +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 . diff --git a/java/README b/java/README index e518e9fbb2f..a909cdd22ef 100644 --- a/java/README +++ b/java/README @@ -22,1027 +22,6 @@ 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 only some 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 may simply iterate 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 emacs_array_container[10]; - -Alas, Java has no equivalent of `typedef'. - -Like in C, Java string literals are delimited by double quotes. -Unlike C, however, strings are not NULL-terminated arrays of -characters, but a distinct type named ``String''. They store their -own length, characters in Java's 16-bit ``char'' type, and are capable -of holding NULL bytes. - -Instead of writing: - -wchar_t character; -extern char *s; -size_t s; - - for (/* determine n, s in a loop. */) - s += mbstowc (&character, s, n); - -or: - -const char *byte; - -for (byte = my_string; *byte; ++byte) - /* do something with *byte. */; - -or perhaps even: - -size_t length, i; -char foo; - -length = strlen (my_string); - -for (i = 0; i < length; ++i) - foo = my_string[i]; - -you write: - -char foo; -int i; - -for (i = 0; i < myString.length (); ++i) - foo = myString.charAt (0); - -Java also has stricter rules on what can be used as a truth value in a -conditional. While in C, any non-zero value is true, Java requires -that every truth value be of the boolean type ``boolean''. - -What this means is that instead of simply writing: - - if (foo || bar) - -where foo can either be 1 or 0, and bar can either be NULL or a -pointer to something, you must explicitly write: - - if (foo != 0 || bar != null) - -in Java. - -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"); - }; - -where ``static'' defines a section of code which will be run upon the -object (containing class) being loaded. This is like: - - __attribute__((constructor)) - -on systems where shared object constructors are supported. - -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: - - - - - - - - - -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, "", - "(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. -} - - - -DRAWABLES, CURSORS AND HANDLES - -Each widget created by Emacs corresponds to a single ``window'', which -has its own backing store. This arrangement is quite similar to X. - -C code does not directly refer to the EmacsView widgets that implement -the UI logic behind windows. Instead, its handles refer to -EmacsWindow structures, which contain the state necessary to interact -with the widgets in an orderly and synchronized manner. - -Like X, both pixmaps and windows are drawable resources, and the same -graphics operations can be applied to both. Thus, a separate -EmacsPixmap structure is used to wrap around Android Bitmap resources, -and the Java-level graphics operation functions are capable of -operating on them both. - -Finally, graphics contexts are maintained on both the C and Java -levels; the C state recorded in `struct android_gc' is kept in sync -with the Java state in the GContext handle's corresponding EmacsGC -structure, and cursors are used through handles that refer to -EmacsCursor structures that hold system PointerIcons. - -In all cases, the interfaces provided are identical to X. - - - -EVENT LOOP - -In a typical Android application, the event loop is managed by the -operating system, and callbacks (implemented through overriding -separate functions in widgets) are run by the event loop wherever -necessary. The thread which runs the event loop is also the only -thread capable of creating and manipulating widgets and activities, -and is referred to as the ``UI thread''. - -These callbacks are used by Emacs to write representations of X-like -events to a separate event queue, which are then read from Emacs's own -event loop running in a separate thread. This is accomplished through -replacing `select' by a function which waits for the event queue to be -occupied, in addition to any file descriptors that `select' would -normally wait for. - -Conversely, Emacs's event loop sometimes needs to send events to the -UI thread. These events are implemented as tiny fragments of code, -which are run as they are received by the main thread. - -A typical example is `displayToast', which is implemented in -EmacsService.java: - - public void - displayToast (final String string) - { - runOnUiThread (new Runnable () { - @Override - public void - run () - { - Toast toast; - - toast = Toast.makeText (getApplicationContext (), - string, Toast.LENGTH_SHORT); - toast.show (); - } - }); - } - -Here, the variable `string' is used by a nested function. This nested -function contains a copy of that variable, and is run on the main -thread using the function `runOnUiThread', in order to display a short -status message on the display. - -When Emacs needs to wait for the nested function to finish, it uses a -mechanism implemented in `syncRunnable'. This mechanism first calls a -deadlock avoidance mechanism, then runs a nested function on the UI -thread, which is expected to signal itself as a condition variable -upon completion. It is typically used to allocate resources that can -only be allocated from the UI thread, or to obtain non-thread-safe -information. The following function is an example; it returns a new -EmacsView widget corresponding to the provided window: - - public EmacsView - getEmacsView (final EmacsWindow window, final int visibility, - final boolean isFocusedByDefault) - { - Runnable runnable; - final EmacsHolder view; - - view = new EmacsHolder (); - - runnable = new Runnable () { - public void - run () - { - synchronized (this) - { - view.thing = new EmacsView (window); - view.thing.setVisibility (visibility); - - /* The following function is only present on Android 26 - or later. */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - view.thing.setFocusedByDefault (isFocusedByDefault); - - notify (); - } - } - }; - - syncRunnable (runnable); - return view.thing; - } - -As no value can be directly returned from the nested function, a -separate container object is used to hold the result after the -function finishes execution. Note the type name inside the angle -brackets: this type is substituted into the class definition as it is -used; a definition such as: - -public class Foo -{ - T bar; -}; - -can not be used alone: - - Foo holder; /* Error! */ - -but must have a type specified: - - Foo holder; - -in which case the effective definition is: - -public class Foo -{ - Object bar; -}; +Refer to the file `admin/notes/java' in the toplevel directory of the +Emacs distribution or repository for specifics regarding writing Java +code for Emacs and the organization of the Android port.