316 lines
9.7 KiB
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;
|
|
} |