/** * Looking Glass * Copyright © 2017-2024 The Looking Glass Authors * https://looking-glass.io * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "wayland.h" #include #include #include #include #include "app.h" #include "common/debug.h" #include "util.h" #if defined(ENABLE_EGL) || defined(ENABLE_OPENGL) #include "egl_dynprocs.h" #include "eglutil.h" bool waylandEGLInit(int w, int h) { wlWm.eglWindow = wl_egl_window_create(wlWm.surface, w, h); if (!wlWm.eglWindow) { DEBUG_ERROR("Failed to create EGL window"); return false; } return true; } EGLDisplay waylandGetEGLDisplay(void) { EGLNativeDisplayType native = (EGLNativeDisplayType) wlWm.display; const char *early_exts = eglQueryString(NULL, EGL_EXTENSIONS); if (util_hasGLExt(early_exts, "EGL_KHR_platform_wayland") && g_egl_dynProcs.eglGetPlatformDisplay) { DEBUG_INFO("Using eglGetPlatformDisplay"); return g_egl_dynProcs.eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, native, NULL); } if (util_hasGLExt(early_exts, "EGL_EXT_platform_wayland") && 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); } void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count) { if (!wlWm.swapWithDamage.init) { if (wl_proxy_get_version((struct wl_proxy *) wlWm.surface) < 4) { DEBUG_INFO("Swapping buffers with damage: not supported, need wl_compositor v4"); swapWithDamageDisable(&wlWm.swapWithDamage); } else swapWithDamageInit(&wlWm.swapWithDamage, display); } waylandPresentationFrame(); swapWithDamage(&wlWm.swapWithDamage, display, surface, damage, count); if (wlWm.needsResize) { bool skipResize = false; int width, height; wlWm.desktop->getSize(&width, &height); wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(width * wlWm.scale), wl_fixed_to_int(height * wlWm.scale), 0, 0); if (width == 0 || height == 0) skipResize = true; else if (wlWm.fractionalScale) { wl_surface_set_buffer_scale(wlWm.surface, 1); if (!wlWm.viewport) wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface); wp_viewport_set_source(wlWm.viewport, 0, 0, width * wlWm.scale, height * wlWm.scale); wp_viewport_set_destination(wlWm.viewport, width, height); } else { if (wlWm.viewport) { // Clearing the source and destination rectangles should happen in wp_viewport_destroy. // However, wlroots does not clear the rectangle until fixed in 456c6e22 (2021-08-02). // This should be kept to work around old versions of wlroots. wl_fixed_t clear = wl_fixed_from_int(-1); wp_viewport_set_source(wlWm.viewport, clear, clear, clear, clear); wp_viewport_set_destination(wlWm.viewport, -1, -1); wp_viewport_destroy(wlWm.viewport); wlWm.viewport = NULL; } wl_surface_set_buffer_scale(wlWm.surface, wl_fixed_to_int(wlWm.scale)); } struct wl_region * region = wl_compositor_create_region(wlWm.compositor); wl_region_add(region, 0, 0, width, height); wl_surface_set_opaque_region(wlWm.surface, region); wl_region_destroy(region); app_handleResizeEvent(width, height, wl_fixed_to_double(wlWm.scale), (struct Border) {0, 0, 0, 0}); app_invalidateWindow(true); waylandStopWaitFrame(); wlWm.needsResize = skipResize; } wlWm.desktop->shellAckConfigureIfNeeded(); } #endif #ifdef ENABLE_EGL EGLNativeWindowType waylandGetEGLNativeWindow(void) { return (EGLNativeWindowType) wlWm.eglWindow; } #endif #ifdef ENABLE_OPENGL bool waylandOpenGLInit(void) { EGLint attr[] = { EGL_BUFFER_SIZE , 24, EGL_CONFORMANT , EGL_OPENGL_BIT, EGL_RENDERABLE_TYPE , EGL_OPENGL_BIT, EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, EGL_RED_SIZE , 8, EGL_GREEN_SIZE , 8, EGL_BLUE_SIZE , 8, EGL_SAMPLE_BUFFERS , 0, EGL_SAMPLES , 0, EGL_NONE }; wlWm.glDisplay = waylandGetEGLDisplay(); int maj, min; if (!eglInitialize(wlWm.glDisplay, &maj, &min)) { DEBUG_ERROR("Unable to initialize EGL"); return false; } if (wlWm.glDisplay == EGL_NO_DISPLAY) { DEBUG_ERROR("Failed to get EGL display (eglError: 0x%x)", eglGetError()); return false; } EGLint num_config; if (!eglChooseConfig(wlWm.glDisplay, attr, &wlWm.glConfig, 1, &num_config)) { DEBUG_ERROR("Failed to choose config (eglError: 0x%x)", eglGetError()); return false; } wlWm.glSurface = eglCreateWindowSurface(wlWm.glDisplay, wlWm.glConfig, wlWm.eglWindow, NULL); if (wlWm.glSurface == EGL_NO_SURFACE) { DEBUG_ERROR("Failed to create EGL surface (eglError: 0x%x)", eglGetError()); return false; } return true; } LG_DSGLContext waylandGLCreateContext(void) { eglBindAPI(EGL_OPENGL_API); return eglCreateContext(wlWm.glDisplay, wlWm.glConfig, EGL_NO_CONTEXT, NULL); } void waylandGLDeleteContext(LG_DSGLContext context) { eglDestroyContext(wlWm.glDisplay, context); } void waylandGLMakeCurrent(LG_DSGLContext context) { eglMakeCurrent(wlWm.glDisplay, wlWm.glSurface, wlWm.glSurface, context); } void waylandGLSetSwapInterval(int interval) { eglSwapInterval(wlWm.glDisplay, interval); } void waylandGLSwapBuffers(void) { waylandEGLSwapBuffers(wlWm.glDisplay, wlWm.glSurface, NULL, 0); } #endif