#if 0 libdecor_path="/home/mr4th/mr4th/libdecor" root_path="$PWD" gtk_flags="$(pkg-config --cflags --libs gtk+-3.0)" 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/digesting_libdecor.c $gtk_flags $dbus_flags $my_flags exit 0 #endif /* ** Reading From: ** (1) Wayland Docs https://wayland.freedesktop.org/docs/html/ ** (egl) EGL spec https://registry.khronos.org/EGL/sdk/docs/man/ ** (libdecor.h) /usr/include/libdecor-0/libdecor.h ** ** (nodocs-wl_egl) I cannot find any documentation for wl_egl_ except for ** headers and example code. */ /* [1] IMPORTANT NOTE @see([1] in wayland_xdg_egl.c) */ #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 #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 "digesting_libdecor.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 xdg_wm_base_ping(void *user_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 = { xdg_wm_base_ping, }; static void shm_format(void *user_data, struct wl_shm *wl_shm, uint32_t format){ if (format == WL_SHM_FORMAT_ARGB8888){ ctx.has_argb = true; } } const struct wl_shm_listener shm_listener = { shm_format }; static void pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y){ struct seat *seat = data; seat->pointer_x = wl_fixed_to_int(x); seat->pointer_y = wl_fixed_to_int(y); seat->serial = serial; seat->pointer_focus = surface; ctx.active = component_slot_from_wl_surface(surface); ctx.pointer_enter = 1; } static void pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface){ struct seat *seat = data; seat->pointer_focus = 0; seat->serial = serial; ctx.active = 0; ctx.pointer_leave = 1; } static void pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y){ struct seat *seat = data; seat->pointer_x = wl_fixed_to_int(surface_x); seat->pointer_y = wl_fixed_to_int(surface_y); ctx.pointer_motion = 1; } static void pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state){ struct seat *seat = data; seat->serial = serial; ctx.pointer_button = 1; ctx.pointer_button_time = time; ctx.pointer_button_button = button; ctx.pointer_button_state = state; } static void pointer_axis(void *data, 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 *data, struct wl_seat *wl_seat, uint32_t capabilities){ struct seat *seat = data; if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer == 0){ seat->wl_pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat); } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer != 0){ wl_pointer_release(seat->wl_pointer); seat->wl_pointer = 0; } } static void seat_name(void *data, struct wl_seat *wl_seat, const char *name){ struct seat *seat = (struct seat*)data; seat->name = strdup(name); } const struct wl_seat_listener seat_listener = { seat_capabilities, seat_name }; static void wl_registry_global(void *data, 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, 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)){ uint32_t desired_version = 2; /* Find the required version for the available features. */ #ifdef XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION desired_version = MAX(desired_version, XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION); #endif #ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION desired_version = MAX(desired_version, XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION); #endif #ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION desired_version = MAX(desired_version, XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION); #endif #ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION desired_version = MAX(desired_version, XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION); #endif ctx.decoration_manager = wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, MIN(version, desired_version)); } 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, "wl_seat") == 0){ struct seat *seat; if (version < 3){ ctx.has_error = true; } seat = calloc(1, sizeof *seat); wl_list_insert(&ctx.seat_list, &seat->link); ctx.seat = seat; seat->wl_seat = wl_registry_bind(ctx.wl_registry, name, &wl_seat_interface, 3); wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); seat->cursor_surface = wl_compositor_create_surface(ctx.wl_compositor); } } static void wl_registry_global_remove(void *data, struct wl_registry *registry, uint32_t name){} const struct wl_registry_listener wl_registry_listener = { wl_registry_global, wl_registry_global_remove, }; 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; } const struct xdg_surface_listener xdg_surface_listener = { xdg_surface_configure, }; static void xdg_toplevel_configure(void *user_data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states){ enum libdecor_window_state window_state = LIBDECOR_WINDOW_STATE_NONE; uint32_t *p; wl_array_for_each(p, states) { enum xdg_toplevel_state state = *p; switch (state) { case XDG_TOPLEVEL_STATE_FULLSCREEN: window_state |= LIBDECOR_WINDOW_STATE_FULLSCREEN; break; case XDG_TOPLEVEL_STATE_MAXIMIZED: window_state |= LIBDECOR_WINDOW_STATE_MAXIMIZED; break; case XDG_TOPLEVEL_STATE_ACTIVATED: window_state |= LIBDECOR_WINDOW_STATE_ACTIVE; break; case XDG_TOPLEVEL_STATE_TILED_LEFT: window_state |= LIBDECOR_WINDOW_STATE_TILED_LEFT; break; case XDG_TOPLEVEL_STATE_TILED_RIGHT: window_state |= LIBDECOR_WINDOW_STATE_TILED_RIGHT; break; case XDG_TOPLEVEL_STATE_TILED_TOP: window_state |= LIBDECOR_WINDOW_STATE_TILED_TOP; break; case XDG_TOPLEVEL_STATE_TILED_BOTTOM: window_state |= LIBDECOR_WINDOW_STATE_TILED_BOTTOM; break; case XDG_TOPLEVEL_STATE_RESIZING: window_state |= LIBDECOR_WINDOW_STATE_RESIZING; break; #ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION case XDG_TOPLEVEL_STATE_SUSPENDED: window_state |= LIBDECOR_WINDOW_STATE_SUSPENDED; break; #endif #ifdef XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION case XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT: window_state |= LIBDECOR_WINDOW_STATE_CONSTRAINED_LEFT; break; case XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT: window_state |= LIBDECOR_WINDOW_STATE_CONSTRAINED_RIGHT; break; case XDG_TOPLEVEL_STATE_CONSTRAINED_TOP: window_state |= LIBDECOR_WINDOW_STATE_CONSTRAINED_TOP; break; case XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM: window_state |= LIBDECOR_WINDOW_STATE_CONSTRAINED_BOTTOM; break; #endif default: break; } } 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; } static void xdg_toplevel_close(void *user_data, struct xdg_toplevel *xdg_toplevel){ ctx.close_signal = 1; } #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){ int32_t inner_w = w; int32_t inner_h = h; if (ctx.visible && ctx.decoration_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE){ Sides2D border_size = border_size_from_window_state(0); inner_w -= border_size.x[0] + border_size.x[1]; inner_h -= border_size.y[0] + border_size.y[1]; } // event to user (inner_w,inner_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; } } } #endif const struct xdg_toplevel_listener xdg_toplevel_listener = { xdg_toplevel_configure, xdg_toplevel_close, #ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION xdg_toplevel_configure_bounds, #endif #ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION xdg_toplevel_wm_capabilities, #endif }; static void xdg_toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1 *toplevel, uint32_t mode){ if (!ctx.has_decoration_mode){ ctx.has_decoration_mode = true; ctx.decoration_mode = mode; } } const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = { xdg_toplevel_decoration_configure, }; int main(){ /* get desktop settings */ ctx.color_scheme = libdecor_get_color_scheme(); if (libdecor_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; { gdk_set_allowed_backends("wayland"); gtk_disable_setlocale(); if (gtk_init_check(0, 0)){ gtk_init_success = 1; } g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", (ctx.color_scheme == LIBDECOR_COLOR_SCHEME_PREFER_DARK), NULL); if (!gtk_init_success){ printf("failed to initialize gtk\n"); } } /*~ NOTE: **~ initialize Wayland, Libdecor, & EGL */ { ctx.w = 640; ctx.h = 480; wl_list_init(&ctx.seat_list); } if (gtk_init_success){ ctx.wl_display = wl_display_connect(0); if (ctx.wl_display == 0){ printf("wl_display_connect failed\n"); } } 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.wl_registry != 0){ wl_registry_add_listener(ctx.wl_registry, &wl_registry_listener, 0); 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"); ctx.has_error = 1; } if (ctx.wl_subcompositor == 0){ printf("failed to get wl_subcompositor\n"); ctx.has_error = 1; } if (ctx.wl_shm == 0){ printf("failed to get wl_shm\n"); ctx.has_error = 1; } } if (!ctx.has_error){ ctx.cursor_theme = wl_cursor_theme_load(ctx.cursor_theme_name, ctx.cursor_size, ctx.wl_shm); if (ctx.cursor_theme != 0){ for (unsigned int i = 0; i < ARRAY_LENGTH(cursor_names); i += 1){ ctx.cursors[i] = wl_cursor_theme_get_cursor(ctx.cursor_theme, cursor_names[i]); } ctx.cursor_left_ptr = wl_cursor_theme_get_cursor(ctx.cursor_theme, "left_ptr"); } } int opengl_load_success = 0; if (!ctx.has_error){ /* (egl) eglGetDisplay ** " obtains the EGL display connection for the native display " */ ctx.egl_display = eglGetDisplay(ctx.wl_display); if (ctx.egl_display == 0){ printf("eglGetDisplay failed\n"); } /* (egl) eglInitialize ** " initializes* the EGL display connection obtained with eglGetDisplay " */ int egl_init_success = 0; EGLint major = 0, minor = 0; if (ctx.egl_display != 0){ egl_init_success = eglInitialize(ctx.egl_display, &major, &minor); if (!egl_init_success){ printf("eglInitialize failed\n"); } } // print version if (egl_init_success){ printf("EGL version %d.%d\n", major, minor); if (major < 1 || (major == 1 && minor < 5)){ printf("version 1.5 or higher\n"); egl_init_success = 0; } } /* (egl) eglBindAPI ** " defines the current rendering API for EGL in the thread it is ** called from " */ EGLBoolean bind_api_success = 0; if (egl_init_success){ bind_api_success = eglBindAPI(EGL_OPENGL_API); } /* (egl) eglCreateContext ** " creates an EGL rendering context for the current rendering API ** (as set with eglBindAPI) and returns a handle to the context " */ if (bind_api_success){ 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); if (ctx.egl_context == EGL_NO_CONTEXT){ printf("eglCreateContext failed\n"); } } /* (egl) eglMakeCurrent ** " binds context to the current rendering thread " */ EGLBoolean make_current_success = 0; if (ctx.egl_context != 0){ make_current_success = eglMakeCurrent(ctx.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx.egl_context); if (!make_current_success){ printf("eglMakeCurrent failed\n"); } } /* (egl) eglGetProcAddress ** " returns the address of the client API or EGL function " */ if (make_current_success){ opengl_load_success = 1; #define X(N,R,P) N = (R(*)P)(eglGetProcAddress(#N)); if (N == 0) opengl_load_success = 0; GL_FUNCS_XLIST(X) #undef X if (!opengl_load_success){ printf("GL procedure loading failed\n"); } } } /*~ NOTE: **~ Create a window */ if (opengl_load_success){ /* (1) Appendix A: wl_compositor::create_surface ** " create new surface " */ 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){ { static const int size = 128; static const int boundary = 32; cairo_surface_t *shadow_blur = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size, size); cairo_t *cr = cairo_create(shadow_blur); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cr, 0, 0, 0, 1); cairo_rectangle(cr, boundary, boundary, size - 2*boundary, size - 2*boundary); cairo_fill(cr); cairo_destroy(cr); blur_surface(shadow_blur, 64); ctx.shadow_blur = shadow_blur; } 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 | LIBDECOR_ACTION_FULLSCREEN | LIBDECOR_ACTION_CLOSE); ctx.xdg_surface = xdg_wm_base_get_xdg_surface(ctx.xdg_wm_base, ctx.wl_surface); xdg_surface_add_listener(ctx.xdg_surface, &xdg_surface_listener, 0); ctx.xdg_toplevel = xdg_surface_get_toplevel(ctx.xdg_surface); xdg_toplevel_add_listener(ctx.xdg_toplevel, &xdg_toplevel_listener, 0); ctx.decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; if (ctx.decoration_manager != 0){ ctx.toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(ctx.decoration_manager, ctx.xdg_toplevel); zxdg_toplevel_decoration_v1_add_listener(ctx.toplevel_decoration, &xdg_toplevel_decoration_listener, 0); } ctx.size_bounds.x[0] = 80; ctx.size_bounds.y[0] = 60; ctx.size_bounds.x[1] = (1 << 30); ctx.size_bounds.y[1] = (1 << 30); xdg_toplevel_set_app_id(ctx.xdg_toplevel, "demo"); xdg_toplevel_set_title(ctx.xdg_toplevel, "Example Window"); ctx.title = strdup("Example Window"); wl_surface_commit(ctx.wl_surface); /* (nodocs-wl_egl) */ ctx.wl_egl_window = wl_egl_window_create(ctx.wl_surface, ctx.w, ctx.h); if (ctx.wl_egl_window == EGL_NO_SURFACE){ printf("wl_egl_window_create failed\n"); } } if (ctx.wl_egl_window != EGL_NO_SURFACE){ /* (nodocs-wl_egl) eglChooseConfig ** " returns in configs a list of all EGL frame buffer configurations ** that match the attributes specified in attrib_list " */ 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, }; if (!eglChooseConfig(ctx.egl_display, attributes, configs, config_cap, &config_count)){ printf("eglChooseConfig failed\n"); config_count = 0; } } /* (egl) eglCreateWindowSurface ** " creates an on-screen EGL window surface and returns a handle to it " */ { EGLint attributes[] = { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE, }; for (EGLint i = 0; i < config_count; i += 1){ ctx.egl_surface = eglCreateWindowSurface(ctx.egl_display, configs[i], ctx.wl_egl_window, attributes); if (ctx.egl_surface != EGL_NO_SURFACE){ break; } } if (ctx.egl_surface == EGL_NO_SURFACE){ printf("eglCreateWindowSurface failed\n"); } } } 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); } EGLBoolean swap_interval_success = 0; if (make_current_success2){ swap_interval_success = eglSwapInterval(ctx.egl_display, 1); if (!swap_interval_success){ printf("eglSwapInterval failed\n"); } } /*~ NOTE: Main loop */ int exit_loop = 0; if (!swap_interval_success){ exit_loop = 1; } for (;!exit_loop;){ for (;g_main_context_iteration(0, 0);){} { 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, ARRAY_LENGTH(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); } } /* re-render cursor */ if (ctx.pointer_leave){ ctx.pointer_leave = 0; ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_INIT; ctx.titlebar_gesture.first_pressed_button = 0; ctx.hdr_focus.widget = 0; ctx.hdr_focus.type = HEADER_NONE; draw_decoration(); wl_surface_commit(ctx.wl_surface); ctx.seat->current_cursor = wl_cursor_from_pos(ctx.seat->pointer_x, ctx.seat->pointer_y); } if (ctx.pointer_enter){ ctx.pointer_enter = 0; if (ctx.active != 0){ draw_decoration(); wl_surface_commit(ctx.wl_surface); } ctx.seat->current_cursor = wl_cursor_from_pos(ctx.seat->pointer_x, ctx.seat->pointer_y); if (ctx.seat->current_cursor != 0){ struct wl_cursor_image *image = ctx.seat->current_cursor->images[0]; struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); wl_surface_set_buffer_scale(ctx.seat->cursor_surface, 1); wl_surface_attach(ctx.seat->cursor_surface, buffer, 0, 0); wl_surface_damage_buffer(ctx.seat->cursor_surface, 0, 0, image->width, image->height); wl_surface_commit(ctx.seat->cursor_surface); wl_pointer_set_cursor(ctx.seat->wl_pointer, ctx.seat->serial, ctx.seat->cursor_surface, image->hotspot_x, image->hotspot_y); } } if (ctx.pointer_motion){ ctx.pointer_motion = 0; ctx.seat->current_cursor = wl_cursor_from_pos(ctx.seat->pointer_x, ctx.seat->pointer_y); if (ctx.seat->current_cursor != 0){ struct wl_cursor_image *image = ctx.seat->current_cursor->images[0]; struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); wl_surface_attach(ctx.seat->cursor_surface, buffer, 0, 0); wl_surface_set_buffer_scale(ctx.seat->cursor_surface, 1); wl_surface_damage_buffer(ctx.seat->cursor_surface, 0, 0, image->width, image->height); wl_surface_commit(ctx.seat->cursor_surface); wl_pointer_set_cursor(ctx.seat->wl_pointer, ctx.seat->serial, ctx.seat->cursor_surface, image->hotspot_x, image->hotspot_y); } if (!GTK_IS_WIDGET(ctx.header) || ctx.active != COMPONENT_SLOT_HEADER){ ctx.hdr_focus.type = HEADER_NONE; } struct header_element_data new_focus = get_header_focus(GTK_HEADER_BAR(ctx.header), ctx.seat->pointer_x, ctx.seat->pointer_y); if (ctx.hdr_focus.widget != new_focus.widget){ ctx.hdr_focus = new_focus; ctx.hdr_state = 0; } ctx.hdr_state |= GTK_STATE_FLAG_PRELIGHT; draw_title_bar(); wl_surface_commit(ctx.wl_surface); if (ctx.titlebar_gesture.state == TITLEBAR_GESTURE_STATE_BUTTON_PRESSED){ if (ctx.titlebar_gesture.first_pressed_button == BTN_LEFT){ int xd = ABS(ctx.seat->pointer_x - ctx.titlebar_gesture.pressed_x); int yd = ABS(ctx.seat->pointer_y - ctx.titlebar_gesture.pressed_y); if (xd > ctx.drag_threshold || yd > ctx.drag_threshold){ xdg_toplevel_move(ctx.xdg_toplevel, ctx.seat->wl_seat, ctx.titlebar_gesture.serial); } } } } if (ctx.pointer_button){ ctx.pointer_button = 0; switch (ctx.active){ case COMPONENT_SLOT_SHADOW: { enum libdecor_resize_edge edge = edge_from_pos(ctx.seat->pointer_x, ctx.seat->pointer_y); if (edge != LIBDECOR_RESIZE_EDGE_NONE && (ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){ xdg_toplevel_resize(ctx.xdg_toplevel, ctx.seat->wl_seat, ctx.seat->serial, xdg_edge_from_edge(edge)); } }break; case COMPONENT_SLOT_HEADER: { switch (ctx.titlebar_gesture.state){ case TITLEBAR_GESTURE_STATE_INIT: { if (ctx.pointer_button_state == WL_POINTER_BUTTON_STATE_PRESSED){ if (ctx.pointer_button_button == BTN_RIGHT){ const int title_height = gtk_widget_get_allocated_height(ctx.header); xdg_toplevel_show_window_menu(ctx.xdg_toplevel, ctx.seat->wl_seat, ctx.seat->serial, ctx.seat->pointer_x, -title_height); ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_CONSUMED; } else{ if (ctx.pointer_button_button == BTN_LEFT && ctx.titlebar_gesture.first_pressed_button == BTN_LEFT && ctx.pointer_button_time - ctx.titlebar_gesture.first_pressed_time < (uint32_t)ctx.double_click_time_ms){ if ((ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){ toggle_maximized(); } ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_CONSUMED; } else{ ctx.titlebar_gesture.first_pressed_button = ctx.pointer_button_button; ctx.titlebar_gesture.first_pressed_time = ctx.pointer_button_time; ctx.titlebar_gesture.pressed_x = ctx.seat->pointer_x; ctx.titlebar_gesture.pressed_y = ctx.seat->pointer_y; ctx.titlebar_gesture.serial = ctx.seat->serial; ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_BUTTON_PRESSED; } } ctx.titlebar_gesture.button_pressed_count = 1; switch (ctx.hdr_focus.type){ case HEADER_MIN: case HEADER_MAX: case HEADER_CLOSE: { ctx.hdr_state |= GTK_STATE_FLAG_ACTIVE; draw_title_bar(); wl_surface_commit(ctx.wl_surface); }break; default: break; } } }break; case TITLEBAR_GESTURE_STATE_BUTTON_PRESSED: { if (ctx.pointer_button_state == WL_POINTER_BUTTON_STATE_PRESSED) { ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_DISCARDED; ctx.titlebar_gesture.button_pressed_count += 1; } else{ ctx.titlebar_gesture.button_pressed_count -= 1; if (ctx.titlebar_gesture.button_pressed_count == 0) { ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_INIT; if (ctx.titlebar_gesture.first_pressed_button == ctx.pointer_button_button && ctx.pointer_button_button == BTN_LEFT){ switch (ctx.hdr_focus.type){ case HEADER_MIN: { if (ctx.frame_capabilities & LIBDECOR_ACTION_MINIMIZE){ xdg_toplevel_set_minimized(ctx.xdg_toplevel); } }break; case HEADER_MAX: { if ((ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){ toggle_maximized(); } }break; case HEADER_CLOSE: { if (ctx.frame_capabilities & LIBDECOR_ACTION_CLOSE){ ctx.close_signal = 1; ctx.seat->pointer_focus = 0; } }break; default: break; } ctx.hdr_state &= ~GTK_STATE_FLAG_ACTIVE; if (GTK_IS_WIDGET(ctx.header)){ draw_title_bar(); wl_surface_commit(ctx.wl_surface); } } } else{ ctx.hdr_state &= ~GTK_STATE_FLAG_ACTIVE; if (GTK_IS_WIDGET(ctx.header)) { draw_title_bar(); wl_surface_commit(ctx.wl_surface); } } } }break; case TITLEBAR_GESTURE_STATE_CONSUMED: case TITLEBAR_GESTURE_STATE_DISCARDED: { if (ctx.pointer_button_state == WL_POINTER_BUTTON_STATE_PRESSED){ ctx.titlebar_gesture.button_pressed_count += 1; } else{ ctx.titlebar_gesture.button_pressed_count -= 1; if (ctx.titlebar_gesture.button_pressed_count == 0) { ctx.titlebar_gesture.state = TITLEBAR_GESTURE_STATE_INIT; ctx.titlebar_gesture.first_pressed_button = 0; } } }break; } }break; default: break; } } /* apply new surface config */ if (ctx.has_cached_config){ ctx.has_cached_config = 0; if (ctx.cached_config.initialized && ctx.cached_config.window_width != 0 && ctx.cached_config.window_height != 0){ int w = ctx.cached_config.window_width; int h = ctx.cached_config.window_height; if (ctx.visible && ctx.decoration_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE){ ctx.frame_window_state = ctx.cached_config.window_state; Sides2D border_size = border_size_from_window_state(ctx.cached_config.window_state); w -= border_size.x[0] + border_size.x[1]; h -= border_size.y[0] + border_size.y[1]; } if (!(ctx.cached_config.window_state & LIBDECOR_WINDOW_STATE_NON_FLOATING)){ if (ctx.size_bounds.x[0] > 0){ w = MAX(ctx.size_bounds.x[0], w); } if (ctx.size_bounds.x[1] > 0){ w = MIN(w, ctx.size_bounds.x[1]); } if (ctx.size_bounds.y[0] > 0){ h = MAX(ctx.size_bounds.y[0], h); } if (ctx.size_bounds.y[1] > 0){ h = MIN(h, ctx.size_bounds.y[1]); } } ctx.w = w; ctx.h = h; } if (ctx.cached_config.initialized){ ctx.frame_window_state = ctx.cached_config.window_state; } ctx.frame_content_width = ctx.w; ctx.frame_content_height = ctx.h; libdecor_frame_commit(); xdg_surface_ack_configure(ctx.xdg_surface, ctx.cached_config.serial); } wl_egl_window_resize(ctx.wl_egl_window, ctx.w, ctx.h, 0, 0); { glDrawBuffer(GL_BACK); glViewport(0, 0, 640, 480); glClearColor(0.40f, 0.90f, 0.15f, 1.f); glClear(GL_COLOR_BUFFER_BIT); } if (ctx.close_signal){ exit_loop = 1; } EGLBoolean swap_success = eglSwapBuffers(ctx.egl_display, ctx.egl_surface); if (!swap_success){ printf("eglSwapBuffers failed\n"); } } if (ctx.wl_display != 0){ wl_display_disconnect(ctx.wl_display); } return(0); } /*libdecor.so, libdecor-gtk.so*/ /* * Copyright © 2012 Collabora, Ltd. * Copyright © 2012 Intel Corporation * Copyright © 2017-2018 Red Hat Inc. * Copyright © 2018 Jonas Ådahl * Copyright © 2021 Christian Rauch * Copyright © 2024 Colin Kinloch * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ //#include "libdecor.c" static enum decoration_type decoration_type_from_window_state(enum libdecor_window_state window_state){ enum decoration_type result = DECORATION_TYPE_ALL; if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN){ result = DECORATION_TYPE_NONE; } else if (window_state & (LIBDECOR_WINDOW_STATE_MAXIMIZED | LIBDECOR_WINDOW_STATE_TILED_LEFT | LIBDECOR_WINDOW_STATE_TILED_RIGHT | LIBDECOR_WINDOW_STATE_TILED_TOP | LIBDECOR_WINDOW_STATE_TILED_BOTTOM)){ result = DECORATION_TYPE_TITLE_ONLY; } return(result); } static enum xdg_toplevel_resize_edge xdg_edge_from_edge(enum libdecor_resize_edge edge){ enum xdg_toplevel_resize_edge result = XDG_TOPLEVEL_RESIZE_EDGE_NONE; switch (edge) { default: case LIBDECOR_RESIZE_EDGE_NONE: break; case LIBDECOR_RESIZE_EDGE_TOP: result = XDG_TOPLEVEL_RESIZE_EDGE_TOP; break; case LIBDECOR_RESIZE_EDGE_BOTTOM: result = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; break; case LIBDECOR_RESIZE_EDGE_LEFT: result = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; break; case LIBDECOR_RESIZE_EDGE_TOP_LEFT: result = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; break; case LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT: result = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; break; case LIBDECOR_RESIZE_EDGE_RIGHT: result = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; break; case LIBDECOR_RESIZE_EDGE_TOP_RIGHT: result = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; break; case LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT: result = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; break; } return(result); } void libdecor_frame_commit(void){ bool csd = false; if (ctx.visible && ctx.decoration_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE){ csd = true; } Sides2D border_size = {0}; if (csd){ border_size = border_size_from_window_state(ctx.frame_window_state); } if (!(ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){ int mw = ctx.frame_content_width; int mh = ctx.frame_content_height; xdg_toplevel_set_min_size(ctx.xdg_toplevel, mw, mh); xdg_toplevel_set_max_size(ctx.xdg_toplevel, mw, mh); } else{ int csd_added_w = 0; int csd_added_h = 0; if (csd){ csd_added_w = border_size.x[0] + border_size.x[1]; csd_added_h = border_size.y[0] + border_size.y[1]; } for (int i = 0; i < 2; i += 1){ int mw = 0; int mh = 0; if (ctx.size_bounds.x[i] > 0 && ctx.size_bounds.y[i] > 0){ mw = ctx.size_bounds.x[i] + csd_added_w; mh = ctx.size_bounds.y[i] + csd_added_h; } if (i == 0){ xdg_toplevel_set_min_size(ctx.xdg_toplevel, mw, mh); } else{ xdg_toplevel_set_max_size(ctx.xdg_toplevel, mw, mh); } } } if (csd){ ctx.gtk_content_width = ctx.frame_content_width; ctx.gtk_content_height = ctx.frame_content_height; ctx.gtk_window_state = ctx.frame_window_state; ctx.decoration_type = decoration_type_from_window_state(ctx.frame_window_state); draw_decoration(); } else{ g_clear_pointer(&ctx.header, gtk_widget_destroy); g_clear_pointer(&ctx.window, gtk_widget_destroy); for (int i = 1; i < COMPONENT_SLOT_COUNT; i += 1){ struct border_component * border_component = &ctx.component_slot[i]; if (border_component->wl_subsurface != 0){ wl_subsurface_destroy(border_component->wl_subsurface); border_component->wl_subsurface = 0; } if (border_component->wl_surface != 0){ wl_surface_destroy(border_component->wl_surface); border_component->wl_surface = 0; } if (border_component->wl_buffer != 0){ wl_buffer_destroy(border_component->wl_buffer); border_component->wl_buffer = 0; } if (border_component->data != 0){ munmap(border_component->data, border_component->data_size); border_component->data = 0; } border_component->data_size = 0; border_component->width = 0; border_component->height = 0; } ctx.shadow_showing = false; g_clear_pointer(&ctx.title, free); ctx.decoration_type = DECORATION_TYPE_NONE; } { Extent2D extent = {0}; extent.w = ctx.frame_content_width; extent.h = ctx.frame_content_height; if (csd){ extent.x = -border_size.x[0]; extent.y = -border_size.y[0]; extent.w += border_size.x[0] + border_size.x[1]; extent.h += border_size.y[0] + border_size.y[1]; } xdg_surface_set_window_geometry(ctx.xdg_surface, extent.x, extent.y, extent.w, extent.h); } } void cleanup(void){ { struct seat *seat, *seat_tmp; wl_list_for_each_safe(seat, seat_tmp, &ctx.seat_list, link) { if (seat->wl_pointer){ wl_pointer_destroy(seat->wl_pointer); } wl_surface_destroy(seat->cursor_surface); wl_seat_destroy(seat->wl_seat); if (ctx.cursor_theme != 0){ wl_cursor_theme_destroy(ctx.cursor_theme); } if (seat->name != 0){ free(seat->name); } free(seat); } } if (ctx.wl_shm){ wl_shm_destroy(ctx.wl_shm); } if (ctx.wl_subcompositor != 0){ wl_subcompositor_destroy(ctx.wl_subcompositor); } if (ctx.xdg_wm_base != 0){ xdg_wm_base_destroy(ctx.xdg_wm_base); } if (ctx.decoration_manager != 0){ zxdg_decoration_manager_v1_destroy(ctx.decoration_manager); } } //#include "libdecor-cairo-blur.c" int blur_surface(cairo_surface_t *surface, int margin){ int32_t width, height, stride, x, y, z, w; uint8_t *src, *dst; uint32_t *s, *d, a, p; int i, j, k, size, half; uint32_t kernel[71]; double f; size = ARRAY_LENGTH(kernel); width = cairo_image_surface_get_width(surface); height = cairo_image_surface_get_height(surface); stride = cairo_image_surface_get_stride(surface); src = cairo_image_surface_get_data(surface); dst = malloc(height * stride); if (dst == NULL) return -1; half = size / 2; a = 0; for (i = 0; i < size; i++) { f = (i - half); kernel[i] = exp(- f * f / ARRAY_LENGTH(kernel)) * 10000; a += kernel[i]; } for (i = 0; i < height; i++) { s = (uint32_t *) (src + i * stride); d = (uint32_t *) (dst + i * stride); for (j = 0; j < width; j++) { if (margin < j && j < width - margin) { d[j] = s[j]; continue; } x = 0; y = 0; z = 0; w = 0; for (k = 0; k < size; k++) { if (j - half + k < 0 || j - half + k >= width) continue; p = s[j - half + k]; x += (p >> 24) * kernel[k]; y += ((p >> 16) & 0xff) * kernel[k]; z += ((p >> 8) & 0xff) * kernel[k]; w += (p & 0xff) * kernel[k]; } d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; } } for (i = 0; i < height; i++) { s = (uint32_t *) (dst + i * stride); d = (uint32_t *) (src + i * stride); for (j = 0; j < width; j++) { if (margin <= i && i < height - margin) { d[j] = s[j]; continue; } x = 0; y = 0; z = 0; w = 0; for (k = 0; k < size; k++) { if (i - half + k < 0 || i - half + k >= height) continue; s = (uint32_t *) (dst + (i - half + k) * stride); p = s[j]; x += (p >> 24) * kernel[k]; y += ((p >> 16) & 0xff) * kernel[k]; z += ((p >> 8) & 0xff) * kernel[k]; w += (p & 0xff) * kernel[k]; } d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; } } free(dst); cairo_surface_mark_dirty(surface); return 0; } void render_shadow(cairo_t *cr, cairo_surface_t *surface, int x, int y, int width, int height, int margin, int top_margin){ cairo_pattern_t *pattern; cairo_matrix_t matrix; int i, fx, fy, shadow_height, shadow_width; cairo_set_source_rgba(cr, 0, 0, 0, 0.45); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); pattern = cairo_pattern_create_for_surface (surface); cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); for (i = 0; i < 4; i++) { /* when fy is set, then we are working with lower corners, * when fx is set, then we are working with right corners * * 00 ------- 01 * | | * | | * 10 ------- 11 */ fx = i & 1; fy = i >> 1; cairo_matrix_init_translate(&matrix, -x + fx * (128 - width), -y + fy * (128 - height)); cairo_pattern_set_matrix(pattern, &matrix); shadow_width = margin; shadow_height = fy ? margin : top_margin; /* if the shadows together are greater than the surface, we need * to fix it - set the shadow size to the half of * the size of surface. Also handle the case when the size is * not divisible by 2. In that case we need one part of the * shadow to be one pixel greater. !fy or !fx, respectively, * will do the work. */ if (height < 2 * shadow_height) shadow_height = (height + !fy) / 2; if (width < 2 * shadow_width) shadow_width = (width + !fx) / 2; cairo_reset_clip(cr); cairo_rectangle(cr, x + fx * (width - shadow_width), y + fy * (height - shadow_height), shadow_width, shadow_height); cairo_clip (cr); cairo_mask(cr, pattern); } shadow_width = width - 2 * margin; shadow_height = top_margin; if (height < 2 * shadow_height) shadow_height = height / 2; if (shadow_width > 0 && shadow_height) { /* Top stretch */ cairo_matrix_init_translate(&matrix, 60, 0); cairo_matrix_scale(&matrix, 8.0 / width, 1); cairo_matrix_translate(&matrix, -x - width / 2, -y); cairo_pattern_set_matrix(pattern, &matrix); cairo_rectangle(cr, x + margin, y, shadow_width, shadow_height); cairo_reset_clip(cr); cairo_rectangle(cr, x + margin, y, shadow_width, shadow_height); cairo_clip (cr); cairo_mask(cr, pattern); /* Bottom stretch */ cairo_matrix_translate(&matrix, 0, -height + 128); cairo_pattern_set_matrix(pattern, &matrix); cairo_reset_clip(cr); cairo_rectangle(cr, x + margin, y + height - margin, shadow_width, margin); cairo_clip (cr); cairo_mask(cr, pattern); } shadow_width = margin; if (width < 2 * shadow_width) shadow_width = width / 2; shadow_height = height - margin - top_margin; /* if height is smaller than sum of margins, * then the shadow is already done by the corners */ if (shadow_height > 0 && shadow_width) { /* Left stretch */ cairo_matrix_init_translate(&matrix, 0, 60); cairo_matrix_scale(&matrix, 1, 8.0 / height); cairo_matrix_translate(&matrix, -x, -y - height / 2); cairo_pattern_set_matrix(pattern, &matrix); cairo_reset_clip(cr); cairo_rectangle(cr, x, y + top_margin, shadow_width, shadow_height); cairo_clip (cr); cairo_mask(cr, pattern); /* Right stretch */ cairo_matrix_translate(&matrix, -width + 128, 0); cairo_pattern_set_matrix(pattern, &matrix); cairo_rectangle(cr, x + width - shadow_width, y + top_margin, shadow_width, shadow_height); cairo_reset_clip(cr); cairo_clip (cr); cairo_mask(cr, pattern); } cairo_pattern_destroy(pattern); cairo_reset_clip(cr); } //#include "os-compatibility.c" #ifndef HAVE_MKOSTEMP static int set_cloexec_or_close(int fd){ bool error = 0; long flags; if (fd >= 0){ if (fcntl(fd, F_GETFD) == -1){ error = 1; } else if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1){ error = 1; } if (error){ close(fd); fd = -1; } } return 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 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 } int libdecor_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); fd = create_tmpfile_cloexec(name); free(name); } } } if (fd >= 0 && os_resize_anonymous_file(fd, size) < 0){ close(fd); fd = -1; } return(fd); } //#include "plugins/gtk/libdecor-gtk.c" struct find_widget_variables{ char *name; GtkWidget *widget; } find_widget_variables; static void fill_widget_from_name(GtkWidget *widget, void *data){ struct find_widget_variables *vars = data; if (vars->widget == 0){ bool match = false; if (GTK_IS_WIDGET(widget)){ GtkStyleContext *style_context = gtk_widget_get_style_context(widget); char *style_str = gtk_style_context_to_string(style_context, GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE); if (strstr(style_str, vars->name) != 0){ vars->widget = widget; match = true; } free(style_str); } if (!match && GTK_IS_CONTAINER(widget)){ gtk_container_forall(GTK_CONTAINER(widget), &fill_widget_from_name, data); } } } static GtkWidget* find_widget_by_type(GtkWidget *root, enum header_element type){ struct find_widget_variables vars = {0}; switch (type){ case HEADER_FULL: vars.name = "headerbar.titlebar:"; break; case HEADER_TITLE: vars.name = "label.title:"; break; case HEADER_MIN: vars.name = ".minimize"; break; case HEADER_MAX: vars.name = ".maximize"; break; case HEADER_CLOSE: vars.name = ".close"; break; default:break; } fill_widget_from_name(root, &vars); return(vars.widget); } static struct header_element_data get_header_focus(const GtkHeaderBar *header_bar, int x, int y){ static const enum header_element elems[] = { HEADER_TITLE, HEADER_MIN, HEADER_MAX, HEADER_CLOSE }; struct header_element_data result = {0}; for (size_t i = 0; i < ARRAY_LENGTH(elems); i += 1){ GtkWidget *widget = find_widget_by_type(GTK_WIDGET(header_bar), elems[i]); if (widget != 0){ GtkAllocation allocation; gtk_widget_get_allocation(GTK_WIDGET(widget), &allocation); if (allocation.x <= x && x < allocation.x + allocation.width && allocation.y <= y && y < allocation.y + allocation.height){ result.type = elems[i]; result.widget = widget; break; } } } return(result); } static bool own_proxy(void *proxy){ bool result = false; if (proxy != 0){ result = (wl_proxy_get_tag((struct wl_proxy*)proxy) == &libdecor_gtk_proxy_tag); } return(result); } static void toggle_maximized(void){ if (ctx.frame_window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED){ xdg_toplevel_unset_maximized(ctx.xdg_toplevel); } else{ xdg_toplevel_set_maximized(ctx.xdg_toplevel); } } static void hide_border_component(struct border_component *border_component){ if (border_component->wl_surface){ wl_surface_attach(border_component->wl_surface, 0, 0, 0); wl_surface_commit(border_component->wl_surface); } } static enum component_slot component_slot_from_wl_surface(const struct wl_surface *surface){ enum component_slot result = 0; for (int i = 1; i < COMPONENT_SLOT_COUNT; i += 1){ if (ctx.component_slot[i].wl_surface == surface){ result = i; break; } } return(result); } static void ensure_component(struct border_component *cmpnt){ if (cmpnt->wl_surface == 0){ cmpnt->wl_surface = wl_compositor_create_surface(ctx.wl_compositor); wl_proxy_set_tag((struct wl_proxy *)cmpnt->wl_surface, &libdecor_gtk_proxy_tag); cmpnt->wl_subsurface = wl_subcompositor_get_subsurface(ctx.wl_subcompositor, cmpnt->wl_surface, ctx.wl_surface); } } static void ensure_title_bar_surfaces(void){ GtkStyleContext *context_hdr; ctx.component_slot[COMPONENT_SLOT_HEADER].opaque = false; ensure_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]); if (ctx.component_slot[COMPONENT_SLOT_SHADOW].wl_surface){ wl_subsurface_place_above(ctx.component_slot[COMPONENT_SLOT_HEADER].wl_subsurface, ctx.component_slot[COMPONENT_SLOT_SHADOW].wl_surface); } if (GTK_IS_WIDGET(ctx.header)){ gtk_widget_destroy(ctx.header); ctx.header = 0; } if (GTK_IS_WIDGET(ctx.window)){ gtk_widget_destroy(ctx.window); ctx.window = 0; } ctx.window = gtk_offscreen_window_new(); ctx.header = gtk_header_bar_new(); g_object_get(gtk_widget_get_settings(ctx.window), "gtk-double-click-time", &ctx.double_click_time_ms, "gtk-dnd-drag-threshold", &ctx.drag_threshold, NULL); g_object_set(ctx.header, "title", ctx.title, "has-subtitle", FALSE, "show-close-button", TRUE, NULL); context_hdr = gtk_widget_get_style_context(ctx.header); gtk_style_context_add_class(context_hdr, GTK_STYLE_CLASS_TITLEBAR); gtk_style_context_add_class(context_hdr, "default-decoration"); gtk_window_set_titlebar(GTK_WINDOW(ctx.window), ctx.header); gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(ctx.header), TRUE); gtk_window_set_resizable(GTK_WINDOW(ctx.window), (ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE) != 0); } static Extent2D extent2d_from_component_slot(enum component_slot slot){ Extent2D result = {0}; int width = ctx.frame_content_width; int height = ctx.frame_content_height; int title_height = 0; if (GTK_IS_WIDGET(ctx.header)){ title_height = gtk_widget_get_allocated_height(ctx.header); } switch (slot){ default: case COMPONENT_SLOT_NONE: break; case COMPONENT_SLOT_SHADOW: { result.x = -(int)SHADOW_MARGIN; result.y = -(int)(SHADOW_MARGIN + title_height); result.w = width + 2*SHADOW_MARGIN; result.h = title_height + height + 2*SHADOW_MARGIN; }break; case COMPONENT_SLOT_HEADER: { result.x = 0; result.y = -title_height; result.w = gtk_widget_get_allocated_width(ctx.header); result.h = title_height; }break; } return(result); } static void array_append(enum header_element **array, size_t *n, enum header_element item){ (*n)++; *array = realloc(*array, (*n) * sizeof (enum header_element)); (*array)[(*n)-1] = item; } static void draw_header_button(cairo_t *cr, cairo_surface_t *surface, enum header_element button_type){ GtkWidget *button; GtkAllocation allocation; gchar *icon_name; int scale; GtkWidget *icon_widget; GtkAllocation allocation_icon; GtkIconInfo* icon_info; double sx, sy; gint icon_width, icon_height; GdkPixbuf* icon_pixbuf; cairo_surface_t* icon_surface; gint width = 0, height = 0; gint left = 0, top = 0, right = 0, bottom = 0; GtkBorder border; GtkBorder padding; button = find_widget_by_type(ctx.header, button_type); if (button){ GtkStyleContext *button_style = gtk_widget_get_style_context(button); GtkStateFlags style_state = 0; /* change style based on window state and focus */ if (!(ctx.frame_window_state & LIBDECOR_WINDOW_STATE_ACTIVE)){ style_state |= GTK_STATE_FLAG_BACKDROP; } if (ctx.hdr_focus.widget == button){ style_state |= GTK_STATE_FLAG_PRELIGHT; if (ctx.hdr_state & GTK_STATE_FLAG_ACTIVE){ style_state |= GTK_STATE_FLAG_ACTIVE; } } /* background */ gtk_widget_get_clip(button, &allocation); gtk_style_context_save(button_style); gtk_style_context_set_state(button_style, style_state); gtk_render_background(button_style, cr, allocation.x, allocation.y, allocation.width, allocation.height); gtk_render_frame(button_style, cr, allocation.x, allocation.y, allocation.width, allocation.height); gtk_style_context_restore(button_style); /* symbol */ switch (button_type) { case HEADER_MIN: { icon_name = "window-minimize-symbolic"; }break; case HEADER_MAX:{ icon_name = (ctx.frame_window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) ? "window-restore-symbolic" : "window-maximize-symbolic"; }break; case HEADER_CLOSE: { icon_name = "window-close-symbolic"; }break; default: { icon_name = NULL; }break; } /* get scale */ cairo_surface_get_device_scale(surface, &sx, &sy); 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); /* icon info */ if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &icon_width, &icon_height)) { icon_width = 16; icon_height = 16; } icon_info = gtk_icon_theme_lookup_icon_for_scale(gtk_icon_theme_get_default(), icon_name, icon_width, scale, (GtkIconLookupFlags)0); /* icon pixel buffer*/ gtk_style_context_save(button_style); gtk_style_context_set_state(button_style, style_state); icon_pixbuf = gtk_icon_info_load_symbolic_for_context( icon_info, button_style, NULL, NULL); icon_surface = gdk_cairo_surface_create_from_pixbuf(icon_pixbuf, scale, NULL); gtk_style_context_restore(button_style); /* dimensions and position */ gtk_style_context_get(button_style, gtk_style_context_get_state(button_style), "min-width", &width, "min-height", &height, NULL); if (width < icon_width){ width = icon_width; } if (height < icon_height){ height = icon_height; } gtk_style_context_get_border(button_style, gtk_style_context_get_state(button_style), &border); left += border.left; right += border.right; top += border.top; bottom += border.bottom; gtk_style_context_get_padding(button_style, gtk_style_context_get_state(button_style), &padding); left += padding.left; right += padding.right; top += padding.top; bottom += padding.bottom; width += left + right; height += top + bottom; gtk_render_icon_surface(gtk_widget_get_style_context(icon_widget), cr, icon_surface, allocation.x + ((width - icon_width) / 2), allocation.y + ((height - icon_height) / 2)); cairo_paint(cr); cairo_surface_destroy(icon_surface); g_object_unref(icon_pixbuf); } } static void draw_border_component(enum component_slot slot){ if (slot < COMPONENT_SLOT_COUNT && ctx.component_slot[slot].wl_surface != 0){ struct border_component *component = &ctx.component_slot[slot]; if (slot == COMPONENT_SLOT_SHADOW && ctx.shadow_showing){ struct wl_region *input_region; Extent2D extent = extent2d_from_component_slot(COMPONENT_SLOT_SHADOW); input_region = wl_compositor_create_region(ctx.wl_compositor); wl_region_add(input_region, 0, 0, extent.w, extent.h); wl_region_subtract(input_region, -extent.x, -extent.y, ctx.frame_content_width, ctx.frame_content_height); wl_surface_set_input_region(component->wl_surface, input_region); wl_region_destroy(input_region); } { if (component->wl_buffer != 0){ wl_buffer_destroy(component->wl_buffer); component->wl_buffer = 0; } if (component->data != 0){ munmap(component->data, component->data_size); component->data = 0; } component->data_size = 0; component->width = 0; component->height = 0; } Extent2D extent = extent2d_from_component_slot(slot); { int width = extent.w; int height = extent.h; int stride = 4*width; int size = stride*height; int fd = libdecor_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){ enum wl_shm_format fmt = (ctx.component_slot[slot].opaque ? WL_SHM_FORMAT_XRGB8888 : WL_SHM_FORMAT_ARGB8888); 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, width, height, stride, fmt); wl_shm_pool_destroy(pool); component->wl_buffer = wl_buffer; component->data = data; component->data_size = size; component->width = width; component->height = height; } close(fd); } } memset(component->data, 0, component->data_size); { int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, component->width); cairo_surface_t *surface = cairo_image_surface_create_for_data(component->data, CAIRO_FORMAT_ARGB32, component->width, component->height, stride); cairo_t *cr = cairo_create(surface); cairo_surface_set_device_scale(surface, 1, 1); switch (slot){ default:break; case COMPONENT_SLOT_SHADOW: { render_shadow(cr, ctx.shadow_blur, -(int)SHADOW_MARGIN/2, -(int)SHADOW_MARGIN/2, component->width + SHADOW_MARGIN, component->height + SHADOW_MARGIN, 64, 64); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_rectangle(cr, -extent.x, -extent.y, ctx.frame_content_width, ctx.frame_content_height); cairo_fill(cr); }break; case COMPONENT_SLOT_HEADER: { /* background */ { GtkAllocation allocation; gtk_widget_get_allocation(GTK_WIDGET(ctx.header), &allocation); GtkStyleContext* style = gtk_widget_get_style_context(ctx.header); gtk_render_background(style, cr, allocation.x, allocation.y, allocation.width, allocation.height); } /* title */ { GtkWidget *label = find_widget_by_type(ctx.header, HEADER_TITLE); GtkAllocation allocation; gtk_widget_get_allocation(label, &allocation); cairo_surface_t *label_surface = cairo_surface_create_for_rectangle(surface, allocation.x, allocation.y, allocation.width, allocation.height); cairo_t *cr2 = cairo_create(label_surface); gtk_widget_size_allocate(label, &allocation); gtk_widget_draw(label, cr2); cairo_destroy(cr2); cairo_surface_destroy(label_surface); } /* buttons */ { enum header_element buttons[3] = {0}; size_t nbuttons = 0; if ((ctx.frame_capabilities & LIBDECOR_ACTION_MINIMIZE)){ buttons[nbuttons] = HEADER_MIN; nbuttons += 1; } if ((ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){ buttons[nbuttons] = HEADER_MAX; nbuttons += 1; } if ((ctx.frame_capabilities & LIBDECOR_ACTION_CLOSE)){ buttons[nbuttons] = HEADER_CLOSE; nbuttons += 1; } for (int i = 0; i < nbuttons; i += 1){ enum header_element button_type = buttons[i]; draw_header_button(cr, surface, button_type); } } }break; } cairo_destroy(cr); cairo_surface_destroy(surface); } struct wl_surface *component_surface = ctx.component_slot[slot].wl_surface; struct wl_subsurface *component_subsurface = ctx.component_slot[slot].wl_subsurface; wl_surface_attach(component_surface, component->wl_buffer, 0, 0); wl_surface_set_buffer_scale(component_surface, 1); wl_surface_commit(component_surface); wl_surface_damage_buffer(component_surface, 0, 0, extent.w, extent.h); wl_subsurface_set_position(component_subsurface, extent.x, extent.y); } } static void draw_title_bar(void){ enum libdecor_window_state state = ctx.frame_window_state; if (!(state & LIBDECOR_WINDOW_STATE_ACTIVE)){ gtk_widget_set_state_flags(ctx.window, GTK_STATE_FLAG_BACKDROP, true); } else{ gtk_widget_unset_state_flags(ctx.window, GTK_STATE_FLAG_BACKDROP); } GtkStyleContext *style = gtk_widget_get_style_context(ctx.window); if (!(ctx.frame_window_state & LIBDECOR_WINDOW_STATE_NON_FLOATING)){ gtk_style_context_remove_class(style, "maximized"); } else{ gtk_style_context_add_class(style, "maximized"); } gtk_widget_show_all(ctx.window); int pref_width; { gtk_header_bar_set_title(GTK_HEADER_BAR(ctx.header), ""); gtk_widget_get_preferred_width(ctx.header, NULL, &pref_width); gtk_header_bar_set_title(GTK_HEADER_BAR(ctx.header), ctx.title); } ctx.size_bounds.x[0] = CLAMP_BOT(ctx.size_bounds.x[0], pref_width); if (ctx.size_bounds.x[1] != 0){ ctx.size_bounds.x[1] = CLAMP_BOT(ctx.size_bounds.x[1], ctx.size_bounds.x[0]); } int w = ctx.frame_content_width; int h = ctx.frame_content_height; if (w < ctx.size_bounds.x[0]){ w = ctx.size_bounds.x[0]; ctx.frame_content_width = w; ctx.frame_content_height = h; libdecor_frame_commit(); } else{ GtkAllocation allocation = {0, 0, ctx.gtk_content_width, 0}; gtk_widget_get_preferred_height(ctx.header, 0, &allocation.height); gtk_widget_size_allocate(ctx.header, &allocation); draw_border_component(COMPONENT_SLOT_HEADER); } } static void draw_decoration(void){ switch (ctx.decoration_type){ case DECORATION_TYPE_NONE: { hide_border_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]); ctx.shadow_showing = false; hide_border_component(&ctx.component_slot[COMPONENT_SLOT_HEADER]); }break; case DECORATION_TYPE_ALL: { ctx.component_slot[COMPONENT_SLOT_SHADOW].opaque = false; ensure_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]); draw_border_component(COMPONENT_SLOT_SHADOW); ctx.shadow_showing = true; ensure_title_bar_surfaces(); draw_title_bar(); }break; case DECORATION_TYPE_TITLE_ONLY: { hide_border_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]); ctx.shadow_showing = false; ensure_title_bar_surfaces(); draw_title_bar(); }break; } } static Sides2D border_size_from_window_state(enum libdecor_window_state window_state){ Sides2D border_size = {0}; switch (decoration_type_from_window_state(window_state)) { case DECORATION_TYPE_NONE: break; case DECORATION_TYPE_ALL: { ctx.component_slot[COMPONENT_SLOT_SHADOW].opaque = false; ensure_component(&ctx.component_slot[COMPONENT_SLOT_SHADOW]); } G_GNUC_FALLTHROUGH; case DECORATION_TYPE_TITLE_ONLY: { if (ctx.header == 0){ ensure_title_bar_surfaces(); } gtk_widget_show_all(ctx.window); gtk_widget_get_preferred_height(ctx.header, 0, &border_size.y[0]); }break; } return(border_size); } enum libdecor_resize_edge edge_from_pos(int x, int y){ static const enum libdecor_resize_edge box[9] = { LIBDECOR_RESIZE_EDGE_TOP_LEFT , LIBDECOR_RESIZE_EDGE_TOP , LIBDECOR_RESIZE_EDGE_TOP_RIGHT , LIBDECOR_RESIZE_EDGE_LEFT, LIBDECOR_RESIZE_EDGE_NONE , LIBDECOR_RESIZE_EDGE_RIGHT , LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT, LIBDECOR_RESIZE_EDGE_BOTTOM, LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT, }; enum libdecor_resize_edge result = 0; struct border_component *component = &ctx.component_slot[COMPONENT_SLOT_SHADOW]; if (component->data != 0){ int w = component->width; int h = component->height; int top = (y < 2*SHADOW_MARGIN); int bottom = (!top && y > (h - 2*SHADOW_MARGIN)); int left = (x < 2*SHADOW_MARGIN); int right = (!left && x > (w - 2*SHADOW_MARGIN)); int i = 4 + 3*(bottom - top) + (right - left); result = box[i]; } return(result); } static struct wl_cursor * wl_cursor_from_pos(int x, int y){ struct wl_cursor *result = ctx.cursor_left_ptr; if (ctx.active == COMPONENT_SLOT_SHADOW && (ctx.frame_capabilities & LIBDECOR_ACTION_RESIZE)){ enum libdecor_resize_edge edge = edge_from_pos(x, y); if (edge != LIBDECOR_RESIZE_EDGE_NONE){ result = ctx.cursors[edge - 1]; } } return(result); } //#include "desktop-settings.c" static bool get_cursor_settings_from_env(char **theme, int *size){ char *env_xtheme = getenv("XCURSOR_THEME"); char *env_xsize = getenv("XCURSOR_SIZE"); bool got_theme = (env_xtheme != 0 && env_xsize != 0); if (got_theme){ *theme = strdup(env_xtheme); *size = atoi(env_xsize); } return(got_theme); } #ifdef HAS_DBUS #include static DBusMessage * get_setting_sync(DBusConnection *const connection, const char *key, const char *value){ DBusMessage *reply = 0; DBusMessage *message; 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, &key, DBUS_TYPE_STRING, &value, 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 bool parse_type(DBusMessage *const reply, const int type, void *value){ bool result = false; 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 = true; } } } return(result); } bool libdecor_get_cursor_settings(char **theme, int *size){ static const char name[] = "org.gnome.desktop.interface"; static const char key_theme[] = "cursor-theme"; static const char key_size[] = "cursor-size"; bool got_theme = false; const char *value_theme = 0; DBusError error; DBusConnection *connection; dbus_error_init(&error); connection = dbus_bus_get(DBUS_BUS_SESSION, &error); if (!dbus_error_is_set(&error)){ DBusMessage *reply = get_setting_sync(connection, name, key_theme); if (reply != 0){ if (parse_type(reply, DBUS_TYPE_STRING, &value_theme)) { *theme = strdup(value_theme); } dbus_message_unref(reply); } } if (value_theme != 0){ DBusMessage *reply = get_setting_sync(connection, name, key_size); if (reply){ if (parse_type(reply, DBUS_TYPE_INT32, size)){ got_theme = true; } dbus_message_unref(reply); } } if (!got_theme){ got_theme = get_cursor_settings_from_env(theme, size); } return(got_theme); } enum libdecor_color_scheme libdecor_get_color_scheme(){ static const char name[] = "org.freedesktop.appearance"; static const char key_color_scheme[] = "color-scheme"; uint32_t color = 0; DBusError error; DBusConnection *connection; DBusMessage *reply; dbus_error_init(&error); connection = dbus_bus_get(DBUS_BUS_SESSION, &error); if (!dbus_error_is_set(&error)){ reply = get_setting_sync(connection, name, key_color_scheme); if (reply){ if (!parse_type(reply, DBUS_TYPE_UINT32, &color)) { color = 0; } dbus_message_unref(reply); } } return(color); } #else bool libdecor_get_cursor_settings(char **theme, int *size){ return(get_cursor_settings_from_env(theme, size)); } uint32_t libdecor_get_color_scheme(){ return(LIBDECOR_COLOR_SCHEME_DEFAULT); } #endif