Cursor is not visible here. Replacing rendering with GL with blitting to Surface.

This commit is contained in:
Twaik Yont 2023-02-03 16:07:22 +02:00
parent a868f37c8a
commit 3794838b76
15 changed files with 608 additions and 1018 deletions

View File

@ -13,6 +13,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.net.Uri;
@ -41,7 +42,10 @@ import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import com.termux.x11.utils.KeyboardUtils;
@ -52,7 +56,7 @@ import java.io.InputStream;
@SuppressWarnings({"ConstantConditions", "SameParameterValue", "SdCardPath"})
@SuppressLint({"ClickableViewAccessibility", "StaticFieldLeak"})
public class LorieService extends Service {
public class LorieService extends Service implements View.OnApplyWindowInsetsListener {
static final String LAUNCHED_BY_COMPATION = "com.termux.x11.launched_by_companion";
static final String ACTION_STOP_SERVICE = "com.termux.x11.service_stop";
static final String ACTION_START_FROM_ACTIVITY = "com.termux.x11.start_from_activity";
@ -205,13 +209,12 @@ public class LorieService extends Service {
int mode = Integer.parseInt(preferences.getString("touchMode", "3"));
instance.mTP.setMode(mode);
if (preferences.getBoolean("showAdditionalKbd", true)) {
act.getWindow().getDecorView().setOnApplyWindowInsetsListener(act);
act.getWindow().getDecorView().setOnApplyWindowInsetsListener(act);
if (preferences.getBoolean("showAdditionalKbd", true))
act.kbd.setVisibility(View.VISIBLE);
} else {
act.getWindow().getDecorView().setOnApplyWindowInsetsListener(null);
else
act.kbd.setVisibility(View.GONE);
}
Log.e("LorieService", "Preferences changed");
}
@ -295,7 +298,7 @@ public class LorieService extends Service {
return instance;
}
void setListeners(@NonNull SurfaceView view) {
void setListeners(@NonNull SurfaceView view, @NonNull SurfaceView cursor) {
Context a = view.getRootView().findViewById(android.R.id.content).getContext();
if (!(a instanceof MainActivity)) {
Log.e("LorieService", "Context is not an activity!!!");
@ -308,7 +311,7 @@ public class LorieService extends Service {
view.requestFocus();
listener.svc = this;
listener.setAsListenerTo(view);
listener.setAsListenerTo(view, cursor);
mTP = new TouchParser(view, listener);
onPreferencesChanged();
@ -328,13 +331,24 @@ public class LorieService extends Service {
LorieService svc;
@SuppressLint("WrongConstant")
private void setAsListenerTo(SurfaceView view) {
private void setAsListenerTo(SurfaceView view, SurfaceView cursor) {
view.getHolder().addCallback(this);
view.setOnTouchListener(this);
view.setOnHoverListener(this);
view.setOnGenericMotionListener(this);
view.setOnKeyListener(this);
surfaceChanged(view.getHolder(), PixelFormat.UNKNOWN, view.getWidth(), view.getHeight());
cursor.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override public void surfaceCreated(@NonNull SurfaceHolder holder) {}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
svc.cursorChanged(holder.getSurface());
}
@Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
svc.cursorChanged(null);
}
});
}
public void onPointerButton(int button, int state) {
@ -436,8 +450,10 @@ public class LorieService extends Service {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
DisplayMetrics dm = new DisplayMetrics();
Rect r = new Rect();
int mmWidth, mmHeight;
act.getWindowManager().getDefaultDisplay().getMetrics(dm);
act.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
if (act.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mmWidth = (int) Math.round((width * 25.4) / dm.xdpi);
@ -447,11 +463,14 @@ public class LorieService extends Service {
mmHeight = (int) Math.round((height * 25.4) / dm.xdpi);
}
svc.windowChanged(holder.getSurface(), width, height, mmWidth, mmHeight);
svc.windowChanged(holder.getSurface(), r.right, r.bottom, mmWidth, mmHeight);
}
@Override public void surfaceCreated(SurfaceHolder holder) {}
@Override public void surfaceDestroyed(SurfaceHolder holder) {}
@SuppressLint("WrongConstant")
@Override public void surfaceDestroyed(SurfaceHolder holder) {
surfaceChanged(holder, PixelFormat.UNKNOWN, 0, 0);
}
}
@ -490,12 +509,47 @@ public class LorieService extends Service {
};
}
@SuppressLint("WrongConstant")
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
SurfaceView c = v.getRootView().findViewById(R.id.lorieView);
SurfaceHolder h = (c != null) ? c.getHolder() : null;
if (h != null)
listener.surfaceChanged(h, PixelFormat.UNKNOWN, 0, 0);
return insets;
}
@SuppressWarnings("unused")
// It is used in native code
void setRendererVisibility(boolean visible) {
act.runOnUiThread(()-> act.findViewById(visible ? R.id.lorieView : R.id.stub).bringToFront());
act.runOnUiThread(()-> act.findViewById(R.id.stub).setVisibility(visible?View.GONE:View.VISIBLE));
}
@SuppressWarnings("unused")
// It is used in native code
void setCursorVisibility(boolean visible) {
act.runOnUiThread(()-> act.findViewById(R.id.cursorView).setVisibility(visible?View.VISIBLE:View.GONE));
}
@SuppressWarnings("unused")
// It is used in native code
void setCursorRect(int x, int y, int w, int h) {
act.runOnUiThread(()-> {
Log.d("POSITIONER", "pointing cursor: " + x + " " + y + " " + w + " " + h);
SurfaceView v = act.findViewById(R.id.cursorView);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(w*10, h*10);
params.leftMargin = x;
params.topMargin = y;
v.setLayoutParams(params);
v.setVisibility(View.VISIBLE);
v.setZOrderOnTop(true);
v.bringToFront();
FrameLayout frm = act.findViewById(R.id.frame);
});
}
private native void cursorChanged(Surface surface);
private native void windowChanged(Surface surface, int width, int height, int mmWidth, int mmHeight);
private native void touchDown(int id, float x, float y);
private native void touchMotion(int id, float x, float y);

View File

@ -11,6 +11,7 @@ import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.PointerIcon;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
@ -131,8 +132,9 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo
public void onLorieServiceStart(LorieService instance) {
SurfaceView lorieView = findViewById(R.id.lorieView);
SurfaceView cursorView = findViewById(R.id.cursorView);
instance.setListeners(lorieView);
instance.setListeners(lorieView, cursorView);
kbd.reload(keys, lorieView, LorieService.getOnKeyListener());
}
@ -187,13 +189,15 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
if (kbd != null) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(LorieService.getInstance());
if (preferences.getBoolean("showAdditionalKbd", true) && kbd != null) {
Rect r = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
boolean isSoftKbdVisible = Objects.requireNonNull(ViewCompat.getRootWindowInsets(kbd)).isVisible(WindowInsetsCompat.Type.ime());
kbd.setVisibility(isSoftKbdVisible ? View.VISIBLE : View.GONE);
kbd.setY(r.bottom - kbd.getHeight());
}
return insets;
return LorieService.getInstance().onApplyWindowInsets(v, insets);
}
}

View File

@ -5,12 +5,10 @@ include $(CLEAR_VARS)
LOCAL_MODULE := lorie
LOCAL_SRC_FILES := \
compositor.cpp \
lorie_egl_helper.cpp \
lorie_message_queue.cpp \
renderer.cpp \
utils/log.cpp \
android.cpp \
\
backend/android/android_app.cpp \
backend/android/utils.c \
lorie_wayland_server.cpp \
$(wildcard $(WAYLAND_GENERATED)/*.cpp)

View File

@ -0,0 +1,347 @@
#include <thread>
#include "lorie_compositor.hpp"
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "ashmem.h"
#include <csignal>
#include <android/native_window_jni.h>
#include <sys/socket.h>
#include <dirent.h>
#include <android/log.h>
#pragma ide diagnostic ignored "hicpp-signed-bitwise"
#define unused __attribute__((__unused__))
#define always_inline __attribute__((always_inline)) inline
JavaVM *vm{};
jfieldID lorie_compositor::compositor_field_id{};
lorie_compositor::lorie_compositor(jobject thiz): lorie_compositor() {
this->thiz = thiz;
self = std::thread([=, this]{
vm->AttachCurrentThread(&env, nullptr);
compositor_field_id = env->GetFieldID(env->GetObjectClass(thiz), "compositor", "J");
set_renderer_visibility_id = env->GetMethodID(env->GetObjectClass(thiz), "setRendererVisibility", "(Z)V");
set_cursor_visibility_id = env->GetMethodID(env->GetObjectClass(thiz), "setCursorVisibility", "(Z)V");
set_cursor_rect_id =env->GetMethodID( env->GetObjectClass(thiz), "setCursorRect", "(IIII)V");
set_renderer_visibility = [=](bool visible) {
env->CallVoidMethod(thiz, set_renderer_visibility_id, visible);
};
set_cursor_visibility = [=](bool visible) {
env->CallVoidMethod(thiz, set_cursor_visibility_id, visible);
if (!visible)
set_cursor_position = [=](int, int) {};
else
set_cursor_position = [=](int x, int y) {
auto b = cursor.sfc ? any_cast<surface_data*>(cursor.sfc->user_data())->buffer : nullptr;
int sx = x - cursor.hotspot_x;
int sy = y - cursor.hotspot_y;
int w = b ? b->shm_width() : 0;
int h = b ? b->shm_width() : 0;
env->CallVoidMethod(thiz, set_cursor_rect_id, sx, sy, w, h);
};
};
run();
vm->DetachCurrentThread();
env = nullptr;
});
}
void lorie_compositor::get_keymap(int *fd, int *size) { // NOLINT(readability-convert-member-functions-to-static)
int keymap_fd = open("/data/data/com.termux.x11/files/en_us.xkbmap", O_RDONLY);
struct stat s = {};
fstat(keymap_fd, &s);
*size = s.st_size; // NOLINT(cppcoreguidelines-narrowing-conversions)
*fd = keymap_fd;
}
// For some reason both static_cast and reinterpret_cast returning 0 when casting b.bits.
static always_inline uint32_t* cast(void* p) { union { void* a; uint32_t* b; } c {p}; return c.b; } // NOLINT(cppcoreguidelines-pro-type-member-init)
static always_inline void blit_exact(EGLNativeWindowType win, const uint32_t* src, int width, int height) {
if (width == 0 || height == 0) {
width = ANativeWindow_getWidth(win);
height = ANativeWindow_getHeight(win);
}
ARect bounds{ 0, 0, width, height };
ANativeWindow_Buffer b{};
ANativeWindow_acquire(win);
auto ret = ANativeWindow_setBuffersGeometry(win, width, height, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM);
if (ret != 0) {
LOGE("Failed to set buffers geometry (%d)", ret);
return;
}
ret = ANativeWindow_lock(win, &b, &bounds);
if (ret != 0) {
LOGE("Failed to lock");
return;
}
uint32_t* dst = cast(b.bits);
if (src) {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
//uint32_t* d = &dst[b.stride*i + j];
uint32_t s = src[width * i + j];
// Cast BGRA to RGBA
dst[b.stride * i + j] = ((s & 0x0000FF) << 16) | (s & 0x00FF00) | ((s & 0xFF0000) >> 16);
}
}
} else
memset(dst, 0, b.stride*b.height);
ret = ANativeWindow_unlockAndPost(win);
if (ret != 0) {
LOGE("Failed to post");
return;
}
ANativeWindow_release(win);
}
void lorie_compositor::blit(EGLNativeWindowType win, wayland::surface_t* sfc) {
if (!win)
return;
auto buffer = sfc ? any_cast<surface_data*>(sfc->user_data())->buffer : nullptr;
if (buffer)
blit_exact(win, cast(buffer->shm_data()), buffer->shm_width(), buffer->shm_height());
else
blit_exact(win, nullptr, 0, 0);
}
template<class F, F f, auto defaultValue = 0> struct wrapper_impl;
template<class R, class C, class... A, R(C::*f)(A...), auto defaultValue>
struct wrapper_impl<R(C::*)(A...), f, defaultValue> {
// Be careful and do passing jobjects here!!!
[[maybe_unused]] static always_inline R execute(JNIEnv* env, jobject obj, A... args) {
auto native = lorie_compositor::compositor_field_id ? reinterpret_cast<C*>(env->GetLongField(obj, lorie_compositor::compositor_field_id)) : nullptr;
if (native != nullptr)
return (native->*f)(args...);
return static_cast<R>(defaultValue);
}
[[maybe_unused]] static always_inline void queue(JNIEnv* env, jobject obj, A... args) {
auto native = lorie_compositor::compositor_field_id ? reinterpret_cast<C*>(env->GetLongField(obj, lorie_compositor::compositor_field_id)) : nullptr;
if (native != nullptr)
native->post([=]{ (native->*f)(args...); });
}
};
template<auto f, auto defaultValue = 0>
auto execute = wrapper_impl<decltype(f), f, defaultValue>::execute;
template<auto f, auto defaultValue = 0>
auto queue = wrapper_impl<decltype(f), f, defaultValue>::queue;
[[maybe_unused]] static always_inline lorie_compositor* get(JNIEnv* env, jobject obj) {
return lorie_compositor::compositor_field_id ?
reinterpret_cast<lorie_compositor*>(env->GetLongField(obj, lorie_compositor::compositor_field_id)) :
nullptr;
}
///////////////////////////////////////////////////////////
#define JNI_DECLARE_INNER(package, classname, methodname ) \
Java_ ## package ## _ ## classname ## _ ## methodname
#define JNI_DECLARE(classname, methodname) \
JNI_DECLARE_INNER(com_termux_x11, classname, methodname)
extern "C" JNIEXPORT jlong JNICALL
JNI_DECLARE(LorieService, createLorieThread)(JNIEnv *env, jobject thiz) {
#if 0
// It is needed to redirect stderr to logcat
setenv("WAYLAND_DEBUG", "1", 1);
new std::thread([]{
FILE *fp;
int p[2];
size_t read, len;
char* line = nullptr;
pipe(p);
fp = fdopen(p[0], "r");
dup2(p[1], 2);
while ((read = getline(&line, &len, fp)) != -1) {
__android_log_write(ANDROID_LOG_VERBOSE, "WAYLAND_STDERR", line);
}
});
#endif
return (jlong) new lorie_compositor(env->NewGlobalRef(thiz));
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, passWaylandFD)(JNIEnv *env, jobject thiz, jint fd) {
LOGI("JNI: got fd %d", fd);
execute<&lorie_compositor::add_socket_fd>(env, thiz, fd);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, terminate)(JNIEnv *env, jobject obj) {
auto b = reinterpret_cast<lorie_compositor*>(env->GetLongField(obj, lorie_compositor::compositor_field_id));
b->terminate();
b->self.join();
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, cursorChanged)(JNIEnv *env, jobject thiz, jobject surface) {
EGLNativeWindowType win = surface?ANativeWindow_fromSurface(env, surface):nullptr;
auto c = get(env, thiz);
c->post([=]{ c->cursor.win = win; });
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, windowChanged)(JNIEnv *env, jobject thiz, jobject surface, jint width, jint height, jint mm_width, jint mm_height) {
EGLNativeWindowType win = surface?ANativeWindow_fromSurface(env, surface):nullptr;
queue<&lorie_compositor::output_resize>(env, thiz, win, width, height, mm_width, mm_height);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, touchDown)(JNIEnv *env, jobject thiz, jint id, jfloat x, jfloat y) {
queue<&lorie_compositor::touch_down>(env, thiz, static_cast<uint32_t>(id), static_cast<uint32_t>(x), static_cast<uint32_t>(y));
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, touchMotion)(JNIEnv *env, jobject thiz, jint id, jfloat x, jfloat y) {
queue<&lorie_compositor::touch_motion>(env, thiz, static_cast<uint32_t>(id), static_cast<uint32_t>(x), static_cast<uint32_t>(y));
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, touchUp)(JNIEnv *env, jobject thiz, jint id) {
queue<&lorie_compositor::touch_up>(env, thiz, id);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, touchFrame)(JNIEnv *env, jobject thiz) {
queue<&lorie_compositor::touch_frame>(env, thiz);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, pointerMotion)(JNIEnv *env, jobject thiz, jint x, jint y) {
queue<&lorie_compositor::pointer_motion>(env, thiz, x, y);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, pointerScroll)(JNIEnv *env, jobject thiz, jint axis, jfloat value) {
queue<&lorie_compositor::pointer_scroll>(env, thiz, axis, value);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, pointerButton)(JNIEnv *env, jobject thiz, jint button, jint type) {
queue<&lorie_compositor::pointer_button>(env, thiz, uint32_t(button), uint32_t(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);
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, keyboardKey)(JNIEnv *env, jobject thiz,
jint type, jint key_code, jint jshift, jstring characters_) {
char *characters = nullptr;
int event_code = 0;
int shift = jshift;
if (characters_ != nullptr) characters = (char*) env->GetStringUTFChars(characters_, nullptr);
if (key_code && !characters) {
android_keycode_get_eventcode(key_code, &event_code, &shift);
LOGE("kc: %d ec: %d", key_code, event_code);
}
if (!key_code && characters) {
char *layout = nullptr;
get_character_data(&layout, &shift, &event_code, characters);
}
LOGE("Keyboard input: keyCode: %d; eventCode: %d; characters: %s; shift: %d, type: %d", key_code, event_code, characters, shift, type);
if (shift || jshift)
queue<&lorie_compositor::keyboard_key>(env, thiz, 42, wayland::keyboard_key_state::pressed);
// For some reason Android do not send ACTION_DOWN for non-English characters
if (characters)
queue<&lorie_compositor::keyboard_key>(env, thiz, event_code, wayland::keyboard_key_state::pressed);
queue<&lorie_compositor::keyboard_key>(env, thiz, event_code, wayland::keyboard_key_state(type));
if (shift || jshift)
queue<&lorie_compositor::keyboard_key>(env, thiz, 42, wayland::keyboard_key_state::released);
if (characters_ != nullptr) env->ReleaseStringUTFChars(characters_, characters);
}
static bool sameUid(int pid) {
char path[32] = {0};
struct stat s = {0};
sprintf(path, "/proc/%d", pid);
stat(path, &s);
return s.st_uid == getuid();
}
static void killAllLogcats() {
DIR* proc;
struct dirent* dir_elem;
char path[64] = {0}, link[64] = {0};
pid_t pid, self = getpid();
if ((proc = opendir("/proc")) == nullptr) {
LOGE("opendir: %s", strerror(errno));
return;
}
while((dir_elem = readdir(proc)) != nullptr) {
if (!(pid = (pid_t) atoi (dir_elem->d_name)) || pid == self || !sameUid(pid)) // NOLINT(cert-err34-c)
continue;
memset(path, 0, sizeof(path));
memset(link, 0, sizeof(link));
sprintf(path, "/proc/%d/exe", pid);
if (readlink(path, link, sizeof(link)) < 0) {
LOGE("readlink %s: %s", path, strerror(errno));
continue;
}
if (strstr(link, "/logcat") != nullptr) {
if (kill(pid, SIGKILL) < 0) {
LOGE("kill %d (%s): %s", pid, link, strerror);
}
}
}
}
void fork(const std::function<void()>& f) {
switch(fork()) {
case -1: LOGE("fork: %s", strerror(errno)); return;
case 0: f(); return;
default: return;
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_termux_x11_LorieService_startLogcatForFd(unused JNIEnv *env, unused jclass clazz, jint fd) {
killAllLogcats();
LOGI("Starting logcat with output to given fd");
fork([]() {
execl("/system/bin/logcat", "logcat", "-c", nullptr);
LOGE("exec logcat: %s", strerror(errno));
});
fork([fd]() {
dup2(fd, 1);
dup2(fd, 2);
execl("/system/bin/logcat", "logcat", nullptr);
LOGE("exec logcat: %s", strerror(errno));
});
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
extern "C" jint JNI_OnLoad(JavaVM* vm, [[maybe_unused]] void* reserved) {
::vm = vm;
return JNI_VERSION_1_6;
}
#pragma clang diagnostic pop

View File

@ -1,305 +0,0 @@
#include <thread>
#include <lorie_compositor.hpp>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <ashmem.h>
#include <csignal>
#include <lorie_egl_helper.hpp>
#include <android/native_window_jni.h>
#include <sys/socket.h>
#include <dirent.h>
#include <android/log.h>
#pragma ide diagnostic ignored "hicpp-signed-bitwise"
#define unused __attribute__((__unused__))
class lorie_backend_android : public lorie_compositor {
public:
lorie_backend_android(JavaVM *env, jobject obj, jmethodID pId);
void backend_init() override;
void swap_buffers() override;
void get_keymap(int *fd, int *size) override;
void window_change_callback(EGLNativeWindowType win, uint32_t width, uint32_t height,
uint32_t physical_width, uint32_t physical_height);
void passfd(int fd);
void on_egl_init();
LorieEGLHelper helper;
int keymap_fd = -1;
std::thread self;
JavaVM *vm{};
JNIEnv *env{};
jobject thiz{};
static jfieldID compositor_field_id;
jmethodID set_renderer_visibility_id{};
};
jfieldID lorie_backend_android::compositor_field_id{};
lorie_backend_android::lorie_backend_android(JavaVM *vm, jobject obj, jmethodID mid)
: vm(vm), thiz(obj), set_renderer_visibility_id(mid), self(&lorie_compositor::start, this) {}
void lorie_backend_android::on_egl_init() {
renderer.on_surface_create();
}
void lorie_backend_android::backend_init() {
if (!helper.init(EGL_DEFAULT_DISPLAY)) {
LOGE("Failed to initialize EGL context");
}
helper.onInit = [this](){ on_egl_init(); };
renderer.set_renderer_visibility = [=](bool visible) {
vm->AttachCurrentThread(&env, nullptr);
env->CallVoidMethod(thiz, set_renderer_visibility_id, visible);
vm->DetachCurrentThread();
};
}
void lorie_backend_android::swap_buffers () {
helper.swap();
}
void lorie_backend_android::get_keymap(int *fd, int *size) {
if (keymap_fd != -1)
close(keymap_fd);
keymap_fd = open("/data/data/com.termux.x11/files/en_us.xkbmap", O_RDONLY);
struct stat s = {};
fstat(keymap_fd, &s);
*size = s.st_size;
*fd = keymap_fd;
}
void lorie_backend_android::window_change_callback(EGLNativeWindowType win, uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height) {
LOGV("JNI: window is changed: %p %dx%d (%dmm x %dmm)", win, width, height, physical_width, physical_height);
helper.setWindow(win);
post([=, this]() {
output_resize(width, height, physical_width, physical_height);
});
}
void lorie_backend_android::passfd(int fd) {
LOGI("JNI: got fd %d", fd);
dpy.add_socket_fd(fd);
}
template<class F, F f, auto defaultValue = 0> struct wrapper_impl;
template<class R, class C, class... A, R(C::*f)(A...), auto defaultValue>
struct wrapper_impl<R(C::*)(A...), f, defaultValue> {
// Be careful and do passing jobjects here!!!
static inline R execute(JNIEnv* env, jobject obj, A... args) {
auto native = reinterpret_cast<C*>(env->GetLongField(obj, lorie_backend_android::compositor_field_id));
if (native != nullptr)
return (native->*f)(args...);
return static_cast<R>(defaultValue);
}
static inline void queue(JNIEnv* env, jobject obj, A... args) {
auto native = reinterpret_cast<C*>(env->GetLongField(obj, lorie_backend_android::compositor_field_id));
if (native != nullptr)
native->post([=]{ (native->*f)(args...); });
}
};
template<auto f, auto defaultValue = 0>
auto execute = wrapper_impl<decltype(f), f, defaultValue>::execute;
template<auto f, auto defaultValue = 0>
auto queue = wrapper_impl<decltype(f), f, defaultValue>::queue;
///////////////////////////////////////////////////////////
#define JNI_DECLARE_INNER(package, classname, methodname ) \
Java_ ## package ## _ ## classname ## _ ## methodname
#define JNI_DECLARE(classname, methodname) \
JNI_DECLARE_INNER(com_termux_x11, classname, methodname)
extern "C" JNIEXPORT jlong JNICALL
JNI_DECLARE(LorieService, createLorieThread)(JNIEnv *env, jobject thiz) {
#if 0
// It is needed to redirect stderr to logcat
setenv("WAYLAND_DEBUG", "1", 1);
new std::thread([]{
FILE *fp;
int p[2];
size_t read, len;
char* line = nullptr;
pipe(p);
fp = fdopen(p[0], "r");
dup2(p[1], 2);
while ((read = getline(&line, &len, fp)) != -1) {
__android_log_write(ANDROID_LOG_VERBOSE, "WAYLAND_STDERR", line);
}
});
#endif
JavaVM* vm{};
env->GetJavaVM(&vm);
lorie_backend_android::compositor_field_id = env->GetFieldID(env->GetObjectClass(thiz), "compositor", "J");
jmethodID set_renderer_visibility_id = env->GetMethodID(env->GetObjectClass(thiz), "setRendererVisibility", "(Z)V");
return (jlong) new lorie_backend_android(vm, env->NewGlobalRef(thiz),
set_renderer_visibility_id);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, passWaylandFD)(JNIEnv *env, jobject thiz, jint fd) {
execute<&lorie_backend_android::passfd>(env, thiz, fd);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, terminate)(JNIEnv *env, jobject obj) {
auto b = reinterpret_cast<lorie_backend_android*>(env->GetLongField(obj, lorie_backend_android::compositor_field_id));
b->terminate();
b->self.join();
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, windowChanged)(JNIEnv *env, jobject thiz, jobject jsurface, jint width, jint height, jint mm_width, jint mm_height) {
EGLNativeWindowType win = ANativeWindow_fromSurface(env, jsurface);
queue<&lorie_backend_android::window_change_callback>(env, thiz, win, width, height, mm_width, mm_height);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, touchDown)(JNIEnv *env, jobject thiz, jint id, jfloat x, jfloat y) {
queue<&lorie_backend_android::touch_down>(env, thiz, static_cast<uint32_t>(id), static_cast<uint32_t>(x), static_cast<uint32_t>(y));
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, touchMotion)(JNIEnv *env, jobject thiz, jint id, jfloat x, jfloat y) {
queue<&lorie_backend_android::touch_motion>(env, thiz, static_cast<uint32_t>(id), static_cast<uint32_t>(x), static_cast<uint32_t>(y));
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, touchUp)(JNIEnv *env, jobject thiz, jint id) {
queue<&lorie_backend_android::touch_up>(env, thiz, id);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, touchFrame)(JNIEnv *env, jobject thiz) {
queue<&lorie_backend_android::touch_frame>(env, thiz);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, pointerMotion)(JNIEnv *env, jobject thiz, jint x, jint y) {
queue<&lorie_backend_android::pointer_motion>(env, thiz, static_cast<uint32_t>(x), static_cast<uint32_t>(y));
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, pointerScroll)(JNIEnv *env, jobject thiz, jint axis, jfloat value) {
queue<&lorie_backend_android::pointer_scroll>(env, thiz, static_cast<uint32_t>(axis), value);
}
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, pointerButton)(JNIEnv *env, jobject thiz, jint button, jint type) {
queue<&lorie_backend_android::pointer_button>(env, thiz, static_cast<uint32_t>(button), static_cast<uint32_t>(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);
extern "C" JNIEXPORT void JNICALL
JNI_DECLARE(LorieService, keyboardKey)(JNIEnv *env, jobject thiz,
jint type, jint key_code, jint jshift, jstring characters_) {
char *characters = nullptr;
int event_code = 0;
int shift = jshift;
if (characters_ != nullptr) characters = (char*) env->GetStringUTFChars(characters_, nullptr);
if (key_code && !characters) {
android_keycode_get_eventcode(key_code, &event_code, &shift);
LOGE("kc: %d ec: %d", key_code, event_code);
}
if (!key_code && characters) {
char *layout = nullptr;
get_character_data(&layout, &shift, &event_code, characters);
}
LOGE("Keyboard input: keyCode: %d; eventCode: %d; characters: %s; shift: %d, type: %d", key_code, event_code, characters, shift, type);
if (shift || jshift)
queue<&lorie_backend_android::keyboard_key>(env, thiz, 42, wayland::keyboard_key_state::pressed);
// For some reason Android do not send ACTION_DOWN for non-English characters
if (characters)
queue<&lorie_backend_android::keyboard_key>(env, thiz, event_code, wayland::keyboard_key_state::pressed);
queue<&lorie_backend_android::keyboard_key>(env, thiz, event_code, wayland::keyboard_key_state(type));
if (shift || jshift)
queue<&lorie_backend_android::keyboard_key>(env, thiz, 42, wayland::keyboard_key_state::released);
if (characters_ != nullptr) env->ReleaseStringUTFChars(characters_, characters);
}
static bool sameUid(int pid) {
char path[32] = {0};
struct stat s = {0};
sprintf(path, "/proc/%d", pid);
stat(path, &s);
return s.st_uid == getuid();
}
static void killAllLogcats() {
DIR* proc;
struct dirent* dir_elem;
char path[64] = {0}, link[64] = {0};
pid_t pid, self = getpid();
if ((proc = opendir("/proc")) == nullptr) {
LOGE("opendir: %s", strerror(errno));
return;
}
while((dir_elem = readdir(proc)) != nullptr) {
if (!(pid = (pid_t) atoi (dir_elem->d_name)) || pid == self || !sameUid(pid)) // NOLINT(cert-err34-c)
continue;
memset(path, 0, sizeof(path));
memset(link, 0, sizeof(link));
sprintf(path, "/proc/%d/exe", pid);
if (readlink(path, link, sizeof(link)) < 0) {
LOGE("readlink %s: %s", path, strerror(errno));
continue;
}
if (strstr(link, "/logcat") != nullptr) {
if (kill(pid, SIGKILL) < 0) {
LOGE("kill %d (%s): %s", pid, link, strerror);
}
}
}
}
void fork(const std::function<void()>& f) {
switch(fork()) {
case -1: LOGE("fork: %s", strerror(errno)); return;
case 0: f(); return;
default: return;
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_termux_x11_LorieService_startLogcatForFd(unused JNIEnv *env, unused jclass clazz, jint fd) {
killAllLogcats();
LOGI("Starting logcat with output to given fd");
fork([]() {
execl("/system/bin/logcat", "logcat", "-c", nullptr);
LOGE("exec logcat: %s", strerror(errno));
});
fork([fd]() {
dup2(fd, 1);
dup2(fd, 2);
execl("/system/bin/logcat", "logcat", nullptr);
LOGE("exec logcat: %s", strerror(errno));
});
}

View File

@ -7,18 +7,27 @@
using namespace wayland;
lorie_compositor::lorie_compositor() :
display(dpy),
renderer(*this) {
dpy.on_client = [=](client_t* client) {
lorie_compositor::lorie_compositor() {
on_client = [=](client_t* client) {
client->user_data() = new client_data;
client->on_destroy = [=] {
if (toplevel && toplevel->client() == client)
renderer.set_toplevel(nullptr);
bool request_redraw{};
if (cursor && cursor->client() == client)
renderer.set_cursor(nullptr, 0, 0);
if (screen.sfc && screen.sfc->client() == client) {
screen.sfc = nullptr;
request_redraw = true;
}
if (cursor.sfc && cursor.sfc->client() == client) {
screen.sfc = nullptr;
request_redraw = true;
}
if (request_redraw)
redraw(true);
if(!screen.sfc)
set_renderer_visibility(false);
LOGI("Client destroyed");
};
};
@ -32,8 +41,6 @@ renderer(*this) {
auto data = new surface_data;
surface->user_data() = data;
surface->on_attach = [=](buffer_t* b, int32_t, int32_t) {
if (data->buffer)
data->buffer->release();
data->buffer = b;
};
surface->on_damage = [=](int32_t, int32_t, int32_t, int32_t) {
@ -43,7 +50,8 @@ renderer(*this) {
data->frame_callback = cb;
};
surface->on_commit = [=] {
renderer.request_redraw();
redraw();
data->buffer->release();
data->frame_callback->done(resource_t::timestamp());
};
surface->on__destroy = [=] {
@ -57,14 +65,17 @@ renderer(*this) {
auto data = any_cast<client_data*>(client->user_data());
seat->on_get_pointer = [=](pointer_t* pointer) {
seat->on_get_pointer = [=, this](pointer_t* pointer) {
LOGV("Client requested seat pointer");
data->pointer = pointer;
if (toplevel)
pointer->enter(next_serial(), toplevel, 0, 0);
if (screen.sfc)
pointer->enter(next_serial(), screen.sfc, 0, 0);
pointer->on_set_cursor = [=](uint32_t, wayland::surface_t* sfc, int32_t x, int32_t y) {
renderer.set_cursor(sfc, (uint32_t) x, (uint32_t) y);
cursor.sfc = sfc;
cursor.hotspot_x = x;
cursor.hotspot_y = y;
if (sfc)
any_cast<surface_data*>(sfc->user_data())->damaged = true;
};
@ -89,8 +100,8 @@ renderer(*this) {
wl_array keys{};
wl_array_init(&keys);
if (toplevel)
kbd->enter(next_serial(), toplevel, &keys);
if (screen.sfc)
kbd->enter(next_serial(), screen.sfc, &keys);
kbd->on_release = [=] {
kbd->destroy();
@ -119,178 +130,178 @@ renderer(*this) {
shell->on_set_toplevel = [=] () {
wl_array keys{};
wl_array_init(&keys);
renderer.set_toplevel(sfc);
screen.sfc = sfc;
redraw();
set_renderer_visibility(sfc != nullptr);
if (data->pointer)
data->pointer->enter(next_serial(),sfc, 0, 0);
if(data->kbd)
data->kbd->enter(next_serial(), sfc, &keys);
shell->configure(shell_surface_resize::none, renderer.width, renderer.height);
auto buffer = sfc ? any_cast<surface_data*>(sfc->user_data())->buffer : nullptr;
if (buffer)
shell->configure(shell_surface_resize::none, buffer->shm_width(), buffer->shm_height());
};
};
};
global_xdg_wm_base.on_bind = [=, this](client_t* client, xdg_wm_base_t* wm_base) {
wm_base->on_get_xdg_surface = [=, this](xdg_surface_t* xdg_surface, surface_t* sfc) {
xdg_surface->on_get_toplevel = [=, this](xdg_toplevel_t*) {
auto data = any_cast<client_data*>(toplevel->client()->user_data());
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
wl_array keys{};
wl_array_init(&keys);
renderer.set_toplevel(sfc);
screen.sfc = sfc;
if (data->pointer)
data->pointer->enter(next_serial(),sfc, 0, 0);
if (data->kbd)
data->kbd->enter(next_serial(), sfc, &keys);
redraw();
};
};
wm_base->on__destroy = [=]() { wm_base->destroy(); };
};
LogInit();
LOGV("Starting compositor");
wl_display_init_shm (*this);
add_fd_listener(queue.get_fd(), WL_EVENT_READABLE, [&](int, uint){ queue.run(); return 0; });
}
int proc(int fd, uint32_t mask, void *data) {
lorie_compositor *b = static_cast<lorie_compositor*>(data);
if (b == nullptr) {LOGF("b == nullptr"); return 0;}
void lorie_compositor::redraw(bool force) {
if (screen.win) {
auto data = screen.sfc ? any_cast<surface_data *>(screen.sfc->user_data()) : nullptr;
bool damaged = (data && data->damaged) || force;
if (damaged)
blit(screen.win, screen.sfc);
}
b->queue.run();
return 0;
};
void lorie_compositor::start() {
LogInit();
LOGV("Starting compositor");
wl_display_add_socket_auto(display);
wl_event_loop_add_fd(wl_display_get_event_loop(display), queue.get_fd(), WL_EVENT_READABLE, &proc, this);
wl_display_init_shm (display);
backend_init();
wl_display_run(display);
if (cursor.win) {
auto data = cursor.sfc ? any_cast<surface_data *>(cursor.sfc->user_data()) : nullptr;
bool damaged = (data && data->damaged) || force;
if (damaged)
blit(cursor.win, cursor.sfc);
}
}
void lorie_compositor::post(std::function<void()> f) {
queue.write(f);
queue.write(std::move(f));
}
void lorie_compositor::terminate() {
LOGI("JNI: requested termination");
if (display != nullptr)
wl_display_terminate(display);
}
void lorie_compositor::output_resize(int width, int height, uint32_t physical_width, uint32_t physical_height) {
void lorie_compositor::output_resize(EGLNativeWindowType win, int real_width, int real_height, int physical_width, int 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);
post([this]() {
renderer.request_redraw();
});
LOGV("JNI: window is changed: %p %dx%d (%dmm x %dmm)", win, real_width, real_height, physical_width, physical_height);
if (real_width == 0 || real_height == 0 || physical_width == 0 || physical_height == 0) return;
screen.real_width = real_width;
screen.real_height = real_height;
screen.physical_width = physical_width;
screen.physical_height = physical_height;
screen.win = win;
if (screen.sfc) {
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
report_mode(data->output);
}
}
void lorie_compositor::report_mode(wayland::output_t* output) const {
output->geometry(0, 0, renderer.physical_width, renderer.physical_height, output_subpixel::unknown, "Lorie", "none", output_transform::normal);
output->geometry(0, 0, screen.physical_width, screen.physical_height, output_subpixel::unknown, "Lorie", "none", output_transform::normal);
output->scale(1.0);
output->mode(output_mode::current | output_mode::preferred, renderer.width, renderer.height, 60000);
output->mode(output_mode::current | output_mode::preferred, screen.real_width, screen.real_height, 60000);
output->done();
}
void lorie_compositor::touch_down(uint32_t id, uint32_t x, uint32_t y) {
LOGV("JNI: touch down");
if (!toplevel)
if (!screen.sfc)
return;
auto data = any_cast<client_data*>(toplevel->client()->user_data());
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
data->touch->down(next_serial(), resource_t::timestamp(), toplevel, id, x, y);
renderer.set_cursor_visibility(false);
data->touch->down(next_serial(), resource_t::timestamp(), screen.sfc, id, x, y);
set_cursor_visibility(false);
}
void lorie_compositor::touch_motion(uint32_t id, uint32_t x, uint32_t y) {
LOGV("JNI: touch motion");
if (!toplevel)
if (!screen.sfc)
return;
auto data = any_cast<client_data*>(toplevel->client()->user_data());
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
data->touch->motion(resource_t::timestamp(), id, x, y);
renderer.set_cursor_visibility(false);
set_cursor_visibility(false);
}
void lorie_compositor::touch_up(uint32_t id) {
LOGV("JNI: touch up");
if (!toplevel)
if (!screen.sfc)
return;
auto data = any_cast<client_data*>(toplevel->client()->user_data());
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
data->touch->up(next_serial(), resource_t::timestamp(), id);
renderer.set_cursor_visibility(false);
set_cursor_visibility(false);
}
void lorie_compositor::touch_frame() {
LOGV("JNI: touch frame");
if (!toplevel)
if (!screen.sfc)
return;
auto data = any_cast<client_data*>(toplevel->client()->user_data());
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
data->touch->frame();
renderer.set_cursor_visibility(false);
set_cursor_visibility(false);
}
void lorie_compositor::pointer_motion(uint32_t x, uint32_t y) {
void lorie_compositor::pointer_motion(int x, int y) {
LOGV("JNI: pointer motion %dx%d", x, y);
if (!toplevel)
if (!screen.sfc)
return;
auto data = any_cast<client_data*>(toplevel->client()->user_data());
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
data->pointer->motion(resource_t::timestamp(), x, y);
data->pointer->motion(resource_t::timestamp(), double(x), double(y));
data->pointer->frame();
renderer.set_cursor_visibility(true);
renderer.cursor_move(x, y);
set_cursor_visibility(true);
set_cursor_position(x, y);
}
void lorie_compositor::pointer_scroll(uint32_t axis, float value) {
void lorie_compositor::pointer_scroll(int axis, float value) {
LOGV("JNI: pointer scroll %d %f", axis, value);
if (!toplevel)
if (!screen.sfc)
return;
auto data = any_cast<client_data*>(toplevel->client()->user_data());
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
data->pointer->axis_discrete(pointer_axis(axis), (value >= 0) ? 1 : -1);
data->pointer->axis(resource_t::timestamp(), pointer_axis(axis), value);
data->pointer->frame();
renderer.set_cursor_visibility(true);
set_cursor_visibility(true);
}
void lorie_compositor::pointer_button(uint32_t button, uint32_t state) {
void lorie_compositor::pointer_button(int button, int state) {
LOGV("JNI: pointer button %d type %d", button, state);
if (!toplevel)
if (!screen.sfc)
return;
auto data = any_cast<client_data*>(toplevel->client()->user_data());
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
LOGI("pointer button: %d %d", button, state);
data->pointer->button(next_serial(), resource_t::timestamp(), button, pointer_button_state(state));
data->pointer->frame();
renderer.set_cursor_visibility(true);
set_cursor_visibility(true);
}
void lorie_compositor::keyboard_key(uint32_t key, keyboard_key_state state) {
if (!toplevel)
if (!screen.sfc)
return;
auto data = any_cast<client_data*>(toplevel->client()->user_data());
auto data = any_cast<client_data*>(screen.sfc->client()->user_data());
data->kbd->key (next_serial(), resource_t::timestamp(), key, keyboard_key_state(state));
}
uint32_t lorie_compositor::next_serial() const {
if (display == nullptr) return 0;
return wl_display_next_serial(display);
}
#pragma clang diagnostic pop

View File

@ -2,65 +2,91 @@
#include <lorie_wayland_server.hpp>
#include <wayland-server-protocol.hpp>
#include <xdg-shell-server-protocol.hpp>
#include <lorie_renderer.hpp>
#include <lorie_message_queue.hpp>
#include "log.h"
class lorie_compositor {
#ifdef ANDROID
#include <android/native_window.h>
#include <jni.h>
#include <thread>
typedef ANativeWindow* EGLNativeWindowType;
#else
typedef void* EGLNativeWindowType;
#endif
class lorie_compositor: public wayland::display_t {
public:
lorie_compositor();
lorie_compositor();
// compositor features
void start();
void post(std::function<void()> f);
void start();
void post(std::function<void()> f);
void redraw(bool force = false);
void terminate();
void output_resize(int width, int height, uint32_t physical_width, uint32_t physical_height);
void report_mode(wayland::output_t* output) const;
void output_resize(EGLNativeWindowType win, int real_width, int real_height, int physical_width, int physical_height);
void report_mode(wayland::output_t* output) const;
void touch_down(uint32_t id, uint32_t x, uint32_t y);
void touch_motion(uint32_t id, uint32_t x, uint32_t y);
void touch_up(uint32_t id);
void touch_frame();
void pointer_motion(uint32_t x, uint32_t y); // absolute values
void pointer_scroll(uint32_t axis, float value);
void pointer_button(uint32_t button, uint32_t state);
void keyboard_key(uint32_t key, wayland::keyboard_key_state state);
void touch_down(uint32_t id, uint32_t x, uint32_t y);
void touch_motion(uint32_t id, uint32_t x, uint32_t y);
void touch_up(uint32_t id);
void touch_frame();
void pointer_motion(int x, int y); // absolute values
void pointer_scroll(int axis, float value);
void pointer_button(int button, int state);
void keyboard_key(uint32_t key, wayland::keyboard_key_state state);
struct client_data {
wayland::output_t* output{};
wayland::pointer_t* pointer{};
wayland::keyboard_t* kbd{};
wayland::touch_t* touch{};
};
struct client_data {
wayland::output_t* output{};
wayland::pointer_t* pointer{};
wayland::keyboard_t* kbd{};
wayland::touch_t* touch{};
};
struct surface_data {
uint32_t x{}, y{};
bool damaged{};
wayland::buffer_t *buffer{};
wayland::callback_t *frame_callback{};
};
struct surface_data {
uint32_t x{}, y{};
bool damaged{};
wayland::buffer_t *buffer{};
wayland::callback_t *frame_callback{};
};
lorie_renderer renderer;
wayland::surface_t* toplevel{};
wayland::surface_t* cursor{};
struct {
int real_width, real_height;
int physical_width, physical_height;
wayland::surface_t* sfc;
EGLNativeWindowType win;
} screen{};
struct {
int width, height;
int hotspot_x, hotspot_y;
int x, y;
wayland::surface_t* sfc;
EGLNativeWindowType win;
} cursor{};
static void blit(EGLNativeWindowType win, wayland::surface_t* sfc);
std::function<void(bool)> set_renderer_visibility = [](bool){};
std::function<void(bool)> set_cursor_visibility = [](bool){};
std::function<void(int, int)> set_cursor_position = [](int, int){};
// backend features
virtual void backend_init() = 0;
virtual void swap_buffers() = 0;
virtual void get_keymap(int *fd, int *size) = 0;
virtual ~lorie_compositor() {};
void get_keymap(int *fd, int *size);
//private:
wayland::display_t dpy;
wayland::global_compositor_t global_compositor{dpy};
wayland::global_seat_t global_seat{dpy};
wayland::global_output_t global_output{dpy};
wayland::global_shell_t global_shell{dpy};
wayland::global_xdg_wm_base_t global_xdg_wm_base{dpy};
struct wl_display *display = nullptr;
wayland::global_compositor_t global_compositor{this};
wayland::global_seat_t global_seat{this};
wayland::global_output_t global_output{this};
wayland::global_shell_t global_shell{this};
wayland::global_xdg_wm_base_t global_xdg_wm_base{this};
lorie_message_queue queue;
private:
uint32_t next_serial() const;
#ifdef ANDROID
JNIEnv *env{};
jobject thiz{};
static jfieldID compositor_field_id;
jmethodID set_renderer_visibility_id{};
jmethodID set_cursor_visibility_id{};
jmethodID set_cursor_rect_id{};
lorie_compositor(jobject thiz);
std::thread self;
#endif
};

View File

@ -1,27 +0,0 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <functional>
#if defined(__ANDROID__)
#define EGL_NO_WINDOW nullptr
#else
#define EGL_NO_WINDOW 0
#endif
class LorieEGLHelper {
public:
EGLDisplay dpy = EGL_NO_DISPLAY;
EGLConfig cfg;
EGLSurface sfc = EGL_NO_SURFACE;
EGLContext ctx = EGL_NO_CONTEXT;
EGLNativeWindowType win = EGL_NO_WINDOW;
std::function<void()> onInit = nullptr;
std::function<void()> onUninit = nullptr;
bool init(EGLNativeDisplayType display);
bool setWindow(EGLNativeWindowType window);
void swap();
void uninit();
};

View File

@ -1,73 +0,0 @@
#pragma once
#include <GLES2/gl2.h>
#include <limits.h>
class lorie_renderer;
class lorie_texture {
private:
lorie_renderer* r = nullptr;
bool damaged = false;
public:
lorie_texture();
int width{}, height{};
void *data{};
void set_data(lorie_renderer* renderer, uint32_t width, uint32_t height, void *data);
void damage(int32_t x, int32_t y, int32_t width, int32_t height);
void uninit();
void reinit();
bool valid();
private:
GLuint id = UINT_MAX;
void draw(float x0, float y0, float x1, float y1);
friend class lorie_renderer;
};
class lorie_compositor;
class lorie_renderer {
public:
lorie_renderer(lorie_compositor& compositor);
void request_redraw();
void on_surface_create();
uint32_t width = 1024;
uint32_t height = 600;
uint32_t physical_width = 270;
uint32_t physical_height = 158;
bool cursor_visible = false;
uint32_t hotspot_x{}, hotspot_y{};
void resize(int w, int h, uint32_t pw, uint32_t ph);
void cursor_move(uint32_t x, uint32_t y);
void set_cursor_visibility(bool visibility);
std::function<void(bool)> set_renderer_visibility = [](bool){};
private:
lorie_compositor& compositor;
void set_toplevel(wayland::surface_t* surface);
void set_cursor(wayland::surface_t* surface, uint32_t hotspot_x, uint32_t hotspot_y);
struct wl_event_source *idle = NULL;
void draw(GLuint id, float x0, float y0, float x1, float y1) const;
void draw_cursor();
void redraw();
GLuint g_texture_program = 0;
GLuint gv_pos = 0;
GLuint gv_coords = 0;
GLuint gv_texture_sampler_handle = 0;
struct {
GLuint id;
GLsizei width, height;
inline void reset() { width = 0; height = 0; }
inline bool valid() { return width != 0 && height != 0; }
} toplevel_texture{}, cursor_texture{};
bool ready = false;
bool visible = false;
friend class lorie_texture;
friend class lorie_compositor;
};

View File

@ -1,181 +0,0 @@
#include <log.h>
#include <lorie_egl_helper.hpp>
#include <GLES2/gl2.h>
static char* eglGetErrorText(int code) {
switch(eglGetError()) {
#define E(code, desc) case code: return (char*) desc; break;
case EGL_SUCCESS: return nullptr; // "No error"
E(EGL_NOT_INITIALIZED, "EGL not initialized or failed to initialize");
E(EGL_BAD_ACCESS, "Resource inaccessible");
E(EGL_BAD_ALLOC, "Cannot allocate resources");
E(EGL_BAD_ATTRIBUTE, "Unrecognized attribute or attribute value");
E(EGL_BAD_CONTEXT, "Invalid EGL context");
E(EGL_BAD_CONFIG, "Invalid EGL frame buffer configuration");
E(EGL_BAD_CURRENT_SURFACE, "Current surface is no longer valid");
E(EGL_BAD_DISPLAY, "Invalid EGL display");
E(EGL_BAD_SURFACE, "Invalid surface");
E(EGL_BAD_MATCH, "Inconsistent arguments");
E(EGL_BAD_PARAMETER, "Invalid argument");
E(EGL_BAD_NATIVE_PIXMAP, "Invalid native pixmap");
E(EGL_BAD_NATIVE_WINDOW, "Invalid native window");
E(EGL_CONTEXT_LOST, "Context lost");
#undef E
default: return (char*) "Unknown error";
}
}
#undef checkEGLError
void checkEGLError(int line) {
char *error = eglGetErrorText(eglGetError());
if (error != nullptr)
LOGE("EGL: %s after %d", error, line);
}
#define checkEGLError() checkEGLError(__LINE__)
bool LorieEGLHelper::init(EGLNativeDisplayType display) {
EGLint majorVersion, minorVersion;
LOGV("Initializing EGL");
dpy = eglGetDisplay(display); checkEGLError();
if (dpy == EGL_NO_DISPLAY) {
LOGE("LorieEGLHelper::init : eglGetDisplay failed: %s", eglGetErrorText(eglGetError()));
return false;
}
if (eglInitialize(dpy, &majorVersion, &minorVersion) != EGL_TRUE) {
LOGE("LorieEGLHelper::init : eglInitialize failed: %s", eglGetErrorText(eglGetError()));
return false;
}
const EGLint configAttribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_NONE
};
EGLint numConfigs;
if (eglChooseConfig(dpy, configAttribs, &cfg, 1, &numConfigs) != EGL_TRUE) {
LOGE("LorieEGLHelper::init : eglChooseConfig failed: %s", eglGetErrorText(eglGetError()));
return false;
}
eglBindAPI(EGL_OPENGL_ES_API);
const EGLint ctxattribs[] = {
EGL_CONTEXT_CLIENT_VERSION,
2, // Request opengl ES2.0
EGL_NONE
};
ctx = eglCreateContext(dpy, cfg, NULL, ctxattribs);
if (ctx == EGL_NO_CONTEXT) {
LOGE("LorieEGLHelper::init : eglCreateContext failed: %s", eglGetErrorText(eglGetError()));
return false;
}
if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONFIG_KHR) != EGL_TRUE) {
LOGE("LorieEGLHelper::init : eglMakeCurrent failed: %s", eglGetErrorText(eglGetError()));
return false;
}
return true;
}
bool LorieEGLHelper::setWindow(EGLNativeWindowType window) {
LOGV("Trying to use window %p", window);
if (sfc != EGL_NO_SURFACE) {
if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) {
LOGE("LorieEGLHelper::setWindow : eglMakeCurrent (EGL_NO_SURFACE) failed: %s", eglGetErrorText(eglGetError()));
return false;
}
if (eglDestroySurface(dpy, sfc) != EGL_TRUE) {
LOGE("LorieEGLHelper::setWindow : eglDestoySurface failed: %s", eglGetErrorText(eglGetError()));
return false;
}
};
sfc = EGL_NO_SURFACE;
win = window;
if (win == EGL_NO_WINDOW) {
if (onUninit != nullptr) onUninit();
return true;
}
sfc = eglCreateWindowSurface(dpy, cfg, win, NULL);
if (sfc == EGL_NO_SURFACE) {
LOGE("LorieEGLHelper::setWindow : eglCreateWindowSurface failed: %s", eglGetErrorText(eglGetError()));
if (onUninit != nullptr) onUninit();
return false;
}
if (eglMakeCurrent(dpy, sfc, sfc, ctx) != EGL_TRUE) {
LOGE("LorieEGLHelper::setWindow : eglMakeCurrent failed: %s", eglGetErrorText(eglGetError()));
if (onUninit != nullptr) onUninit();
return false;
}
glClearColor(0.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
swap();
if (onInit != nullptr)
onInit();
return true;
}
void LorieEGLHelper::swap() {
EGLint b = eglSwapBuffers(dpy, sfc);
if (b != EGL_TRUE) {
EGLint err = eglGetError();
if (err == EGL_BAD_SURFACE) {
LOGE("eglSwapBuffers failed because of invalid surface. Regenerating surface.");
setWindow(win);
} else if (err == EGL_BAD_CONTEXT || err == EGL_CONTEXT_LOST) {
LOGE("eglSwapBuffers failed because of invalid context. Regenerating context.");
const EGLint ctxattribs[] = {
EGL_CONTEXT_CLIENT_VERSION,
2, // Request opengl ES2.0
EGL_NONE
};
ctx = eglCreateContext(dpy, cfg, NULL, ctxattribs);
if (ctx == EGL_NO_CONTEXT) {
LOGE("LorieEGLHelper::init : eglCreateContext failed: %s", eglGetErrorText(eglGetError()));
return;
}
}
}
}
void LorieEGLHelper::uninit() {
if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) {
LOGE("LorieEGLHelper::uninit : eglMakeCurrent failed: %s", eglGetErrorText(eglGetError()));
return;
}
if (eglDestroyContext(dpy, ctx) != EGL_TRUE) {
LOGE("LorieEGLHelper::uninit : eglDestroyContext failed: %s", eglGetErrorText(eglGetError()));
return;
}
ctx = EGL_NO_CONTEXT;
if (eglDestroySurface(dpy, sfc) != EGL_TRUE) {
LOGE("LorieEGLHelper::uninit : eglDestroySurface failed: %s", eglGetErrorText(eglGetError()));
return;
}
sfc = EGL_NO_SURFACE;
if (eglTerminate(dpy) != EGL_TRUE) {
LOGE("LorieEGLHelper::uninit : eglTerminate failed: %s", eglGetErrorText(eglGetError()));
return;
}
dpy = EGL_NO_DISPLAY;
}

View File

@ -61,6 +61,7 @@ void display_t::add_fd_listener(int fd, uint32_t mask, std::function<int(int fd,
wl_event_loop* loop = wl_display_get_event_loop(display);
auto l = new fd_listener(loop, std::move(listener));
l->source = wl_event_loop_add_fd(loop, fd, mask, fd_listener::fire, l);
wl_event_loop_add_destroy_listener(loop, l);
}
// It is wl_display_socket_add_fd version which drops server fd if it is faulty.
@ -96,7 +97,7 @@ void display_t::add_socket_fd(int fd) {
client_fd = accept4(fd, (struct sockaddr *) &name, &length, SOCK_CLOEXEC);
if (client_fd < 0) {
LOGE("failed to accept: %s\n", strerror(errno));
if (errno == EBADF || errno == ENOTSOCK) {
if (errno == EBADF || errno == ENOTSOCK || errno == EPERM) {
l->destroy(l, nullptr);
return 0;
}

View File

@ -56,6 +56,10 @@ namespace wayland {
inline void set_log_handler(wl_log_func_t f) {
wl_log_set_handler_server(f);
}
inline uint32_t next_serial() {
return wl_display_next_serial(*this);
};
};
class client_t: public wl_listener {
@ -128,10 +132,6 @@ namespace wayland {
inline client_t* client() {
return m_client;
}
inline uint32_t next_serial() {
return wl_display_next_serial(display);
};
static inline resource_t* get(wl_resource* r) {
return r == nullptr ? nullptr : static_cast<resource_t*>(wl_resource_get_destroy_listener(r, &resource_destroyed));

View File

@ -1,266 +0,0 @@
#include <lorie_compositor.hpp>
#include <GLES2/gl2ext.h>
#include <sys/types.h>
#include <unistd.h>
using namespace wayland;
static const char vertex_shader[] = R"(
attribute vec4 position;
attribute vec2 texCoords;
varying vec2 outTexCoords;
void main(void) {
outTexCoords = texCoords;
gl_Position = position;
}
)";
static const char fragment_shader[] = R"(
precision mediump float;
varying vec2 outTexCoords;
uniform sampler2D texture;
void main(void) {
gl_FragColor = texture2D(texture, outTexCoords).bgra;
}
)";
static GLuint load_shader(GLenum shaderType, const char* pSource) {
GLuint shader = glCreateShader(shaderType);
if (shader) {
glShaderSource(shader, 1, &pSource, nullptr);
glCompileShader(shader);
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen) {
char* buf = (char*) malloc(infoLen);
if (buf) {
glGetShaderInfoLog(shader, infoLen, nullptr, buf);
LOGE("Could not compile shader %d:\n%s\n", shaderType, buf);
free(buf);
}
glDeleteShader(shader);
shader = 0;
}
}
}
return shader;
}
static GLuint create_program(const char* p_vertex_source, const char* p_fragment_source) {
GLuint vertexShader = load_shader(GL_VERTEX_SHADER, p_vertex_source);
if (!vertexShader) {
return 0;
}
GLuint pixelShader = load_shader(GL_FRAGMENT_SHADER, p_fragment_source);
if (!pixelShader) {
return 0;
}
GLuint program = glCreateProgram();
if (program) {
glAttachShader(program, vertexShader);
glAttachShader(program, pixelShader);
glLinkProgram(program);
GLint linkStatus = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
GLint bufLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
if (bufLength) {
char* buf = (char*) malloc(bufLength);
if (buf) {
glGetProgramInfoLog(program, bufLength, nullptr, buf);
LOGE("Could not link program:\n%s\n", buf);
free(buf);
}
}
glDeleteProgram(program);
program = 0;
}
}
return program;
}
static inline lorie_compositor::surface_data* data(surface_t* sfc) {
return any_cast<lorie_compositor::surface_data*>(sfc->user_data());
}
lorie_renderer::lorie_renderer(lorie_compositor& compositor) : compositor(compositor) {}
void lorie_renderer::on_surface_create() {
LOGV("Initializing renderer (tid %d)", ::gettid());
g_texture_program = create_program(vertex_shader, fragment_shader);
if (!g_texture_program) {
LOGE("GLESv2: Unable to create shader program");
return;
}
gv_pos = (GLuint) glGetAttribLocation(g_texture_program, "position");
gv_coords = (GLuint) glGetAttribLocation(g_texture_program, "texCoords");
gv_texture_sampler_handle = (GLuint) glGetUniformLocation(g_texture_program, "texture");
ready = true;
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &toplevel_texture.id);
glGenTextures(1, &cursor_texture.id);
redraw();
}
void lorie_renderer::request_redraw() {
//LOGV("Requesting redraw");
if (idle) return;
idle = wl_event_loop_add_idle(
wl_display_get_event_loop(compositor.display),
[](void* data) { reinterpret_cast<lorie_renderer*>(data)->redraw(); },
this
);
}
void lorie_renderer::resize(int w, int h, uint32_t pw, uint32_t ph) {
LOGV("Resizing renderer to %dx%d (%dmm x %dmm)", w, h, pw, ph);
if (w == width &&
h == height &&
pw == physical_width &&
ph == physical_height) return;
width = w;
height = h;
physical_width = pw;
physical_height = ph;
glViewport(0, 0, w, h);
if (compositor.toplevel) {
auto data = any_cast<lorie_compositor::client_data*>(compositor.toplevel->client()->user_data());
compositor.report_mode(data->output);
}
}
void lorie_renderer::cursor_move(uint32_t x, uint32_t y) {
if (compositor.cursor == nullptr) return;
data(compositor.cursor)->x = x;
data(compositor.cursor)->y = y;
request_redraw();
}
void lorie_renderer::set_cursor_visibility(bool visibility) {
if (cursor_visible != visibility)
cursor_visible = visibility;
}
void lorie_renderer::set_toplevel(surface_t* sfc) {
LOGV("Setting surface %p as toplevel", sfc);
compositor.toplevel = sfc;
request_redraw();
}
void lorie_renderer::set_cursor(surface_t* sfc, uint32_t hs_x, uint32_t hs_y) {
LOGV("Setting surface %p as cursor", sfc);
compositor.cursor = sfc;
this->hotspot_x = hs_x;
this->hotspot_y = hs_y;
request_redraw();
}
void lorie_renderer::draw(GLuint id, float x0, float y0, float x1, float y1) const {
float coords[20] = {
x0, -y0, 0.f, 0.f, 0.f,
x1, -y0, 0.f, 1.f, 0.f,
x0, -y1, 0.f, 0.f, 1.f,
x1, -y1, 0.f, 1.f, 1.f,
};
glActiveTexture(GL_TEXTURE0);
glUseProgram(g_texture_program);
glBindTexture(GL_TEXTURE_2D, id);
glVertexAttribPointer(gv_pos, 3, GL_FLOAT, GL_FALSE, 20, coords);
glVertexAttribPointer(gv_coords, 2, GL_FLOAT, GL_FALSE, 20, &coords[3]);
glEnableVertexAttribArray(gv_pos);
glEnableVertexAttribArray(gv_coords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void lorie_renderer::draw_cursor() {
if (compositor.cursor == nullptr) return;
auto toplevel = compositor.toplevel ? data(compositor.toplevel) : nullptr;
auto cursor = compositor.cursor ? data(compositor.cursor) : nullptr;
if (cursor && cursor->damaged && cursor->buffer && cursor->buffer->is_shm()) {
GLsizei w = cursor_texture.width = cursor->buffer->shm_width();
GLsizei h = cursor_texture.height = cursor->buffer->shm_height();
void* d = cursor->buffer->shm_data();
glBindTexture(GL_TEXTURE_2D, cursor_texture.id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, d);
}
if (!cursor_texture.valid()) return;
float x, y, w, h;
x = 2*(((float)(cursor->x-hotspot_x))/(float)toplevel_texture.width) - 1;
y = 2*(((float)(cursor->y-hotspot_y))/(float)toplevel_texture.height) - 1;
w = 2*((float)cursor_texture.width)/(float)toplevel_texture.width;
h = 2*((float)cursor_texture.height)/(float)toplevel_texture.height;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
draw(cursor_texture.id, x, y, x + w, y + h);
glDisable(GL_BLEND);
}
void lorie_renderer::redraw() {
idle = nullptr;
if (!ready) return;
if (compositor.toplevel && data(compositor.toplevel)) {
auto d = data(compositor.toplevel);
if (d->damaged && d->buffer && d->buffer->is_shm()) {
if (d->buffer->shm_width() != toplevel_texture.width || d->buffer->shm_width() != toplevel_texture.width) {
GLsizei w = toplevel_texture.width = d->buffer->shm_width();
GLsizei h = toplevel_texture.height = d->buffer->shm_height();
void* dd = d->buffer->shm_data();
glBindTexture(GL_TEXTURE_2D, toplevel_texture.id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, dd);
} else {
glBindTexture(GL_TEXTURE_2D, toplevel_texture.id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
toplevel_texture.width, toplevel_texture.height, GL_RGBA, GL_UNSIGNED_BYTE, d->buffer->shm_data());
if (glGetError() == GL_INVALID_OPERATION)
/* For some reason if our activity goes to background it loses access
* to this texture. In this case it should be regenerated. */
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toplevel_texture.width, toplevel_texture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, d->buffer->shm_data());
}
}
draw(toplevel_texture.id, -1.0, -1.0, 1.0, 1.0);
if (cursor_visible)
draw_cursor();
if (!visible)
set_renderer_visibility(visible = true);
} else {
if (visible)
set_renderer_visibility(visible = false);
glClearColor(0.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
}
compositor.swap_buffers();
}

