util: clarify a bit of the code for parsing commands in wmgenmenu
[wmaker-crm.git] / util / wmiv.c
blob45cc4e3dbe0c0bc029422a4990355b1509a53e05
1 /*
2 * Window Maker window manager
4 * Copyright (c) 2014 Window Maker Team - David Maciejak
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #if !defined(_GNU_SOURCE)
22 #define _GNU_SOURCE
23 #endif
25 #include <X11/keysym.h>
26 #include <X11/XKBlib.h>
27 #include <X11/Xatom.h>
28 #include <X11/Xlib.h>
29 #include "wraster.h"
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <dirent.h>
34 #include <limits.h>
35 #include <unistd.h>
36 #include <sys/stat.h>
37 #include "config.h"
39 #ifdef HAVE_EXIF
40 #include <libexif/exif-data.h>
41 #endif
43 #ifdef HAVE_PTHREAD
44 #include <pthread.h>
45 #endif
47 #ifdef USE_XPM
48 extern int XpmCreatePixmapFromData(Display *, Drawable, char **, Pixmap *, Pixmap *, void *);
49 /* this is the icon from eog project
50 git.gnome.org/browse/eog
52 #include "wmiv.h"
53 #endif
55 #define DEBUG 0
56 #define FILE_SEPARATOR '/'
58 Display *dpy;
59 Window win;
60 RContext *ctx;
61 RImage *img;
62 Pixmap pix;
64 const char *APPNAME = "wmiv";
65 int APPVERSION_MAJOR = 0;
66 int APPVERSION_MINOR = 7;
67 int NEXT = 0;
68 int PREV = 1;
69 float zoom_factor = 0;
70 int max_width = 0;
71 int max_height = 0;
73 Bool fullscreen_flag = False;
74 Bool focus = False;
75 Bool back_from_fullscreen = False;
77 #ifdef HAVE_PTHREAD
78 Bool diaporama_flag = False;
79 int diaporama_delay = 5;
80 pthread_t tid = 0;
81 #endif
82 XTextProperty title_property;
83 XTextProperty icon_property;
84 unsigned current_index = 1;
85 unsigned max_index = 1;
87 RColor lightGray;
88 RColor darkGray;
89 RColor black;
90 RColor red;
92 typedef struct link link_t;
93 struct link {
94 const void *data;
95 link_t *prev;
96 link_t *next;
99 typedef struct linked_list {
100 int count;
101 link_t *first;
102 link_t *last;
103 } linked_list_t;
105 linked_list_t list;
106 link_t *current_link;
110 load_oriented_image: used to load an image and optionally
111 get its orientation if libexif is available
112 return the image on success, NULL on failure
114 RImage *load_oriented_image(RContext *context, const char *file, int index)
116 RImage *image;
117 #ifdef HAVE_EXIF
118 int orientation = 0;
119 #endif
120 image = RLoadImage(context, file, index);
121 if (!image)
122 return NULL;
123 #ifdef HAVE_EXIF
124 ExifData *exifData = exif_data_new_from_file(file);
125 if (exifData) {
126 ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
127 ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
128 if (exifEntry)
129 orientation = exif_get_short(exifEntry->data, byteOrder);
131 exif_data_free(exifData);
135 0th Row 0th Column
136 1 top left side
137 2 top right side
138 3 bottom right side
139 4 bottom left side
140 5 left side top
141 6 right side top
142 7 right side bottom
143 8 left side bottom
146 if (image && (orientation > 1)) {
147 RImage *tmp = NULL;
148 switch (orientation) {
149 case 2:
150 tmp = RFlipImage(image, RHorizontalFlip);
151 break;
152 case 3:
153 tmp = RRotateImage(image, 180);
154 break;
155 case 4:
156 tmp = RFlipImage(image, RVerticalFlip);
157 break;
158 case 5: {
159 RImage *tmp2;
160 tmp2 = RFlipImage(image, RVerticalFlip);
161 if (tmp2) {
162 tmp = RRotateImage(tmp2, 90);
163 RReleaseImage(tmp2);
166 break;
167 case 6:
168 tmp = RRotateImage(image, 90);
169 break;
170 case 7: {
171 RImage *tmp2;
172 tmp2 = RFlipImage(image, RVerticalFlip);
173 if (tmp2) {
174 tmp = RRotateImage(tmp2, 270);
175 RReleaseImage(tmp2);
178 break;
179 case 8:
180 tmp = RRotateImage(image, 270);
181 break;
183 if (tmp) {
184 RReleaseImage(image);
185 image = tmp;
188 #endif
189 return image;
193 change_title: used to change window title
194 return EXIT_SUCCESS on success, 1 on failure
196 int change_title(XTextProperty *prop, char *filename)
198 char *combined_title = NULL;
199 if (!asprintf(&combined_title, "%s - %u/%u - %s", APPNAME, current_index, max_index, filename))
200 if (!asprintf(&combined_title, "%s - %u/%u", APPNAME, current_index, max_index))
201 return EXIT_FAILURE;
202 XStringListToTextProperty(&combined_title, 1, prop);
203 XSetWMName(dpy, win, prop);
204 if (prop->value)
205 XFree(prop->value);
206 free(combined_title);
207 return EXIT_SUCCESS;
211 rescale_image: used to rescale the current image based on the screen size
212 return EXIT_SUCCESS on success
214 int rescale_image(void)
216 long final_width = img->width;
217 long final_height = img->height;
219 /* check if there is already a zoom factor applied */
220 if (zoom_factor != 0) {
221 final_width = img->width + (int)(img->width * zoom_factor);
222 final_height = img->height + (int)(img->height * zoom_factor);
224 if ((max_width < final_width) || (max_height < final_height)) {
225 long val = 0;
226 if (final_width > final_height) {
227 val = final_height * max_width / final_width;
228 final_width = final_width * val / final_height;
229 final_height = val;
230 if (val > max_height) {
231 val = final_width * max_height / final_height;
232 final_height = final_height * val / final_width;
233 final_width = val;
235 } else {
236 val = final_width * max_height / final_height;
237 final_height = final_height * val / final_width;
238 final_width = val;
239 if (val > max_width) {
240 val = final_height * max_width / final_width;
241 final_width = final_width * val / final_height;
242 final_height = val;
246 if ((final_width != img->width) || (final_height != img->height)) {
247 RImage *old_img = img;
248 img = RScaleImage(img, final_width, final_height);
249 if (!img) {
250 img = old_img;
251 return EXIT_FAILURE;
253 RReleaseImage(old_img);
255 if (!RConvertImage(ctx, img, &pix)) {
256 fprintf(stderr, "%s\n", RMessageForError(RErrorCode));
257 return EXIT_FAILURE;
259 return EXIT_SUCCESS;
263 maximize_image: find the best image size for the current display
264 return EXIT_SUCCESS on success
266 int maximize_image(void)
268 rescale_image();
269 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
270 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
271 return EXIT_SUCCESS;
275 merge_with_background: merge the current image with with a checkboard background
276 return EXIT_SUCCESS on success, 1 on failure
278 int merge_with_background(RImage *i)
280 if (i) {
281 RImage *back;
282 back = RCreateImage(i->width, i->height, True);
283 if (back) {
284 int opaq = 255;
285 int x = 0, y = 0;
287 RFillImage(back, &lightGray);
288 for (x = 0; x <= i->width; x += 8) {
289 if (x/8 % 2)
290 y = 8;
291 else
292 y = 0;
293 for (; y <= i->height; y += 16)
294 ROperateRectangle(back, RAddOperation, x, y, x+8, y+8, &darkGray);
297 RCombineImagesWithOpaqueness(i, back, opaq);
298 RReleaseImage(back);
299 return EXIT_SUCCESS;
302 return EXIT_FAILURE;
306 turn_image: rotate the image by the angle passed
307 return EXIT_SUCCESS on success, EXIT_FAILURE on failure
309 int turn_image(float angle)
311 RImage *tmp;
313 if (!img)
314 return EXIT_FAILURE;
316 tmp = RRotateImage(img, angle);
317 if (!tmp)
318 return EXIT_FAILURE;
320 if (!fullscreen_flag) {
321 if (img->width != tmp->width || img->height != tmp->height)
322 XResizeWindow(dpy, win, tmp->width, tmp->height);
325 RReleaseImage(img);
326 img = tmp;
328 rescale_image();
329 if (!fullscreen_flag) {
330 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
331 } else {
332 XClearWindow(dpy, win);
333 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
334 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
337 return EXIT_SUCCESS;
341 turn_image_right: rotate the image by 90 degree
342 return EXIT_SUCCESS on success, EXIT_FAILURE on failure
344 int turn_image_right(void)
346 return turn_image(90.0);
350 turn_image_left: rotate the image by -90 degree
351 return EXIT_SUCCESS on success, 1 on failure
353 int turn_image_left(void)
355 return turn_image(-90.0);
359 draw_failed_image: create a red crossed image to indicate an error loading file
360 return the image on success, NULL on failure
363 RImage *draw_failed_image(void)
365 RImage *failed_image = NULL;
366 XWindowAttributes attr;
368 if (win && (XGetWindowAttributes(dpy, win, &attr) >= 0))
369 failed_image = RCreateImage(attr.width, attr.height, False);
370 else
371 failed_image = RCreateImage(50, 50, False);
372 if (!failed_image)
373 return NULL;
375 RFillImage(failed_image, &black);
376 ROperateLine(failed_image, RAddOperation, 0, 0, failed_image->width, failed_image->height, &red);
377 ROperateLine(failed_image, RAddOperation, 0, failed_image->height, failed_image->width, 0, &red);
379 return failed_image;
383 full_screen: sending event to the window manager to switch from/to full screen mode
384 return EXIT_SUCCESS on success, 1 on failure
386 int full_screen(void)
388 XEvent xev;
390 Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", True);
391 Atom fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", True);
392 long mask = SubstructureNotifyMask;
394 if (fullscreen_flag) {
395 fullscreen_flag = False;
396 zoom_factor = 0;
397 back_from_fullscreen = True;
398 } else {
399 fullscreen_flag = True;
400 zoom_factor = 1000;
403 memset(&xev, 0, sizeof(xev));
404 xev.type = ClientMessage;
405 xev.xclient.display = dpy;
406 xev.xclient.window = win;
407 xev.xclient.message_type = wm_state;
408 xev.xclient.format = 32;
409 xev.xclient.data.l[0] = fullscreen_flag;
410 xev.xclient.data.l[1] = fullscreen;
412 if (!XSendEvent(dpy, DefaultRootWindow(dpy), False, mask, &xev)) {
413 fprintf(stderr, "Error: sending fullscreen event to xserver\n");
414 return EXIT_FAILURE;
416 return EXIT_SUCCESS;
420 zoom_in_out: apply a zoom factor on the current image
421 arg: 1 to zoom in, 0 to zoom out
422 return EXIT_SUCCESS on success, 1 on failure
424 int zoom_in_out(int z)
426 RImage *old_img = img;
427 RImage *tmp = load_oriented_image(ctx, current_link->data, 0);
428 if (!tmp)
429 return EXIT_FAILURE;
431 if (z) {
432 zoom_factor += 0.2;
433 img = RScaleImage(tmp, tmp->width + (int)(tmp->width * zoom_factor),
434 tmp->height + (int)(tmp->height * zoom_factor));
435 if (!img) {
436 img = old_img;
437 return EXIT_FAILURE;
439 } else {
440 zoom_factor -= 0.2;
441 int new_width = tmp->width + (int) (tmp->width * zoom_factor);
442 int new_height = tmp->height + (int)(tmp->height * zoom_factor);
443 if ((new_width <= 0) || (new_height <= 0)) {
444 zoom_factor += 0.2;
445 RReleaseImage(tmp);
446 return EXIT_FAILURE;
448 img = RScaleImage(tmp, new_width, new_height);
449 if (!img) {
450 img = old_img;
451 return EXIT_FAILURE;
454 RReleaseImage(old_img);
455 RReleaseImage(tmp);
456 XFreePixmap(dpy, pix);
458 merge_with_background(img);
459 if (!RConvertImage(ctx, img, &pix)) {
460 fprintf(stderr, "%s\n", RMessageForError(RErrorCode));
461 return EXIT_FAILURE;
463 XResizeWindow(dpy, win, img->width, img->height);
464 return EXIT_SUCCESS;
468 zoom_in: transitional fct used to call zoom_in_out with zoom in flag
469 return EXIT_SUCCESS on success, 1 on failure
471 int zoom_in(void)
473 return zoom_in_out(1);
477 zoom_out: transitional fct used to call zoom_in_out with zoom out flag
478 return EXIT_SUCCESS on success, 1 on failure
480 int zoom_out(void)
482 return zoom_in_out(0);
486 change_image: load previous or next image
487 arg: way which could be PREV or NEXT constant
488 return EXIT_SUCCESS on success, 1 on failure
490 int change_image(int way)
492 if (img && current_link) {
493 int old_img_width = img->width;
494 int old_img_height = img->height;
496 RReleaseImage(img);
498 if (way == NEXT) {
499 current_link = current_link->next;
500 current_index++;
501 } else {
502 current_link = current_link->prev;
503 current_index--;
505 if (current_link == NULL) {
506 if (way == NEXT) {
507 current_link = list.first;
508 current_index = 1;
509 } else {
510 current_link = list.last;
511 current_index = max_index;
514 if (DEBUG)
515 fprintf(stderr, "current file is> %s\n", (char *)current_link->data);
516 img = load_oriented_image(ctx, current_link->data, 0);
518 if (!img) {
519 fprintf(stderr, "Error: %s %s\n", (char *)current_link->data,
520 RMessageForError(RErrorCode));
521 img = draw_failed_image();
522 } else {
523 merge_with_background(img);
525 rescale_image();
526 if (!fullscreen_flag) {
527 if ((old_img_width != img->width) || (old_img_height != img->height))
528 XResizeWindow(dpy, win, img->width, img->height);
529 else
530 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
531 change_title(&title_property, (char *)current_link->data);
532 } else {
533 XClearWindow(dpy, win);
534 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
535 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
537 return EXIT_SUCCESS;
539 return EXIT_FAILURE;
542 #ifdef HAVE_PTHREAD
544 diaporama: send a xevent to display the next image at every delay set to diaporama_delay
545 arg: not used
546 return void
548 void *diaporama(void *arg)
550 (void) arg;
552 XKeyEvent event;
553 event.display = dpy;
554 event.window = win;
555 event.root = DefaultRootWindow(dpy);
556 event.subwindow = None;
557 event.time = CurrentTime;
558 event.x = 1;
559 event.y = 1;
560 event.x_root = 1;
561 event.y_root = 1;
562 event.same_screen = True;
563 event.keycode = XKeysymToKeycode(dpy, XK_Right);
564 event.state = 0;
565 event.type = KeyPress;
567 while (diaporama_flag) {
568 int r;
569 r = XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
570 if (!r)
571 fprintf(stderr, "Error sending event\n");
572 XFlush(dpy);
573 /* default sleep time between moving to next image */
574 sleep(diaporama_delay);
576 tid = 0;
577 return arg;
579 #endif
582 linked_list_init: init the linked list
584 void linked_list_init(linked_list_t *list)
586 list->first = list->last = 0;
587 list->count = 0;
591 linked_list_add: add an element to the linked list
592 return EXIT_SUCCESS on success, 1 otherwise
594 int linked_list_add(linked_list_t *list, const void *data)
596 link_t *link;
598 /* calloc sets the "next" field to zero. */
599 link = calloc(1, sizeof(link_t));
600 if (!link) {
601 fprintf(stderr, "Error: memory allocation failed\n");
602 return EXIT_FAILURE;
604 link->data = data;
605 if (list->last) {
606 /* Join the two final links together. */
607 list->last->next = link;
608 link->prev = list->last;
609 list->last = link;
610 } else {
611 list->first = link;
612 list->last = link;
614 list->count++;
615 return EXIT_SUCCESS;
619 linked_list_free: deallocate the whole linked list
621 void linked_list_free(linked_list_t *list)
623 link_t *link;
624 link_t *next;
625 for (link = list->first; link; link = next) {
626 /* Store the next value so that we don't access freed memory. */
627 next = link->next;
628 if (link->data)
629 free((char *)link->data);
630 free(link);
635 connect_dir: list and sort by name all files from a given directory
636 arg: the directory path that contains images, the linked list where to add the new file refs
637 return: the first argument of the list or NULL on failure
639 link_t *connect_dir(char *dirpath, linked_list_t *li)
641 struct dirent **dir;
642 int dv, idx;
643 char path[PATH_MAX] = "";
645 if (!dirpath)
646 return NULL;
648 dv = scandir(dirpath, &dir, 0, alphasort);
649 if (dv < 0) {
650 /* maybe it's a file */
651 struct stat stDirInfo;
652 if (lstat(dirpath, &stDirInfo) == 0) {
653 linked_list_add(li, strdup(dirpath));
654 return li->first;
655 } else {
656 return NULL;
659 for (idx = 0; idx < dv; idx++) {
660 struct stat stDirInfo;
661 if (dirpath[strlen(dirpath)-1] == FILE_SEPARATOR)
662 snprintf(path, PATH_MAX, "%s%s", dirpath, dir[idx]->d_name);
663 else
664 snprintf(path, PATH_MAX, "%s%c%s", dirpath, FILE_SEPARATOR, dir[idx]->d_name);
666 free(dir[idx]);
667 if ((lstat(path, &stDirInfo) == 0) && !S_ISDIR(stDirInfo.st_mode))
668 linked_list_add(li, strdup(path));
670 free(dir);
671 return li->first;
675 main
677 int main(int argc, char **argv)
679 int option = -1;
680 RContextAttributes attr;
681 XEvent e;
682 KeySym keysym;
683 char *reading_filename = "";
684 int screen, file_i;
685 int quit = 0;
686 XClassHint *class_hints;
687 XSizeHints *size_hints;
688 XWMHints *win_hints;
689 #ifdef USE_XPM
690 Pixmap icon_pixmap, icon_shape;
691 #endif
692 class_hints = XAllocClassHint();
693 if (!class_hints) {
694 fprintf(stderr, "Error: failure allocating memory\n");
695 return EXIT_FAILURE;
697 class_hints->res_name = (char *)APPNAME;
698 class_hints->res_class = "default";
700 /* init colors */
701 lightGray.red = lightGray.green = lightGray.blue = 211;
702 darkGray.red = darkGray.green = darkGray.blue = 169;
703 lightGray.alpha = darkGray.alpha = 1;
704 black.red = black.green = black.blue = 0;
705 red.red = 255;
706 red.green = red.blue = 0;
708 option = getopt(argc, argv, "hv");
709 if (option != -1) {
710 switch (option) {
711 case 'h':
712 fprintf(stderr, "Usage: %s [image(s)|directory]\n"
713 "Keys:\n"
714 "+: zoom in\n"
715 "-: zoom out\n"
716 "esc: actual size\n"
717 #ifdef HAVE_PTHREAD
718 "d: launch diaporama mode\n"
719 #endif
720 "l: rotate image on the left\n"
721 "q: quit\n"
722 "r: rotate image on the right\n"
723 "right: next image\n"
724 "left: previous image\n"
725 "up: first image\n"
726 "down: last image\n",
727 argv[0]);
728 return EXIT_SUCCESS;
729 case 'v':
730 fprintf(stderr, "%s version %d.%d\n", APPNAME, APPVERSION_MAJOR, APPVERSION_MINOR);
731 return EXIT_SUCCESS;
732 case '?':
733 return EXIT_FAILURE;
737 linked_list_init(&list);
739 dpy = XOpenDisplay(NULL);
740 if (!dpy) {
741 fprintf(stderr, "Error: can't open display");
742 linked_list_free(&list);
743 return EXIT_FAILURE;
746 screen = DefaultScreen(dpy);
747 max_width = DisplayWidth(dpy, screen);
748 max_height = DisplayHeight(dpy, screen);
750 attr.flags = RC_RenderMode | RC_ColorsPerChannel;
751 attr.render_mode = RDitheredRendering;
752 attr.colors_per_channel = 4;
753 ctx = RCreateContext(dpy, DefaultScreen(dpy), &attr);
755 if (argc < 2) {
756 argv[1] = ".";
757 argc = 2;
760 for (file_i = 1; file_i < argc; file_i++) {
761 current_link = connect_dir(argv[file_i], &list);
762 if (current_link) {
763 reading_filename = (char *)current_link->data;
764 max_index = list.count;
768 img = load_oriented_image(ctx, reading_filename, 0);
770 if (!img) {
771 fprintf(stderr, "Error: %s %s\n", reading_filename, RMessageForError(RErrorCode));
772 img = draw_failed_image();
773 if (!current_link)
774 return EXIT_FAILURE;
777 merge_with_background(img);
778 rescale_image();
780 if (DEBUG)
781 fprintf(stderr, "display size: %dx%d\n", max_width, max_height);
783 win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
784 img->width, img->height, 0, 0, BlackPixel(dpy, screen));
785 XSelectInput(dpy, win, KeyPressMask|StructureNotifyMask|ExposureMask|ButtonPressMask|FocusChangeMask);
787 size_hints = XAllocSizeHints();
788 if (!size_hints) {
789 fprintf(stderr, "Error: failure allocating memory\n");
790 return EXIT_FAILURE;
792 size_hints->width = img->width;
793 size_hints->height = img->height;
795 Atom delWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
796 XSetWMProtocols(dpy, win, &delWindow, 1);
797 change_title(&title_property, reading_filename);
799 win_hints = XAllocWMHints();
800 if (win_hints) {
801 win_hints->flags = StateHint|InputHint|WindowGroupHint;
803 #ifdef USE_XPM
804 if ((XpmCreatePixmapFromData(dpy, win, wmiv_xpm, &icon_pixmap, &icon_shape, NULL)) == 0) {
805 win_hints->flags |= IconPixmapHint|IconMaskHint|IconPositionHint;
806 win_hints->icon_pixmap = icon_pixmap;
807 win_hints->icon_mask = icon_shape;
808 win_hints->icon_x = 0;
809 win_hints->icon_y = 0;
811 #endif
812 win_hints->initial_state = NormalState;
813 win_hints->input = True;
814 win_hints->window_group = win;
815 XStringListToTextProperty((char **)&APPNAME, 1, &icon_property);
816 XSetWMProperties(dpy, win, NULL, &icon_property, argv, argc, size_hints, win_hints, class_hints);
817 if (icon_property.value)
818 XFree(icon_property.value);
819 XFree(win_hints);
820 XFree(class_hints);
821 XFree(size_hints);
824 XMapWindow(dpy, win);
825 XFlush(dpy);
826 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
828 while (!quit) {
829 XNextEvent(dpy, &e);
830 if (e.type == ClientMessage) {
831 if (e.xclient.data.l[0] == delWindow)
832 quit = 1;
833 break;
835 if (e.type == FocusIn) {
836 focus = True;
837 continue;
839 if (e.type == FocusOut) {
840 focus = False;
841 continue;
843 if (!fullscreen_flag && (e.type == Expose)) {
844 XExposeEvent xev = e.xexpose;
845 if (xev.count == 0)
846 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
847 continue;
849 if (!fullscreen_flag && e.type == ConfigureNotify) {
850 XConfigureEvent xce = e.xconfigure;
851 if (xce.width != img->width || xce.height != img->height) {
852 RImage *old_img = img;
853 img = load_oriented_image(ctx, current_link->data, 0);
854 if (!img) {
855 /* keep the old img and window size */
856 img = old_img;
857 XResizeWindow(dpy, win, img->width, img->height);
858 } else {
859 RImage *tmp2;
860 if (!back_from_fullscreen)
861 /* manually resized window */
862 tmp2 = RScaleImage(img, xce.width, xce.height);
863 else {
864 /* back from fullscreen mode, maybe img was rotated */
865 tmp2 = img;
866 back_from_fullscreen = False;
867 XClearWindow(dpy, win);
869 merge_with_background(tmp2);
870 if (RConvertImage(ctx, tmp2, &pix)) {
871 RReleaseImage(old_img);
872 img = RCloneImage(tmp2);
873 RReleaseImage(tmp2);
874 change_title(&title_property, (char *)current_link->data);
875 XSync(dpy, True);
876 XResizeWindow(dpy, win, img->width, img->height);
877 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
878 img->width, img->height, 0, 0);
883 continue;
885 if (fullscreen_flag && e.type == ConfigureNotify) {
886 maximize_image();
887 continue;
889 if (e.type == ButtonPress) {
890 switch (e.xbutton.button) {
891 case Button1: {
892 if (focus) {
893 if (img && (e.xbutton.x > img->width/2))
894 change_image(NEXT);
895 else
896 change_image(PREV);
899 break;
900 case Button4:
901 zoom_in();
902 break;
903 case Button5:
904 zoom_out();
905 break;
906 case 8:
907 change_image(PREV);
908 break;
909 case 9:
910 change_image(NEXT);
911 break;
913 continue;
915 if (e.type == KeyPress) {
916 keysym = XkbKeycodeToKeysym(dpy, e.xkey.keycode, 0, e.xkey.state & ShiftMask?1:0);
917 #ifdef HAVE_PTHREAD
918 if (keysym != XK_Right)
919 diaporama_flag = False;
920 #endif
921 switch (keysym) {
922 case XK_Right:
923 change_image(NEXT);
924 break;
925 case XK_Left:
926 change_image(PREV);
927 break;
928 case XK_Up:
929 if (current_link) {
930 current_link = list.last;
931 change_image(NEXT);
933 break;
934 case XK_Down:
935 if (current_link) {
936 current_link = list.first;
937 change_image(PREV);
939 break;
940 #ifdef HAVE_PTHREAD
941 case XK_F5:
942 case XK_d:
943 if (!tid) {
944 if (current_link && !diaporama_flag) {
945 diaporama_flag = True;
946 pthread_create(&tid, NULL, &diaporama, NULL);
947 } else {
948 fprintf(stderr, "Can't use diaporama mode\n");
951 break;
952 #endif
953 case XK_q:
954 quit = 1;
955 break;
956 case XK_Escape:
957 if (!fullscreen_flag) {
958 zoom_factor = -0.2;
959 /* zoom_in will increase the zoom factor by 0.2 */
960 zoom_in();
961 } else {
962 /* we are in fullscreen mode already, want to return to normal size */
963 full_screen();
965 break;
966 case XK_plus:
967 zoom_in();
968 break;
969 case XK_minus:
970 zoom_out();
971 break;
972 case XK_F11:
973 case XK_f:
974 full_screen();
975 break;
976 case XK_r:
977 turn_image_right();
978 break;
979 case XK_l:
980 turn_image_left();
981 break;
987 if (img)
988 RReleaseImage(img);
989 if (pix)
990 XFreePixmap(dpy, pix);
991 #ifdef USE_XPM
992 if (icon_pixmap)
993 XFreePixmap(dpy, icon_pixmap);
994 if (icon_shape)
995 XFreePixmap(dpy, icon_shape);
996 #endif
997 linked_list_free(&list);
998 RDestroyContext(ctx);
999 RShutdown();
1000 XCloseDisplay(dpy);
1001 return EXIT_SUCCESS;