我正在尝试从wayland客户端使用cairo。参考指南来自 https://jan.newmarch.name/Wayland/Cairo/
不幸的是,我的系统没有cairo-gl,我也不想手动编译支持gl的Cairo。所以我决定直接把Cairo的曲面数据绑定到EGL曲面上。
below is the entire source code.
// utils.c
#include "utils.h"
double pixel_to_pango_size(double pixel)
return (pixel * 0.75) * PANGO_SCALE;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include "xdg-shell.h"
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include "utils.h"
struct wl_display *display = NULL;
struct wl_compositor *compositor = NULL;
struct wl_surface *surface;
struct zxdg_shell_v6 *xdg_shell = NULL;
struct zxdg_surface_v6 *xdg_surface;
struct zxdg_toplevel_v6 *xdg_toplevel;
struct wl_egl_window *egl_window;
struct wl_region *region;
EGLDisplay egl_display;
EGLConfig egl_conf;
EGLSurface egl_surface;
EGLContext egl_context;
cairo_surface_t *cairo_surface;
cairo_device_t *cairo_device;
const char* egl_error_string(int err)
switch (err) {
case EGL_SUCCESS:
return "EGL_SUCCESS";
case EGL_NOT_INITIALIZED:
return "EGL_NOT_INITIALIZED";
case EGL_BAD_ACCESS:
return "EGL_BAD_ACCESS";
case EGL_BAD_ALLOC:
return "EGL_BAD_ALLOC";
case EGL_BAD_ATTRIBUTE:
return "EGL_BAD_ATTRIBUTE";
case EGL_BAD_CONTEXT:
return "EGL_BAD_CONTEXT";
case EGL_BAD_CONFIG:
return "EGL_BAD_CONFIG";
case EGL_BAD_CURRENT_SURFACE:
return "EGL_BAD_CURRENT_SURFACE";
case EGL_BAD_DISPLAY:
return "EGL_BAD_DISPLAY";
case EGL_BAD_SURFACE:
return "EGL_BAD_SURFACE";
case EGL_BAD_MATCH:
return "EGL_BAD_MATCH";
case EGL_BAD_PARAMETER:
return "EGL_BAD_PARAMETER";
case EGL_BAD_NATIVE_PIXMAP:
return "EGL_BAD_NATIVE_PIXMAP";
case EGL_BAD_NATIVE_WINDOW:
return "EGL_BAD_NATIVE_WINDOW";
case EGL_CONTEXT_LOST:
return "EGL_CONTEXT_LOST";
default:
return "UNKNOWN ERROR!!";
//==============
// Xdg
//==============
void xdg_toplevel_configure_handler(void *data,
struct zxdg_toplevel_v6 *xdg_toplevel, int32_t width, int32_t height,
struct wl_array *states)
printf("Configure: %dx%d\n", width, height);
void xdg_toplevel_close_handler(void *data,
struct zxdg_toplevel_v6 *xdg_toplevel)
printf("Close.\n");
const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_configure_handler,
.close = xdg_toplevel_close_handler,
void xdg_surface_configure_handler(void *data,
struct zxdg_surface_v6 *xdg_surface, uint32_t serial)
fprintf(stderr, " = xdg_surface_configure_handler(). serial: %d\n",
serial);
zxdg_surface_v6_ack_configure(xdg_surface, serial);
const struct zxdg_surface_v6_listener xdg_surface_listener = {
.configure = xdg_surface_configure_handler,
void xdg_shell_ping_handler(void *data, struct zxdg_shell_v6 *xdg_shell,
uint32_t serial)
zxdg_shell_v6_pong(xdg_shell, serial);
printf("Pong!\n");
const struct zxdg_shell_v6_listener xdg_shell_listener = {
.ping = xdg_shell_ping_handler,
//==============
// Global
//==============
static void global_registry_handler(void *data, struct wl_registry *registry,
uint32_t id, const char *interface, uint32_t version)
if (strcmp(interface, "wl_compositor") == 0) {
fprintf(stderr, "Interface is <wl_compositor>.\n");
compositor = wl_registry_bind(
registry,
&wl_compositor_interface,
version
} else if (strcmp(interface, "zxdg_shell_v6") == 0) {
fprintf(stderr, "Interface is <zxdg_shell_v6>.\n");
xdg_shell = wl_registry_bind(
registry, id, &zxdg_shell_v6_interface, 1);
static void global_registry_remover(void *data, struct wl_registry *registry,
uint32_t id)
printf("Got a registry losing event for <%d>\n", id);
static const struct wl_registry_listener registry_listener = {
global_registry_handler,
global_registry_remover
//================
// Cairo / Pango
//================
static void draw_text(cairo_t *cr)
PangoLayout *layout;
PangoFontDescription *desc;
layout = pango_cairo_create_layout(cr);
pango_layout_set_text(layout, "おはよう!", -1);
desc = pango_font_description_from_string("serif");
pango_font_description_set_size(desc, pixel_to_pango_size(16));
pango_layout_set_font_description(layout, desc);
pango_font_description_free(desc);
cairo_save(cr);
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
pango_cairo_update_layout(cr, layout);
// pango_layout_get_size(layout, &width, &height);
cairo_move_to(cr, 0, 0);
pango_cairo_show_layout(cr, layout);
cairo_restore(cr);
g_object_unref(layout);
static void init_cairo()
// cairo_device = cairo_egl_device_create(egl_display, egl_context);
//==============
// EGL
//==============
static void create_opaque_region()
fprintf(stderr, " = Begin create_opaque_region()\n");
region = wl_compositor_create_region(compositor);
wl_region_add(
region,
wl_surface_set_opaque_region(surface, region);
fprintf(stderr, " = End create_opaque_region()\n");
static void create_window()
egl_window = wl_egl_window_create(surface, 480, 360);
if (egl_window == EGL_NO_SURFACE) {
exit(1);
egl_surface =
eglCreateWindowSurface(egl_display, egl_conf, egl_window, NULL);
if (egl_surface == NULL) {
fprintf(stderr, "Can't create EGL window surface.\n");
// Cairo
cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 480, 360);
cairo_t *cr = cairo_create(cairo_surface);
int err = cairo_status(cr);
if (err != CAIRO_STATUS_SUCCESS) {
fprintf(stderr, "Cairo error on create %s\n",
cairo_status_to_string(err));
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
cairo_paint(cr);
draw_text(cr);
unsigned char *data = cairo_image_surface_get_data(cairo_surface);
if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)) {
fprintf(stderr, "Made current.\n");
} else {
fprintf(stderr, "Made current failed!\n");
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 480, 360, 0, GL_RGBA, GL_UNSIGNED_BYTE,
data);
GLenum gl_error = glGetError();
fprintf(stderr, "glGetError() returns %d\n", gl_error);
err = eglBindTexImage(egl_display, egl_surface, EGL_BACK_BUFFER);
int egl_error = eglGetError();
fprintf(stderr, "eglGetError() returns %s.\n", egl_error_string(egl_error)); // It prints EGL_BAD_SURFACE
if (err != EGL_TRUE) {
fprintf(stderr, "Can't bind EGL tex image.\n");
exit(1); // Program exited here
if (eglSwapBuffers(egl_display, egl_surface)) {
fprintf(stderr, "Swapped buffers.\n");
} else {
fprintf(stderr, "Swapped buffers failed!\n");
static void init_egl()
EGLint major, minor, count ,n, size;
EGLConfig *configs;
EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE,
static const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE,
egl_display = eglGetDisplay((EGLNativeDisplayType)display);
if (egl_display == EGL_NO_DISPLAY) {
exit(1);
if (eglInitialize(egl_display, &major, &minor) != EGL_TRUE) {
exit(1);
printf("EGL major: %d, minor: %d.\n", major, minor);
eglGetConfigs(egl_display, NULL, 0, &count);
printf("EGL has %d configs.\n", count);
configs = calloc(count, sizeof *configs);
eglChooseConfig(egl_display, config_attribs, configs, count, &n);
for (int i = 0; i < n; ++i) {
eglGetConfigAttrib(
egl_display,
configs[i],
EGL_BUFFER_SIZE,
printf("Buffer size for config %d is %d.\n", i, size);
// Just choose the first one
egl_conf = configs[i];
break;
egl_context = eglCreateContext(
egl_display,
egl_conf,
EGL_NO_CONTEXT,
context_attribs
if (egl_context == NULL) {
fprintf(stderr, "Failed create EGL context.\n");
exit(1);
int main(int argc, char *argv[])
display = wl_display_connect(NULL);
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, ®istry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
surface = wl_compositor_create_surface(compositor);
zxdg_shell_v6_add_listener(xdg_shell, &xdg_shell_listener, NULL);
xdg_surface = zxdg_shell_v6_get_xdg_surface(xdg_shell, surface);
zxdg_surface_v6_add_listener(xdg_surface, &xdg_surface_listener, NULL);
xdg_toplevel = zxdg_surface_v6_get_toplevel(xdg_surface);
zxdg_toplevel_v6_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
wl_surface_commit(surface);
// Wait for the surface to be configured.
wl_display_roundtrip(display);
create_opaque_region();
init_egl();
create_window();
wl_surface_commit(surface);
while (wl_display_dispatch(display) != -1) {
wl_display_disconnect(display);
return 0;
该问题发生时调用eglBindTexImage()
。
我从EGL api docs中找到了一些提示。