WPrefs: Hot corner preferences
[wmaker-crm.git] / util / wmiv.c
blob0d33a53539de740fcd526d4cd0975e1c460b6e47
1 /*
2 * Window Maker window manager
4 * Copyright (c) 2014-2023 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 NEXT = 0;
67 int PREV = 1;
68 float zoom_factor = 0;
69 int max_width = 0;
70 int max_height = 0;
72 Bool fullscreen_flag = False;
73 Bool focus = False;
74 Bool back_from_fullscreen = False;
76 #ifdef HAVE_PTHREAD
77 Bool diaporama_flag = False;
78 int diaporama_delay = 5;
79 pthread_t tid = 0;
80 #endif
81 XTextProperty title_property;
82 XTextProperty icon_property;
83 unsigned current_index = 1;
84 unsigned max_index = 1;
86 RColor lightGray;
87 RColor darkGray;
88 RColor black;
89 RColor red;
91 typedef struct link link_t;
92 struct link {
93 const void *data;
94 link_t *prev;
95 link_t *next;
98 typedef struct linked_list {
99 int count;
100 link_t *first;
101 link_t *last;
102 } linked_list_t;
104 linked_list_t list;
105 link_t *current_link;
109 load_oriented_image: used to load an image and optionally
110 get its orientation if libexif is available
111 return the image on success, NULL on failure
113 RImage *load_oriented_image(RContext *context, const char *file, int index)
115 RImage *image;
116 #ifdef HAVE_EXIF
117 int orientation = 0;
118 #endif
119 image = RLoadImage(context, file, index);
120 if (!image)
121 return NULL;
122 #ifdef HAVE_EXIF
123 ExifData *exifData = exif_data_new_from_file(file);
124 if (exifData) {
125 ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
126 ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
127 if (exifEntry)
128 orientation = exif_get_short(exifEntry->data, byteOrder);
130 exif_data_free(exifData);
134 0th Row 0th Column
135 1 top left side
136 2 top right side
137 3 bottom right side
138 4 bottom left side
139 5 left side top
140 6 right side top
141 7 right side bottom
142 8 left side bottom
145 if (image && (orientation > 1)) {
146 RImage *tmp = NULL;
147 switch (orientation) {
148 case 2:
149 tmp = RFlipImage(image, RHorizontalFlip);
150 break;
151 case 3:
152 tmp = RRotateImage(image, 180);
153 break;
154 case 4:
155 tmp = RFlipImage(image, RVerticalFlip);
156 break;
157 case 5: {
158 RImage *tmp2;
159 tmp2 = RFlipImage(image, RVerticalFlip);
160 if (tmp2) {
161 tmp = RRotateImage(tmp2, 90);
162 RReleaseImage(tmp2);
165 break;
166 case 6:
167 tmp = RRotateImage(image, 90);
168 break;
169 case 7: {
170 RImage *tmp2;
171 tmp2 = RFlipImage(image, RVerticalFlip);
172 if (tmp2) {
173 tmp = RRotateImage(tmp2, 270);
174 RReleaseImage(tmp2);
177 break;
178 case 8:
179 tmp = RRotateImage(image, 270);
180 break;
182 if (tmp) {
183 RReleaseImage(image);
184 image = tmp;
187 #endif
188 return image;
192 change_title: used to change window title
193 return EXIT_SUCCESS on success, 1 on failure
195 int change_title(XTextProperty *prop, char *filename)
197 char *combined_title = NULL;
198 if (!asprintf(&combined_title, "%s - %u/%u - %s", APPNAME, current_index, max_index, filename))
199 if (!asprintf(&combined_title, "%s - %u/%u", APPNAME, current_index, max_index))
200 return EXIT_FAILURE;
201 XStringListToTextProperty(&combined_title, 1, prop);
202 XSetWMName(dpy, win, prop);
203 if (prop->value)
204 XFree(prop->value);
205 free(combined_title);
206 return EXIT_SUCCESS;
210 rescale_image: used to rescale the current image based on the screen size
211 return EXIT_SUCCESS on success
213 int rescale_image(void)
215 long final_width = img->width;
216 long final_height = img->height;
218 /* check if there is already a zoom factor applied */
219 if (fabsf(zoom_factor) <= 0.0f) {
220 final_width = img->width + (int)(img->width * zoom_factor);
221 final_height = img->height + (int)(img->height * zoom_factor);
223 if ((max_width < final_width) || (max_height < final_height)) {
224 long val = 0;
225 if (final_width > final_height) {
226 val = final_height * max_width / final_width;
227 final_width = final_width * val / final_height;
228 final_height = val;
229 if (val > max_height) {
230 val = final_width * max_height / final_height;
231 final_height = final_height * val / final_width;
232 final_width = val;
234 } else {
235 val = final_width * max_height / final_height;
236 final_height = final_height * val / final_width;
237 final_width = val;
238 if (val > max_width) {
239 val = final_height * max_width / final_width;
240 final_width = final_width * val / final_height;
241 final_height = val;
245 if ((final_width != img->width) || (final_height != img->height)) {
246 RImage *old_img = img;
247 img = RScaleImage(img, final_width, final_height);
248 if (!img) {
249 img = old_img;
250 return EXIT_FAILURE;
252 RReleaseImage(old_img);
254 if (!RConvertImage(ctx, img, &pix)) {
255 fprintf(stderr, "%s\n", RMessageForError(RErrorCode));
256 return EXIT_FAILURE;
258 return EXIT_SUCCESS;
262 maximize_image: find the best image size for the current display
263 return EXIT_SUCCESS on success
265 int maximize_image(void)
267 rescale_image();
268 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
269 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
270 return EXIT_SUCCESS;
274 merge_with_background: merge the current image with with a checkboard background
275 return EXIT_SUCCESS on success, 1 on failure
277 int merge_with_background(RImage *i)
279 if (i) {
280 RImage *back;
281 back = RCreateImage(i->width, i->height, True);
282 if (back) {
283 int opaq = 255;
284 int x = 0, y = 0;
286 RFillImage(back, &lightGray);
287 for (x = 0; x <= i->width; x += 8) {
288 if (x/8 % 2)
289 y = 8;
290 else
291 y = 0;
292 for (; y <= i->height; y += 16)
293 ROperateRectangle(back, RAddOperation, x, y, x+8, y+8, &darkGray);
296 RCombineImagesWithOpaqueness(i, back, opaq);
297 RReleaseImage(back);
298 return EXIT_SUCCESS;
301 return EXIT_FAILURE;
305 turn_image: rotate the image by the angle passed
306 return EXIT_SUCCESS on success, EXIT_FAILURE on failure
308 int turn_image(float angle)
310 RImage *tmp;
312 if (!img)
313 return EXIT_FAILURE;
315 tmp = RRotateImage(img, angle);
316 if (!tmp)
317 return EXIT_FAILURE;
319 if (!fullscreen_flag) {
320 if (img->width != tmp->width || img->height != tmp->height)
321 XResizeWindow(dpy, win, tmp->width, tmp->height);
324 RReleaseImage(img);
325 img = tmp;
327 rescale_image();
328 if (!fullscreen_flag) {
329 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
330 } else {
331 XClearWindow(dpy, win);
332 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
333 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
336 return EXIT_SUCCESS;
340 turn_image_right: rotate the image by 90 degree
341 return EXIT_SUCCESS on success, EXIT_FAILURE on failure
343 int turn_image_right(void)
345 return turn_image(90.0);
349 turn_image_left: rotate the image by -90 degree
350 return EXIT_SUCCESS on success, 1 on failure
352 int turn_image_left(void)
354 return turn_image(-90.0);
358 draw_failed_image: create a red crossed image to indicate an error loading file
359 return the image on success, NULL on failure
362 RImage *draw_failed_image(void)
364 RImage *failed_image = NULL;
365 XWindowAttributes attr;
367 if (win && (XGetWindowAttributes(dpy, win, &attr) >= 0))
368 failed_image = RCreateImage(attr.width, attr.height, False);
369 else
370 failed_image = RCreateImage(50, 50, False);
371 if (!failed_image)
372 return NULL;
374 RFillImage(failed_image, &black);
375 ROperateLine(failed_image, RAddOperation, 0, 0, failed_image->width, failed_image->height, &red);
376 ROperateLine(failed_image, RAddOperation, 0, failed_image->height, failed_image->width, 0, &red);
378 return failed_image;
382 full_screen: sending event to the window manager to switch from/to full screen mode
383 return EXIT_SUCCESS on success, 1 on failure
385 int full_screen(void)
387 XEvent xev;
389 Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", True);
390 Atom fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", True);
391 long mask = SubstructureNotifyMask;
393 if (fullscreen_flag) {
394 fullscreen_flag = False;
395 zoom_factor = 0;
396 back_from_fullscreen = True;
397 } else {
398 fullscreen_flag = True;
399 zoom_factor = 1000;
402 memset(&xev, 0, sizeof(xev));
403 xev.type = ClientMessage;
404 xev.xclient.display = dpy;
405 xev.xclient.window = win;
406 xev.xclient.message_type = wm_state;
407 xev.xclient.format = 32;
408 xev.xclient.data.l[0] = fullscreen_flag;
409 xev.xclient.data.l[1] = fullscreen;
411 if (!XSendEvent(dpy, DefaultRootWindow(dpy), False, mask, &xev)) {
412 fprintf(stderr, "Error: sending fullscreen event to xserver\n");
413 return EXIT_FAILURE;
415 return EXIT_SUCCESS;
419 zoom_in_out: apply a zoom factor on the current image
420 arg: 1 to zoom in, 0 to zoom out
421 return EXIT_SUCCESS on success, 1 on failure
423 int zoom_in_out(int z)
425 RImage *old_img = img;
426 RImage *tmp = load_oriented_image(ctx, current_link->data, 0);
427 if (!tmp)
428 return EXIT_FAILURE;
430 if (z) {
431 zoom_factor += 0.2f;
432 img = RScaleImage(tmp, tmp->width + (int)(tmp->width * zoom_factor),
433 tmp->height + (int)(tmp->height * zoom_factor));
434 if (!img) {
435 img = old_img;
436 RReleaseImage(tmp);
437 return EXIT_FAILURE;
439 } else {
440 zoom_factor -= 0.2f;
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.2f;
445 RReleaseImage(tmp);
446 return EXIT_FAILURE;
448 img = RScaleImage(tmp, new_width, new_height);
449 if (!img) {
450 img = old_img;
451 RReleaseImage(tmp);
452 return EXIT_FAILURE;
455 RReleaseImage(old_img);
456 RReleaseImage(tmp);
457 XFreePixmap(dpy, pix);
459 merge_with_background(img);
460 if (!RConvertImage(ctx, img, &pix)) {
461 fprintf(stderr, "%s\n", RMessageForError(RErrorCode));
462 return EXIT_FAILURE;
464 XResizeWindow(dpy, win, img->width, img->height);
465 return EXIT_SUCCESS;
469 zoom_in: transitional fct used to call zoom_in_out with zoom in flag
470 return EXIT_SUCCESS on success, 1 on failure
472 int zoom_in(void)
474 return zoom_in_out(1);
478 zoom_out: transitional fct used to call zoom_in_out with zoom out flag
479 return EXIT_SUCCESS on success, 1 on failure
481 int zoom_out(void)
483 return zoom_in_out(0);
487 change_image: load previous or next image
488 arg: way which could be PREV or NEXT constant
489 return EXIT_SUCCESS on success, 1 on failure
491 int change_image(int way)
493 if (img && current_link) {
494 int old_img_width = img->width;
495 int old_img_height = img->height;
497 RReleaseImage(img);
499 if (way == NEXT) {
500 current_link = current_link->next;
501 current_index++;
502 } else {
503 current_link = current_link->prev;
504 current_index--;
506 if (current_link == NULL) {
507 if (way == NEXT) {
508 current_link = list.first;
509 current_index = 1;
510 } else {
511 current_link = list.last;
512 current_index = max_index;
515 if (WMIV_DEBUG)
516 fprintf(stderr, "current file is> %s\n", (char *)current_link->data);
517 img = load_oriented_image(ctx, current_link->data, 0);
519 if (!img) {
520 fprintf(stderr, "Error: %s %s\n", (char *)current_link->data,
521 RMessageForError(RErrorCode));
522 img = draw_failed_image();
523 } else {
524 merge_with_background(img);
526 rescale_image();
527 if (!fullscreen_flag) {
528 if ((old_img_width != img->width) || (old_img_height != img->height))
529 XResizeWindow(dpy, win, img->width, img->height);
530 else
531 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
532 change_title(&title_property, (char *)current_link->data);
533 } else {
534 XClearWindow(dpy, win);
535 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
536 img->width, img->height, max_width/2-img->width/2, max_height/2-img->height/2);
538 return EXIT_SUCCESS;
540 return EXIT_FAILURE;
543 #ifdef HAVE_PTHREAD
545 diaporama: send a xevent to display the next image at every delay set to diaporama_delay
546 arg: not used
547 return void
549 void *diaporama(void *arg)
551 (void) arg;
553 XKeyEvent event;
554 event.display = dpy;
555 event.window = win;
556 event.root = DefaultRootWindow(dpy);
557 event.subwindow = None;
558 event.time = CurrentTime;
559 event.x = 1;
560 event.y = 1;
561 event.x_root = 1;
562 event.y_root = 1;
563 event.same_screen = True;
564 event.keycode = XKeysymToKeycode(dpy, XK_Right);
565 event.state = 0;
566 event.type = KeyPress;
568 while (diaporama_flag) {
569 int r;
570 r = XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
571 if (!r)
572 fprintf(stderr, "Error sending event\n");
573 XFlush(dpy);
574 /* default sleep time between moving to next image */
575 sleep(diaporama_delay);
577 tid = 0;
578 return arg;
580 #endif
583 linked_list_init: init the linked list
585 void linked_list_init(linked_list_t *list)
587 list->first = list->last = 0;
588 list->count = 0;
592 linked_list_add: add an element to the linked list
593 return EXIT_SUCCESS on success, 1 otherwise
595 int linked_list_add(linked_list_t *list, const void *data)
597 link_t *link;
599 /* calloc sets the "next" field to zero. */
600 link = calloc(1, sizeof(link_t));
601 if (!link) {
602 fprintf(stderr, "Error: memory allocation failed\n");
603 return EXIT_FAILURE;
605 link->data = data;
606 if (list->last) {
607 /* Join the two final links together. */
608 list->last->next = link;
609 link->prev = list->last;
610 list->last = link;
611 } else {
612 list->first = link;
613 list->last = link;
615 list->count++;
616 return EXIT_SUCCESS;
620 linked_list_free: deallocate the whole linked list
622 void linked_list_free(linked_list_t *list)
624 link_t *link;
625 link_t *next;
626 for (link = list->first; link; link = next) {
627 /* Store the next value so that we don't access freed memory. */
628 next = link->next;
629 if (link->data)
630 free((char *)link->data);
631 free(link);
636 connect_dir: list and sort by name all files from a given directory
637 arg: the directory path that contains images, the linked list where to add the new file refs
638 return: the first argument of the list or NULL on failure
640 link_t *connect_dir(char *dirpath, linked_list_t *li)
642 struct dirent **dir;
643 int dv, idx;
644 char path[PATH_MAX] = "";
646 if (!dirpath)
647 return NULL;
649 dv = scandir(dirpath, &dir, 0, alphasort);
650 if (dv < 0) {
651 /* maybe it's a file */
652 struct stat stDirInfo;
653 if (lstat(dirpath, &stDirInfo) == 0) {
654 linked_list_add(li, strdup(dirpath));
655 return li->first;
656 } else {
657 return NULL;
660 for (idx = 0; idx < dv; idx++) {
661 struct stat stDirInfo;
662 if (dirpath[strlen(dirpath)-1] == FILE_SEPARATOR)
663 snprintf(path, PATH_MAX, "%s%s", dirpath, dir[idx]->d_name);
664 else
665 snprintf(path, PATH_MAX, "%s%c%s", dirpath, FILE_SEPARATOR, dir[idx]->d_name);
667 free(dir[idx]);
668 if ((lstat(path, &stDirInfo) == 0) && !S_ISDIR(stDirInfo.st_mode))
669 linked_list_add(li, strdup(path));
671 free(dir);
672 return li->first;
676 main
678 int main(int argc, char **argv)
680 int option = -1;
681 RContextAttributes attr = {};
682 XEvent e;
683 KeySym keysym;
684 char *reading_filename = "";
685 int screen, file_i;
686 int quit = 0;
687 XClassHint *class_hints;
688 XSizeHints *size_hints;
689 XWMHints *win_hints;
690 #ifdef USE_XPM
691 Pixmap icon_pixmap, icon_shape;
692 #endif
693 class_hints = XAllocClassHint();
694 if (!class_hints) {
695 fprintf(stderr, "Error: failure allocating memory\n");
696 return EXIT_FAILURE;
698 class_hints->res_name = (char *)APPNAME;
699 class_hints->res_class = "default";
701 /* init colors */
702 lightGray.red = lightGray.green = lightGray.blue = 211;
703 darkGray.red = darkGray.green = darkGray.blue = 169;
704 lightGray.alpha = darkGray.alpha = 1;
705 black.red = black.green = black.blue = 0;
706 red.red = 255;
707 red.green = red.blue = 0;
709 static struct option long_options[] = {
710 {"version", no_argument, 0, 'v'},
711 {"help", no_argument, 0, 'h'},
712 {0, 0, 0, 0}
714 int option_index = 0;
716 option = getopt_long (argc, argv, "hv", long_options, &option_index);
717 if (option != -1) {
718 switch (option) {
719 case 'h':
720 printf("Usage: %s [image(s)|directory]\n"
721 "Options:\n"
722 " -h, --help print this help text\n"
723 " -v, --version print version\n"
724 "Keys:\n"
725 " [+] zoom in\n"
726 " [-] zoom out\n"
727 " [Esc] actual size\n"
728 #ifdef HAVE_PTHREAD
729 " [D] launch diaporama mode\n"
730 #endif
731 " [L] rotate image on the left\n"
732 " [Q] quit\n"
733 " [R] rotate image on the right\n"
734 " [â–¸] next image\n"
735 " [â—‚] previous image\n"
736 " [â–´] first image\n"
737 " [â–¾] last image\n",
738 argv[0]);
739 return EXIT_SUCCESS;
740 case 'v':
741 printf("%s version %s\n", APPNAME, VERSION);
742 return EXIT_SUCCESS;
743 case '?':
744 return EXIT_FAILURE;
748 linked_list_init(&list);
750 dpy = XOpenDisplay(NULL);
751 if (!dpy) {
752 fprintf(stderr, "Error: can't open display\n");
753 linked_list_free(&list);
754 return EXIT_FAILURE;
757 screen = DefaultScreen(dpy);
758 max_width = DisplayWidth(dpy, screen);
759 max_height = DisplayHeight(dpy, screen);
761 attr.flags = RC_RenderMode | RC_ColorsPerChannel;
762 attr.render_mode = RDitheredRendering;
763 attr.colors_per_channel = 4;
764 ctx = RCreateContext(dpy, DefaultScreen(dpy), &attr);
766 if (argc < 2) {
767 argv[1] = ".";
768 argc = 2;
771 for (file_i = 1; file_i < argc; file_i++) {
772 current_link = connect_dir(argv[file_i], &list);
773 if (current_link) {
774 reading_filename = (char *)current_link->data;
775 max_index = list.count;
779 img = load_oriented_image(ctx, reading_filename, 0);
781 if (!img) {
782 fprintf(stderr, "Error: %s %s\n", reading_filename, RMessageForError(RErrorCode));
783 img = draw_failed_image();
784 if (!current_link)
785 return EXIT_FAILURE;
788 merge_with_background(img);
789 rescale_image();
791 if (WMIV_DEBUG)
792 fprintf(stderr, "display size: %dx%d\n", max_width, max_height);
794 win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
795 img->width, img->height, 0, 0, BlackPixel(dpy, screen));
796 XSelectInput(dpy, win, KeyPressMask|StructureNotifyMask|ExposureMask|ButtonPressMask|FocusChangeMask);
798 size_hints = XAllocSizeHints();
799 if (!size_hints) {
800 fprintf(stderr, "Error: failure allocating memory\n");
801 return EXIT_FAILURE;
803 size_hints->width = img->width;
804 size_hints->height = img->height;
806 Atom delWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
807 XSetWMProtocols(dpy, win, &delWindow, 1);
808 change_title(&title_property, reading_filename);
810 win_hints = XAllocWMHints();
811 if (win_hints) {
812 win_hints->flags = StateHint|InputHint|WindowGroupHint;
814 #ifdef USE_XPM
815 if ((XpmCreatePixmapFromData(dpy, win, wmiv_xpm, &icon_pixmap, &icon_shape, NULL)) == 0) {
816 win_hints->flags |= IconPixmapHint|IconMaskHint|IconPositionHint;
817 win_hints->icon_pixmap = icon_pixmap;
818 win_hints->icon_mask = icon_shape;
819 win_hints->icon_x = 0;
820 win_hints->icon_y = 0;
822 #endif
823 win_hints->initial_state = NormalState;
824 win_hints->input = True;
825 win_hints->window_group = win;
826 XStringListToTextProperty((char **)&APPNAME, 1, &icon_property);
827 XSetWMProperties(dpy, win, NULL, &icon_property, argv, argc, size_hints, win_hints, class_hints);
828 if (icon_property.value)
829 XFree(icon_property.value);
830 XFree(win_hints);
831 XFree(class_hints);
832 XFree(size_hints);
835 XMapWindow(dpy, win);
836 XFlush(dpy);
837 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
839 while (!quit) {
840 XNextEvent(dpy, &e);
841 if (e.type == ClientMessage) {
842 if (e.xclient.data.l[0] == delWindow)
843 quit = 1;
846 * This break could be related to all ClientMessages or
847 * related to delWindow. Before the patch about this comment
848 * the break was indented with one tab more (at the same level
849 * than "quit = 1;" in the previous line.
851 break;
853 if (e.type == FocusIn) {
854 focus = True;
855 continue;
857 if (e.type == FocusOut) {
858 focus = False;
859 continue;
861 if (!fullscreen_flag && (e.type == Expose)) {
862 XExposeEvent xev = e.xexpose;
863 if (xev.count == 0)
864 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0, img->width, img->height, 0, 0);
865 continue;
867 if (!fullscreen_flag && e.type == ConfigureNotify) {
868 XConfigureEvent xce = e.xconfigure;
869 if (xce.width != img->width || xce.height != img->height) {
870 RImage *old_img = img;
871 img = load_oriented_image(ctx, current_link->data, 0);
872 if (!img) {
873 /* keep the old img and window size */
874 img = old_img;
875 XResizeWindow(dpy, win, img->width, img->height);
876 } else {
877 RImage *tmp2;
878 if (!back_from_fullscreen)
879 /* manually resized window */
880 tmp2 = RScaleImage(img, xce.width, xce.height);
881 else {
882 /* back from fullscreen mode, maybe img was rotated */
883 tmp2 = img;
884 back_from_fullscreen = False;
885 XClearWindow(dpy, win);
887 merge_with_background(tmp2);
888 if (RConvertImage(ctx, tmp2, &pix)) {
889 RReleaseImage(old_img);
890 img = RCloneImage(tmp2);
891 RReleaseImage(tmp2);
892 change_title(&title_property, (char *)current_link->data);
893 XSync(dpy, True);
894 XResizeWindow(dpy, win, img->width, img->height);
895 XCopyArea(dpy, pix, win, ctx->copy_gc, 0, 0,
896 img->width, img->height, 0, 0);
901 continue;
903 if (fullscreen_flag && e.type == ConfigureNotify) {
904 maximize_image();
905 continue;
907 if (e.type == ButtonPress) {
908 switch (e.xbutton.button) {
909 case Button1: {
910 if (focus) {
911 if (img && (e.xbutton.x > img->width/2))
912 change_image(NEXT);
913 else
914 change_image(PREV);
917 break;
918 case Button4:
919 zoom_in();
920 break;
921 case Button5:
922 zoom_out();
923 break;
924 case 8:
925 change_image(PREV);
926 break;
927 case 9:
928 change_image(NEXT);
929 break;
931 continue;
933 if (e.type == KeyPress) {
934 keysym = W_KeycodeToKeysym(dpy, e.xkey.keycode, e.xkey.state & ShiftMask?1:0);
935 #ifdef HAVE_PTHREAD
936 if (keysym != XK_Right)
937 diaporama_flag = False;
938 #endif
939 switch (keysym) {
940 case XK_Right:
941 change_image(NEXT);
942 break;
943 case XK_Left:
944 change_image(PREV);
945 break;
946 case XK_Up:
947 if (current_link) {
948 current_link = list.last;
949 change_image(NEXT);
951 break;
952 case XK_Down:
953 if (current_link) {
954 current_link = list.first;
955 change_image(PREV);
957 break;
958 #ifdef HAVE_PTHREAD
959 case XK_F5:
960 case XK_d:
961 if (!tid) {
962 if (current_link && !diaporama_flag) {
963 diaporama_flag = True;
964 pthread_create(&tid, NULL, &diaporama, NULL);
965 } else {
966 fprintf(stderr, "Can't use diaporama mode\n");
969 break;
970 #endif
971 case XK_q:
972 quit = 1;
973 break;
974 case XK_Escape:
975 if (!fullscreen_flag) {
976 zoom_factor = -0.2f;
977 /* zoom_in will increase the zoom factor by 0.2 */
978 zoom_in();
979 } else {
980 /* we are in fullscreen mode already, want to return to normal size */
981 full_screen();
983 break;
984 case XK_plus:
985 zoom_in();
986 break;
987 case XK_minus:
988 zoom_out();
989 break;
990 case XK_F11:
991 case XK_f:
992 full_screen();
993 break;
994 case XK_r:
995 turn_image_right();
996 break;
997 case XK_l:
998 turn_image_left();
999 break;
1005 if (img)
1006 RReleaseImage(img);
1007 if (pix)
1008 XFreePixmap(dpy, pix);
1009 #ifdef USE_XPM
1010 if (icon_pixmap)
1011 XFreePixmap(dpy, icon_pixmap);
1012 if (icon_shape)
1013 XFreePixmap(dpy, icon_shape);
1014 #endif
1015 linked_list_free(&list);
1016 RDestroyContext(ctx);
1017 RShutdown();
1018 XCloseDisplay(dpy);
1019 return EXIT_SUCCESS;