#include #include #include #include #include #include #include // 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; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; EGLBoolean errcode = 0; void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { printf("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(void *data, struct wl_registry *registry, 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(stdout, "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, ®istry_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); switch (errcode) { case EGL_FALSE: case EGL_BAD_DISPLAY: case EGL_NOT_INITIALIZED: fprintf(stderr, "Could not initialize EGL display\n"); break; case EGL_TRUE: ; break; } int config_count; errcode = eglGetConfigs(egl_display, NULL, 0, &config_count); switch (errcode) { case EGL_FALSE: case EGL_BAD_DISPLAY: case EGL_NOT_INITIALIZED: case EGL_BAD_PARAMETER: fprintf(stderr, "Could not retrieve EGL config number\n"); break; case EGL_TRUE: ; break; } 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); switch (errcode) { case EGL_FALSE: case EGL_BAD_DISPLAY: case EGL_NOT_INITIALIZED: case EGL_BAD_PARAMETER: fprintf(stderr, "Could not choose EGL config parameters\n"); break; case EGL_TRUE: ; break; } 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, "Could not choose EGL config parameters\n"); break; case EGL_TRUE: ; break; } static const EGLint context_attribs[] = { EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 2, #ifdef DEBUG EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE, #endif EGL_NONE }; /* Change from OpenGL ES to regular OpenGL */ errcode = eglBindAPI(EGL_OPENGL_API); switch (errcode) { case EGL_FALSE: case EGL_BAD_PARAMETER: fprintf(stderr, "Could not bind OpenGL API to EGL\n"); break; } EGLContext egl_context = eglCreateContext(egl_display, chosen_config, EGL_NO_CONTEXT, context_attribs); errcode = eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); switch (errcode) { case EGL_BAD_CONTEXT: case EGL_BAD_SURFACE: case EGL_BAD_MATCH: case EGL_BAD_NATIVE_WINDOW: case EGL_BAD_CURRENT_SURFACE: case EGL_BAD_ALLOC: case EGL_CONTEXT_LOST: case EGL_NOT_INITIALIZED: case EGL_BAD_DISPLAY: fprintf(stderr, "Could not make EGL context current\n"); break; } /* 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"); wl_surface_commit(surface); int framecount = 0; // Main event loop 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); } wl_display_disconnect(display); return 0; }