diff --git a/client/displayservers/SDL/sdl.c b/client/displayservers/SDL/sdl.c index 5ed2f50d..6dcbbbe1 100644 --- a/client/displayservers/SDL/sdl.c +++ b/client/displayservers/SDL/sdl.c @@ -229,6 +229,11 @@ static EGLNativeWindowType sdlGetEGLNativeWindow(void) return 0; } } + +static void sdlEGLSwapBuffers(EGLDisplay display, EGLSurface surface) +{ + eglSwapBuffers(display, surface); +} #endif static void sdlSwapBuffers(void) @@ -470,6 +475,7 @@ struct LG_DisplayServerOps LGDS_SDL = #ifdef ENABLE_EGL .getEGLDisplay = sdlGetEGLDisplay, .getEGLNativeWindow = sdlGetEGLNativeWindow, + .eglSwapBuffers = sdlEGLSwapBuffers, #endif .glSwapBuffers = sdlSwapBuffers, diff --git a/client/displayservers/Wayland/CMakeLists.txt b/client/displayservers/Wayland/CMakeLists.txt index bb417028..ec4c8518 100644 --- a/client/displayservers/Wayland/CMakeLists.txt +++ b/client/displayservers/Wayland/CMakeLists.txt @@ -46,6 +46,9 @@ endmacro() file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland") include_directories("${CMAKE_BINARY_DIR}/wayland") +wayland_generate( + "${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml" + "${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol") wayland_generate( "${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" "${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol") diff --git a/client/displayservers/Wayland/wayland.c b/client/displayservers/Wayland/wayland.c index d56eb63b..d4d984b4 100644 --- a/client/displayservers/Wayland/wayland.c +++ b/client/displayservers/Wayland/wayland.c @@ -25,13 +25,21 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include +#include #include #include +#ifdef ENABLE_EGL +# include +# include "egl_dynprocs.h" +# include +#endif + #include "app.h" #include "common/debug.h" +#include "wayland-xdg-shell-client-protocol.h" #include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h" @@ -46,6 +54,19 @@ struct WaylandDSState struct wl_surface * surface; struct wl_registry * registry; struct wl_seat * seat; + struct wl_compositor * compositor; + + int32_t width, height; + uint32_t resizeSerial; + bool configured; + +#ifdef ENABLE_EGL + struct wl_egl_window * eglWindow; +#endif + + struct xdg_wm_base * xdgWmBase; + struct xdg_surface * xdgSurface; + struct xdg_toplevel * xdgToplevel; struct wl_data_device_manager * dataDeviceManager; struct wl_data_device * dataDevice; @@ -90,6 +111,17 @@ struct WCBState static struct WaylandDSState wm; static struct WCBState wcb; +// XDG WM base listeners. + +static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial) +{ + xdg_wm_base_pong(xdgWmBase, serial); +} + +static const struct xdg_wm_base_listener xdgWmBaseListener = { + .ping = xdgWmBasePing, +}; + // Registry-handling listeners. static void registryGlobalHandler(void * data, struct wl_registry * registry, @@ -97,6 +129,10 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry, { if (!strcmp(interface, wl_seat_interface.name) && !wm.seat) wm.seat = wl_registry_bind(wm.registry, name, &wl_seat_interface, 1); + else if (!strcmp(interface, wl_compositor_interface.name)) + wm.compositor = wl_registry_bind(wm.registry, name, &wl_compositor_interface, 4); + else if (!strcmp(interface, xdg_wm_base_interface.name)) + wm.xdgWmBase = wl_registry_bind(wm.registry, name, &xdg_wm_base_interface, 1); else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name)) wm.relativePointerManager = wl_registry_bind(wm.registry, name, &zwp_relative_pointer_manager_v1_interface, 1); @@ -321,19 +357,37 @@ static const struct wl_seat_listener seatListener = { .name = seatNameHandler, }; +// Surface-handling listeners. + +static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface, + uint32_t serial) +{ + if (wm.configured) + wm.resizeSerial = serial; + else + { + xdg_surface_ack_configure(xdgSurface, serial); + wm.configured = true; + } +} + +static const struct xdg_surface_listener xdgSurfaceListener = { + .configure = xdgSurfaceConfigure, +}; + +static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel, + int32_t width, int32_t height, struct wl_array * states) +{ + wm.width = width; + wm.height = height; +} + +static const struct xdg_toplevel_listener xdgToplevelListener = { + .configure = xdgToplevelConfigure, +}; + static bool waylandEarlyInit(void) { - if (!getenv("SDL_VIDEODRIVER")) - { - int err = setenv("SDL_VIDEODRIVER", "wayland", 1); - if (err < 0) - { - DEBUG_ERROR("Unable to set the env variable SDL_VIDEODRIVER: %d", err); - return false; - } - DEBUG_INFO("SDL_VIDEODRIVER has been set to wayland"); - } - // Request to receive EPIPE instead of SIGPIPE when one end of a pipe // disconnects while a write is pending. This is useful to the Wayland // clipboard backend, where an arbitrary application is on the other end of @@ -352,14 +406,13 @@ static bool waylandInit(const LG_DSInitParams params) { memset(&wm, 0, sizeof(wm)); - wm.display = info->info.wl.display; - wm.surface = info->info.wl.surface; + wm.display = wl_display_connect(NULL); wm.registry = wl_display_get_registry(wm.display); wl_registry_add_listener(wm.registry, ®istryListener, NULL); wl_display_roundtrip(wm.display); - if (!wm.seat || !wm.dataDeviceManager) + if (!wm.seat || !wm.dataDeviceManager || !wm.compositor || !wm.xdgWmBase) { DEBUG_ERROR("Compositor missing a required interface, will not proceed"); return false; @@ -379,11 +432,30 @@ static bool waylandInit(const LG_DSInitParams params) "not be able to suppress idle states"); wl_seat_add_listener(wm.seat, &seatListener, NULL); + xdg_wm_base_add_listener(wm.xdgWmBase, &xdgWmBaseListener, NULL); wl_display_roundtrip(wm.display); wm.dataDevice = wl_data_device_manager_get_data_device( wm.dataDeviceManager, wm.seat); + wm.surface = wl_compositor_create_surface(wm.compositor); + wm.eglWindow = wl_egl_window_create(wm.surface, params.w, params.h); + wm.xdgSurface = xdg_wm_base_get_xdg_surface(wm.xdgWmBase, wm.surface); + xdg_surface_add_listener(wm.xdgSurface, &xdgSurfaceListener, NULL); + + wm.xdgToplevel = xdg_surface_get_toplevel(wm.xdgSurface); + xdg_toplevel_add_listener(wm.xdgToplevel, &xdgToplevelListener, NULL); + xdg_toplevel_set_title(wm.xdgToplevel, params.title); + xdg_toplevel_set_app_id(wm.xdgToplevel, "looking-glass-client"); + + if (params.fullscreen) + xdg_toplevel_set_fullscreen(wm.xdgToplevel, NULL); + + wl_surface_commit(wm.surface); + + wm.width = params.w; + wm.height = params.h; + return true; } @@ -391,6 +463,109 @@ static void waylandStartup(void) { } +static void waylandShutdown(void) +{ +} + +#ifdef ENABLE_EGL +static EGLDisplay waylandGetEGLDisplay(void) +{ + EGLNativeDisplayType native = (EGLNativeDisplayType) wm.display; + + const char *early_exts = eglQueryString(NULL, EGL_EXTENSIONS); + + if (strstr(early_exts, "EGL_KHR_platform_base") != NULL && + g_egl_dynProcs.eglGetPlatformDisplay) + { + DEBUG_INFO("Using eglGetPlatformDisplay"); + return g_egl_dynProcs.eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, native, NULL); + } + + if (strstr(early_exts, "EGL_EXT_platform_base") != NULL && + g_egl_dynProcs.eglGetPlatformDisplayEXT) + { + DEBUG_INFO("Using eglGetPlatformDisplayEXT"); + return g_egl_dynProcs.eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, native, NULL); + } + + DEBUG_INFO("Using eglGetDisplay"); + return eglGetDisplay(native); +} + +static EGLNativeWindowType waylandGetEGLNativeWindow(void) +{ + return (EGLNativeWindowType) wm.eglWindow; +} + +static void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface) +{ + eglSwapBuffers(display, surface); + + if (wm.resizeSerial) + { + wl_egl_window_resize(wm.eglWindow, wm.width, wm.height, 0, 0); + + struct wl_region * region = wl_compositor_create_region(wm.compositor); + wl_region_add(region, 0, 0, wm.width, wm.height); + wl_surface_set_opaque_region(wm.surface, region); + wl_region_destroy(region); + + app_handleResizeEvent(wm.width, wm.height, (struct Border) {0, 0, 0, 0}); + xdg_surface_ack_configure(wm.xdgSurface, wm.resizeSerial); + wm.resizeSerial = 0; + } +} +#endif + +static void waylandGLSwapBuffers(void) +{ + // FIXME: implement. +} + +static void waylandShowPointer(bool show) +{ + // FIXME: implement. +} + +static void waylandWait(unsigned int time) +{ + while (wl_display_prepare_read(wm.display)) + wl_display_dispatch_pending(wm.display); + wl_display_flush(wm.display); + + struct pollfd pollfd = { + .fd = wl_display_get_fd(wm.display), + .events = POLLIN, + }; + + if (poll(&pollfd, 1, time) == -1 || pollfd.revents & POLLERR) + { + if (errno != EINTR) + DEBUG_INFO("Poll failed: %d\n", errno); + wl_display_cancel_read(wm.display); + } + else + wl_display_read_events(wm.display); + + wl_display_dispatch_pending(wm.display); +} + +static void waylandSetWindowSize(int x, int y) +{ + // FIXME: implement. +} + +static void waylandSetFullscreen(bool fs) +{ + // FIXME: implement. +} + +static bool waylandGetFullscreen(void) +{ + // FIXME: implement. + return false; +} + static void relativePointerMotionHandler(void * data, struct zwp_relative_pointer_v1 *pointer, uint32_t timeHi, uint32_t timeLo, wl_fixed_t dxW, wl_fixed_t dyW, wl_fixed_t dxUnaccelW, @@ -479,6 +654,11 @@ static void waylandRealignPointer(void) app_handleMouseBasic(); } +static bool waylandIsValidPointerPos(int x, int y) +{ + return x >= 0 && x < wm.width && y >= 0 && y < wm.height; +} + static void waylandFree(void) { waylandUngrabPointer(); @@ -508,23 +688,6 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret) return false; } -static bool waylandEventFilter(SDL_Event * event) -{ - /* prevent the default processing for the following events */ - switch(event->type) - { - case SDL_MOUSEMOTION: - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - case SDL_MOUSEWHEEL: - case SDL_KEYDOWN: - case SDL_KEYUP: - return true; - } - - return false; -} - static const char * textMimetypes[] = { "text/plain", @@ -862,21 +1025,36 @@ static void waylandCBRelease(void) struct LG_DisplayServerOps LGDS_Wayland = { - .probe = waylandProbe, - .earlyInit = waylandEarlyInit, - .init = waylandInit, - .startup = waylandStartup, - .free = waylandFree, - .getProp = waylandGetProp, - .eventFilter = waylandEventFilter, - .grabPointer = waylandGrabPointer, - .ungrabPointer = waylandUngrabPointer, - .grabKeyboard = waylandGrabKeyboard, - .ungrabKeyboard = waylandUngrabKeyboard, - .warpPointer = waylandWarpPointer, - .realignPointer = waylandRealignPointer, - .inhibitIdle = waylandInhibitIdle, - .uninhibitIdle = waylandUninhibitIdle, + .probe = waylandProbe, + .earlyInit = waylandEarlyInit, + .init = waylandInit, + .startup = waylandStartup, + .shutdown = waylandShutdown, + .free = waylandFree, + .getProp = waylandGetProp, + +#ifdef ENABLE_EGL + .getEGLDisplay = waylandGetEGLDisplay, + .getEGLNativeWindow = waylandGetEGLNativeWindow, + .eglSwapBuffers = waylandEGLSwapBuffers, +#endif + + .glSwapBuffers = waylandGLSwapBuffers, + + .showPointer = waylandShowPointer, + .grabPointer = waylandGrabPointer, + .ungrabPointer = waylandUngrabPointer, + .grabKeyboard = waylandGrabKeyboard, + .ungrabKeyboard = waylandUngrabKeyboard, + .warpPointer = waylandWarpPointer, + .realignPointer = waylandRealignPointer, + .isValidPointerPos = waylandIsValidPointerPos, + .inhibitIdle = waylandInhibitIdle, + .uninhibitIdle = waylandUninhibitIdle, + .wait = waylandWait, + .setWindowSize = waylandSetWindowSize, + .setFullscreen = waylandSetFullscreen, + .getFullscreen = waylandGetFullscreen, .cbInit = waylandCBInit, .cbNotice = waylandCBNotice, diff --git a/client/displayservers/X11/x11.c b/client/displayservers/X11/x11.c index 8c4aa9ad..d18d273a 100644 --- a/client/displayservers/X11/x11.c +++ b/client/displayservers/X11/x11.c @@ -814,6 +814,11 @@ static EGLNativeWindowType x11GetEGLNativeWindow(void) { return (EGLNativeWindowType)x11.window; } + +static void x11EGLSwapBuffers(EGLDisplay display, EGLSurface surface) +{ + eglSwapBuffers(display, surface); +} #endif static void x11GLSwapBuffers(void) @@ -1370,6 +1375,7 @@ struct LG_DisplayServerOps LGDS_X11 = #ifdef ENABLE_EGL .getEGLDisplay = x11GetEGLDisplay, .getEGLNativeWindow = x11GetEGLNativeWindow, + .eglSwapBuffers = x11EGLSwapBuffers, #endif .glSwapBuffers = x11GLSwapBuffers, .showPointer = x11ShowPointer, diff --git a/client/include/app.h b/client/include/app.h index 7dead1a7..ac4d69ea 100644 --- a/client/include/app.h +++ b/client/include/app.h @@ -60,6 +60,7 @@ bool app_getProp(LG_DSProperty prop, void * ret); EGLDisplay app_getEGLDisplay(void); EGLNativeWindowType app_getEGLNativeWindow(void); +void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface); void app_glSwapBuffers(void); void app_clipboardRelease(void); diff --git a/client/include/interface/displayserver.h b/client/include/interface/displayserver.h index eb2365e9..8d734b4d 100644 --- a/client/include/interface/displayserver.h +++ b/client/include/interface/displayserver.h @@ -100,6 +100,7 @@ struct LG_DisplayServerOps /* EGL support */ EGLDisplay (*getEGLDisplay)(void); EGLNativeWindowType (*getEGLNativeWindow)(void); + void (*eglSwapBuffers)(EGLDisplay display, EGLSurface surface); #endif /* opengl platform specific methods */ @@ -152,6 +153,7 @@ struct LG_DisplayServerOps (x)->getProp && \ (x)->getEGLDisplay && \ (x)->getEGLNativeWindow && \ + (x)->eglSwapBuffers && \ (x)->glSwapBuffers && \ (x)->showPointer && \ (x)->grabPointer && \ diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 41d13299..4b069293 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -164,13 +164,6 @@ void egl_setup(void) bool egl_create(void ** opaque, const LG_RendererParams params) { - // Fail if running on Wayland so that OpenGL is used instead. Wayland-EGL - // is broken (https://github.com/gnif/LookingGlass/issues/306) and isn't - // fixable until SDL is dropped entirely. Until then, the OpenGL renderer - // "mostly works". - if (getenv("WAYLAND_DISPLAY")) - return false; - // check if EGL is even available if (!eglQueryString(EGL_NO_DISPLAY, EGL_VERSION)) return false; @@ -707,7 +700,7 @@ bool egl_render(void * opaque, LG_RendererRotate rotate) } egl_fps_render(this->fps, this->screenScaleX, this->screenScaleY); - eglSwapBuffers(this->display, this->surface); + app_eglSwapBuffers(this->display, this->surface); return true; } diff --git a/client/src/app.c b/client/src/app.c index e512402c..64018376 100644 --- a/client/src/app.c +++ b/client/src/app.c @@ -576,6 +576,11 @@ EGLNativeWindowType app_getEGLNativeWindow(void) return g_state.ds->getEGLNativeWindow(); } +void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface) +{ + g_state.ds->eglSwapBuffers(display, surface); +} + void app_glSwapBuffers(void) { g_state.ds->glSwapBuffers();