View File

@ -17,9 +17,6 @@ static int enabled = 0;
using namespace std;
extern "C" {
#define COLORIZE "\033[0;32m"
#define COLOR_RESET "\033[0m"
extern void *blacklist[];
#define skip_blacklisted(f) for (int z=0; blacklist[z]!=NULL; z++) if (blacklist[z]==f) return;
@ -122,11 +119,11 @@ void LogInit(void) {
LogMessageInternal(LOG_ERROR, "Logger mutex init failed\n");
return;
}
LOGD("Logging initialized");
}
static int level = -1;
static thread_local int level = -1;
void print_func(void *func, int enter);
@ -150,13 +147,12 @@ __cyg_profile_func_exit (void *func, void *caller) {
}
void print_func(void *func, int enter) {
for (int i=0; i<level; i++) printf(" ");
Dl_info info;
if (dladdr(func, &info) && info.dli_sname != NULL) {
char *demangled = NULL;
int status;
demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
LOGP("%s%s %s%s", (level==2)?COLORIZE:"", enter ? ">" : "<", status == 0 ? demangled : info.dli_sname, (level==2)?COLOR_RESET:"");
LOGP("%*c%s %s", level, ' ', enter ? ">" : "<", status == 0 ? demangled : info.dli_sname);
free(demangled);
}
}
@ -168,7 +164,7 @@ void *blacklist[] = {
#if defined(__ANDROID__)
(void*) androidLogFn,
#endif
(void*) LogInit,
(void*) LogInit,
(void*) LogMessage,
(void*) LogMessageInternal,
(void*) __cyg_profile_func_enter,

View File

@ -10,6 +10,11 @@
android:layout_height="match_parent"
android:id="@+id/frame">
<SurfaceView
android:id="@+id/cursorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<SurfaceView
android:id="@+id/lorieView"
android:visibility="visible"