diya-shell/src/session.c

316 lines
9.7 KiB
C

#include <assert.h>
// #include <wayland-egl.h>
// #include <EGL/egl.h>
#include <cairo/cairo.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#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;
}