Undo XDG string-escaping when generating menu-entries.
[wmaker-crm.git] / util / wmiv.c
blob57ad4d4e90e4eb30055a26f1c3511c8383ee6eee
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 <getopt.h>
38 #include <math.h>
39 #include "config.h"
41 #ifdef HAVE_EXIF
42 #include <libexif/exif-data.h>
43 #endif
45 #ifdef HAVE_PTHREAD
46 #include <pthread.h>
47 #endif
49 #ifdef USE_XPM
50 extern int XpmCreatePixmapFromData(Display *, Drawable, char **, Pixmap *, Pixmap *, void *);
51 /* this is the icon from eog project
52 git.gnome.org/browse/eog
54 #include "wmiv.h"
55 #endif
57 #define DEBUG 0
58 #define FILE_SEPARATOR '/'
60 Display *dpy;
61 Window win;
62 RContext *ctx;
63 RImage *img;
64 Pixmap pix;
66 const char *APPNAME = "wmiv";
67 int APPVERSION_MAJOR = 0;
68 int APPVERSION_MINOR = 7;
69 int NEXT = 0;
70 int PREV = 1;
71 float zoom_factor = 0;
72 int max_width = 0;
73 int max_height = 0;
75 Bool fullscreen_flag = False;
76 Bool focus = False;
77 Bool back_from_fullscreen = False;
79 #ifdef HAVE_PTHREAD
80 Bool diaporama_flag = False;
81 int diaporama_delay = 5;
82 pthread_t tid = 0;
83 #endif
84 XTextProperty title_property;
85 XTextProperty icon_property;
86 unsigned current_index = 1;
87 unsigned max_index = 1;
89 RColor lightGray;
90 RColor darkGray;
91 RColor black;
92 RColor red;
94 typedef struct link link_t;
95 struct link {
96 const void *data;
97 link_t *prev;
98 link_t *next;
101 typedef struct linked_list {
102 int count;
103 link_t *first;
104 link_t *last;
105 } linked_list_t;
107 linked_list_t list;
108 link_t *current_link;
112 load_oriented_image: used to load an image and optionally
113 get its orientation if libexif is available
114 return the image on success, NULL on failure
116 RImage *load_oriented_image(RContext *context, const char *file, int index)
118 RImage *image;
119 #ifdef HAVE_EXIF
120 int orientation = 0;
121 #endif
122 image = RLoadImage(context, file, index);
123 if (!image)
124 return NULL;
125 #ifdef HAVE_EXIF
126 ExifData *exifData = exif_data_new_from_file(file);
127 if (exifData) {
128 ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
129 ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
130 if (exifEntry)
131 orientation = exif_get_short(exifEntry->data, byteOrder);
133 exif_data_free(exifData);
137 0th Row 0th Column
138 1 top left side
139 2 top right side
140 3 bottom right side
141 4 bottom left side
142 5 left side top
143 6 right side top
144 7 right side bottom
145 8 left side bottom
148 if (image && (orientation > 1)) {
149 RImage *tmp = NULL;
150 switch (orientation) {
151 case 2:
152 tmp = RFlipImage(image, RHorizontalFlip);
153 break;
154 case 3:
155 tmp = RRotateImage(image, 180);
156 break;
157 case 4:
158 tmp = RFlipImage(image, RVerticalFlip);
159 break;
160 case 5: {
161 RImage *tmp2;
162 tmp2 = RFlipImage(image, RVerticalFlip);
163 if (tmp2) {
164 tmp = RRotateImage(tmp2, 90);
165 RReleaseImage(tmp2);
168 break;
169 case 6:
170 tmp = RRotateImage(image, 90);
171 break;
172 case 7: {
173 RImage *tmp2;
174 tmp2 = RFlipImage(image, RVerticalFlip);
175 if (tmp2) {
176 tmp = RRotateImage(tmp2, 270);
177 RReleaseImage(tmp2);
180 break;
181 case 8:
182 tmp = RRotateImage(image, 270);
183 break;
185 if (tmp) {
186 RReleaseImage(image);
187 image = tmp;
190 #endif
191 return image;
195 change_title: used to change window title
196 return EXIT_SUCCESS on success, 1 on failure
198 int change_title(XTextProperty *prop, char *filename)
200 char *combined_title = NULL;
201 if (!asprintf(&combined_title, "%s - %u/%u - %s", APPNAME, current_index, max_index, filename))
202 if (!asprintf(&combined_title, "%s - %u/%u", APPNAME, current_index, max_index))
203 return EXIT_FAILURE;
204 XStringListToTextProperty(&combined_title, 1, prop);
205 XSetWMName(dpy, win, prop);
206 if (prop->value)
207 XFree(prop->value);
208 free(combined_title);
209 return EXIT_SUCCESS;
213 rescale_image: used to rescale the current image based on the screen size
214 return EXIT_SUCCESS on success
216 int rescale_image(void)
218 long final_width = img->width;
219 long final_height = img->height;
221 /* check if there is already a zoom factor applied */
222 if (fabsf(zoom_factor) <= 0.0f) {
223 final_width = img->width + (int)(img->width * zoom_factor);
224 final_height = img->height + (int)(img->height * zoom_factor);
226 if ((max_width < final_width) || (max_height < final_height)) {
227 long val = 0;
228 if (final_width > final_height) {
229 val = final_height * max_width / final_width;
230 final_width = final_width * val / final_height;
231 final_height = val;
232 if (val > max_height) {
233 val = final_width * max_height / final_height;
234 final_height = final_height * val / final_width;
235 final_width = val;
237 } else {
238 val = final_width * max_height / final_height;
239 final_height = final_height * val / final_width;
240 final_width = val;
241 if (val > max_width) {
242 val = final_height * max_width / final_width;
243 final_width = final_width * val / final_height;
244 final_height = val;
248 if ((final_width != img->width) || (final_height != img->height)) {
249 RImage *old_img = img;
250 img = RScaleImage(img, final_width, final_height);
251 if (!img) {
252 img = old_img;
253 return EXIT_FAILURE;
255 RReleaseImage(old_img);
257 if (!RConvertImage(ctx, img, &pix)) {
258 fprintf(stderr, "%s\n", RMessageForError(RErrorCode));
259 return EXIT_FAILURE;
261 return EXIT_SUCCESS;
265 maximize_image: find the best image size for the current display
266 return EXIT_SUCCESS on success
268 int maximize_image(void)
270 rescale_image();
271 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
272 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
273 return EXIT_SUCCESS;
277 merge_with_background: merge the current image with with a checkboard background
278 return EXIT_SUCCESS on success, 1 on failure
280 int merge_with_background(RImage *i)
282 if (i) {
283 RImage *back;
284 back = RCreateImage(i->width, i->height, True);
285 if (back) {
286 int opaq = 255;
287 int x = 0, y = 0;
289 RFillImage(back, &lightGray);
290 for (x = 0; x <= i->width; x += 8) {
291 if (x/8 % 2)
292 y = 8;
293 else
294 y = 0;
295 for (; y <= i->height; y += 16)
296 ROperateRectangle(back, RAddOperation, x, y, x+8, y+8, &darkGray);
299 RCombineImagesWithOpaqueness(i, back, opaq);
300 RReleaseImage(back);
301 return EXIT_SUCCESS;
304 return EXIT_FAILURE;
308 turn_image: rotate the image by the angle passed
309 return EXIT_SUCCESS on success, EXIT_FAILURE on failure
311 int turn_image(float angle)
313 RImage *tmp;
315 if (!img)
316 return EXIT_FAILURE;
318 tmp = RRotateImage(img, angle);
319 if (!tmp)
320 return EXIT_FAILURE;
322 if (!fullscreen_flag) {
323 if (img->width != tmp->width || img->height != tmp->height)
324 XResizeWindow(dpy, win, tmp->width, tmp->height);
327 RReleaseImage(img);
328 img = tmp;
330 rescale_image();
331 if (!fullscreen_flag) {
332 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
333 } else {
334 XClearWindow(dpy, win);
335 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
336 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
339 return EXIT_SUCCESS;
343 turn_image_right: rotate the image by 90 degree
344 return EXIT_SUCCESS on success, EXIT_FAILURE on failure
346 int turn_image_right(void)
348 return turn_image(90.0);
352 turn_image_left: rotate the image by -90 degree
353 return EXIT_SUCCESS on success, 1 on failure
355 int turn_image_left(void)
357 return turn_image(-90.0);
361 draw_failed_image: create a red crossed image to indicate an error loading file
362 return the image on success, NULL on failure
365 RImage *draw_failed_image(void)
367 RImage *failed_image = NULL;
368 XWindowAttributes attr;
370 if (win && (XGetWindowAttributes(dpy, win, &attr) >= 0))
371 failed_image = RCreateImage(attr.width, attr.height, False);
372 else
373 failed_image = RCreateImage(50, 50, False);
374 if (!failed_image)
375 return NULL;
377 RFillImage(failed_image, &black);
378 ROperateLine(failed_image, RAddOperation, 0, 0, failed_image->width, failed_image->height, &red);
379 ROperateLine(failed_image, RAddOperation, 0, failed_image->height, failed_image->width, 0, &red);
381 return failed_image;
385 full_screen: sending event to the window manager to switch from/to full screen mode
386 return EXIT_SUCCESS on success, 1 on failure
388 int full_screen(void)
390 XEvent xev;
392 Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", True);
393 Atom fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", True);
394 long mask = SubstructureNotifyMask;
396 if (fullscreen_flag) {
397 fullscreen_flag = False;
398 zoom_factor = 0;
399 back_from_fullscreen = True;
400 } else {
401 fullscreen_flag = True;
402 zoom_factor = 1000;
405 memset(&xev, 0, sizeof(xev));
406 xev.type = ClientMessage;
407 xev.xclient.display = dpy;
408 xev.xclient.window = win;
409 xev.xclient.message_type = wm_state;
410 xev.xclient.format = 32;
411 xev.xclient.data.l[0] = fullscreen_flag;
412 xev.xclient.data.l[1] = fullscreen;
414 if (!XSendEvent(dpy, DefaultRootWindow(dpy), False, mask, &xev)) {
415 fprintf(stderr, "Error: sending fullscreen event to xserver\n");
416 return EXIT_FAILURE;
418 return EXIT_SUCCESS;
422 zoom_in_out: apply a zoom factor on the current image
423 arg: 1 to zoom in, 0 to zoom out
424 return EXIT_SUCCESS on success, 1 on failure
426 int zoom_in_out(int z)
428 RImage *old_img = img;
429 RImage *tmp = load_oriented_image(ctx, current_link->data, 0);
430 if (!tmp)
431 return EXIT_FAILURE;
433 if (z) {
434 zoom_factor += 0.2f;
435 img = RScaleImage(tmp, tmp->width + (int)(tmp->width * zoom_factor),
436 tmp->height + (int)(tmp->height * zoom_factor));
437 if (!img) {
438 img = old_img;
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 return EXIT_FAILURE;
456 RReleaseImage(old_img);
457 RReleaseImage(tmp);
458 XFreePixmap(dpy, pix);
460 merge_with_background(img);
461 if (!RConvertImage(ctx, img, &pix)) {
462 fprintf(stderr, "%s\n", RMessageForError(RErrorCode));
463 return EXIT_FAILURE;
465 XResizeWindow(dpy, win, img->width, img->height);
466 return EXIT_SUCCESS;
470 zoom_in: transitional fct used to call zoom_in_out with zoom in flag
471 return EXIT_SUCCESS on success, 1 on failure
473 int zoom_in(void)
475 return zoom_in_out(1);
479 zoom_out: transitional fct used to call zoom_in_out with zoom out flag
480 return EXIT_SUCCESS on success, 1 on failure
482 int zoom_out(void)
484 return zoom_in_out(0);
488 change_image: load previous or next image
489 arg: way which could be PREV or NEXT constant
490 return EXIT_SUCCESS on success, 1 on failure
492 int change_image(int way)
494 if (img && current_link) {
495 int old_img_width = img->width;
496 int old_img_height = img->height;
498 RReleaseImage(img);
500 if (way == NEXT) {
501 current_link = current_link->next;
502 current_index++;
503 } else {
504 current_link = current_link->prev;
505 current_index--;
507 if (current_link == NULL) {
508 if (way == NEXT) {
509 current_link = list.first;
510 current_index = 1;
511 } else {
512 current_link = list.last;
513 current_index = max_index;
516 if (DEBUG)
517 fprintf(stderr, "current file is> %s\n", (char *)current_link->data);
518 img = load_oriented_image(ctx, current_link->data, 0);
520 if (!img) {
521 fprintf(stderr, "Error: %s %s\n", (char *)current_link->data,
522 RMessageForError(RErrorCode));
523 img = draw_failed_image();
524 } else {
525 merge_with_background(img);
527 rescale_image();
528 if (!fullscreen_flag) {
529 if ((old_img_width != img->width) || (old_img_height != img->height))
530 XResizeWindow(dpy, win, img->width, img->height);
531 else
532 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
533 change_title(&title_property, (char *)current_link->data);
534 } else {
535 XClearWindow(dpy, win);
536 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
537 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
539 return EXIT_SUCCESS;
541 return EXIT_FAILURE;
544 #ifdef HAVE_PTHREAD
546 diaporama: send a xevent to display the next image at every delay set to diaporama_delay
547 arg: not used
548 return void
550 void *diaporama(void *arg)
552 (void) arg;
554 XKeyEvent event;
555 event.display = dpy;
556 event.window = win;
557 event.root = DefaultRootWindow(dpy);
558 event.subwindow = None;
559 event.time = CurrentTime;
560 event.x = 1;
561 event.y = 1;
562 event.x_root = 1;
563 event.y_root = 1;
564 event.same_screen = True;
565 event.keycode = XKeysymToKeycode(dpy, XK_Right);
566 event.state = 0;
567 event.type = KeyPress;
569 while (diaporama_flag) {
570 int r;
571 r = XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
572 if (!r)
573 fprintf(stderr, "Error sending event\n");
574 XFlush(dpy);
575 /* default sleep time between moving to next image */
576 sleep(diaporama_delay);
578 tid = 0;
579 return arg;
581 #endif
584 linked_list_init: init the linked list
586 void linked_list_init(linked_list_t *list)
588 list->first = list->last = 0;
589 list->count = 0;
593 linked_list_add: add an element to the linked list
594 return EXIT_SUCCESS on success, 1 otherwise
596 int linked_list_add(linked_list_t *list, const void *data)
598 link_t *link;
600 /* calloc sets the "next" field to zero. */
601 link = calloc(1, sizeof(link_t));
602 if (!link) {
603 fprintf(stderr, "Error: memory allocation failed\n");
604 return EXIT_FAILURE;
606 link->data = data;
607 if (list->last) {
608 /* Join the two final links together. */
609 list->last->next = link;
610 link->prev = list->last;
611 list->last = link;
612 } else {
613 list->first = link;
614 list->last = link;
616 list->count++;
617 return EXIT_SUCCESS;
621 linked_list_free: deallocate the whole linked list
623 void linked_list_free(linked_list_t *list)
625 link_t *link;
626 link_t *next;
627 for (link = list->first; link; link = next) {
628 /* Store the next value so that we don't access freed memory. */
629 next = link->next;
630 if (link->data)
631 free((char *)link->data);
632 free(link);
637 connect_dir: list and sort by name all files from a given directory
638 arg: the directory path that contains images, the linked list where to add the new file refs
639 return: the first argument of the list or NULL on failure
641 link_t *connect_dir(char *dirpath, linked_list_t *li)
643 struct dirent **dir;
644 int dv, idx;
645 char path[PATH_MAX] = "";
647 if (!dirpath)
648 return NULL;
650 dv = scandir(dirpath, &dir, 0, alphasort);
651 if (dv < 0) {
652 /* maybe it's a file */
653 struct stat stDirInfo;
654 if (lstat(dirpath, &stDirInfo) == 0) {
655 linked_list_add(li, strdup(dirpath));
656 return li->first;
657 } else {
658 return NULL;
661 for (idx = 0; idx < dv; idx++) {
662 struct stat stDirInfo;
663 if (dirpath[strlen(dirpath)-1] == FILE_SEPARATOR)
664 snprintf(path, PATH_MAX, "%s%s", dirpath, dir[idx]->d_name);
665 else
666 snprintf(path, PATH_MAX, "%s%c%s", dirpath, FILE_SEPARATOR, dir[idx]->d_name);
668 free(dir[idx]);
669 if ((lstat(path, &stDirInfo) == 0) && !S_ISDIR(stDirInfo.st_mode))
670 linked_list_add(li, strdup(path));
672 free(dir);
673 return li->first;
677 main
679 int main(int argc, char **argv)
681 int option = -1;
682 RContextAttributes attr;
683 XEvent e;
684 KeySym keysym;
685 char *reading_filename = "";
686 int screen, file_i;
687 int quit = 0;
688 XClassHint *class_hints;
689 XSizeHints *size_hints;
690 XWMHints *win_hints;
691 #ifdef USE_XPM
692 Pixmap icon_pixmap, icon_shape;
693 #endif
694 class_hints = XAllocClassHint();
695 if (!class_hints) {
696 fprintf(stderr, "Error: failure allocating memory\n");
697 return EXIT_FAILURE;
699 class_hints->res_name = (char *)APPNAME;
700 class_hints->res_class = "default";
702 /* init colors */
703 lightGray.red = lightGray.green = lightGray.blue = 211;
704 darkGray.red = darkGray.green = darkGray.blue = 169;
705 lightGray.alpha = darkGray.alpha = 1;
706 black.red = black.green = black.blue = 0;
707 red.red = 255;
708 red.green = red.blue = 0;
710 static struct option long_options[] = {
711 {"version", no_argument, 0, 'v'},
712 {"help", no_argument, 0, 'h'},
713 {0, 0, 0, 0}
715 int option_index = 0;
717 option = getopt_long (argc, argv, "hv", long_options, &option_index);
718 if (option != -1) {
719 switch (option) {
720 case 'h':
721 printf("Usage: %s [image(s)|directory]\n"
722 "Options:\n"
723 " -h, --help print this help text\n"
724 " -v, --version print version\n"
725 "Keys:\n"
726 " [+] zoom in\n"
727 " [-] zoom out\n"
728 " [Esc] actual size\n"
729 #ifdef HAVE_PTHREAD
730 " [D] launch diaporama mode\n"
731 #endif
732 " [L] rotate image on the left\n"
733 " [Q] quit\n"
734 " [R] rotate image on the right\n"
735 " [â–¸] next image\n"
736 " [â—‚] previous image\n"
737 " [â–´] first image\n"
738 " [â–¾] last image\n",
739 argv[0]);
740 return EXIT_SUCCESS;
741 case 'v':
742 fprintf(stderr, "%s version %d.%d\n", APPNAME, APPVERSION_MAJOR, APPVERSION_MINOR);
743 return EXIT_SUCCESS;
744 case '?':
745 return EXIT_FAILURE;
749 linked_list_init(&list);
751 dpy = XOpenDisplay(NULL);
752 if (!dpy) {
753 fprintf(stderr, "Error: can't open display");
754 linked_list_free(&list);
755 return EXIT_FAILURE;
758 screen = DefaultScreen(dpy);
759 max_width = DisplayWidth(dpy, screen);
760 max_height = DisplayHeight(dpy, screen);
762 attr.flags = RC_RenderMode | RC_ColorsPerChannel;
763 attr.render_mode = RDitheredRendering;
764 attr.colors_per_channel = 4;
765 ctx = RCreateContext(dpy, DefaultScreen(dpy), &attr);
767 if (argc < 2) {
768 argv[1] = ".";
769 argc = 2;
772 for (file_i = 1; file_i < argc; file_i++) {
773 current_link = connect_dir(argv[file_i], &list);
774 if (current_link) {
775 reading_filename = (char *)current_link->data;
776 max_index = list.count;
780 img = load_oriented_image(ctx, reading_filename, 0);
782 if (!img) {
783 fprintf(stderr, "Error: %s %s\n", reading_filename, RMessageForError(RErrorCode));
784 img = draw_failed_image();
785 if (!current_link)
786 return EXIT_FAILURE;
789 merge_with_background(img);
790 rescale_image();
792 if (DEBUG)
793 fprintf(stderr, "display size: %dx%d\n", max_width, max_height);
795 win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
796 img->width, img->height, 0, 0, BlackPixel(dpy, screen));
797 XSelectInput(dpy, win, KeyPressMask|StructureNotifyMask|ExposureMask|ButtonPressMask|FocusChangeMask);
799 size_hints = XAllocSizeHints();
800 if (!size_hints) {
801 fprintf(stderr, "Error: failure allocating memory\n");
802 return EXIT_FAILURE;
804 size_hints->width = img->width;
805 size_hints->height = img->height;
807 Atom delWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
808 XSetWMProtocols(dpy, win, &delWindow, 1);
809 change_title(&title_property, reading_filename);
811 win_hints = XAllocWMHints();
812 if (win_hints) {
813 win_hints->flags = StateHint|InputHint|WindowGroupHint;
815 #ifdef USE_XPM
816 if ((XpmCreatePixmapFromData(dpy, win, wmiv_xpm, &icon_pixmap, &icon_shape, NULL)) == 0) {
817 win_hints->flags |= IconPixmapHint|IconMaskHint|IconPositionHint;
818 win_hints->icon_pixmap = icon_pixmap;
819 win_hints->icon_mask = icon_shape;
820 win_hints->icon_x = 0;
821 win_hints->icon_y = 0;
823 #endif
824 win_hints->initial_state = NormalState;
825 win_hints->input = True;
826 win_hints->window_group = win;
827 XStringListToTextProperty((char **)&APPNAME, 1, &icon_property);
828 XSetWMProperties(dpy, win, NULL, &icon_property, argv, argc, size_hints, win_hints, class_hints);
829 if (icon_property.value)
830 XFree(icon_property.value);
831 XFree(win_hints);
832 XFree(class_hints);
833 XFree(size_hints);
836 XMapWindow(dpy, win);
837 XFlush(dpy);
838 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
840 while (!quit) {
841 XNextEvent(dpy, &e);
842 if (e.type == ClientMessage) {
843 if (e.xclient.data.l[0] == delWindow)
844 quit = 1;
847 * This break could be related to all ClientMessages or
848 * related to delWindow. Before the patch about this comment
849 * the break was indented with one tab more (at the same level
850 * than "quit = 1;" in the previous line.
852 break;
854 if (e.type == FocusIn) {
855 focus = True;
856 continue;
858 if (e.type == FocusOut) {
859 focus = False;
860 continue;
862 if (!fullscreen_flag && (e.type == Expose)) {
863 XExposeEvent xev = e.xexpose;
864 if (xev.count == 0)
865 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
866 continue;
868 if (!fullscreen_flag && e.type == ConfigureNotify) {
869 XConfigureEvent xce = e.xconfigure;
870 if (xce.width != img->width || xce.height != img->height) {
871 RImage *old_img = img;
872 img = load_oriented_image(ctx, current_link->data, 0);
873 if (!img) {
874 /* keep the old img and window size */
875 img = old_img;
876 XResizeWindow(dpy, win, img->width, img->height);
877 } else {
878 RImage *tmp2;
879 if (!back_from_fullscreen)
880 /* manually resized window */
881 tmp2 = RScaleImage(img, xce.width, xce.height);
882 else {
883 /* back from fullscreen mode, maybe img was rotated */
884 tmp2 = img;
885 back_from_fullscreen = False;
886 XClearWindow(dpy, win);
888 merge_with_background(tmp2);
889 if (RConvertImage(ctx, tmp2, &pix)) {
890 RReleaseImage(old_img);
891 img = RCloneImage(tmp2);
892 RReleaseImage(tmp2);
893 change_title(&title_property, (char *)current_link->data);
894 XSync(dpy, True);
895 XResizeWindow(dpy, win, img->width, img->height);
896 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
897 img->width, img->height, 0, 0);
902 continue;
904 if (fullscreen_flag && e.type == ConfigureNotify) {
905 maximize_image();
906 continue;
908 if (e.type == ButtonPress) {
909 switch (e.xbutton.button) {
910 case Button1: {
911 if (focus) {
912 if (img && (e.xbutton.x > img->width/2))
913 change_image(NEXT);
914 else
915 change_image(PREV);
918 break;
919 case Button4:
920 zoom_in();
921 break;
922 case Button5:
923 zoom_out();
924 break;
925 case 8:
926 change_image(PREV);
927 break;
928 case 9:
929 change_image(NEXT);
930 break;
932 continue;
934 if (e.type == KeyPress) {
935 keysym = XkbKeycodeToKeysym(dpy, e.xkey.keycode, 0, e.xkey.state & ShiftMask?1:0);
936 #ifdef HAVE_PTHREAD
937 if (keysym != XK_Right)
938 diaporama_flag = False;
939 #endif
940 switch (keysym) {
941 case XK_Right:
942 change_image(NEXT);
943 break;
944 case XK_Left:
945 change_image(PREV);
946 break;
947 case XK_Up:
948 if (current_link) {
949 current_link = list.last;
950 change_image(NEXT);
952 break;
953 case XK_Down:
954 if (current_link) {
955 current_link = list.first;
956 change_image(PREV);
958 break;
959 #ifdef HAVE_PTHREAD
960 case XK_F5:
961 case XK_d:
962 if (!tid) {
963 if (current_link && !diaporama_flag) {
964 diaporama_flag = True;
965 pthread_create(&tid, NULL, &diaporama, NULL);
966 } else {
967 fprintf(stderr, "Can't use diaporama mode\n");
970 break;
971 #endif
972 case XK_q:
973 quit = 1;
974 break;
975 case XK_Escape:
976 if (!fullscreen_flag) {
977 zoom_factor = -0.2f;
978 /* zoom_in will increase the zoom factor by 0.2 */
979 zoom_in();
980 } else {
981 /* we are in fullscreen mode already, want to return to normal size */
982 full_screen();
984 break;
985 case XK_plus:
986 zoom_in();
987 break;
988 case XK_minus:
989 zoom_out();
990 break;
991 case XK_F11:
992 case XK_f:
993 full_screen();
994 break;
995 case XK_r:
996 turn_image_right();
997 break;
998 case XK_l:
999 turn_image_left();
1000 break;
1006 if (img)
1007 RReleaseImage(img);
1008 if (pix)
1009 XFreePixmap(dpy, pix);
1010 #ifdef USE_XPM
1011 if (icon_pixmap)
1012 XFreePixmap(dpy, icon_pixmap);
1013 if (icon_shape)
1014 XFreePixmap(dpy, icon_shape);
1015 #endif
1016 linked_list_free(&list);
1017 RDestroyContext(ctx);
1018 RShutdown();
1019 XCloseDisplay(dpy);
1020 return EXIT_SUCCESS;