diff --git a/.gitignore b/.gitignore index 07ed706..27a3716 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -build/* \ No newline at end of file +build/* +wayland/* +scrap.* \ No newline at end of file diff --git a/wayland_egl.c b/wayland_xdg_egl.c similarity index 55% rename from wayland_egl.c rename to wayland_xdg_egl.c index 638860a..9a50e48 100755 --- a/wayland_egl.c +++ b/wayland_xdg_egl.c @@ -1,15 +1,22 @@ #if 0 +if [ "$1" == "meta" ]; then +mkdir -p wayland +wayland-scanner client-header /usr/share/wayland/wayland.xml wayland/wayland-client-protocol.h +wayland-scanner private-code /usr/share/wayland/wayland.xml wayland/wayland-client-protocol.c +wayland-scanner client-header /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml wayland/xdg-shell-client-protocol.h +wayland-scanner private-code /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml wayland/xdg-shell-client-protocol.c +else mkdir -p build -clang -o build/demo -g wayland_egl.c -lwayland-client -lwayland-egl -lEGL +clang -o build/demo -g wayland_xdg_egl.c -lwayland-client -lwayland-egl -lEGL +fi exit 0 #endif /* ** Reading From: -** (1) Wayland Docs https://wayland.freedesktop.org/docs/html/ -** (2) wayland/protocol.xml \ - ** https://chromium.googlesource.com/external/wayland/wayland/+/refs/heads/master/protocol/wayland.xml -** (3) EGL spec https://registry.khronos.org/EGL/sdk/docs/man/ +** (1) Wayland Docs https://wayland.freedesktop.org/docs/html/ +** (2) XDG shell Docs https://wayland.app/protocols/xdg-shell#xdg_wm_base +** (egl) EGL spec https://registry.khronos.org/EGL/sdk/docs/man/ ** ** (?) I cannot find any documentation for wl_egl_ except for headers ** and example usage code. :( @@ -41,7 +48,7 @@ exit 0 **~ function `O_R(O *obj, ...)`. **~ ! Sometimes the arguments for `R(...)` include an argument that **~ doesn't appear to have a documented type (at least in (1)). -**~ We can also refer to the xml (2) and see such arguments have +**~ We can also refer to the xml and see such arguments have **~ the type "new_id". These seem to actually be a return value **~ that does not appear in the generated signature as an id, **~ but instead is translated into a typed return pointer. @@ -63,7 +70,9 @@ exit 0 **~ */ -#include +#include "wayland/wayland-client-protocol.h" +#include "wayland/xdg-shell-client-protocol.h" + #include /*~ NOTE: wayland-egl.h *before* EGL/ */ #include @@ -73,6 +82,9 @@ exit 0 #include #include +#include "wayland/wayland-client-protocol.c" +#include "wayland/xdg-shell-client-protocol.c" + // X(N:name,R:return,P:params) #define GL_FUNCS_XLIST(X)\ X(glDrawBuffer, void, (GLenum buf)) \ @@ -85,51 +97,152 @@ GL_FUNCS_XLIST(X) #undef X typedef struct Ctx{ - struct wl_display *display; - struct wl_registry *registry; - struct wl_compositor *compositor; - struct wl_surface *surface; - struct wl_region *region; - struct wl_egl_window *egl_window; + /* globals */ + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct wl_compositor *wl_compositor; + struct xdg_wm_base *xdg_wm_base; + /* window */ + struct wl_surface *wl_surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct wl_region *wl_region; + struct wl_egl_window *wl_egl_window; EGLDisplay egl_display; EGLContext egl_context; EGLSurface egl_surface; + int close_signal; } Ctx; -/* (1) Appendix A: wl_registry::global */ +static Ctx ctx = {0}; + + +/* (2) xdg_wm_base::ping */ static void -wlevent_registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version){ - printf("wlevent_registry_global: interface=%s\n", interface); - - Ctx *ctx = (Ctx*)data; - +wlevent__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 = { + wlevent__xdg_wm_base_ping, +}; + + +/* (2) xdg_surface::configure +** " marks the end of a configure sequence ... + ** Clients should arrange their surface for the new states, and then +** send an ack_configure request with the serial sent in this +** configure event at some point before committing the new surface. " +*/ +static void +wlevent__xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, + uint32_t serial){ + xdg_surface_ack_configure(xdg_surface, serial); + wl_surface_commit(ctx.wl_surface); +} + +const struct xdg_surface_listener xdg_surface_listener = { + wlevent__xdg_surface_configure, +}; + + +/* (2) xdg_toplevel::configure +** " This configure event asks the client to resize its toplevel surface +** or to change its state. The configured state should not be applied + ** immediately. See xdg_surface.configure for details. " +** @see:(xdg_surface::configure) +*/ +static void +wlevent__xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, + struct wl_array *states){ + if (width > 0 && height > 0){ + /* (?) */ + wl_egl_window_resize(ctx.wl_egl_window, width, height, 0, 0); + } +} + +/* (2) xdg_toplevel::close +** " sent by the compositor when the user wants the surface to be closed. " +*/ +static void +wlevent__xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel){ + ctx.close_signal = 1; +} + +/* (2) xdg_toplevel::configure_bounds +** " sent [...] to communicate the bounds a window geometry size is +** recommended to constrain to " +**~ NOTE: basically "tells you the monitor size". +** @see:xdg_surface.configure +*/ +static void +wlevent__xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height){ +} + +/* (2) xdg_toplevel::wm_capabilities +** " advertises the capabilities supported by the compositor " +**~ NOTE: basically "tells you the monitor size". +** @see:xdg_surface.configure +*/ +static void +wlevent__xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities){ +} + +const struct xdg_toplevel_listener xdg_toplevel_listener = { + wlevent__xdg_toplevel_configure, + wlevent__xdg_toplevel_close, + wlevent__xdg_toplevel_configure_bounds, + wlevent__xdg_toplevel_wm_capabilities, +}; + + +/* (1) Appendix A: wl_registry::global +** " The event notifies the client that a global object with the given +** name is now available " +*/ +static void +wlevent__wl_registry_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, + uint32_t version){ /* (1) Appendix A: wl_registry::bind ** " Binds a new, client-created object to the server " */ + if (strcmp(interface, "wl_compositor") == 0){ - ctx->compositor = (struct wl_compositor*) + ctx.wl_compositor = (struct wl_compositor*) wl_registry_bind(registry, name, &wl_compositor_interface, 1); } + else if (strcmp(interface, "xdg_wm_base") == 0){ + ctx.xdg_wm_base = (struct xdg_wm_base*) + wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + + /* [1] */ + xdg_wm_base_add_listener(ctx.xdg_wm_base, &xdg_wm_base_listener, 0); + } } /* (1) Appendix A: wl_registry::global_remove */ static void -wlevent_registry_global_remove(void *data, struct wl_registry *registry, uint32_t name){} +wlevent__wl_registry_global_remove(void *data, struct wl_registry *registry, + uint32_t name){} -const struct wl_registry_listener registry_listener = { - wlevent_registry_global, - wlevent_registry_global_remove, +const struct wl_registry_listener wl_registry_listener = { + wlevent__wl_registry_global, + wlevent__wl_registry_global_remove, }; + int main(){ - Ctx ctx = {0}; - /* (1) Appendix B: wl_display_connect ** " Connect to a Wayland display. " */ - ctx.display = wl_display_connect(0); - if (ctx.display == 0){ + ctx.wl_display = wl_display_connect(0); + if (ctx.wl_display == 0){ printf("wl_display_connect failed\n"); } @@ -137,44 +250,44 @@ int main(){ ** " creates a registry object that allows the client to list ** and bind the global objects available from the compositor " */ - if (ctx.display != 0){ - ctx.registry = wl_display_get_registry(ctx.display); - if (ctx.registry == 0){ + if (ctx.wl_display != 0){ + ctx.wl_registry = wl_display_get_registry(ctx.wl_display); + if (ctx.wl_registry == 0){ printf("wl_display_get_registry failed\n"); } } - if (ctx.registry != 0){ + if (ctx.wl_registry != 0){ /* [1] */ - wl_registry_add_listener(ctx.registry, ®istry_listener, &ctx); + wl_registry_add_listener(ctx.wl_registry, &wl_registry_listener, 0); /* (1) Appendix B: wl_display::dispatch ** " Dispatch events on the default event queue. ** ... this function blocks until there are events to be read ... " */ - wl_display_dispatch(ctx.display); + wl_display_dispatch(ctx.wl_display); /* (1) Appendix B: wl_display_roundtrip ** " Block until all pending request are processed by the server " */ - wl_display_roundtrip(ctx.display); + wl_display_roundtrip(ctx.wl_display); - if (ctx.compositor == 0){ + if (ctx.wl_compositor == 0){ printf("failed to get wl_compositor\n"); } } int opengl_load_success = 0; - if (ctx.display != 0 && ctx.compositor != 0){ - /* (3) eglGetDisplay + if (ctx.wl_display != 0 && ctx.wl_compositor != 0){ + /* (egl) eglGetDisplay ** " obtains the EGL display connection for the native display " */ - ctx.egl_display = eglGetDisplay(ctx.display); + ctx.egl_display = eglGetDisplay(ctx.wl_display); if (ctx.egl_display == 0){ printf("eglGetDisplay failed\n"); } - /* (3) eglInitialize + /* (egl) eglInitialize ** " initializes* the EGL display connection obtained with eglGetDisplay " */ int egl_init_success = 0; @@ -195,7 +308,7 @@ int main(){ } } - /* (3) eglBindAPI + /* (egl) eglBindAPI ** " defines the current rendering API for EGL in the thread it is ** called from " */ @@ -208,7 +321,7 @@ int main(){ **~ Create OpenGL context & link OpenGL procedures */ - /* (3) eglCreateContext + /* (egl) eglCreateContext ** " creates an EGL rendering context for the current rendering API ** (as set with eglBindAPI) and returns a handle to the context " */ @@ -225,7 +338,7 @@ int main(){ } } - /* (3) eglMakeCurrent + /* (egl) eglMakeCurrent ** " binds context to the current rendering thread " */ EGLBoolean make_current_success = 0; @@ -236,7 +349,7 @@ int main(){ } } - /* (3) eglGetProcAddress + /* (egl) eglGetProcAddress ** " returns the address of the client API or EGL function " */ if (make_current_success){ @@ -254,42 +367,77 @@ int main(){ /* (1) Appendix A: wl_compositor::create_surface ** " create new surface " */ - ctx.surface = wl_compositor_create_surface(ctx.compositor); - if (ctx.surface == 0){ + ctx.wl_surface = wl_compositor_create_surface(ctx.wl_compositor); + if (ctx.wl_surface == 0){ printf("wl_compositor_create_surface failed\n"); } + } + + if (ctx.wl_surface != 0){ + /* (2) xdg_wm_base::get_xdg_surface + ** " creates an xdg_surface for the given surface " + */ + ctx.xdg_surface = xdg_wm_base_get_xdg_surface(ctx.xdg_wm_base, ctx.wl_surface); + if (ctx.xdg_surface == 0){ + printf("xdg_wm_base_get_xdg_surface failed\n"); + } + } + + if (ctx.xdg_surface != 0){ + /* [1] */ + xdg_surface_add_listener(ctx.xdg_surface, &xdg_surface_listener, 0); + /* (2) xdg_surface::get_toplevel + ** " creates an xdg_toplevel object for the given xdg_surface and gives + ** the associated wl_surface the xdg_toplevel role. " + */ + ctx.xdg_toplevel = xdg_surface_get_toplevel(ctx.xdg_surface); + if (ctx.xdg_toplevel == 0){ + printf("xdg_surface_get_toplevel\n"); + } + } + + if (ctx.xdg_toplevel != 0){ + /* (2) xdg_toplevel::set_title " + ** " set a short title for the surface " */ + xdg_toplevel_set_title(ctx.xdg_toplevel, "Wayland EGL example"); + + /* [1] */ + xdg_toplevel_add_listener(ctx.xdg_toplevel, &xdg_toplevel_listener, NULL); + } + + if (ctx.xdg_surface != 0){ /* (1) Appendix A: wl_compositor::create_region ** " create new region " */ - ctx.region = wl_compositor_create_region(ctx.compositor); - if (ctx.region == 0){ + ctx.wl_region = wl_compositor_create_region(ctx.wl_compositor); + if (ctx.wl_region == 0){ printf("wl_compositor_create_region failed\n"); } - if (ctx.surface != 0 && ctx.region != 0){ + if (ctx.wl_surface != 0 && ctx.wl_region != 0){ /* (1) Appendix A: wl_region::add ** " add rectangle to region " */ - wl_region_add(ctx.region, 0, 0, 640, 480); + wl_region_add(ctx.wl_region, 0, 0, 640, 480); /* (1) Appendix A: wl_region::set_opaque_region ** " The opaque region is an optimization hint for the compositor ** that lets it optimize out redrawing of content behind opaque ** regions. " */ - wl_surface_set_opaque_region(ctx.surface, ctx.region); + wl_surface_set_opaque_region(ctx.wl_surface, ctx.wl_region); /* (?) */ - ctx.egl_window = wl_egl_window_create(ctx.surface, 640, 480); - if (ctx.egl_window == EGL_NO_SURFACE){ + ctx.wl_egl_window = wl_egl_window_create(ctx.wl_surface, 640, 480); + if (ctx.wl_egl_window == EGL_NO_SURFACE){ printf("wl_egl_window_create failed\n"); } } } - if (ctx.egl_window != EGL_NO_SURFACE){ - /* (3) eglChooseConfig + if (ctx.wl_egl_window != EGL_NO_SURFACE){ + /* (egl) eglChooseConfig ** " returns in configs a list of all EGL frame buffer configurations ** that match the attributes specified in attrib_list " */ @@ -315,7 +463,7 @@ int main(){ } } - /* (3) eglCreateWindowSurface + /* (egl) eglCreateWindowSurface ** " creates an on-screen EGL window surface and returns a handle to it " */ { @@ -324,7 +472,7 @@ int main(){ EGL_NONE, }; for (EGLint i = 0; i < config_count; i += 1){ - ctx.egl_surface = eglCreateWindowSurface(ctx.egl_display, configs[i], ctx.egl_window, attributes); + ctx.egl_surface = eglCreateWindowSurface(ctx.egl_display, configs[i], ctx.wl_egl_window, attributes); if (ctx.egl_surface != EGL_NO_SURFACE){ break; } @@ -335,13 +483,13 @@ int main(){ } } - /* (3) eglMakeCurrent */ + /* (egl) eglMakeCurrent */ EGLBoolean make_current_success2 = 0; if (ctx.egl_surface != EGL_NO_SURFACE){ make_current_success2 = eglMakeCurrent(ctx.egl_display, ctx.egl_surface, ctx.egl_surface, ctx.egl_context); } - /* (3) eglSwapInterval + /* (egl) eglSwapInterval ** " Specifies the minimum number of video frames that are displayed before ** a buffer swap will occur " */ @@ -357,7 +505,11 @@ int main(){ ** " This function dispatches events on the main event queue. ** ... it doesn't block." */ - wl_display_dispatch_pending(ctx.display); + wl_display_dispatch_pending(ctx.wl_display); + + if (ctx.close_signal){ + exit_loop = 1; + } /*~ NOTE: render */ { @@ -367,7 +519,7 @@ int main(){ glClear(GL_COLOR_BUFFER_BIT); } - /* (2) eglSwapBuffers + /* (egl) eglSwapBuffers ** " back-buffered window surface, then the color buffer is copied ** (posted) to the native window associated with that surface " */ @@ -380,8 +532,8 @@ int main(){ /* (1) #Client-classwl__display_1a9150a7e3213a58b469a6966e60a9f108 ** " Close the connection to display " */ - if (ctx.display != 0){ - wl_display_disconnect(ctx.display); + if (ctx.wl_display != 0){ + wl_display_disconnect(ctx.wl_display); } return(0);