Coverity: fix WPrefs preference negative array index read
[wmaker-crm.git] / util / wmiv.c
blob7717baf3c6055e6ffec0e50ab6f90271ae7bd8eb
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/Xatom.h>
26 #include <X11/Xlib.h>
27 #include <WINGs/WINGsP.h>
28 #include <wraster.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <dirent.h>
33 #include <limits.h>
34 #include <unistd.h>
35 #include <sys/stat.h>
36 #include <getopt.h>
37 #include <math.h>
38 #include "config.h"
40 #ifdef HAVE_EXIF
41 #include <libexif/exif-data.h>
42 #endif
44 #ifdef HAVE_PTHREAD
45 #include <pthread.h>
46 #endif
48 #ifdef USE_XPM
49 extern int XpmCreatePixmapFromData(Display *, Drawable, char **, Pixmap *, Pixmap *, void *);
50 /* this is the icon from eog project
51 git.gnome.org/browse/eog
53 #include "wmiv.h"
54 #endif
56 #define WMIV_DEBUG 0
57 #define FILE_SEPARATOR '/'
59 Display *dpy;
60 Window win;
61 RContext *ctx;
62 RImage *img;
63 Pixmap pix;
65 const char *APPNAME = "wmiv";
66 int APPVERSION_MAJOR = 0;
67 int APPVERSION_MINOR = 7;
68 int NEXT = 0;
69 int PREV = 1;
70 float zoom_factor = 0;
71 int max_width = 0;
72 int max_height = 0;
74 Bool fullscreen_flag = False;
75 Bool focus = False;
76 Bool back_from_fullscreen = False;
78 #ifdef HAVE_PTHREAD
79 Bool diaporama_flag = False;
80 int diaporama_delay = 5;
81 pthread_t tid = 0;
82 #endif
83 XTextProperty title_property;
84 XTextProperty icon_property;
85 unsigned current_index = 1;
86 unsigned max_index = 1;
88 RColor lightGray;
89 RColor darkGray;
90 RColor black;
91 RColor red;
93 typedef struct link link_t;
94 struct link {
95 const void *data;
96 link_t *prev;
97 link_t *next;
100 typedef struct linked_list {
101 int count;
102 link_t *first;
103 link_t *last;
104 } linked_list_t;
106 linked_list_t list;
107 link_t *current_link;
111 load_oriented_image: used to load an image and optionally
112 get its orientation if libexif is available
113 return the image on success, NULL on failure
115 RImage *load_oriented_image(RContext *context, const char *file, int index)
117 RImage *image;
118 #ifdef HAVE_EXIF
119 int orientation = 0;
120 #endif
121 image = RLoadImage(context, file, index);
122 if (!image)
123 return NULL;
124 #ifdef HAVE_EXIF
125 ExifData *exifData = exif_data_new_from_file(file);
126 if (exifData) {
127 ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
128 ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
129 if (exifEntry)
130 orientation = exif_get_short(exifEntry->data, byteOrder);
132 exif_data_free(exifData);
136 0th Row 0th Column
137 1 top left side
138 2 top right side
139 3 bottom right side
140 4 bottom left side
141 5 left side top
142 6 right side top
143 7 right side bottom
144 8 left side bottom
147 if (image && (orientation > 1)) {
148 RImage *tmp = NULL;
149 switch (orientation) {
150 case 2:
151 tmp = RFlipImage(image, RHorizontalFlip);
152 break;
153 case 3:
154 tmp = RRotateImage(image, 180);
155 break;
156 case 4:
157 tmp = RFlipImage(image, RVerticalFlip);
158 break;
159 case 5: {
160 RImage *tmp2;
161 tmp2 = RFlipImage(image, RVerticalFlip);
162 if (tmp2) {
163 tmp = RRotateImage(tmp2, 90);
164 RReleaseImage(tmp2);
167 break;
168 case 6:
169 tmp = RRotateImage(image, 90);
170 break;
171 case 7: {
172 RImage *tmp2;
173 tmp2 = RFlipImage(image, RVerticalFlip);
174 if (tmp2) {
175 tmp = RRotateImage(tmp2, 270);
176 RReleaseImage(tmp2);
179 break;
180 case 8:
181 tmp = RRotateImage(image, 270);
182 break;
184 if (tmp) {
185 RReleaseImage(image);
186 image = tmp;
189 #endif
190 return image;
194 change_title: used to change window title
195 return EXIT_SUCCESS on success, 1 on failure
197 int change_title(XTextProperty *prop, char *filename)
199 char *combined_title = NULL;
200 if (!asprintf(&combined_title, "%s - %u/%u - %s", APPNAME, current_index, max_index, filename))
201 if (!asprintf(&combined_title, "%s - %u/%u", APPNAME, current_index, max_index))
202 return EXIT_FAILURE;
203 XStringListToTextProperty(&combined_title, 1, prop);
204 XSetWMName(dpy, win, prop);
205 if (prop->value)
206 XFree(prop->value);
207 free(combined_title);
208 return EXIT_SUCCESS;
212 rescale_image: used to rescale the current image based on the screen size
213 return EXIT_SUCCESS on success
215 int rescale_image(void)
217 long final_width = img->width;
218 long final_height = img->height;
220 /* check if there is already a zoom factor applied */
221 if (fabsf(zoom_factor) <= 0.0f) {
222 final_width = img->width + (int)(img->width * zoom_factor);
223 final_height = img->height + (int)(img->height * zoom_factor);
225 if ((max_width < final_width) || (max_height < final_height)) {
226 long val = 0;
227 if (final_width > final_height) {
228 val = final_height * max_width / final_width;
229 final_width = final_width * val / final_height;
230 final_height = val;
231 if (val > max_height) {
232 val = final_width * max_height / final_height;
233 final_height = final_height * val / final_width;
234 final_width = val;
236 } else {
237 val = final_width * max_height / final_height;
238 final_height = final_height * val / final_width;
239 final_width = val;
240 if (val > max_width) {
241 val = final_height * max_width / final_width;
242 final_width = final_width * val / final_height;
243 final_height = val;
247 if ((final_width != img->width) || (final_height != img->height)) {
248 RImage *old_img = img;
249 img = RScaleImage(img, final_width, final_height);
250 if (!img) {
251 img = old_img;
252 return EXIT_FAILURE;
254 RReleaseImage(old_img);
256 if (!RConvertImage(ctx, img, &pix)) {
257 fprintf(stderr, "%s\n", RMessageForError(RErrorCode));
258 return EXIT_FAILURE;
260 return EXIT_SUCCESS;
264 maximize_image: find the best image size for the current display
265 return EXIT_SUCCESS on success
267 int maximize_image(void)
269 rescale_image();
270 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
271 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
272 return EXIT_SUCCESS;
276 merge_with_background: merge the current image with with a checkboard background
277 return EXIT_SUCCESS on success, 1 on failure
279 int merge_with_background(RImage *i)
281 if (i) {
282 RImage *back;
283 back = RCreateImage(i->width, i->height, True);
284 if (back) {
285 int opaq = 255;
286 int x = 0, y = 0;
288 RFillImage(back, &lightGray);
289 for (x = 0; x <= i->width; x += 8) {
290 if (x/8 % 2)
291 y = 8;
292 else
293 y = 0;
294 for (; y <= i->height; y += 16)
295 ROperateRectangle(back, RAddOperation, x, y, x+8, y+8, &darkGray);
298 RCombineImagesWithOpaqueness(i, back, opaq);
299 RReleaseImage(back);
300 return EXIT_SUCCESS;
303 return EXIT_FAILURE;
307 turn_image: rotate the image by the angle passed
308 return EXIT_SUCCESS on success, EXIT_FAILURE on failure
310 int turn_image(float angle)
312 RImage *tmp;
314 if (!img)
315 return EXIT_FAILURE;
317 tmp = RRotateImage(img, angle);
318 if (!tmp)
319 return EXIT_FAILURE;
321 if (!fullscreen_flag) {
322 if (img->width != tmp->width || img->height != tmp->height)
323 XResizeWindow(dpy, win, tmp->width, tmp->height);
326 RReleaseImage(img);
327 img = tmp;
329 rescale_image();
330 if (!fullscreen_flag) {
331 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
332 } else {
333 XClearWindow(dpy, win);
334 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
335 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
338 return EXIT_SUCCESS;
342 turn_image_right: rotate the image by 90 degree
343 return EXIT_SUCCESS on success, EXIT_FAILURE on failure
345 int turn_image_right(void)
347 return turn_image(90.0);
351 turn_image_left: rotate the image by -90 degree
352 return EXIT_SUCCESS on success, 1 on failure
354 int turn_image_left(void)
356 return turn_image(-90.0);
360 draw_failed_image: create a red crossed image to indicate an error loading file
361 return the image on success, NULL on failure
364 RImage *draw_failed_image(void)
366 RImage *failed_image = NULL;
367 XWindowAttributes attr;
369 if (win && (XGetWindowAttributes(dpy, win, &attr) >= 0))
370 failed_image = RCreateImage(attr.width, attr.height, False);
371 else
372 failed_image = RCreateImage(50, 50, False);
373 if (!failed_image)
374 return NULL;
376 RFillImage(failed_image, &black);
377 ROperateLine(failed_image, RAddOperation, 0, 0, failed_image->width, failed_image->height, &red);
378 ROperateLine(failed_image, RAddOperation, 0, failed_image->height, failed_image->width, 0, &red);
380 return failed_image;
384 full_screen: sending event to the window manager to switch from/to full screen mode
385 return EXIT_SUCCESS on success, 1 on failure
387 int full_screen(void)
389 XEvent xev;
391 Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", True);
392 Atom fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", True);
393 long mask = SubstructureNotifyMask;
395 if (fullscreen_flag) {
396 fullscreen_flag = False;
397 zoom_factor = 0;
398 back_from_fullscreen = True;
399 } else {
400 fullscreen_flag = True;
401 zoom_factor = 1000;
404 memset(&xev, 0, sizeof(xev));
405 xev.type = ClientMessage;
406 xev.xclient.display = dpy;
407 xev.xclient.window = win;
408 xev.xclient.message_type = wm_state;
409 xev.xclient.format = 32;
410 xev.xclient.data.l[0] = fullscreen_flag;
411 xev.xclient.data.l[1] = fullscreen;
413 if (!XSendEvent(dpy, DefaultRootWindow(dpy), False, mask, &xev)) {
414 fprintf(stderr, "Error: sending fullscreen event to xserver\n");
415 return EXIT_FAILURE;
417 return EXIT_SUCCESS;
421 zoom_in_out: apply a zoom factor on the current image
422 arg: 1 to zoom in, 0 to zoom out
423 return EXIT_SUCCESS on success, 1 on failure
425 int zoom_in_out(int z)
427 RImage *old_img = img;
428 RImage *tmp = load_oriented_image(ctx, current_link->data, 0);
429 if (!tmp)
430 return EXIT_FAILURE;
432 if (z) {
433 zoom_factor += 0.2f;
434 img = RScaleImage(tmp, tmp->width + (int)(tmp->width * zoom_factor),
435 tmp->height + (int)(tmp->height * zoom_factor));
436 if (!img) {
437 img = old_img;
438 RReleaseImage(tmp);
439 return EXIT_FAILURE;
441 } else {
442 zoom_factor -= 0.2f;
443 int new_width = tmp->width + (int) (tmp->width * zoom_factor);
444 int new_height = tmp->height + (int)(tmp->height * zoom_factor);
445 if ((new_width <= 0) || (new_height <= 0)) {
446 zoom_factor += 0.2f;
447 RReleaseImage(tmp);
448 return EXIT_FAILURE;
450 img = RScaleImage(tmp, new_width, new_height);
451 if (!img) {
452 img = old_img;
453 RReleaseImage(tmp);
454 return EXIT_FAILURE;
457 RReleaseImage(old_img);
458 RReleaseImage(tmp);
459 XFreePixmap(dpy, pix);
461 merge_with_background(img);
462 if (!RConvertImage(ctx, img, &pix)) {
463 fprintf(stderr, "%s\n", RMessageForError(RErrorCode));
464 return EXIT_FAILURE;
466 XResizeWindow(dpy, win, img->width, img->height);
467 return EXIT_SUCCESS;
471 zoom_in: transitional fct used to call zoom_in_out with zoom in flag
472 return EXIT_SUCCESS on success, 1 on failure
474 int zoom_in(void)
476 return zoom_in_out(1);
480 zoom_out: transitional fct used to call zoom_in_out with zoom out flag
481 return EXIT_SUCCESS on success, 1 on failure
483 int zoom_out(void)
485 return zoom_in_out(0);
489 change_image: load previous or next image
490 arg: way which could be PREV or NEXT constant
491 return EXIT_SUCCESS on success, 1 on failure
493 int change_image(int way)
495 if (img && current_link) {
496 int old_img_width = img->width;
497 int old_img_height = img->height;
499 RReleaseImage(img);
501 if (way == NEXT) {
502 current_link = current_link->next;
503 current_index++;
504 } else {
505 current_link = current_link->prev;
506 current_index--;
508 if (current_link == NULL) {
509 if (way == NEXT) {
510 current_link = list.first;
511 current_index = 1;
512 } else {
513 current_link = list.last;
514 current_index = max_index;
517 if (WMIV_DEBUG)
518 fprintf(stderr, "current file is> %s\n", (char *)current_link->data);
519 img = load_oriented_image(ctx, current_link->data, 0);
521 if (!img) {
522 fprintf(stderr, "Error: %s %s\n", (char *)current_link->data,
523 RMessageForError(RErrorCode));
524 img = draw_failed_image();
525 } else {
526 merge_with_background(img);
528 rescale_image();
529 if (!fullscreen_flag) {
530 if ((old_img_width != img->width) || (old_img_height != img->height))
531 XResizeWindow(dpy, win, img->width, img->height);
532 else
533 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
534 change_title(&title_property, (char *)current_link->data);
535 } else {
536 XClearWindow(dpy, win);
537 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
538 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
540 return EXIT_SUCCESS;
542 return EXIT_FAILURE;
545 #ifdef HAVE_PTHREAD
547 diaporama: send a xevent to display the next image at every delay set to diaporama_delay
548 arg: not used
549 return void
551 void *diaporama(void *arg)
553 (void) arg;
555 XKeyEvent event;
556 event.display = dpy;
557 event.window = win;
558 event.root = DefaultRootWindow(dpy);
559 event.subwindow = None;
560 event.time = CurrentTime;
561 event.x = 1;
562 event.y = 1;
563 event.x_root = 1;
564 event.y_root = 1;
565 event.same_screen = True;
566 event.keycode = XKeysymToKeycode(dpy, XK_Right);
567 event.state = 0;
568 event.type = KeyPress;
570 while (diaporama_flag) {
571 int r;
572 r = XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
573 if (!r)
574 fprintf(stderr, "Error sending event\n");
575 XFlush(dpy);
576 /* default sleep time between moving to next image */
577 sleep(diaporama_delay);
579 tid = 0;
580 return arg;
582 #endif
585 linked_list_init: init the linked list
587 void linked_list_init(linked_list_t *list)
589 list->first = list->last = 0;
590 list->count = 0;
594 linked_list_add: add an element to the linked list
595 return EXIT_SUCCESS on success, 1 otherwise
597 int linked_list_add(linked_list_t *list, const void *data)
599 link_t *link;
601 /* calloc sets the "next" field to zero. */
602 link = calloc(1, sizeof(link_t));
603 if (!link) {
604 fprintf(stderr, "Error: memory allocation failed\n");
605 return EXIT_FAILURE;
607 link->data = data;
608 if (list->last) {
609 /* Join the two final links together. */
610 list->last->next = link;
611 link->prev = list->last;
612 list->last = link;
613 } else {
614 list->first = link;
615 list->last = link;
617 list->count++;
618 return EXIT_SUCCESS;
622 linked_list_free: deallocate the whole linked list
624 void linked_list_free(linked_list_t *list)
626 link_t *link;
627 link_t *next;
628 for (link = list->first; link; link = next) {
629 /* Store the next value so that we don't access freed memory. */
630 next = link->next;
631 if (link->data)
632 free((char *)link->data);
633 free(link);
638 connect_dir: list and sort by name all files from a given directory
639 arg: the directory path that contains images, the linked list where to add the new file refs
640 return: the first argument of the list or NULL on failure
642 link_t *connect_dir(char *dirpath, linked_list_t *li)
644 struct dirent **dir;
645 int dv, idx;
646 char path[PATH_MAX] = "";
648 if (!dirpath)
649 return NULL;
651 dv = scandir(dirpath, &dir, 0, alphasort);
652 if (dv < 0) {
653 /* maybe it's a file */
654 struct stat stDirInfo;
655 if (lstat(dirpath, &stDirInfo) == 0) {
656 linked_list_add(li, strdup(dirpath));
657 return li->first;
658 } else {
659 return NULL;
662 for (idx = 0; idx < dv; idx++) {
663 struct stat stDirInfo;
664 if (dirpath[strlen(dirpath)-1] == FILE_SEPARATOR)
665 snprintf(path, PATH_MAX, "%s%s", dirpath, dir[idx]->d_name);
666 else
667 snprintf(path, PATH_MAX, "%s%c%s", dirpath, FILE_SEPARATOR, dir[idx]->d_name);
669 free(dir[idx]);
670 if ((lstat(path, &stDirInfo) == 0) && !S_ISDIR(stDirInfo.st_mode))
671 linked_list_add(li, strdup(path));
673 free(dir);
674 return li->first;
678 main
680 int main(int argc, char **argv)
682 int option = -1;
683 RContextAttributes attr = {};
684 XEvent e;
685 KeySym keysym;
686 char *reading_filename = "";
687 int screen, file_i;
688 int quit = 0;
689 XClassHint *class_hints;
690 XSizeHints *size_hints;
691 XWMHints *win_hints;
692 #ifdef USE_XPM
693 Pixmap icon_pixmap, icon_shape;
694 #endif
695 class_hints = XAllocClassHint();
696 if (!class_hints) {
697 fprintf(stderr, "Error: failure allocating memory\n");
698 return EXIT_FAILURE;
700 class_hints->res_name = (char *)APPNAME;
701 class_hints->res_class = "default";
703 /* init colors */
704 lightGray.red = lightGray.green = lightGray.blue = 211;
705 darkGray.red = darkGray.green = darkGray.blue = 169;
706 lightGray.alpha = darkGray.alpha = 1;
707 black.red = black.green = black.blue = 0;
708 red.red = 255;
709 red.green = red.blue = 0;
711 static struct option long_options[] = {
712 {"version", no_argument, 0, 'v'},
713 {"help", no_argument, 0, 'h'},
714 {0, 0, 0, 0}
716 int option_index = 0;
718 option = getopt_long (argc, argv, "hv", long_options, &option_index);
719 if (option != -1) {
720 switch (option) {
721 case 'h':
722 printf("Usage: %s [image(s)|directory]\n"
723 "Options:\n"
724 " -h, --help print this help text\n"
725 " -v, --version print version\n"
726 "Keys:\n"
727 " [+] zoom in\n"
728 " [-] zoom out\n"
729 " [Esc] actual size\n"
730 #ifdef HAVE_PTHREAD
731 " [D] launch diaporama mode\n"
732 #endif
733 " [L] rotate image on the left\n"
734 " [Q] quit\n"
735 " [R] rotate image on the right\n"
736 " [â–¸] next image\n"
737 " [â—‚] previous image\n"
738 " [â–´] first image\n"
739 " [â–¾] last image\n",
740 argv[0]);
741 return EXIT_SUCCESS;
742 case 'v':
743 fprintf(stderr, "%s version %d.%d\n", APPNAME, APPVERSION_MAJOR, APPVERSION_MINOR);
744 return EXIT_SUCCESS;
745 case '?':
746 return EXIT_FAILURE;
750 linked_list_init(&list);
752 dpy = XOpenDisplay(NULL);
753 if (!dpy) {
754 fprintf(stderr, "Error: can't open display\n");
755 linked_list_free(&list);
756 return EXIT_FAILURE;
759 screen = DefaultScreen(dpy);
760 max_width = DisplayWidth(dpy, screen);
761 max_height = DisplayHeight(dpy, screen);
763 attr.flags = RC_RenderMode | RC_ColorsPerChannel;
764 attr.render_mode = RDitheredRendering;
765 attr.colors_per_channel = 4;
766 ctx = RCreateContext(dpy, DefaultScreen(dpy), &attr);
768 if (argc < 2) {
769 argv[1] = ".";
770 argc = 2;
773 for (file_i = 1; file_i < argc; file_i++) {
774 current_link = connect_dir(argv[file_i], &list);
775 if (current_link) {
776 reading_filename = (char *)current_link->data;
777 max_index = list.count;
781 img = load_oriented_image(ctx, reading_filename, 0);
783 if (!img) {
784 fprintf(stderr, "Error: %s %s\n", reading_filename, RMessageForError(RErrorCode));
785 img = draw_failed_image();
786 if (!current_link)
787 return EXIT_FAILURE;
790 merge_with_background(img);
791 rescale_image();
793 if (WMIV_DEBUG)
794 fprintf(stderr, "display size: %dx%d\n", max_width, max_height);
796 win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
797 img->width, img->height, 0, 0, BlackPixel(dpy, screen));
798 XSelectInput(dpy, win, KeyPressMask|StructureNotifyMask|ExposureMask|ButtonPressMask|FocusChangeMask);
800 size_hints = XAllocSizeHints();
801 if (!size_hints) {
802 fprintf(stderr, "Error: failure allocating memory\n");
803 return EXIT_FAILURE;
805 size_hints->width = img->width;
806 size_hints->height = img->height;
808 Atom delWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
809 XSetWMProtocols(dpy, win, &delWindow, 1);
810 change_title(&title_property, reading_filename);
812 win_hints = XAllocWMHints();
813 if (win_hints) {
814 win_hints->flags = StateHint|InputHint|WindowGroupHint;
816 #ifdef USE_XPM
817 if ((XpmCreatePixmapFromData(dpy, win, wmiv_xpm, &icon_pixmap, &icon_shape, NULL)) == 0) {
818 win_hints->flags |= IconPixmapHint|IconMaskHint|IconPositionHint;
819 win_hints->icon_pixmap = icon_pixmap;
820 win_hints->icon_mask = icon_shape;
821 win_hints->icon_x = 0;
822 win_hints->icon_y = 0;
824 #endif
825 win_hints->initial_state = NormalState;
826 win_hints->input = True;
827 win_hints->window_group = win;
828 XStringListToTextProperty((char **)&APPNAME, 1, &icon_property);
829 XSetWMProperties(dpy, win, NULL, &icon_property, argv, argc, size_hints, win_hints, class_hints);
830 if (icon_property.value)
831 XFree(icon_property.value);
832 XFree(win_hints);
833 XFree(class_hints);
834 XFree(size_hints);
837 XMapWindow(dpy, win);
838 XFlush(dpy);
839 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
841 while (!quit) {
842 XNextEvent(dpy, &e);
843 if (e.type == ClientMessage) {
844 if (e.xclient.data.l[0] == delWindow)
845 quit = 1;
848 * This break could be related to all ClientMessages or
849 * related to delWindow. Before the patch about this comment
850 * the break was indented with one tab more (at the same level
851 * than "quit = 1;" in the previous line.
853 break;
855 if (e.type == FocusIn) {
856 focus = True;
857 continue;
859 if (e.type == FocusOut) {
860 focus = False;
861 continue;
863 if (!fullscreen_flag && (e.type == Expose)) {
864 XExposeEvent xev = e.xexpose;
865 if (xev.count == 0)
866 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
867 continue;
869 if (!fullscreen_flag && e.type == ConfigureNotify) {
870 XConfigureEvent xce = e.xconfigure;
871 if (xce.width != img->width || xce.height != img->height) {
872 RImage *old_img = img;
873 img = load_oriented_image(ctx, current_link->data, 0);
874 if (!img) {
875 /* keep the old img and window size */
876 img = old_img;
877 XResizeWindow(dpy, win, img->width, img->height);
878 } else {
879 RImage *tmp2;
880 if (!back_from_fullscreen)
881 /* manually resized window */
882 tmp2 = RScaleImage(img, xce.width, xce.height);
883 else {
884 /* back from fullscreen mode, maybe img was rotated */
885 tmp2 = img;
886 back_from_fullscreen = False;
887 XClearWindow(dpy, win);
889 merge_with_background(tmp2);
890 if (RConvertImage(ctx, tmp2, &pix)) {
891 RReleaseImage(old_img);
892 img = RCloneImage(tmp2);
893 RReleaseImage(tmp2);
894 change_title(&title_property, (char *)current_link->data);
895 XSync(dpy, True);
896 XResizeWindow(dpy, win, img->width, img->height);
897 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
898 img->width, img->height, 0, 0);
903 continue;
905 if (fullscreen_flag && e.type == ConfigureNotify) {
906 maximize_image();
907 continue;
909 if (e.type == ButtonPress) {
910 switch (e.xbutton.button) {
911 case Button1: {
912 if (focus) {
913 if (img && (e.xbutton.x > img->width/2))
914 change_image(NEXT);
915 else
916 change_image(PREV);
919 break;
920 case Button4:
921 zoom_in();
922 break;
923 case Button5:
924 zoom_out();
925 break;
926 case 8:
927 change_image(PREV);
928 break;
929 case 9:
930 change_image(NEXT);
931 break;
933 continue;
935 if (e.type == KeyPress) {
936 keysym = W_KeycodeToKeysym(dpy, e.xkey.keycode, e.xkey.state & ShiftMask?1:0);
937 #ifdef HAVE_PTHREAD
938 if (keysym != XK_Right)
939 diaporama_flag = False;
940 #endif
941 switch (keysym) {
942 case XK_Right:
943 change_image(NEXT);
944 break;
945 case XK_Left:
946 change_image(PREV);
947 break;
948 case XK_Up:
949 if (current_link) {
950 current_link = list.last;
951 change_image(NEXT);
953 break;
954 case XK_Down:
955 if (current_link) {
956 current_link = list.first;
957 change_image(PREV);
959 break;
960 #ifdef HAVE_PTHREAD
961 case XK_F5:
962 case XK_d:
963 if (!tid) {
964 if (current_link && !diaporama_flag) {
965 diaporama_flag = True;
966 pthread_create(&tid, NULL, &diaporama, NULL);
967 } else {
968 fprintf(stderr, "Can't use diaporama mode\n");
971 break;
972 #endif
973 case XK_q:
974 quit = 1;
975 break;
976 case XK_Escape:
977 if (!fullscreen_flag) {
978 zoom_factor = -0.2f;
979 /* zoom_in will increase the zoom factor by 0.2 */
980 zoom_in();
981 } else {
982 /* we are in fullscreen mode already, want to return to normal size */
983 full_screen();
985 break;
986 case XK_plus:
987 zoom_in();
988 break;
989 case XK_minus:
990 zoom_out();
991 break;
992 case XK_F11:
993 case XK_f:
994 full_screen();
995 break;
996 case XK_r:
997 turn_image_right();
998 break;
999 case XK_l:
1000 turn_image_left();
1001 break;
1007 if (img)
1008 RReleaseImage(img);
1009 if (pix)
1010 XFreePixmap(dpy, pix);
1011 #ifdef USE_XPM
1012 if (icon_pixmap)
1013 XFreePixmap(dpy, icon_pixmap);
1014 if (icon_shape)
1015 XFreePixmap(dpy, icon_shape);
1016 #endif
1017 linked_list_free(&list);
1018 RDestroyContext(ctx);
1019 RShutdown();
1020 XCloseDisplay(dpy);
1021 return EXIT_SUCCESS;