blob: 2cdce416179d0680ff0a4c78fa5592ae4c973bea [file] [log] [blame]
Chris Wilson0de19eb2009-02-21 15:22:06 -05001/*
2 * Copyright © 2008 Kristian Høgsberg
3 * Copyright © 2009 Chris Wilson
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no representations
12 * about the suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24#include <stdint.h>
25#include <stdio.h>
26#include <stdlib.h>
Jonas Ådahl972d5062012-09-27 18:40:46 +020027#include <stdbool.h>
Chris Wilson0de19eb2009-02-21 15:22:06 -050028#include <string.h>
29#include <fcntl.h>
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040030#include <libgen.h>
Chris Wilson0de19eb2009-02-21 15:22:06 -050031#include <unistd.h>
32#include <math.h>
33#include <time.h>
34#include <cairo.h>
Jonas Ådahl972d5062012-09-27 18:40:46 +020035#include <assert.h>
36#include <linux/input.h>
Chris Wilson0de19eb2009-02-21 15:22:06 -050037
Pekka Paalanen50719bc2011-11-22 14:18:50 +020038#include <wayland-client.h>
Chris Wilson0de19eb2009-02-21 15:22:06 -050039
40#include "window.h"
Kristian Høgsberg5a315bc2012-05-15 22:33:43 -040041#include "../shared/cairo-util.h"
Chris Wilson0de19eb2009-02-21 15:22:06 -050042
Chris Wilson0de19eb2009-02-21 15:22:06 -050043struct image {
44 struct window *window;
Kristian Høgsbergb67e94b2012-01-10 12:23:19 -050045 struct widget *widget;
Chris Wilson0de19eb2009-02-21 15:22:06 -050046 struct display *display;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040047 char *filename;
48 cairo_surface_t *image;
Kristian Høgsbergdf0faf72012-07-23 22:00:21 -040049 int fullscreen;
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +020050 int *image_counter;
Jonas Ådahl972d5062012-09-27 18:40:46 +020051 int32_t width, height;
52
53 struct {
54 double x;
55 double y;
56 } pointer;
57 bool button_pressed;
58
59 bool initialized;
60 cairo_matrix_t matrix;
Chris Wilson0de19eb2009-02-21 15:22:06 -050061};
62
Jonas Ådahl972d5062012-09-27 18:40:46 +020063static double
64get_scale(struct image *image)
65{
66 assert(image->matrix.xy == 0.0 &&
67 image->matrix.yx == 0.0 &&
68 image->matrix.xx == image->matrix.yy);
69 return image->matrix.xx;
70}
71
72static void
73center_view(struct image *image)
74{
75 struct rectangle allocation;
76 double scale = get_scale(image);
77
78 widget_get_allocation(image->widget, &allocation);
79 image->matrix.x0 = (allocation.width - image->width * scale) / 2;
80 image->matrix.y0 = (allocation.height - image->height * scale) / 2;
81}
82
83static void
84clamp_view(struct image *image)
85{
86 struct rectangle allocation;
87 double scale = get_scale(image);
88 double sw, sh;
89
90 sw = image->width * scale;
91 sh = image->height * scale;
92 widget_get_allocation(image->widget, &allocation);
93
94 if (image->matrix.x0 > 0.0)
95 image->matrix.x0 = 0.0;
96 if (image->matrix.y0 > 0.0)
97 image->matrix.y0 = 0.0;
98 if (sw + image->matrix.x0 < allocation.width)
99 image->matrix.x0 = allocation.width - sw;
100 if (sh + image->matrix.y0 < allocation.height)
101 image->matrix.y0 = allocation.height - sh;
102}
103
Chris Wilson0de19eb2009-02-21 15:22:06 -0500104static void
Kristian Høgsbergb67e94b2012-01-10 12:23:19 -0500105redraw_handler(struct widget *widget, void *data)
Chris Wilson0de19eb2009-02-21 15:22:06 -0500106{
Kristian Høgsbergb67e94b2012-01-10 12:23:19 -0500107 struct image *image = data;
Kristian Høgsbergda846ca2011-01-11 10:00:52 -0500108 struct rectangle allocation;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500109 cairo_t *cr;
Kristian Høgsberge164e4e2011-01-21 11:35:05 -0500110 cairo_surface_t *surface;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400111 double width, height, doc_aspect, window_aspect, scale;
Jonas Ådahl972d5062012-09-27 18:40:46 +0200112 cairo_matrix_t matrix;
113 cairo_matrix_t translate;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500114
Kristian Høgsberge164e4e2011-01-21 11:35:05 -0500115 surface = window_get_surface(image->window);
Kristian Høgsberg09531622010-06-14 23:22:15 -0400116 cr = cairo_create(surface);
Kristian Høgsbergbb977002012-01-10 19:11:42 -0500117 widget_get_allocation(image->widget, &allocation);
Kristian Høgsberge164e4e2011-01-21 11:35:05 -0500118 cairo_rectangle(cr, allocation.x, allocation.y,
119 allocation.width, allocation.height);
120 cairo_clip(cr);
121 cairo_push_group(cr);
122 cairo_translate(cr, allocation.x, allocation.y);
123
Chris Wilson0de19eb2009-02-21 15:22:06 -0500124 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
125 cairo_set_source_rgba(cr, 0, 0, 0, 1);
126 cairo_paint(cr);
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400127
Jonas Ådahl972d5062012-09-27 18:40:46 +0200128 if (!image->initialized) {
129 image->initialized = true;
130 width = cairo_image_surface_get_width(image->image);
131 height = cairo_image_surface_get_height(image->image);
132
133 doc_aspect = width / height;
134 window_aspect = (double) allocation.width / allocation.height;
135 if (doc_aspect < window_aspect)
136 scale = allocation.height / height;
137 else
138 scale = allocation.width / width;
139
140 image->width = width;
141 image->height = height;
142 cairo_matrix_init_scale(&image->matrix, scale, scale);
143
144 center_view(image);
145 }
146
147 matrix = image->matrix;
148 cairo_matrix_init_translate(&translate, allocation.x, allocation.y);
149 cairo_matrix_multiply(&matrix, &matrix, &translate);
150 cairo_set_matrix(cr, &matrix);
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400151
152 cairo_set_source_surface(cr, image->image, 0, 0);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500153 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
154 cairo_paint(cr);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500155
Kristian Høgsberge164e4e2011-01-21 11:35:05 -0500156 cairo_pop_group_to_source(cr);
157 cairo_paint(cr);
158 cairo_destroy(cr);
159
Kristian Høgsberg09531622010-06-14 23:22:15 -0400160 cairo_surface_destroy(surface);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500161}
162
Kristian Høgsberg80d746f2010-06-14 23:52:50 -0400163static void
Kristian Høgsberga369ff52012-10-30 15:09:49 -0400164resize_handler(struct widget *widget,
165 int32_t width, int32_t height, void *data)
166{
167 struct image *image = data;
168
169 center_view(image);
170}
171
172static void
Kristian Høgsberga341fa02010-01-24 18:10:15 -0500173keyboard_focus_handler(struct window *window,
Kristian Høgsberg43788b12010-07-28 23:50:12 -0400174 struct input *device, void *data)
Kristian Høgsberga341fa02010-01-24 18:10:15 -0500175{
176 struct image *image = data;
177
Kristian Høgsberg80d746f2010-06-14 23:52:50 -0400178 window_schedule_redraw(image->window);
Kristian Høgsberga341fa02010-01-24 18:10:15 -0500179}
180
Jonas Ådahl972d5062012-09-27 18:40:46 +0200181static int
182enter_handler(struct widget *widget,
183 struct input *input,
184 float x, float y, void *data)
185{
186 struct image *image = data;
187 struct rectangle allocation;
188
189 widget_get_allocation(image->widget, &allocation);
190 x -= allocation.x;
191 y -= allocation.y;
192
193 image->pointer.x = x;
194 image->pointer.y = y;
195
196 return 1;
197}
198
199static bool
200image_is_smaller(struct image *image)
201{
202 double scale;
203 struct rectangle allocation;
204
205 scale = get_scale(image);
206 widget_get_allocation(image->widget, &allocation);
207
208 return scale * image->width < allocation.width;
209}
210
211static void
212move_viewport(struct image *image, double dx, double dy)
213{
214 double scale = get_scale(image);
215
216 if (!image->initialized)
217 return;
218
219 cairo_matrix_translate(&image->matrix, -dx/scale, -dy/scale);
220
221 if (image_is_smaller(image))
222 center_view(image);
223 else
224 clamp_view(image);
225
226 window_schedule_redraw(image->window);
227}
228
229static int
230motion_handler(struct widget *widget,
231 struct input *input, uint32_t time,
232 float x, float y, void *data)
233{
234 struct image *image = data;
235 struct rectangle allocation;
236
237 widget_get_allocation(image->widget, &allocation);
238 x -= allocation.x;
239 y -= allocation.y;
240
241 if (image->button_pressed)
242 move_viewport(image, image->pointer.x - x,
243 image->pointer.y - y);
244
245 image->pointer.x = x;
246 image->pointer.y = y;
247
248 return image->button_pressed ? CURSOR_DRAGGING : CURSOR_LEFT_PTR;
249}
250
251static void
252button_handler(struct widget *widget,
253 struct input *input, uint32_t time,
254 uint32_t button,
255 enum wl_pointer_button_state state,
256 void *data)
257{
258 struct image *image = data;
259 bool was_pressed;
260
261 if (button == BTN_LEFT) {
262 was_pressed = image->button_pressed;
263 image->button_pressed =
264 state == WL_POINTER_BUTTON_STATE_PRESSED;
265
266 if (!image->button_pressed && was_pressed)
267 input_set_pointer_image(input, CURSOR_LEFT_PTR);
268 }
269}
270
271static void
272zoom(struct image *image, double scale)
273{
274 double x = image->pointer.x;
275 double y = image->pointer.y;
276 cairo_matrix_t scale_matrix;
277
278 if (!image->initialized)
279 return;
280
281 if (get_scale(image) * scale > 20.0 ||
282 get_scale(image) * scale < 0.02)
283 return;
284
285 cairo_matrix_init_identity(&scale_matrix);
286 cairo_matrix_translate(&scale_matrix, x, y);
287 cairo_matrix_scale(&scale_matrix, scale, scale);
288 cairo_matrix_translate(&scale_matrix, -x, -y);
289
290 cairo_matrix_multiply(&image->matrix, &image->matrix, &scale_matrix);
291 if (image_is_smaller(image))
292 center_view(image);
293}
294
295static void
Kristian Høgsberg719b2152012-10-30 15:42:20 -0400296key_handler(struct window *window, struct input *input, uint32_t time,
297 uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
298 void *data)
299{
300 struct image *image = data;
301
302 if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
303 return;
304
305 switch (sym) {
306 case XKB_KEY_minus:
307 zoom(image, 0.8);
308 window_schedule_redraw(image->window);
309 break;
310 case XKB_KEY_equal:
311 case XKB_KEY_plus:
312 zoom(image, 1.2);
313 window_schedule_redraw(image->window);
314 break;
315 case XKB_KEY_1:
316 image->matrix.xx = 1.0;
317 image->matrix.xy = 0.0;
318 image->matrix.yx = 0.0;
319 image->matrix.yy = 1.0;
320 clamp_view(image);
321 window_schedule_redraw(image->window);
322 break;
323 }
324}
325
326static void
Jonas Ådahl972d5062012-09-27 18:40:46 +0200327axis_handler(struct widget *widget, struct input *input, uint32_t time,
328 uint32_t axis, wl_fixed_t value, void *data)
329{
330 struct image *image = data;
331
332 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
333 input_get_modifiers(input) == MOD_CONTROL_MASK) {
334 /* set zoom level to 2% per 10 axis units */
335 zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
336
337 window_schedule_redraw(image->window);
338 } else if (input_get_modifiers(input) == 0) {
339 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
340 move_viewport(image, 0, wl_fixed_to_double(value));
341 else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
342 move_viewport(image, wl_fixed_to_double(value), 0);
343 }
344}
345
Kristian Høgsbergdf0faf72012-07-23 22:00:21 -0400346static void
347fullscreen_handler(struct window *window, void *data)
348{
349 struct image *image = data;
350
351 image->fullscreen ^= 1;
352 window_set_fullscreen(window, image->fullscreen);
353}
354
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200355static void
356close_handler(struct window *window, void *data)
357{
358 struct image *image = data;
359
360 *image->image_counter -= 1;
361
362 if (*image->image_counter == 0)
363 display_exit(image->display);
364
365 widget_destroy(image->widget);
366 window_destroy(image->window);
367
368 free(image);
369}
370
Chris Wilson0de19eb2009-02-21 15:22:06 -0500371static struct image *
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200372image_create(struct display *display, const char *filename,
373 int *image_counter)
Chris Wilson0de19eb2009-02-21 15:22:06 -0500374{
375 struct image *image;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400376 char *b, *copy, title[512];;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500377
378 image = malloc(sizeof *image);
379 if (image == NULL)
380 return image;
381 memset(image, 0, sizeof *image);
382
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400383 copy = strdup(filename);
384 b = basename(copy);
385 snprintf(title, sizeof title, "Wayland Image - %s", b);
386 free(copy);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500387
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400388 image->filename = strdup(filename);
389 image->image = load_cairo_surface(filename);
Juan Zhao19a4c2d2012-08-06 19:45:43 -0700390
391 if (!image->image) {
392 fprintf(stderr, "could not find the image %s!\n", b);
393 return NULL;
394 }
395
Kristian Høgsberg009ac0a2012-01-31 15:24:48 -0500396 image->window = window_create(display);
Kristian Høgsberg29af3eb2012-01-10 22:41:05 -0500397 image->widget = frame_create(image->window, image);
Kristian Høgsberg248c1b62011-01-21 18:03:15 -0500398 window_set_title(image->window, title);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500399 image->display = display;
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200400 image->image_counter = image_counter;
401 *image_counter += 1;
Jonas Ådahl972d5062012-09-27 18:40:46 +0200402 image->initialized = false;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500403
Kristian Høgsbergc8c37342010-06-25 11:19:22 -0400404 window_set_user_data(image->window, image);
Kristian Høgsbergb67e94b2012-01-10 12:23:19 -0500405 widget_set_redraw_handler(image->widget, redraw_handler);
Kristian Høgsberga369ff52012-10-30 15:09:49 -0400406 widget_set_resize_handler(image->widget, resize_handler);
Kristian Høgsberg43788b12010-07-28 23:50:12 -0400407 window_set_keyboard_focus_handler(image->window,
408 keyboard_focus_handler);
Kristian Høgsbergdf0faf72012-07-23 22:00:21 -0400409 window_set_fullscreen_handler(image->window, fullscreen_handler);
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200410 window_set_close_handler(image->window, close_handler);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500411
Jonas Ådahl972d5062012-09-27 18:40:46 +0200412 widget_set_enter_handler(image->widget, enter_handler);
413 widget_set_motion_handler(image->widget, motion_handler);
414 widget_set_button_handler(image->widget, button_handler);
415 widget_set_axis_handler(image->widget, axis_handler);
Kristian Høgsberg719b2152012-10-30 15:42:20 -0400416 window_set_key_handler(image->window, key_handler);
Kristian Høgsbergbb977002012-01-10 19:11:42 -0500417 widget_schedule_resize(image->widget, 500, 400);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500418
419 return image;
420}
421
Chris Wilson0de19eb2009-02-21 15:22:06 -0500422int
423main(int argc, char *argv[])
424{
Chris Wilson0de19eb2009-02-21 15:22:06 -0500425 struct display *d;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500426 int i;
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200427 int image_counter = 0;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500428
Kristian Høgsbergbcacef12012-03-11 21:05:57 -0400429 d = display_create(argc, argv);
Yuval Fledele9f5e362010-11-22 21:34:19 +0200430 if (d == NULL) {
431 fprintf(stderr, "failed to create display: %m\n");
432 return -1;
433 }
Chris Wilson0de19eb2009-02-21 15:22:06 -0500434
Kristian Høgsberg00439612011-01-25 15:16:01 -0500435 for (i = 1; i < argc; i++)
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200436 image_create(d, argv[i], &image_counter);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500437
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200438 if (image_counter > 0)
439 display_run(d);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500440
441 return 0;
442}