Skip to content

Instantly share code, notes, and snippets.

@nongio
Last active October 1, 2021 18:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nongio/cd9397ab7f147218bc20775c1e5f3129 to your computer and use it in GitHub Desktop.
Save nongio/cd9397ab7f147218bc20775c1e5f3129 to your computer and use it in GitHub Desktop.
A very ugly Wayland EGL Cairo-gl example
// https://gist.github.com/Miouyouyou/ca15af1c7f2696f66b0e013058f110b4
// https://gist.github.com/nikp123/bebe2d2dc9a8287efa9ba0a5b38ffab4
#include <wayland-client-core.h>
#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 "xdg-shell-client-protocol.h"
#include "init_window.h"
#include "log.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>
#include <GLES2/gl2.h>
#include <cairo/cairo-gl.h>
struct wl_compositor *compositor = NULL;
struct wl_surface *surface;
struct wl_egl_window *egl_window;
struct wl_region *region;
struct xdg_wm_base *XDGWMBase;
struct xdg_surface *XDGSurface;
struct xdg_toplevel *XDGToplevel;
struct _escontext ESContext = {
.native_display = NULL,
.window_width = 0,
.window_height = 0,
.native_window = 0,
.display = NULL,
.context = NULL,
.surface = NULL
};
cairo_surface_t *cairo_surface;
cairo_device_t *cairo_device;
#define TRUE 1
#define FALSE 0
#define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720
#define GL_BIT EGL_OPENGL_ES2_BIT
bool program_alive;
int32_t old_w, old_h;
static void xdg_toplevel_handle_configure(void *data,
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
struct wl_array *states) {
// no window geometry event, ignore
if(w == 0 && h == 0) return;
// window resized
if(old_w != w && old_h != h) {
old_w = w;
old_h = h;
wl_egl_window_resize(ESContext.native_window, w, h, 0, 0);
wl_surface_commit(surface);
}
}
static void xdg_toplevel_handle_close(void *data,
struct xdg_toplevel *xdg_toplevel) {
// window closed, be sure that this event gets processed
program_alive = false;
}
struct xdg_toplevel_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_handle_configure,
.close = xdg_toplevel_handle_close,
};
static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
uint32_t serial) {
// confirm that you exist to the compositor
xdg_surface_ack_configure(xdg_surface, serial);
}
const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_configure,
};
static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base,
uint32_t serial) {
xdg_wm_base_pong(xdg_wm_base, serial);
}
const struct xdg_wm_base_listener xdg_wm_base_listener = {
.ping = xdg_wm_base_ping,
};
void CreateNativeWindow(char *title, int width, int height) {
old_w = WINDOW_WIDTH;
old_h = WINDOW_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_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 1,
EGL_DEPTH_SIZE, 1,
EGL_RENDERABLE_TYPE, GL_BIT,
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;
}
EGLBoolean CreateWindowWithEGLContext(char *title, int width, int height) {
CreateNativeWindow(title, width, height);
return CreateEGLContext();
}
float i =0;
void draw() {
// cairo_t *cr = cairo_create(cairo_surface);
// i = i + 0.01;
// if(i > 1.0) {
// i = 0.0;
// }
// cairo_set_source_rgb(cr, i, 1.0, 0.0);
// cairo_paint(cr);
// cairo_gl_surface_swapbuffers(cairo_surface);
}
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, xdg_wm_base_interface.name) == 0) {
XDGWMBase = wl_registry_bind(registry, id,
&xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(XDGWMBase, &xdg_wm_base_listener, NULL);
}
}
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 || XDGWMBase == NULL) {
LOG("No compositor !? No XDG !! 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);
xdg_toplevel_destroy(XDGToplevel);
xdg_surface_destroy(XDGSurface);
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");
XDGSurface = xdg_wm_base_get_xdg_surface(XDGWMBase, surface);
xdg_surface_add_listener(XDGSurface, &xdg_surface_listener, NULL);
XDGToplevel = xdg_surface_get_toplevel(XDGSurface);
xdg_toplevel_set_title(XDGToplevel, "Wayland EGL example");
xdg_toplevel_add_listener(XDGToplevel, &xdg_toplevel_listener, NULL);
wl_surface_commit(surface);
CreateWindowWithEGLContext("Nya", 1280, 720);
cairo_device =
cairo_egl_device_create(ESContext.display, ESContext.context);
if (cairo_device_status(cairo_device) != CAIRO_STATUS_SUCCESS) {
fprintf(stderr, "failed to get cairo EGL device\n");
fprintf(stderr, "%s", cairo_status_to_string (cairo_device_status(cairo_device)));
exit(1);
}
cairo_surface = cairo_gl_surface_create_for_egl (cairo_device,
ESContext.surface,
1280, 720);
if (cairo_surface == NULL) {
fprintf(stderr, "Can't create Cairo surface\n");
exit(1);
} else {
LOG("Created Cairo surface !\n");
}
program_alive = true;
wl_display_dispatch_pending(ESContext.native_display);
cairo_t *cr = cairo_create(cairo_surface);
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
cairo_paint(cr);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_rectangle (cr, 50, 50, 50, 50);
cairo_fill (cr);
cairo_text_extents_t te;
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_select_font_face (cr, "Georgia",
CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, 25);
cairo_move_to (cr, 200,
200);
cairo_show_text (cr, "A very ugly Wayland EGL OpenGL example");
cairo_gl_surface_swapbuffers(cairo_surface);
cairo_destroy(cr);
while (program_alive) {
draw();
//RefreshWindow();
}
destroy_window();
wl_display_disconnect(ESContext.native_display);
LOG("Display disconnected !\n");
exit(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment