blob: 2804dbf1f823dfd8a7e2fc68ccbb6dc8ec9c8ed3 [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
296axis_handler(struct widget *widget, struct input *input, uint32_t time,
297 uint32_t axis, wl_fixed_t value, void *data)
298{
299 struct image *image = data;
300
301 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
302 input_get_modifiers(input) == MOD_CONTROL_MASK) {
303 /* set zoom level to 2% per 10 axis units */
304 zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
305
306 window_schedule_redraw(image->window);
307 } else if (input_get_modifiers(input) == 0) {
308 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
309 move_viewport(image, 0, wl_fixed_to_double(value));
310 else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
311 move_viewport(image, wl_fixed_to_double(value), 0);
312 }
313}
314
Kristian Høgsbergdf0faf72012-07-23 22:00:21 -0400315static void
316fullscreen_handler(struct window *window, void *data)
317{
318 struct image *image = data;
319
320 image->fullscreen ^= 1;
321 window_set_fullscreen(window, image->fullscreen);
322}
323
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200324static void
325close_handler(struct window *window, void *data)
326{
327 struct image *image = data;
328
329 *image->image_counter -= 1;
330
331 if (*image->image_counter == 0)
332 display_exit(image->display);
333
334 widget_destroy(image->widget);
335 window_destroy(image->window);
336
337 free(image);
338}
339
Chris Wilson0de19eb2009-02-21 15:22:06 -0500340static struct image *
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200341image_create(struct display *display, const char *filename,
342 int *image_counter)
Chris Wilson0de19eb2009-02-21 15:22:06 -0500343{
344 struct image *image;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400345 char *b, *copy, title[512];;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500346
347 image = malloc(sizeof *image);
348 if (image == NULL)
349 return image;
350 memset(image, 0, sizeof *image);
351
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400352 copy = strdup(filename);
353 b = basename(copy);
354 snprintf(title, sizeof title, "Wayland Image - %s", b);
355 free(copy);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500356
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400357 image->filename = strdup(filename);
358 image->image = load_cairo_surface(filename);
Juan Zhao19a4c2d2012-08-06 19:45:43 -0700359
360 if (!image->image) {
361 fprintf(stderr, "could not find the image %s!\n", b);
362 return NULL;
363 }
364
Kristian Høgsberg009ac0a2012-01-31 15:24:48 -0500365 image->window = window_create(display);
Kristian Høgsberg29af3eb2012-01-10 22:41:05 -0500366 image->widget = frame_create(image->window, image);
Kristian Høgsberg248c1b62011-01-21 18:03:15 -0500367 window_set_title(image->window, title);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500368 image->display = display;
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200369 image->image_counter = image_counter;
370 *image_counter += 1;
Jonas Ådahl972d5062012-09-27 18:40:46 +0200371 image->initialized = false;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500372
Kristian Høgsbergc8c37342010-06-25 11:19:22 -0400373 window_set_user_data(image->window, image);
Kristian Høgsbergb67e94b2012-01-10 12:23:19 -0500374 widget_set_redraw_handler(image->widget, redraw_handler);
Kristian Høgsberga369ff52012-10-30 15:09:49 -0400375 widget_set_resize_handler(image->widget, resize_handler);
Kristian Høgsberg43788b12010-07-28 23:50:12 -0400376 window_set_keyboard_focus_handler(image->window,
377 keyboard_focus_handler);
Kristian Høgsbergdf0faf72012-07-23 22:00:21 -0400378 window_set_fullscreen_handler(image->window, fullscreen_handler);
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200379 window_set_close_handler(image->window, close_handler);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500380
Jonas Ådahl972d5062012-09-27 18:40:46 +0200381 widget_set_enter_handler(image->widget, enter_handler);
382 widget_set_motion_handler(image->widget, motion_handler);
383 widget_set_button_handler(image->widget, button_handler);
384 widget_set_axis_handler(image->widget, axis_handler);
Kristian Høgsbergbb977002012-01-10 19:11:42 -0500385 widget_schedule_resize(image->widget, 500, 400);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500386
387 return image;
388}
389
Chris Wilson0de19eb2009-02-21 15:22:06 -0500390int
391main(int argc, char *argv[])
392{
Chris Wilson0de19eb2009-02-21 15:22:06 -0500393 struct display *d;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500394 int i;
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200395 int image_counter = 0;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500396
Kristian Høgsbergbcacef12012-03-11 21:05:57 -0400397 d = display_create(argc, argv);
Yuval Fledele9f5e362010-11-22 21:34:19 +0200398 if (d == NULL) {
399 fprintf(stderr, "failed to create display: %m\n");
400 return -1;
401 }
Chris Wilson0de19eb2009-02-21 15:22:06 -0500402
Kristian Høgsberg00439612011-01-25 15:16:01 -0500403 for (i = 1; i < argc; i++)
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200404 image_create(d, argv[i], &image_counter);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500405
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200406 if (image_counter > 0)
407 display_run(d);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500408
409 return 0;
410}