replace use of glfw for SurfaceView with use of wayland subsurface positioned over a Gtk widget

This commit is contained in:
Mis012
2022-11-02 14:37:34 +01:00
parent e88709cf9d
commit 6a1d2f4ed4
7 changed files with 175 additions and 57 deletions

View File

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

View File

@@ -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)
{
}

View File

@@ -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));
}

View File

@@ -0,0 +1 @@
VOID:INT,INT

View File

@@ -1,5 +1,5 @@
package android.view;
public class Surface {
public long widget;
}

View File

@@ -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);

View File

@@ -34,17 +34,16 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
//#include <gtk/gtk.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <gtk/gtk.h>
#include <gdk/wayland/gdkwayland.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#define GLFW_EXPOSE_NATIVE_WAYLAND
#include <GLFW/glfw3native.h>
#include <jni.h>
// 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());