#include // #include // #include #include #include #include #include #include #include #include #include "session.h" #include "wayland.h" #include "widgets/cairo-window.h" #define DEF_SURF_W 600 #define DEF_SURF_H 400 struct _DiyaLockSession { DiyaObject parent; struct ext_session_lock_v1 *session_lock; struct ext_session_lock_v1_listener session_listener; struct ext_session_lock_surface_v1_listener surface_listener; struct wl_callback_listener frame_listener; struct wl_surface *wl_surface; struct wl_buffer *wl_buffer; void * raw_shared_buffer; guint32 raw_shared_buffer_size; cairo_surface_t *cairo_surface; DiyaCairoWindow * window; guint surface_width; guint surface_height; guint32 last_frame_time; guint hold; }; G_DEFINE_FINAL_TYPE(DiyaLockSession, diya_lock_session, DIYA_TYPE_SHELL_OBJECT) static struct ext_session_lock_manager_v1 *g_session_lock_manager; static const cairo_user_data_key_t shm_surface_data_key; static void diya_lock_session_dispose(GObject *object) { DiyaLockSession *self = DIYA_LOCK_SESSION(object); g_debug("diya_lock_session_dispose: %s", diya_object_to_string(self)); if (self->session_lock && !self->hold) { ext_session_lock_v1_unlock_and_destroy(self->session_lock); } else { g_warning("diya_lock_session_dispose: the lock session is disposed but the lock has not been released"); } if (self->cairo_surface) { cairo_surface_destroy(self->cairo_surface); self->cairo_surface = NULL; } if(self->window) { g_object_unref(self->window); self->window = NULL; } G_OBJECT_CLASS(diya_lock_session_parent_class)->dispose(object); } static void handle_session_locked(void *data, struct ext_session_lock_v1 *lock) { (void) lock; DiyaShell *shell; GtkApplication *app; DiyaWayland *wayland; DiyaLockSession *self = data; g_object_get(self, "shell", &shell, NULL); assert(shell); g_object_get(shell, "application", &app, "wayland", &wayland, NULL); assert(app); assert(DIYA_IS_WAYLAND(wayland)); g_debug("Session locked on shell: %s", diya_object_to_string(shell)); struct wl_surface *surface = diya_wayland_create_surface(wayland); struct wl_output *output = diya_wayland_get_output(wayland, 0); assert(output); self->wl_surface = surface; struct ext_session_lock_surface_v1 *lock_surface = ext_session_lock_v1_get_lock_surface(self->session_lock, surface, output); ext_session_lock_surface_v1_add_listener(lock_surface, &self->surface_listener, data); struct wl_callback *cb = wl_surface_frame(self->wl_surface); wl_callback_add_listener(cb, &self->frame_listener, data); } static void handle_session_lock_finished(void *data, struct ext_session_lock_v1 *lock) { DiyaShell *shell; g_object_get(data, "shell", &shell, NULL); assert(shell); ext_session_lock_v1_destroy(lock); g_warning("Session locked finished on shell %s", diya_object_to_string(shell)); } static void randname(char *buf) { long r = g_get_real_time() / 1000000; for (int i = 0; i < 6; ++i) { buf[i] = 'A' + (r & 15) + (r & 16) * 2; r >>= 5; } } static int create_shm_file(void) { int retries = 100; do { char name[] = "/diya_shell_wl_shm-XXXXXX"; randname(name + sizeof(name) - 7); --retries; int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd >= 0) { shm_unlink(name); return fd; } } while (retries > 0 && errno == EEXIST); return -1; } static int allocate_shm_file(guint size) { int fd = create_shm_file(); if (fd < 0) { g_error("allocate_shm_file: unable to create_shm_file: %s", strerror(errno)); return -1; } int ret; do { ret = ftruncate(fd, size); } while (ret < 0 && errno == EINTR); if (ret < 0) { g_error("allocate_shm_file: unable to ftruncate: %s", strerror(errno)); close(fd); return -1; } return fd; } static void session_lock_free_surface(void *data) { DiyaLockSession * self = data; assert(DIYA_IS_LOCK_SESSION(self)); if (self->wl_buffer) { g_debug("Destroy share buffer wl_buffer"); wl_buffer_destroy(self->wl_buffer); self->wl_buffer = NULL; } if(self->raw_shared_buffer) { g_debug("unmapped shared buffer of %d bytes", self->raw_shared_buffer_size); munmap(self->raw_shared_buffer, self->raw_shared_buffer_size); self->raw_shared_buffer = NULL; self->raw_shared_buffer_size = 0; } } static void session_lock_realloc_surface(DiyaLockSession *self) { DiyaShell *shell; g_object_get(self, "shell", &shell, NULL); assert(DIYA_IS_SHELL(shell)); DiyaWayland *wayland = diya_shell_get_wayland(shell); assert(DIYA_IS_WAYLAND(wayland)); if (self->cairo_surface) { cairo_surface_destroy(self->cairo_surface); self->cairo_surface = NULL; } int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, self->surface_width); guint size = stride * self->surface_height; int fd = allocate_shm_file(size); if (fd == -1) { g_error("session_lock_realloc_surface: unable to allocate_shm_file"); return; } void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { g_error("session_lock_realloc_surface: unable to mmap: %s", strerror(errno)); close(fd); return; } self->cairo_surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, self->surface_width, self->surface_height, stride); cairo_surface_set_user_data(self->cairo_surface, &shm_surface_data_key, self, session_lock_free_surface); struct wl_shm_pool *pool = diya_wayland_create_shm_pool(wayland, fd, size); self->wl_buffer = wl_shm_pool_create_buffer(pool, 0, self->surface_width, self->surface_height, stride, WL_SHM_FORMAT_XRGB8888); wl_shm_pool_destroy(pool); close(fd); self->raw_shared_buffer = data; self->raw_shared_buffer_size = size; } static void lock_session_draw_frame(DiyaLockSession *self) { if(!self->cairo_surface) { return; } diya_cairo_window_render(self->window, self->cairo_surface); wl_surface_attach(self->wl_surface, self->wl_buffer, 0, 0); wl_surface_damage_buffer(self->wl_surface, 0, 0, self->surface_width, self->surface_height); wl_surface_commit(self->wl_surface); } static void lock_surface_configure(void *data, struct ext_session_lock_surface_v1 *lock_surface, uint32_t serial, uint32_t width, uint32_t height) { DiyaLockSession *self = data; assert(DIYA_IS_LOCK_SESSION(self)); if (!self->wl_buffer || self->surface_width != width || self->surface_height != height) { self->surface_width = width; self->surface_height = height; session_lock_realloc_surface(self); diya_cairo_widget_set_size(self->window, width, height); diya_cairo_window_update(self->window); } ext_session_lock_surface_v1_ack_configure(lock_surface, serial); lock_session_draw_frame(self); } static void lock_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { /* Destroy this callback */ wl_callback_destroy(cb); DiyaLockSession *lock = data; assert(DIYA_IS_LOCK_SESSION(lock)); cb = wl_surface_frame(lock->wl_surface); wl_callback_add_listener(cb, &lock->frame_listener, data); if (!lock->cairo_surface || !lock->wl_buffer || lock->window) { return; } lock->last_frame_time = time; lock_session_draw_frame(lock); } static void diya_lock_session_init(DiyaLockSession *self) { self->session_lock = ext_session_lock_manager_v1_lock(g_session_lock_manager); self->session_listener.locked = handle_session_locked; self->session_listener.finished = handle_session_lock_finished; self->surface_listener.configure = lock_surface_configure; ext_session_lock_v1_add_listener(self->session_lock, &self->session_listener, self); self->frame_listener.done = lock_surface_frame_done; self->surface_width = DEF_SURF_W; self->surface_height = DEF_SURF_H; self->hold = true; self->last_frame_time = 0; self->wl_surface = NULL; self->wl_buffer = NULL; self->window = g_object_new(DIYA_TYPE_CAIRO_WINDOW,NULL); self->cairo_surface = NULL; self->raw_shared_buffer = NULL; self->raw_shared_buffer_size = 0; } static const gchar *diya_lock_session_to_string(DiyaObject *object) { (void)object; // DiyaLockSession* self = DIYA_LOCK_SESSION(object); return "DIYA SHELL LOCK SESSION"; } static void diya_lock_session_class_init(DiyaLockSessionClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS(class); DiyaObjectClass *base_class = DIYA_OBJECT_CLASS(class); gobject_class->dispose = diya_lock_session_dispose; base_class->to_string = diya_lock_session_to_string; } void diya_shell_session_lock_register(struct wl_registry *registry, uint32_t name) { g_session_lock_manager = wl_registry_bind(registry, name, &ext_session_lock_manager_v1_interface, 1); } void diya_shell_session_lock_release(DiyaLockSession *self) { self->hold = false; }