#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 }; #define SHADOW_MARGIN 20 #define SHADOW_THICK 10 static void pointer_enter(void *udata, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t fx, wl_fixed_t fy){ int x = ctx.p[0] = wl_fixed_to_int(fx); int y = ctx.p[1] = wl_fixed_to_int(fy); ctx.hover = surface; if (surface == ctx.main_wl_surface){ printf("pointer_enter (main) %d,%d\n", x, y); } else if (surface == ctx.shadow_wl_surface){ printf("pointer_enter (shadow) %d,%d\n", x, y); } else{ printf("pointer_enter (unidentified) %d,%d\n", x, y); ctx.hover = 0; } ctx.hover_serial = serial; } static void pointer_leave(void *udata, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface){ if (surface == ctx.main_wl_surface){ printf("pointer_leave (main)\n"); } else if (surface == ctx.shadow_wl_surface){ printf("pointer_leave (shadow)\n"); } else{ printf("pointer_leave (unidentified)\n"); } ctx.hover = 0; } static void pointer_motion(void *udata, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t fx, wl_fixed_t fy){ int x = ctx.p[0] = wl_fixed_to_int(fx); int y = ctx.p[1] = wl_fixed_to_int(fy); printf("pointer_motion %d,%d\n", x, y); } static void pointer_button(void *udata, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state){ char *state_str = (state == 1?"press":"release"); if (button == BTN_LEFT){ printf("pointer_button (left) %s\n", state_str); } else if (button == BTN_RIGHT){ printf("pointer_button (right) %s\n", state_str); } else if (button == BTN_MIDDLE){ printf("pointer_button (middle) %s\n", state_str); } else{ printf("pointer_button (unidentified) %s\n", state_str); } if (state){ ctx.button = button; ctx.button_serial = serial; } } static void pointer_axis(void *udata, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t fv){ int v = wl_fixed_to_int(fv); printf("pointer_axis %u,%d\n", axis, v); } 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.control_flags = ~0; ctx.dim[0] = 640; ctx.dim[1] = 480; 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); /* window egl */ ctx.main_wl_egl_window = wl_egl_window_create(ctx.main_wl_surface, ctx.dim[0], ctx.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] = {0}; { 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]; } for (int i = 0; i < 2; i += 1){ int32_t d = ctx.config.dim[i] - csd_dim[i]; if (!ctx.handled_first_size){ d = ctx.dim[i]; ctx.config.dim[i] = d + 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; } ctx.handled_first_size = 1; } /* frame cursor & interaction */ CursorShape cursor_shape = CursorShape_Pointer; if (ctx.control_flags & WindowControlFlag_Resize){ static const CursorShape cursor_box[] = { CursorShape_Resize_TopLeft, CursorShape_Resize_Top, CursorShape_Resize_TopRight, CursorShape_Resize_Left, CursorShape_Pointer, CursorShape_Resize_Right, CursorShape_Resize_BottomLeft, CursorShape_Resize_Bottom, CursorShape_Resize_BottomRight, }; static const enum xdg_toplevel_resize_edge xedge_box[] = { XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_TOP, XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT, XDG_TOPLEVEL_RESIZE_EDGE_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_NONE, XDG_TOPLEVEL_RESIZE_EDGE_RIGHT, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT, }; int l = (ctx.p[0] < SHADOW_MARGIN); int r = (!l && ctx.p[0] >= ctx.dim[0] + SHADOW_MARGIN); int t = (ctx.p[1] < SHADOW_MARGIN); int b = (!t && ctx.p[1] >= ctx.dim[1] + SHADOW_MARGIN); int loc = 3*(b - t) + (r - l); cursor_shape = cursor_box[4 + loc]; if (ctx.button == BTN_LEFT){ ctx.button = 0; if (loc != 0){ xdg_toplevel_resize(ctx.main_xdg_toplevel, ctx.wl_seat, ctx.button_serial, xedge_box[4 + loc]); } else{ xdg_toplevel_move(ctx.main_xdg_toplevel, ctx.wl_seat, ctx.button_serial); } } } /* window frame render */ if (csd){ if (ctx.shadow_data != 0){ munmap(ctx.shadow_data, ctx.shadow_size); ctx.shadow_data = 0; } if (ctx.shadow_wl_buffer != 0){ wl_buffer_destroy(ctx.shadow_wl_buffer); ctx.shadow_wl_buffer = 0; } { int dim[2]; for (int i = 0; i < 2; i += 1){ dim[i] = ctx.dim[i] + SHADOW_MARGIN*2; } 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); } } 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 = 0; { int x0 = SHADOW_MARGIN - SHADOW_THICK; int y0 = SHADOW_MARGIN - SHADOW_THICK; int x1 = x0 + ctx.dim[0] + SHADOW_THICK*2; int y1 = y0 + ctx.dim[1] + SHADOW_THICK*2; if (x0 <= x && x < x1 && y0 <= y && y < y1){ int xd0 = x - x0; int xd1 = x1 - x - 1; int yd0 = y - y0; int yd1 = y1 - y - 1; int d = Min(Min(xd0, xd1), Min(yd0, yd1)); if (d < SHADOW_THICK){ int c = d*0xFF/SHADOW_THICK; ((uint8_t*)pxl)[0] = 0; ((uint8_t*)pxl)[1] = 0; ((uint8_t*)pxl)[2] = 0; ((uint8_t*)pxl)[3] = c; } } } pxl += 1; } } { struct wl_region *region = wl_compositor_create_region(ctx.wl_compositor); wl_region_add(region, 0, 0, ctx.shadow_dim[0], ctx.shadow_dim[1]); wl_region_subtract(region, SHADOW_MARGIN, SHADOW_MARGIN, ctx.dim[0], ctx.dim[1]); wl_surface_set_input_region(ctx.shadow_wl_surface, region); wl_region_destroy(region); } { 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, -SHADOW_MARGIN, -SHADOW_MARGIN); wl_surface_commit(ctx.shadow_wl_surface); } } /* window frame commit */ 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); } } } xdg_surface_set_window_geometry(ctx.main_xdg_surface, -csd_frame.border[0][0], -csd_frame.border[1][0], ctx.dim[0] + csd_dim[0], ctx.dim[1] + csd_dim[1]); wl_egl_window_resize(ctx.main_wl_egl_window, ctx.dim[0], ctx.dim[1], 0, 0); /* app update & render */ { if (ctx.close_signal){ exit_loop = 1; } glDrawBuffer(GL_BACK); glViewport(0, 0, ctx.dim[0], ctx.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); /* commit new cursor */ { struct wl_cursor *cursor = ctx.wl_cursors[cursor_shape]; if (cursor != 0){ struct wl_cursor_image *cursor_image = cursor->images[0]; struct wl_buffer *cursor_buffer = wl_cursor_image_get_buffer(cursor_image); wl_surface_set_buffer_scale(ctx.cursor_surface, 1); wl_surface_attach(ctx.cursor_surface, cursor_buffer, 0, 0); wl_surface_damage_buffer(ctx.cursor_surface, 0, 0, cursor_image->width, cursor_image->height); wl_surface_commit(ctx.cursor_surface); wl_pointer_set_cursor(ctx.wl_pointer, ctx.hover_serial, ctx.cursor_surface, cursor_image->hotspot_x, cursor_image->hotspot_y); } } } 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){ for (int i = 0; i < 2; i += 1){ for (int j = 0; j < 2; j += 1){ frame.border[i][j] = SHADOW_MARGIN; } } } 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