diff --git a/app/.externalNativeBuild/ndkBuild/debug/arm64-v8a/android_gradle_build_lorie_arm64-v8a.stdout.txt b/app/.externalNativeBuild/ndkBuild/debug/arm64-v8a/android_gradle_build_lorie_arm64-v8a.stdout.txt
index 88afae6..30feada 100644
--- a/app/.externalNativeBuild/ndkBuild/debug/arm64-v8a/android_gradle_build_lorie_arm64-v8a.stdout.txt
+++ b/app/.externalNativeBuild/ndkBuild/debug/arm64-v8a/android_gradle_build_lorie_arm64-v8a.stdout.txt
@@ -5,6 +5,7 @@ Android NDK: current module
[arm64-v8a] Compile++ : lorie <= client.cpp
[arm64-v8a] Compile++ : lorie <= clipboard.cpp
[arm64-v8a] Compile++ : lorie <= compositor.cpp
+[arm64-v8a] Compile++ : lorie <= egl-helper.cpp
[arm64-v8a] Compile++ : lorie <= message-queue.cpp
[arm64-v8a] Compile++ : lorie <= renderer.cpp
[arm64-v8a] Compile++ : lorie <= seat.cpp
@@ -14,5 +15,5 @@ Android NDK: current module
[arm64-v8a] Compile++ : lorie <= utils.cpp
[arm64-v8a] Compile++ : lorie <= wayland.cpp
[arm64-v8a] Compile++ : lorie <= android-app.cpp
-[arm64-v8a] Compile++ : lorie <= GLContext.cpp
+[arm64-v8a] Compile : lorie <= utils.c
[arm64-v8a] SharedLibrary : liblorie.so
diff --git a/app/.externalNativeBuild/ndkBuild/debug/x86_64/android_gradle_build_lorie_x86_64.stderr.txt b/app/.externalNativeBuild/ndkBuild/debug/x86_64/android_gradle_build_lorie_x86_64.stderr.txt
index 8e8f6ed..e69de29 100644
--- a/app/.externalNativeBuild/ndkBuild/debug/x86_64/android_gradle_build_lorie_x86_64.stderr.txt
+++ b/app/.externalNativeBuild/ndkBuild/debug/x86_64/android_gradle_build_lorie_x86_64.stderr.txt
@@ -1,52 +0,0 @@
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:274:6: error: use of undeclared identifier 'jcompositor'; did you mean 'compositor'?
- if (jcompositor == 0) return;
- ^~~~~~~~~~~
- compositor
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:271:50: note: 'compositor' declared here
- jlong compositor, jint type,
- ^
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:275:39: error: use of undeclared identifier 'jcompositor'; did you mean 'compositor'?
- LorieBackendAndroid *b = fromLong(jcompositor);
- ^~~~~~~~~~~
- compositor
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:271:50: note: 'compositor' declared here
- jlong compositor, jint type,
- ^
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:282:9: error: use of undeclared identifier 'key_code'; did you mean 'keyCode'?
- if (key_code && !characters) {
- ^~~~~~~~
- keyCode
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:272:49: note: 'keyCode' declared here
- jint keyCode, jint jshift,
- ^
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:285:7: error: use of undeclared identifier 'xkb_names'
- if (xkb_names.layout != "us") {
- ^
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:290:10: error: use of undeclared identifier 'key_code'; did you mean 'keyCode'?
- if (!key_code && characters) {
- ^~~~~~~~
- keyCode
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:272:49: note: 'keyCode' declared here
- jint keyCode, jint jshift,
- ^
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:293:26: error: 'xkb_names' is a private member of 'LorieBackendAndroid'
- if (layout && b->xkb_names.layout != layout) {
- ^
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:45:24: note: declared private here
- struct xkb_rule_names xkb_names = {0};
- ^
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:298:90: error: use of undeclared identifier 'key_code'; did you mean 'keyCode'?
- LOGE("Keyboard input: keyCode: %d; eventCode: %d; characters: %s; shift: %d, type: %d", key_code, event_code, characters, shift, type);
- ^~~~~~~~
- keyCode
-/home/twaik/TermuxX11/app/src/main/jni/lorie/include/log.h:26:34: note: expanded from macro 'LOGE'
-#define LOGE(...) LOG(LOG_ERROR, __VA_ARGS__)
- ^
-/home/twaik/TermuxX11/app/src/main/jni/lorie/include/log.h:20:41: note: expanded from macro 'LOG'
-#define LOG(prio, ...) LogMessage(prio, __VA_ARGS__)
- ^
-/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:272:49: note: 'keyCode' declared here
- jint keyCode, jint jshift,
- ^
-7 errors generated.
-make: *** [/home/twaik/TermuxX11/app/build/intermediates/ndkBuild/debug/obj/local/x86_64/objs-debug/lorie/backend/android/android-app.o] Error 1
diff --git a/app/.externalNativeBuild/ndkBuild/debug/x86_64/android_gradle_build_lorie_x86_64.stdout.txt b/app/.externalNativeBuild/ndkBuild/debug/x86_64/android_gradle_build_lorie_x86_64.stdout.txt
index 3c8cc6a..4d292fe 100644
--- a/app/.externalNativeBuild/ndkBuild/debug/x86_64/android_gradle_build_lorie_x86_64.stdout.txt
+++ b/app/.externalNativeBuild/ndkBuild/debug/x86_64/android_gradle_build_lorie_x86_64.stdout.txt
@@ -2,4 +2,50 @@ Android NDK: WARNING:/home/twaik/TermuxX11/app/src/main/jni/lorie/Android.mk:lor
Android NDK: This is likely to result in incorrect builds. Try using LOCAL_STATIC_LIBRARIES
Android NDK: or LOCAL_SHARED_LIBRARIES instead to list the library dependencies of the
Android NDK: current module
+[x86_64] Compile++ : lorie <= client.cpp
+[x86_64] Compile++ : lorie <= clipboard.cpp
+[x86_64] Compile++ : lorie <= compositor.cpp
+[x86_64] Compile++ : lorie <= egl-helper.cpp
+[x86_64] Compile++ : lorie <= message-queue.cpp
+[x86_64] Compile++ : lorie <= renderer.cpp
+[x86_64] Compile++ : lorie <= seat.cpp
+[x86_64] Compile++ : lorie <= surface.cpp
+[x86_64] Compile++ : lorie <= output.cpp
+[x86_64] Compile++ : lorie <= log.cpp
+[x86_64] Compile++ : lorie <= utils.cpp
+[x86_64] Compile++ : lorie <= wayland.cpp
[x86_64] Compile++ : lorie <= android-app.cpp
+[x86_64] Compile : lorie <= utils.c
+[x86_64] Compile : xkbcommon <= parser.c
+[x86_64] Compile : xkbcommon <= paths.c
+[x86_64] Compile : xkbcommon <= state.c
+[x86_64] Compile : xkbcommon <= table.c
+[x86_64] Compile : xkbcommon <= action.c
+[x86_64] Compile : xkbcommon <= ast-build.c
+[x86_64] Compile : xkbcommon <= compat.c
+[x86_64] Compile : xkbcommon <= expr.c
+[x86_64] Compile : xkbcommon <= include.c
+[x86_64] Compile : xkbcommon <= keycodes.c
+[x86_64] Compile : xkbcommon <= keymap.c
+[x86_64] Compile : xkbcommon <= keymap-dump.c
+[x86_64] Compile : xkbcommon <= keywords.c
+[x86_64] Compile : xkbcommon <= parser.c
+[x86_64] Compile : xkbcommon <= rules.c
+[x86_64] Compile : xkbcommon <= scanner.c
+[x86_64] Compile : xkbcommon <= symbols.c
+[x86_64] Compile : xkbcommon <= types.c
+[x86_64] Compile : xkbcommon <= vmod.c
+[x86_64] Compile : xkbcommon <= xkbcomp.c
+[x86_64] Compile : xkbcommon <= atom.c
+[x86_64] Compile : xkbcommon <= context.c
+[x86_64] Compile : xkbcommon <= context-priv.c
+[x86_64] Compile : xkbcommon <= keysym.c
+[x86_64] Compile : xkbcommon <= keysym-utf.c
+[x86_64] Compile : xkbcommon <= keymap.c
+[x86_64] Compile : xkbcommon <= keymap-priv.c
+[x86_64] Compile : xkbcommon <= state.c
+[x86_64] Compile : xkbcommon <= text.c
+[x86_64] Compile : xkbcommon <= utf8.c
+[x86_64] Compile : xkbcommon <= utils.c
+[x86_64] SharedLibrary : libxkbcommon.so
+[x86_64] SharedLibrary : liblorie.so
diff --git a/app/.gitignore b/app/.gitignore
index 796b96d..085783c 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -1 +1,3 @@
/build
+/release
+.externalNativeBuild
diff --git a/app/release/app-release.apk b/app/release/app-release.apk
new file mode 100644
index 0000000..c437e68
Binary files /dev/null and b/app/release/app-release.apk differ
diff --git a/app/release/app-release.apk.zip b/app/release/app-release.apk.zip
new file mode 100644
index 0000000..f46f7c7
Binary files /dev/null and b/app/release/app-release.apk.zip differ
diff --git a/app/release/output.json b/app/release/output.json
new file mode 100644
index 0000000..c429e31
--- /dev/null
+++ b/app/release/output.json
@@ -0,0 +1 @@
+[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 522b387..37a4dce 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,17 +1,36 @@
+
+
+
+
+ android:theme="@style/AppTheme"
+ tools:ignore="GoogleAppIndexingWarning" >
+
+
@@ -19,6 +38,12 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/aidl/com/termux/x11/ITermuxService.aidl b/app/src/main/aidl/com/termux/x11/ITermuxService.aidl
new file mode 100644
index 0000000..dc2aaf8
--- /dev/null
+++ b/app/src/main/aidl/com/termux/x11/ITermuxService.aidl
@@ -0,0 +1,12 @@
+// ITermuxService.aidl
+package com.termux.x11;
+
+import android.view.Surface;
+
+interface ITermuxService {
+ oneway void windowChanged(in Surface surface, int width, int height);
+ oneway void pointerMotion(int x, int y);
+ oneway void pointerScroll(int axis, float value);
+ oneway void pointerButton(int button, int type);
+ oneway void keyboardKey(int key, int type, int shift, String characters);
+}
diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java
new file mode 100644
index 0000000..2b9c93a
--- /dev/null
+++ b/app/src/main/java/com/termux/x11/LoriePreferences.java
@@ -0,0 +1,118 @@
+package com.termux.x11;
+
+import android.content.SharedPreferences;
+import android.inputmethodservice.Keyboard;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.MenuItem;
+
+public class LoriePreferences extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
+ LoriePreferenceFragment loriePreferenceFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ loriePreferenceFragment = new LoriePreferenceFragment();
+
+ getFragmentManager().beginTransaction().replace(android.R.id.content, loriePreferenceFragment).commit();
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setHomeButtonEnabled(true);
+ actionBar.setTitle("Preferences");
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (loriePreferenceFragment != null)
+ loriePreferenceFragment.getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+
+ }
+
+ @Override
+ public void onPause() {
+ if (loriePreferenceFragment != null)
+ loriePreferenceFragment.getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+ super.onPause();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+
+ if (id == android.R.id.home) {
+ finish();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ LorieService.start(LorieService.ACTION_PREFERENCES_CHAGED);
+ }
+
+ public static class LoriePreferenceFragment extends PreferenceFragment implements PreferenceScreen.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+
+ String showImeEnabled = Settings.Secure.getString(getActivity().getContentResolver(), SHOW_IME_WITH_HARD_KEYBOARD);
+ SharedPreferences.Editor p = getPreferenceManager().getSharedPreferences().edit();
+ p.putBoolean("showIMEWhileExternalConnected", showImeEnabled.equals("1"));
+ p.apply();
+
+ PreferenceScreen s = getPreferenceScreen();
+ for (int i=0; i= Build.VERSION_CODES.O) {
+ act.startForegroundService(intent);
+ } else {
+ act.startService(intent);
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ if (isServiceRunningInForeground(this, LorieService.class)) return;
+
compositor = createLorieThread();
if (compositor == 0) {
Log.e("LorieService", "compositor thread was not created");
return;
}
- int dp = (int) act.getResources().getDisplayMetrics().density;
- ViewGroup.LayoutParams match_parent = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- setLayoutParams(match_parent);
+ instance = this;
+ Toast.makeText(this, "Service was Created", Toast.LENGTH_LONG).show();
+ Log.e("LorieService", "created");
- SurfaceView lorieView = new SurfaceView(act);
- addView(lorieView, match_parent);
+ Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
+ notificationIntent.putExtra("foo_bar_extra_key", "foo_bar_extra_value");
+ notificationIntent.setAction(Long.toString(System.currentTimeMillis()));
- RelativeLayout rl = new RelativeLayout(act);
- ScrollView sv = new ScrollView(act);
- kbd = new AdditionalKeyboardView(act);
- RelativeLayout.LayoutParams svlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
- RelativeLayout.LayoutParams kbdlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 50*dp);
+ Intent exitIntent = new Intent(getApplicationContext(), LorieService.class);
+ exitIntent.setAction(ACTION_STOP_SERVICE);
- svlp.addRule(RelativeLayout.BELOW, SV2_ID);
- kbdlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
- kbd.setId(SV2_ID);
- rl.addView(kbd, kbdlp);
- rl.addView(sv, svlp);
- addView(rl);
+ Intent preferencesIntent = new Intent(getApplicationContext(), LoriePreferences.class);
+ preferencesIntent.setAction(ACTION_START_PREFERENCES_ACTIVITY);
- kbd.setVisibility(View.INVISIBLE);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent pendingExitIntent = PendingIntent.getService(getApplicationContext(), 0, exitIntent, 0);
+ PendingIntent pendingPreferencesIntent = PendingIntent.getActivity(getApplicationContext(), 0, preferencesIntent, 0);
- lorieView.getHolder().addCallback(this);
- lorieView.setOnTouchListener(this);
- lorieView.setOnKeyListener(this);
+ //For creating the Foreground Service
+ int priority = Notification.PRIORITY_HIGH;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
+ priority = NotificationManager.IMPORTANCE_HIGH;
+ NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : "";
+ Notification notification = new NotificationCompat.Builder(this, channelId)
+ .setContentTitle("Termux:X11")
+ .setSmallIcon(R.drawable.ic_x11_icon)
+ .setContentText("is running in foreground")
+ .setContentIntent(pendingIntent)
+ .setOngoing(true)
+ .setPriority(priority)
+ .setShowWhen(false)
+ .setColor(0xFF607D8B)
+ .addAction(0, "Preferences", pendingPreferencesIntent)
+ .addAction(0, "Exit", pendingExitIntent)
+ .build();
+ startForeground(1, notification);
- lorieView.setFocusable(true);
- lorieView.setFocusableInTouchMode(true);
- lorieView.requestFocus();
-
- kbd.reload(keys, lorieView, this);
-
- lorieView.setOnHoverListener(this);
- lorieView.setOnGenericMotionListener(this);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
- setPointerIcon(PointerIcon.getSystemIcon(act, PointerIcon.TYPE_NULL));
- // prevents SurfaceView from being resized but interrupts additional keyboard showing process
- // act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
-
- touchscreenMode = TOUCH_MODE_TOUCHSCREEN;
- tpListener = new touchPadListener(this);
- tsListener = new touchScreenListener(this);
- hmListener = new hardwareMouseListener(this);
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ String packageName = getPackageName();
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ if (!pm.isIgnoringBatteryOptimizations(packageName)) {
+ Intent whitelist = new Intent();
+ whitelist.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ whitelist.setData(Uri.parse("package:" + packageName));
+ whitelist.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(whitelist);
+ }
+ }
}
- void finishInit() {
- KeyboardUtils.addKeyboardToggleListener(act, this);
+ @RequiresApi(Build.VERSION_CODES.O)
+ private String getNotificationChannel(NotificationManager notificationManager){
+ String channelId = getResources().getString(R.string.app_name);
+ String channelName = getResources().getString(R.string.app_name);
+ NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
+ channel.setImportance(NotificationManager.IMPORTANCE_NONE);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
+ notificationManager.createNotificationChannel(channel);
+ return channelId;
+ }
+
+ public static boolean isServiceRunningInForeground(Context context, Class> serviceClass) {
+ ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
+ if (serviceClass.getName().equals(service.service.getClassName())) {
+ return service.foreground;
+ }
+ }
+ return false;
+ }
+
+ private static void onPreferencesChanged() {
+ if (instance == null || act == null) return;
+
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(instance);
+
+ instance.mTP.setMode(
+ Integer.parseInt(
+ preferences.getString("touchMode", "1")
+ )
+ );
+ act.showAdditionalKbd = preferences.getBoolean("showAdditionalKbd", true);
+ Log.e("LorieService", "Preferences changed");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.e("LorieService", "start");
+ String action = intent.getAction();
+
+ if (action.equals(ACTION_START_FROM_ACTIVITY)) {
+ act.onLorieServiceStart(this);
+ }
+
+ if (action.equals(ACTION_STOP_SERVICE)) {
+ Log.e("LorieService", action);
+ terminate();
+ sleep(500);
+ act.finish();
+ stopSelf();
+ }
+
+ onPreferencesChanged();
+
+ return START_STICKY;
+ }
+
+
+ @Override
+ public void onDestroy() {
+ Log.e("LorieService", "destroyed");
+ }
+
+ static LorieService getInstance() {
+ if (instance == null) {
+ Log.e("LorieService", "Instance was requested, but no instances available");
+ }
+ return instance;
+ }
+
+ void setListeners(@NonNull SurfaceView view) {
+ Context a = view.getRootView().findViewById(android.R.id.content).getContext();
+ if (!(a instanceof MainActivity)) {
+ Log.e("LorieService", "Context is not an activity!!!");
+ }
+
+ act = (MainActivity) a;
+
+ view.setFocusable(true);
+ view.setFocusableInTouchMode(true);
+ view.requestFocus();
+
+ listener.svc = this;
+ listener.setAsListenerTo(view);
+
+ mTP = new TouchParser(view, listener);
+ onPreferencesChanged();
+ }
+
+ static View.OnKeyListener getOnKeyListener() {
+ return listener;
}
void terminate() {
terminate(compositor);
compositor = 0;
+ Log.e("LorieService", "terminate");
}
@Override
- public void surfaceCreated(SurfaceHolder holder) {
-
+ public IBinder onBind(Intent intent) {
+ return null;
}
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- DisplayMetrics dm = new DisplayMetrics();
- int mmWidth, mmHeight;
- act.getWindowManager().getDefaultDisplay().getMetrics(dm);
+ private static class ServiceEventListener implements SurfaceHolder.Callback, View.OnTouchListener, View.OnKeyListener, View.OnHoverListener, View.OnGenericMotionListener, TouchParser.OnTouchParseListener {
+ LorieService svc;
- if (act.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- mmWidth = (int) Math.round((width * 25.4) / dm.xdpi);
- mmHeight = (int) Math.round((height * 25.4) / dm.ydpi);
- } else {
- mmWidth = (int) Math.round((width * 25.4) / dm.ydpi);
- mmHeight = (int) Math.round((height * 25.4) / dm.xdpi);
+ private void setAsListenerTo(SurfaceView view) {
+ view.getHolder().addCallback(this);
+ view.setOnTouchListener(this);
+ view.setOnHoverListener(this);
+ view.setOnGenericMotionListener(this);
+ view.setOnKeyListener(this);
+ surfaceChanged(view.getHolder(), 0, view.getWidth(), view.getHeight());
}
- windowChanged(compositor, holder.getSurface(), width, height, mmWidth, mmHeight);
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
-
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent e) {
- switch(e.getSource()) {
- case InputDevice.SOURCE_TOUCHSCREEN:
- switch(touchscreenMode) {
- case TOUCH_MODE_TOUCHPAD: return tpListener.onTouch(v, e);
- case TOUCH_MODE_TOUCHSCREEN: return tsListener.onTouch(v, e);
- default: return tsListener.onTouch(v, e);
- }
- case InputDevice.SOURCE_MOUSE: return hmListener.onTouch(v, e);
- default: return false;
- }
- }
-
- private void toggleKeyboard() {
- if (!hardwareKeyboardConnected)
- KeyboardUtils.toggleKeyboardVisibility(act);
- else {
- boolean isKbdVisible = kbd.getVisibility() == View.VISIBLE;
- kbd.setVisibility(isKbdVisible ? View.INVISIBLE : View.VISIBLE);
- }
- }
-
- static boolean rightPressed = false; // Prevent right button press event from being repeated
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent e) {
- int action = 0;
-
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- if (e.getSource() == InputDevice.SOURCE_MOUSE && rightPressed != (e.getAction() == KeyEvent.ACTION_DOWN)) {
- pointerButton(compositor, BTN_RIGHT, (e.getAction() == KeyEvent.ACTION_DOWN) ? WL_STATE_PRESSED : WL_STATE_RELEASED);
- rightPressed = (e.getAction() == KeyEvent.ACTION_DOWN);
- } else if (e.getAction() == KeyEvent.ACTION_UP) {
- toggleKeyboard();
- }
- return true;
+ public void onPointerButton(int button, int state) {
+ if (svc == null) return;
+ svc.pointerButton(button, state);
}
- if (e.getAction() == KeyEvent.ACTION_DOWN) action = WL_STATE_PRESSED;
- if (e.getAction() == KeyEvent.ACTION_UP) action = WL_STATE_RELEASED;
- keyboardKey(compositor, action, keyCode, e.isShiftPressed() ? 1 : 0, e.getCharacters());
- //onKey(compositor, action, keyCode, e.isShiftPressed() ? 1 : 0, e.getCharacters());
- return true;
- }
-
- @Override
- public void onToggleSoftKeyboard(boolean isVisible) {
- kbd.setVisibility((isVisible)?View.VISIBLE:View.INVISIBLE);
- //Log.d("LorieActivity", "keyboard is " + (isVisible?"visible":"not visible"));
- }
-
- public void onConfigurationChanged(Configuration config) {
- hardwareKeyboardConnected = config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
- }
-
- @Override
- public boolean onGenericMotion(View v, MotionEvent e) {
- onTouch(v, e);
- return true;
- }
-
- @Override
- public boolean onHover(View v, MotionEvent e) {
- onTouch(v, e);
- return true;
- }
-
- private class hardwareMouseListener implements View.OnTouchListener {
- private int savedBS = 0;
- private int currentBS = 0;
- private LorieService svc;
-
- hardwareMouseListener(LorieService service) { svc = service; }
-
- boolean isMouseButtonChanged(int mask) {
- return (savedBS & mask) != (currentBS & mask);
+ public void onPointerMotion(int x, int y) {
+ if (svc == null) return;
+ svc.pointerMotion(x, y);
}
- int mouseButtonState(int mask) {
- return ((currentBS & mask) != 0) ? WL_STATE_PRESSED : WL_STATE_RELEASED;
+ public void onPointerScroll(int axis, float value) {
+ if (svc == null) return;
+ svc.pointerScroll(axis, value);
}
- public boolean onTouch(View v, MotionEvent e) {
- if (e.getAction() == MotionEvent.ACTION_SCROLL) {
- float scrollY = -5 * e.getAxisValue(MotionEvent.AXIS_VSCROLL);
- float scrollX = -5 * e.getAxisValue(MotionEvent.AXIS_HSCROLL);
-
- if (scrollY != 0) pointerScroll(WL_POINTER_AXIS_VERTICAL_SCROLL, scrollY);
- if (scrollX != 0) pointerScroll(WL_POINTER_AXIS_HORIZONTAL_SCROLL, scrollX);
- return true;
- }
-
- svc.pointerMotion(e.getX(), e.getY());
-
- currentBS = e.getButtonState();
- if (isMouseButtonChanged(MotionEvent.BUTTON_PRIMARY)) {
- svc.pointerButton(svc.compositor, BTN_LEFT, mouseButtonState(MotionEvent.BUTTON_PRIMARY));
- }
- if (isMouseButtonChanged(MotionEvent.BUTTON_TERTIARY)) {
- svc.pointerButton(svc.compositor, BTN_MIDDLE, mouseButtonState(MotionEvent.BUTTON_TERTIARY));
- }
- if (isMouseButtonChanged(MotionEvent.BUTTON_SECONDARY)) {
- svc.pointerButton(svc.compositor, BTN_RIGHT, mouseButtonState(MotionEvent.BUTTON_SECONDARY));
- }
- savedBS = currentBS;
- return true;
- }
- }
-
- private class touchScreenListener implements View.OnTouchListener {
- private LorieService svc;
-
- touchScreenListener(LorieService service) { svc = service; }
-
- public boolean onTouch(View v, MotionEvent e) {
- int type = WL_POINTER_MOTION;
- switch (e.getAction()) {
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_BUTTON_PRESS:
- case MotionEvent.ACTION_DOWN:
- type = WL_STATE_PRESSED;
- break;
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_BUTTON_RELEASE:
- case MotionEvent.ACTION_UP:
- type = WL_STATE_RELEASED;
- break;
- }
-
- svc.pointerMotion(e.getX(), e.getY());
- if (type != WL_POINTER_MOTION)
- svc.pointerButton(svc.compositor, BTN_LEFT, type);
- return true;
+ public void onTouchDown(int id, float x, float y) {
+ if (svc == null) return;
+ svc.touchDown(id, x, y);
}
- }
+ public void onTouchMotion(int id, float x, float y) {
+ if (svc == null) return;
+ svc.touchMotion(id, x, y);
+ }
- private class touchPadListener implements View.OnTouchListener {
- private int startX, startY;
- private int curX, curY;
- private int dX, dY;
- private long curTime;
- private LorieService svc;
+ public void onTouchUp(int id) {
+ if (svc == null) return;
+ svc.touchUp(id);
+ }
- touchPadListener(LorieService service) { svc = service; }
+ public void onTouchFrame() {
+ if (svc == null) return;
+ svc.touchFrame();
+ }
@Override
public boolean onTouch(View v, MotionEvent e) {
- int X, Y;
- switch (e.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- startX = (int) e.getX();
- startY = (int) e.getY();
- curTime= System.currentTimeMillis();
- break;
- case MotionEvent.ACTION_MOVE:
+ if (svc == null) return false;
+ return svc.mTP.onTouchEvent(e);
+ }
- X = (int) e.getX();
- Y = (int) e.getY();
- //ClientThread.update(Integer.toString(X - startX) + " " + Integer.toString(Y - startY));
- curX = X;
- curY = Y;
- if (dX < curX - startX) dX = curX - startX;
- if (dY < curY - startY) dY = curY - startY;
- break;
- case MotionEvent.ACTION_UP:
- long duration=System.currentTimeMillis()-curTime;
- if(duration<100) {
- Log.d("Click",Long.toString(duration));
- //ClientThread.update("3");
- }
- break;
+ @Override
+ public boolean onGenericMotion(View v, MotionEvent e) {
+ if (svc == null) return false;
+ return svc.mTP.onTouchEvent(e);
+ }
+
+ @Override
+ public boolean onHover(View v, MotionEvent e) {
+ if (svc == null) return false;
+ return svc.mTP.onTouchEvent(e);
+ }
+
+ private boolean isSource(KeyEvent e, int source) {
+ return (e.getSource() & source) == source;
+ }
+
+ private boolean rightPressed = false; // Prevent right button press event from being repeated
+ private boolean middlePressed = false; // Prevent middle button press event from being repeated
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent e) {
+ if (svc == null) return false;
+ int action = 0;
+
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (
+ isSource(e, InputDevice.SOURCE_MOUSE) &&
+ rightPressed != (e.getAction() == KeyEvent.ACTION_DOWN)
+ ) {
+ svc.pointerButton(TouchParser.BTN_RIGHT, (e.getAction() == KeyEvent.ACTION_DOWN) ? TouchParser.ACTION_DOWN : TouchParser.ACTION_UP);
+ rightPressed = (e.getAction() == KeyEvent.ACTION_DOWN);
+ } else if (e.getAction() == KeyEvent.ACTION_UP) {
+ KeyboardUtils.toggleKeyboardVisibility(act);
+ }
+ return true;
}
+
+ if (
+ keyCode == KeyEvent.KEYCODE_MENU &&
+ isSource(e, InputDevice.SOURCE_MOUSE) &&
+ middlePressed != (e.getAction() == KeyEvent.ACTION_DOWN)
+ ) {
+ svc.pointerButton(TouchParser.BTN_MIDDLE, (e.getAction() == KeyEvent.ACTION_DOWN) ? TouchParser.ACTION_DOWN : TouchParser.ACTION_UP);
+ middlePressed = (e.getAction() == KeyEvent.ACTION_DOWN);
+ return true;
+ }
+
+ if (e.getAction() == KeyEvent.ACTION_DOWN) action = TouchParser.ACTION_DOWN;
+ if (e.getAction() == KeyEvent.ACTION_UP) action = TouchParser.ACTION_UP;
+ svc.keyboardKey(action, keyCode, e.isShiftPressed() ? 1 : 0, e.getCharacters());
return true;
}
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ DisplayMetrics dm = new DisplayMetrics();
+ int mmWidth, mmHeight;
+ act.getWindowManager().getDefaultDisplay().getMetrics(dm);
+
+ if (act.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ mmWidth = (int) Math.round((width * 25.4) / dm.xdpi);
+ mmHeight = (int) Math.round((height * 25.4) / dm.ydpi);
+ } else {
+ mmWidth = (int) Math.round((width * 25.4) / dm.ydpi);
+ mmHeight = (int) Math.round((height * 25.4) / dm.xdpi);
+ }
+
+ svc.windowChanged(holder.getSurface(), width, height, mmWidth, mmHeight);
+ }
+
+ @Override public void surfaceCreated(SurfaceHolder holder) {}
+ @Override public void surfaceDestroyed(SurfaceHolder holder) {}
+
+ }
+
+ void sleep(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
}
+ private void windowChanged(Surface s, int w, int h, int pw, int ph) {windowChanged(compositor, s, w, h, pw, ph);}
+ private void touchDown(int id, float x, float y) { touchDown(compositor, id, (int) x, (int) y); }
+ private void touchMotion(int id, float x, float y) { touchMotion(compositor, id, (int) x, (int) y); }
+ private void touchUp(int id) { touchUp(compositor, id); }
+ private void touchFrame() { touchFrame(compositor); }
private void pointerMotion(float x, float y) { pointerMotion(compositor, (int) x, (int) y); }
private void pointerScroll(int axis, float value) { pointerScroll(compositor, axis, value); }
private void pointerButton(int button, int type) { pointerButton(compositor, button, type); }
+ private void keyboardKey(int key, int type, int shift, String characters) {keyboardKey(compositor, key, type, shift, characters);}
private native long createLorieThread();
private native void windowChanged(long compositor, Surface surface, int width, int height, int mmWidth, int mmHeight);
+ private native void touchDown(long compositor, int id, int x, int y);
+ private native void touchMotion(long compositor, int id, int x, int y);
+ private native void touchUp(long compositor, int id);
+ private native void touchFrame(long compositor);
private native void pointerMotion(long compositor, int x, int y);
private native void pointerScroll(long compositor, int axis, float value);
private native void pointerButton(long compositor, int button, int type);
diff --git a/app/src/main/java/com/termux/x11/LorieTestService.java b/app/src/main/java/com/termux/x11/LorieTestService.java
new file mode 100644
index 0000000..6c18e65
--- /dev/null
+++ b/app/src/main/java/com/termux/x11/LorieTestService.java
@@ -0,0 +1,69 @@
+package com.termux.x11;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.support.annotation.RequiresApi;
+import android.support.v4.app.NotificationCompat;
+
+public class LorieTestService extends Service {
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ static void start(Context context) {
+ Intent intent = new Intent(context, LorieTestService.class);
+ intent.setAction("start");
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ context.startForegroundService(intent);
+ } else {
+ context.startService(intent);
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
+ notificationIntent.putExtra("foo_bar_extra_key", "foo_bar_extra_value");
+ notificationIntent.setAction(Long.toString(System.currentTimeMillis()));
+
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ //For creating the Foreground Service
+ int priority = Notification.PRIORITY_HIGH;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
+ priority = NotificationManager.IMPORTANCE_HIGH;
+ NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : "";
+ Notification notification = new NotificationCompat.Builder(this, channelId)
+ .setContentTitle("Termux:X11 Test service")
+ .setSmallIcon(R.drawable.ic_x11_icon)
+ .setContentText("foreground service")
+ .setContentIntent(pendingIntent)
+ .setOngoing(true)
+ .setPriority(priority)
+ .setShowWhen(false)
+ .setColor(0xFF607D8B)
+ .build();
+ startForeground(3, notification);
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private String getNotificationChannel(NotificationManager notificationManager){
+ String channelId = getResources().getString(R.string.app_name);
+ String channelName = getResources().getString(R.string.app_name);
+ NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
+ channel.setImportance(NotificationManager.IMPORTANCE_NONE);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
+ notificationManager.createNotificationChannel(channel);
+ return channelId;
+ }
+}
diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java
index 3d178a6..e996d61 100644
--- a/app/src/main/java/com/termux/x11/MainActivity.java
+++ b/app/src/main/java/com/termux/x11/MainActivity.java
@@ -1,36 +1,100 @@
package com.termux.x11;
-import android.content.res.Configuration;
+import android.content.SharedPreferences;
+import android.inputmethodservice.InputMethodService;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.PointerIcon;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.Window;
import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+
+public class MainActivity extends AppCompatActivity implements KeyboardUtils.SoftKeyboardToggleListener {
+
+ private static int[] keys = {
+ KeyEvent.KEYCODE_ESCAPE,
+ KeyEvent.KEYCODE_TAB,
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_UP,
+ KeyEvent.KEYCODE_DPAD_DOWN,
+ KeyEvent.KEYCODE_DPAD_LEFT,
+ KeyEvent.KEYCODE_DPAD_RIGHT,
+ };
+
+ AdditionalKeyboardView kbd;
+ boolean showAdditionalKbd = true;
-public class MainActivity extends AppCompatActivity {
- LorieService svc;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ LorieService.setMainActivity(this);
+ LorieService.start(LorieService.ACTION_START_FROM_ACTIVITY);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- svc = new LorieService(this);
- setContentView(svc);
- svc.finishInit();
- svc.onConfigurationChanged(getResources().getConfiguration());
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+ setContentView(R.layout.main_activity);
+
+ kbd = findViewById(R.id.additionalKbd);
+ kbd.setVisibility(View.INVISIBLE);
+ KeyboardUtils.addKeyboardToggleListener(this, this);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ getWindow().
+ getDecorView().
+ setPointerIcon(PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
+ getResources().getConfiguration();
}
+
+ public void onLorieServiceStart(LorieService instance) {
+ SurfaceView lorieView = findViewById(R.id.lorieView);
+
+ instance.setListeners(lorieView);
+ kbd.reload(keys, lorieView, LorieService.getOnKeyListener());
+ }
+
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig );
- svc.onConfigurationChanged(newConfig);
+ public void onWindowFocusChanged(boolean hasFocus)
+ {
+ super.onWindowFocusChanged(hasFocus);
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ Window window = getWindow();
+ View decorView = window.getDecorView();
+
+ if (hasFocus && preferences.getBoolean("fullscreen", false))
+ {
+ window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ decorView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ } else {
+ window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ decorView.setSystemUiVisibility(0);
+ }
+ }
+
+
+ @Override
+ public void onToggleSoftKeyboard(boolean isVisible) {
+ if (kbd != null && showAdditionalKbd)
+ kbd.setVisibility((isVisible)?View.VISIBLE:View.INVISIBLE);
}
@Override
public void onBackPressed() {}
-
-
- @Override
- public void onDestroy() {
- svc.terminate();
- super.onDestroy();
- }
}
diff --git a/app/src/main/java/com/termux/x11/TouchParser.java b/app/src/main/java/com/termux/x11/TouchParser.java
new file mode 100644
index 0000000..00fdaf4
--- /dev/null
+++ b/app/src/main/java/com/termux/x11/TouchParser.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.termux.x11;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+@SuppressWarnings("unused")
+public class TouchParser {
+
+ private static final int WL_STATE_PRESSED = 1;
+ private static final int WL_STATE_RELEASED = 0;
+
+ private static final int WL_POINTER_AXIS_VERTICAL_SCROLL = 0;
+ private static final int WL_POINTER_AXIS_HORIZONTAL_SCROLL = 1;
+
+ static final int TOUCH_MODE_DIRECT = 1;
+ static final int TOUCH_MODE_MOUSE = 2;
+ static final int TOUCH_MODE_TOUCHPAD = 3;
+
+ static final int BTN_LEFT = 0x110;
+ static final int BTN_RIGHT = 0x111;
+ static final int BTN_MIDDLE = 0x112;
+
+ static final int ACTION_DOWN = WL_STATE_PRESSED;
+ static final int ACTION_UP = WL_STATE_RELEASED;
+
+ private static final int AXIS_X = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
+ private static final int AXIS_Y = WL_POINTER_AXIS_VERTICAL_SCROLL;
+
+ public interface OnTouchParseListener {
+ void onPointerButton(int button, int state);
+ void onPointerMotion(int x, int y);
+ void onPointerScroll(int axis, float value);
+
+ void onTouchDown(int id, float x, float y);
+ void onTouchMotion(int id, float x, float y);
+ void onTouchUp(int id);
+ void onTouchFrame();
+ }
+
+ private int mTouchSlopSquare;
+ private int mDoubleTapTouchSlopSquare;
+ private int mDoubleTapSlopSquare;
+
+ private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
+ private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
+ private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
+ private static final int DOUBLE_TAP_MIN_TIME = 40;
+ private static final int FLAG_IS_GENERATED_GESTURE = 0x8;
+ private static final int TOUCH_SLOP = 8;
+ private static final int DOUBLE_TAP_SLOP = 100;
+ private static final int DOUBLE_TAP_TOUCH_SLOP = TOUCH_SLOP;
+
+ // constants for Message.what used by GestureHandler below
+ private static final int SHOW_PRESS = 1;
+ private static final int LONG_PRESS = 2;
+ private static final int TAP = 3;
+ private static final int RIGHT_TAP = 4;
+ private static final int TOUCH_FRAME = 5;
+
+ private final Handler mHandler;
+ private final OnTouchParseListener mListener;
+ private final HardwareMouseListener hmListener;
+
+ private boolean mStillDown;
+ private boolean mDeferConfirmSingleTap;
+ private boolean mInLongPress;
+ private boolean mInContextClick;
+ private boolean mAlwaysInTapRegion;
+ private boolean mAlwaysInBiggerTapRegion;
+
+ private MotionEvent mCurrentDownEvent;
+ private MotionEvent mPreviousUpEvent;
+
+ private boolean mIsDoubleTapping;
+
+ private float mLastFocusX;
+ private float mLastFocusY;
+ private float mDownFocusX;
+ private float mDownFocusY;
+
+ private boolean mIsLongpressEnabled;
+
+ private View target;
+ private Point cursor;
+ private int mMode = TOUCH_MODE_DIRECT;
+
+ private boolean mInDrag = false;
+ private boolean mWasInDrag = false;
+
+ @SuppressLint("HandlerLeak")
+ private class GestureHandler extends Handler {
+ GestureHandler() {
+ super();
+ }
+
+ GestureHandler(Handler handler) {
+ super(handler.getLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SHOW_PRESS:
+ break;
+
+ case LONG_PRESS:
+ dispatchLongPress();
+ break;
+
+ case TAP:
+ // If the user's finger is still down, do not count it as a tap
+ if (!mStillDown) {
+ Log.e("tp", "tap");
+ mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
+ mListener.onPointerButton(BTN_LEFT, ACTION_UP);
+ } else {
+ mDeferConfirmSingleTap = true;
+ }
+ break;
+ case RIGHT_TAP:
+ break;
+
+ default:
+ throw new RuntimeException("Unknown message " + msg); //never
+ }
+ }
+ }
+
+
+ TouchParser(View view, OnTouchParseListener listener) {
+ mListener = listener;
+ hmListener = new HardwareMouseListener();
+ mHandler = new GestureHandler();
+
+ target = view;
+ cursor = new Point(target.getWidth()/2, target.getHeight()/2);
+
+ init();
+ }
+
+ private void init() {
+ Context context;
+ if (target == null) {
+ Log.e("TouchParser","TouchParser did not receive target view");
+ context = null;
+ }
+ else
+ context = target.getRootView().findViewById(android.R.id.content).getContext();
+
+ mIsLongpressEnabled = true;
+
+ // Fallback to support pre-donuts releases
+ int touchSlop, doubleTapSlop, doubleTapTouchSlop;
+ if (context == null) {
+ //noinspection deprecation
+ touchSlop = ViewConfiguration.getTouchSlop();
+ doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
+ doubleTapSlop = DOUBLE_TAP_SLOP;
+ //noinspection
+ } else {
+ final ViewConfiguration configuration = ViewConfiguration.get(context);
+ touchSlop = configuration.getScaledTouchSlop();
+ doubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
+ doubleTapSlop = configuration.getScaledDoubleTapSlop();
+ }
+ mTouchSlopSquare = touchSlop * touchSlop;
+ mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
+ mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
+ }
+
+ void setMode(int mode) {
+ mMode = mode;
+ }
+
+ public void setIsLongpressEnabled(boolean isLongpressEnabled) {
+ mIsLongpressEnabled = isLongpressEnabled;
+ }
+
+ public boolean isLongpressEnabled() {
+ return mIsLongpressEnabled;
+ }
+
+ boolean onTouchEvent(MotionEvent ev) {
+ if ((ev.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
+ return hmListener.onTouch(ev);
+ }
+
+ if (mMode == TOUCH_MODE_DIRECT) {
+ // get pointer index from the event object
+ int pointerIndex = ev.getActionIndex();
+
+ // get pointer ID
+ int pointerId = ev.getPointerId(pointerIndex);
+
+ switch(ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ mListener.onTouchDown(pointerId, ev.getX(pointerIndex), ev.getY(pointerIndex));
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ mListener.onTouchUp(pointerId);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ for (int i=0; i< ev.getPointerCount(); i++)
+ mListener.onTouchMotion(ev.getPointerId(i), ev.getX(i), ev.getY(i));
+ break;
+ }
+
+ mListener.onTouchFrame();
+ return true;
+ }
+
+ final int action = ev.getAction();
+ final boolean pointerUp =
+ (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
+ final boolean isGeneratedGesture =
+ (ev.getFlags() & FLAG_IS_GENERATED_GESTURE) != 0;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += ev.getX(i);
+ sumY += ev.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ final float focusX = sumX / div;
+ final float focusY = sumY / div;
+
+ boolean handled = false;
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_POINTER_DOWN:
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
+ // Cancel long press and taps
+ stopDrag();
+ cancelTaps();
+ mHandler.sendEmptyMessageDelayed(RIGHT_TAP, TAP_TIMEOUT);
+
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
+ stopDrag();
+
+ break;
+
+ case MotionEvent.ACTION_DOWN:
+ boolean hadTapMessage = mHandler.hasMessages(TAP);
+ if (hadTapMessage) {
+ mHandler.removeMessages(TAP);
+ mHandler.removeMessages(RIGHT_TAP);
+ }
+ if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null)
+ && hadTapMessage
+ && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
+ // This is a second tap
+ mIsDoubleTapping = true;
+ } else {
+ // This is a first tap
+ mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
+ }
+
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
+ if (mCurrentDownEvent != null) {
+ mCurrentDownEvent.recycle();
+ }
+ mCurrentDownEvent = MotionEvent.obtain(ev);
+ mAlwaysInTapRegion = true;
+ mAlwaysInBiggerTapRegion = true;
+ mStillDown = true;
+ mInLongPress = false;
+ mDeferConfirmSingleTap = false;
+
+ if (mIsLongpressEnabled) {
+ mHandler.removeMessages(LONG_PRESS);
+ if (!mIsDoubleTapping)
+ mHandler.sendEmptyMessageAtTime(LONG_PRESS,
+ mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
+ }
+ if (!mIsDoubleTapping)
+ mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
+ mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
+ handled = true;
+
+ if (mMode == TOUCH_MODE_MOUSE)
+ mListener.onPointerMotion((int) ev.getX(), (int) ev.getY());
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (mInContextClick) {
+ break;
+ }
+
+ final float scrollX = mLastFocusX - focusX;
+ final float scrollY = mLastFocusY - focusY;
+ if (mAlwaysInTapRegion) {
+ final int deltaX = (int) (focusX - mDownFocusX);
+ final int deltaY = (int) (focusY - mDownFocusY);
+ int distance = (deltaX * deltaX) + (deltaY * deltaY);
+ int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare;
+ if (distance > slopSquare) {
+ handled = true;
+ onScroll(ev, scrollX, scrollY);
+ mLastFocusX = focusX;
+ mLastFocusY = focusY;
+ mAlwaysInTapRegion = false;
+ mHandler.removeMessages(TAP);
+ mHandler.removeMessages(RIGHT_TAP);
+ mHandler.removeMessages(SHOW_PRESS);
+ mHandler.removeMessages(LONG_PRESS);
+ }
+ int doubleTapSlopSquare = isGeneratedGesture ? 0 : mDoubleTapTouchSlopSquare;
+ if (distance > doubleTapSlopSquare) {
+ mAlwaysInBiggerTapRegion = false;
+ }
+ } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
+ handled = true;
+ onScroll(ev, scrollX, scrollY);
+ mLastFocusX = focusX;
+ mLastFocusY = focusY;
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ mStillDown = false;
+ MotionEvent currentUpEvent = MotionEvent.obtain(ev);
+ if (mMode == TOUCH_MODE_MOUSE) {
+ stopDrag();
+ }
+
+ if (mIsDoubleTapping) {
+ Log.e("1", "1");
+ mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
+ mListener.onPointerButton(BTN_LEFT, ACTION_UP);
+ mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
+ mListener.onPointerButton(BTN_LEFT, ACTION_UP);
+ handled = true;
+ } else if (mInLongPress) {
+ Log.e("2", "2");
+ mHandler.removeMessages(TAP);
+ mInLongPress = false;
+ mListener.onPointerButton(BTN_LEFT, ACTION_UP);
+ } else if (mAlwaysInTapRegion) {
+ Log.e("3", "3");
+ //if (mDeferConfirmSingleTap) {
+ //handled = mListener.onSingleTapConfirmed(ev);
+ //}
+ } else if (mHandler.hasMessages(RIGHT_TAP)) {
+ //Log.e("touch", "right tap confirmed");
+ mListener.onPointerButton(BTN_RIGHT, ACTION_DOWN);
+ mListener.onPointerButton(BTN_RIGHT, ACTION_UP);
+ }
+
+ if (mPreviousUpEvent != null) {
+ mPreviousUpEvent.recycle();
+ }
+ // Hold the event we obtained above - listeners may have changed the original.
+ mPreviousUpEvent = currentUpEvent;
+ mIsDoubleTapping = false;
+ mInLongPress = false;
+ mDeferConfirmSingleTap = false;
+ mInDrag = false;
+ mWasInDrag = false;
+ mHandler.removeMessages(SHOW_PRESS);
+ mHandler.removeMessages(LONG_PRESS);
+ mHandler.removeMessages(RIGHT_TAP);
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ cancel();
+ break;
+ }
+
+ return handled;
+ }
+
+ private void onScroll(MotionEvent ev, float scrollX, float scrollY) {
+ if (ev.getPointerCount() == 1) {
+ if (mMode == TOUCH_MODE_MOUSE) {
+ if (!mWasInDrag) {
+ startDrag();
+ mListener.onPointerMotion((int) ev.getX(), (int) ev.getY());
+ }
+ return;
+ }
+
+ cursor.x -= (int) scrollX;
+ cursor.y -= (int) scrollY;
+
+ if (cursor.x < 0) cursor.x = 0;
+ if (cursor.y < 0) cursor.y = 0;
+ if (cursor.x > target.getWidth()) cursor.x = target.getWidth();
+ if (cursor.y > target.getHeight()) cursor.y = target.getHeight();
+
+ mListener.onPointerMotion(cursor.x, cursor.y);
+ } else if (ev.getPointerCount() == 2) {
+ if (scrollX != 0)
+ mListener.onPointerScroll(MotionEvent.AXIS_Y, (int)scrollX);
+ if (scrollY != 0)
+ mListener.onPointerScroll(MotionEvent.AXIS_X, (int)scrollY);
+ }
+ }
+
+ private void startDrag() {
+ if (mWasInDrag) return;
+ if (!mInDrag) {
+ Log.e("drag", "start");
+ mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
+ mInDrag = true;
+ }
+ }
+
+ private void stopDrag() {
+ if (mInDrag) {
+ Log.e("drag", "stop");
+ mListener.onPointerButton(BTN_LEFT, ACTION_UP);
+ mInDrag = false;
+ }
+ mWasInDrag = true;
+ }
+
+ private void cancel() {
+ mHandler.removeMessages(SHOW_PRESS);
+ mHandler.removeMessages(LONG_PRESS);
+ mHandler.removeMessages(TAP);
+ mHandler.removeMessages(RIGHT_TAP);
+ mIsDoubleTapping = false;
+ mStillDown = false;
+ mAlwaysInTapRegion = false;
+ mAlwaysInBiggerTapRegion = false;
+ mDeferConfirmSingleTap = false;
+ mInLongPress = false;
+ mInContextClick = false;
+ }
+
+ private void cancelTaps() {
+ mHandler.removeMessages(SHOW_PRESS);
+ mHandler.removeMessages(LONG_PRESS);
+ mHandler.removeMessages(TAP);
+ mIsDoubleTapping = false;
+ mAlwaysInTapRegion = false;
+ mAlwaysInBiggerTapRegion = false;
+ mDeferConfirmSingleTap = false;
+ mInLongPress = false;
+ mInContextClick = false;
+ }
+
+ private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
+ MotionEvent secondDown) {
+ if (!mAlwaysInBiggerTapRegion) {
+ return false;
+ }
+
+ final long deltaTime = secondDown.getEventTime() - firstUp.getEventTime();
+ if (deltaTime > DOUBLE_TAP_TIMEOUT || deltaTime < DOUBLE_TAP_MIN_TIME) {
+ return false;
+ }
+
+ int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
+ int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
+ final boolean isGeneratedGesture =
+ (firstDown.getFlags() & FLAG_IS_GENERATED_GESTURE) != 0;
+ int slopSquare = isGeneratedGesture ? 0 : mDoubleTapSlopSquare;
+ return (deltaX * deltaX + deltaY * deltaY < slopSquare);
+ }
+
+ private void dispatchLongPress() {
+ mHandler.removeMessages(TAP);
+ mDeferConfirmSingleTap = false;
+ mInLongPress = true;
+ Log.e("tp", "long press");
+ mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
+ }
+
+
+
+ private class HardwareMouseListener {
+ private int savedBS = 0;
+ private int currentBS = 0;
+
+ boolean isMouseButtonChanged(int mask) {
+ return (savedBS & mask) != (currentBS & mask);
+ }
+
+ int mouseButtonState(int mask) {
+ return ((currentBS & mask) != 0) ? ACTION_DOWN : ACTION_UP;
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ boolean onTouch(MotionEvent e) {
+ if (e.getAction() == MotionEvent.ACTION_SCROLL) {
+ float scrollY = -5 * e.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ float scrollX = -5 * e.getAxisValue(MotionEvent.AXIS_HSCROLL);
+
+ if (scrollY != 0) mListener.onPointerScroll(AXIS_Y, scrollY);
+ if (scrollX != 0) mListener.onPointerScroll(AXIS_X, scrollX);
+ return true;
+ }
+
+ mListener.onPointerMotion((int) e.getX(), (int) e.getY());
+
+ currentBS = e.getButtonState();
+ if (isMouseButtonChanged(MotionEvent.BUTTON_PRIMARY)) {
+ mListener.onPointerButton(BTN_LEFT, mouseButtonState(MotionEvent.BUTTON_PRIMARY));
+ }
+ if (isMouseButtonChanged(MotionEvent.BUTTON_TERTIARY)) {
+ mListener.onPointerButton(BTN_MIDDLE, mouseButtonState(MotionEvent.BUTTON_TERTIARY));
+ }
+ if (isMouseButtonChanged(MotionEvent.BUTTON_SECONDARY)) {
+ mListener.onPointerButton(BTN_RIGHT, mouseButtonState(MotionEvent.BUTTON_SECONDARY));
+ }
+ savedBS = currentBS;
+ return true;
+ }
+ }
+}
diff --git a/app/src/main/jni/lorie/backend/android/android-app.cpp b/app/src/main/jni/lorie/backend/android/android-app.cpp
index 490ff59..37fc776 100644
--- a/app/src/main/jni/lorie/backend/android/android-app.cpp
+++ b/app/src/main/jni/lorie/backend/android/android-app.cpp
@@ -122,7 +122,7 @@ void LorieBackendAndroid::get_keymap(int *fd, int *size) {
}
void LorieBackendAndroid::window_change_callback(EGLNativeWindowType win, uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height) {
-
+ LOGE("WindowChangeCallback");
helper.setWindow(win);
output_resize(width, height, physical_width, physical_height);
}
@@ -194,6 +194,42 @@ JNI_DECLARE(LorieService, windowChanged)(JNIEnv *env, jobject __unused instance,
b->queue.call(&LorieBackendAndroid::window_change_callback, b, win, width, height, mmWidth, mmHeight);
}
+extern "C" JNIEXPORT void JNICALL
+JNI_DECLARE(LorieService, touchDown)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor, jint id, jint x, jint y) {
+ if (jcompositor == 0) return;
+ LorieBackendAndroid *b = fromLong(jcompositor);
+ LOGV("JNI: touch down");
+
+ b->touch_down(static_cast(id), static_cast(x), static_cast(y));
+}
+
+extern "C" JNIEXPORT void JNICALL
+JNI_DECLARE(LorieService, touchMotion)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor, jint id, jint x, jint y) {
+ if (jcompositor == 0) return;
+ LorieBackendAndroid *b = fromLong(jcompositor);
+ LOGV("JNI: touch motion");
+
+ b->touch_motion(static_cast(id), static_cast(x), static_cast(y));
+}
+
+extern "C" JNIEXPORT void JNICALL
+JNI_DECLARE(LorieService, touchUp)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor, jint id) {
+ if (jcompositor == 0) return;
+ LorieBackendAndroid *b = fromLong(jcompositor);
+ LOGV("JNI: touch up");
+
+ b->touch_up(static_cast(id));
+}
+
+extern "C" JNIEXPORT void JNICALL
+JNI_DECLARE(LorieService, touchFrame)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor) {
+ if (jcompositor == 0) return;
+ LorieBackendAndroid *b = fromLong(jcompositor);
+ LOGV("JNI: touch frame");
+
+ b->touch_frame();
+}
+
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, pointerMotion)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor, jint x, jint y) {
if (jcompositor == 0) return;
@@ -208,8 +244,8 @@ JNI_DECLARE(LorieService, pointerScroll)(JNIEnv __unused *env, jobject __unused
if (jcompositor == 0) return;
LorieBackendAndroid *b = fromLong(jcompositor);
- LOGV("JNI: pointer scroll %d %d", axis, value);
- b->pointer_scroll(axis, value);
+ LOGV("JNI: pointer scroll %d %f", axis, value);
+ b->pointer_scroll(static_cast(axis), value);
}
extern "C" JNIEXPORT void JNICALL
@@ -221,21 +257,6 @@ JNI_DECLARE(LorieService, pointerButton)(JNIEnv __unused *env, jobject __unused
b->pointer_button(static_cast(button), static_cast(type));
}
-/*extern "C" JNIEXPORT void JNICALL
-JNI_DECLARE(LorieService, onTouch)(JNIEnv __unused *env, jobject __unused instance, jlong __unused jcompositor, jint type, jint button, jint x, jint y) {
- if (jcompositor == 0) return;
- LorieBackendAndroid *b = fromLong(jcompositor);
-
- if (type == WL_POINTER_MOTION) {
- LOGV("JNI: pointer motion %dx%d", x, y);
- b->pointer_motion(x, y);
- }
- else {
- LOGV("JNI: pointer button %d type %d", button, type);
- b->pointer_button(button, type);
- }
-}*/
-
extern "C" void get_character_data(char** layout, int *shift, int *ec, char *ch);
extern "C" void android_keycode_get_eventcode(int kc, int *ec, int *shift);
@@ -257,7 +278,6 @@ JNI_DECLARE(LorieService, keyboardKey)(JNIEnv *env, jobject __unused instance,
LOGE("kc: %d ec: %d", key_code, event_code);
if (strcmp(b->xkb_names.layout, "us") && event_code != 0) {
b->queue.call(&LorieBackendAndroid::layout_change_callback, b, (char*)"us");
- //lorie_layout_change_event(backend, "us");
}
}
if (!key_code && characters) {
@@ -265,7 +285,6 @@ JNI_DECLARE(LorieService, keyboardKey)(JNIEnv *env, jobject __unused instance,
get_character_data(&layout, &shift, &event_code, characters);
if (layout && b->xkb_names.layout != layout) {
b->queue.call(&LorieBackendAndroid::layout_change_callback, b, layout);
- //lorie_layout_change_event(backend, layout);
}
}
LOGE("Keyboard input: keyCode: %d; eventCode: %d; characters: %s; shift: %d, type: %d", key_code, event_code, characters, shift, type);
diff --git a/app/src/main/jni/lorie/backend/android/locale/log.h b/app/src/main/jni/lorie/backend/android/locale/log.h
index ae7a4b5..b3da08e 100644
--- a/app/src/main/jni/lorie/backend/android/locale/log.h
+++ b/app/src/main/jni/lorie/backend/android/locale/log.h
@@ -2,7 +2,7 @@
#include
#ifndef LOG_TAG
-#define LOG_TAG "XLorie"
+#define LOG_TAG "LorieNative"
#endif
#define LOG(prio, ...) __android_log_print(prio, LOG_TAG, __VA_ARGS__)
diff --git a/app/src/main/jni/lorie/compositor.cpp b/app/src/main/jni/lorie/compositor.cpp
index 48d92bf..edcb4cc 100644
--- a/app/src/main/jni/lorie/compositor.cpp
+++ b/app/src/main/jni/lorie/compositor.cpp
@@ -15,6 +15,10 @@ LorieCompositor::LorieCompositor() :
wrapper(terminate),
wrapper(output_redraw),
wrapper(output_resize),
+wrapper(touch_down),
+wrapper(touch_motion),
+wrapper(touch_up),
+wrapper(touch_frame),
wrapper(pointer_motion),
wrapper(pointer_scroll),
wrapper(pointer_button),
@@ -97,21 +101,62 @@ void LorieCompositor::real_output_redraw() {
}
void LorieCompositor::real_output_resize(uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height) {
+ // Xwayland segfaults without that line
+ if (width == 0 || height == 0 || physical_width == 0 || physical_height == 0) return;
renderer.resize(width, height, physical_width, physical_height);
output_redraw();
}
+void LorieCompositor::real_touch_down(uint32_t id, uint32_t x, uint32_t y) {
+ LorieClient *client = get_toplevel_client();
+ if (toplevel == nullptr || client == nullptr) return;
+
+ wl_fixed_t surface_x = wl_fixed_from_int (x);
+ wl_fixed_t surface_y = wl_fixed_from_int (y);
+
+ client->touch.send_down(next_serial(), LorieUtils::timestamp(), toplevel->resource, id, surface_x, surface_y);
+ renderer.setCursorVisibility(false);
+}
+
+void LorieCompositor::real_touch_motion(uint32_t id, uint32_t x, uint32_t y) {
+ LorieClient *client = get_toplevel_client();
+ if (toplevel == nullptr || client == nullptr) return;
+
+ wl_fixed_t surface_x = wl_fixed_from_int (x);
+ wl_fixed_t surface_y = wl_fixed_from_int (y);
+
+ client->touch.send_motion(LorieUtils::timestamp(), id, surface_x, surface_y);
+ renderer.setCursorVisibility(false);
+}
+
+void LorieCompositor::real_touch_up(uint32_t id) {
+ LorieClient *client = get_toplevel_client();
+ if (toplevel == nullptr || client == nullptr) return;
+
+ client->touch.send_up(next_serial(), LorieUtils::timestamp(), id);
+ renderer.setCursorVisibility(false);
+}
+
+void LorieCompositor::real_touch_frame() {
+ LorieClient *client = get_toplevel_client();
+ if (toplevel == nullptr || client == nullptr) return;
+
+ client->touch.send_frame();
+ renderer.setCursorVisibility(false);
+}
+
void LorieCompositor::real_pointer_motion(uint32_t x, uint32_t y) {
- LorieClient *client = get_toplevel_client();
- if (client == nullptr) return;
+ LorieClient *client = get_toplevel_client();
+ if (client == nullptr) return;
- wl_fixed_t surface_x = wl_fixed_from_int (x);
- wl_fixed_t surface_y = wl_fixed_from_int (y);
+ wl_fixed_t surface_x = wl_fixed_from_int (x);
+ wl_fixed_t surface_y = wl_fixed_from_int (y);
- client->pointer.send_motion(LorieUtils::timestamp(), surface_x, surface_y);
- client->pointer.send_frame();
+ client->pointer.send_motion(LorieUtils::timestamp(), surface_x, surface_y);
+ client->pointer.send_frame();
- renderer.cursorMove(x, y);
+ renderer.setCursorVisibility(true);
+ renderer.cursorMove(x, y);
}
void LorieCompositor::real_pointer_scroll(uint32_t axis, float value) {
@@ -123,6 +168,7 @@ void LorieCompositor::real_pointer_scroll(uint32_t axis, float value) {
client->pointer.send_axis_discrete(axis, (scroll>=0)?1:-1);
client->pointer.send_axis(LorieUtils::timestamp(), axis, scroll);
client->pointer.send_frame();
+ renderer.setCursorVisibility(true);
}
void LorieCompositor::real_pointer_button(uint32_t button, uint32_t state) {
@@ -132,6 +178,7 @@ void LorieCompositor::real_pointer_button(uint32_t button, uint32_t state) {
LOGI("pointer button: %d %d", button, state);
client->pointer.send_button (next_serial(), LorieUtils::timestamp(), button, state);
client->pointer.send_frame();
+ renderer.setCursorVisibility(true);
}
void LorieCompositor::real_keyboard_key(uint32_t key, uint32_t state) {
@@ -177,4 +224,4 @@ uint32_t LorieCompositor::next_serial() {
return wl_display_next_serial(display);
}
-#pragma clang diagnostic pop
\ No newline at end of file
+#pragma clang diagnostic pop
diff --git a/app/src/main/jni/lorie/include/lorie-compositor.hpp b/app/src/main/jni/lorie/include/lorie-compositor.hpp
index 8d3178d..ba87a9d 100644
--- a/app/src/main/jni/lorie/include/lorie-compositor.hpp
+++ b/app/src/main/jni/lorie/include/lorie-compositor.hpp
@@ -25,6 +25,11 @@ public:
void real_terminate();
void real_output_redraw();
void real_output_resize(uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height);
+
+ void real_touch_down(uint32_t id, uint32_t x, uint32_t y);
+ void real_touch_motion(uint32_t id, uint32_t x, uint32_t y);
+ void real_touch_up(uint32_t id);
+ void real_touch_frame();
void real_pointer_motion(uint32_t x, uint32_t y); // absolute values
void real_pointer_scroll(uint32_t axis, float value);
void real_pointer_button(uint32_t button, uint32_t state);
@@ -37,6 +42,10 @@ public:
wrapper(terminate);
wrapper(output_redraw);
wrapper(output_resize);
+ wrapper(touch_down);
+ wrapper(touch_motion);
+ wrapper(touch_up);
+ wrapper(touch_frame);
wrapper(pointer_motion);
wrapper(pointer_scroll);
wrapper(pointer_button);
diff --git a/app/src/main/jni/lorie/include/lorie-renderer.hpp b/app/src/main/jni/lorie/include/lorie-renderer.hpp
index b1e49ef..b4b277e 100644
--- a/app/src/main/jni/lorie/include/lorie-renderer.hpp
+++ b/app/src/main/jni/lorie/include/lorie-renderer.hpp
@@ -14,10 +14,13 @@ public:
uint32_t physical_width = 270;
uint32_t physical_height = 158;
+ bool cursorVisible = false;
+
uint32_t hotspot_x, hotspot_y;
void resize(uint32_t w, uint32_t h, uint32_t pw, uint32_t ph);
void cursorMove(uint32_t x, uint32_t y);
+ void setCursorVisibility(bool visibility);
~LorieRenderer();
private:
static void idleDraw(void *data);
diff --git a/app/src/main/jni/lorie/renderer.cpp b/app/src/main/jni/lorie/renderer.cpp
index 4486780..98c10de 100644
--- a/app/src/main/jni/lorie/renderer.cpp
+++ b/app/src/main/jni/lorie/renderer.cpp
@@ -171,6 +171,11 @@ void LorieRenderer::cursorMove(uint32_t x, uint32_t y) {
requestRedraw();
}
+void LorieRenderer::setCursorVisibility(bool visibility) {
+ if (cursorVisible != visibility)
+ cursorVisible = visibility;
+}
+
void LorieRenderer::idleDraw(void *data) {
LorieRenderer* r = static_cast (data);
@@ -207,7 +212,8 @@ void LorieRenderer::redraw() {
compositor.toplevel->texture.draw(-1.0, -1.0, 1.0, 1.0);
}
- drawCursor();
+ if (cursorVisible)
+ drawCursor();
compositor.swap_buffers();
}
diff --git a/app/src/main/jni/lorie/seat.cpp b/app/src/main/jni/lorie/seat.cpp
index 189a4b0..c21b160 100644
--- a/app/src/main/jni/lorie/seat.cpp
+++ b/app/src/main/jni/lorie/seat.cpp
@@ -4,7 +4,8 @@
#include
void LorieSeat::on_create() {
- send_capabilities (WL_SEAT_CAPABILITY_POINTER|WL_SEAT_CAPABILITY_KEYBOARD);
+ uint32_t capabilities = (*client)->compositor.input_capabilities();
+ send_capabilities (capabilities);
send_name("default");
}
diff --git a/app/src/main/jni/lorie/utils/log.cpp b/app/src/main/jni/lorie/utils/log.cpp
index 51e931d..49e6041 100644
--- a/app/src/main/jni/lorie/utils/log.cpp
+++ b/app/src/main/jni/lorie/utils/log.cpp
@@ -23,6 +23,13 @@ extern "C" {
extern void *blacklist[];
#define skip_blacklisted(f) for (int z=0; blacklist[z]!=NULL; z++) if (blacklist[z]==f) return;
+static bool lock_needed =
+#ifndef __ANDROID__
+ true;
+#else
+ false;
+#endif
+
static pthread_mutex_t lock;
// see https://github.com/littlstar/asprintf.c/blob/master/asprintf.c
@@ -102,16 +109,16 @@ static void LogMessageInternal(int priority, const char *format, ...) {
void LogMessage(int priority, const char *format, ...) {
if (logFunction == NULL) return;
- pthread_mutex_lock(&::lock);
+ if (lock_needed) pthread_mutex_lock(&::lock);
va_list argptr;
va_start(argptr, format);
logFunction(priority, format, argptr);
va_end(argptr);
- pthread_mutex_unlock(&::lock);
+ if (lock_needed) pthread_mutex_unlock(&::lock);
}
void LogInit(void) {
- if (pthread_mutex_init(&::lock, NULL) != 0) {
+ if (lock_needed && pthread_mutex_init(&::lock, NULL) != 0) {
LogMessageInternal(LOG_ERROR, "Logger mutex init failed\n");
return;
}
diff --git a/app/src/main/res/drawable-anydpi-v24/ic_x11_icon.xml b/app/src/main/res/drawable-anydpi-v24/ic_x11_icon.xml
new file mode 100644
index 0000000..3046d35
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi-v24/ic_x11_icon.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-hdpi/ic_x11_icon.png b/app/src/main/res/drawable-hdpi/ic_x11_icon.png
new file mode 100644
index 0000000..2d86741
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_x11_icon.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_x11_icon.png b/app/src/main/res/drawable-mdpi/ic_x11_icon.png
new file mode 100644
index 0000000..b108458
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_x11_icon.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_x11_icon.png b/app/src/main/res/drawable-xhdpi/ic_x11_icon.png
new file mode 100644
index 0000000..ea7caaa
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_x11_icon.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_x11_icon.png b/app/src/main/res/drawable-xxhdpi/ic_x11_icon.png
new file mode 100644
index 0000000..8f9022f
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_x11_icon.png differ
diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml
new file mode 100644
index 0000000..b13d208
--- /dev/null
+++ b/app/src/main/res/layout/main_activity.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/preferences_toolbar.xml b/app/src/main/res/layout/preferences_toolbar.xml
new file mode 100644
index 0000000..499232d
--- /dev/null
+++ b/app/src/main/res/layout/preferences_toolbar.xml
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
new file mode 100644
index 0000000..6442cc1
--- /dev/null
+++ b/app/src/main/res/values/arrays.xml
@@ -0,0 +1,13 @@
+
+
+
+ - Direct mode
+ - Emulate mouse
+ - Emulate touchpad
+
+
+ - 1
+ - 2
+ - 3
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
new file mode 100644
index 0000000..248b7b0
--- /dev/null
+++ b/app/src/main/res/xml/preferences.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+