2018-11-15 23:42:29 +00:00
|
|
|
/*****************************************************************************\
|
|
|
|
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
|
|
|
|
This file is licensed under the Snes9x License.
|
|
|
|
For further information, consult the LICENSE file in the root directory.
|
|
|
|
\*****************************************************************************/
|
|
|
|
|
2018-10-21 23:03:35 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
#include "gtk_s9x.h"
|
2018-10-26 20:49:56 +00:00
|
|
|
#include "gtk_wayland_egl_context.h"
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
static void wl_global(void *data,
|
|
|
|
struct wl_registry *wl_registry,
|
|
|
|
uint32_t name,
|
|
|
|
const char *interface,
|
|
|
|
uint32_t version)
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
WaylandEGLContext *wl = (WaylandEGLContext *)data;
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
if (!strcmp(interface, "wl_compositor"))
|
|
|
|
wl->compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3);
|
|
|
|
else if (!strcmp(interface, "wl_subcompositor"))
|
|
|
|
wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1);
|
2018-10-21 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
static void wl_global_remove(void *data,
|
|
|
|
struct wl_registry *wl_registry,
|
|
|
|
uint32_t name)
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct wl_registry_listener wl_registry_listener = {
|
|
|
|
wl_global,
|
|
|
|
wl_global_remove
|
|
|
|
};
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
WaylandEGLContext::WaylandEGLContext()
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
display = NULL;
|
|
|
|
registry = NULL;
|
|
|
|
compositor = NULL;
|
2018-10-21 23:03:35 +00:00
|
|
|
subcompositor = NULL;
|
2019-03-02 21:25:59 +00:00
|
|
|
parent = NULL;
|
|
|
|
child = NULL;
|
|
|
|
region = NULL;
|
|
|
|
subsurface = NULL;
|
|
|
|
egl_display = NULL;
|
|
|
|
egl_surface = NULL;
|
|
|
|
egl_context = NULL;
|
|
|
|
egl_config = NULL;
|
|
|
|
egl_window = NULL;
|
|
|
|
use_sync_control = false;
|
|
|
|
ust = msc = sbc = 0;
|
2018-10-21 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
WaylandEGLContext::~WaylandEGLContext()
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
|
|
|
if (subsurface)
|
2019-03-02 21:25:59 +00:00
|
|
|
wl_subsurface_destroy(subsurface);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
|
|
|
if (region)
|
2019-03-02 21:25:59 +00:00
|
|
|
wl_region_destroy(region);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
|
|
|
if (child)
|
2019-03-02 21:25:59 +00:00
|
|
|
wl_surface_destroy(child);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
|
|
|
if (egl_context)
|
2019-03-02 21:25:59 +00:00
|
|
|
eglDestroyContext(egl_display, egl_context);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
|
|
|
if (egl_surface)
|
2019-03-02 21:25:59 +00:00
|
|
|
eglDestroySurface(egl_display, egl_surface);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
|
|
|
if (egl_window)
|
2019-03-02 21:25:59 +00:00
|
|
|
wl_egl_window_destroy(egl_window);
|
2018-10-21 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
bool WaylandEGLContext::attach(GtkWidget *widget)
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
GdkWindow *window = gtk_widget_get_window(widget);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
if (!GDK_IS_WAYLAND_WINDOW(window))
|
2018-10-21 23:03:35 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
gdk_window = window;
|
2019-03-02 21:25:59 +00:00
|
|
|
gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
display = gdk_wayland_display_get_wl_display(gdk_window_get_display(gdk_window));
|
|
|
|
parent = gdk_wayland_window_get_wl_surface(gdk_window);
|
|
|
|
registry = wl_display_get_registry(display);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
wl_registry_add_listener(registry, &wl_registry_listener, this);
|
|
|
|
wl_display_roundtrip(display);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
|
|
|
if (!compositor || !subcompositor)
|
|
|
|
return false;
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
child = wl_compositor_create_surface(compositor);
|
|
|
|
region = wl_compositor_create_region(compositor);
|
|
|
|
subsurface = wl_subcompositor_get_subsurface(subcompositor, child, parent);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
wl_surface_set_input_region(child, region);
|
|
|
|
wl_subsurface_set_desync(subsurface);
|
|
|
|
wl_subsurface_set_position(subsurface, x, y);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
bool WaylandEGLContext::create_context()
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
int scale = gdk_window_get_scale_factor(gdk_window);
|
|
|
|
gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
|
|
|
EGLint surface_attribs[] = {
|
|
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
|
|
|
|
EGL_RED_SIZE, 8,
|
|
|
|
EGL_BLUE_SIZE, 8,
|
|
|
|
EGL_GREEN_SIZE, 8,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
2018-10-27 23:13:51 +00:00
|
|
|
EGLint core_context_attribs[] = {
|
|
|
|
EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
|
|
|
|
EGL_CONTEXT_MAJOR_VERSION, 3,
|
|
|
|
EGL_CONTEXT_MINOR_VERSION, 3,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
EGLint compatibility_context_attribs[] = {
|
2018-10-21 23:03:35 +00:00
|
|
|
EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT,
|
|
|
|
EGL_NONE
|
|
|
|
};
|
|
|
|
|
|
|
|
EGLint num_configs = 0;
|
|
|
|
|
|
|
|
if (!subsurface)
|
|
|
|
return false;
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
egl_display = eglGetDisplay((EGLNativeDisplayType)display);
|
|
|
|
eglInitialize(egl_display, NULL, NULL);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
if (!eglChooseConfig(egl_display, surface_attribs, &egl_config, 1, &num_configs))
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
printf("Couldn't find matching config.\n");
|
2018-10-21 23:03:35 +00:00
|
|
|
return false;
|
|
|
|
}
|
2019-03-02 21:25:59 +00:00
|
|
|
eglBindAPI(EGL_OPENGL_API);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
egl_window = wl_egl_window_create(child, width * scale, height * scale);
|
2018-10-21 23:03:35 +00:00
|
|
|
if (!egl_window)
|
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
printf("Couldn't create window.\n");
|
2018-10-21 23:03:35 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
egl_surface = eglCreateWindowSurface(egl_display, egl_config, (EGLNativeWindowType)egl_window, NULL);
|
2018-10-21 23:03:35 +00:00
|
|
|
if (!egl_surface)
|
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
printf("Couldn't create surface.\n");
|
2018-10-21 23:03:35 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, core_context_attribs);
|
2018-10-21 23:03:35 +00:00
|
|
|
if (!egl_context)
|
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, compatibility_context_attribs);
|
2018-10-27 23:13:51 +00:00
|
|
|
if (!egl_context)
|
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
printf("Couldn't create context.\n");
|
2018-10-27 23:13:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-10-21 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
if (gui_config->use_sync_control && epoxy_has_egl_extension(egl_display, "EGL_CHROMIUM_sync_control"))
|
|
|
|
{
|
|
|
|
eglGetSyncValuesCHROMIUM = (PEGLGETSYNCVALUESCHROMIUM)eglGetProcAddress("eglGetSyncValuesCHROMIUM");
|
|
|
|
if (eglGetSyncValuesCHROMIUM)
|
|
|
|
use_sync_control = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl_surface_set_buffer_scale(child, scale);
|
|
|
|
gdk_window_invalidate_rect(gdk_window, NULL, false);
|
2018-10-22 19:36:52 +00:00
|
|
|
|
2018-10-21 23:03:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
void WaylandEGLContext::resize()
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
2018-10-26 23:22:51 +00:00
|
|
|
int scale;
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
|
|
|
|
scale = gdk_window_get_scale_factor(gdk_window);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
wl_egl_window_resize(egl_window, width * scale, height * scale, 0, 0);
|
|
|
|
wl_subsurface_set_position(subsurface, x, y);
|
2018-10-21 23:03:35 +00:00
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
make_current();
|
2018-10-21 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
void WaylandEGLContext::swap_buffers()
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
if (use_sync_control)
|
|
|
|
eglGetSyncValuesCHROMIUM(egl_display, egl_surface, &ust, &msc, &sbc);
|
|
|
|
|
|
|
|
eglSwapBuffers(egl_display, egl_surface);
|
|
|
|
wl_surface_commit(child);
|
2018-10-21 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
bool WaylandEGLContext::ready()
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
if (use_sync_control)
|
|
|
|
{
|
|
|
|
EGLuint64KHR ust, msc, sbc;
|
|
|
|
eglGetSyncValuesCHROMIUM(egl_display, egl_surface, &ust, &msc, &sbc);
|
|
|
|
if (sbc != this->sbc || msc - this->msc > 2)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2018-10-21 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
void WaylandEGLContext::make_current()
|
2018-10-21 23:03:35 +00:00
|
|
|
{
|
2019-03-02 21:25:59 +00:00
|
|
|
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
2018-10-21 23:03:35 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 21:25:59 +00:00
|
|
|
void WaylandEGLContext::swap_interval(int frames)
|
|
|
|
{
|
|
|
|
eglSwapInterval(egl_display, frames);
|
|
|
|
}
|