summaryrefslogtreecommitdiff
path: root/clients/image.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/image.c')
-rw-r--r--clients/image.c426
1 files changed, 426 insertions, 0 deletions
diff --git a/clients/image.c b/clients/image.c
new file mode 100644
index 00000000..4d3f3b87
--- /dev/null
+++ b/clients/image.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ * Copyright © 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <cairo.h>
+#include <assert.h>
+#include <linux/input.h>
+
+#include <wayland-client.h>
+
+#include "window.h"
+#include "../shared/cairo-util.h"
+
+struct image {
+ struct window *window;
+ struct widget *widget;
+ struct display *display;
+ char *filename;
+ cairo_surface_t *image;
+ int fullscreen;
+ int *image_counter;
+ int32_t width, height;
+
+ struct {
+ double x;
+ double y;
+ } pointer;
+ bool button_pressed;
+
+ bool initialized;
+ cairo_matrix_t matrix;
+};
+
+static double
+get_scale(struct image *image)
+{
+ assert(image->matrix.xy == 0.0 &&
+ image->matrix.yx == 0.0 &&
+ image->matrix.xx == image->matrix.yy);
+ return image->matrix.xx;
+}
+
+static void
+clamp_view(struct image *image)
+{
+ struct rectangle allocation;
+ double scale = get_scale(image);
+ double sw, sh;
+
+ sw = image->width * scale;
+ sh = image->height * scale;
+ widget_get_allocation(image->widget, &allocation);
+
+ if (sw < allocation.width) {
+ image->matrix.x0 =
+ (allocation.width - image->width * scale) / 2;
+ } else {
+ if (image->matrix.x0 > 0.0)
+ image->matrix.x0 = 0.0;
+ if (sw + image->matrix.x0 < allocation.width)
+ image->matrix.x0 = allocation.width - sw;
+ }
+
+ if (sh < allocation.width) {
+ image->matrix.y0 =
+ (allocation.height - image->height * scale) / 2;
+ } else {
+ if (image->matrix.y0 > 0.0)
+ image->matrix.y0 = 0.0;
+ if (sh + image->matrix.y0 < allocation.height)
+ image->matrix.y0 = allocation.height - sh;
+ }
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+ struct image *image = data;
+ struct rectangle allocation;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ double width, height, doc_aspect, window_aspect, scale;
+ cairo_matrix_t matrix;
+ cairo_matrix_t translate;
+
+ surface = window_get_surface(image->window);
+ cr = cairo_create(surface);
+ widget_get_allocation(image->widget, &allocation);
+ cairo_rectangle(cr, allocation.x, allocation.y,
+ allocation.width, allocation.height);
+ cairo_clip(cr);
+ cairo_push_group(cr);
+ cairo_translate(cr, allocation.x, allocation.y);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba(cr, 0, 0, 0, 1);
+ cairo_paint(cr);
+
+ if (!image->initialized) {
+ image->initialized = true;
+ width = cairo_image_surface_get_width(image->image);
+ height = cairo_image_surface_get_height(image->image);
+
+ doc_aspect = width / height;
+ window_aspect = (double) allocation.width / allocation.height;
+ if (doc_aspect < window_aspect)
+ scale = allocation.height / height;
+ else
+ scale = allocation.width / width;
+
+ image->width = width;
+ image->height = height;
+ cairo_matrix_init_scale(&image->matrix, scale, scale);
+
+ clamp_view(image);
+ }
+
+ matrix = image->matrix;
+ cairo_matrix_init_translate(&translate, allocation.x, allocation.y);
+ cairo_matrix_multiply(&matrix, &matrix, &translate);
+ cairo_set_matrix(cr, &matrix);
+
+ cairo_set_source_surface(cr, image->image, 0, 0);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_paint(cr);
+
+ cairo_pop_group_to_source(cr);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+}
+
+static void
+resize_handler(struct widget *widget,
+ int32_t width, int32_t height, void *data)
+{
+ struct image *image = data;
+
+ clamp_view(image);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+ struct input *device, void *data)
+{
+ struct image *image = data;
+
+ window_schedule_redraw(image->window);
+}
+
+static int
+enter_handler(struct widget *widget,
+ struct input *input,
+ float x, float y, void *data)
+{
+ struct image *image = data;
+ struct rectangle allocation;
+
+ widget_get_allocation(image->widget, &allocation);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ image->pointer.x = x;
+ image->pointer.y = y;
+
+ return 1;
+}
+
+static void
+move_viewport(struct image *image, double dx, double dy)
+{
+ double scale = get_scale(image);
+
+ if (!image->initialized)
+ return;
+
+ cairo_matrix_translate(&image->matrix, -dx/scale, -dy/scale);
+ clamp_view(image);
+
+ window_schedule_redraw(image->window);
+}
+
+static int
+motion_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ float x, float y, void *data)
+{
+ struct image *image = data;
+ struct rectangle allocation;
+
+ widget_get_allocation(image->widget, &allocation);
+ x -= allocation.x;
+ y -= allocation.y;
+
+ if (image->button_pressed)
+ move_viewport(image, image->pointer.x - x,
+ image->pointer.y - y);
+
+ image->pointer.x = x;
+ image->pointer.y = y;
+
+ return image->button_pressed ? CURSOR_DRAGGING : CURSOR_LEFT_PTR;
+}
+
+static void
+button_handler(struct widget *widget,
+ struct input *input, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state state,
+ void *data)
+{
+ struct image *image = data;
+
+ if (button == BTN_LEFT) {
+ image->button_pressed =
+ state == WL_POINTER_BUTTON_STATE_PRESSED;
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED)
+ input_set_pointer_image(input, CURSOR_DRAGGING);
+ else
+ input_set_pointer_image(input, CURSOR_LEFT_PTR);
+ }
+}
+
+static void
+zoom(struct image *image, double scale)
+{
+ double x = image->pointer.x;
+ double y = image->pointer.y;
+ cairo_matrix_t scale_matrix;
+
+ if (!image->initialized)
+ return;
+
+ if (get_scale(image) * scale > 20.0 ||
+ get_scale(image) * scale < 0.02)
+ return;
+
+ cairo_matrix_init_identity(&scale_matrix);
+ cairo_matrix_translate(&scale_matrix, x, y);
+ cairo_matrix_scale(&scale_matrix, scale, scale);
+ cairo_matrix_translate(&scale_matrix, -x, -y);
+
+ cairo_matrix_multiply(&image->matrix, &image->matrix, &scale_matrix);
+ clamp_view(image);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+ uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
+ void *data)
+{
+ struct image *image = data;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ switch (sym) {
+ case XKB_KEY_minus:
+ zoom(image, 0.8);
+ window_schedule_redraw(image->window);
+ break;
+ case XKB_KEY_equal:
+ case XKB_KEY_plus:
+ zoom(image, 1.2);
+ window_schedule_redraw(image->window);
+ break;
+ case XKB_KEY_1:
+ image->matrix.xx = 1.0;
+ image->matrix.xy = 0.0;
+ image->matrix.yx = 0.0;
+ image->matrix.yy = 1.0;
+ clamp_view(image);
+ window_schedule_redraw(image->window);
+ break;
+ }
+}
+
+static void
+axis_handler(struct widget *widget, struct input *input, uint32_t time,
+ uint32_t axis, wl_fixed_t value, void *data)
+{
+ struct image *image = data;
+
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
+ input_get_modifiers(input) == MOD_CONTROL_MASK) {
+ /* set zoom level to 2% per 10 axis units */
+ zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
+
+ window_schedule_redraw(image->window);
+ } else if (input_get_modifiers(input) == 0) {
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
+ move_viewport(image, 0, wl_fixed_to_double(value));
+ else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
+ move_viewport(image, wl_fixed_to_double(value), 0);
+ }
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+ struct image *image = data;
+
+ image->fullscreen ^= 1;
+ window_set_fullscreen(window, image->fullscreen);
+}
+
+static void
+close_handler(struct window *window, void *data)
+{
+ struct image *image = data;
+
+ *image->image_counter -= 1;
+
+ if (*image->image_counter == 0)
+ display_exit(image->display);
+
+ widget_destroy(image->widget);
+ window_destroy(image->window);
+
+ free(image);
+}
+
+static struct image *
+image_create(struct display *display, const char *filename,
+ int *image_counter)
+{
+ struct image *image;
+ char *b, *copy, title[512];;
+
+ image = zalloc(sizeof *image);
+ if (image == NULL)
+ return image;
+
+ copy = strdup(filename);
+ b = basename(copy);
+ snprintf(title, sizeof title, "Wayland Image - %s", b);
+ free(copy);
+
+ image->filename = strdup(filename);
+ image->image = load_cairo_surface(filename);
+
+ if (!image->image) {
+ fprintf(stderr, "could not find the image %s!\n", b);
+ free(image->filename);
+ free(image);
+ return NULL;
+ }
+
+ image->window = window_create(display);
+ image->widget = frame_create(image->window, image);
+ window_set_title(image->window, title);
+ image->display = display;
+ image->image_counter = image_counter;
+ *image_counter += 1;
+ image->initialized = false;
+
+ window_set_user_data(image->window, image);
+ widget_set_redraw_handler(image->widget, redraw_handler);
+ widget_set_resize_handler(image->widget, resize_handler);
+ window_set_keyboard_focus_handler(image->window,
+ keyboard_focus_handler);
+ window_set_fullscreen_handler(image->window, fullscreen_handler);
+ window_set_close_handler(image->window, close_handler);
+
+ widget_set_enter_handler(image->widget, enter_handler);
+ widget_set_motion_handler(image->widget, motion_handler);
+ widget_set_button_handler(image->widget, button_handler);
+ widget_set_axis_handler(image->widget, axis_handler);
+ window_set_key_handler(image->window, key_handler);
+ widget_schedule_resize(image->widget, 500, 400);
+
+ return image;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct display *d;
+ int i;
+ int image_counter = 0;
+
+ d = display_create(&argc, argv);
+ if (d == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ for (i = 1; i < argc; i++)
+ image_create(d, argv[i], &image_counter);
+
+ if (image_counter > 0)
+ display_run(d);
+
+ return 0;
+}