blob: 14f05507ccbf7b5bca02ed8c554266fe0e4a46c3 [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øgsberga341fa02010-01-24 18:10:15 -0500164keyboard_focus_handler(struct window *window,
Kristian Høgsberg43788b12010-07-28 23:50:12 -0400165 struct input *device, void *data)
Kristian Høgsberga341fa02010-01-24 18:10:15 -0500166{
167 struct image *image = data;
168
Kristian Høgsberg80d746f2010-06-14 23:52:50 -0400169 window_schedule_redraw(image->window);
Kristian Høgsberga341fa02010-01-24 18:10:15 -0500170}
171
Jonas Ådahl972d5062012-09-27 18:40:46 +0200172static int
173enter_handler(struct widget *widget,
174 struct input *input,
175 float x, float y, void *data)
176{
177 struct image *image = data;
178 struct rectangle allocation;
179
180 widget_get_allocation(image->widget, &allocation);
181 x -= allocation.x;
182 y -= allocation.y;
183
184 image->pointer.x = x;
185 image->pointer.y = y;
186
187 return 1;
188}
189
190static bool
191image_is_smaller(struct image *image)
192{
193 double scale;
194 struct rectangle allocation;
195
196 scale = get_scale(image);
197 widget_get_allocation(image->widget, &allocation);
198
199 return scale * image->width < allocation.width;
200}
201
202static void
203move_viewport(struct image *image, double dx, double dy)
204{
205 double scale = get_scale(image);
206
207 if (!image->initialized)
208 return;
209
210 cairo_matrix_translate(&image->matrix, -dx/scale, -dy/scale);
211
212 if (image_is_smaller(image))
213 center_view(image);
214 else
215 clamp_view(image);
216
217 window_schedule_redraw(image->window);
218}
219
220static int
221motion_handler(struct widget *widget,
222 struct input *input, uint32_t time,
223 float x, float y, void *data)
224{
225 struct image *image = data;
226 struct rectangle allocation;
227
228 widget_get_allocation(image->widget, &allocation);
229 x -= allocation.x;
230 y -= allocation.y;
231
232 if (image->button_pressed)
233 move_viewport(image, image->pointer.x - x,
234 image->pointer.y - y);
235
236 image->pointer.x = x;
237 image->pointer.y = y;
238
239 return image->button_pressed ? CURSOR_DRAGGING : CURSOR_LEFT_PTR;
240}
241
242static void
243button_handler(struct widget *widget,
244 struct input *input, uint32_t time,
245 uint32_t button,
246 enum wl_pointer_button_state state,
247 void *data)
248{
249 struct image *image = data;
250 bool was_pressed;
251
252 if (button == BTN_LEFT) {
253 was_pressed = image->button_pressed;
254 image->button_pressed =
255 state == WL_POINTER_BUTTON_STATE_PRESSED;
256
257 if (!image->button_pressed && was_pressed)
258 input_set_pointer_image(input, CURSOR_LEFT_PTR);
259 }
260}
261
262static void
263zoom(struct image *image, double scale)
264{
265 double x = image->pointer.x;
266 double y = image->pointer.y;
267 cairo_matrix_t scale_matrix;
268
269 if (!image->initialized)
270 return;
271
272 if (get_scale(image) * scale > 20.0 ||
273 get_scale(image) * scale < 0.02)
274 return;
275
276 cairo_matrix_init_identity(&scale_matrix);
277 cairo_matrix_translate(&scale_matrix, x, y);
278 cairo_matrix_scale(&scale_matrix, scale, scale);
279 cairo_matrix_translate(&scale_matrix, -x, -y);
280
281 cairo_matrix_multiply(&image->matrix, &image->matrix, &scale_matrix);
282 if (image_is_smaller(image))
283 center_view(image);
284}
285
286static void
287axis_handler(struct widget *widget, struct input *input, uint32_t time,
288 uint32_t axis, wl_fixed_t value, void *data)
289{
290 struct image *image = data;
291
292 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
293 input_get_modifiers(input) == MOD_CONTROL_MASK) {
294 /* set zoom level to 2% per 10 axis units */
295 zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
296
297 window_schedule_redraw(image->window);
298 } else if (input_get_modifiers(input) == 0) {
299 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
300 move_viewport(image, 0, wl_fixed_to_double(value));
301 else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
302 move_viewport(image, wl_fixed_to_double(value), 0);
303 }
304}
305
Kristian Høgsbergdf0faf72012-07-23 22:00:21 -0400306static void
307fullscreen_handler(struct window *window, void *data)
308{
309 struct image *image = data;
310
311 image->fullscreen ^= 1;
312 window_set_fullscreen(window, image->fullscreen);
313}
314
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200315static void
316close_handler(struct window *window, void *data)
317{
318 struct image *image = data;
319
320 *image->image_counter -= 1;
321
322 if (*image->image_counter == 0)
323 display_exit(image->display);
324
325 widget_destroy(image->widget);
326 window_destroy(image->window);
327
328 free(image);
329}
330
Chris Wilson0de19eb2009-02-21 15:22:06 -0500331static struct image *
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200332image_create(struct display *display, const char *filename,
333 int *image_counter)
Chris Wilson0de19eb2009-02-21 15:22:06 -0500334{
335 struct image *image;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400336 char *b, *copy, title[512];;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500337
338 image = malloc(sizeof *image);
339 if (image == NULL)
340 return image;
341 memset(image, 0, sizeof *image);
342
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400343 copy = strdup(filename);
344 b = basename(copy);
345 snprintf(title, sizeof title, "Wayland Image - %s", b);
346 free(copy);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500347
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400348 image->filename = strdup(filename);
349 image->image = load_cairo_surface(filename);
Juan Zhao19a4c2d2012-08-06 19:45:43 -0700350
351 if (!image->image) {
352 fprintf(stderr, "could not find the image %s!\n", b);
353 return NULL;
354 }
355
Kristian Høgsberg009ac0a2012-01-31 15:24:48 -0500356 image->window = window_create(display);
Kristian Høgsberg29af3eb2012-01-10 22:41:05 -0500357 image->widget = frame_create(image->window, image);
Kristian Høgsberg248c1b62011-01-21 18:03:15 -0500358 window_set_title(image->window, title);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500359 image->display = display;
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200360 image->image_counter = image_counter;
361 *image_counter += 1;
Jonas Ådahl972d5062012-09-27 18:40:46 +0200362 image->initialized = false;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500363
Kristian Høgsbergc8c37342010-06-25 11:19:22 -0400364 window_set_user_data(image->window, image);
Kristian Høgsbergb67e94b2012-01-10 12:23:19 -0500365 widget_set_redraw_handler(image->widget, redraw_handler);
Kristian Høgsberg43788b12010-07-28 23:50:12 -0400366 window_set_keyboard_focus_handler(image->window,
367 keyboard_focus_handler);
Kristian Høgsbergdf0faf72012-07-23 22:00:21 -0400368 window_set_fullscreen_handler(image->window, fullscreen_handler);
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200369 window_set_close_handler(image->window, close_handler);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500370
Jonas Ådahl972d5062012-09-27 18:40:46 +0200371 widget_set_enter_handler(image->widget, enter_handler);
372 widget_set_motion_handler(image->widget, motion_handler);
373 widget_set_button_handler(image->widget, button_handler);
374 widget_set_axis_handler(image->widget, axis_handler);
Kristian Høgsbergbb977002012-01-10 19:11:42 -0500375 widget_schedule_resize(image->widget, 500, 400);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500376
377 return image;
378}
379
Chris Wilson0de19eb2009-02-21 15:22:06 -0500380int
381main(int argc, char *argv[])
382{
Chris Wilson0de19eb2009-02-21 15:22:06 -0500383 struct display *d;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500384 int i;
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200385 int image_counter = 0;
Chris Wilson0de19eb2009-02-21 15:22:06 -0500386
Kristian Høgsbergbcacef12012-03-11 21:05:57 -0400387 d = display_create(argc, argv);
Yuval Fledele9f5e362010-11-22 21:34:19 +0200388 if (d == NULL) {
389 fprintf(stderr, "failed to create display: %m\n");
390 return -1;
391 }
Chris Wilson0de19eb2009-02-21 15:22:06 -0500392
Kristian Høgsberg00439612011-01-25 15:16:01 -0500393 for (i = 1; i < argc; i++)
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200394 image_create(d, argv[i], &image_counter);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500395
Philipp Brüschweiler1f54f172012-08-13 21:16:47 +0200396 if (image_counter > 0)
397 display_run(d);
Chris Wilson0de19eb2009-02-21 15:22:06 -0500398
399 return 0;
400}