pl/wayland.c
2024-11-19 17:12:41 +01:00

261 lines
9.3 KiB
C

#include <stdio.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <EGL/egl.h>
#include <GL/gl.h>
#include <string.h>
#include <stdlib.h>
// Generated header using wayland-scanner
#include "xdg-shell-client-protocol.h"
struct wl_compositor *compositor = NULL;
struct wl_registry *registry = NULL;
struct wl_surface *surface = NULL;
struct xdg_wm_base *wm_base = NULL;
struct xdg_surface *xdg_surface = NULL;
struct xdg_toplevel *xdg_toplevel = NULL;
EGLBoolean errcode = 0;
char *egl_s(int code)
{
switch (code)
{
case (intptr_t)EGL_NO_SURFACE: return "EGL_NO_SURFACE";
case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
default: return "UNKNOWN";
}
}
void registry_handle_global([[maybe_unused]] void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
fprintf(stdout, "Wayland interface: '%s', version: %d, name: %d\n",
interface, version, name);
if (!strcmp(interface, wl_compositor_interface.name))
{
fprintf(stdout, "Registering compositor...\n");
compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
}
if (!strcmp(interface, xdg_wm_base_interface.name))
{
fprintf(stdout, "Registering XDG WM base interface...\n");
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
}
}
void registry_handle_global_remove([[maybe_unused]]void *data, [[maybe_unused]]struct wl_registry *registry,
[[maybe_unused]]uint32_t name)
{
// This space deliberately left blank
}
static const struct wl_registry_listener
registry_listener = {
.global = registry_handle_global,
.global_remove = registry_handle_global_remove,
};
int main()
{
fprintf(stdout, "Starting platform layer...\n");
struct wl_display *display = wl_display_connect(NULL);
if (!display)
{
fprintf(stderr, "Cannot retrieve wayland display!\n");
}
registry = wl_display_get_registry(display);
if (!registry)
{
fprintf(stderr, "Cannot retrieve wayland registry!\n");
}
fprintf(stdout, "Linking registry listener...\n");
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
surface = wl_compositor_create_surface(compositor);
if (!surface)
{
fprintf(stderr, "Can't create surface!\n");
}
xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, surface);
xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
xdg_toplevel_set_title(xdg_toplevel, "Hi");
/* WL EGL initialization */
struct wl_egl_window *window = wl_egl_window_create(surface, 480, 460);
EGLDisplay egl_display = eglGetDisplay((EGLNativeDisplayType) display);
if (egl_display == EGL_NO_DISPLAY)
{
fprintf(stderr, "Could not retrieve EGL display from wayland display\n");
}
errcode = eglInitialize(egl_display, NULL, NULL);
if (errcode != EGL_TRUE)
fprintf(stderr, "ERR %s: Could not initialize EGL display\n", egl_s(errcode));
int config_count;
errcode = eglGetConfigs(egl_display, NULL, 0, &config_count);
if (errcode != EGL_TRUE)
fprintf(stderr, "ERR %s: Could not retrieve EGL config number\n", egl_s(errcode));
EGLConfig *configs = calloc(config_count, sizeof(EGLConfig));
EGLint attr_list[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_RENDERABLE_TYPE, (EGL_OPENGL_BIT | EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT), // better be safe than sorry with our compatibility bitmask
EGL_NONE
};
int chosen_config_count;
errcode = eglChooseConfig(egl_display, attr_list, configs, config_count * sizeof(EGLConfig), &chosen_config_count);
if (errcode != EGL_TRUE)
fprintf(stderr, "ERR %s: Could not choose EGL config parameters\n", egl_s(errcode));
int chosen_config_id = 0;
EGLConfig *chosen_config = configs[0];
fprintf(stdout, "Found %d possible configurations:\n", chosen_config_count);
for (int i = 0; i < chosen_config_count; ++i)
{
fprintf(stdout, "\tCONFIG %d:\n", i);
int attr;
#define PRINT_ATTR(attr_name) \
errcode = eglGetConfigAttrib(egl_display, configs[i], attr_name, &attr); \
[[maybe_unused]] int attr_##attr_name = -1; \
do { \
if (errcode == EGL_TRUE) { \
fprintf(stdout, "\t\t" #attr_name ": %d (%#0x)\n", attr, attr); \
attr_##attr_name = attr; \
} \
} while (0)
PRINT_ATTR(EGL_ALPHA_SIZE);
PRINT_ATTR(EGL_ALPHA_MASK_SIZE);
PRINT_ATTR(EGL_BIND_TO_TEXTURE_RGB);
PRINT_ATTR(EGL_BIND_TO_TEXTURE_RGBA);
PRINT_ATTR(EGL_BLUE_SIZE);
PRINT_ATTR(EGL_BUFFER_SIZE);
PRINT_ATTR(EGL_COLOR_BUFFER_TYPE);
PRINT_ATTR(EGL_CONFIG_CAVEAT);
PRINT_ATTR(EGL_CONFIG_ID);
PRINT_ATTR(EGL_CONFORMANT);
PRINT_ATTR(EGL_DEPTH_SIZE);
PRINT_ATTR(EGL_GREEN_SIZE);
PRINT_ATTR(EGL_LEVEL);
PRINT_ATTR(EGL_LUMINANCE_SIZE);
PRINT_ATTR(EGL_MAX_PBUFFER_WIDTH);
PRINT_ATTR(EGL_MAX_PBUFFER_HEIGHT);
PRINT_ATTR(EGL_MAX_PBUFFER_PIXELS);
PRINT_ATTR(EGL_MAX_SWAP_INTERVAL);
PRINT_ATTR(EGL_NATIVE_RENDERABLE);
PRINT_ATTR(EGL_NATIVE_VISUAL_ID);
PRINT_ATTR(EGL_NATIVE_VISUAL_TYPE);
PRINT_ATTR(EGL_RED_SIZE);
PRINT_ATTR(EGL_RENDERABLE_TYPE);
PRINT_ATTR(EGL_SAMPLE_BUFFERS);
PRINT_ATTR(EGL_SAMPLES);
PRINT_ATTR(EGL_STENCIL_SIZE);
PRINT_ATTR(EGL_SURFACE_TYPE);
PRINT_ATTR(EGL_TRANSPARENT_TYPE);
PRINT_ATTR(EGL_TRANSPARENT_RED_VALUE);
PRINT_ATTR(EGL_TRANSPARENT_GREEN_VALUE);
PRINT_ATTR(EGL_TRANSPARENT_BLUE_VALUE);
// Try to at least choose an RGBA 32bit config
if (attr_EGL_ALPHA_SIZE == 8 &&
attr_EGL_BLUE_SIZE == 8 &&
attr_EGL_RED_SIZE == 8 &&
attr_EGL_GREEN_SIZE == 8 &&
attr_EGL_BUFFER_SIZE == 32 &&
attr_EGL_COLOR_BUFFER_TYPE == EGL_RGB_BUFFER &&
attr_EGL_SAMPLE_BUFFERS == 0) // with no multisampling
{
chosen_config_id = i;
}
}
#undef PRINT_ATTR
chosen_config = configs[chosen_config_id];
fprintf(stdout, "Chosen config #%d\n", chosen_config_id);
EGLSurface egl_surface = eglCreateWindowSurface(egl_display, chosen_config, (EGLNativeWindowType)window, NULL);
switch ((intptr_t)egl_surface)
{
case (intptr_t)EGL_NO_SURFACE:
case EGL_BAD_DISPLAY:
case EGL_NOT_INITIALIZED:
case EGL_BAD_CONFIG:
case EGL_BAD_NATIVE_WINDOW:
case EGL_BAD_ATTRIBUTE:
case EGL_BAD_ALLOC:
case EGL_BAD_MATCH:
fprintf(stderr, "ERR %s: Could not create EGL window surface\n", egl_s((intptr_t)egl_surface));
break;
}
/* Even if we ask for a particular OGL version, the driver is likely to choose the latest one, so we do not even bother */
static const EGLint context_attribs[] = {
#ifdef DEBUG
EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE,
#endif
EGL_NONE
};
/* Change from OpenGL ES to regular OpenGL */
errcode = eglBindAPI(EGL_OPENGL_API);
if (errcode != EGL_TRUE)
fprintf(stderr, "ERR %s: Could not bind OpenGL API to EGL\n", egl_s(errcode));
// Since we already bound desktop OGL, this is actually making a OGL context instead of an OGL ES one
EGLContext egl_context = eglCreateContext(egl_display, chosen_config, EGL_NO_CONTEXT, context_attribs);
errcode = eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
if (errcode != EGL_TRUE)
fprintf(stderr, "ERR %s: could not make EGL context current\n", egl_s(errcode));
/* NOTE: It would seem that having a non-zero swap interval can hang the Mesa driver forever, according to this SDL GH issue:
https://github.com/libsdl-org/SDL/issues/4335#issuecomment-829789881
*/
if (eglSwapInterval(egl_display, 0) != EGL_TRUE)
fprintf(stderr, "Could not set EGL swap interval\n");
// necessary before our first eglSwapBuffers
wl_surface_commit(surface);
// Main event loop
int framecount = 0;
while (1)
{
wl_display_dispatch_pending(display);
//fprintf(stdout, "Frame %d\n", framecount);
glClearColor(0.5, 0.3, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
eglSwapBuffers(egl_display, egl_surface);
++framecount;
}
wl_display_disconnect(display);
return 0;
}