summaryrefslogtreecommitdiff
path: root/java/org/gnu/emacs/EmacsService.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/org/gnu/emacs/EmacsService.java')
-rw-r--r--java/org/gnu/emacs/EmacsService.java384
1 files changed, 384 insertions, 0 deletions
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java
new file mode 100644
index 00000000000..311226e6f7e
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -0,0 +1,384 @@
+/* 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.lang.Runnable;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+
+import android.graphics.Canvas;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+
+import android.annotation.TargetApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.AssetManager;
+import android.os.Build;
+import android.os.Looper;
+import android.os.IBinder;
+import android.os.Handler;
+import android.util.Log;
+
+class Holder<T>
+{
+ T thing;
+};
+
+/* EmacsService is the service that starts the thread running Emacs
+ and handles requests by that Emacs instance. */
+
+public class EmacsService extends Service
+{
+ public static final String TAG = "EmacsService";
+ public static final int MAX_PENDING_REQUESTS = 256;
+ public static volatile EmacsService SERVICE;
+
+ private EmacsThread thread;
+ private Handler handler;
+ private EmacsPaintQueue paintQueue;
+
+ /* List of all EmacsWindows that are available to attach to an
+ activity. */
+ public static List<EmacsWindow> availableChildren;
+
+ static
+ {
+ availableChildren = new ArrayList<EmacsWindow> ();
+ };
+
+ @Override
+ public int
+ onStartCommand (Intent intent, int flags, int startId)
+ {
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public IBinder
+ onBind (Intent intent)
+ {
+ return null;
+ }
+
+ @TargetApi (Build.VERSION_CODES.GINGERBREAD)
+ String
+ getLibraryDirectory ()
+ {
+ int apiLevel;
+ Context context;
+
+ context = getApplicationContext ();
+ apiLevel = android.os.Build.VERSION.SDK_INT;
+
+ if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
+ return context.getApplicationInfo().nativeLibraryDir;
+ else if (apiLevel >= Build.VERSION_CODES.DONUT)
+ return context.getApplicationInfo().dataDir + "/lib";
+
+ return "/data/data/" + context.getPackageName() + "/lib";
+ }
+
+ @Override
+ public void
+ onCreate ()
+ {
+ AssetManager manager;
+ Context app_context;
+ String filesDir, libDir;
+
+ SERVICE = this;
+ handler = new Handler (Looper.getMainLooper ());
+ manager = getAssets ();
+ app_context = getApplicationContext ();
+
+ try
+ {
+ /* Configure Emacs with the asset manager and other necessary
+ parameters. */
+ filesDir = app_context.getFilesDir ().getCanonicalPath ();
+ libDir = getLibraryDirectory ();
+
+ Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
+ + " and libDir = " + libDir);
+
+ EmacsNative.setEmacsParams (manager, filesDir, libDir,
+ this);
+
+ /* Start the thread that runs Emacs. */
+ thread = new EmacsThread (this);
+ thread.start ();
+ }
+ catch (IOException exception)
+ {
+ EmacsNative.emacsAbort ();
+ return;
+ }
+ }
+
+
+
+ /* Functions from here on must only be called from the Emacs
+ thread. */
+
+ void
+ runOnUiThread (Runnable runnable)
+ {
+ handler.post (runnable);
+ }
+
+ EmacsView
+ getEmacsView (final EmacsWindow window)
+ {
+ Runnable runnable;
+ final Holder<EmacsView> view;
+
+ view = new Holder<EmacsView> ();
+
+ runnable = new Runnable () {
+ public void
+ run ()
+ {
+ synchronized (this)
+ {
+ view.thing = new EmacsView (window);
+ notify ();
+ }
+ }
+ };
+
+ synchronized (runnable)
+ {
+ runOnUiThread (runnable);
+
+ try
+ {
+ runnable.wait ();
+ }
+ catch (InterruptedException e)
+ {
+ EmacsNative.emacsAbort ();
+ }
+ }
+
+ return view.thing;
+ }
+
+ /* Notice that a child of the root window named WINDOW is now
+ available for attachment to a specific activity. */
+
+ public void
+ noticeAvailableChild (final EmacsWindow window)
+ {
+ Log.d (TAG, "A new child is available: " + window);
+
+ handler.post (new Runnable () {
+ public void
+ run ()
+ {
+ for (EmacsActivity activity
+ : EmacsActivity.availableActivities)
+ {
+ /* TODO: check if the activity matches. */
+ activity.attachChild (window);
+ break;
+ }
+
+ /* Nope, wait for an activity to become available. */
+ availableChildren.add (window);
+ }
+ });
+ }
+
+ /* Notice that a child of the root window named WINDOW has been
+ destroyed. */
+
+ public void
+ noticeChildDestroyed (final EmacsWindow child)
+ {
+ handler.post (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ availableChildren.remove (child);
+ }
+ });
+ }
+
+ /* X drawing operations. These are quite primitive operations. The
+ drawing queue is kept on the Emacs thread, but is periodically
+ flushed to the application thread, upon buffers swaps and once it
+ gets too big. */
+
+
+
+ private void
+ ensurePaintQueue ()
+ {
+ if (paintQueue == null)
+ paintQueue = new EmacsPaintQueue ();
+ }
+
+ public void
+ flushPaintQueue ()
+ {
+ final EmacsPaintQueue queue;
+
+ if (paintQueue == null)
+ return;
+
+ if (paintQueue.numRequests < 1)
+ /* No requests to flush. */
+ return;
+
+ queue = paintQueue;
+
+ handler.post (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ queue.run ();
+ }
+ });
+
+ /* Clear the paint queue. */
+ paintQueue = null;
+ }
+
+ private void
+ checkFlush ()
+ {
+ if (paintQueue != null
+ && paintQueue.numRequests > MAX_PENDING_REQUESTS)
+ flushPaintQueue ();
+ }
+
+ public void
+ fillRectangle (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y, int width, int height)
+ {
+ EmacsPaintReq req;
+
+ ensurePaintQueue ();
+
+ req = new EmacsFillRectangle (drawable, x, y,
+ width, height,
+ gc.immutableGC ());
+ paintQueue.appendPaintOperation (req);
+ checkFlush ();
+ }
+
+ public void
+ fillPolygon (EmacsDrawable drawable, EmacsGC gc,
+ Point points[])
+ {
+ EmacsPaintReq req;
+
+ ensurePaintQueue ();
+
+ req = new EmacsFillPolygon (drawable, points,
+ gc.immutableGC ());
+ paintQueue.appendPaintOperation (req);
+ checkFlush ();
+ }
+
+ public void
+ drawRectangle (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y, int width, int height)
+ {
+ EmacsPaintReq req;
+
+ ensurePaintQueue ();
+
+ if (gc.clip_rects != null && gc.clip_rects.length >= 1)
+ android.util.Log.d ("drawRectangle",
+ gc.clip_rects[0].toString ()
+ + " " + gc.toString ());
+
+ req = new EmacsDrawRectangle (drawable, x, y,
+ width, height,
+ gc.immutableGC ());
+ paintQueue.appendPaintOperation (req);
+ checkFlush ();
+ }
+
+ public void
+ drawLine (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y, int x2, int y2)
+ {
+ EmacsPaintReq req;
+
+ ensurePaintQueue ();
+
+ req = new EmacsDrawLine (drawable, x, y,
+ x2, y2,
+ gc.immutableGC ());
+ paintQueue.appendPaintOperation (req);
+ checkFlush ();
+ }
+
+ public void
+ drawPoint (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y)
+ {
+ EmacsPaintReq req;
+
+ ensurePaintQueue ();
+
+ req = new EmacsDrawPoint (drawable, x, y,
+ gc.immutableGC ());
+ paintQueue.appendPaintOperation (req);
+ checkFlush ();
+ }
+
+ public void
+ copyArea (EmacsDrawable srcDrawable, EmacsDrawable dstDrawable,
+ EmacsGC gc,
+ int srcX, int srcY, int width, int height, int destX,
+ int destY)
+ {
+ EmacsPaintReq req;
+
+ ensurePaintQueue ();
+
+ req = new EmacsCopyArea (srcDrawable, dstDrawable,
+ srcX, srcY, width, height, destX,
+ destY, gc.immutableGC ());
+ paintQueue.appendPaintOperation (req);
+ checkFlush ();
+ }
+
+ public void
+ clearWindow (EmacsWindow window)
+ {
+ window.clearWindow ();
+ }
+
+ public void
+ clearArea (EmacsWindow window, int x, int y, int width,
+ int height)
+ {
+ window.clearArea (x, y, width, height);
+ }
+};