blob: 6a2bda318f291cf5838401738d9cc31715e5203c [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
Jonas Ådahl972d5062012-09-27 18:40:46 +020073clamp_view(struct image *image)
74{
75 struct rectangle allocation;
76 double scale = get_scale(image);
77 double sw, sh;
78
79 sw = image->width * scale;
80 sh = image->height * scale;
81 widget_get_allocation(image->widget, &allocation);
82
Kristian Høgsbergd3bf6762012-10-30 15:46:25 -040083 if (sw < allocation.width) {
84 image->matrix.x0 =
85 (allocation.width - image->width * scale) / 2;
86 } else {
87 if (image->matrix.x0 > 0.0)
88 image->matrix.x0 = 0.0;
89 if (sw + image->matrix.x0 < allocation.width)
90 image->matrix.x0 = allocation.width - sw;
91 }
92
93 if (sh < allocation.width) {
94 image->matrix.y0 =
95 (allocation.height - image->height * scale) / 2;
96 } else {
97 if (image->matrix.y0 > 0.0)
98 image->matrix.y0 = 0.0;
99 if (sh + image->matrix.y0 < allocation.height)
100 image->matrix.y0 = allocation.height - sh;
101 }
Jonas Ådahl972d5062012-09-27 18:40:46 +0200102}
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
Kristian Høgsbergd3bf6762012-10-30 15:46:25 -0400144 clamp_view(image);
Jonas Ådahl972d5062012-09-27 18:40:46 +0200145 }
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
Kristian Høgsbergd3bf6762012-10-30 15:46:25 -0400169 clamp_view(image);
Kristian Høgsberga369ff52012-10-30 15:09:49 -0400170}
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
Jonas Ådahl972d5062012-09-27 18:40:46 +0200199static void
200move_viewport(struct image *image, double dx, double dy)
201{
202 double scale = get_scale(image);
203
204 if (!image->initialized)
205 return;
206
207 cairo_matrix_translate(&image->matrix, -dx/scale, -dy/scale);
Kristian Høgsbergd3bf6762012-10-30 15:46:25 -0400208 clamp_view(image);
Jonas Ådahl972d5062012-09-27 18:40:46 +0200209
210 window_schedule_redraw(image->window);
211}
212
213static int
214motion_handler(struct widget *widget,
215 struct input *input, uint32_t time,
216 float x, float y, void *data)
217{
218 struct image *image = data;
219 struct rectangle allocation;
220
221 widget_get_allocation(image->widget, &allocation);
222 x -= allocation.x;
223 y -= allocation.y;
224
225 if (image->button_pressed)
226 move_viewport(image, image->pointer.x - x,
227 image->pointer.y - y);
228
229 image->pointer.x = x;
230 image->pointer.y = y;
231
232 return image->button_pressed ? CURSOR_DRAGGING : CURSOR_LEFT_PTR;
233}
234
235static void
236button_handler(struct widget *widget,
237 struct input *input, uint32_t time,
238 uint32_t button,
239 enum wl_pointer_button_state state,
240 void *data)
241{
242 struct image *image = data;
Jonas Ådahl972d5062012-09-27 18:40:46 +0200243
244 if (button == BTN_LEFT) {
Jonas Ådahl972d5062012-09-27 18:40:46 +0200245 image->button_pressed =
246 state == WL_POINTER_BUTTON_STATE_PRESSED;
247
Kristian Høgsberg94687082012-10-30 15:50:37 -0400248 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
249 input_set_pointer_image(input, CURSOR_DRAGGING);
250 else
Jonas Ådahl972d5062012-09-27 18:40:46 +0200251 input_set_pointer_image(input, CURSOR_LEFT_PTR);
252 }
253}
254
255static void
256zoom(struct image *image, double scale)
257{
258 double x = image->pointer.x;
259 double y = image->pointer.y;
260 cairo_matrix_t scale_matrix;
261
262 if (!image->initialized)
263 return;
264
265 if (get_scale(image) * scale > 20.0 ||
266 get_scale(image) * scale < 0.02)
267 return;
268
269 cairo_matrix_init_identity(&scale_matrix);
270 cairo_matrix_translate(&scale_matrix, x, y);
271 cairo_matrix_scale(&scale_matrix, scale, scale);
272 cairo_matrix_translate(&scale_matrix, -x, -y);
273
274 cairo_matrix_multiply(&image->matrix, &image->matrix, &scale_matrix);
Kristian Høgsbergd3bf6762012-10-30 15:46:25 -0400275 clamp_view(image);
Jonas Ådahl972d5062012-09-27 18:40:46 +0200276}
277
278static void
Kristian Høgsberg719b2152012-10-30 15:42:20 -0400279key_handler(struct window *window, struct input *input, uint32_t time,
280 uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
281 void *data)
282{
283 struct image *image = data;
284
285 if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
286 return;
287
288 switch (sym) {
289 case XKB_KEY_minus:
290 zoom(image, 0.8);
291 window_schedule_redraw(image->window);
292 break;
293 case XKB_KEY_equal:
294 case XKB_KEY_plus:
295 zoom(image, 1.2);
296 window_schedule_redraw(image->window);
297 break;
298 case XKB_KEY_1:
299 image->matrix.xx = 1.0;
300 image->matrix.xy = 0.0;
301 image->matrix.yx = 0.0;
302 image->matrix.yy = 1.0;
303 clamp_view(image);
304 window_schedule_redraw(image->window);
305 break;
306 }
307}
308
309static void
Jonas Ådahl972d5062012-09-27 18:40:46 +0200310axis_handler(struct widget *widget, struct input *input, uint32_t time,
311 uint32_t axis, wl_fixed_t value, void *data)
312{
313 struct image *image = data;
314
315 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
316 input_get_modifiers(input) == MOD_CONTROL_MASK) {
317 /* set zoom level to 2% per 10 axis units */
318 zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
319
320 window_schedule_redraw(image->window);
321 } else if (input_get_modifiers(input) == 0) {
322 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
323 move_viewport(image, 0, wl_fixed_to_double(value));
324 else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
325 move_viewport(image, wl_fixed_to_double(value), 0);
326 }
327}
328
Kristian Høgsbergdf0faf72012-07-23 22:00:21 -0400329static void
330fullscreen_handler(struct window *window, void *data)
331{
332 struct image *image = data;
333
334 image->fullscreen ^= 1;
335 window_set_fullscreen(window, image->fullscreen);
336}
337
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200338static void
339close_handler(struct window *window, void *data)
340{
341 struct image *image = data;
342
343 *image->image_counter -= 1;
344
345 if (*image->image_counter == 0)
346 display_exit(image->display);
347
348 widget_destroy(image->widget);
349 window_destroy(image->window);
350
351 free(image);
352}
353
Chris Wilson0de19eb2009-02-21 15:22:06 -0500354static struct image *
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200355image_create(struct display *display, const char *filename,
356 int *image_counter)
Chris Wilson0de19eb2009-02-21 15:22:06 -0500357{
358 struct image *image;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400359 char *b, *copy, title[512];;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500360
361 image = malloc(sizeof *image);
362 if (image == NULL)
363 return image;
364 memset(image, 0, sizeof *image);
365
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400366 copy = strdup(filename);
367 b = basename(copy);
368 snprintf(title, sizeof title, "Wayland Image - %s", b);
369 free(copy);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500370
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400371 image->filename = strdup(filename);
372 image->image = load_cairo_surface(filename);
Juan Zhao19a4c2d2012-08-06 19:45:43 -0700373
374 if (!image->image) {
375 fprintf(stderr, "could not find the image %s!\n", b);
376 return NULL;
377 }
378
Kristian Høgsberg009ac0a2012-01-31 15:24:48 -0500379 image->window = window_create(display);
Kristian Høgsberg29af3eb2012-01-10 22:41:05 -0500380 image->widget = frame_create(image->window, image);
Kristian Høgsberg248c1b62011-01-21 18:03:15 -0500381 window_set_title(image->window, title);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500382 image->display = display;
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200383 image->image_counter = image_counter;
384 *image_counter += 1;
Jonas Ådahl972d5062012-09-27 18:40:46 +0200385 image->initialized = false;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500386
Kristian Høgsbergc8c37342010-06-25 11:19:22 -0400387 window_set_user_data(image->window, image);
Kristian Høgsbergb67e94b2012-01-10 12:23:19 -0500388 widget_set_redraw_handler(image->widget, redraw_handler);
Kristian Høgsberga369ff52012-10-30 15:09:49 -0400389 widget_set_resize_handler(image->widget, resize_handler);
Kristian Høgsberg43788b12010-07-28 23:50:12 -0400390 window_set_keyboard_focus_handler(image->window,
391 keyboard_focus_handler);
Kristian Høgsbergdf0faf72012-07-23 22:00:21 -0400392 window_set_fullscreen_handler(image->window, fullscreen_handler);
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200393 window_set_close_handler(image->window, close_handler);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500394
Jonas Ådahl972d5062012-09-27 18:40:46 +0200395 widget_set_enter_handler(image->widget, enter_handler);
396 widget_set_motion_handler(image->widget, motion_handler);
397 widget_set_button_handler(image->widget, button_handler);
398 widget_set_axis_handler(image->widget, axis_handler);
Kristian Høgsberg719b2152012-10-30 15:42:20 -0400399 window_set_key_handler(image->window, key_handler);
Kristian Høgsbergbb977002012-01-10 19:11:42 -0500400 widget_schedule_resize(image->widget, 500, 400);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500401
402 return image;
403}
404
Chris Wilson0de19eb2009-02-21 15:22:06 -0500405int
406main(int argc, char *argv[])
407{
Chris Wilson0de19eb2009-02-21 15:22:06 -0500408 struct display *d;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500409 int i;
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200410 int image_counter = 0;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500411
Kristian Høgsbergbcacef12012-03-11 21:05:57 -0400412 d = display_create(argc, argv);
Yuval Fledele9f5e362010-11-22 21:34:19 +0200413 if (d == NULL) {
414 fprintf(stderr, "failed to create display: %m\n");
415 return -1;
416 }
Chris Wilson0de19eb2009-02-21 15:22:06 -0500417
Kristian Høgsberg00439612011-01-25 15:16:01 -0500418 for (i = 1; i < argc; i++)
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200419 image_create(d, argv[i], &image_counter);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500420
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200421 if (image_counter > 0)
422 display_run(d);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500423
424 return 0;
425}