Skip to content

Instantly share code, notes, and snippets.

@Miouyouyou
Created December 15, 2016 07:55
Show Gist options
  • Save Miouyouyou/ca15af1c7f2696f66b0e013058f110b4 to your computer and use it in GitHub Desktop.
Save Miouyouyou/ca15af1c7f2696f66b0e013058f110b4 to your computer and use it in GitHub Desktop.
A very ugly Wayland EGL OpenGL example
// 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
@Miouyouyou
Copy link
Author

Yay ! 😃

@nikp123
Copy link

nikp123 commented May 16, 2021

Anyway updated init_window.c to support XDG instead of wl_shell (because sway)

https://gist.github.com/nikp123/bebe2d2dc9a8287efa9ba0a5b38ffab4

Before you compile it you need to generate protocol files which is done by doing the following:

wayland-scanner private-code /usr/share/wayland-protocols/unstable/xdg-shell/xdg-shell-unstable-v6.xml xdg-shell-client-protocol.c
wayland-scanner client-header /usr/share/wayland-protocols/unstable/xdg-shell/xdg-shell-unstable-v6.xml xdg-shell-client-protocol.h

And adding the xdg-shell-client-protocol.c file into GCC args.

@nongio
Copy link

nongio commented Sep 30, 2021

thanks @Miouyouyou and @nikp123 i used your gists to make a version with cairo-gl rendering ✏️
https://gist.github.com/nongio/cd9397ab7f147218bc20775c1e5f3129

@Miouyouyou
Copy link
Author

Well, glad it serves you well. I might try to update it before the end of the year, just to get along with new standards, as stated above.
Still, nice to see that this example is still useable after several years, clearly show that the API is stable.

@nikp123
Copy link

nikp123 commented Oct 1, 2021

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.

@Miouyouyou
Copy link
Author

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.

@rmader
Copy link

rmader commented Jan 6, 2022

@md-sami
Copy link

md-sami commented Nov 7, 2023

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

@rmader
Copy link

rmader commented Nov 7, 2023

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)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment