From 6a1d2f4ed42d62ab2b75fd1a26b1f49c263f6f74 Mon Sep 17 00:00:00 2001 From: Mis012 Date: Wed, 2 Nov 2022 14:37:34 +0100 Subject: [PATCH] replace use of glfw for SurfaceView with use of wayland subsurface positioned over a Gtk widget --- meson.build | 13 +- src/api-impl-jni/widgets/WrapperWidget.c | 2 +- .../widgets/android_view_SurfaceView.c | 76 +++++++++- src/api-impl-jni/widgets/marshal.list | 1 + src/api-impl/android/view/Surface.java | 2 +- src/api-impl/android/view/SurfaceView.java | 2 + src/libandroid/native_window.c | 136 +++++++++++------- 7 files changed, 175 insertions(+), 57 deletions(-) create mode 100644 src/api-impl-jni/widgets/marshal.list diff --git a/meson.build b/meson.build index 8165b4af..d66b9f9e 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,7 @@ project('android_translation_layer', ['c', 'java'], default_options: ['b_lundef=false']) +gnome = import('gnome') + incdir_dep = declare_dependency(include_directories: '.') add_project_dependencies(incdir_dep, language: 'c') @@ -15,6 +17,12 @@ libdl_bio_dep = [ cc.find_library('dl_bio') ] +marshal_files = gnome.genmarshal('marshal', + sources: 'src/api-impl-jni/widgets/marshal.list', + valist_marshallers: true, + internal: true, +) + libtranslationlayer_so = shared_library('translation_layer_main', [ 'src/api-impl-jni/egl/com_google_android_gles_jni_EGLImpl.c', 'src/api-impl-jni/android_os_Environment.c', @@ -37,11 +45,11 @@ libtranslationlayer_so = shared_library('translation_layer_main', [ 'src/api-impl-jni/views/android_view_View.c', 'src/api-impl-jni/views/android_view_ViewGroup.c', 'src/api-impl-jni/android_graphics_Bitmap.c' - ], + ] + marshal_files, install: true, install_dir : get_option('libdir') / 'java/dex/android_translation_layer/natives', dependencies: [ - dependency('gtk4'), dependency('gl'), dependency('egl'), dependency('jni') + dependency('gtk4'), dependency('gl'), dependency('egl'), dependency('wayland-client'), dependency('jni') ], link_args: [ '-lasound' @@ -63,7 +71,6 @@ executable('android-translation-layer', [ shared_library('android', [ 'src/libandroid/asset_manager.c', 'src/libandroid/media.c', - 'src/libandroid/misc.c', 'src/libandroid/native_window.c', 'src/libandroid/sensor.c', 'src/libandroid/looper.c' diff --git a/src/api-impl-jni/widgets/WrapperWidget.c b/src/api-impl-jni/widgets/WrapperWidget.c index 13ff5871..30e60e1e 100644 --- a/src/api-impl-jni/widgets/WrapperWidget.c +++ b/src/api-impl-jni/widgets/WrapperWidget.c @@ -44,7 +44,7 @@ GdkTexture * gdk_texture_new_for_surface (cairo_surface_t *surface) G_DEFINE_TYPE(WrapperWidget, wrapper_widget, GTK_TYPE_WIDGET) -static void wrapper_widget_init (WrapperWidget *frame_layout) +static void wrapper_widget_init (WrapperWidget *wrapper_widget) { } diff --git a/src/api-impl-jni/widgets/android_view_SurfaceView.c b/src/api-impl-jni/widgets/android_view_SurfaceView.c index 80c504e1..d89919c9 100644 --- a/src/api-impl-jni/widgets/android_view_SurfaceView.c +++ b/src/api-impl-jni/widgets/android_view_SurfaceView.c @@ -4,25 +4,93 @@ #include "../util.h" #include "WrapperWidget.h" +#include "marshal.h" #include "../generated_headers/android_view_SurfaceView.h" +// TODO: currently this widget class doesn't do anything special, will we ever need it to? +G_DECLARE_FINAL_TYPE (SurfaceViewWidget, surface_view_widget, SURFACE_VIEW, WIDGET, GtkWidget) + +struct _SurfaceViewWidget +{ + GtkWidget parent_instance; +}; + +struct _SurfaceViewWidgetClass +{ + GtkWidgetClass parent_class; +}; + + +G_DEFINE_TYPE(SurfaceViewWidget, surface_view_widget, GTK_TYPE_WIDGET) + +static void surface_view_widget_init (SurfaceViewWidget *wrapper_widget) +{ + +} + +// resize signal copied from GtkDrawingArea +enum { + RESIZE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + +static void surface_view_widget_size_allocate(GtkWidget *widget, int width, int height, int baseline) +{ + g_signal_emit(widget, signals[RESIZE], 0, width, height); +} + +static void surface_view_widget_class_init(SurfaceViewWidgetClass *class) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + // resize signal copied from GtkDrawingArea + widget_class->size_allocate = surface_view_widget_size_allocate; + + signals[RESIZE] = + g_signal_new("resize", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkDrawingAreaClass, resize), + NULL, NULL, + g_cclosure_user_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + g_signal_set_va_marshaller(signals[RESIZE], + G_TYPE_FROM_CLASS (class), + g_cclosure_user_marshal_VOID__INT_INTv); +} + +GtkWidget * surface_view_widget_new(void) +{ + return g_object_new (surface_view_widget_get_type(), NULL); +} + +// --- + struct jni_callback_data { JavaVM *jvm; jobject this; jclass this_class;}; -static void on_mapped(GtkWidget* self, struct jni_callback_data *d) +static void on_resize(GtkWidget* self, gint width, gint height, struct jni_callback_data *d) { JNIEnv *env; (*d->jvm)->GetEnv(d->jvm, (void**)&env, JNI_VERSION_1_6); - (*env)->CallVoidMethod(env, d->this, _METHOD(d->this_class, "surfaceChanged", "(Landroid/view/SurfaceHolder;III)V"), _GET_OBJ_FIELD(d->this, "mSurfaceHolder", "Landroid/view/SurfaceHolder;"), 1 /*RGBA_8888*/, /*FIXME*/700, /*FIXME*/700); + // TODO: are there cases where returning RGBA_8888 is a bad idea? + (*env)->CallVoidMethod(env, d->this, _METHOD(d->this_class, "surfaceChanged", "(Landroid/view/SurfaceHolder;III)V"), + _GET_OBJ_FIELD(d->this, "mSurfaceHolder", "Landroid/view/SurfaceHolder;"), 1 /*RGBA_8888*/, + width, height); } JNIEXPORT void JNICALL Java_android_view_SurfaceView_native_1constructor(JNIEnv *env, jobject this, jobject context) { GtkWidget *wrapper = wrapper_widget_new(); - GtkWidget *dummy = gtk_fixed_new(); + GtkWidget *dummy = surface_view_widget_new(); gtk_widget_set_name(dummy, "dummy widget for SurfaceView"); wrapper_widget_set_child(WRAPPER_WIDGET(wrapper), dummy); + // TODO: is this correct for all usecases? how do we know when it's not? + gtk_widget_set_hexpand(wrapper, true); + gtk_widget_set_vexpand(wrapper, true); JavaVM *jvm; (*env)->GetJavaVM(env, &jvm); @@ -32,7 +100,7 @@ JNIEXPORT void JNICALL Java_android_view_SurfaceView_native_1constructor(JNIEnv callback_data->this = _REF(this); callback_data->this_class = _REF(_CLASS(this)); - g_signal_connect(dummy, "map", G_CALLBACK(on_mapped), callback_data); + g_signal_connect(dummy, "resize", G_CALLBACK(on_resize), callback_data); _SET_LONG_FIELD(this, "widget", _INTPTR(dummy)); } diff --git a/src/api-impl-jni/widgets/marshal.list b/src/api-impl-jni/widgets/marshal.list new file mode 100644 index 00000000..1f953ddf --- /dev/null +++ b/src/api-impl-jni/widgets/marshal.list @@ -0,0 +1 @@ +VOID:INT,INT diff --git a/src/api-impl/android/view/Surface.java b/src/api-impl/android/view/Surface.java index 27675ffc..51d0902f 100644 --- a/src/api-impl/android/view/Surface.java +++ b/src/api-impl/android/view/Surface.java @@ -1,5 +1,5 @@ package android.view; public class Surface { - + public long widget; } diff --git a/src/api-impl/android/view/SurfaceView.java b/src/api-impl/android/view/SurfaceView.java index cb6c5396..401eabf5 100644 --- a/src/api-impl/android/view/SurfaceView.java +++ b/src/api-impl/android/view/SurfaceView.java @@ -11,6 +11,8 @@ public class SurfaceView extends View { super(context); native_constructor(context); + + mSurface.widget = this.widget; } private native void native_constructor(Context context); diff --git a/src/libandroid/native_window.c b/src/libandroid/native_window.c index abec0d3e..2a3ac9bf 100644 --- a/src/libandroid/native_window.c +++ b/src/libandroid/native_window.c @@ -34,17 +34,16 @@ #include #include -//#include +#include +#include + +#include #include -#define GLFW_INCLUDE_NONE -#include - -#define GLFW_EXPOSE_NATIVE_WAYLAND -#include - #include +// FIXME: put the header in a common place +#include "../api-impl-jni/defines.h" /** * Transforms that can be applied to buffers as they are displayed to a window. @@ -66,8 +65,8 @@ enum ANativeWindowTransform { }; struct ANativeWindow { - GLFWwindow *glfw_window; EGLNativeWindowType egl_window; + GtkWidget *surface_view_widget; }; /** @@ -119,16 +118,12 @@ void ANativeWindow_release(struct ANativeWindow *native_window) int32_t ANativeWindow_getWidth(struct ANativeWindow *native_window) { - int v = 0; - glfwGetWindowSize(native_window->glfw_window, &v, NULL); - return v; + return gtk_widget_get_width(native_window->surface_view_widget); } int32_t ANativeWindow_getHeight(struct ANativeWindow *native_window) { - int v = 0; - glfwGetWindowSize(native_window->glfw_window, NULL, &v); - return v; + return gtk_widget_get_height(native_window->surface_view_widget); } /** @@ -205,47 +200,91 @@ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transfo return -1; } -// FIXME: include the header -struct wl_surface; -struct wl_egl_window *wl_egl_window_create(struct wl_surface *surface,int width, int height); - -static void -glfw_error_cb(int code, const char *error) +void wl_registry_global_handler(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - fprintf(stderr, "glfw: (%d) %s\n", code, error); + struct wl_subcompositor **subcompositor = data; + printf("interface: '%s', version: %u, name: %u\n", interface, version, name); + if (!strcmp(interface, "wl_subcompositor")) { + *subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); + } +} + +void wl_registry_global_remove_handler(void *data, struct wl_registry *registry, uint32_t name) +{ + printf("removed: %u\n", name); +} + +// TODO: handle X11 +static void on_resize(GtkWidget* self, gint width, gint height, struct wl_egl_window *egl_window) +{ + wl_egl_window_resize(egl_window, width, height, 0, 0); } ANativeWindow * ANativeWindow_fromSurface(JNIEnv* env, jobject surface) { - // FIXME: add a path for x11 -// TODO: something with subsurfaces -/* GdkDisplay *display = gdk_display_get_default(); //TODO: edge cases? - struct wl_display *wl_display = gdk_wayland_display_get_wl_display(display); - struct wl_compositor *wl_compositor = gdk_wayland_display_get_wl_compositor(display); - //GdkSurface *popup_surface = gdk_surface_new_popup (GdkSurface* parent, gboolean autohide) // TODO: could this work better for us? (need to somehow get the parent surface) - struct wl_surface *toplevel_surface = gdk_wayland_surface_get_wl_surface(gdk_surface_new_toplevel(display)); - struct wl_surface *our_surface = wl_compositor_create_surface(wl_compositor); -// wl_subsurface* our_subsurface = wl_subcompositor_get_subsurface(wl_compositor, our_surface; our_parent); - struct wl_egl_window *egl_window = wl_egl_window_create(our_surface, 700, 700); - return (ANativeWindow *)egl_window;*/ + + int width; + int height; + + double pos_x; + double pos_y; + double off_x; + double off_y; + + GtkWidget *surface_view_widget = _PTR(_GET_LONG_FIELD(surface, "widget")); + GtkWidget *window = GTK_WIDGET(gtk_widget_get_native(surface_view_widget)); + while( (width = gtk_widget_get_width(surface_view_widget)) == 0 ) { + // FIXME: UGLY: this loop waits until the SurfaceView widget gets mapped + } + height = gtk_widget_get_height(surface_view_widget); + + + // get position of the SurfaceView widget wrt the toplevel window + gtk_widget_translate_coordinates(surface_view_widget, window, 0, 0, &pos_x, &pos_y); + // compensate for offset between the widget coordinates and the surface coordinates + gtk_native_get_surface_transform(GTK_NATIVE(window), &off_x, &off_y); + pos_x += off_x; + pos_y += off_y; + + printf("XXXXX: SurfaceView widget: %p (%s), width: %d, height: %d\n", surface_view_widget, gtk_widget_get_name(surface_view_widget), width, height); + printf("XXXXX: SurfaceView widget: x: %lf, y: %lf\n", pos_x, pos_y); + printf("XXXXX: native offset: x: %lf, y: %lf\n", off_x, off_y); struct ANativeWindow *native_window = malloc(sizeof(struct ANativeWindow)); + native_window->surface_view_widget = surface_view_widget; - glfwInit(); - fprintf(stderr, "glfw: %s\n", glfwGetVersionString()); - glfwSetErrorCallback(glfw_error_cb); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - native_window->glfw_window = glfwCreateWindow(700, 700, "FIXME: don't create a separate window for this widget", NULL, NULL); - struct wl_surface *wayland_surface = glfwGetWaylandWindow(native_window->glfw_window); - int width, height; - glfwGetWindowSize(native_window->glfw_window, &width, &height); - printf("glfw::: width: %d, height: %d\n", width, height); - native_window->egl_window = (EGLNativeWindowType)wl_egl_window_create(wayland_surface, width, height); + GdkDisplay *display = gtk_root_get_display(GTK_ROOT(window)); + // FIXME: add a path for x11 + // start of wayland-specific code + struct wl_display *wl_display = gdk_wayland_display_get_wl_display(display); + struct wl_compositor *wl_compositor = gdk_wayland_display_get_wl_compositor(display); + struct wl_registry *wl_registry = wl_display_get_registry(wl_display); + struct wl_registry_listener wl_registry_listener = { + .global = wl_registry_global_handler, + .global_remove = wl_registry_global_remove_handler + }; + struct wl_subcompositor *wl_subcompositor = NULL; + wl_registry_add_listener(wl_registry, &wl_registry_listener, &wl_subcompositor); + wl_display_roundtrip(wl_display); + printf("XXX: wl_subcompositor: %p\n", wl_subcompositor); + + struct wl_surface *toplevel_surface = gdk_wayland_surface_get_wl_surface(gtk_native_get_surface(GTK_NATIVE(window))); + + struct wl_surface *wayland_surface = wl_compositor_create_surface(wl_compositor); + + struct wl_subsurface *subsurface = wl_subcompositor_get_subsurface(wl_subcompositor, wayland_surface, toplevel_surface); + wl_subsurface_set_desync(subsurface); + wl_subsurface_set_position(subsurface, pos_x, pos_y); + + struct wl_egl_window *egl_window = wl_egl_window_create(wayland_surface, width, height); + native_window->egl_window = (EGLNativeWindowType)egl_window; printf("EGL::: wayland_surface: %p\n", wayland_surface); - printf("EGL::: native_window->glfw_window: %p\n", native_window->glfw_window); + // end of wayland-specific code printf("EGL::: native_window->egl_window: %ld\n", native_window->egl_window); + g_signal_connect(surface_view_widget, "resize", G_CALLBACK(on_resize), egl_window); + return native_window; } @@ -315,6 +354,8 @@ static void PrintConfigAttributes(EGLDisplay display, EGLConfig config) // FIXME: this possibly belongs elsewhere +extern GtkWindow *window; // TODO: how do we get rid of this? the app won't pass anyhting useful to eglGetDisplay + EGLDisplay bionic_eglGetDisplay(NativeDisplayType native_display) { /* @@ -322,18 +363,17 @@ EGLDisplay bionic_eglGetDisplay(NativeDisplayType native_display) * We obviously want to make the app use the correct display, which may happen to be a different one * than the "default" display (especially on Wayland) */ - glfwInit(); // it's allegedly safe to call this multiple times - struct wl_display *glfw_wl_display = glfwGetWaylandDisplay(); - return eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, glfw_wl_display, NULL); + GdkDisplay *display = gtk_root_get_display(GTK_ROOT(window)); + struct wl_display *wl_display = gdk_wayland_display_get_wl_display(display); + + return eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wl_display, NULL); } EGLSurface bionic_eglCreateWindowSurface(EGLDisplay display, EGLConfig config, struct ANativeWindow *native_window, EGLint const *attrib_list) { - // TODO: eglGetDisplay((EGLNativeDisplayType)0) isn't ideal... PrintConfigAttributes(display, config); EGLSurface ret = eglCreateWindowSurface(display, config, native_window->egl_window, attrib_list); - printf("EGL::: native_window->glfw_window: %p\n", native_window->glfw_window); printf("EGL::: native_window->egl_window: %ld\n", native_window->egl_window); printf("EGL::: eglGetError: %d\n", eglGetError());