compositor-x11: Rename the output make to "weston-X11"
[platform/upstream/weston.git] / clients / image.c
1 /*
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 "config.h"
25
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdbool.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <libgen.h>
33 #include <unistd.h>
34 #include <math.h>
35 #include <time.h>
36 #include <cairo.h>
37 #include <assert.h>
38 #include <linux/input.h>
39
40 #include <wayland-client.h>
41
42 #include "window.h"
43 #include "../shared/cairo-util.h"
44
45 struct image {
46         struct window *window;
47         struct widget *widget;
48         struct display *display;
49         char *filename;
50         cairo_surface_t *image;
51         int fullscreen;
52         int *image_counter;
53         int32_t width, height;
54
55         struct {
56                 double x;
57                 double y;
58         } pointer;
59         bool button_pressed;
60
61         bool initialized;
62         cairo_matrix_t matrix;
63 };
64
65 static double
66 get_scale(struct image *image)
67 {
68         assert(image->matrix.xy == 0.0 &&
69                image->matrix.yx == 0.0 &&
70                image->matrix.xx == image->matrix.yy);
71         return image->matrix.xx;
72 }
73
74 static void
75 clamp_view(struct image *image)
76 {
77         struct rectangle allocation;
78         double scale = get_scale(image);
79         double sw, sh;
80
81         sw = image->width * scale;
82         sh = image->height * scale;
83         widget_get_allocation(image->widget, &allocation);
84
85         if (sw < allocation.width) {
86                 image->matrix.x0 =
87                         (allocation.width - image->width * scale) / 2;
88         } else {
89                 if (image->matrix.x0 > 0.0)
90                         image->matrix.x0 = 0.0;
91                 if (sw + image->matrix.x0 < allocation.width)
92                         image->matrix.x0 = allocation.width - sw;
93         }
94
95         if (sh < allocation.width) {
96                 image->matrix.y0 =
97                         (allocation.height - image->height * scale) / 2;
98         } else {
99                 if (image->matrix.y0 > 0.0)
100                         image->matrix.y0 = 0.0;
101                 if (sh + image->matrix.y0 < allocation.height)
102                         image->matrix.y0 = allocation.height - sh;
103         }
104 }
105
106 static void
107 redraw_handler(struct widget *widget, void *data)
108 {
109         struct image *image = data;
110         struct rectangle allocation;
111         cairo_t *cr;
112         cairo_surface_t *surface;
113         double width, height, doc_aspect, window_aspect, scale;
114         cairo_matrix_t matrix;
115         cairo_matrix_t translate;
116
117         surface = window_get_surface(image->window);
118         cr = cairo_create(surface);
119         widget_get_allocation(image->widget, &allocation);
120         cairo_rectangle(cr, allocation.x, allocation.y,
121                         allocation.width, allocation.height);
122         cairo_clip(cr);
123         cairo_push_group(cr);
124         cairo_translate(cr, allocation.x, allocation.y);
125
126         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
127         cairo_set_source_rgba(cr, 0, 0, 0, 1);
128         cairo_paint(cr);
129
130         if (!image->initialized) {
131                 image->initialized = true;
132                 width = cairo_image_surface_get_width(image->image);
133                 height = cairo_image_surface_get_height(image->image);
134
135                 doc_aspect = width / height;
136                 window_aspect = (double) allocation.width / allocation.height;
137                 if (doc_aspect < window_aspect)
138                         scale = allocation.height / height;
139                 else
140                         scale = allocation.width / width;
141
142                 image->width = width;
143                 image->height = height;
144                 cairo_matrix_init_scale(&image->matrix, scale, scale);
145
146                 clamp_view(image);
147         }
148
149         matrix = image->matrix;
150         cairo_matrix_init_translate(&translate, allocation.x, allocation.y);
151         cairo_matrix_multiply(&matrix, &matrix, &translate);
152         cairo_set_matrix(cr, &matrix);
153
154         cairo_set_source_surface(cr, image->image, 0, 0);
155         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
156         cairo_paint(cr);
157
158         cairo_pop_group_to_source(cr);
159         cairo_paint(cr);
160         cairo_destroy(cr);
161
162         cairo_surface_destroy(surface);
163 }
164
165 static void
166 resize_handler(struct widget *widget,
167                int32_t width, int32_t height, void *data)
168 {
169         struct image *image = data;
170
171         clamp_view(image);
172 }
173
174 static void
175 keyboard_focus_handler(struct window *window,
176                        struct input *device, void *data)
177 {
178         struct image *image = data;
179
180         window_schedule_redraw(image->window);
181 }
182
183 static int
184 enter_handler(struct widget *widget,
185               struct input *input,
186               float x, float y, void *data)
187 {
188         struct image *image = data;
189         struct rectangle allocation;
190
191         widget_get_allocation(image->widget, &allocation);
192         x -= allocation.x;
193         y -= allocation.y;
194
195         image->pointer.x = x;
196         image->pointer.y = y;
197
198         return 1;
199 }
200
201 static void
202 move_viewport(struct image *image, double dx, double dy)
203 {
204         double scale = get_scale(image);
205
206         if (!image->initialized)
207                 return;
208
209         cairo_matrix_translate(&image->matrix, -dx/scale, -dy/scale);
210         clamp_view(image);
211
212         window_schedule_redraw(image->window);
213 }
214
215 static int
216 motion_handler(struct widget *widget,
217                struct input *input, uint32_t time,
218                float x, float y, void *data)
219 {
220         struct image *image = data;
221         struct rectangle allocation;
222
223         widget_get_allocation(image->widget, &allocation);
224         x -= allocation.x;
225         y -= allocation.y;
226
227         if (image->button_pressed)
228                 move_viewport(image, image->pointer.x - x,
229                               image->pointer.y - y);
230
231         image->pointer.x = x;
232         image->pointer.y = y;
233
234         return image->button_pressed ? CURSOR_DRAGGING : CURSOR_LEFT_PTR;
235 }
236
237 static void
238 button_handler(struct widget *widget,
239                struct input *input, uint32_t time,
240                uint32_t button,
241                enum wl_pointer_button_state state,
242                void *data)
243 {
244         struct image *image = data;
245
246         if (button == BTN_LEFT) {
247                 image->button_pressed =
248                         state == WL_POINTER_BUTTON_STATE_PRESSED;
249
250                 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
251                         input_set_pointer_image(input, CURSOR_DRAGGING);
252                 else
253                         input_set_pointer_image(input, CURSOR_LEFT_PTR);
254         }
255 }
256
257 static void
258 zoom(struct image *image, double scale)
259 {
260         double x = image->pointer.x;
261         double y = image->pointer.y;
262         cairo_matrix_t scale_matrix;
263
264         if (!image->initialized)
265                 return;
266
267         if (get_scale(image) * scale > 20.0 ||
268             get_scale(image) * scale < 0.02)
269                 return;
270
271         cairo_matrix_init_identity(&scale_matrix);
272         cairo_matrix_translate(&scale_matrix, x, y);
273         cairo_matrix_scale(&scale_matrix, scale, scale);
274         cairo_matrix_translate(&scale_matrix, -x, -y);
275
276         cairo_matrix_multiply(&image->matrix, &image->matrix, &scale_matrix);
277         clamp_view(image);
278 }
279
280 static void
281 key_handler(struct window *window, struct input *input, uint32_t time,
282             uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
283             void *data)
284 {
285         struct image *image = data;
286
287         if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
288                 return;
289
290         switch (sym) {
291         case XKB_KEY_minus:
292                 zoom(image, 0.8);
293                 window_schedule_redraw(image->window);
294                 break;
295         case XKB_KEY_equal:
296         case XKB_KEY_plus:
297                 zoom(image, 1.2);
298                 window_schedule_redraw(image->window);
299                 break;
300         case XKB_KEY_1:
301                 image->matrix.xx = 1.0;
302                 image->matrix.xy = 0.0;
303                 image->matrix.yx = 0.0;
304                 image->matrix.yy = 1.0;
305                 clamp_view(image);
306                 window_schedule_redraw(image->window);
307                 break;
308         }
309 }
310
311 static void
312 axis_handler(struct widget *widget, struct input *input, uint32_t time,
313              uint32_t axis, wl_fixed_t value, void *data)
314 {
315         struct image *image = data;
316
317         if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
318             input_get_modifiers(input) == MOD_CONTROL_MASK) {
319                 /* set zoom level to 2% per 10 axis units */
320                 zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
321
322                 window_schedule_redraw(image->window);
323         } else if (input_get_modifiers(input) == 0) {
324                 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
325                         move_viewport(image, 0, wl_fixed_to_double(value));
326                 else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
327                         move_viewport(image, wl_fixed_to_double(value), 0);
328         }
329 }
330
331 static void
332 fullscreen_handler(struct window *window, void *data)
333 {
334         struct image *image = data;
335
336         image->fullscreen ^= 1;
337         window_set_fullscreen(window, image->fullscreen);
338 }
339
340 static void
341 close_handler(void *data)
342 {
343         struct image *image = data;
344
345         *image->image_counter -= 1;
346
347         if (*image->image_counter == 0)
348                 display_exit(image->display);
349
350         widget_destroy(image->widget);
351         window_destroy(image->window);
352
353         free(image);
354 }
355
356 static struct image *
357 image_create(struct display *display, const char *filename,
358              int *image_counter)
359 {
360         struct image *image;
361         char *b, *copy, title[512];;
362
363         image = zalloc(sizeof *image);
364         if (image == NULL)
365                 return image;
366
367         copy = strdup(filename);
368         b = basename(copy);
369         snprintf(title, sizeof title, "Wayland Image - %s", b);
370         free(copy);
371
372         image->filename = strdup(filename);
373         image->image = load_cairo_surface(filename);
374
375         if (!image->image) {
376                 free(image->filename);
377                 free(image);
378                 return NULL;
379         }
380
381         image->window = window_create(display);
382         image->widget = window_frame_create(image->window, image);
383         window_set_title(image->window, title);
384         image->display = display;
385         image->image_counter = image_counter;
386         *image_counter += 1;
387         image->initialized = false;
388
389         window_set_user_data(image->window, image);
390         widget_set_redraw_handler(image->widget, redraw_handler);
391         widget_set_resize_handler(image->widget, resize_handler);
392         window_set_keyboard_focus_handler(image->window,
393                                           keyboard_focus_handler);
394         window_set_fullscreen_handler(image->window, fullscreen_handler);
395         window_set_close_handler(image->window, close_handler);
396
397         widget_set_enter_handler(image->widget, enter_handler);
398         widget_set_motion_handler(image->widget, motion_handler);
399         widget_set_button_handler(image->widget, button_handler);
400         widget_set_axis_handler(image->widget, axis_handler);
401         window_set_key_handler(image->window, key_handler);
402         widget_schedule_resize(image->widget, 500, 400);
403
404         return image;
405 }
406
407 int
408 main(int argc, char *argv[])
409 {
410         struct display *d;
411         int i;
412         int image_counter = 0;
413
414         if (argc <= 1 || argv[1][0]=='-') {
415                 printf("Usage: %s image...\n", argv[0]);
416                 return 1;
417         }
418
419         d = display_create(&argc, argv);
420         if (d == NULL) {
421                 fprintf(stderr, "failed to create display: %m\n");
422                 return -1;
423         }
424
425         for (i = 1; i < argc; i++)
426                 image_create(d, argv[i], &image_counter);
427
428         if (image_counter > 0)
429                 display_run(d);
430
431         display_destroy(d);
432
433         return 0;
434 }