From c059e3aa7f93da470f052a5cd8ee0d8dc969e679 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Tue, 13 Oct 2026 13:44:13 +0300 Subject: rename and simplification --- src/platform/wayland/shell.c | 401 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 src/platform/wayland/shell.c (limited to 'src/platform/wayland/shell.c') diff --git a/src/platform/wayland/shell.c b/src/platform/wayland/shell.c new file mode 100644 index 0000000..4c1caad --- /dev/null +++ b/src/platform/wayland/shell.c @@ -0,0 +1,401 @@ +#include "internal.h" + +static void wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial); +static const struct xdg_wm_base_listener wm_base_listener; + +static void presentation_clock_id(void *data, struct wp_presentation *presentation, + uint32_t clk_id) +{ + (void)presentation; + struct wayland_display *c = data; + struct timespec ts; + c->pres_clock = (clockid_t)clk_id; + c->pres_clock_known = clock_gettime(c->pres_clock, &ts) == 0; +} + +static uint64_t clock_ns(clockid_t clock) +{ + struct timespec ts; + if (clock_gettime(clock, &ts) != 0) + return 0; + return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec; +} + +uint64_t conn_pres_to_mono(const struct wayland_display *c, uint64_t ns) +{ + if (c->pres_clock == CLOCK_MONOTONIC) + return ns; + uint64_t mono = clock_ns(CLOCK_MONOTONIC); + uint64_t pres = clock_ns(c->pres_clock); + return ns + mono - pres; +} + +uint64_t conn_mono_to_pres(const struct wayland_display *c, uint64_t ns) +{ + if (c->pres_clock == CLOCK_MONOTONIC) + return ns; + uint64_t mono = clock_ns(CLOCK_MONOTONIC); + uint64_t pres = clock_ns(c->pres_clock); + return ns + pres - mono; +} + +static const struct wp_presentation_listener presentation_listener = { + .clock_id = presentation_clock_id, +}; + +static void output_geometry(void *data, struct wl_output *output, + int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, + int32_t subpixel, const char *make, const char *model, int32_t transform) +{ + (void)data; (void)output; (void)x; (void)y; (void)physical_width; + (void)physical_height; (void)subpixel; (void)make; (void)model; + (void)transform; +} + +static void output_mode(void *data, struct wl_output *output, uint32_t flags, + int32_t width, int32_t height, int32_t refresh) +{ + (void)output; (void)width; (void)height; + struct wl_conn_output *o = data; + if ((flags & WL_OUTPUT_MODE_CURRENT) && refresh > 0) + o->pending_refresh_ns = (uint32_t)(1000000000000ull / (uint32_t)refresh); +} + +static void conn_follow_outputs(struct wayland_display *c) +{ + for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) + surface_follow_outputs(w); +} + +static void output_done(void *data, struct wl_output *output) +{ + (void)output; + struct wl_conn_output *o = data; + if (o->pending_scale == o->scale && o->pending_refresh_ns == o->refresh_ns) + return; + o->scale = o->pending_scale; + o->refresh_ns = o->pending_refresh_ns; + conn_follow_outputs(o->conn); +} + +static void output_scale(void *data, struct wl_output *output, int32_t factor) +{ + (void)output; + struct wl_conn_output *o = data; + if (factor > 0) + o->pending_scale = factor; +} + +static void output_name(void *data, struct wl_output *output, const char *name) +{ (void)data; (void)output; (void)name; } + +static void output_description(void *data, struct wl_output *output, const char *description) +{ (void)data; (void)output; (void)description; } + +static const struct wl_output_listener output_listener = { + .name = output_name, + .description = output_description, + .geometry = output_geometry, + .mode = output_mode, + .done = output_done, + .scale = output_scale, +}; + +static void conn_bind_output(struct wayland_display *c, struct wl_registry *registry, + uint32_t name, uint32_t version) +{ + struct wl_conn_output *o = calloc(1, sizeof(*o)); + if (!o) return; + o->proxy = wl_registry_bind(registry, name, &wl_output_interface, + version < 3 ? version : 3); + if (!o->proxy) { + free(o); + return; + } + o->conn = c; + o->name = name; + o->scale = o->pending_scale = 1; + o->mask = ~c->output_masks & (c->output_masks + 1); + c->output_masks |= o->mask; + struct wl_conn_output **tail = &c->outputs; + while (*tail) + tail = &(*tail)->next; + *tail = o; + wl_output_add_listener(o->proxy, &output_listener, o); +} + +static void output_destroy(struct wl_conn_output *o) +{ + if (wl_output_get_version(o->proxy) >= WL_OUTPUT_RELEASE_SINCE_VERSION) + wl_output_release(o->proxy); + else + wl_output_destroy(o->proxy); + o->conn->output_masks &= ~o->mask; + free(o); +} + +static bool conn_remove_output(struct wayland_display *c, uint32_t name) +{ + struct wl_conn_output **link = &c->outputs; + while (*link && (*link)->name != name) + link = &(*link)->next; + struct wl_conn_output *o = *link; + if (!o) + return false; + *link = o->next; + for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) + w->outputs &= ~o->mask; + output_destroy(o); + conn_follow_outputs(c); + return true; +} + +void conn_destroy_outputs(struct wayland_display *c) +{ + while (c->outputs) { + struct wl_conn_output *o = c->outputs; + c->outputs = o->next; + output_destroy(o); + } +} + +uint64_t conn_output_mask(const struct wayland_display *c, const struct wl_output *proxy) +{ + for (const struct wl_conn_output *o = c->outputs; o; o = o->next) + if (o->proxy == proxy) + return o->mask; + return 0; +} + +uint32_t conn_output_scale_120(const struct wayland_display *c, uint64_t outputs) +{ + int32_t scale = 0; + for (const struct wl_conn_output *o = c->outputs; o; o = o->next) + if ((o->mask & outputs) && o->scale > scale) + scale = o->scale; + return (uint32_t)scale * YASL_SCALE_UNIT; +} + +static void registry_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + struct wayland_display *c = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + c->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, version < 6 ? version : 6); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + c->shm = wl_registry_bind(registry, name, + &wl_shm_interface, version < 2 ? version : 2); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + conn_bind_output(c, registry, name, version); + } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { + c->data_manager = wl_registry_bind(registry, name, &wl_data_device_manager_interface, + version < 3 ? version : 3); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + c->wm_base = wl_registry_bind(registry, name, + &xdg_wm_base_interface, version < 7 ? version : 7); + if (c->wm_base) + xdg_wm_base_add_listener(c->wm_base, &wm_base_listener, c); + } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { + c->decor_mgr = wl_registry_bind(registry, name, + &zxdg_decoration_manager_v1_interface, 1); + } else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) { + c->primary_selection_mgr = wl_registry_bind(registry, name, + &zwp_primary_selection_device_manager_v1_interface, 1); + for (struct seat *s = c->seats; s; s = s->next) + if (s->clipboard) + clipboard_set_primary_manager(s->clipboard, c->primary_selection_mgr); + } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { + c->cursor_shape_mgr = wl_registry_bind(registry, name, + &wp_cursor_shape_manager_v1_interface, 1); + } else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) { + c->scale_mgr = wl_registry_bind(registry, name, + &wp_fractional_scale_manager_v1_interface, 1); + } else if (strcmp(interface, wp_viewporter_interface.name) == 0) { + c->viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1); + } else if (strcmp(interface, zwp_tablet_manager_v2_interface.name) == 0) { + c->tablet_mgr = wl_registry_bind(registry, name, + &zwp_tablet_manager_v2_interface, 1); + } else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) { + c->text_input_mgr = wl_registry_bind(registry, name, + &zwp_text_input_manager_v3_interface, 1); + } else if (strcmp(interface, wp_tearing_control_manager_v1_interface.name) == 0) { + c->tearing_mgr = wl_registry_bind(registry, name, + &wp_tearing_control_manager_v1_interface, 1); + } else if (strcmp(interface, wp_presentation_interface.name) == 0) { + c->presentation = wl_registry_bind(registry, name, + &wp_presentation_interface, 1); + if (c->presentation) + wp_presentation_add_listener(c->presentation, &presentation_listener, c); + } else if (strcmp(interface, wp_fifo_manager_v1_interface.name) == 0) { + c->fifo_mgr = wl_registry_bind(registry, name, + &wp_fifo_manager_v1_interface, 1); + } else if (strcmp(interface, wp_commit_timing_manager_v1_interface.name) == 0) { + c->commit_timing_mgr = wl_registry_bind(registry, name, + &wp_commit_timing_manager_v1_interface, 1); + } else if (strcmp(interface, xdg_toplevel_drag_manager_v1_interface.name) == 0) { + c->toplevel_drag_mgr = wl_registry_bind(registry, name, + &xdg_toplevel_drag_manager_v1_interface, 1); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + seat_bind(c, registry, name, version); + } +} + +static void registry_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + (void)registry; + struct wayland_display *c = data; + if (c && !conn_remove_output(c, name)) + seat_remove(c, name); +} + +const struct wl_registry_listener registry_listener = { + .global = registry_global, + .global_remove = registry_global_remove, +}; + +static void wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial) +{ + (void)data; + xdg_wm_base_pong(wm_base, serial); +} + +static const struct xdg_wm_base_listener wm_base_listener = { + .ping = wm_base_ping, +}; + +static void notify_state(struct wayland_surface *w) +{ + if (w->cbs && w->cbs->state) + w->cbs->state(&w->base, wayland_surface_state(w), w->ud); +} + +static void toplevel_configure(void *data, struct xdg_toplevel *toplevel, + int32_t width, int32_t height, struct wl_array *states) +{ + (void)toplevel; + struct wayland_surface *w = data; + + uint32_t state_mask = 0; + uint32_t *s; + wl_array_for_each(s, states) { + if (*s < 32) + state_mask |= (1u << *s); + } + + bool max = (state_mask & (1u << XDG_TOPLEVEL_STATE_MAXIMIZED)) != 0; + bool fs = (state_mask & (1u << XDG_TOPLEVEL_STATE_FULLSCREEN)) != 0; + bool act = (state_mask & (1u << XDG_TOPLEVEL_STATE_ACTIVATED)) != 0; + bool susp = (state_mask & (1u << XDG_TOPLEVEL_STATE_SUSPENDED)) != 0; + bool tiled = (state_mask & ( + (1u << XDG_TOPLEVEL_STATE_TILED_LEFT) | + (1u << XDG_TOPLEVEL_STATE_TILED_RIGHT) | + (1u << XDG_TOPLEVEL_STATE_TILED_TOP) | + (1u << XDG_TOPLEVEL_STATE_TILED_BOTTOM))) != 0; + + if ((fs ^ w->fullscreen) | (max ^ w->maximized) | + (act ^ w->activated) | (susp ^ w->suspended)) { + w->fullscreen = fs; + w->maximized = max; + w->activated = act; + w->suspended = susp; + notify_state(w); + } + + w->tiled = tiled; + w->req_width = width; + w->req_height = height; +} + +static void toplevel_close(void *data, struct xdg_toplevel *toplevel) +{ + (void)toplevel; + struct wayland_surface *w = data; + if (w && w->cbs && w->cbs->close) + w->cbs->close(&w->base, w->ud); +} + +static void toplevel_configure_bounds(void *data, struct xdg_toplevel *toplevel, + int32_t width, int32_t height) +{ + (void)toplevel; + struct wayland_surface *w = data; + w->bound_width = width; + w->bound_height = height; + if ((width > 0 && w->logical_width > width) || + (height > 0 && w->logical_height > height)) { + w->size_pending = true; + wayland_surface_schedule_redraw(w); + } +} + +static void toplevel_wm_capabilities(void *data, struct xdg_toplevel *toplevel, + struct wl_array *capabilities) +{ + (void)data; (void)toplevel; (void)capabilities; +} + +static const struct xdg_toplevel_listener toplevel_listener = { + .configure = toplevel_configure, + .close = toplevel_close, + .configure_bounds = toplevel_configure_bounds, + .wm_capabilities = toplevel_wm_capabilities, +}; + +static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) +{ + struct wayland_surface *w = data; + (void)xdg_surface; + w->pending_serial = serial; + w->has_pending_serial = true; + w->size_pending = true; + w->configured = true; + wayland_surface_schedule_redraw(w); +} + +const struct xdg_surface_listener shell_surface_listener = { + .configure = xdg_surface_configure, +}; + +static void toplevel_decoration_configure(void *data, + struct zxdg_toplevel_decoration_v1 *toplevel_decor, uint32_t mode) +{ + (void)toplevel_decor; + struct wayland_surface *w = data; + bool was_server = w->server_decorated; + w->server_decorated = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + if (was_server != w->server_decorated) + notify_state(w); +} + +static const struct zxdg_toplevel_decoration_v1_listener toplevel_decoration_listener = { + .configure = toplevel_decoration_configure, +}; + +int surface_init_shell(struct wayland_surface *w, const char *title) +{ + w->xdg_surface = xdg_wm_base_get_xdg_surface(w->conn->wm_base, w->surface); + if (!w->xdg_surface) return -1; + xdg_surface_add_listener(w->xdg_surface, &shell_surface_listener, w); + w->toplevel = xdg_surface_get_toplevel(w->xdg_surface); + if (!w->toplevel) return -1; + xdg_toplevel_add_listener(w->toplevel, &toplevel_listener, w); + + wayland_surface_set_title(w, title ? title : "yasl_app"); + + const char *app_id = w->conn->app_id ? w->conn->app_id : "yasl_app"; + xdg_toplevel_set_app_id(w->toplevel, app_id); + xdg_toplevel_set_min_size(w->toplevel, YASL_MIN_LOGICAL_PX, YASL_MIN_LOGICAL_PX); + + if (w->conn->decor_mgr) { + w->toplevel_decor = zxdg_decoration_manager_v1_get_toplevel_decoration( + w->conn->decor_mgr, w->toplevel); + if (!w->toplevel_decor) return -1; + zxdg_toplevel_decoration_v1_add_listener(w->toplevel_decor, + &toplevel_decoration_listener, w); + zxdg_toplevel_decoration_v1_set_mode(w->toplevel_decor, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + return 0; +} -- cgit v1.2.3