ref: 9620904ebb8cf2eea22a1a81cb9bb5274dda710d
parent: a130d441722ac3f759d2d83b98eb6aef7e84f97e
author: Lorenzo Bivens <git@lorenzobivens.info>
date: Fri Aug 13 23:39:50 EDT 2021
Merging echoline's android and fbdev forks
--- /dev/null
+++ b/Make.android
@@ -1,0 +1,38 @@
+# Android
+SDKPREFIX=$(HOME)/Android/Sdk
+JAVA_HOME=/usr
+OBJS=lib/arm64-v8a/libdrawterm.so lib/armeabi-v7a/libdrawterm.so lib/x86/libdrawterm.so lib/x86_64/libdrawterm.so
+
+all: drawterm.apk
+
+clean:
+ rm -f *.apk lib/*/*.so
+
+lib/arm64-v8a/libdrawterm.so:
+ CONF=android-arm64 make -j5;
+ CONF=android-arm64 make clean;
+
+lib/armeabi-v7a/libdrawterm.so:
+ CONF=android-arm make -j5;
+ CONF=android-arm make clean;
+
+lib/x86/libdrawterm.so:
+ CONF=android-386 make -j5;
+ CONF=android-386 make clean;
+
+lib/x86_64/libdrawterm.so:
+ CONF=android-amd64 make -j5;
+ CONF=android-amd64 make clean;
+
+drawterm.apk: drawterm-signed.apk
+ $(SDKPREFIX)/build-tools/30.0.3/zipalign -v -f 4 $< $@
+
+drawterm-signed.apk: drawterm-unsigned.apk drawterm.keystore
+ $(JAVA_HOME)/bin/jarsigner -verbose -keystore ./drawterm.keystore -storepass glendarocks -keypass glendarocks -signedjar $@ $< drawtermKey
+
+drawterm-unsigned.apk: $(OBJS)
+ $(SDKPREFIX)/build-tools/30.0.3/aapt package -v -f -M gui-android/AndroidManifest.xml -S gui-android/res -I $(SDKPREFIX)/platforms/android-21/android.jar -F $@ gui-android/bin
+ $(SDKPREFIX)/build-tools/30.0.3/aapt add $@ $(OBJS)
+
+drawterm.keystore:
+ $(JAVA_HOME)/bin/keytool -genkeypair -validity 1000 -dname "CN=9front,O=Android,C=US" -keystore $@ -storepass glendarocks -keypass glendarocks -alias drawtermKey -keyalg RSA -v
--- /dev/null
+++ b/Make.android-386
@@ -1,0 +1,26 @@
+# Android
+SDKPREFIX=$(HOME)/Android/Sdk
+NDKPREFIX=$(SDKPREFIX)/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin
+JAVA_HOME=/usr
+
+PTHREAD=-pthread
+AR=$(NDKPREFIX)/i686-linux-android-ar
+AS=$(NDKPREFIX)/i686-linux-android-as
+RANLIB=$(NDKPREFIX)/i686-linux-android-ranlib
+STRIP=$(NDKPREFIX)/i686-linux-android-strip
+CC=$(NDKPREFIX)/i686-linux-android21-clang
+CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -Dmain=dt_main -fPIC
+O=o
+OS=posix
+GUI=android
+LDADD=-ggdb -lm -shared -llog -landroid
+LDFLAGS=$(PTHREAD)
+TARG=lib/x86/libdrawterm.so
+AUDIO=none
+
+all: default
+
+libmachdep.a:
+ arch=386; \
+ (cd posix-$$arch && make)
+
--- /dev/null
+++ b/Make.android-amd64
@@ -1,0 +1,26 @@
+# Android
+SDKPREFIX=$(HOME)/Android/Sdk
+NDKPREFIX=$(SDKPREFIX)/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin
+JAVA_HOME=/usr
+
+PTHREAD=-pthread
+AR=$(NDKPREFIX)/x86_64-linux-android-ar
+AS=$(NDKPREFIX)/x86_64-linux-android-as
+RANLIB=$(NDKPREFIX)/x86_64-linux-android-ranlib
+STRIP=$(NDKPREFIX)/x86_64-linux-android-strip
+CC=$(NDKPREFIX)/x86_64-linux-android21-clang
+CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -Dmain=dt_main -fPIC
+O=o
+OS=posix
+GUI=android
+LDADD=-ggdb -lm -shared -llog -landroid
+LDFLAGS=$(PTHREAD)
+TARG=lib/x86_64/libdrawterm.so
+AUDIO=none
+
+all: default
+
+libmachdep.a:
+ arch=amd64; \
+ (cd posix-$$arch && make)
+
--- /dev/null
+++ b/Make.android-arm
@@ -1,0 +1,26 @@
+# Android
+SDKPREFIX=$(HOME)/Android/Sdk
+NDKPREFIX=$(SDKPREFIX)/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin
+JAVA_HOME=/usr
+
+PTHREAD=-pthread
+AR=$(NDKPREFIX)/arm-linux-androideabi-ar
+AS=$(NDKPREFIX)/arm-linux-androideabi-as
+RANLIB=$(NDKPREFIX)/arm-linux-androideabi-ranlib
+STRIP=$(NDKPREFIX)/arm-linux-androideabi-strip
+CC=$(NDKPREFIX)/armv7a-linux-androideabi21-clang
+CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -Dmain=dt_main -fPIC
+O=o
+OS=posix
+GUI=android
+LDADD=-ggdb -lm -shared -llog -landroid
+LDFLAGS=$(PTHREAD)
+TARG=lib/armeabi-v7a/libdrawterm.so
+AUDIO=none
+
+all: default
+
+libmachdep.a:
+ arch=arm; \
+ (cd posix-$$arch && make)
+
--- /dev/null
+++ b/Make.android-arm64
@@ -1,0 +1,26 @@
+# Android
+SDKPREFIX=$(HOME)/Android/Sdk
+NDKPREFIX=$(SDKPREFIX)/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin
+JAVA_HOME=/usr
+
+PTHREAD=-pthread
+AR=$(NDKPREFIX)/aarch64-linux-android-ar
+AS=$(NDKPREFIX)/aarch64-linux-android-as
+RANLIB=$(NDKPREFIX)/aarch64-linux-android-ranlib
+STRIP=$(NDKPREFIX)/aarch64-linux-android-strip
+CC=$(NDKPREFIX)/aarch64-linux-android21-clang
+CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -Dmain=dt_main -fPIC
+O=o
+OS=posix
+GUI=android
+LDADD=-ggdb -lm -shared -llog -landroid
+LDFLAGS=$(PTHREAD)
+TARG=lib/arm64-v8a/libdrawterm.so
+AUDIO=none
+
+all: default
+
+libmachdep.a:
+ arch=arm64; \
+ (cd posix-$$arch && make)
+
--- /dev/null
+++ b/Make.fbdev
@@ -1,0 +1,22 @@
+# Unix
+#PTHREAD= # for Mac
+PTHREAD=-pthread
+AR=ar
+AS=as
+RANLIB=ranlib
+CC=gcc
+CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -D_THREAD_SAFE $(PTHREAD) -O2
+O=o
+OS=posix
+GUI=fbdev
+LDADD=-ggdb -lm -lasound
+LDFLAGS=$(PTHREAD)
+TARG=drawterm
+# AUDIO=none
+AUDIO=alsa
+
+all: default
+
+libmachdep.a:
+ arch=`uname -m|sed 's/i.86/386/;s/Power Macintosh/power/; s/x86_64/amd64/; s/armv[567].*/arm/; s/aarch64/arm64/'`; \
+ (cd posix-$$arch && make)
--- a/README
+++ b/README
@@ -18,6 +18,17 @@
To build on Mac OS X with Cocoa, run CONF=osx-cocoa make and "cp drawterm gui-cocoa/drawterm.app/".
+To build for Android, make sure Make.android* and gui-android/Makefile are correct for your build and target systems, then run make -f Make.android
+
+USAGE
+-------
+On Android the five checkboxes at the top represent the three mouse buttons and mousewheel, determining which "buttons" are clicked. The "kb" button toggles the soft keyboard.
+
+
+CAVEATS
+--------
+Be aware that right now on Android the login details are saved as a plaintext string if saved, and there is no secstore support.
+
BINARIES
---------
--- /dev/null
+++ b/gui-android/AndroidManifest.xml
@@ -1,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.echoline.drawterm">
+
+ <supports-screens android:largeScreens="true"
+ android:normalScreens="true" android:smallScreens="true"
+ android:anyDensity="true" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:windowSoftInputMode="stateUnchanged|adjustNothing">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!--<uses-permission android:name="android.permission.SET_DEBUG_APP"/>-->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
+</manifest>
--- /dev/null
+++ b/gui-android/Makefile
@@ -1,0 +1,23 @@
+ROOT=..
+include ../Make.config
+LIB=libgui.a
+
+OFILES=\
+ cpp/android.$O\
+ cpp/native-lib.$O\
+ cpp/devandroid.$O\
+
+default: $(LIB) gen/org/echoline/drawterm/R.java bin/classes.dex
+$(LIB): $(OFILES)
+ $(AR) r $(LIB) $(OFILES)
+ $(RANLIB) $(LIB)
+
+gen/org/echoline/drawterm/R.java: $(shell find res/ -type f)
+ $(SDKPREFIX)/build-tools/30.0.3/aapt package -f -m -M AndroidManifest.xml -I $(SDKPREFIX)/platforms/android-21/android.jar -S res/ -J gen
+
+bin/classes.dex: obj/org/echoline/drawterm/MainActivity.class obj/org/echoline/drawterm/DrawTermThread.class obj/org/echoline/drawterm/MySurfaceView.class
+ $(SDKPREFIX)/build-tools/30.0.3/dx --dex --verbose --output=$@ obj/
+
+obj/org/echoline/drawterm/%.class: java/org/echoline/drawterm/%.java
+ $(JAVA_HOME)/bin/javac -d obj/ -classpath $(SDKPREFIX)/platforms/android-21/android.jar -sourcepath java java/org/echoline/drawterm/$*.java gen/org/echoline/drawterm/R.java
+
--- /dev/null
+++ b/gui-android/cpp/android.c
@@ -1,0 +1,230 @@
+#include <jni.h>
+#include <android/native_window.h>
+#include <android/log.h>
+
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <keyboard.h>
+#include <cursor.h>
+#include "screen.h"
+
+Memimage *gscreen = nil;
+extern int screenWidth;
+extern int screenHeight;
+extern ANativeWindow *window;
+extern jobject mainActivityObj;
+extern JavaVM *jvm;
+
+char*
+clipread(void)
+{
+ char *ret;
+ const char *s;
+ JNIEnv *env;
+ jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
+ if (rs != JNI_OK) {
+ __android_log_print(ANDROID_LOG_WARN, "drawterm", "AttachCurrentThread returned error: %d", rs);
+ return strdup("");
+ }
+ jclass clazz = (*env)->GetObjectClass(env, mainActivityObj);
+ jmethodID methodID = (*env)->GetMethodID(env, clazz, "getClipBoard", "()Ljava/lang/String;");
+ jstring str = (jstring)(*env)->CallObjectMethod(env, mainActivityObj, methodID);
+ s = (*env)->GetStringUTFChars(env, str, NULL);
+ ret = strdup(s);
+ (*env)->ReleaseStringUTFChars(env, str, s);
+ (*jvm)->DetachCurrentThread(jvm);
+ return ret;
+}
+
+int
+clipwrite(char *buf)
+{
+ JNIEnv *env;
+ jint rs = (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_6);
+ if(rs != JNI_OK) {
+ __android_log_print(ANDROID_LOG_WARN, "drawterm", "GetEnv returned error: %d", rs);
+ return 0;
+ }
+ jclass clazz = (*env)->GetObjectClass(env, mainActivityObj);
+ jmethodID methodID = (*env)->GetMethodID(env, clazz, "setClipBoard", "(Ljava/lang/String;)V");
+ jstring str = (*env)->NewStringUTF(env, buf);
+ (*env)->CallVoidMethod(env, mainActivityObj, methodID, str);
+ return 0;
+}
+
+void
+show_notification(char *buf)
+{
+ JNIEnv *env;
+ jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
+ if(rs != JNI_OK) {
+ __android_log_print(ANDROID_LOG_WARN, "drawterm", "AttachCurrentThread returned error: %d", rs);
+ return;
+ }
+ jclass clazz = (*env)->GetObjectClass(env, mainActivityObj);
+ jmethodID methodID = (*env)->GetMethodID(env, clazz, "showNotification", "(Ljava/lang/String;)V");
+ jstring str = (*env)->NewStringUTF(env, buf);
+ (*env)->CallVoidMethod(env, mainActivityObj, methodID, str);
+ (*jvm)->DetachCurrentThread(jvm);
+ return;
+}
+
+int
+num_cameras()
+{
+ JNIEnv *env;
+ int n;
+ jint rs = (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_6);
+ if(rs != JNI_OK) {
+ __android_log_print(ANDROID_LOG_WARN, "drawterm", "GetEnv returned error: %d", rs);
+ return 0;
+ }
+ jclass clazz = (*env)->GetObjectClass(env, mainActivityObj);
+ jmethodID methodID = (*env)->GetMethodID(env, clazz, "numCameras", "()I");
+ n = (*env)->CallIntMethod(env, mainActivityObj, methodID);
+ return n;
+}
+
+void
+take_picture(int id)
+{
+ JNIEnv *env;
+ jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
+ if(rs != JNI_OK) {
+ __android_log_print(ANDROID_LOG_WARN, "drawterm", "AttachCurrentThread returned error: %d", rs);
+ return;
+ }
+ jclass clazz = (*env)->GetObjectClass(env, mainActivityObj);
+ jmethodID methodID = (*env)->GetMethodID(env, clazz, "takePicture", "(I)V");
+ (*env)->CallVoidMethod(env, mainActivityObj, methodID, id);
+ (*jvm)->DetachCurrentThread(jvm);
+ return;
+}
+
+void
+setcolor(ulong i, ulong r, ulong g, ulong b)
+{
+ return;
+}
+
+void
+getcolor(ulong v, ulong *r, ulong *g, ulong *b)
+{
+ *r = (v>>16)&0xFF;
+ *g = (v>>8)&0xFF;
+ *b = v&0xFF;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ ANativeWindow_Buffer buffer;
+ uint8_t *pixels;
+ int x, y, o, b;
+ ARect bounds;
+
+ if (window == NULL)
+ return;
+
+ memset(&buffer, 0, sizeof(buffer));
+
+ bounds.left = r.min.x;
+ bounds.top = r.min.y;
+ bounds.right = r.max.x;
+ bounds.bottom = r.max.y;
+
+ if (ANativeWindow_lock(window, &buffer, &bounds) != 0) {
+ __android_log_print(ANDROID_LOG_WARN, "drawterm", "Unable to lock window buffer");
+ return;
+ }
+
+ r.min.x = bounds.left;
+ r.min.y = bounds.top;
+ r.max.x = bounds.right;
+ r.max.y = bounds.bottom;
+
+ pixels = (uint8_t*)buffer.bits;
+ for (y = r.min.y; y < r.max.y; y++)
+ for (x = r.min.x; x < r.max.x; x++) {
+ o = (y * screenWidth + x) * 4;
+ b = (y * buffer.stride + x) * 4;
+ pixels[b+3] = 0xFF;
+ pixels[b+2] = gscreen->data->bdata[o+0];
+ pixels[b+1] = gscreen->data->bdata[o+1];
+ pixels[b+0] = gscreen->data->bdata[o+2];
+ }
+
+ if (ANativeWindow_unlockAndPost(window) != 0) {
+ __android_log_print(ANDROID_LOG_WARN, "drawterm", "Unable to unlock and post window buffer");
+ }
+ return;
+}
+
+void
+screeninit(void)
+{
+ Rectangle r = Rect(0,0,screenWidth,screenHeight);
+ memimageinit();
+ screensize(r, XRGB32);
+ if (gscreen == nil)
+ panic("screensize failed");
+ gscreen->clipr = r;
+ terminit();
+ qlock(&drawlock);
+ flushmemscreen(r);
+ qunlock(&drawlock);
+ return;
+}
+
+void
+screensize(Rectangle r, ulong chan)
+{
+ Memimage *mi;
+
+ mi = allocmemimage(r, chan);
+ if (mi == nil)
+ return;
+
+ if (gscreen != nil)
+ freememimage(gscreen);
+
+ gscreen = mi;
+ gscreen->clipr = ZR;
+}
+
+Memdata*
+attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
+{
+ *r = gscreen->clipr;
+ *depth = gscreen->depth;
+ *chan = gscreen->chan;
+ *width = gscreen->width;
+ *softscreen = 1;
+
+ gscreen->data->ref++;
+ return gscreen->data;
+}
+
+void
+setcursor(void)
+{
+ return;
+}
+
+void
+mouseset(Point xy)
+{
+ return;
+}
+
+void
+guimain(void)
+{
+ cpubody();
+}
+
--- /dev/null
+++ b/gui-android/cpp/devandroid.c
@@ -1,0 +1,248 @@
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <android/log.h>
+#include <android/sensor.h>
+
+void show_notification(char *buf);
+void take_picture(int id);
+int num_cameras();
+
+int Ncameras = 0;
+
+uchar *cambuf = nil;
+int camlen;
+
+ASensorManager *sensorManager = NULL;
+
+enum
+{
+ Qdir = 0,
+ Qcam = 1,
+ Qaccel = 2,
+ Qcompass = 4,
+ Qnotification = 6,
+};
+#define QID(p, c, y) (((p)<<16) | ((c)<<4) | (y))
+
+static void androidinit(void);
+
+static void
+androidinit(void)
+{
+ sensorManager = ASensorManager_getInstance();
+
+ Ncameras = num_cameras();
+}
+
+static Chan*
+androidattach(char *param)
+{
+ Chan *c;
+
+ c = devattach('N', param);
+ c->qid.path = QID(0, 0, Qdir);
+ c->qid.type = QTDIR;
+ c->qid.vers = 0;
+
+ return c;
+}
+
+static int
+androidgen(Chan *c, char *n, Dirtab *d, int nd, int s, Dir *dp)
+{
+ Qid q;
+
+ if (s == DEVDOTDOT) {
+ mkqid(&q, Qdir, 0, QTDIR);
+ devdir(c, q, "#N", 0, eve, 0555, dp);
+ return 1;
+ }
+ if (s < Ncameras) {
+ sprintf(up->genbuf, "cam%d.jpg", s);
+ mkqid(&q, (s << 16) | Qcam, 0, QTFILE);
+ devdir(c, q, up->genbuf, 0, eve, 0444, dp);
+ return 1;
+ }
+ if (s == Ncameras) {
+ sprintf(up->genbuf, "accel");
+ mkqid(&q, Qaccel, 0, QTFILE);
+ devdir(c, q, up->genbuf, 0, eve, 0444, dp);
+ return 1;
+ }
+ if (s == (Ncameras+1)) {
+ sprintf(up->genbuf, "compass");
+ mkqid(&q, Qcompass, 0, QTFILE);
+ devdir(c, q, up->genbuf, 0, eve, 0444, dp);
+ return 1;
+ }
+ if (s == (Ncameras+2)) {
+ sprintf(up->genbuf, "notification");
+ mkqid(&q, Qnotification, 0, QTFILE);
+ devdir(c, q, up->genbuf, 0, eve, 0222, dp);
+ return 1;
+ }
+ return -1;
+}
+
+static Walkqid*
+androidwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, androidgen);
+}
+
+static int
+androidstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, androidgen);
+}
+
+static Chan*
+androidopen(Chan *c, int omode)
+{
+ p9_uvlong s;
+
+ c = devopen(c, omode, 0, 0, androidgen);
+
+ if (c->qid.path & Qcam) {
+ s = c->qid.path >> 16;
+ take_picture(s);
+ }
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ c->iounit = 8192;
+
+ return c;
+}
+
+static void
+androidclose(Chan *c)
+{
+ if (c->qid.path & Qcam && cambuf != nil) {
+ free(cambuf);
+ cambuf = nil;
+ }
+}
+
+static long
+androidread(Chan *c, void *v, long n, vlong off)
+{
+ char *a = v;
+ long l;
+ const ASensor *sensor;
+ ASensorEventQueue *queue = NULL;
+ ASensorEvent data;
+
+ switch((ulong)c->qid.path & 0xF) {
+ default:
+ error(Eperm);
+ return -1;
+
+ case Qcam:
+ while(cambuf == nil)
+ usleep(10 * 1000);
+
+ l = camlen - off;
+ if (l > n)
+ l = n;
+
+ if (l > 0)
+ memcpy(a, cambuf + off, l);
+
+ return l;
+ case Qaccel:
+ queue = ASensorManager_createEventQueue(sensorManager, ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS), 1, NULL, NULL);
+ if (queue == NULL)
+ return 0;
+ sensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
+ if (sensor == NULL) {
+ ASensorManager_destroyEventQueue(sensorManager, queue);
+ return 0;
+ }
+ if (ASensorEventQueue_enableSensor(queue, sensor)) {
+ ASensorEventQueue_disableSensor(queue, sensor);
+ ASensorManager_destroyEventQueue(sensorManager, queue);
+ return 0;
+ }
+ l = 0;
+ if (ALooper_pollAll(1000, NULL, NULL, NULL) == 1) {
+ if (ASensorEventQueue_getEvents(queue, &data, 1)) {
+ l = snprint(a, n, "%11f %11f %11f\n", data.vector.x, data.vector.y, data.vector.z);
+ }
+ }
+ ASensorEventQueue_disableSensor(queue, sensor);
+ ASensorManager_destroyEventQueue(sensorManager, queue);
+ return l;
+ case Qcompass:
+ queue = ASensorManager_createEventQueue(sensorManager, ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS), 1, NULL, NULL);
+ if (queue == NULL)
+ return 0;
+ sensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_MAGNETIC_FIELD);
+ if (sensor == NULL) {
+ ASensorManager_destroyEventQueue(sensorManager, queue);
+ return 0;
+ }
+ if (ASensorEventQueue_enableSensor(queue, sensor)) {
+ ASensorEventQueue_disableSensor(queue, sensor);
+ ASensorManager_destroyEventQueue(sensorManager, queue);
+ return 0;
+ }
+ l = 0;
+ if (ALooper_pollAll(1000, NULL, NULL, NULL) == 1) {
+ if (ASensorEventQueue_getEvents(queue, &data, 1)) {
+ l = snprint(a, n, "%11f %11f %11f\n", data.vector.x, data.vector.y, data.vector.z);
+ }
+ }
+ ASensorEventQueue_disableSensor(queue, sensor);
+ ASensorManager_destroyEventQueue(sensorManager, queue);
+ return l;
+ case Qdir:
+ return devdirread(c, a, n, 0, 0, androidgen);
+ }
+}
+
+static long
+androidwrite(Chan *c, void *vp, long n, vlong off)
+{
+ char *a = vp;
+ char *str;
+
+ switch((ulong)c->qid.path) {
+ case Qnotification:
+ str = malloc(n+1);
+ memcpy(str, a, n);
+ str[n] = '\0';
+ show_notification(str);
+ free(str);
+ return n;
+ default:
+ error(Eperm);
+ break;
+ }
+ return -1;
+}
+
+Dev androiddevtab = {
+ 'N',
+ "android",
+
+ devreset,
+ androidinit,
+ devshutdown,
+ androidattach,
+ androidwalk,
+ androidstat,
+ androidopen,
+ devcreate,
+ androidclose,
+ androidread,
+ devbread,
+ androidwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/gui-android/cpp/native-lib.c
@@ -1,0 +1,173 @@
+#include <jni.h>
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+#include <android/log.h>
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include <draw.h>
+#include <string.h>
+#include <keyboard.h>
+
+void absmousetrack(int, int, int, ulong);
+ulong ticks(void);
+int dt_main(int, char**);
+int screenWidth;
+int screenHeight;
+Point mousept = {0, 0};
+int buttons = 0;
+float ws = 1;
+float hs = 1;
+extern char *snarfbuf;
+int mPaused = 0;
+ANativeWindow *window = NULL;
+jobject mainActivityObj;
+JavaVM *jvm;
+void flushmemscreen(Rectangle r);
+extern uchar *cambuf;
+extern int camlen;
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_setObject(
+ JNIEnv *env,
+ jobject obj) {
+ mainActivityObj = (*env)->NewGlobalRef(env, obj);
+ jint rs = (*env)->GetJavaVM(env, &jvm);
+ assert(rs == JNI_OK);
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_keyDown(
+ JNIEnv *env,
+ jobject obj,
+ jint c) {
+ kbdkey(c, 1);
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_keyUp(
+ JNIEnv *env,
+ jobject obj,
+ jint c) {
+ kbdkey(c, 0);
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_setPass(
+ JNIEnv *env,
+ jobject obj,
+ jstring str) {
+ setenv("PASS", (char*)(*env)->GetStringUTFChars(env, str, 0), 1);
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_setWidth(
+ JNIEnv *env,
+ jobject obj,
+ jint width) {
+ screenWidth = width;
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_setHeight(
+ JNIEnv *env,
+ jobject obj,
+ jint height) {
+ screenHeight = height;
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_setWidthScale(
+ JNIEnv *env,
+ jobject obj,
+ jfloat s) {
+ ws = s;
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_setHeightScale(
+ JNIEnv *env,
+ jobject obj,
+ jfloat s) {
+ hs = s;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_echoline_drawterm_MainActivity_dtmain(
+ JNIEnv *env,
+ jobject obj,
+ jobjectArray argv) {
+ int i, ret;
+ char **args = (char **) malloc(((*env)->GetArrayLength(env, argv)+1) * sizeof(char *));
+
+ for (i = 0; i < (*env)->GetArrayLength(env, argv); i++) {
+ jobject str = (jobject) (*env)->GetObjectArrayElement(env, argv, i);
+ args[i] = strdup((char*)(*env)->GetStringUTFChars(env, (jstring)str, 0));
+ }
+ args[(*env)->GetArrayLength(env, argv)] = NULL;
+
+ ret = dt_main(i, args);
+
+ for (i = 0; args[i] != NULL; i++) {
+ free(args[i]);
+ }
+ free(args);
+
+ return ret;
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_setMouse(
+ JNIEnv *env,
+ jobject obj,
+ jintArray args) {
+ jboolean isCopy;
+ jint *data;
+ if ((*env)->GetArrayLength(env, args) < 3)
+ return;
+ data = (*env)->GetIntArrayElements(env, args, &isCopy);
+ mousept.x = (int)(data[0] / ws);
+ mousept.y = (int)(data[1] / hs);
+ buttons = data[2];
+ (*env)->ReleaseIntArrayElements(env, args, data, 0);
+ absmousetrack(mousept.x, mousept.y, buttons, ticks());
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_setDTSurface(
+ JNIEnv* jenv,
+ jobject obj,
+ jobject surface) {
+ if (surface != NULL) {
+ window = ANativeWindow_fromSurface(jenv, surface);
+ ANativeWindow_setBuffersGeometry(window, screenWidth, screenHeight,
+ AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM);
+ flushmemscreen(Rect(0, 0, screenWidth, screenHeight));
+ } else if (window != NULL) {
+ ANativeWindow_release(window);
+ window = NULL;
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_exitDT(
+ JNIEnv* jenv,
+ jobject obj) {
+ exit(0);
+}
+
+JNIEXPORT void JNICALL
+Java_org_echoline_drawterm_MainActivity_sendPicture(
+ JNIEnv* env,
+ jobject obj,
+ jbyteArray array) {
+ jint len = (*env)->GetArrayLength(env, array);
+ jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL);
+ camlen = len;
+ cambuf = malloc(camlen);
+ memcpy(cambuf, bytes, camlen);
+ (*env)->ReleaseByteArrayElements(env, array, bytes, 0);
+}
+
--- /dev/null
+++ b/gui-android/java/org/echoline/drawterm/DrawTermThread.java
@@ -1,0 +1,32 @@
+package org.echoline.drawterm;
+
+/**
+ * Created by eli on 12/4/17.
+ */
+
+public class DrawTermThread extends Thread {
+ private MainActivity m;
+ private String p;
+ private String []args;
+
+ public DrawTermThread(String []args, String p, MainActivity m) {
+ this.m = m;
+ this.p = p;
+ this.args = args;
+ }
+
+ @Override
+ public void run() {
+ if (p != null && !p.equals(""))
+ m.setPass(p);
+ m.dtmain(args);
+ m.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ m.exitDT();
+ m.setContentView(R.layout.server_main);
+ m.populateServers(m);
+ }
+ });
+ }
+}
--- /dev/null
+++ b/gui-android/java/org/echoline/drawterm/MainActivity.java
@@ -1,0 +1,437 @@
+package org.echoline.drawterm;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.Environment;
+
+import android.app.Activity;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.WindowManager;
+import android.view.Surface;
+import android.view.inputmethod.InputMethodManager;
+import android.view.KeyEvent;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
+import android.media.ImageReader;
+import android.graphics.ImageFormat;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import java.io.File;
+import java.util.Map;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.ArrayList;
+
+public class MainActivity extends Activity {
+ private Map<String, ?> map;
+ private MainActivity mainActivity;
+ private boolean dtrunning = false;
+ private DrawTermThread dthread;
+ private int notificationId;
+ private CameraDevice cameraDevice = null;
+ private byte []jpegBytes;
+
+ static {
+ System.loadLibrary("drawterm");
+ }
+
+ public void showNotification(String text) {
+ Notification.Builder builder = new Notification.Builder(this)
+ .setDefaults(Notification.DEFAULT_SOUND)
+ .setSmallIcon(R.drawable.ic_small)
+ .setContentText(text)
+ .setStyle(new Notification.BigTextStyle().bigText(text))
+ .setPriority(Notification.PRIORITY_DEFAULT);
+
+ ((NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE)).notify(notificationId, builder.build());
+ notificationId++;
+ }
+
+ public int numCameras() {
+ try {
+ return ((CameraManager)getSystemService(Context.CAMERA_SERVICE)).getCameraIdList().length;
+ } catch (CameraAccessException e) {
+ Log.w("drawterm", e.toString());
+ return 0;
+ }
+ }
+
+ public void takePicture(int id) {
+ try {
+ HandlerThread mBackgroundThread = new HandlerThread("Camera Background");
+ mBackgroundThread.start();
+ Handler mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+ CameraManager manager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);
+ String []cameraIdList = manager.getCameraIdList();
+ manager.openCamera(cameraIdList[id], new CameraDevice.StateCallback() {
+ public void onOpened(CameraDevice device) {
+ cameraDevice = device;
+ }
+ public void onDisconnected(CameraDevice device) {
+ if (cameraDevice != null)
+ cameraDevice.close();
+ cameraDevice = null;
+ }
+ public void onError(CameraDevice device, int error) {
+ if (cameraDevice != null)
+ cameraDevice.close();
+ cameraDevice = null;
+ }
+ }, mBackgroundHandler);
+ ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.JPEG, 1);
+ CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
+ captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
+ captureBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_AUTO);
+ captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
+ captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getWindowManager().getDefaultDisplay().getRotation());
+ captureBuilder.addTarget(reader.getSurface());
+ reader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
+ public void onImageAvailable(ImageReader reader) {
+ Image image = null;
+ try {
+ image = reader.acquireLatestImage();
+ ByteBuffer buffer = image.getPlanes()[0].getBuffer();
+ jpegBytes = new byte[buffer.capacity()];
+ buffer.get(jpegBytes);
+ } catch (Exception e) {
+ Log.w("drawterm", e.toString());
+ } finally {
+ if (image != null) {
+ image.close();
+ }
+ }
+ }
+ }, mBackgroundHandler);
+ List<Surface> outputSurfaces = new ArrayList<Surface>(1);
+ outputSurfaces.add(reader.getSurface());
+ cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
+ public void onConfigured(CameraCaptureSession session) {
+ try {
+ List<CaptureRequest> captureRequests = new ArrayList<CaptureRequest>(10);
+ for (int i = 0; i < 10; i++)
+ captureRequests.add(captureBuilder.build());
+ session.captureBurst(captureRequests, new CameraCaptureSession.CaptureCallback() {
+ public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId, long frameNumber) {
+ try {
+ sendPicture(jpegBytes);
+ mBackgroundThread.quitSafely();
+ mBackgroundThread.join();
+ } catch (Exception e) {
+ Log.w("drawterm", e.toString());
+ }
+ }
+ }, mBackgroundHandler);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+ public void onConfigureFailed(CameraCaptureSession session) {
+ }
+ }, mBackgroundHandler);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void serverView(View v) {
+ setContentView(R.layout.server_main);
+ serverButtons();
+
+ String s = (String)map.get(((TextView)v).getText().toString());
+ String []a = s.split("\007");
+
+ ((EditText)findViewById(R.id.cpuServer)).setText((String)a[0]);
+ ((EditText)findViewById(R.id.authServer)).setText((String)a[1]);
+ ((EditText)findViewById(R.id.userName)).setText((String)a[2]);
+ if (a.length > 3)
+ ((EditText)findViewById(R.id.passWord)).setText((String)a[3]);
+ }
+
+ public void populateServers(Context context) {
+ ListView ll = (ListView)findViewById(R.id.servers);
+ ArrayAdapter<String> la = new ArrayAdapter<String>(this, R.layout.item_main);
+ SharedPreferences settings = getSharedPreferences("DrawtermPrefs", 0);
+ map = (Map<String, ?>)settings.getAll();
+ String key;
+ Object []keys = map.keySet().toArray();
+ for (int i = 0; i < keys.length; i++) {
+ key = (String)keys[i];
+ la.add(key);
+ }
+ ll.setAdapter(la);
+
+ setDTSurface(null);
+ dtrunning = false;
+ }
+
+ public void runDrawterm(String []args, String pass) {
+ Resources res = getResources();
+ DisplayMetrics dm = res.getDisplayMetrics();
+
+ int wp = dm.widthPixels;
+ int hp = dm.heightPixels;
+
+ setContentView(R.layout.drawterm_main);
+
+ Button kbutton = (Button)findViewById(R.id.keyboardToggle);
+ kbutton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
+ }
+ });
+
+ int rid = res.getIdentifier("navigation_bar_height", "dimen", "android");
+ if (rid > 0) {
+ hp -= res.getDimensionPixelSize(rid);
+ }
+ LinearLayout ll = (LinearLayout)findViewById(R.id.dtButtons);
+ hp -= ll.getHeight();
+
+ int w = (int)(wp * (160.0/dm.xdpi));
+ int h = (int)(hp * (160.0/dm.ydpi));
+ float ws = (float)wp/w;
+ float hs = (float)hp/h;
+ // only scale up
+ if (ws < 1) {
+ ws = 1;
+ w = wp;
+ }
+ if (hs < 1) {
+ hs = 1;
+ h = hp;
+ }
+
+ MySurfaceView mView = new MySurfaceView(mainActivity, w, h, ws, hs);
+ mView.getHolder().setFixedSize(w, h);
+
+ LinearLayout l = (LinearLayout)findViewById(R.id.dlayout);
+ l.addView(mView, 1, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
+
+ dthread = new DrawTermThread(args, pass, mainActivity);
+ dthread.start();
+
+ dtrunning = true;
+ }
+
+ public void serverButtons() {
+ Button button = (Button)findViewById(R.id.save);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String cpu = ((EditText)findViewById(R.id.cpuServer)).getText().toString();
+ String auth = ((EditText)findViewById(R.id.authServer)).getText().toString();
+ String user = ((EditText)findViewById(R.id.userName)).getText().toString();
+ String pass = ((EditText)findViewById(R.id.passWord)).getText().toString();
+
+ SharedPreferences settings = getSharedPreferences("DrawtermPrefs", 0);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putString(user + "@" + cpu + " (auth=" + auth + ")", cpu + "\007" + auth + "\007" + user + "\007" + pass);
+ editor.commit();
+ }
+ });
+
+ button = (Button) findViewById(R.id.connect);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ String cpu = ((EditText)findViewById(R.id.cpuServer)).getText().toString();
+ String auth = ((EditText)findViewById(R.id.authServer)).getText().toString();
+ String user = ((EditText)findViewById(R.id.userName)).getText().toString();
+ String pass = ((EditText)findViewById(R.id.passWord)).getText().toString();
+
+ String args[] = {"drawterm", "-p", "-h", cpu, "-a", auth, "-u", user};
+ runDrawterm(args, pass);
+ }
+ });
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ mainActivity = this;
+ setObject();
+ setContentView(R.layout.activity_main);
+ populateServers(this);
+
+ View fab = findViewById(R.id.fab);
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setContentView(R.layout.server_main);
+ serverButtons();
+ }
+ });
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event)
+ {
+ if (!dtrunning) {
+ return super.dispatchKeyEvent(event);
+ }
+
+ int k = event.getUnicodeChar();
+ if (k == 0) {
+ k = event.getDisplayLabel();
+ if (k >= 'A' && k <= 'Z')
+ k |= 0x20;
+ }
+ String chars = event.getCharacters();
+ if (k == 0 && chars != null) {
+ for (int i = 0; i < chars.length(); i++) {
+ k = chars.codePointAt(i);
+ keyDown(k);
+ keyUp(k);
+ }
+ return true;
+ }
+
+ if (k == 0) switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DEL:
+ k = 0x0008;
+ break;
+ case KeyEvent.KEYCODE_FORWARD_DEL:
+ k = 0x007F;
+ break;
+ case KeyEvent.KEYCODE_ESCAPE:
+ k = 0x001B;
+ break;
+ case KeyEvent.KEYCODE_MOVE_HOME:
+ k = 0xF00D;
+ break;
+ case KeyEvent.KEYCODE_MOVE_END:
+ k = 0xF018;
+ break;
+ case KeyEvent.KEYCODE_PAGE_UP:
+ k = 0xF00F;
+ break;
+ case KeyEvent.KEYCODE_PAGE_DOWN:
+ k = 0xF013;
+ break;
+ case KeyEvent.KEYCODE_INSERT:
+ k = 0xF014;
+ break;
+ case KeyEvent.KEYCODE_SYSRQ:
+ k = 0xF010;
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ k = 0xF00E;
+ break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ k = 0xF011;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ k = 0xF012;
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ k = 0xF800;
+ break;
+ }
+
+ if (k == 0)
+ return true;
+
+ if (event.isCtrlPressed()) {
+ keyDown(0xF017);
+ }
+ if (event.isAltPressed() && k < 128) {
+ keyDown(0xF015);
+ }
+
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ keyDown(k);
+ }
+ else if (event.getAction() == KeyEvent.ACTION_UP) {
+ keyUp(k);
+ }
+
+ if (event.isCtrlPressed()) {
+ keyUp(0xF017);
+ }
+ if (event.isAltPressed() && k < 128) {
+ keyUp(0xF015);
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onBackPressed()
+ {
+ }
+
+ @Override
+ public void onDestroy()
+ {
+ setDTSurface(null);
+ dtrunning = false;
+ exitDT();
+ super.onDestroy();
+ }
+
+ public void setClipBoard(String str) {
+ ClipboardManager cm = (ClipboardManager)getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cm != null) {
+ ClipData cd = ClipData.newPlainText(null, str);
+ cm.setPrimaryClip(cd);
+ }
+ }
+
+ public String getClipBoard() {
+ ClipboardManager cm = (ClipboardManager)getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cm != null) {
+ ClipData cd = cm.getPrimaryClip();
+ if (cd != null)
+ return (String)(cd.getItemAt(0).coerceToText(mainActivity.getApplicationContext()).toString());
+ }
+ return "";
+ }
+
+ public native void dtmain(Object[] args);
+ public native void setPass(String arg);
+ public native void setWidth(int arg);
+ public native void setHeight(int arg);
+ public native void setWidthScale(float arg);
+ public native void setHeightScale(float arg);
+ public native void setDTSurface(Surface surface);
+ public native void setMouse(int[] args);
+ public native void setObject();
+ public native void keyDown(int c);
+ public native void keyUp(int c);
+ public native void exitDT();
+ public native void sendPicture(byte[] array);
+}
--- /dev/null
+++ b/gui-android/java/org/echoline/drawterm/MySurfaceView.java
@@ -1,0 +1,91 @@
+package org.echoline.drawterm;
+
+import android.util.Log;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.SurfaceView;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.security.spec.ECField;
+
+/**
+ * Created by eli on 12/3/17.
+ */
+public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+ private int screenWidth, screenHeight;
+ private MainActivity mainActivity;
+ private float ws, hs;
+
+ public MySurfaceView(Context context, int w, int h, float ws, float hs) {
+ super(context);
+ screenHeight = h;
+ screenWidth = w;
+ this.ws = ws;
+ this.hs = hs;
+ mainActivity = (MainActivity)context;
+ mainActivity.setWidth(screenWidth);
+ mainActivity.setHeight(screenHeight);
+ mainActivity.setWidthScale(ws);
+ mainActivity.setHeightScale(hs);
+ setWillNotDraw(true);
+
+ getHolder().addCallback(this);
+
+ setOnTouchListener(new View.OnTouchListener() {
+ private int[] mouse = new int[3];
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ CheckBox left = (CheckBox)mainActivity.findViewById(R.id.mouseLeft);
+ CheckBox middle = (CheckBox)mainActivity.findViewById(R.id.mouseMiddle);
+ CheckBox right = (CheckBox)mainActivity.findViewById(R.id.mouseRight);
+ CheckBox up = (CheckBox)mainActivity.findViewById(R.id.mouseUp);
+ CheckBox down = (CheckBox)mainActivity.findViewById(R.id.mouseDown);
+ int buttons = (left.isChecked()? 1: 0) |
+ (middle.isChecked()? 2: 0) |
+ (right.isChecked()? 4: 0) |
+ (up.isChecked()? 8: 0) |
+ (down.isChecked()? 16: 0);
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mouse[0] = Math.round(event.getX());
+ mouse[1] = Math.round(event.getY());
+ mouse[2] = buttons;
+ mainActivity.setMouse(mouse);
+ } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ mouse[0] = Math.round(event.getX());
+ mouse[1] = Math.round(event.getY());
+ mouse[2] = buttons;
+ mainActivity.setMouse(mouse);
+ } else if (event.getAction() == MotionEvent.ACTION_UP) {
+ mouse[0] = Math.round(event.getX());
+ mouse[1] = Math.round(event.getY());
+ mouse[2] = 0;
+ mainActivity.setMouse(mouse);
+ }
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mainActivity.setDTSurface(holder.getSurface());
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int w, int h, int format) {
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mainActivity.setDTSurface(null);
+ }
+}
--- /dev/null
+++ b/gui-android/res/layout/activity_main.xml
@@ -1,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.widget.FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="org.echoline.drawterm.MainActivity">
+
+ <include layout="@layout/content_main" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end">
+
+ <Button
+ android:id="@+id/fab"
+ android:text="add server"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+</android.widget.FrameLayout>
--- /dev/null
+++ b/gui-android/res/layout/content_main.xml
@@ -1,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.widget.FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="org.echoline.drawterm.MainActivity"
+ tools:showIn="@layout/activity_main">
+ <ListView
+ android:id="@+id/servers"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ </ListView>
+</android.widget.FrameLayout>
--- /dev/null
+++ b/gui-android/res/layout/drawterm_main.xml
@@ -1,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/dlayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context="org.echoline.drawterm.MainActivity">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:id="@+id/dtButtons">
+
+ <CheckBox
+ android:id="@+id/mouseLeft"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+ <CheckBox
+ android:id="@+id/mouseMiddle"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+ <CheckBox
+ android:id="@+id/mouseRight"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+ <CheckBox
+ android:id="@+id/mouseUp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+ <CheckBox
+ android:id="@+id/mouseDown"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+ <Button
+ android:id="@+id/keyboardToggle"
+ android:text="kb"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+</LinearLayout>
--- /dev/null
+++ b/gui-android/res/layout/item_main.xml
@@ -1,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent" android:layout_height="match_parent"
+ android:padding="10dp"
+ android:textSize="16sp"
+ android:onClick="serverView">
+</TextView>
+
--- /dev/null
+++ b/gui-android/res/layout/server_main.xml
@@ -1,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.widget.FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="org.echoline.drawterm.MainActivity">
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical">
+
+ <EditText
+ android:id="@+id/cpuServer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="CPU" />
+ <EditText
+ android:id="@+id/authServer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="Auth" />
+ <EditText
+ android:id="@+id/userName"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="Username" />
+
+ <EditText
+ android:id="@+id/passWord"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:password="true"
+ android:hint="Password"/>
+
+ <Button
+ android:id="@+id/save"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Save" />
+
+ <Button
+ android:id="@+id/connect"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Connect" />
+
+ </LinearLayout>
+
+</android.widget.FrameLayout>
--- /dev/null
+++ b/gui-android/res/values/colors.xml
@@ -1,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#3FB551</color>
+</resources>
--- /dev/null
+++ b/gui-android/res/values/strings.xml
@@ -1,0 +1,3 @@
+<resources>
+ <string name="app_name">Drawterm</string>
+</resources>
--- /dev/null
+++ b/gui-android/res/values/styles.xml
@@ -1,0 +1,8 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="android:Theme.NoTitleBar">
+ <!-- Customize your theme here. -->
+ </style>
+
+</resources>
--- /dev/null
+++ b/gui-fbdev/Makefile
@@ -1,0 +1,12 @@
+ROOT=..
+include ../Make.config
+LIB=libgui.a
+
+OFILES=\
+ fbdev.$O\
+
+default: $(LIB)
+$(LIB): $(OFILES)
+ $(AR) r $(LIB) $(OFILES)
+ $(RANLIB) $(LIB)
+
--- /dev/null
+++ b/gui-fbdev/fbdev.c
@@ -1,0 +1,673 @@
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <keyboard.h>
+#include <cursor.h>
+#include "screen.h"
+
+#undef long
+#undef ulong
+
+#include <linux/fb.h>
+#include <linux/input.h>
+
+uchar* fbp;
+Memimage* screenimage;
+Memimage* backbuf;
+Rectangle screenr;
+char* snarfbuf;
+struct fb_fix_screeninfo finfo;
+struct fb_var_screeninfo vinfo;
+int *eventfds = NULL;
+int neventfds;
+Point mousexy;
+char shift_state;
+int ttyfd;
+char* tty;
+char hidden;
+int devicesfd;
+ulong chan;
+int depth;
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <limits.h>
+
+#include <fcntl.h>
+#include <termios.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <linux/keyboard.h>
+#include <signal.h>
+
+#include <termios.h>
+
+#define ulong p9_ulong
+
+int code2key[] = {
+ Kesc, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\x08',
+ '\x09', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
+ Kctl, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', Kshift,
+ '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', Kshift, '*', Kalt,
+ ' ', Kcaps, KF|1, KF|2, KF|3, KF|4, KF|5, KF|6, KF|7, KF|8, KF|9, KF|10,
+ Knum, Kscroll,
+ '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.',
+};
+
+int code2key_shift[] = {
+ Kesc, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\x08',
+ '\x09', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
+ Kctl, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '\"', '~', Kshift,
+ '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', Kshift, '*', Kalt,
+ ' ', Kcaps, KF|1, KF|2, KF|3, KF|4, KF|5, KF|6, KF|7, KF|8, KF|9, KF|10,
+ Knum, Kscroll,
+ '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.',
+};
+
+Memimage *gscreen;
+char *snarfbuf = nil;
+
+int onevent(struct input_event*);
+void termctl(uint32_t o, int or);
+void ctrlc(int sig);
+
+void
+_fbput(Memimage *m, Rectangle r) {
+ int y;
+
+ for (y = r.min.y; y < r.max.y; y++){
+ long loc = y * finfo.line_length + r.min.x * depth;
+ void *ptr = m->data->bdata + y * m->width * 4 + r.min.x * depth;
+
+ memcpy(fbp + loc, ptr, Dx(r) * depth);
+ }
+}
+
+Memimage*
+fbattach(int fbdevidx)
+{
+ Rectangle r;
+ char devname[64];
+ size_t size;
+ int fd;
+
+ /*
+ * Connect to /dev/fb0
+ */
+ snprintf(devname, sizeof(devname) - 1, "/dev/fb%d", fbdevidx);
+ if ((fd = open(devname, O_RDWR)) < 0)
+ goto err;
+
+ if (ioctl(fd, FBIOGET_VSCREENINFO, &(vinfo)) < 0)
+ goto err;
+
+ switch (vinfo.bits_per_pixel) {
+ case 32:
+ chan = XRGB32;
+ depth = 4;
+ break;
+ case 16:
+ chan = RGB16;
+ depth = 2;
+ break;
+ default:
+ goto err;
+ }
+
+ if (ioctl(fd, FBIOGET_FSCREENINFO, &(finfo)) < 0)
+ goto err;
+
+ size = vinfo.yres_virtual * finfo.line_length;
+ if ((fbp = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0)) < 0)
+ goto err;
+ /*
+ * Figure out underlying screen format.
+ */
+ r = Rect(0, 0, vinfo.xres_virtual, vinfo.yres_virtual);
+
+ screenr = r;
+
+ screenimage = allocmemimage(r, chan);
+ backbuf = allocmemimage(r, chan);
+ return backbuf;
+
+err:
+ return nil;
+}
+
+int
+eventattach()
+{
+ char eventfile[PATH_MAX] = "";
+ char line[PATH_MAX];
+ FILE *devices;
+ char *ptr;
+
+ neventfds = 0;
+ devices = fopen("/proc/bus/input/devices", "r");
+ if (devices == NULL)
+ return -1;
+ while (fgets(line, sizeof(line)-1, devices) != NULL)
+ if (line[0] == 'H') {
+ ptr = strstr(line, "event");
+ if (ptr == NULL)
+ continue;
+ ptr[strcspn(ptr, " \r\n")] = '\0';
+ snprintf(eventfile, sizeof(eventfile)-1, "/dev/input/%s", ptr);
+ neventfds++;
+ eventfds = realloc(eventfds, neventfds * sizeof(int));
+ eventfds[neventfds-1] = open(eventfile, O_RDONLY);
+ if (eventfds[neventfds-1] < 0)
+ neventfds--;
+ }
+ fclose(devices);
+
+ if (neventfds == 0)
+ return -1;
+ return 1;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ int x, y, i;
+ Point p;
+ long fbloc;
+ int x2, y2;
+
+ if (rectclip(&r, screenimage->r) == 0)
+ return;
+ if (Dx(r) == 0 || Dy(r) == 0)
+ return;
+
+ assert(!canqlock(&drawlock));
+
+ memimagedraw(screenimage, r, backbuf, r.min, nil, r.min, S);
+
+ if (hidden != 0)
+ return;
+
+ p = mousexy;
+
+ // draw cursor
+ for (x = 0; x < 16; x++) {
+ x2 = x + cursor.offset.x;
+
+ if ((p.x + x2) < 0)
+ continue;
+
+ if ((p.x + x2) >= screenimage->r.max.x)
+ break;
+
+ for (y = 0; y < 16; y++) {
+ y2 = y + cursor.offset.y;
+
+ if ((p.y + y2) < 0)
+ continue;
+
+ if ((p.y + y2) >= screenimage->r.max.y)
+ break;
+
+ i = y * 2 + x / 8;
+ fbloc = ((p.y+y2) * screenimage->r.max.x + (p.x+x2)) * depth;
+
+ if (cursor.clr[i] & (128 >> (x % 8))) {
+ switch (depth) {
+ case 2:
+ *((uint16_t*)(screenimage->data->bdata + fbloc)) = 0xFFFF;
+ break;
+ case 4:
+ *((uint32_t*)(screenimage->data->bdata + fbloc)) = 0xFFFFFFFF;
+ break;
+ }
+ }
+
+ if (cursor.set[i] & (128 >> (x % 8))) {
+ switch (depth) {
+ case 2:
+ *((uint16_t*)(screenimage->data->bdata + fbloc)) = 0x0000;
+ break;
+ case 4:
+ *((uint32_t*)(screenimage->data->bdata + fbloc)) = 0xFF000000;
+ break;
+ }
+ }
+ }
+ }
+
+ _fbput(screenimage, r);
+}
+
+static void
+fbproc(void *v)
+{
+ struct input_event data;
+ char buf[32];
+ struct pollfd *pfd;
+ int r;
+ int ioctlarg;
+
+ pfd = calloc(3, sizeof(struct pollfd));
+ pfd[0].fd = ttyfd; // for virtual console switches
+ pfd[0].events = POLLPRI;
+ pfd[1].fd = 0; // stdin goes to nowhere
+ pfd[1].events = POLLIN;
+ pfd[2].fd = open("/proc/bus/input/devices", O_RDONLY); // input hotplug
+ if (pfd[2].fd < 0)
+ panic("cannot open /proc/bus/input/devices: %r");
+ pfd[2].events = POLLIN;
+
+TOP:
+ while(read(pfd[2].fd, buf, 31) > 0);
+
+ pfd = realloc(pfd, sizeof(struct pollfd) * (neventfds + 3));
+ for (r = 0; r < neventfds; r++) {
+ pfd[r+3].fd = eventfds[r];
+ pfd[r+3].events = POLLIN;
+ }
+
+ for(;;) {
+ shift_state = 6;
+ if (ioctl(0, TIOCLINUX, &shift_state) < 0)
+ panic("ioctl TIOCLINUX 6: %r");
+
+ r = poll(pfd, 3+neventfds, -1);
+ if (r < 0)
+ oserror();
+ if (pfd[0].revents & POLLPRI) {
+ if ((r = read(ttyfd, buf, 31)) <= 0)
+ panic("ttyfd read: %r");
+ buf[r] = '\0';
+ if (strcmp(buf, tty) == 0) {
+ hidden = 0;
+ printf("\e[?25l");
+ fflush(stdout);
+ qlock(&drawlock);
+ flushmemscreen(gscreen->clipr);
+ qunlock(&drawlock);
+ }
+ else
+ hidden = 1;
+ close(ttyfd);
+ ttyfd = open("/sys/class/tty/tty0/active", O_RDONLY);
+ if (ttyfd < 0)
+ panic("cannot open tty active fd: %r");
+ pfd[0].fd = ttyfd;
+ read(ttyfd, buf, 0);
+ }
+ if (pfd[1].revents & POLLIN)
+ read(pfd[1].fd, buf, 31);
+ if (pfd[2].revents & POLLIN) {
+ for (r = 0; r < neventfds; r++)
+ close(eventfds[r]);
+ if(eventattach() < 0) {
+ panic("cannot open event files: %r");
+ }
+ goto TOP;
+ }
+ for (r = 0; r < neventfds; r++)
+ if (pfd[r+3].revents & POLLIN) {
+ if (read(pfd[r+3].fd, &data, sizeof(data)) != sizeof(data))
+ panic("eventfd read: %r");
+ if (onevent(&data) == 0) {
+ ioctlarg = 15;
+ if (ioctl(0, TIOCLINUX, &ioctlarg) != 0) {
+ ioctlarg = 4;
+ ioctl(0, TIOCLINUX, &ioctlarg);
+ qlock(&drawlock);
+ flushmemscreen(gscreen->clipr);
+ qunlock(&drawlock);
+ } else {
+ write(1, "\033[9;30]", 7);
+ }
+ }
+ }
+ }
+
+ printf("\e[?25h");
+ fflush(stdout);
+ termctl(ECHO, 1);
+ free(pfd);
+}
+
+void
+screensize(Rectangle r, ulong chan)
+{
+ gscreen = backbuf;
+ gscreen->clipr = ZR;
+}
+
+void
+screeninit(void)
+{
+ int r;
+ char buf[1];
+
+ // set up terminal
+ printf("\e[?25l");
+ fflush(stdout);
+ termctl(~(ICANON|ECHO), 0);
+ signal(SIGINT, ctrlc);
+
+ memimageinit();
+
+ // tty switching
+ ttyfd = open("/sys/class/tty/tty0/active", O_RDONLY);
+ if (ttyfd >= 0) {
+ tty = malloc(32);
+ r = read(ttyfd, tty, 31);
+ if (r >= 0)
+ tty[r] = '\0';
+ else
+ tty[0] = '\0';
+ close(ttyfd);
+ ttyfd = open("/sys/class/tty/tty0/active", O_RDONLY);
+ }
+ if (ttyfd < 0)
+ panic("cannot open tty active fd: %r");
+ read(ttyfd, buf, 0);
+ hidden = 0;
+
+ if(fbattach(0) == nil) {
+ panic("cannot open framebuffer: %r");
+ }
+
+ if(eventattach() < 0) {
+ panic("cannot open event files: %r");
+ }
+
+ screensize(screenr, chan);
+ if (gscreen == nil)
+ panic("screensize failed");
+
+ gscreen->clipr = screenr;
+ kproc("fbdev", fbproc, nil);
+
+ terminit();
+
+ qlock(&drawlock);
+ flushmemscreen(gscreen->clipr);
+ qunlock(&drawlock);
+}
+
+Memdata*
+attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
+{
+ *r = gscreen->clipr;
+ *chan = gscreen->chan;
+ *depth = gscreen->depth;
+ *width = gscreen->width;
+ *softscreen = 1;
+
+ gscreen->data->ref++;
+ return gscreen->data;
+}
+
+void
+getcolor(ulong i, ulong *r, ulong *g, ulong *b)
+{
+ ulong v;
+
+ v = cmap2rgb(i);
+ *r = (v>>16)&0xFF;
+ *g = (v>>8)&0xFF;
+ *b = v&0xFF;
+}
+
+void
+setcolor(ulong i, ulong r, ulong g, ulong b)
+{
+ /* no-op */
+ return;
+}
+
+char*
+clipread(void)
+{
+ if(snarfbuf)
+ return strdup(snarfbuf);
+ return nil;
+}
+
+int
+clipwrite(char *buf)
+{
+ if(snarfbuf)
+ free(snarfbuf);
+ snarfbuf = strdup(buf);
+ return 0;
+}
+
+void
+guimain(void)
+{
+ cpubody();
+}
+
+void
+termctl(uint32_t o, int or)
+{
+ struct termios t;
+
+ tcgetattr(0, &t);
+ if (or)
+ t.c_lflag |= o;
+ else
+ t.c_lflag &= o;
+ tcsetattr(0, TCSANOW, &t);
+}
+
+void
+ctrlc(int sig) {
+}
+
+int
+onevent(struct input_event *data)
+{
+ Rectangle old, new;
+ ulong msec;
+ static int buttons;
+ static Point coord;
+ static char touched;
+ static Point startmousept;
+ static Point startpt;
+ int key;
+ static ulong lastmsec = 0;
+
+ if (hidden != 0)
+ return -1;
+
+ msec = ticks();
+
+ old.min = mousexy;
+ old.max = addpt(old.min, Pt(16, 16));
+
+ buttons &= ~0x18;
+
+ switch(data->type) {
+ case 3:
+ switch(data->code) {
+ case 0:
+ coord.x = data->value;
+ break;
+ case 1:
+ coord.y = data->value;
+ break;
+ case 0x18:
+ case 0x1c:
+ if (data->value == 0)
+ touched = 0;
+ else if (data->value > 24) {
+ touched = 1;
+ startmousept = coord;
+ startpt = mousexy;
+ }
+ break;
+ default:
+ return -1;
+ }
+ if (touched)
+ mousexy = addpt(startpt, divpt(subpt(coord, startmousept), 4));
+ break;
+ case 2:
+ switch(data->code) {
+ case 0:
+ mousexy.x += data->value;
+ break;
+ case 1:
+ mousexy.y += data->value;
+ break;
+ case 8:
+ buttons |= data->value == 1? 8: 16;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case 1:
+ switch(data->code) {
+ case 0x110:
+ if (data->value == 1)
+ buttons |= 1;
+ else
+ buttons &= ~1;
+ break;
+ case 0x111:
+ if (data->value == 1)
+ buttons |= shift_state & (1 << KG_SHIFT)? 2: 4;
+ else
+ buttons &= ~(shift_state & (1 << KG_SHIFT)? 2: 4);
+ break;
+ case 0x112:
+ if (data->value == 1)
+ buttons |= 2;
+ else
+ buttons &= ~2;
+ break;
+ default:
+ if (hidden)
+ return 0;
+ if (data->code > 0 && data->code <= nelem(code2key)) {
+ if (shift_state & (1 << KG_SHIFT))
+ key = code2key_shift[data->code-1];
+ else
+ key = code2key[data->code-1];
+ if (key == Kshift)
+ return -1;
+ kbdkey(key, data->value);
+ return 0;
+ }
+ switch(data->code) {
+ case 87:
+ kbdkey(KF|11, data->value);
+ break;
+ case 88:
+ kbdkey(KF|12, data->value);
+ break;
+ case 96:
+ kbdkey('\n', data->value);
+ break;
+ case 97:
+ kbdkey(Kctl, data->value);
+ break;
+ case 98:
+ kbdkey('/', data->value);
+ break;
+ case 100:
+ kbdkey(Kalt, data->value);
+ break;
+ case 102:
+ kbdkey(Khome, data->value);
+ break;
+ case 103:
+ kbdkey(Kup, data->value);
+ break;
+ case 104:
+ kbdkey(Kpgup, data->value);
+ break;
+ case 105:
+ kbdkey(Kleft, data->value);
+ break;
+ case 106:
+ kbdkey(Kright, data->value);
+ break;
+ case 107:
+ kbdkey(Kend, data->value);
+ break;
+ case 108:
+ kbdkey(Kdown, data->value);
+ break;
+ case 109:
+ kbdkey(Kpgdown, data->value);
+ break;
+ case 110:
+ kbdkey(Kins, data->value);
+ break;
+ case 111:
+ kbdkey(Kdel, data->value);
+ break;
+ }
+ return 0;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ if (mousexy.x < screenimage->r.min.x)
+ mousexy.x = screenimage->r.min.x;
+ if (mousexy.y < screenimage->r.min.y)
+ mousexy.y = screenimage->r.min.y;
+ if (mousexy.x > screenimage->r.max.x)
+ mousexy.x = screenimage->r.max.x;
+ if (mousexy.y > screenimage->r.max.y)
+ mousexy.y = screenimage->r.max.y;
+
+ new.min = mousexy;
+ new.max = addpt(new.min, Pt(16, 16)); // size of cursor bitmap
+
+ combinerect(&new, old);
+ new.min = subpt(new.min, Pt(16, 16)); // to encompass any cursor->offset
+
+ qlock(&drawlock);
+ flushmemscreen(new);
+ qunlock(&drawlock);
+
+ if ((msec - lastmsec) < 10)
+ if (data->type != 1)
+ return 0;
+
+ lastmsec = msec;
+
+ absmousetrack(mousexy.x, mousexy.y, buttons, msec);
+
+ return 0;
+}
+
+void
+mouseset(Point p)
+{
+ qlock(&drawlock);
+ mousexy = p;
+ flushmemscreen(screenr);
+ qunlock(&drawlock);
+}
+
+void
+setcursor(void)
+{
+ qlock(&drawlock);
+ flushmemscreen(screenr);
+ qunlock(&drawlock);
+}
+
+void
+titlewrite(char* buf)
+{
+}
+
--- /dev/null
+++ b/kern/devaudio-alsa.c
@@ -1,0 +1,108 @@
+/*
+ * ALSA
+ */
+#include <alsa/asoundlib.h>
+#include "u.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "devaudio.h"
+
+enum
+{
+ Channels = 2,
+ Rate = 44100,
+ Bits = 16,
+};
+
+static snd_pcm_t *playback;
+static snd_pcm_t *capture;
+static int speed = Rate;
+
+/* maybe this should return -1 instead of sysfatal */
+void
+audiodevopen(void)
+{
+ if(snd_pcm_open(&playback, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0)
+ error("snd_pcm_open playback");
+
+ if(snd_pcm_set_params(playback, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 2, speed, 1, 500000) < 0)
+ error("snd_pcm_set_params playback");
+
+ if(snd_pcm_prepare(playback) < 0)
+ error("snd_pcm_prepare playback");
+
+ if(snd_pcm_open(&capture, "default", SND_PCM_STREAM_CAPTURE, 0) < 0)
+ error("snd_pcm_open capture");
+
+ if(snd_pcm_set_params(capture, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 2, speed, 1, 500000) < 0)
+ error("snd_pcm_set_params capture");
+
+ if(snd_pcm_prepare(capture) < 0)
+ error("snd_pcm_prepare capture");
+}
+
+void
+audiodevclose(void)
+{
+ snd_pcm_drain(playback);
+ snd_pcm_close(playback);
+
+ snd_pcm_close(capture);
+}
+
+void
+audiodevsetvol(int what, int left, int right)
+{
+ if(what == Vspeed){
+ speed = left;
+ return;
+ }
+}
+
+void
+audiodevgetvol(int what, int *left, int *right)
+{
+ if(what == Vspeed){
+ *left = *right = speed;
+ return;
+ }
+
+ *left = *right = 100;
+}
+
+int
+audiodevwrite(void *v, int n)
+{
+ snd_pcm_sframes_t frames;
+ int tot, m;
+
+ for(tot = 0; tot < n; tot += m){
+ do {
+ frames = snd_pcm_writei(playback, v+tot, (n-tot)/4);
+ } while(frames == -EAGAIN);
+ if (frames < 0)
+ frames = snd_pcm_recover(playback, frames, 0);
+ if (frames < 0)
+ error((char*)snd_strerror(frames));
+ m = frames*4;
+ }
+
+ return tot;
+}
+
+int
+audiodevread(void *v, int n)
+{
+ snd_pcm_sframes_t frames;
+
+ do {
+ frames = snd_pcm_readi(capture, v, n/4);
+ } while(frames == -EAGAIN);
+
+ if (frames < 0)
+ error((char*)snd_strerror(frames));
+
+ return frames*4;
+}
--- a/main.c
+++ b/main.c
@@ -54,6 +54,7 @@
if(bind("#U", "/root", MREPL) < 0)
panic("bind #U: %r");
bind("#A", "/dev", MAFTER);
+ bind("#N", "/dev", MAFTER);
bind("#C", "/", MAFTER);
if(open("/dev/cons", OREAD) != 0)