Enable viewing all X11 cursor sizes
This commit is contained in:
parent
085f2d7eef
commit
c39ac1a9da
68
fastiv-io.c
68
fastiv-io.c
|
@ -955,39 +955,58 @@ open_xcursor(const gchar *data, gsize len, GError **error)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's just arrange the pixel data in a recording surface.
|
// Interpret cursors as animated pages.
|
||||||
static cairo_user_data_key_t key = {};
|
cairo_surface_t *pages = NULL, *frames_head = NULL, *frames_tail = NULL;
|
||||||
cairo_surface_t *recording =
|
|
||||||
cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, NULL);
|
|
||||||
cairo_surface_set_user_data(
|
|
||||||
recording, &key, images, (cairo_destroy_func_t) XcursorImagesDestroy);
|
|
||||||
cairo_t *cr = cairo_create(recording);
|
|
||||||
|
|
||||||
// Unpack horizontally by animation, vertically by size.
|
// XXX: Assuming that all "nominal sizes" have the same dimensions.
|
||||||
XcursorDim last_nominal = -1;
|
XcursorDim last_nominal = -1;
|
||||||
int x = 0, y = 0, row_height = 0;
|
|
||||||
for (int i = 0; i < images->nimage; i++) {
|
for (int i = 0; i < images->nimage; i++) {
|
||||||
XcursorImage *image = images->images[i];
|
XcursorImage *image = images->images[i];
|
||||||
if (image->size != last_nominal) {
|
|
||||||
x = 0;
|
|
||||||
y += row_height;
|
|
||||||
row_height = 0;
|
|
||||||
last_nominal = image->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The library automatically byte swaps in _XcursorReadImage().
|
// The library automatically byte swaps in _XcursorReadImage().
|
||||||
cairo_surface_t *source = cairo_image_surface_create_for_data(
|
cairo_surface_t *surface = cairo_image_surface_create_for_data(
|
||||||
(unsigned char *) image->pixels, CAIRO_FORMAT_ARGB32,
|
(unsigned char *) image->pixels, CAIRO_FORMAT_ARGB32,
|
||||||
image->width, image->height, image->width * sizeof *image->pixels);
|
image->width, image->height, image->width * sizeof *image->pixels);
|
||||||
cairo_set_source_surface(cr, source, x, y);
|
cairo_surface_set_user_data(surface, &fastiv_io_key_frame_duration,
|
||||||
cairo_surface_destroy(source);
|
(void *) (intptr_t) image->delay, NULL);
|
||||||
cairo_paint(cr);
|
|
||||||
|
|
||||||
x += image->width;
|
if (pages && image->size == last_nominal) {
|
||||||
row_height = MAX(row_height, (int) image->height);
|
cairo_surface_set_user_data(surface,
|
||||||
|
&fastiv_io_key_frame_previous, frames_tail, NULL);
|
||||||
|
cairo_surface_set_user_data(frames_tail,
|
||||||
|
&fastiv_io_key_frame_next, surface,
|
||||||
|
(cairo_destroy_func_t) cairo_surface_destroy);
|
||||||
|
} else if (frames_head) {
|
||||||
|
cairo_surface_set_user_data(frames_head,
|
||||||
|
&fastiv_io_key_frame_previous, frames_tail, NULL);
|
||||||
|
|
||||||
|
cairo_surface_set_user_data(frames_head,
|
||||||
|
&fastiv_io_key_page_next, surface,
|
||||||
|
(cairo_destroy_func_t) cairo_surface_destroy);
|
||||||
|
cairo_surface_set_user_data(surface,
|
||||||
|
&fastiv_io_key_page_previous, frames_head, NULL);
|
||||||
|
frames_head = surface;
|
||||||
|
} else {
|
||||||
|
pages = frames_head = surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
frames_tail = surface;
|
||||||
|
last_nominal = image->size;
|
||||||
}
|
}
|
||||||
cairo_destroy(cr);
|
if (!pages) {
|
||||||
return recording;
|
XcursorImagesDestroy(images);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap around animations in the last page.
|
||||||
|
cairo_surface_set_user_data(
|
||||||
|
frames_head, &fastiv_io_key_frame_previous, frames_tail, NULL);
|
||||||
|
|
||||||
|
// There is no need to copy data, assign it to the surface.
|
||||||
|
static cairo_user_data_key_t key = {};
|
||||||
|
cairo_surface_set_user_data(
|
||||||
|
pages, &key, images, (cairo_destroy_func_t) XcursorImagesDestroy);
|
||||||
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_XCURSOR --------------------------------------------------------
|
#endif // HAVE_XCURSOR --------------------------------------------------------
|
||||||
|
@ -1041,6 +1060,9 @@ cairo_user_data_key_t fastiv_io_key_frame_previous;
|
||||||
cairo_user_data_key_t fastiv_io_key_frame_duration;
|
cairo_user_data_key_t fastiv_io_key_frame_duration;
|
||||||
cairo_user_data_key_t fastiv_io_key_loops;
|
cairo_user_data_key_t fastiv_io_key_loops;
|
||||||
|
|
||||||
|
cairo_user_data_key_t fastiv_io_key_page_next;
|
||||||
|
cairo_user_data_key_t fastiv_io_key_page_previous;
|
||||||
|
|
||||||
cairo_surface_t *
|
cairo_surface_t *
|
||||||
fastiv_io_open(const gchar *path, GError **error)
|
fastiv_io_open(const gchar *path, GError **error)
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,6 +46,13 @@ extern cairo_user_data_key_t fastiv_io_key_frame_duration;
|
||||||
/// How many times to repeat the animation, or zero for +inf, as a uintptr_t.
|
/// How many times to repeat the animation, or zero for +inf, as a uintptr_t.
|
||||||
extern cairo_user_data_key_t fastiv_io_key_loops;
|
extern cairo_user_data_key_t fastiv_io_key_loops;
|
||||||
|
|
||||||
|
/// The first frame of the next page, as a surface, in a chain.
|
||||||
|
/// There is no wrap-around.
|
||||||
|
extern cairo_user_data_key_t fastiv_io_key_page_next;
|
||||||
|
/// The first frame of the previous page, as a surface, in a chain.
|
||||||
|
/// There is no wrap-around. This is a weak pointer.
|
||||||
|
extern cairo_user_data_key_t fastiv_io_key_page_previous;
|
||||||
|
|
||||||
cairo_surface_t *fastiv_io_open(const gchar *path, GError **error);
|
cairo_surface_t *fastiv_io_open(const gchar *path, GError **error);
|
||||||
cairo_surface_t *fastiv_io_open_from_data(
|
cairo_surface_t *fastiv_io_open_from_data(
|
||||||
const char *data, size_t len, const gchar *path, GError **error);
|
const char *data, size_t len, const gchar *path, GError **error);
|
||||||
|
|
|
@ -31,8 +31,9 @@
|
||||||
|
|
||||||
struct _FastivView {
|
struct _FastivView {
|
||||||
GtkWidget parent_instance;
|
GtkWidget parent_instance;
|
||||||
cairo_surface_t *surface; ///< The loaded image (sequence)
|
cairo_surface_t *image; ///< The loaded image (sequence)
|
||||||
cairo_surface_t *frame; ///< Current frame within, unreferenced
|
cairo_surface_t *page; ///< Current page within image, weak
|
||||||
|
cairo_surface_t *frame; ///< Current frame within page, weak
|
||||||
FastivIoOrientation orientation; ///< Current orientation
|
FastivIoOrientation orientation; ///< Current orientation
|
||||||
bool filter;
|
bool filter;
|
||||||
bool scale_to_fit;
|
bool scale_to_fit;
|
||||||
|
@ -89,7 +90,7 @@ static void
|
||||||
fastiv_view_finalize(GObject *gobject)
|
fastiv_view_finalize(GObject *gobject)
|
||||||
{
|
{
|
||||||
FastivView *self = FASTIV_VIEW(gobject);
|
FastivView *self = FASTIV_VIEW(gobject);
|
||||||
cairo_surface_destroy(self->surface);
|
cairo_surface_destroy(self->image);
|
||||||
|
|
||||||
G_OBJECT_CLASS(fastiv_view_parent_class)->finalize(gobject);
|
G_OBJECT_CLASS(fastiv_view_parent_class)->finalize(gobject);
|
||||||
}
|
}
|
||||||
|
@ -115,28 +116,28 @@ static void
|
||||||
get_surface_dimensions(FastivView *self, double *width, double *height)
|
get_surface_dimensions(FastivView *self, double *width, double *height)
|
||||||
{
|
{
|
||||||
*width = *height = 0;
|
*width = *height = 0;
|
||||||
if (!self->surface)
|
if (!self->image)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cairo_rectangle_t extents = {};
|
cairo_rectangle_t extents = {};
|
||||||
switch (cairo_surface_get_type(self->surface)) {
|
switch (cairo_surface_get_type(self->page)) {
|
||||||
case CAIRO_SURFACE_TYPE_IMAGE:
|
case CAIRO_SURFACE_TYPE_IMAGE:
|
||||||
switch (self->orientation) {
|
switch (self->orientation) {
|
||||||
case FastivIoOrientation90:
|
case FastivIoOrientation90:
|
||||||
case FastivIoOrientationMirror90:
|
case FastivIoOrientationMirror90:
|
||||||
case FastivIoOrientation270:
|
case FastivIoOrientation270:
|
||||||
case FastivIoOrientationMirror270:
|
case FastivIoOrientationMirror270:
|
||||||
*width = cairo_image_surface_get_height(self->surface);
|
*width = cairo_image_surface_get_height(self->page);
|
||||||
*height = cairo_image_surface_get_width(self->surface);
|
*height = cairo_image_surface_get_width(self->page);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
*width = cairo_image_surface_get_width(self->surface);
|
*width = cairo_image_surface_get_width(self->page);
|
||||||
*height = cairo_image_surface_get_height(self->surface);
|
*height = cairo_image_surface_get_height(self->page);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case CAIRO_SURFACE_TYPE_RECORDING:
|
case CAIRO_SURFACE_TYPE_RECORDING:
|
||||||
if (!cairo_recording_surface_get_extents(self->surface, &extents)) {
|
if (!cairo_recording_surface_get_extents(self->page, &extents)) {
|
||||||
cairo_recording_surface_ink_extents(self->surface,
|
cairo_recording_surface_ink_extents(self->page,
|
||||||
&extents.x, &extents.y, &extents.width, &extents.height);
|
&extents.x, &extents.y, &extents.width, &extents.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +199,7 @@ fastiv_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
|
||||||
->size_allocate(widget, allocation);
|
->size_allocate(widget, allocation);
|
||||||
|
|
||||||
FastivView *self = FASTIV_VIEW(widget);
|
FastivView *self = FASTIV_VIEW(widget);
|
||||||
if (!self->surface || !self->scale_to_fit)
|
if (!self->image || !self->scale_to_fit)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
double w, h;
|
double w, h;
|
||||||
|
@ -271,7 +272,7 @@ fastiv_view_draw(GtkWidget *widget, cairo_t *cr)
|
||||||
allocation.width, allocation.height);
|
allocation.width, allocation.height);
|
||||||
|
|
||||||
FastivView *self = FASTIV_VIEW(widget);
|
FastivView *self = FASTIV_VIEW(widget);
|
||||||
if (!self->surface ||
|
if (!self->image ||
|
||||||
!gtk_cairo_should_draw_window(cr, gtk_widget_get_window(widget)))
|
!gtk_cairo_should_draw_window(cr, gtk_widget_get_window(widget)))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
@ -408,7 +409,7 @@ static gboolean
|
||||||
fastiv_view_scroll_event(GtkWidget *widget, GdkEventScroll *event)
|
fastiv_view_scroll_event(GtkWidget *widget, GdkEventScroll *event)
|
||||||
{
|
{
|
||||||
FastivView *self = FASTIV_VIEW(widget);
|
FastivView *self = FASTIV_VIEW(widget);
|
||||||
if (!self->surface)
|
if (!self->image)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (event->state & gtk_accelerator_get_default_mod_mask())
|
if (event->state & gtk_accelerator_get_default_mod_mask())
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -431,7 +432,7 @@ fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)
|
||||||
FastivView *self = FASTIV_VIEW(widget);
|
FastivView *self = FASTIV_VIEW(widget);
|
||||||
if (event->state & ~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask())
|
if (event->state & ~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask())
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (!self->surface)
|
if (!self->image)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
switch (event->keyval) {
|
switch (event->keyval) {
|
||||||
|
@ -462,16 +463,33 @@ fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)
|
||||||
gtk_widget_queue_resize(widget);
|
gtk_widget_queue_resize(widget);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case GDK_KEY_bracketleft:
|
case GDK_KEY_bracketleft: {
|
||||||
|
cairo_surface_t *page = cairo_surface_get_user_data(
|
||||||
|
self->page, &fastiv_io_key_page_previous);
|
||||||
|
if (page)
|
||||||
|
self->frame = self->page = page;
|
||||||
|
gtk_widget_queue_resize(widget);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
case GDK_KEY_bracketright: {
|
||||||
|
cairo_surface_t *page = cairo_surface_get_user_data(
|
||||||
|
self->page, &fastiv_io_key_page_next);
|
||||||
|
if (page)
|
||||||
|
self->frame = self->page = page;
|
||||||
|
gtk_widget_queue_resize(widget);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GDK_KEY_braceleft:
|
||||||
if (!(self->frame = cairo_surface_get_user_data(
|
if (!(self->frame = cairo_surface_get_user_data(
|
||||||
self->frame, &fastiv_io_key_frame_previous)))
|
self->frame, &fastiv_io_key_frame_previous)))
|
||||||
self->frame = self->surface;
|
self->frame = self->page;
|
||||||
gtk_widget_queue_draw(widget);
|
gtk_widget_queue_draw(widget);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
case GDK_KEY_bracketright:
|
case GDK_KEY_braceright:
|
||||||
if (!(self->frame = cairo_surface_get_user_data(
|
if (!(self->frame = cairo_surface_get_user_data(
|
||||||
self->frame, &fastiv_io_key_frame_next)))
|
self->frame, &fastiv_io_key_frame_next)))
|
||||||
self->frame = self->surface;
|
self->frame = self->page;
|
||||||
gtk_widget_queue_draw(widget);
|
gtk_widget_queue_draw(widget);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -527,14 +545,15 @@ fastiv_view_open(FastivView *self, const gchar *path, GError **error)
|
||||||
cairo_surface_t *surface = fastiv_io_open(path, error);
|
cairo_surface_t *surface = fastiv_io_open(path, error);
|
||||||
if (!surface)
|
if (!surface)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (self->surface)
|
if (self->image)
|
||||||
cairo_surface_destroy(self->surface);
|
cairo_surface_destroy(self->image);
|
||||||
|
|
||||||
self->frame = self->surface = surface;
|
self->frame = self->page = self->image = surface;
|
||||||
set_scale_to_fit(self, true);
|
set_scale_to_fit(self, true);
|
||||||
|
|
||||||
|
// TODO(p): This is actually per-page.
|
||||||
if ((self->orientation = (uintptr_t) cairo_surface_get_user_data(
|
if ((self->orientation = (uintptr_t) cairo_surface_get_user_data(
|
||||||
self->surface, &fastiv_io_key_orientation)) ==
|
self->image, &fastiv_io_key_orientation)) ==
|
||||||
FastivIoOrientationUnknown)
|
FastivIoOrientationUnknown)
|
||||||
self->orientation = FastivIoOrientation0;
|
self->orientation = FastivIoOrientation0;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
Loading…
Reference in New Issue