source: mainline/uspace/app/viewer/viewer.c@ 7b7a7d2

Last change on this file since 7b7a7d2 was 41e1258, checked in by Jiri Svoboda <jiri@…>, 19 months ago

Viewer should quit on Ctrl-Q and Escape, apart from just Q.

  • Property mode set to 100644
File size: 11.9 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
3 * Copyright (c) 2013 Martin Decky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup viewer
31 * @{
32 */
33/** @file
34 */
35
36#include <errno.h>
37#include <gfximage/tga.h>
38#include <stdbool.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <str.h>
42#include <ui/filedialog.h>
43#include <ui/image.h>
44#include <ui/ui.h>
45#include <ui/wdecor.h>
46#include <ui/window.h>
47#include <vfs/vfs.h>
48
49#define NAME "viewer"
50
51typedef struct {
52 ui_t *ui;
53
54 size_t imgs_count;
55 size_t imgs_current;
56 char **imgs;
57
58 bool fullscreen;
59 ui_window_t *window;
60 gfx_bitmap_t *bitmap;
61 ui_image_t *image;
62 gfx_context_t *window_gc;
63 ui_file_dialog_t *dialog;
64
65 gfx_rect_t img_rect;
66} viewer_t;
67
68static bool viewer_img_load(viewer_t *, const char *, gfx_bitmap_t **,
69 gfx_rect_t *);
70static bool viewer_img_setup(viewer_t *, gfx_bitmap_t *, gfx_rect_t *);
71static errno_t viewer_window_create(viewer_t *);
72static void viewer_window_destroy(viewer_t *);
73
74static void wnd_close(ui_window_t *, void *);
75static void wnd_kbd_event(ui_window_t *, void *, kbd_event_t *);
76
77static ui_window_cb_t window_cb = {
78 .close = wnd_close,
79 .kbd = wnd_kbd_event
80};
81
82static void file_dialog_bok(ui_file_dialog_t *, void *, const char *);
83static void file_dialog_bcancel(ui_file_dialog_t *, void *);
84static void file_dialog_close(ui_file_dialog_t *, void *);
85
86static ui_file_dialog_cb_t file_dialog_cb = {
87 .bok = file_dialog_bok,
88 .bcancel = file_dialog_bcancel,
89 .close = file_dialog_close
90};
91
92/** Window close request
93 *
94 * @param window Window
95 * @param arg Argument (calc_t *)
96 */
97static void wnd_close(ui_window_t *window, void *arg)
98{
99 viewer_t *viewer = (viewer_t *) arg;
100
101 ui_quit(viewer->ui);
102}
103
104/** Viewer unmodified key press.
105 *
106 * @param viewer Viewer
107 * @param event Keyboard event
108 */
109static void viewer_kbd_event_unmod(viewer_t *viewer, kbd_event_t *event)
110{
111 bool update = false;
112
113 if (event->key == KC_Q || event->key == KC_ESCAPE)
114 ui_quit(viewer->ui);
115
116 if (event->key == KC_PAGE_DOWN) {
117 if (viewer->imgs_current == viewer->imgs_count - 1)
118 viewer->imgs_current = 0;
119 else
120 viewer->imgs_current++;
121
122 update = true;
123 }
124
125 if (event->key == KC_PAGE_UP) {
126 if (viewer->imgs_current == 0)
127 viewer->imgs_current = viewer->imgs_count - 1;
128 else
129 viewer->imgs_current--;
130
131 update = true;
132 }
133
134 if (update) {
135 gfx_bitmap_t *lbitmap;
136 gfx_rect_t lrect;
137
138 if (!viewer_img_load(viewer, viewer->imgs[viewer->imgs_current],
139 &lbitmap, &lrect)) {
140 printf("Cannot load image \"%s\".\n",
141 viewer->imgs[viewer->imgs_current]);
142 exit(4);
143 }
144 if (!viewer_img_setup(viewer, lbitmap, &lrect)) {
145 printf("Cannot setup image \"%s\".\n",
146 viewer->imgs[viewer->imgs_current]);
147 exit(6);
148 }
149 }
150}
151
152/** Viewer ctrl-key key press.
153 *
154 * @param viewer Viewer
155 * @param event Keyboard event
156 */
157static void viewer_kbd_event_ctrl(viewer_t *viewer, kbd_event_t *event)
158{
159 if (event->key == KC_Q)
160 ui_quit(viewer->ui);
161}
162
163/** Viewer window keyboard event.
164 *
165 * @param window UI window
166 * @param arg Argument (viewer_t *)
167 * @param event Keyboard event
168 */
169static void wnd_kbd_event(ui_window_t *window, void *arg,
170 kbd_event_t *event)
171{
172 viewer_t *viewer = (viewer_t *)arg;
173
174 if (event->type != KEY_PRESS)
175 return;
176
177 if ((event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
178 viewer_kbd_event_unmod(viewer, event);
179
180 if ((event->mods & KM_CTRL) != 0 &&
181 (event->mods & (KM_ALT | KM_SHIFT)) == 0)
182 viewer_kbd_event_ctrl(viewer, event);
183}
184
185/** File dialog OK button press.
186 *
187 * @param dialog File dialog
188 * @param arg Argument (viewer_t *)
189 * @param fname File name
190 */
191static void file_dialog_bok(ui_file_dialog_t *dialog, void *arg,
192 const char *fname)
193{
194 viewer_t *viewer = (viewer_t *) arg;
195 errno_t rc;
196
197 viewer->imgs_count = 1;
198 viewer->imgs = calloc(viewer->imgs_count, sizeof(char *));
199 if (viewer->imgs == NULL) {
200 printf("Out of memory.\n");
201 ui_quit(viewer->ui);
202 return;
203 }
204
205 viewer->imgs[0] = str_dup(fname);
206 if (viewer->imgs[0] == NULL) {
207 printf("Out of memory.\n");
208 ui_quit(viewer->ui);
209 return;
210 }
211
212 rc = viewer_window_create(viewer);
213 if (rc != EOK)
214 ui_quit(viewer->ui);
215
216 ui_file_dialog_destroy(dialog);
217 viewer->dialog = NULL;
218}
219
220/** File dialog cancel button press.
221 *
222 * @param dialog File dialog
223 * @param arg Argument (viewer_t *)
224 */
225static void file_dialog_bcancel(ui_file_dialog_t *dialog, void *arg)
226{
227 viewer_t *viewer = (viewer_t *) arg;
228
229 ui_file_dialog_destroy(dialog);
230 ui_quit(viewer->ui);
231}
232
233/** File dialog close request.
234 *
235 * @param dialog File dialog
236 * @param arg Argument (viewer_t *)
237 */
238static void file_dialog_close(ui_file_dialog_t *dialog, void *arg)
239{
240 viewer_t *viewer = (viewer_t *) arg;
241
242 ui_file_dialog_destroy(dialog);
243 ui_quit(viewer->ui);
244}
245
246static bool viewer_img_load(viewer_t *viewer, const char *fname,
247 gfx_bitmap_t **rbitmap, gfx_rect_t *rect)
248{
249 int fd;
250 errno_t rc = vfs_lookup_open(fname, WALK_REGULAR, MODE_READ, &fd);
251 if (rc != EOK)
252 return false;
253
254 vfs_stat_t stat;
255 rc = vfs_stat(fd, &stat);
256 if (rc != EOK) {
257 vfs_put(fd);
258 return false;
259 }
260
261 void *tga = malloc(stat.size);
262 if (tga == NULL) {
263 vfs_put(fd);
264 return false;
265 }
266
267 size_t nread;
268 rc = vfs_read(fd, (aoff64_t []) { 0 }, tga, stat.size, &nread);
269 if (rc != EOK || nread != stat.size) {
270 free(tga);
271 vfs_put(fd);
272 return false;
273 }
274
275 vfs_put(fd);
276
277 rc = decode_tga(viewer->window_gc, tga, stat.size, rbitmap, rect);
278 if (rc != EOK) {
279 free(tga);
280 return false;
281 }
282
283 free(tga);
284
285 viewer->img_rect = *rect;
286 return true;
287}
288
289static bool viewer_img_setup(viewer_t *viewer, gfx_bitmap_t *bmp,
290 gfx_rect_t *rect)
291{
292 gfx_rect_t arect;
293 gfx_rect_t irect;
294 ui_resource_t *ui_res;
295 errno_t rc;
296
297 ui_res = ui_window_get_res(viewer->window);
298
299 ui_window_get_app_rect(viewer->window, &arect);
300
301 /* Center image on application area */
302 gfx_rect_ctr_on_rect(rect, &arect, &irect);
303
304 if (viewer->image != NULL) {
305 ui_image_set_bmp(viewer->image, bmp, rect);
306 (void) ui_image_paint(viewer->image);
307 ui_image_set_rect(viewer->image, &irect);
308 } else {
309 rc = ui_image_create(ui_res, bmp, rect, &viewer->image);
310 if (rc != EOK) {
311 gfx_bitmap_destroy(bmp);
312 return false;
313 }
314
315 ui_image_set_rect(viewer->image, &irect);
316 ui_window_add(viewer->window, ui_image_ctl(viewer->image));
317 }
318
319 if (viewer->bitmap != NULL)
320 gfx_bitmap_destroy(viewer->bitmap);
321
322 viewer->bitmap = bmp;
323 return true;
324}
325
326static void print_syntax(void)
327{
328 printf("Syntax: %s [<options] <image-file>...\n", NAME);
329 printf("\t-d <display-spec> Use the specified display\n");
330 printf("\t-f Full-screen mode\n");
331}
332
333static errno_t viewer_window_create(viewer_t *viewer)
334{
335 ui_wnd_params_t params;
336 gfx_bitmap_t *lbitmap;
337 gfx_rect_t lrect;
338 gfx_rect_t wrect;
339 gfx_coord2_t off;
340 gfx_rect_t rect;
341 errno_t rc;
342
343 /*
344 * We don't know the image size yet, so create tiny window and resize
345 * later.
346 */
347 ui_wnd_params_init(&params);
348 params.caption = "Viewer";
349 params.rect.p0.x = 0;
350 params.rect.p0.y = 0;
351 params.rect.p1.x = 1;
352 params.rect.p1.y = 1;
353
354 if (viewer->fullscreen) {
355 params.style &= ~ui_wds_decorated;
356 params.placement = ui_wnd_place_full_screen;
357 }
358
359 rc = ui_window_create(viewer->ui, &params, &viewer->window);
360 if (rc != EOK) {
361 printf("Error creating window.\n");
362 goto error;
363 }
364
365 viewer->window_gc = ui_window_get_gc(viewer->window);
366
367 ui_window_set_cb(viewer->window, &window_cb, (void *)viewer);
368
369 if (!viewer_img_load(viewer, viewer->imgs[viewer->imgs_current],
370 &lbitmap, &lrect)) {
371 printf("Cannot load image \"%s\".\n",
372 viewer->imgs[viewer->imgs_current]);
373 goto error;
374 }
375
376 /*
377 * Compute window rectangle such that application area corresponds
378 * to rect
379 */
380 ui_wdecor_rect_from_app(viewer->ui, params.style, &lrect, &wrect);
381 off = wrect.p0;
382 gfx_rect_rtranslate(&off, &wrect, &rect);
383
384 if (!viewer->fullscreen) {
385 rc = ui_window_resize(viewer->window, &rect);
386 if (rc != EOK) {
387 printf("Error resizing window.\n");
388 goto error;
389 }
390 }
391
392 if (!viewer_img_setup(viewer, lbitmap, &lrect)) {
393 printf("Cannot setup image \"%s\".\n",
394 viewer->imgs[viewer->imgs_current]);
395 goto error;
396 }
397
398 rc = ui_window_paint(viewer->window);
399 if (rc != EOK) {
400 printf("Error painting window.\n");
401 goto error;
402 }
403
404 return EOK;
405error:
406 viewer_window_destroy(viewer);
407 ui_quit(viewer->ui);
408 return rc;
409}
410
411static void viewer_window_destroy(viewer_t *viewer)
412{
413 if (viewer->window != NULL)
414 ui_window_destroy(viewer->window);
415 viewer->window = NULL;
416}
417
418int main(int argc, char *argv[])
419{
420 const char *display_spec = UI_ANY_DEFAULT;
421 ui_t *ui = NULL;
422 viewer_t *viewer;
423 errno_t rc;
424 int i;
425 unsigned u;
426 ui_file_dialog_params_t fdparams;
427
428 viewer = calloc(1, sizeof(viewer_t));
429 if (viewer == NULL) {
430 printf("Out of memory.\n");
431 goto error;
432 }
433
434 i = 1;
435 while (i < argc && argv[i][0] == '-') {
436 if (str_cmp(argv[i], "-d") == 0) {
437 ++i;
438 if (i >= argc) {
439 printf("Argument missing.\n");
440 print_syntax();
441 goto error;
442 }
443
444 display_spec = argv[i++];
445 } else if (str_cmp(argv[i], "-f") == 0) {
446 ++i;
447 viewer->fullscreen = true;
448 } else {
449 printf("Invalid option '%s'.\n", argv[i]);
450 print_syntax();
451 goto error;
452 }
453 }
454
455 /* Images specified? */
456 if (i < argc) {
457 viewer->imgs_count = argc - i;
458 viewer->imgs = calloc(viewer->imgs_count, sizeof(char *));
459 if (viewer->imgs == NULL) {
460 printf("Out of memory.\n");
461 goto error;
462 }
463
464 for (int j = 0; j < argc - i; j++) {
465 viewer->imgs[j] = str_dup(argv[i + j]);
466 if (viewer->imgs[j] == NULL) {
467 printf("Out of memory.\n");
468 goto error;
469 }
470 }
471 }
472
473 rc = ui_create(display_spec, &ui);
474 if (rc != EOK) {
475 printf("Error creating UI on display %s.\n", display_spec);
476 goto error;
477 }
478
479 if (ui_is_fullscreen(ui))
480 viewer->fullscreen = true;
481
482 viewer->ui = ui;
483
484 if (viewer->imgs != NULL) {
485 /* We have images, create viewer window. */
486 rc = viewer_window_create(viewer);
487 if (rc != EOK)
488 goto error;
489 } else {
490 /* No images specified, browse for one. */
491 ui_file_dialog_params_init(&fdparams);
492 fdparams.caption = "Open Image";
493
494 rc = ui_file_dialog_create(viewer->ui, &fdparams,
495 &viewer->dialog);
496 if (rc != EOK) {
497 printf("Error creating file dialog.\n");
498 goto error;
499 }
500
501 ui_file_dialog_set_cb(viewer->dialog, &file_dialog_cb,
502 (void *)viewer);
503 }
504
505 ui_run(ui);
506
507 ui_window_destroy(viewer->window);
508 ui_destroy(ui);
509 free(viewer);
510
511 return 0;
512error:
513 if (viewer != NULL && viewer->dialog != NULL)
514 ui_file_dialog_destroy(viewer->dialog);
515 if (viewer != NULL && viewer->imgs != NULL) {
516 for (u = 0; u < viewer->imgs_count; u++) {
517 if (viewer->imgs[i] != NULL)
518 free(viewer->imgs[i]);
519 }
520 free(viewer->imgs);
521 }
522 if (viewer != NULL)
523 viewer_window_destroy(viewer);
524 if (ui != NULL)
525 ui_destroy(ui);
526 if (viewer != NULL)
527 free(viewer);
528 return 1;
529}
530
531/** @}
532 */
Note: See TracBrowser for help on using the repository browser.