-
-
Save Miouyouyou/ca15af1c7f2696f66b0e013058f110b4 to your computer and use it in GitHub Desktop.
// gcc -o test init_window.c -I. -lwayland-client -lwayland-server -lwayland-client-protocol -lwayland-egl -lEGL -lGLESv2 | |
#include <wayland-client.h> | |
#include <wayland-server.h> | |
#include <wayland-client-protocol.h> | |
#include <wayland-egl.h> // Wayland EGL MUST be included before EGL headers | |
#include "init_window.h" | |
#include "log.h" | |
#include <string.h> | |
#include <stdlib.h> | |
#include <GLES2/gl2.h> | |
struct wl_compositor *compositor = NULL; | |
struct wl_surface *surface; | |
struct wl_egl_window *egl_window; | |
struct wl_region *region; | |
struct wl_shell *shell; | |
struct wl_shell_surface *shell_surface; | |
struct _escontext ESContext = { | |
.native_display = NULL, | |
.window_width = 0, | |
.window_height = 0, | |
.native_window = 0, | |
.display = NULL, | |
.context = NULL, | |
.surface = NULL | |
}; | |
#define TRUE 1 | |
#define FALSE 0 | |
#define WINDOW_WIDTH 1280 | |
#define WINDOW_HEIGHT 720 | |
void CreateNativeWindow(char *title, int width, int height) { | |
region = wl_compositor_create_region(compositor); | |
wl_region_add(region, 0, 0, width, height); | |
wl_surface_set_opaque_region(surface, region); | |
struct wl_egl_window *egl_window = | |
wl_egl_window_create(surface, width, height); | |
if (egl_window == EGL_NO_SURFACE) { | |
LOG("No window !?\n"); | |
exit(1); | |
} | |
else LOG("Window created !\n"); | |
ESContext.window_width = width; | |
ESContext.window_height = height; | |
ESContext.native_window = egl_window; | |
} | |
EGLBoolean CreateEGLContext () | |
{ | |
EGLint numConfigs; | |
EGLint majorVersion; | |
EGLint minorVersion; | |
EGLContext context; | |
EGLSurface surface; | |
EGLConfig config; | |
EGLint fbAttribs[] = | |
{ | |
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | |
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | |
EGL_RED_SIZE, 8, | |
EGL_GREEN_SIZE, 8, | |
EGL_BLUE_SIZE, 8, | |
EGL_NONE | |
}; | |
EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE }; | |
EGLDisplay display = eglGetDisplay( ESContext.native_display ); | |
if ( display == EGL_NO_DISPLAY ) | |
{ | |
LOG("No EGL Display...\n"); | |
return EGL_FALSE; | |
} | |
// Initialize EGL | |
if ( !eglInitialize(display, &majorVersion, &minorVersion) ) | |
{ | |
LOG("No Initialisation...\n"); | |
return EGL_FALSE; | |
} | |
// Get configs | |
if ( (eglGetConfigs(display, NULL, 0, &numConfigs) != EGL_TRUE) || (numConfigs == 0)) | |
{ | |
LOG("No configuration...\n"); | |
return EGL_FALSE; | |
} | |
// Choose config | |
if ( (eglChooseConfig(display, fbAttribs, &config, 1, &numConfigs) != EGL_TRUE) || (numConfigs != 1)) | |
{ | |
LOG("No configuration...\n"); | |
return EGL_FALSE; | |
} | |
// Create a surface | |
surface = eglCreateWindowSurface(display, config, ESContext.native_window, NULL); | |
if ( surface == EGL_NO_SURFACE ) | |
{ | |
LOG("No surface...\n"); | |
return EGL_FALSE; | |
} | |
// Create a GL context | |
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs ); | |
if ( context == EGL_NO_CONTEXT ) | |
{ | |
LOG("No context...\n"); | |
return EGL_FALSE; | |
} | |
// Make the context current | |
if ( !eglMakeCurrent(display, surface, surface, context) ) | |
{ | |
LOG("Could not make the current window current !\n"); | |
return EGL_FALSE; | |
} | |
ESContext.display = display; | |
ESContext.surface = surface; | |
ESContext.context = context; | |
return EGL_TRUE; | |
} | |
void shell_surface_ping | |
(void *data, struct wl_shell_surface *shell_surface, uint32_t serial) { | |
wl_shell_surface_pong(shell_surface, serial); | |
} | |
void shell_surface_configure | |
(void *data, struct wl_shell_surface *shell_surface, uint32_t edges, | |
int32_t width, int32_t height) { | |
struct window *window = data; | |
wl_egl_window_resize(ESContext.native_window, width, height, 0, 0); | |
} | |
void shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface) { | |
} | |
static struct wl_shell_surface_listener shell_surface_listener = { | |
&shell_surface_ping, | |
&shell_surface_configure, | |
&shell_surface_popup_done | |
}; | |
EGLBoolean CreateWindowWithEGLContext(char *title, int width, int height) { | |
CreateNativeWindow(title, width, height); | |
return CreateEGLContext(); | |
} | |
void draw() { | |
glClearColor(0.5, 0.3, 0.0, 1.0); | |
glClear(GL_COLOR_BUFFER_BIT); | |
} | |
unsigned long last_click = 0; | |
void RefreshWindow() { eglSwapBuffers(ESContext.display, ESContext.surface); } | |
static void global_registry_handler | |
(void *data, struct wl_registry *registry, uint32_t id, | |
const char *interface, uint32_t version) { | |
LOG("Got a registry event for %s id %d\n", interface, id); | |
if (strcmp(interface, "wl_compositor") == 0) | |
compositor = | |
wl_registry_bind(registry, id, &wl_compositor_interface, 1); | |
else if (strcmp(interface, "wl_shell") == 0) | |
shell = wl_registry_bind(registry, id, &wl_shell_interface, 1); | |
} | |
static void global_registry_remover | |
(void *data, struct wl_registry *registry, uint32_t id) { | |
LOG("Got a registry losing event for %d\n", id); | |
} | |
const struct wl_registry_listener listener = { | |
global_registry_handler, | |
global_registry_remover | |
}; | |
static void | |
get_server_references() { | |
struct wl_display * display = wl_display_connect(NULL); | |
if (display == NULL) { | |
LOG("Can't connect to wayland display !?\n"); | |
exit(1); | |
} | |
LOG("Got a display !"); | |
struct wl_registry *wl_registry = | |
wl_display_get_registry(display); | |
wl_registry_add_listener(wl_registry, &listener, NULL); | |
// This call the attached listener global_registry_handler | |
wl_display_dispatch(display); | |
wl_display_roundtrip(display); | |
// If at this point, global_registry_handler didn't set the | |
// compositor, nor the shell, bailout ! | |
if (compositor == NULL || shell == NULL) { | |
LOG("No compositor !? No Shell !! There's NOTHING in here !\n"); | |
exit(1); | |
} | |
else { | |
LOG("Okay, we got a compositor and a shell... That's something !\n"); | |
ESContext.native_display = display; | |
} | |
} | |
void destroy_window() { | |
eglDestroySurface(ESContext.display, ESContext.surface); | |
wl_egl_window_destroy(ESContext.native_window); | |
wl_shell_surface_destroy(shell_surface); | |
wl_surface_destroy(surface); | |
eglDestroyContext(ESContext.display, ESContext.context); | |
} | |
int main() { | |
get_server_references(); | |
surface = wl_compositor_create_surface(compositor); | |
if (surface == NULL) { | |
LOG("No Compositor surface ! Yay....\n"); | |
exit(1); | |
} | |
else LOG("Got a compositor surface !\n"); | |
shell_surface = wl_shell_get_shell_surface(shell, surface); | |
wl_shell_surface_set_toplevel(shell_surface); | |
CreateWindowWithEGLContext("Nya", 1280, 720); | |
while (1) { | |
wl_display_dispatch_pending(ESContext.native_display); | |
draw(); | |
RefreshWindow(); | |
} | |
wl_display_disconnect(ESContext.native_display); | |
LOG("Display disconnected !\n"); | |
exit(0); | |
} |
#ifndef INIT_WINDOW_INCLUDED | |
#define INIT_WINDOW_INCLUDED 1 | |
#include <stdint.h> | |
#include <EGL/egl.h> | |
#include <EGL/eglplatform.h> | |
struct _escontext | |
{ | |
/// Native System informations | |
EGLNativeDisplayType native_display; | |
EGLNativeWindowType native_window; | |
uint16_t window_width, window_height; | |
/// EGL display | |
EGLDisplay display; | |
/// EGL context | |
EGLContext context; | |
/// EGL surface | |
EGLSurface surface; | |
}; | |
void CreateNativeWindow(char* title, int width, int height); | |
EGLBoolean CreateEGLContext(); | |
EGLBoolean CreateWindowWithEGLContext(char *title, int width, int height); | |
void RefreshWindow(); | |
#endif |
#ifndef MYY_LOG_H | |
#define MYY_LOG_H 1 | |
#include <string.h> | |
#if defined(DEBUG) | |
#if defined(__ANDROID__) | |
#include <android/log.h> | |
#define LOG(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-insanity", __VA_ARGS__)) | |
#define LOG_ERRNO(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "native-insanity", "Error : %s\n", strerror(errno))); ((void)__android_log_print(ANDROID_LOG_ERROR, "native-insanity", __VA_ARGS__) | |
#else | |
#include <stdio.h> | |
#define LOG(...) fprintf(stderr, __VA_ARGS__) | |
#define LOG_ERRNO(...) fprintf(stderr, "Error : %s\n", strerror(errno)); fprintf(stderr, __VA_ARGS__) | |
#endif | |
#else // DEBUG | |
#define LOG(...) | |
#define LOG_ERRNO(...) | |
#endif // DEBUG | |
#endif |
It's okay, I generally put these simple examples as a stepping stone for a few technologies, like Wayland or KMS/DRM/GLES, since, indeed, these API are easily overwhelming and some "simple" examples either do nothing, or try to do too much for a simple example.
In all cases, glad to see it still serves its purpose well. I'll try to delve back into Wayland and see if I can provide another example about window management and composition.
Note that Gnome will drop support for wl_shell
in version 42 as well. You'll need to use xdg-shell, see https://gist.github.com/Miouyouyou/ca15af1c7f2696f66b0e013058f110b4#gistcomment-3744284
Thanks for the code.
how to compile this on ubuntu 22.04? I got below error when I run the following command.
gcc -o test init_window.c xdg-shell-client-protocol.c -I. -lwayland-client -lwayland-server -lwayland-client-protocol -lwayland-egl -lEGL -lGLESv2
/usr/bin/ld: cannot find -lwayland-client-protocol: No such file or directory
collect2: error: ld returned 1 exit status
I tried installing sudo apt-get install libwayland-dev
but that didn't help
I tried installing
sudo apt-get install libwayland-dev
but that didn't help
Probably you're missing wayland-protocols
(https://packages.ubuntu.com/jammy/wayland-protocols)?
Well wl-shell is basically dead outside of GNOME, but yeah, I think it's really important to have a small snippet of code for other to use and this is the one I chose because it was simple enough, despite the title calling it ugly. Trust me it's really not. Actual Wayland projects are way uglier than this. Because of this many parts of Wayland initially overwhelmed me, because the API can be so complex at times, ESPECIALLY when you're the one drawing graphics manually, then you need to take care of memory allocations in the backend and make sure that you don't give the wrong pointer**.
I wish I could point to my visualizer as reference (as an example), but it already has way too many abstractions just like the projects I initially struggled with. Although it's unrealistic to make actual projects use barebones code (mostly because of the maintainability), I think that these should exist for the simple fact that they put less pressure on newcomers.
Apologies if this answer came out as too much, as I've been struggling with certain parts of wayland for a while.