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 @@ + + + + + + +