[wayland_xdg_egl]
parent
c72e5e4769
commit
4f95b9e72c
|
|
@ -1 +1,3 @@
|
|||
build/*
|
||||
build/*
|
||||
wayland/*
|
||||
scrap.*
|
||||
|
|
@ -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 <wayland-client.h>
|
||||
#include "wayland/wayland-client-protocol.h"
|
||||
#include "wayland/xdg-shell-client-protocol.h"
|
||||
|
||||
#include <wayland-egl.h>
|
||||
/*~ NOTE: wayland-egl.h *before* EGL/ */
|
||||
#include <EGL/egl.h>
|
||||
|
|
@ -73,6 +82,9 @@ exit 0
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
Loading…
Reference in New Issue