"Fossies" - the Fresh Open Source Software Archive

Member "feh-3.4.1/src/winwidget.c" (29 May 2020, 32984 Bytes) of package /linux/privat/feh-3.4.1.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "winwidget.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.4_vs_3.4.1.

    1 /* winwidget.c
    2 
    3 Copyright (C) 1999-2003 Tom Gilbert.
    4 Copyright (C) 2010-2020 Daniel Friesel.
    5 
    6 Permission is hereby granted, free of charge, to any person obtaining a copy
    7 of this software and associated documentation files (the "Software"), to
    8 deal in the Software without restriction, including without limitation the
    9 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   10 sell copies of the Software, and to permit persons to whom the Software is
   11 furnished to do so, subject to the following conditions:
   12 
   13 The above copyright notice and this permission notice shall be included in
   14 all copies of the Software and its documentation and acknowledgment shall be
   15 given in the documentation and software packages that this Software was
   16 used.
   17 
   18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   21 THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   22 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   24 
   25 */
   26 
   27 #include "feh.h"
   28 #include "filelist.h"
   29 #include "winwidget.h"
   30 #include "options.h"
   31 #include "events.h"
   32 
   33 #ifdef HAVE_INOTIFY
   34 #include <sys/inotify.h>
   35 #endif
   36 
   37 static void winwidget_unregister(winwidget win);
   38 static void winwidget_register(winwidget win);
   39 static winwidget winwidget_allocate(void);
   40 
   41 
   42 int window_num = 0;     /* For window list */
   43 winwidget *windows = NULL;  /* List of windows to loop though */
   44 
   45 static winwidget winwidget_allocate(void)
   46 {
   47     winwidget ret = NULL;
   48 
   49     ret = emalloc(sizeof(_winwidget));
   50     memset(ret, 0, sizeof(_winwidget));
   51 
   52     ret->win = 0;
   53     ret->w = 0;
   54     ret->h = 0;
   55     ret->full_screen = 0;
   56     ret->im_w = 0;
   57     ret->im_h = 0;
   58     ret->im_angle = 0;
   59     ret->bg_pmap = 0;
   60     ret->bg_pmap_cache = 0;
   61     ret->im = NULL;
   62     ret->name = NULL;
   63     ret->file = NULL;
   64     ret->errstr = NULL;
   65     ret->type = WIN_TYPE_UNSET;
   66     ret->visible = 0;
   67     ret->caption_entry = 0;
   68     ret->force_aliasing = opt.force_aliasing;
   69 
   70     /* Zoom stuff */
   71     ret->mode = MODE_NORMAL;
   72 
   73     ret->gc = None;
   74 
   75     /* New stuff */
   76     ret->im_x = 0;
   77     ret->im_y = 0;
   78     ret->zoom = 1.0;
   79     ret->old_zoom = 1.0;
   80 
   81     ret->click_offset_x = 0;
   82     ret->click_offset_y = 0;
   83     ret->has_rotated = 0;
   84 
   85 #ifdef HAVE_INOTIFY
   86     ret->inotify_wd = -1;
   87 #endif
   88 
   89     return(ret);
   90 }
   91 
   92 winwidget winwidget_create_from_image(Imlib_Image im, char type)
   93 {
   94     winwidget ret = NULL;
   95 
   96     if (im == NULL)
   97         return(NULL);
   98 
   99     ret = winwidget_allocate();
  100     ret->type = type;
  101 
  102     ret->im = im;
  103     ret->w = ret->im_w = gib_imlib_image_get_width(ret->im);
  104     ret->h = ret->im_h = gib_imlib_image_get_height(ret->im);
  105 
  106     if (opt.full_screen && (type != WIN_TYPE_THUMBNAIL))
  107         ret->full_screen = True;
  108     winwidget_create_window(ret, ret->w, ret->h);
  109     winwidget_render_image(ret, 1, 0);
  110 
  111     return(ret);
  112 }
  113 
  114 winwidget winwidget_create_from_file(gib_list * list, char type)
  115 {
  116     winwidget ret = NULL;
  117     feh_file *file = FEH_FILE(list->data);
  118 
  119     if (!file || !file->filename)
  120         return(NULL);
  121 
  122     ret = winwidget_allocate();
  123     ret->file = list;
  124     ret->type = type;
  125 
  126     if ((winwidget_loadimage(ret, file) == 0) || feh_should_ignore_image(ret->im)) {
  127         winwidget_destroy(ret);
  128         return(NULL);
  129     }
  130 
  131     if (!ret->win) {
  132         ret->w = ret->im_w = gib_imlib_image_get_width(ret->im);
  133         ret->h = ret->im_h = gib_imlib_image_get_height(ret->im);
  134         D(("image is %dx%d pixels, format %s\n", ret->w, ret->h, gib_imlib_image_format(ret->im)));
  135         if (opt.full_screen)
  136             ret->full_screen = True;
  137         winwidget_create_window(ret, ret->w, ret->h);
  138         winwidget_render_image(ret, 1, 0);
  139     }
  140 
  141     return(ret);
  142 }
  143 
  144 void winwidget_create_window(winwidget ret, int w, int h)
  145 {
  146     XSetWindowAttributes attr;
  147     XEvent ev;
  148     XClassHint *xch;
  149     MWMHints mwmhints;
  150     Atom prop = None;
  151     pid_t pid;
  152     int x = 0;
  153     int y = 0;
  154     char *tmpname;
  155 #ifdef HOST_NAME_MAX
  156     char hostname[HOST_NAME_MAX];
  157 #else /* ! HOST_NAME_MAX */
  158     long host_name_max;
  159     char *hostname;
  160 #endif /* HOST_NAME_MAX */
  161 
  162     D(("winwidget_create_window %dx%d\n", w, h));
  163 
  164     if (ret->full_screen) {
  165         w = scr->width;
  166         h = scr->height;
  167 
  168 #ifdef HAVE_LIBXINERAMA
  169         if (opt.xinerama && xinerama_screens) {
  170             w = xinerama_screens[xinerama_screen].width;
  171             h = xinerama_screens[xinerama_screen].height;
  172             x = xinerama_screens[xinerama_screen].x_org;
  173             y = xinerama_screens[xinerama_screen].y_org;
  174         }
  175 #endif              /* HAVE_LIBXINERAMA */
  176     } else if (opt.geom_flags) {
  177         if (opt.geom_flags & WidthValue) {
  178             w = opt.geom_w;
  179         }
  180         if (opt.geom_flags & HeightValue) {
  181             h = opt.geom_h;
  182         }
  183         if (opt.geom_flags & XValue) {
  184             if (opt.geom_flags & XNegative) {
  185                 x = scr->width - opt.geom_x;
  186             } else {
  187                 x = opt.geom_x;
  188             }
  189         }
  190         if (opt.geom_flags & YValue) {
  191             if (opt.geom_flags & YNegative) {
  192                 y = scr->height - opt.geom_y;
  193             } else {
  194                 y = opt.geom_y;
  195             }
  196         }
  197     } else if (opt.screen_clip) {
  198         if (w > scr->width)
  199             w = scr->width;
  200         if (h > scr->height)
  201             h = scr->height;
  202 
  203 #ifdef HAVE_LIBXINERAMA
  204         if (opt.xinerama && xinerama_screens) {
  205             if (w > xinerama_screens[xinerama_screen].width)
  206                 w = xinerama_screens[xinerama_screen].width;
  207             if (h > xinerama_screens[xinerama_screen].height)
  208                 h = xinerama_screens[xinerama_screen].height;
  209         }
  210 #endif              /* HAVE_LIBXINERAMA */
  211     }
  212 
  213     if (opt.paused) {
  214         tmpname = estrjoin(" ", ret->name, "[Paused]", NULL);
  215         free(ret->name);
  216         ret->name = tmpname;
  217     }
  218 
  219     ret->x = x;
  220     ret->y = y;
  221     ret->w = w;
  222     ret->h = h;
  223     ret->visible = False;
  224 
  225     attr.backing_store = NotUseful;
  226     attr.override_redirect = False;
  227     attr.colormap = cm;
  228     attr.border_pixel = 0;
  229     attr.background_pixel = 0;
  230     attr.save_under = False;
  231     attr.event_mask =
  232         StructureNotifyMask | ButtonPressMask | ButtonReleaseMask |
  233         PointerMotionMask | EnterWindowMask | LeaveWindowMask |
  234         KeyPressMask | KeyReleaseMask | ButtonMotionMask | ExposureMask
  235         | FocusChangeMask | PropertyChangeMask | VisibilityChangeMask;
  236 
  237     memset(&mwmhints, 0, sizeof(mwmhints));
  238     if (opt.borderless || ret->full_screen) {
  239         prop = XInternAtom(disp, "_MOTIF_WM_HINTS", True);
  240         if (prop == None) {
  241             weprintf
  242                 ("Window Manager does not support MWM hints. "
  243                  "To get a borderless window I have to bypass your wm.");
  244             attr.override_redirect = True;
  245             mwmhints.flags = 0;
  246         } else {
  247             mwmhints.flags = MWM_HINTS_DECORATIONS;
  248             mwmhints.decorations = 0;
  249         }
  250     }
  251 
  252     ret->win =
  253         XCreateWindow(disp, DefaultRootWindow(disp), x, y, w, h, 0,
  254               depth, InputOutput, vis,
  255               CWOverrideRedirect | CWSaveUnder | CWBackingStore
  256               | CWColormap | CWBackPixel | CWBorderPixel | CWEventMask, &attr);
  257 
  258     if (mwmhints.flags) {
  259         XChangeProperty(disp, ret->win, prop, prop, 32,
  260                 PropModeReplace, (unsigned char *) &mwmhints, PROP_MWM_HINTS_ELEMENTS);
  261     }
  262     if (ret->full_screen) {
  263         Atom prop_fs = XInternAtom(disp, "_NET_WM_STATE_FULLSCREEN", False);
  264         Atom prop_state = XInternAtom(disp, "_NET_WM_STATE", False);
  265 
  266         memset(&ev, 0, sizeof(ev));
  267         ev.xclient.type = ClientMessage;
  268         ev.xclient.message_type = prop_state;
  269         ev.xclient.display = disp;
  270         ev.xclient.window = ret->win;
  271         ev.xclient.format = 32;
  272         ev.xclient.data.l[0] = 1;
  273         ev.xclient.data.l[1] = prop_fs;
  274 
  275         XChangeProperty(disp, ret->win, prop_state, XA_ATOM, 32,
  276                 PropModeReplace, (unsigned char *) &prop_fs, 1);
  277     }
  278 
  279     pid = getpid();
  280     prop = XInternAtom(disp, "_NET_WM_PID", False);
  281     XChangeProperty(disp, ret->win, prop, XA_CARDINAL, sizeof(pid_t) * 8,
  282             PropModeReplace, (const unsigned char *)&pid, 1);
  283 
  284 #ifdef HOST_NAME_MAX
  285     if (gethostname(hostname, HOST_NAME_MAX) == 0) {
  286         hostname[HOST_NAME_MAX-1] = '\0';
  287         prop = XInternAtom(disp, "WM_CLIENT_MACHINE", False);
  288         XChangeProperty(disp, ret->win, prop, XA_STRING, sizeof(char) * 8,
  289                 PropModeReplace, (unsigned char *)hostname, strlen(hostname));
  290     }
  291 #else /* ! HOST_NAME_MAX */
  292     if ((host_name_max = sysconf(_SC_HOST_NAME_MAX)) != -1 ) {
  293         if ((hostname = calloc(1, host_name_max + 1)) != NULL ) {
  294             if (gethostname(hostname, host_name_max) == 0) {
  295                 prop = XInternAtom(disp, "WM_CLIENT_MACHINE", False);
  296                 XChangeProperty(disp, ret->win, prop, XA_STRING, sizeof(char) * 8,
  297                         PropModeReplace, (unsigned char *)hostname, strlen(hostname));
  298             }
  299             free(hostname);
  300         }
  301     }
  302 #endif /* HOST_NAME_MAX */
  303 
  304     XSetWMProtocols(disp, ret->win, &wmDeleteWindow, 1);
  305     winwidget_update_title(ret);
  306     xch = XAllocClassHint();
  307     xch->res_name = "feh";
  308     xch->res_class = opt.x11_class ? opt.x11_class : "feh";
  309     XSetClassHint(disp, ret->win, xch);
  310     XFree(xch);
  311 
  312     /* Size hints */
  313     if (ret->full_screen || opt.geom_flags) {
  314         XSizeHints xsz;
  315 
  316         xsz.flags = USPosition;
  317         xsz.x = x;
  318         xsz.y = y;
  319         XSetWMNormalHints(disp, ret->win, &xsz);
  320         XMoveWindow(disp, ret->win, x, y);
  321     }
  322     if (opt.hide_pointer)
  323         winwidget_set_pointer(ret, 0);
  324 
  325     /* set the icon name property */
  326     XSetIconName(disp, ret->win, "feh");
  327     /* set the command hint */
  328     XSetCommand(disp, ret->win, cmdargv, cmdargc);
  329 
  330     winwidget_register(ret);
  331 
  332     /* do not scale down a thumbnail list window, only those created from it */
  333     if (opt.geom_enabled && (ret->type != WIN_TYPE_THUMBNAIL)) {
  334         opt.geom_w = w;
  335         opt.geom_h = h;
  336         opt.geom_flags |= WidthValue | HeightValue;
  337     }
  338 
  339     return;
  340 }
  341 
  342 void winwidget_update_title(winwidget ret)
  343 {
  344     char *name;
  345     Atom prop_name = XInternAtom(disp, "_NET_WM_NAME", False);
  346     Atom prop_icon = XInternAtom(disp, "_NET_WM_ICON_NAME", False);
  347     Atom prop_utf8 = XInternAtom(disp, "UTF8_STRING", False);
  348 
  349     D(("winwid->name = %s\n", ret->name));
  350     name = ret->name ? ret->name : "feh";
  351     XStoreName(disp, ret->win, name);
  352     XSetIconName(disp, ret->win, name);
  353 
  354     XChangeProperty(disp, ret->win, prop_name, prop_utf8, 8,
  355             PropModeReplace, (const unsigned char *)name, strlen(name));
  356 
  357     XChangeProperty(disp, ret->win, prop_icon, prop_utf8, 8,
  358             PropModeReplace, (const unsigned char *)name, strlen(name));
  359 
  360     return;
  361 }
  362 
  363 void winwidget_update_caption(winwidget winwid)
  364 {
  365     if (opt.caption_path) {
  366         /* TODO: Does someone understand the caching here. Is this the right
  367          * approach now that I have broken this out into a separate function. -lsmith */
  368 
  369         /* cache bg pixmap. during caption entry, multiple redraws are done
  370          * because the caption overlay changes - the image doesn't though, so re-
  371          * rendering that is a waste of time */
  372         if (winwid->caption_entry) {
  373             GC gc;
  374             if (winwid->bg_pmap_cache)
  375                 XFreePixmap(disp, winwid->bg_pmap_cache);
  376             winwid->bg_pmap_cache = XCreatePixmap(disp, winwid->win, winwid->w, winwid->h, depth);
  377             gc = XCreateGC(disp, winwid->win, 0, NULL);
  378             XCopyArea(disp, winwid->bg_pmap, winwid->bg_pmap_cache, gc, 0, 0, winwid->w, winwid->h, 0, 0);
  379             XFreeGC(disp, gc);
  380         }
  381         feh_draw_caption(winwid);
  382     }
  383     return;
  384 }
  385 
  386 void winwidget_setup_pixmaps(winwidget winwid)
  387 {
  388     if (winwid->full_screen) {
  389         if (!(winwid->bg_pmap)) {
  390             if (winwid->gc == None) {
  391                 XGCValues gcval;
  392 
  393                 if (!opt.image_bg || !strcmp(opt.image_bg, "default")) {
  394                     gcval.foreground = BlackPixel(disp, DefaultScreen(disp));
  395                     winwid->gc = XCreateGC(disp, winwid->win, GCForeground, &gcval);
  396                 } else if (!strcmp(opt.image_bg, "checks")) {
  397                     gcval.tile = feh_create_checks();
  398                     gcval.fill_style = FillTiled;
  399                     winwid->gc = XCreateGC(disp, winwid->win, GCTile | GCFillStyle, &gcval);
  400                 } else {
  401                     XColor color;
  402                     Colormap cmap = DefaultColormap(disp, DefaultScreen(disp));
  403                     XAllocNamedColor(disp, cmap, (char*) opt.image_bg, &color, &color);
  404                     gcval.foreground = color.pixel;
  405                     winwid->gc = XCreateGC(disp, winwid->win, GCForeground, &gcval);
  406                 }
  407             }
  408             winwid->bg_pmap = XCreatePixmap(disp, winwid->win, scr->width, scr->height, depth);
  409         }
  410         XFillRectangle(disp, winwid->bg_pmap, winwid->gc, 0, 0, scr->width, scr->height);
  411     } else {
  412         if (!winwid->bg_pmap || winwid->had_resize) {
  413             D(("recreating background pixmap (%dx%d)\n", winwid->w, winwid->h));
  414             if (winwid->bg_pmap)
  415                 XFreePixmap(disp, winwid->bg_pmap);
  416 
  417             if (winwid->w == 0)
  418                 winwid->w = 1;
  419             if (winwid->h == 0)
  420                 winwid->h = 1;
  421             winwid->bg_pmap = XCreatePixmap(disp, winwid->win, winwid->w, winwid->h, depth);
  422             winwid->had_resize = 0;
  423         }
  424     }
  425     return;
  426 }
  427 
  428 void winwidget_render_image(winwidget winwid, int resize, int force_alias)
  429 {
  430     int sx, sy, sw, sh, dx, dy, dw, dh;
  431     int calc_w, calc_h;
  432     int antialias = 0;
  433 
  434     if (!winwid->full_screen && resize) {
  435         winwidget_resize(winwid, winwid->im_w, winwid->im_h, 0);
  436         winwidget_reset_image(winwid);
  437     }
  438 
  439     D(("winwidget_render_image resize %d force_alias %d im %dx%d\n",
  440           resize, force_alias, winwid->im_w, winwid->im_h));
  441 
  442     /* winwidget_setup_pixmaps(winwid) resets the winwid->had_resize flag */
  443     int had_resize = winwid->had_resize || resize;
  444 
  445     winwidget_setup_pixmaps(winwid);
  446 
  447     if (had_resize && !opt.keep_zoom_vp && (winwid->type != WIN_TYPE_THUMBNAIL)) {
  448         double required_zoom = 1.0;
  449         feh_calc_needed_zoom(&required_zoom, winwid->im_w, winwid->im_h, winwid->w, winwid->h);
  450 
  451         winwid->zoom = opt.default_zoom ? (0.01 * opt.default_zoom) : 1.0;
  452 
  453         if ((opt.scale_down || (winwid->full_screen && !opt.default_zoom))
  454                 && winwid->zoom > required_zoom)
  455             winwid->zoom = required_zoom;
  456         else if ((opt.zoom_mode && required_zoom > 1)
  457                 && (!opt.default_zoom || required_zoom < winwid->zoom))
  458             winwid->zoom = required_zoom;
  459 
  460         if (opt.offset_flags & XValue) {
  461             if (opt.offset_flags & XNegative) {
  462                 winwid->im_x = winwid->w - (winwid->im_w * winwid->zoom) - opt.offset_x;
  463             } else {
  464                 winwid->im_x = - opt.offset_x * winwid->zoom;
  465             }
  466         } else {
  467             winwid->im_x = (int) (winwid->w - (winwid->im_w * winwid->zoom)) >> 1;
  468         }
  469         if (opt.offset_flags & YValue) {
  470             if (opt.offset_flags & YNegative) {
  471                 winwid->im_y = winwid->h - (winwid->im_h * winwid->zoom) - opt.offset_y;
  472             } else {
  473                 winwid->im_y = - opt.offset_y * winwid->zoom;
  474             }
  475         } else {
  476             winwid->im_y = (int) (winwid->h - (winwid->im_h * winwid->zoom)) >> 1;
  477         }
  478     }
  479 
  480     winwid->had_resize = 0;
  481 
  482     if (opt.keep_zoom_vp)
  483         winwidget_sanitise_offsets(winwid);
  484 
  485     if (!winwid->full_screen && ((gib_imlib_image_has_alpha(winwid->im))
  486                      || (opt.geom_flags & (WidthValue | HeightValue))
  487                      || (winwid->im_x || winwid->im_y)
  488                      || (winwid->w > winwid->im_w * winwid->zoom)
  489                      || (winwid->h > winwid->im_h * winwid->zoom)
  490                      || (winwid->has_rotated)))
  491         feh_draw_checks(winwid);
  492 
  493     /* Now we ensure only to render the area we're looking at */
  494     dx = winwid->im_x;
  495     dy = winwid->im_y;
  496     if (dx < 0)
  497         dx = 0;
  498     if (dy < 0)
  499         dy = 0;
  500 
  501     if (winwid->im_x < 0)
  502         sx = 0 - lround(winwid->im_x / winwid->zoom);
  503     else
  504         sx = 0;
  505 
  506     if (winwid->im_y < 0)
  507         sy = 0 - lround(winwid->im_y / winwid->zoom);
  508     else
  509         sy = 0;
  510 
  511     calc_w = lround(winwid->im_w * winwid->zoom);
  512     calc_h = lround(winwid->im_h * winwid->zoom);
  513     dw = (winwid->w - winwid->im_x);
  514     dh = (winwid->h - winwid->im_y);
  515     if (calc_w < dw)
  516         dw = calc_w;
  517     if (calc_h < dh)
  518         dh = calc_h;
  519     if (dw > winwid->w)
  520         dw = winwid->w;
  521     if (dh > winwid->h)
  522         dh = winwid->h;
  523 
  524     sw = lround(dw / winwid->zoom);
  525     sh = lround(dh / winwid->zoom);
  526 
  527     D(("sx: %d sy: %d sw: %d sh: %d dx: %d dy: %d dw: %d dh: %d zoom: %f\n",
  528        sx, sy, sw, sh, dx, dy, dw, dh, winwid->zoom));
  529 
  530     if ((winwid->zoom != 1.0 || winwid->has_rotated) && !force_alias && !winwid->force_aliasing)
  531         antialias = 1;
  532 
  533     D(("winwidget_render(): winwid->im_angle = %f\n", winwid->im_angle));
  534     if (winwid->has_rotated)
  535         gib_imlib_render_image_part_on_drawable_at_size_with_rotation
  536             (winwid->bg_pmap, winwid->im, sx, sy, sw, sh, dx, dy, dw, dh,
  537             winwid->im_angle, 1, 1, antialias);
  538     else
  539         gib_imlib_render_image_part_on_drawable_at_size(winwid->bg_pmap,
  540                                 winwid->im,
  541                                 sx, sy, sw,
  542                                 sh, dx, dy,
  543                                 dw, dh, 1,
  544                                 gib_imlib_image_has_alpha(winwid->im),
  545                                 antialias);
  546 
  547     if (opt.mode == MODE_NORMAL) {
  548         if (opt.caption_path)
  549             winwidget_update_caption(winwid);
  550         if (opt.draw_filename)
  551             feh_draw_filename(winwid);
  552 #ifdef HAVE_LIBEXIF
  553         if (opt.draw_exif)
  554             feh_draw_exif(winwid);
  555 #endif
  556         if (opt.draw_actions)
  557             feh_draw_actions(winwid);
  558         if (opt.draw_info && opt.info_cmd)
  559             feh_draw_info(winwid);
  560         if (winwid->errstr)
  561             feh_draw_errstr(winwid);
  562         if (winwid->file != NULL) {
  563             if (opt.title && winwid->type != WIN_TYPE_THUMBNAIL_VIEWER) {
  564                 winwidget_rename(winwid, feh_printf(opt.title, FEH_FILE(winwid->file->data), winwid));
  565             } else if (opt.thumb_title && winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) {
  566                 winwidget_rename(winwid, feh_printf(opt.thumb_title, FEH_FILE(winwid->file->data), winwid));
  567             }
  568         }
  569     } else if ((opt.mode == MODE_ZOOM) && !antialias)
  570         feh_draw_zoom(winwid);
  571 
  572     XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap);
  573     XClearWindow(disp, winwid->win);
  574     return;
  575 }
  576 
  577 void winwidget_render_image_cached(winwidget winwid)
  578 {
  579     static GC gc = None;
  580 
  581     if (gc == None) {
  582         gc = XCreateGC(disp, winwid->win, 0, NULL);
  583     }
  584     XCopyArea(disp, winwid->bg_pmap_cache, winwid->bg_pmap, gc, 0, 0, winwid->w, winwid->h, 0, 0);
  585 
  586     if (opt.caption_path)
  587         feh_draw_caption(winwid);
  588     if (opt.draw_filename)
  589         feh_draw_filename(winwid);
  590     if (opt.draw_actions)
  591         feh_draw_actions(winwid);
  592     if (opt.draw_info && opt.info_cmd)
  593         feh_draw_info(winwid);
  594     XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap);
  595     XClearWindow(disp, winwid->win);
  596 }
  597 
  598 double feh_calc_needed_zoom(double *zoom, int orig_w, int orig_h, int dest_w, int dest_h)
  599 {
  600     double ratio = 0.0;
  601 
  602     ratio = ((double) orig_w / orig_h) / ((double) dest_w / dest_h);
  603 
  604     if (opt.zoom_mode == ZOOM_MODE_FILL)
  605         ratio = 1.0 / ratio;
  606 
  607     if (ratio > 1.0)
  608         *zoom = ((double) dest_w / orig_w);
  609     else
  610         *zoom = ((double) dest_h / orig_h);
  611 
  612     return(ratio);
  613 }
  614 
  615 Pixmap feh_create_checks(void)
  616 {
  617     static Pixmap checks_pmap = None;
  618     Imlib_Image checks = NULL;
  619 
  620     if (checks_pmap == None) {
  621         checks = imlib_create_image(16, 16);
  622 
  623         if (!checks)
  624             eprintf("Unable to create a teeny weeny imlib image. I detect problems");
  625 
  626         if (!opt.image_bg || !strcmp(opt.image_bg, "default") || !strcmp(opt.image_bg, "checks")) {
  627             gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, 144, 144, 144, 255);
  628             gib_imlib_image_fill_rectangle(checks, 0, 0,  8,  8, 100, 100, 100, 255);
  629             gib_imlib_image_fill_rectangle(checks, 8, 8,  8,  8, 100, 100, 100, 255);
  630         } else {
  631             XColor color;
  632             Colormap cmap = DefaultColormap(disp, DefaultScreen(disp));
  633             XAllocNamedColor(disp, cmap, (char*) opt.image_bg, &color, &color);
  634             gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, color.red, color.green, color.blue, 255);
  635         }
  636 
  637         checks_pmap = XCreatePixmap(disp, root, 16, 16, depth);
  638         gib_imlib_render_image_on_drawable(checks_pmap, checks, 0, 0, 1, 0, 0);
  639         gib_imlib_free_image_and_decache(checks);
  640     }
  641     return(checks_pmap);
  642 }
  643 
  644 void feh_draw_checks(winwidget win)
  645 {
  646     static GC gc = None;
  647     XGCValues gcval;
  648 
  649     if (gc == None) {
  650         gcval.tile = feh_create_checks();
  651         gcval.fill_style = FillTiled;
  652         gc = XCreateGC(disp, win->win, GCTile | GCFillStyle, &gcval);
  653     }
  654     XFillRectangle(disp, win->bg_pmap, gc, 0, 0, win->w, win->h);
  655     return;
  656 }
  657 
  658 void winwidget_destroy_xwin(winwidget winwid)
  659 {
  660     if (winwid->win) {
  661         winwidget_unregister(winwid);
  662         XDestroyWindow(disp, winwid->win);
  663     }
  664     if (winwid->bg_pmap) {
  665         XFreePixmap(disp, winwid->bg_pmap);
  666         winwid->bg_pmap = None;
  667     }
  668     return;
  669 }
  670 
  671 void winwidget_destroy(winwidget winwid)
  672 {
  673 #ifdef HAVE_INOTIFY
  674     winwidget_inotify_remove(winwid);
  675 #endif
  676     winwidget_destroy_xwin(winwid);
  677     if (winwid->name)
  678         free(winwid->name);
  679     if (winwid->gc)
  680         XFreeGC(disp, winwid->gc);
  681     if (winwid->im)
  682         gib_imlib_free_image_and_decache(winwid->im);
  683     free(winwid);
  684     return;
  685 }
  686 
  687 #ifdef HAVE_INOTIFY
  688 void winwidget_inotify_remove(winwidget winwid)
  689 {
  690     if (winwid->inotify_wd >= 0) {
  691         D(("Removing inotify watch\n"));
  692         if (inotify_rm_watch(opt.inotify_fd, winwid->inotify_wd))
  693             weprintf("inotify_rm_watch failed:");
  694         winwid->inotify_wd = -1;
  695     }
  696 }
  697 #endif
  698 
  699 #ifdef HAVE_INOTIFY
  700 void winwidget_inotify_add(winwidget winwid, feh_file * file)
  701 {
  702     if (opt.auto_reload) {
  703         D(("Adding inotify watch for %s\n", file->filename));
  704         char dir[PATH_MAX];
  705         feh_file_dirname(dir, file, PATH_MAX);
  706 
  707         /*
  708          * Handle files without directory part, e.g. "feh somefile.jpg".
  709          * These always reside in the current directory.
  710          */
  711         if (dir[0] == '\0') {
  712             dir[0] = '.';
  713             dir[1] = '\0';
  714         }
  715         winwid->inotify_wd = inotify_add_watch(opt.inotify_fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO);
  716         if (winwid->inotify_wd < 0)
  717             weprintf("inotify_add_watch failed:");
  718     }
  719 }
  720 #endif
  721 
  722 #ifdef HAVE_INOTIFY
  723 #define INOTIFY_BUFFER_LEN (1024 * (sizeof (struct inotify_event)) + 16)
  724 void feh_event_handle_inotify(void)
  725 {
  726     D(("Received inotify events\n"));
  727     char buf[INOTIFY_BUFFER_LEN];
  728     int i = 0;
  729     int len = read (opt.inotify_fd, buf, INOTIFY_BUFFER_LEN);
  730     if (len < 0) {
  731         if (errno != EINTR)
  732             eprintf("inotify event read failed");
  733     } else if (!len)
  734         eprintf("inotify event read failed");
  735     while (i < len) {
  736         struct inotify_event *event;
  737         event = (struct inotify_event *) &buf[i];
  738         for (int j = 0; j < window_num; j++) {
  739             if(windows[j]->inotify_wd == event->wd) {
  740                 if (event->mask & IN_IGNORED) {
  741                     D(("inotify watch was implicitely removed\n"));
  742                     windows[j]->inotify_wd = -1;
  743                 } else if (event->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) {
  744                     if (strcmp(event->name, FEH_FILE(windows[j]->file->data)->name) == 0) {
  745                         D(("inotify says file changed\n"));
  746                         feh_reload_image(windows[j], 0, 0);
  747                     }
  748                 }
  749                 break;
  750             }
  751         }
  752         i += sizeof(struct inotify_event) + event->len;
  753     }
  754 }
  755 #endif
  756 
  757 void winwidget_destroy_all(void)
  758 {
  759     int i;
  760 
  761     /* Have to DESCEND the list here, 'cos of the way _unregister works */
  762     for (i = window_num - 1; i >= 0; i--)
  763         winwidget_destroy(windows[i]);
  764     return;
  765 }
  766 
  767 void winwidget_rerender_all(int resize)
  768 {
  769     int i;
  770 
  771     /* Have to DESCEND the list here, 'cos of the way _unregister works */
  772     for (i = window_num - 1; i >= 0; i--)
  773         winwidget_render_image(windows[i], resize, 0);
  774     return;
  775 }
  776 
  777 winwidget winwidget_get_first_window_of_type(unsigned int type)
  778 {
  779     int i;
  780 
  781     for (i = 0; i < window_num; i++)
  782         if (windows[i]->type == type)
  783             return(windows[i]);
  784     return(NULL);
  785 }
  786 
  787 int winwidget_loadimage(winwidget winwid, feh_file * file)
  788 {
  789     D(("filename %s\n", file->filename));
  790 #ifdef HAVE_INOTIFY
  791     winwidget_inotify_remove(winwid);
  792 #endif
  793     int res = feh_load_image(&(winwid->im), file);
  794 #ifdef HAVE_INOTIFY
  795     if (res) {
  796         winwidget_inotify_add(winwid, file);
  797     }
  798 #endif
  799     return(res);
  800 }
  801 
  802 void winwidget_show(winwidget winwid)
  803 {
  804     XEvent ev;
  805 
  806     /* feh_debug_print_winwid(winwid); */
  807     if (!winwid->visible) {
  808         XMapWindow(disp, winwid->win);
  809         if (opt.full_screen)
  810             XMoveWindow(disp, winwid->win, 0, 0);
  811         /* wait for the window to map */
  812         D(("Waiting for window to map\n"));
  813         XMaskEvent(disp, StructureNotifyMask, &ev);
  814         winwidget_get_geometry(winwid, NULL);
  815 
  816         /* Unfortunately, StructureNotifyMask does not only mask
  817          * the events of type MapNotify (which we want to mask here)
  818          * but also such of type ConfigureNotify (and others, see
  819          * https://tronche.com/gui/x/xlib/events/processing-overview.html),
  820          * which should be handled, especially on tiling wm's. To
  821          * remedy this, the handler is executed explicitly:
  822          */
  823         if (ev.type == ConfigureNotify)
  824             feh_event_handle_ConfigureNotify(&ev);
  825         D(("Window mapped\n"));
  826         winwid->visible = 1;
  827     }
  828     return;
  829 }
  830 
  831 void winwidget_move(winwidget winwid, int x, int y)
  832 {
  833     if (winwid && ((winwid->x != x) || (winwid->y != y))) {
  834         winwid->x = (x > scr->width) ? scr->width : x;
  835         winwid->y = (y > scr->height) ? scr->height : y;
  836         XMoveWindow(disp, winwid->win, winwid->x, winwid->y);
  837         XFlush(disp);
  838     } else {
  839         D(("No move actually needed\n"));
  840     }
  841     return;
  842 }
  843 
  844 void winwidget_resize(winwidget winwid, int w, int h, int force_resize)
  845 {
  846     XWindowAttributes attributes;
  847     int tc_x, tc_y, px, py;
  848     int scr_width = scr->width;
  849     int scr_height = scr->height;
  850 
  851     /* discarded */
  852     Window dw;
  853     int di, i;
  854     unsigned int du;
  855 
  856     XGetWindowAttributes(disp, winwid->win, &attributes);
  857 
  858 #ifdef HAVE_LIBXINERAMA
  859     if (opt.xinerama && xinerama_screens) {
  860         xinerama_screen = 0;
  861         XQueryPointer(disp, root, &dw, &dw, &px, &py, &di, &di, &du);
  862         for (i = 0; i < num_xinerama_screens; i++) {
  863             if (XY_IN_RECT(px, py,
  864                         xinerama_screens[i].x_org,
  865                         xinerama_screens[i].y_org,
  866                         xinerama_screens[i].width,
  867                         xinerama_screens[i].height)) {
  868                 xinerama_screen = i;
  869                 break;
  870             }
  871 
  872         }
  873         if (opt.xinerama_index >= 0)
  874             xinerama_screen = opt.xinerama_index;
  875 
  876         scr_width = xinerama_screens[xinerama_screen].width;
  877         scr_height = xinerama_screens[xinerama_screen].height;
  878     }
  879 #endif
  880 
  881 
  882     D(("   x %d y %d w %d h %d\n", attributes.x, attributes.y, winwid->w,
  883         winwid->h));
  884 
  885     if ((opt.geom_flags & (WidthValue | HeightValue)) && !force_resize) {
  886         winwid->had_resize = 1;
  887         return;
  888     }
  889     if (winwid && ((winwid->w != w) || (winwid->h != h))) {
  890         if (opt.screen_clip) {
  891             double required_zoom = winwid->zoom;
  892             if (opt.scale_down && !opt.keep_zoom_vp) {
  893                 int max_w = (w > scr_width) ? scr_width : w;
  894                 int max_h = (h > scr_height) ? scr_height : h;
  895                 feh_calc_needed_zoom(&required_zoom, winwid->im_w, winwid->im_h, max_w, max_h);
  896             }
  897             int desired_w = winwid->im_w * required_zoom;
  898             int desired_h = winwid->im_h * required_zoom;
  899             winwid->w = (desired_w > scr_width) ? scr_width : desired_w;
  900             winwid->h = (desired_h > scr_height) ? scr_height : desired_h;
  901         }
  902         if (winwid->full_screen) {
  903             XTranslateCoordinates(disp, winwid->win, attributes.root,
  904                         -attributes.border_width - attributes.x,
  905                         -attributes.border_width - attributes.y, &tc_x, &tc_y, &dw);
  906             winwid->x = tc_x;
  907             winwid->y = tc_y;
  908             XMoveResizeWindow(disp, winwid->win, tc_x, tc_y, winwid->w, winwid->h);
  909         } else
  910             XResizeWindow(disp, winwid->win, winwid->w, winwid->h);
  911 
  912         winwid->had_resize = 1;
  913         XFlush(disp);
  914 
  915         winwidget_get_geometry(winwid, NULL);
  916 
  917         if (force_resize && (opt.geom_flags & (WidthValue | HeightValue))
  918                 && (winwid->type != WIN_TYPE_THUMBNAIL)) {
  919             opt.geom_w = winwid->w;
  920             opt.geom_h = winwid->h;
  921         }
  922 
  923         D(("-> x %d y %d w %d h %d\n", winwid->x, winwid->y, winwid->w,
  924             winwid->h));
  925 
  926     } else {
  927         D(("No resize actually needed\n"));
  928     }
  929 
  930     return;
  931 }
  932 
  933 void winwidget_hide(winwidget winwid)
  934 {
  935     XUnmapWindow(disp, winwid->win);
  936     winwid->visible = 0;
  937     return;
  938 }
  939 
  940 static void winwidget_register(winwidget win)
  941 {
  942     D(("window %p\n", win));
  943     window_num++;
  944     if (windows)
  945         windows = erealloc(windows, window_num * sizeof(winwidget));
  946     else
  947         windows = emalloc(window_num * sizeof(winwidget));
  948     windows[window_num - 1] = win;
  949 
  950     XSaveContext(disp, win->win, xid_context, (XPointer) win);
  951     return;
  952 }
  953 
  954 static void winwidget_unregister(winwidget win)
  955 {
  956     int i, j;
  957 
  958     for (i = 0; i < window_num; i++) {
  959         if (windows[i] == win) {
  960             for (j = i; j < window_num - 1; j++)
  961                 windows[j] = windows[j + 1];
  962             window_num--;
  963             if (window_num > 0)
  964                 windows = erealloc(windows, window_num * sizeof(winwidget));
  965             else {
  966                 free(windows);
  967                 windows = NULL;
  968             }
  969         }
  970     }
  971     XDeleteContext(disp, win->win, xid_context);
  972     return;
  973 }
  974 
  975 winwidget winwidget_get_from_window(Window win)
  976 {
  977     winwidget ret = NULL;
  978 
  979     if (XFindContext(disp, win, xid_context, (XPointer *) & ret) != XCNOENT)
  980         return(ret);
  981     return(NULL);
  982 }
  983 
  984 void winwidget_rename(winwidget winwid, char *newname)
  985 {
  986     /* newname == NULL -> update current title */
  987     char *p_str;
  988 
  989     if (newname == NULL)
  990         newname = estrdup(winwid->name ? winwid->name : "");
  991     if (winwid->name)
  992         free(winwid->name);
  993 
  994     winwid->name = emalloc(strlen(newname) + 10);
  995     strcpy(winwid->name, newname);
  996 
  997     if (strlen(winwid->name) > 9)
  998         p_str = winwid->name + strlen(winwid->name) - 9;
  999     else
 1000         p_str = winwid->name;
 1001 
 1002     if (opt.paused && strcmp(p_str, " [Paused]") != 0)
 1003         strcat(winwid->name, " [Paused]");
 1004     else if (!opt.paused && strcmp(p_str, " [Paused]") == 0)
 1005         *p_str = '\0';
 1006 
 1007     winwidget_update_title(winwid);
 1008     return;
 1009 }
 1010 
 1011 void winwidget_free_image(winwidget w)
 1012 {
 1013     if (w->im)
 1014         gib_imlib_free_image(w->im);
 1015     w->im = NULL;
 1016     w->im_w = 0;
 1017     w->im_h = 0;
 1018     return;
 1019 }
 1020 
 1021 void feh_debug_print_winwid(winwidget w)
 1022 {
 1023     printf("winwid_debug:\n" "winwid = %p\n" "win = %ld\n" "w = %d\n"
 1024            "h = %d\n" "im_w = %d\n" "im_h = %d\n" "im_angle = %f\n"
 1025            "type = %d\n" "had_resize = %d\n" "im = %p\n" "GC = %p\n"
 1026            "pixmap = %ld\n" "name = %s\n" "file = %p\n" "mode = %d\n"
 1027            "im_x = %d\n" "im_y = %d\n" "zoom = %f\n" "old_zoom = %f\n"
 1028            "click_offset_x = %d\n" "click_offset_y = %d\n"
 1029            "has_rotated = %d\n", (void *)w, w->win, w->w, w->h, w->im_w,
 1030            w->im_h, w->im_angle, w->type, w->had_resize, w->im, (void *)w->gc,
 1031            w->bg_pmap, w->name, (void *)w->file, w->mode, w->im_x, w->im_y,
 1032            w->zoom, w->old_zoom, w->click_offset_x, w->click_offset_y,
 1033            w->has_rotated);
 1034 }
 1035 
 1036 void winwidget_reset_image(winwidget winwid)
 1037 {
 1038     if (!opt.keep_zoom_vp) {
 1039         winwid->zoom = 1.0;
 1040         winwid->old_zoom = 1.0;
 1041         winwid->im_x = 0;
 1042         winwid->im_y = 0;
 1043     }
 1044     winwid->im_angle = 0.0;
 1045     winwid->has_rotated = 0;
 1046     return;
 1047 }
 1048 
 1049 void winwidget_center_image(winwidget winwid)
 1050 {
 1051     int scr_width, scr_height;
 1052 
 1053     scr_width = scr->width;
 1054     scr_height = scr->height;
 1055 
 1056 #ifdef HAVE_LIBXINERAMA
 1057     if (opt.xinerama && xinerama_screens) {
 1058         scr_width = xinerama_screens[xinerama_screen].width;
 1059         scr_height = xinerama_screens[xinerama_screen].height;
 1060     }
 1061 #endif              /* HAVE_LIBXINERAMA */
 1062 
 1063     if (winwid->full_screen) {
 1064         winwid->im_x = (scr_width - lround(winwid->im_w * winwid->zoom)) >> 1;
 1065         winwid->im_y = (scr_height - lround(winwid->im_h * winwid->zoom)) >> 1;
 1066     } else {
 1067         if (opt.geom_flags & WidthValue)
 1068             winwid->im_x = ((int)opt.geom_w - lround(winwid->im_w * winwid->zoom)) >> 1;
 1069         else
 1070             winwid->im_x = 0;
 1071         if (opt.geom_flags & HeightValue)
 1072             winwid->im_y = ((int)opt.geom_h - lround(winwid->im_h * winwid->zoom)) >> 1;
 1073         else
 1074             winwid->im_y = 0;
 1075     }
 1076 }
 1077 
 1078 void winwidget_sanitise_offsets(winwidget winwid)
 1079 {
 1080     int far_left, far_top;
 1081     int min_x, max_x, max_y, min_y;
 1082 
 1083     far_left = winwid->w - (winwid->im_w * winwid->zoom);
 1084     far_top = winwid->h - (winwid->im_h * winwid->zoom);
 1085 
 1086     if ((winwid->im_w * winwid->zoom) > winwid->w) {
 1087         min_x = far_left;
 1088         max_x = 0;
 1089     } else {
 1090         min_x = 0;
 1091         max_x = far_left;
 1092     }
 1093     if ((winwid->im_h * winwid->zoom) > winwid->h) {
 1094         min_y = far_top;
 1095         max_y = 0;
 1096     } else {
 1097         min_y = 0;
 1098         max_y = far_top;
 1099     }
 1100     if (winwid->im_x > max_x)
 1101         winwid->im_x = max_x;
 1102     if (winwid->im_x < min_x)
 1103         winwid->im_x = min_x;
 1104     if (winwid->im_y > max_y)
 1105         winwid->im_y = max_y;
 1106     if (winwid->im_y < min_y)
 1107         winwid->im_y = min_y;
 1108 
 1109     return;
 1110 }
 1111 
 1112 void winwidget_size_to_image(winwidget winwid)
 1113 {
 1114     winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom, 1);
 1115     winwid->im_x = winwid->im_y = 0;
 1116     winwidget_render_image(winwid, 0, 0);
 1117     return;
 1118 }
 1119 
 1120 void winwidget_set_pointer(winwidget winwid, int visible)
 1121 {
 1122     Cursor no_ptr;
 1123     XColor black, dummy;
 1124     Pixmap bm_no;
 1125     static char bm_no_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
 1126 
 1127     if (visible)
 1128         XUndefineCursor(disp, winwid->win);
 1129     else {
 1130         bm_no = XCreateBitmapFromData(disp, winwid->win, bm_no_data, 8, 8);
 1131         XAllocNamedColor(disp, DefaultColormapOfScreen(DefaultScreenOfDisplay(disp)), "black", &black, &dummy);
 1132 
 1133         no_ptr = XCreatePixmapCursor(disp, bm_no, bm_no, &black, &black, 0, 0);
 1134         XDefineCursor(disp, winwid->win, no_ptr);
 1135     }
 1136 }
 1137 
 1138 int winwidget_get_width(winwidget winwid)
 1139 {
 1140     int rect[4];
 1141     winwidget_get_geometry(winwid, rect);
 1142     return(rect[2]);
 1143 }
 1144 
 1145 int winwidget_get_height(winwidget winwid)
 1146 {
 1147     int rect[4];
 1148     winwidget_get_geometry(winwid, rect);
 1149     return(rect[3]);
 1150 }
 1151 
 1152 void winwidget_get_geometry(winwidget winwid, int *rect)
 1153 {
 1154     unsigned int bw, bp;
 1155     Window child;
 1156 
 1157     int inner_rect[4];
 1158 
 1159     if (!rect)
 1160         rect = inner_rect;
 1161 
 1162     XGetGeometry(disp, winwid->win, &root, &(rect[0]), &(rect[1]), (unsigned
 1163                 int *)&(rect[2]), (unsigned int *)&(rect[3]), &bw, &bp);
 1164 
 1165     XTranslateCoordinates(disp, winwid->win, root, 0, 0, &(rect[0]), &(rect[1]), &child);
 1166 
 1167     /* update the window geometry (in case it's inaccurate) */
 1168     winwid->x = rect[0];
 1169     winwid->y = rect[1];
 1170     winwid->w = rect[2];
 1171     winwid->h = rect[3];
 1172     return;
 1173 }
 1174 
 1175 void winwidget_show_menu(winwidget winwid)
 1176 {
 1177     int x, y, b;
 1178     unsigned int c;
 1179     Window r;
 1180 
 1181     XQueryPointer(disp, winwid->win, &r, &r, &x, &y, &b, &b, &c);
 1182     if (winwid->type == WIN_TYPE_SINGLE) {
 1183         if (!menu_single_win)
 1184             feh_menu_init_single_win();
 1185         feh_menu_show_at_xy(menu_single_win, winwid, x, y);
 1186     } else if (winwid->type == WIN_TYPE_THUMBNAIL) {
 1187         if (!menu_thumbnail_win)
 1188             feh_menu_init_thumbnail_win();
 1189         feh_menu_show_at_xy(menu_thumbnail_win, winwid, x, y);
 1190     } else if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) {
 1191         if (!menu_single_win)
 1192             feh_menu_init_thumbnail_viewer();
 1193         feh_menu_show_at_xy(menu_thumbnail_viewer, winwid, x, y);
 1194     } else {
 1195         if (!menu_main)
 1196             feh_menu_init_main();
 1197         feh_menu_show_at_xy(menu_main, winwid, x, y);
 1198     }
 1199 }