diff --git a/build.sh b/build.sh index 5391f3b..ba08184 100755 --- a/build.sh +++ b/build.sh @@ -1,2 +1,2 @@ #!/bin/bash -./digesting_libdecor.c \ No newline at end of file +./wayland_egl.c \ No newline at end of file diff --git a/digesting_libdecor.c b/digesting_libdecor.c index c7e1dcf..a7fea02 100755 --- a/digesting_libdecor.c +++ b/digesting_libdecor.c @@ -244,7 +244,7 @@ static void xdg_surface_configure(void *user_data, struct xdg_surface *xdg_surface, uint32_t serial){ ctx.has_cached_config = 1; - ctx.cached_config.serial = serial; + ctx.cached_config_serial = serial; } const struct xdg_surface_listener xdg_surface_listener = { xdg_surface_configure, }; @@ -280,10 +280,10 @@ xdg_toplevel_configure(void *user_data, struct xdg_toplevel *xdg_toplevel, } ctx.has_cached_config = 1; - ctx.cached_config.initialized = 1; - ctx.cached_config.window_width = width; - ctx.cached_config.window_height = height; - ctx.cached_config.window_state = window_state; + ctx.cached_config_initialized = 1; + ctx.cached_config_window_width = width; + ctx.cached_config_window_height = height; + ctx.cached_config_window_state = window_state; } static void @@ -293,30 +293,14 @@ xdg_toplevel_close(void *user_data, struct xdg_toplevel *xdg_toplevel){ #ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION static void -xdg_toplevel_configure_bounds(void *user_data, struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h){ - // TODO(allen): -} +xdg_toplevel_configure_bounds(void *user_data, struct xdg_toplevel *xdg_toplevel, + int32_t w, int32_t h){} #endif #ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION static void -xdg_toplevel_wm_capabilities(void *user_data, - struct xdg_toplevel *xdg_toplevel, - struct wl_array *capabilities){ - enum xdg_toplevel_wm_capabilities *wm_cap; - - ctx.wm_capabilities = 0; - - wl_array_for_each(wm_cap, capabilities) { - switch (*wm_cap) { - case XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU: ctx.wm_capabilities |= LIBDECOR_WM_CAPABILITIES_WINDOW_MENU; break; - case XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE: ctx.wm_capabilities |= LIBDECOR_WM_CAPABILITIES_MAXIMIZE; break; - case XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN: ctx.wm_capabilities |= LIBDECOR_WM_CAPABILITIES_FULLSCREEN; break; - case XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE: ctx.wm_capabilities |= LIBDECOR_WM_CAPABILITIES_MINIMIZE; break; - default: break; - } - } -} +xdg_toplevel_wm_capabilities(void *user_data, struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities){} #endif const struct xdg_toplevel_listener xdg_toplevel_listener = { @@ -425,11 +409,8 @@ border_component_reallocate(struct border_component *component, int w, int h){ int main(){ /* get desktop settings */ + desktop_settings_get_cursor_settings(&ctx.cursor_theme_name, &ctx.cursor_size); ctx.color_scheme = desktop_settings_get_color_scheme(); - if (desktop_settings_get_cursor_settings(&ctx.cursor_theme_name, &ctx.cursor_size)){ - ctx.cursor_theme_name = 0; - ctx.cursor_size = 24; - } /* setup GTK context */ int gtk_init_success = 0; @@ -476,7 +457,6 @@ int main(){ wl_display_flush(ctx.wl_display); wl_display_dispatch(ctx.wl_display); - wl_display_roundtrip(ctx.wl_display); if (ctx.wl_compositor == 0){ printf("failed to get wl_compositor\n"); @@ -591,10 +571,6 @@ int main(){ if (ctx.wl_surface != 0){ ctx.visible = true; - ctx.wm_capabilities = (LIBDECOR_WM_CAPABILITIES_WINDOW_MENU | - LIBDECOR_WM_CAPABILITIES_MAXIMIZE | - LIBDECOR_WM_CAPABILITIES_FULLSCREEN | - LIBDECOR_WM_CAPABILITIES_MINIMIZE); ctx.frame_capabilities = (LIBDECOR_ACTION_MOVE | LIBDECOR_ACTION_RESIZE | LIBDECOR_ACTION_MINIMIZE | @@ -689,8 +665,6 @@ int main(){ exit_loop = 1; } for (;!exit_loop;){ - for (;g_main_context_iteration(0, 0);){} - { struct pollfd fds[1] = {0}; @@ -727,6 +701,8 @@ int main(){ } } + for (;g_main_context_iteration(0, 0);){} + #if 1 if (ctx.pointer_leave){ ctx.pointer_leave = 0; @@ -775,10 +751,7 @@ int main(){ if (ctx.titlebar_gesture.state == TITLEBAR_GESTURE_STATE_BUTTON_PRESSED){ if (ctx.titlebar_gesture.first_pressed_button == BTN_LEFT){ - if (ctx.seat->pointer_x != ctx.titlebar_gesture.pressed_x || - ctx.seat->pointer_y != ctx.titlebar_gesture.pressed_y){ - xdg_toplevel_move(ctx.xdg_toplevel, ctx.seat->wl_seat, ctx.titlebar_gesture.serial); - } + xdg_toplevel_move(ctx.xdg_toplevel, ctx.seat->wl_seat, ctx.titlebar_gesture.serial); } } } @@ -986,8 +959,8 @@ int main(){ } if (ctx.has_cached_config){ - if (ctx.cached_config.initialized){ - ctx.frame_window_state = ctx.cached_config.window_state; + if (ctx.cached_config_initialized){ + ctx.frame_window_state = ctx.cached_config_window_state; } } @@ -998,7 +971,6 @@ int main(){ if (csd){ if (csd_show_title_from_state(ctx.frame_window_state)){ gtk_widget_get_preferred_height(ctx.header, 0, &csd_size.y[0]); - gtk_header_bar_set_title(GTK_HEADER_BAR(ctx.header), ""); gtk_widget_get_preferred_width(ctx.header, 0, &csd_size_bounds.x[0]); gtk_header_bar_set_title(GTK_HEADER_BAR(ctx.header), ctx.title); @@ -1009,11 +981,11 @@ int main(){ int csd_h = csd_size.y[0] + csd_size.y[1]; if (ctx.has_cached_config){ - if (ctx.cached_config.initialized && - ctx.cached_config.window_width != 0 && - ctx.cached_config.window_height != 0){ - ctx.w = ctx.cached_config.window_width - csd_w; - ctx.h = ctx.cached_config.window_height - csd_h; + if (ctx.cached_config_initialized && + ctx.cached_config_window_width != 0 && + ctx.cached_config_window_height != 0){ + ctx.w = ctx.cached_config_window_width - csd_w; + ctx.h = ctx.cached_config_window_height - csd_h; } } @@ -1244,9 +1216,13 @@ int main(){ } if (ctx.has_cached_config){ - xdg_surface_ack_configure(ctx.xdg_surface, ctx.cached_config.serial); + xdg_surface_ack_configure(ctx.xdg_surface, ctx.cached_config_serial); ctx.has_cached_config = 0; - memset(&ctx.cached_config, 0, sizeof ctx.cached_config); + ctx.cached_config_initialized = 0; + ctx.cached_config_window_state = 0; + ctx.cached_config_window_width = 0; + ctx.cached_config_window_height = 0; + ctx.cached_config_serial = 0; } wl_surface_commit(ctx.wl_surface); @@ -1497,6 +1473,7 @@ render_shadow(cairo_t *cr, cairo_surface_t *surface, } //#include "os-compatibility.c" + #ifndef HAVE_MKOSTEMP static int set_cloexec_or_close(int fd){ @@ -1521,26 +1498,6 @@ set_cloexec_or_close(int fd){ } #endif -static int -create_tmpfile_cloexec(char *tmpname){ - int fd; - -#ifdef HAVE_MKOSTEMP - fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0){ - unlink(tmpname); - } -#else - fd = mkstemp(tmpname); - fd = set_cloexec_or_close(fd); - if (fd >= 0) { - unlink(tmpname); - } -#endif - - return(fd); -} - static int os_resize_anonymous_file(int fd, off_t size){ #ifdef HAVE_POSIX_FALLOCATE @@ -1599,7 +1556,20 @@ int os_create_anonymous_file(off_t size){ if (name != 0){ strcpy(name, path); strcat(name, key); - fd = create_tmpfile_cloexec(name); + +#ifdef HAVE_MKOSTEMP + fd = mkostemp(name, O_CLOEXEC); + if (fd >= 0){ + unlink(name); + } +#else + fd = mkstemp(name); + fd = set_cloexec_or_close(fd); + if (fd >= 0) { + unlink(name); + } +#endif + free(name); } } @@ -1630,14 +1600,8 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface, enum header_element button_type){ GtkAllocation allocation; - gchar *icon_name; - int scale; - GtkWidget *icon_widget; - GtkAllocation allocation_icon; - GtkIconInfo* icon_info; - double sx; - double sy; + GtkIconInfo* icon_info; gint icon_width, icon_height; @@ -1679,7 +1643,10 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface, gtk_style_context_restore(button_style); /* symbol */ - switch (button_type) { + gchar *icon_name = 0; + switch (button_type){ + default: break; + case HEADER_MIN: { icon_name = "window-minimize-symbolic"; }break; @@ -1693,19 +1660,16 @@ draw_header_button(cairo_t *cr, cairo_surface_t *surface, case HEADER_CLOSE: { icon_name = "window-close-symbolic"; }break; - - default: { - icon_name = NULL; - }break; } /* get scale */ + double sx; + double sy; cairo_surface_get_device_scale(surface, &sx, &sy); - scale = (sx+sy)/2.0; + int scale = (sx + sy)/2.0; /* get original icon dimensions */ - icon_widget = gtk_bin_get_child(GTK_BIN(button)); - gtk_widget_get_allocation(icon_widget, &allocation_icon); + GtkWidget *icon_widget = gtk_bin_get_child(GTK_BIN(button)); /* icon info */ if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &icon_width, &icon_height)) { @@ -1787,16 +1751,18 @@ edge_from_pos(int x, int y){ //#include "desktop-settings.c" -static int +static void get_cursor_settings_from_env(char **theme, int *size){ char *env_xtheme = getenv("XCURSOR_THEME"); char *env_xsize = getenv("XCURSOR_SIZE"); - int got_theme = (env_xtheme != 0 && env_xsize != 0); - if (got_theme){ + if (env_xtheme != 0 && env_xsize != 0){ *theme = strdup(env_xtheme); *size = atoi(env_xsize); } - return(got_theme); + else{ + *theme = 0; + *size = 24; + } } #ifdef HAS_DBUS @@ -1851,7 +1817,7 @@ parse_type(DBusMessage *const reply, const int type, void *value){ return(result); } -bool +void desktop_settings_get_cursor_settings(char **theme, int *size){ static const char name[] = "org.gnome.desktop.interface"; static const char key_theme[] = "cursor-theme"; @@ -1885,10 +1851,8 @@ desktop_settings_get_cursor_settings(char **theme, int *size){ } if (!got_theme){ - got_theme = get_cursor_settings_from_env(theme, size); + get_cursor_settings_from_env(theme, size); } - - return(got_theme); } enum libdecor_color_scheme @@ -1915,9 +1879,9 @@ desktop_settings_get_color_scheme(){ #else -bool +void desktop_settings_get_cursor_settings(char **theme, int *size){ - return(get_cursor_settings_from_env(theme, size)); + get_cursor_settings_from_env(theme, size); } uint32_t diff --git a/digesting_libdecor.h b/digesting_libdecor.h index b09dee5..894bd25 100644 --- a/digesting_libdecor.h +++ b/digesting_libdecor.h @@ -122,19 +122,12 @@ enum libdecor_capabilities { LIBDECOR_ACTION_CLOSE = 1 << 4, }; -enum libdecor_wm_capabilities { - LIBDECOR_WM_CAPABILITIES_WINDOW_MENU = 1 << 0, - LIBDECOR_WM_CAPABILITIES_MAXIMIZE = 1 << 1, - LIBDECOR_WM_CAPABILITIES_FULLSCREEN = 1 << 2, - LIBDECOR_WM_CAPABILITIES_MINIMIZE = 1 << 3 -}; - // libdecor-plugin.h enum header_element { HEADER_NONE, - HEADER_FULL, /* entire header bar */ - HEADER_TITLE, /* label */ + HEADER_FULL, + HEADER_TITLE, HEADER_MIN, HEADER_MAX, HEADER_CLOSE, @@ -179,14 +172,6 @@ struct libdecor_state { int content_height; }; -struct libdecor_configuration{ - bool initialized; - uint32_t serial; - enum libdecor_window_state window_state; - int window_width; - int window_height; -}; - // #include "desktop-settings.h" enum libdecor_color_scheme { @@ -240,7 +225,7 @@ void render_shadow(cairo_t *cr, cairo_surface_t *surface, int x, int y, int width, int height, int margin, int top_margin); // #include "desktop-settings.h" -bool desktop_settings_get_cursor_settings(char **theme, int *size); +void desktop_settings_get_cursor_settings(char **theme, int *size); enum libdecor_color_scheme desktop_settings_get_color_scheme(); // #include "os-compatibility.h" @@ -287,8 +272,12 @@ typedef struct Ctx{ EGLContext egl_context; EGLSurface egl_surface; - int has_cached_config; - struct libdecor_configuration cached_config; + int has_cached_config; + int cached_config_initialized; + enum libdecor_window_state cached_config_window_state; + int cached_config_window_width; + int cached_config_window_height; + uint32_t cached_config_serial; /* uncategorized experiments */ struct seat *seat; @@ -314,7 +303,6 @@ typedef struct Ctx{ enum zxdg_toplevel_decoration_v1_mode decoration_mode; enum libdecor_capabilities frame_capabilities; - enum libdecor_wm_capabilities wm_capabilities; bool visible; diff --git a/wayland_egl.c b/wayland_egl.c new file mode 100755 index 0000000..d33957c --- /dev/null +++ b/wayland_egl.c @@ -0,0 +1,799 @@ +#if 0 +libdecor_path="/home/mr4th/mr4th/libdecor" +root_path="$PWD" +dbus_flags="$(pkg-config --cflags --libs dbus-1)" +my_flags="-Iwayland -I$libdecor_path/src -I$libdecor_path/src/plugins -I$libdecor_path/build" +my_flags+=" -lwayland-client -lwayland-cursor -lwayland-egl -lEGL -lm" +mkdir -p build +clang -o build/demo -g $root_path/wayland_egl.c $dbus_flags $my_flags +exit 0 +#endif + + +#define _GNU_SOURCE + +#include +#include +#include + +#include +/*~ NOTE: wayland-egl.h *before* EGL/ */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "xdg-shell-client-protocol.h" +#include "xdg-decoration-client-protocol.h" + +#include "xdg-shell-client-protocol.c" +#include "xdg-decoration-client-protocol.c" + +#include "wayland_egl.h" + +// X(N:name,R:return,P:params) +#define GL_FUNCS_XLIST(X)\ +X(glDrawBuffer, void, (GLenum buf)) \ +X(glViewport, void, (GLint x, GLint y, GLsizei w, GLsizei h)) \ +X(glClear, void, (GLbitfield mask)) \ +X(glClearColor, void, (GLfloat r, GLfloat g, GLfloat b, GLfloat a)) + +#define X(N,R,P) R (*N)P = 0; +GL_FUNCS_XLIST(X) +#undef X + +static Ctx ctx = {0}; + +static void +shm_format(void *udata, struct wl_shm *wl_shm, uint32_t format){ +} + +const struct wl_shm_listener shm_listener = { + shm_format +}; + +static void +xdg_wm_base_ping(void *udata, 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 = { + xdg_wm_base_ping +}; + +static void +pointer_enter(void *udata, struct wl_pointer *wl_pointer, uint32_t serial, + struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y){ +} + +static void +pointer_leave(void *udata, struct wl_pointer *wl_pointer, uint32_t serial, + struct wl_surface *surface){ +} + +static void +pointer_motion(void *udata, struct wl_pointer *wl_pointer, uint32_t time, + wl_fixed_t surface_x, wl_fixed_t surface_y){ +} + +static void +pointer_button(void *udata, struct wl_pointer *wl_pointer, uint32_t serial, + uint32_t time, uint32_t button, uint32_t state){ +} + +static void +pointer_axis(void *udata, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value){ +} + +const struct wl_pointer_listener pointer_listener = { + pointer_enter, + pointer_leave, + pointer_motion, + pointer_button, + pointer_axis +}; + +static void +seat_capabilities(void *udata, struct wl_seat *wl_seat, uint32_t capabilities){ + if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && ctx.wl_pointer == 0){ + ctx.wl_pointer = wl_seat_get_pointer(ctx.wl_seat); + wl_pointer_add_listener(ctx.wl_pointer, &pointer_listener, 0); + } + else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && ctx.wl_pointer != 0){ + wl_pointer_release(ctx.wl_pointer); + ctx.wl_pointer = 0; + } +} + +static void +seat_name(void *udata, struct wl_seat *wl_seat, const char *name){ +} + +const struct wl_seat_listener seat_listener = { + seat_capabilities, + seat_name +}; + +static void +registry_global(void *udata, struct wl_registry *wl_registry, + uint32_t name, const char *interface, + uint32_t version){ + if (strcmp(interface, "wl_compositor") == 0){ + ctx.wl_compositor = (struct wl_compositor*) + wl_registry_bind(wl_registry, name, &wl_compositor_interface, Min(version, 4)); + } + else if (strcmp(interface, "wl_subcompositor") == 0){ + ctx.wl_subcompositor = wl_registry_bind(ctx.wl_registry, name, &wl_subcompositor_interface, 1); + } + else if (strcmp(interface, "wl_shm") == 0){ + ctx.wl_shm = wl_registry_bind(ctx.wl_registry, name, &wl_shm_interface, 1); + wl_shm_add_listener(ctx.wl_shm, &shm_listener, 0); + } + else if (!strcmp(interface, xdg_wm_base_interface.name)){ + ctx.xdg_wm_base = wl_registry_bind(ctx.wl_registry, name, &xdg_wm_base_interface, Min(version, 2)); + xdg_wm_base_add_listener(ctx.xdg_wm_base, &xdg_wm_base_listener, 0); + } + else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name)){ + ctx.zxdg_decoration_manager = wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, Min(version, 2)); + } + else if (strcmp(interface, "wl_seat") == 0){ + ctx.wl_seat = wl_registry_bind(ctx.wl_registry, name, &wl_seat_interface, 3); + wl_seat_add_listener(ctx.wl_seat, &seat_listener, 0); + ctx.cursor_surface = wl_compositor_create_surface(ctx.wl_compositor); + } +} + +static void +registry_global_remove(void *udata, struct wl_registry *registry, + uint32_t name){ +} + +const struct wl_registry_listener registry_listener = { + registry_global, + registry_global_remove, +}; + +static void +xdg_surface_configure(void *udata, struct xdg_surface *xdg_surface, + uint32_t serial){ + ctx.config_staged.serial = serial; +} + +const struct xdg_surface_listener xdg_surface_listener = { xdg_surface_configure, }; + +static void +xdg_toplevel_configure(void *udata, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, struct wl_array *states){ + if (width != 0 && height != 0){ + ctx.config_staged.dim[0] = width; + ctx.config_staged.dim[1] = height; + } + else{ + ctx.config_staged.dim[0] = ctx.config.dim[0]; + ctx.config_staged.dim[1] = ctx.config.dim[1]; + } + ctx.config_staged.flags = window_flags_from_states_array(states); +} + +static void +xdg_toplevel_close(void *udata, struct xdg_toplevel *xdg_toplevel){ + ctx.close_signal = 1; +} + +static void +xdg_toplevel_configure_bounds(void *udata, struct xdg_toplevel *xdg_toplevel, + int32_t w, int32_t h){ +} + +static void +xdg_toplevel_wm_capabilities(void *udata, struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities){ +} + +const struct xdg_toplevel_listener xdg_toplevel_listener = { + xdg_toplevel_configure, + xdg_toplevel_close, + xdg_toplevel_configure_bounds, + xdg_toplevel_wm_capabilities, +}; + +static void +xdg_toplevel_decoration_configure(void *udata, struct zxdg_toplevel_decoration_v1 *toplevel, + uint32_t mode){ + ctx.config_staged.decoration_mode = mode; +} + +const struct zxdg_toplevel_decoration_v1_listener zxdg_toplevel_decoration_listener = { + xdg_toplevel_decoration_configure +}; + +int main(){ + /* desktop settings */ + ctx.color_scheme = ds_get_color_scheme(); + ctx.cursor_theme = ds_get_cursor_theme(); + + /* setup Wayland */ + { + ctx.wl_display = wl_display_connect(0); + ctx.wl_registry = wl_display_get_registry(ctx.wl_display); + wl_registry_add_listener(ctx.wl_registry, ®istry_listener, 0); + + wl_display_flush(ctx.wl_display); + wl_display_dispatch(ctx.wl_display); + + ctx.wl_cursor_theme = wl_cursor_theme_load(ctx.cursor_theme.name, + ctx.cursor_theme.size, + ctx.wl_shm); + +#define X(k,n) ctx.wl_cursors[k] = wl_cursor_theme_get_cursor(ctx.wl_cursor_theme, n); + X(CursorShape_Pointer, "left_ptr"); + X(CursorShape_Resize_Top, "top_side"); + X(CursorShape_Resize_Bottom, "bottom_side"); + X(CursorShape_Resize_Left, "left_side"); + X(CursorShape_Resize_Right, "right_side"); + X(CursorShape_Resize_TopLeft, "top_left_corner"); + X(CursorShape_Resize_BottomLeft, "bottom_left_corner"); + X(CursorShape_Resize_TopRight, "top_right_corner"); + X(CursorShape_Resize_BottomRight, "bottom_right_corner"); +#undef X + } + + /* setup EGL */ + { + ctx.egl_display = eglGetDisplay(ctx.wl_display); + + EGLint major = 0, minor = 0; + eglInitialize(ctx.egl_display, &major, &minor); + eglBindAPI(EGL_OPENGL_API); + + { + EGLint attr[] = { + EGL_CONTEXT_MAJOR_VERSION, 3, + EGL_CONTEXT_MINOR_VERSION, 3, + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_NONE, + }; + ctx.egl_context = eglCreateContext(ctx.egl_display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attr); + } + + eglMakeCurrent(ctx.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx.egl_context); + +#define X(N,R,P) N = (R(*)P)(eglGetProcAddress(#N)); + GL_FUNCS_XLIST(X) +#undef X + } + + /* create a window */ + { + /* window main surface */ + ctx.main_wl_surface = wl_compositor_create_surface(ctx.wl_compositor); + ctx.main_xdg_surface = xdg_wm_base_get_xdg_surface(ctx.xdg_wm_base, ctx.main_wl_surface); + xdg_surface_add_listener(ctx.main_xdg_surface, &xdg_surface_listener, 0); + ctx.main_xdg_toplevel = xdg_surface_get_toplevel(ctx.main_xdg_surface); + xdg_toplevel_add_listener(ctx.main_xdg_toplevel, &xdg_toplevel_listener, 0); + + ctx.config.dim[0] = 640; + ctx.config.dim[1] = 480; + ctx.control_flags = ~0; + + ctx.config_staged.decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; + if (ctx.zxdg_decoration_manager != 0){ + ctx.main_zxdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(ctx.zxdg_decoration_manager, ctx.main_xdg_toplevel); + zxdg_toplevel_decoration_v1_add_listener(ctx.main_zxdg_toplevel_decoration, &zxdg_toplevel_decoration_listener, 0); + } + + for (int k = 0; k < 2; k += 1){ + ctx.mmbox[k][0] = 0; + ctx.mmbox[k][1] = (1 << 30); + } + + xdg_toplevel_set_app_id(ctx.main_xdg_toplevel, "demo"); + xdg_toplevel_set_title(ctx.main_xdg_toplevel, "Example Window"); + wl_surface_commit(ctx.main_wl_surface); + + /* window subsurface */ + ctx.shadow_wl_surface = wl_compositor_create_surface(ctx.wl_compositor); + ctx.shadow_wl_subsurface = + wl_subcompositor_get_subsurface(ctx.wl_subcompositor, + ctx.shadow_wl_surface, + ctx.main_wl_surface); + + { + int dim[2] = {640, 25}; + + int stride = 4*dim[0]; + uint64_t size = 4*dim[0]*dim[1]; + + int fd = os_create_anonymous_file(size); + if (fd >= 0){ + void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data != MAP_FAILED){ + struct wl_shm_pool *pool = wl_shm_create_pool(ctx.wl_shm, fd, size); + struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(pool, 0, dim[0], dim[1], stride, + WL_SHM_FORMAT_ARGB8888); + wl_shm_pool_destroy(pool); + + ctx.shadow_wl_buffer = wl_buffer; + ctx.shadow_data = data; + ctx.shadow_size = size; + for (int i = 0; i < 2; i += 1){ + ctx.shadow_dim[i] = dim[i]; + } + } + + close(fd); + } + } + + /* window egl */ + ctx.main_wl_egl_window = wl_egl_window_create(ctx.main_wl_surface, + ctx.config.dim[0], + ctx.config.dim[1]); + + EGLConfig configs[64]; + EGLint config_cap = sizeof(configs)/sizeof(*configs); + EGLint config_count = 0; + { + EGLint attributes[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_CONFORMANT, EGL_OPENGL_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_NONE, + }; + eglChooseConfig(ctx.egl_display, attributes, configs, config_cap, &config_count); + } + + { + EGLint attributes[] = { + EGL_RENDER_BUFFER, EGL_BACK_BUFFER, + EGL_NONE, + }; + for (EGLint i = 0; i < config_count; i += 1){ + ctx.main_egl_surface = eglCreateWindowSurface(ctx.egl_display, configs[i], ctx.main_wl_egl_window, attributes); + if (ctx.main_egl_surface != EGL_NO_SURFACE){ + break; + } + } + } + + eglMakeCurrent(ctx.egl_display, ctx.main_egl_surface, + ctx.main_egl_surface, ctx.egl_context); + + eglSwapInterval(ctx.egl_display, 1); + } + + /* main loop */ + int exit_loop = 0; + for (;!exit_loop;){ + /* poll for events */ + { + struct pollfd fds[1] = {0}; + + /* register fds[0] ~ wayland events */ + bool wayland_started_read = false; + bool wayland_did_read = false; + { + fds[0].fd = -1; + + wl_display_dispatch_pending(ctx.wl_display); + wayland_started_read = (wl_display_prepare_read(ctx.wl_display) != -1); + if (wayland_started_read){ + fds[0].fd = wl_display_get_fd(ctx.wl_display); + fds[0].events = POLLIN; + wl_display_flush(ctx.wl_display); + } + } + + /* poll and handle events */ + int ret = poll(fds, sizeof(fds)/sizeof(*fds), 0); + if (ret > 0){ + + /* handle fds[0] ~ wayland events */ + if (fds[0].revents & POLLIN){ + wayland_did_read = true; + wl_display_read_events(ctx.wl_display); + wl_display_dispatch_pending(ctx.wl_display); + } + } + + /* wayland event read cleanup */ + if (wayland_started_read && !wayland_did_read){ + wl_display_cancel_read(ctx.wl_display); + } + } + + /* apply config */ + if (ctx.config.serial != ctx.config_staged.serial){ + ctx.config = ctx.config_staged; + xdg_surface_ack_configure(ctx.main_xdg_surface, ctx.config.serial); + } + + int csd = (ctx.config.decoration_mode == + ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); + + /* window sizing */ + CSD_Frame csd_frame = {0}; + int32_t csd_dim[2]; + { + if (csd){ + csd_frame = csd_impl_calculate_frame(); + } + for (int i = 0; i < 2; i += 1){ + csd_dim[i] = csd_frame.border[i][0] + csd_frame.border[i][1]; + int32_t d = ctx.config.dim[i] - csd_dim[i]; + d = ClampBot(d, ctx.mmbox[i][0]); + d = ClampTop(d, ctx.mmbox[i][1]); + d = ClampBot(d, csd_frame.minbox[i]); + ctx.dim[i] = d; + } + } + + /* window frame render */ + if (csd){ + uint32_t *pxl = (uint32_t*)ctx.shadow_data; + for (int32_t y = 0; y < ctx.shadow_dim[1]; y += 1){ + for (int32_t x = 0; x < ctx.shadow_dim[0]; x += 1){ + *pxl = 0xFFFF0000; + pxl += 1; + } + } + } + + /* window frame commit */ + if (csd){ + { + wl_surface_attach(ctx.shadow_wl_surface, ctx.shadow_wl_buffer, 0, 0); + wl_surface_set_buffer_scale(ctx.shadow_wl_surface, 1); + wl_surface_damage_buffer(ctx.shadow_wl_surface, 0, 0, + ctx.shadow_dim[0], ctx.shadow_dim[1]); + wl_subsurface_set_position(ctx.shadow_wl_subsurface, 0, -25); + wl_surface_commit(ctx.shadow_wl_surface); + } + + if (!(ctx.control_flags & WindowControlFlag_Resize)){ + xdg_toplevel_set_min_size(ctx.main_xdg_toplevel, ctx.dim[0], ctx.dim[1]); + xdg_toplevel_set_max_size(ctx.main_xdg_toplevel, ctx.dim[0], ctx.dim[1]); + } + else{ + for (int i = 0; i < 2; i += 1){ + int32_t mw = ClampBot(ctx.mmbox[0][i], csd_frame.minbox[i]) + csd_dim[0]; + int32_t mh = ctx.mmbox[1][i] + csd_dim[1]; + if (i == 0){ + xdg_toplevel_set_min_size(ctx.main_xdg_toplevel, mw, mh); + } + else{ + xdg_toplevel_set_max_size(ctx.main_xdg_toplevel, mw, mh); + } + } + } + } + + /* app update & render */ + { + if (ctx.close_signal){ + exit_loop = 1; + } + + glDrawBuffer(GL_BACK); + glViewport(0, 0, ctx.config.dim[0], ctx.config.dim[1]); + glClearColor(0.40f, 0.90f, 0.15f, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + } + + eglSwapBuffers(ctx.egl_display, ctx.main_egl_surface); + wl_surface_commit(ctx.main_wl_surface); + } + + return(0); +} + +/* csd implementation */ + +static CSD_Frame +csd_impl_calculate_frame(void){ + CSD_Frame frame = {0}; + bool show_title = (!(ctx.config.flags & WindowFlag_IsFullscreen)); + if (show_title){ + frame.border[1][0] = 25; + } + return(frame); +} + +/* wayland helpers */ + +static WindowFlags +window_flags_from_states_array(struct wl_array *states){ + WindowFlags flags = 0; + uint32_t *p; + wl_array_for_each(p, states){ + switch (*p) { + case XDG_TOPLEVEL_STATE_FULLSCREEN: flags |= WindowFlag_IsFullscreen; break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: flags |= WindowFlag_IsMax; break; + case XDG_TOPLEVEL_STATE_ACTIVATED: flags |= WindowFlag_IsActivated; break; + case XDG_TOPLEVEL_STATE_TILED_LEFT: flags |= WindowFlag_IsTiledLeft; break; + case XDG_TOPLEVEL_STATE_TILED_RIGHT: flags |= WindowFlag_IsTiledRight; break; + case XDG_TOPLEVEL_STATE_TILED_TOP: flags |= WindowFlag_IsTiledTop; break; + case XDG_TOPLEVEL_STATE_TILED_BOTTOM: flags |= WindowFlag_IsTiledBottom; break; + case XDG_TOPLEVEL_STATE_RESIZING: flags |= WindowFlag_IsResizing; break; + case XDG_TOPLEVEL_STATE_SUSPENDED: flags |= WindowFlag_IsSuspended; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT: flags |= WindowFlag_IsConstrainedLeft; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT: flags |= WindowFlag_IsConstrainedRight; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_TOP: flags |= WindowFlag_IsConstrainedTop; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM: flags |= WindowFlag_IsConstrainedBottom; break; + default: break; + } + } + return(flags); +} + +/* os */ + +static int +os_resize_anonymous_file(int fd, off_t size){ +#ifdef HAVE_POSIX_FALLOCATE + + sigset_t mask; + sigset_t old_mask; + sigemptyset(&mask); + sigaddset(&mask, SIGALRM); + sigprocmask(SIG_BLOCK, &mask, &old_mask); + + do { + errno = posix_fallocate(fd, 0, size); + } while (errno == EINTR); + sigprocmask(SIG_SETMASK, &old_mask, NULL); + + int result = 0; + if (errno != 0){ + result = -1; + if (errno == EINVAL || errno == EOPNOTSUPP){ + if (ftruncate(fd, size) >= 0){ + result = 0; + } + } + } + return(result); + +#else + + int result = -1; + if (ftruncate(fd, size) >= 0){ + result = 0; + } + return(result); + +#endif +} + +static int +os_create_anonymous_file(off_t size){ + static const char key[] = "/libdecor-shared-XXXXXX"; + int fd = -1; + +#ifdef HAVE_MEMFD_CREATE + fd = memfd_create("libdecor", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0){ + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + } +#endif + + if (fd < 0){ + const char *path = getenv("XDG_RUNTIME_DIR"); + if (path == 0){ + errno = ENOENT; + } + else{ + char *name = malloc(strlen(path) + sizeof(key)); + if (name != 0){ + strcpy(name, path); + strcat(name, key); + +#ifdef HAVE_MKOSTEMP + fd = mkostemp(name, O_CLOEXEC); + if (fd >= 0){ + unlink(name); + } +#else + fd = mkstemp(name); + + if (fd >= 0){ + if (fcntl(fd, F_GETFD) == -1){ + close(fd); + fd = -1; + } + else if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1){ + close(fd); + fd = -1; + } + } + + if (fd >= 0) { + unlink(name); + } +#endif + + free(name); + } + } + } + + if (fd >= 0 && os_resize_anonymous_file(fd, size) < 0){ + close(fd); + fd = -1; + } + + return(fd); +} + +/* desktop settings */ + +static CursorTheme +ds__get_cursor_theme_from_env(void){ + CursorTheme result = {0}; + char *env_xtheme = getenv("XCURSOR_THEME"); + char *env_xsize = getenv("XCURSOR_SIZE"); + if (env_xtheme != 0 && env_xsize != 0){ + result.name = strdup(env_xtheme); + result.size = atoi(env_xsize); + } + else{ + result.name = 0; + result.size = 24; + } + return(result); +} + +#ifdef HAS_DBUS +#include + +static DBusMessage * +ds__get_setting_sync(DBusConnection *const connection, const char *key1, const char *key2){ + DBusMessage *reply = 0; + DBusMessage *message = + dbus_message_new_method_call("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", + "Read"); + + if (message != 0){ + dbus_bool_t success = dbus_message_append_args(message, + DBUS_TYPE_STRING, &key1, + DBUS_TYPE_STRING, &key2, + DBUS_TYPE_INVALID); + if (success){ + DBusError error; + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error); + if (dbus_error_is_set(&error) && reply != 0){ + dbus_message_unref(reply); + reply = 0; + } + dbus_error_free(&error); + } + dbus_message_unref(message); + } + + return(reply); +} + +static int +ds__parse_type(DBusMessage *const reply, const int type, void *value){ + int result = 0; + DBusMessageIter iter[3]; + dbus_message_iter_init(reply, &iter[0]); + if (dbus_message_iter_get_arg_type(&iter[0]) == DBUS_TYPE_VARIANT){ + dbus_message_iter_recurse(&iter[0], &iter[1]); + if (dbus_message_iter_get_arg_type(&iter[1]) == DBUS_TYPE_VARIANT){ + dbus_message_iter_recurse(&iter[1], &iter[2]); + if (dbus_message_iter_get_arg_type(&iter[2]) == type){ + dbus_message_iter_get_basic(&iter[2], value); + result = 1; + } + } + } + return(result); +} + +static CursorTheme +ds_get_cursor_theme(void){ + static const char key[] = "org.gnome.desktop.interface"; + static const char key_theme[] = "cursor-theme"; + static const char key_size[] = "cursor-size"; + + char *name = 0; + int size = 0; + int success = 0; + + DBusError error; + dbus_error_init(&error); + DBusConnection *connection = dbus_bus_get(DBUS_BUS_SESSION, &error); + + if (!dbus_error_is_set(&error)){ + DBusMessage *reply = ds__get_setting_sync(connection, key, key_theme); + if (reply != 0){ + if (!ds__parse_type(reply, DBUS_TYPE_STRING, &name)){ + name = 0; + } + dbus_message_unref(reply); + } + } + + if (name != 0){ + DBusMessage *reply = ds__get_setting_sync(connection, key, key_size); + if (reply){ + if (ds__parse_type(reply, DBUS_TYPE_INT32, &size)){ + success = 1; + } + dbus_message_unref(reply); + } + } + + CursorTheme result = {0}; + if (success){ + result.name = name; + result.size = size; + } + else{ + result = ds__get_cursor_theme_from_env(); + } + + return(result); +} + +static ColorScheme +ds_get_color_scheme(){ + static const char name[] = "org.freedesktop.appearance"; + static const char key_color_scheme[] = "color-scheme"; + uint32_t color = 0; + + DBusError error; + dbus_error_init(&error); + DBusConnection *connection = dbus_bus_get(DBUS_BUS_SESSION, &error); + + if (!dbus_error_is_set(&error)){ + DBusMessage *reply = ds__get_setting_sync(connection, name, key_color_scheme); + if (reply){ + if (!ds__parse_type(reply, DBUS_TYPE_UINT32, &color)) { + color = 0; + } + dbus_message_unref(reply); + } + } + return(color); +} + +#else + +static CursorTheme +ds_get_cursor_them(void){ + return(ds__get_cursor_theme_from_env()); +} + +static ColorScheme +ds_get_color_scheme(){ + return(ColorScheme_Default); +} + +#endif + diff --git a/wayland_egl.h b/wayland_egl.h new file mode 100644 index 0000000..0922776 --- /dev/null +++ b/wayland_egl.h @@ -0,0 +1,159 @@ +#ifndef WAYLAND_EGL_EXAMPLE_H +#define WAYLAND_EGL_EXAMPLE_H + +#define HAS_DBUS +#define HAVE_MEMFD_CREATE +#define HAVE_POSIX_FALLOCATE + +#define Min(a, b) ((a)>(b)?(b):(a)) +#define Max(a, b) ((a)<(b)?(b):(a)) + +#define ClampTop(x, a) Min(x, a) +#define ClampBot(x, a) Max(x, a) + +typedef enum ColorScheme{ + ColorScheme_Default, + ColorScheme_Dark, + ColorScheme_Light +} ColorScheme; + +typedef struct CursorTheme{ + char *name; + int size; +} CursorTheme; + +enum{ + CursorShape_Pointer, + CursorShape_Resize_Top, + CursorShape_Resize_Bottom, + CursorShape_Resize_Left, + CursorShape_Resize_Right, + CursorShape_Resize_TopLeft, + CursorShape_Resize_TopRight, + CursorShape_Resize_BottomLeft, + CursorShape_Resize_BottomRight, + CursorShape_COUNT +}; + +typedef uint32_t WindowFlags; +enum{ + WindowFlag_IsFullscreen = (1 << 0), + WindowFlag_IsMax = (1 << 1), + WindowFlag_IsActivated = (1 << 2), + WindowFlag_IsTiledLeft = (1 << 3), + WindowFlag_IsTiledRight = (1 << 4), + WindowFlag_IsTiledTop = (1 << 5), + WindowFlag_IsTiledBottom = (1 << 6), + WindowFlag_IsResizing = (1 << 7), + WindowFlag_IsSuspended = (1 << 8), + WindowFlag_IsConstrainedLeft = (1 << 9), + WindowFlag_IsConstrainedRight = (1 << 10), + WindowFlag_IsConstrainedTop = (1 << 11), + WindowFlag_IsConstrainedBottom = (1 << 12), +}; + +typedef uint32_t WindowControlFlags; +enum{ + WindowControlFlag_Move = (1 << 0), + WindowControlFlag_Resize = (1 << 1), + WindowControlFlag_Min = (1 << 2), + WindowControlFlag_Max = (1 << 3), + WindowControlFlag_Close = (1 << 4), +}; + +typedef struct Config{ + uint32_t serial; + int32_t dim[2]; + uint32_t flags; + uint32_t decoration_mode; +} Config; + +typedef struct CSD_Frame{ + int32_t border[2][2]; // [0][]:x [1][]:y [][0]:min [][1]:max + int32_t minbox[2]; +} CSD_Frame; + +typedef struct Ctx{ + /* "application variables" */ + int close_signal; + + /* globals: desktop settings */ + ColorScheme color_scheme; + CursorTheme cursor_theme; + + /* globals: wayland */ + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct wl_compositor *wl_compositor; + struct wl_subcompositor *wl_subcompositor; + struct wl_shm *wl_shm; + struct xdg_wm_base *xdg_wm_base; + struct zxdg_decoration_manager_v1 *zxdg_decoration_manager; + + struct wl_cursor_theme *wl_cursor_theme; + struct wl_cursor *wl_cursors[CursorShape_COUNT]; + + /* globals: egl */ + EGLDisplay egl_display; + EGLContext egl_context; + + /* per-seat: wayland */ + struct wl_seat *wl_seat; + struct wl_pointer *wl_pointer; + struct wl_surface *cursor_surface; + + /* per-window: wayland */ + struct wl_surface *main_wl_surface; + struct xdg_surface *main_xdg_surface; + struct xdg_toplevel *main_xdg_toplevel; + struct zxdg_toplevel_decoration_v1 *main_zxdg_toplevel_decoration; + + struct wl_surface *shadow_wl_surface; + struct wl_subsurface *shadow_wl_subsurface; + struct wl_buffer *shadow_wl_buffer; + void *shadow_data; + uint64_t shadow_size; + int32_t shadow_dim[2]; + + /* per-window: egl */ + struct wl_egl_window *main_wl_egl_window; + EGLSurface main_egl_surface; + + /* per-window */ + WindowControlFlags control_flags; + Config config; + Config config_staged; + int32_t mmbox[2][2]; // [0][]:x [1][]:y [][0]:min [][1]:max + int32_t dim[2]; +} Ctx; + + +/* csd implementation */ + +static CSD_Frame csd_impl_calculate_frame(void); + + +/* wayland helpers */ + +static WindowFlags window_flags_from_states_array(struct wl_array *states); + +/* os */ + +static int os_resize_anonymous_file(int fd, off_t size); +static int os_create_anonymous_file(off_t size); + +/* desktop settings */ + +static CursorTheme ds_get_cursor_theme(void); +static ColorScheme ds_get_color_scheme(void); + +static CursorTheme ds__get_cursor_theme_from_env(void); + +#if defined(HAS_DBUS) +#include +static DBusMessage* ds__get_setting_sync(DBusConnection *const connection, const char *k, const char *v); +static int ds__parse_type(DBusMessage *const reply, const int type, void *value); +#endif /* defined(HAS_DBUS) */ + + +#endif /* WAYLAND_EGL_EXAMPLE_H */