Skip to content

Instantly share code, notes, and snippets.

@gulafaran
Created March 27, 2025 16:23
Show Gist options
  • Save gulafaran/31c6a7f2bcffd13f0e2573b25e54da50 to your computer and use it in GitHub Desktop.
Save gulafaran/31c6a7f2bcffd13f0e2573b25e54da50 to your computer and use it in GitHub Desktop.
#include <wayland-client.h>
#include <wayland-egl.h>
#include <EGL/egl.h>
#include <EGL/eglplatform.h>
#include <GLES2/gl2.h>
#include "xdg-shell-client-protocol.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
const int SCREEN_WIDTH = 512;
const int SCREEN_HEIGHT = 512;
struct wl_display* display;
struct wl_registry* registry;
struct wl_compositor* compositor;
struct wl_surface* surface;
struct xdg_wm_base* wm_base;
struct xdg_surface* xdg_surface;
struct xdg_toplevel* xdg_toplevel;
struct wl_egl_window* egl_window;
EGLDisplay egl_display;
EGLContext egl_context;
EGLSurface egl_surface;
GLuint shader_program, vbo, vao, texture;
const char* vertex_shader_src = "#version 100\n"
"attribute vec2 a_position;\n"
"attribute vec2 a_texcoord;\n"
"varying vec2 v_texcoord;\n"
"void main() {\n"
" gl_Position = vec4(a_position, 0.0, 1.0);\n"
" v_texcoord = a_texcoord;\n"
"}";
const char* fragment_shader_src = "#version 100\n"
"precision mediump float;\n"
"varying vec2 v_texcoord;\n"
"uniform sampler2D u_texture;\n"
"void main() {\n"
" gl_FragColor = texture2D(u_texture, v_texcoord);\n"
"}";
void registry_handler(void* data, struct wl_registry* registry, uint32_t id, const char* interface, uint32_t version) {
if (strcmp(interface, "wl_compositor") == 0) {
compositor = wl_registry_bind(registry, id, &wl_compositor_interface, version < 4 ? version : 4);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, version < 3 ? version : 3);
}
}
void registry_remover(void* data, struct wl_registry* registry, uint32_t id) {}
const struct wl_registry_listener registry_listener = {
registry_handler,
registry_remover
};
void init_wayland() {
display = wl_display_connect(NULL);
if (!display) {
fprintf(stderr, "Failed to connect to Wayland display\n");
exit(1);
}
registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_roundtrip(display);
if (!compositor || !wm_base) {
fprintf(stderr, "Failed to get Wayland compositor or XDG WM Base\n");
exit(1);
}
surface = wl_compositor_create_surface(compositor);
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, "OpenGL Wayland Window");
wl_surface_commit(surface);
}
void init_egl() {
egl_display = eglGetDisplay((EGLNativeDisplayType)display);
eglInitialize(egl_display, NULL, NULL);
EGLConfig config;
EGLint num_config;
EGLint attributes[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
eglChooseConfig(egl_display, attributes, &config, 1, &num_config);
egl_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, NULL);
egl_window = wl_egl_window_create(surface, SCREEN_WIDTH, SCREEN_HEIGHT);
egl_surface = eglCreateWindowSurface(egl_display, config, (EGLNativeWindowType)egl_window, NULL);
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
}
void check_shader_compile(GLuint shader) {
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char info_log[512];
glGetShaderInfoLog(shader, 512, NULL, info_log);
fprintf(stderr, "Shader compilation error: %s\n", info_log);
exit(1);
}
}
void init_gl() {
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_src, NULL);
glCompileShader(vertex_shader);
check_shader_compile(vertex_shader);
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_src, NULL);
glCompileShader(fragment_shader);
check_shader_compile(fragment_shader);
shader_program = glCreateProgram();
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);
glLinkProgram(shader_program);
glUseProgram(shader_program);
// Vertex data (X, Y, U, V)
float vertices[] = {
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLint posAttrib = glGetAttribLocation(shader_program, "a_position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
GLint texAttrib = glGetAttribLocation(shader_program, "a_texcoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
unsigned char pixels[] = { 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 0, 255 };
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
int main() {
init_wayland();
init_egl();
init_gl();
bool running = true;
while (running) {
wl_display_dispatch_pending(display);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader_program);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(0); // Position
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1); // Texcoord
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glBindTexture(GL_TEXTURE_2D, texture);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
eglSwapBuffers(egl_display, egl_surface);
}
wl_display_disconnect(display);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment