mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2025-08-28 18:23:54 +02:00
276 lines
9.3 KiB
C
276 lines
9.3 KiB
C
/*
|
|
* Wayland OpenGL functions
|
|
*
|
|
* Copyright 2020 Alexandros Frantzis for Collabora Ltd.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#if 0
|
|
#pragma makedep unix
|
|
#endif
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <dlfcn.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "waylanddrv.h"
|
|
#include "wine/debug.h"
|
|
|
|
#ifdef HAVE_LIBWAYLAND_EGL
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
|
|
|
|
#include <wayland-egl.h>
|
|
|
|
#include "wine/opengl_driver.h"
|
|
|
|
static const struct egl_platform *egl;
|
|
static const struct opengl_funcs *funcs;
|
|
static const struct opengl_drawable_funcs wayland_drawable_funcs;
|
|
|
|
struct wayland_gl_drawable
|
|
{
|
|
struct opengl_drawable base;
|
|
struct wl_egl_window *wl_egl_window;
|
|
};
|
|
|
|
static struct wayland_gl_drawable *impl_from_opengl_drawable(struct opengl_drawable *base)
|
|
{
|
|
return CONTAINING_RECORD(base, struct wayland_gl_drawable, base);
|
|
}
|
|
|
|
static void wayland_drawable_destroy(struct opengl_drawable *base)
|
|
{
|
|
struct wayland_gl_drawable *gl = impl_from_opengl_drawable(base);
|
|
if (gl->wl_egl_window) wl_egl_window_destroy(gl->wl_egl_window);
|
|
}
|
|
|
|
static EGLConfig egl_config_for_format(int format)
|
|
{
|
|
assert(format > 0 && format <= 2 * egl->config_count);
|
|
if (format <= egl->config_count) return egl->configs[format - 1];
|
|
return egl->configs[format - egl->config_count - 1];
|
|
}
|
|
|
|
static void wayland_gl_drawable_sync_size(struct wayland_gl_drawable *gl)
|
|
{
|
|
int client_width, client_height;
|
|
RECT client_rect = {0};
|
|
|
|
NtUserGetClientRect(gl->base.client->hwnd, &client_rect, NtUserGetDpiForWindow(gl->base.client->hwnd));
|
|
client_width = client_rect.right - client_rect.left;
|
|
client_height = client_rect.bottom - client_rect.top;
|
|
if (client_width == 0 || client_height == 0) client_width = client_height = 1;
|
|
|
|
wl_egl_window_resize(gl->wl_egl_window, client_width, client_height, 0, 0);
|
|
}
|
|
|
|
static BOOL wayland_opengl_surface_create(HWND hwnd, HDC hdc, int format, struct opengl_drawable **drawable)
|
|
{
|
|
EGLConfig config = egl_config_for_format(format);
|
|
struct wayland_client_surface *client;
|
|
EGLint attribs[4], *attrib = attribs;
|
|
struct opengl_drawable *previous;
|
|
struct wayland_gl_drawable *gl;
|
|
RECT rect;
|
|
|
|
TRACE("hwnd=%p format=%d\n", hwnd, format);
|
|
|
|
if ((previous = *drawable) && previous->format == format) return TRUE;
|
|
|
|
NtUserGetClientRect(hwnd, &rect, NtUserGetDpiForWindow(hwnd));
|
|
if (rect.right == rect.left) rect.right = rect.left + 1;
|
|
if (rect.bottom == rect.top) rect.bottom = rect.top + 1;
|
|
|
|
if (!egl->has_EGL_EXT_present_opaque)
|
|
WARN("Missing EGL_EXT_present_opaque extension\n");
|
|
else
|
|
{
|
|
*attrib++ = EGL_PRESENT_OPAQUE_EXT;
|
|
*attrib++ = EGL_TRUE;
|
|
}
|
|
*attrib++ = EGL_NONE;
|
|
|
|
if (!(client = wayland_client_surface_create(hwnd))) return FALSE;
|
|
gl = opengl_drawable_create(sizeof(*gl), &wayland_drawable_funcs, format, &client->client);
|
|
client_surface_release(&client->client);
|
|
if (!gl) return FALSE;
|
|
|
|
if (!(gl->wl_egl_window = wl_egl_window_create(client->wl_surface, rect.right, rect.bottom))) goto err;
|
|
if (!(gl->base.surface = funcs->p_eglCreateWindowSurface(egl->display, config, gl->wl_egl_window, attribs))) goto err;
|
|
set_client_surface(hwnd, client);
|
|
|
|
TRACE("Created drawable %s with egl_surface %p\n", debugstr_opengl_drawable(&gl->base), gl->base.surface);
|
|
|
|
if (previous) opengl_drawable_release( previous );
|
|
*drawable = &gl->base;
|
|
return TRUE;
|
|
|
|
err:
|
|
opengl_drawable_release(&gl->base);
|
|
return FALSE;
|
|
}
|
|
|
|
static void wayland_init_egl_platform(struct egl_platform *platform)
|
|
{
|
|
platform->type = EGL_PLATFORM_WAYLAND_KHR;
|
|
platform->native_display = process_wayland.wl_display;
|
|
platform->force_pbuffer_formats = TRUE;
|
|
egl = platform;
|
|
}
|
|
|
|
static void wayland_drawable_flush(struct opengl_drawable *base, UINT flags)
|
|
{
|
|
struct wayland_gl_drawable *gl = impl_from_opengl_drawable(base);
|
|
|
|
TRACE("drawable %s, flags %#x\n", debugstr_opengl_drawable(base), flags);
|
|
|
|
if (flags & GL_FLUSH_INTERVAL) funcs->p_eglSwapInterval(egl->display, abs(base->interval));
|
|
|
|
/* Since context_flush is called from operations that may latch the native size,
|
|
* perform any pending resizes before calling them. */
|
|
if (flags & GL_FLUSH_UPDATED) wayland_gl_drawable_sync_size(gl);
|
|
}
|
|
|
|
static BOOL wayland_drawable_swap(struct opengl_drawable *base)
|
|
{
|
|
struct wayland_gl_drawable *gl = impl_from_opengl_drawable(base);
|
|
|
|
client_surface_present(base->client, NULL);
|
|
funcs->p_eglSwapBuffers(egl->display, gl->base.surface);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
struct wayland_pbuffer
|
|
{
|
|
struct opengl_drawable base;
|
|
struct wl_surface *surface;
|
|
struct wl_egl_window *window;
|
|
};
|
|
|
|
static struct wayland_pbuffer *pbuffer_from_opengl_drawable(struct opengl_drawable *base)
|
|
{
|
|
return CONTAINING_RECORD(base, struct wayland_pbuffer, base);
|
|
}
|
|
|
|
static void wayland_pbuffer_destroy(struct opengl_drawable *base)
|
|
{
|
|
struct wayland_pbuffer *gl = pbuffer_from_opengl_drawable(base);
|
|
|
|
TRACE("%s\n", debugstr_opengl_drawable(base));
|
|
|
|
if (gl->window)
|
|
wl_egl_window_destroy(gl->window);
|
|
if (gl->surface)
|
|
wl_surface_destroy(gl->surface);
|
|
}
|
|
|
|
static const struct opengl_drawable_funcs wayland_pbuffer_funcs =
|
|
{
|
|
.destroy = wayland_pbuffer_destroy,
|
|
};
|
|
|
|
static BOOL wayland_pbuffer_create(HDC hdc, int format, BOOL largest, GLenum texture_format, GLenum texture_target,
|
|
GLint max_level, GLsizei *width, GLsizei *height, struct opengl_drawable **surface)
|
|
{
|
|
EGLConfig config = egl_config_for_format(format);
|
|
struct wayland_pbuffer *gl;
|
|
|
|
TRACE("hdc %p, format %d, largest %u, texture_format %#x, texture_target %#x, max_level %#x, width %d, height %d, private %p\n",
|
|
hdc, format, largest, texture_format, texture_target, max_level, *width, *height, surface);
|
|
|
|
if (!(gl = opengl_drawable_create(sizeof(*gl), &wayland_pbuffer_funcs, format, NULL))) return FALSE;
|
|
/* Wayland EGL doesn't support pixmap or pbuffer, create a dummy window surface to act as the target render surface. */
|
|
if (!(gl->surface = wl_compositor_create_surface(process_wayland.wl_compositor))) goto err;
|
|
if (!(gl->window = wl_egl_window_create(gl->surface, *width, *height))) goto err;
|
|
if (!(gl->base.surface = funcs->p_eglCreateWindowSurface(egl->display, config, gl->window, NULL))) goto err;
|
|
|
|
TRACE("Created pbuffer %s with egl_surface %p\n", debugstr_opengl_drawable(&gl->base), gl->base.surface);
|
|
*surface = &gl->base;
|
|
return TRUE;
|
|
|
|
err:
|
|
opengl_drawable_release(&gl->base);
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL wayland_pbuffer_updated(HDC hdc, struct opengl_drawable *base, GLenum cube_face, GLint mipmap_level)
|
|
{
|
|
return GL_TRUE;
|
|
}
|
|
|
|
static UINT wayland_pbuffer_bind(HDC hdc, struct opengl_drawable *base, GLenum buffer)
|
|
{
|
|
return -1; /* use default implementation */
|
|
}
|
|
|
|
static struct opengl_driver_funcs wayland_driver_funcs =
|
|
{
|
|
.p_init_egl_platform = wayland_init_egl_platform,
|
|
.p_surface_create = wayland_opengl_surface_create,
|
|
.p_pbuffer_create = wayland_pbuffer_create,
|
|
.p_pbuffer_updated = wayland_pbuffer_updated,
|
|
.p_pbuffer_bind = wayland_pbuffer_bind,
|
|
};
|
|
|
|
static const struct opengl_drawable_funcs wayland_drawable_funcs =
|
|
{
|
|
.destroy = wayland_drawable_destroy,
|
|
.flush = wayland_drawable_flush,
|
|
.swap = wayland_drawable_swap,
|
|
};
|
|
|
|
/**********************************************************************
|
|
* WAYLAND_OpenGLInit
|
|
*/
|
|
UINT WAYLAND_OpenGLInit(UINT version, const struct opengl_funcs *opengl_funcs, const struct opengl_driver_funcs **driver_funcs)
|
|
{
|
|
if (version != WINE_OPENGL_DRIVER_VERSION)
|
|
{
|
|
ERR("Version mismatch, opengl32 wants %u but driver has %u\n",
|
|
version, WINE_OPENGL_DRIVER_VERSION);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!opengl_funcs->egl_handle) return STATUS_NOT_SUPPORTED;
|
|
funcs = opengl_funcs;
|
|
|
|
wayland_driver_funcs.p_get_proc_address = (*driver_funcs)->p_get_proc_address;
|
|
wayland_driver_funcs.p_init_pixel_formats = (*driver_funcs)->p_init_pixel_formats;
|
|
wayland_driver_funcs.p_describe_pixel_format = (*driver_funcs)->p_describe_pixel_format;
|
|
wayland_driver_funcs.p_init_wgl_extensions = (*driver_funcs)->p_init_wgl_extensions;
|
|
wayland_driver_funcs.p_context_create = (*driver_funcs)->p_context_create;
|
|
wayland_driver_funcs.p_context_destroy = (*driver_funcs)->p_context_destroy;
|
|
wayland_driver_funcs.p_make_current = (*driver_funcs)->p_make_current;
|
|
|
|
*driver_funcs = &wayland_driver_funcs;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#else /* No GL */
|
|
|
|
UINT WAYLAND_OpenGLInit(UINT version, const struct opengl_funcs *opengl_funcs, const struct opengl_driver_funcs **driver_funcs)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
#endif
|