diff options
Diffstat (limited to 'src/winwidget.c')
-rw-r--r-- | src/winwidget.c | 859 |
1 files changed, 532 insertions, 327 deletions
diff --git a/src/winwidget.c b/src/winwidget.c index 6ab39e2..809a0a6 100644 --- a/src/winwidget.c +++ b/src/winwidget.c @@ -1,6 +1,7 @@ /* winwidget.c Copyright (C) 1999-2003 Tom Gilbert. +Copyright (C) 2010-2025 Birte Kristina Friesel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -27,6 +28,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "filelist.h" #include "winwidget.h" #include "options.h" +#include "events.h" +#include "timers.h" + +#ifdef HAVE_INOTIFY +#include <sys/inotify.h> +#endif static void winwidget_unregister(winwidget win); static void winwidget_register(winwidget win); @@ -40,8 +47,8 @@ static winwidget winwidget_allocate(void) { winwidget ret = NULL; - D_ENTER(4); ret = emalloc(sizeof(_winwidget)); + memset(ret, 0, sizeof(_winwidget)); ret->win = 0; ret->w = 0; @@ -55,9 +62,11 @@ static winwidget winwidget_allocate(void) ret->im = NULL; ret->name = NULL; ret->file = NULL; + ret->errstr = NULL; ret->type = WIN_TYPE_UNSET; ret->visible = 0; ret->caption_entry = 0; + ret->force_aliasing = opt.force_aliasing; /* Zoom stuff */ ret->mode = MODE_NORMAL; @@ -74,17 +83,19 @@ static winwidget winwidget_allocate(void) ret->click_offset_y = 0; ret->has_rotated = 0; - D_RETURN(4, ret); +#ifdef HAVE_INOTIFY + ret->inotify_wd = -1; +#endif + + return(ret); } -winwidget winwidget_create_from_image(Imlib_Image im, char *name, char type) +winwidget winwidget_create_from_image(Imlib_Image im, char type) { winwidget ret = NULL; - D_ENTER(4); - if (im == NULL) - D_RETURN(4, NULL); + return(NULL); ret = winwidget_allocate(); ret->type = type; @@ -93,66 +104,74 @@ winwidget winwidget_create_from_image(Imlib_Image im, char *name, char type) ret->w = ret->im_w = gib_imlib_image_get_width(ret->im); ret->h = ret->im_h = gib_imlib_image_get_height(ret->im); - if (name) - ret->name = estrdup(name); - else - ret->name = estrdup(PACKAGE); - - if (opt.full_screen) + if (opt.full_screen && (type != WIN_TYPE_THUMBNAIL)) { ret->full_screen = True; + } else if (opt.default_zoom) { + ret->zoom = 0.01 * opt.default_zoom; + ret->w *= ret->zoom; + ret->h *= ret->zoom; + } winwidget_create_window(ret, ret->w, ret->h); - winwidget_render_image(ret, 1, 1); + winwidget_render_image(ret, 1, 0); - D_RETURN(4, ret); + return(ret); } -winwidget winwidget_create_from_file(gib_list * list, char *name, char type) +winwidget winwidget_create_from_file(gib_list * list, char type) { winwidget ret = NULL; feh_file *file = FEH_FILE(list->data); - D_ENTER(4); - if (!file || !file->filename) - D_RETURN(4, NULL); + return(NULL); ret = winwidget_allocate(); ret->file = list; ret->type = type; - if (name) - ret->name = estrdup(name); - else - ret->name = estrdup(file->filename); - if (winwidget_loadimage(ret, file) == 0) { + if ((winwidget_loadimage(ret, file) == 0) || feh_should_ignore_image(ret->im)) { winwidget_destroy(ret); - D_RETURN(4, NULL); + return(NULL); } if (!ret->win) { ret->w = ret->im_w = gib_imlib_image_get_width(ret->im); ret->h = ret->im_h = gib_imlib_image_get_height(ret->im); - D(3, ("image is %dx%d pixels, format %s\n", ret->w, ret->h, gib_imlib_image_format(ret->im))); - if (opt.full_screen) + D(("image is %dx%d pixels, format %s\n", ret->w, ret->h, gib_imlib_image_format(ret->im))); + if (opt.full_screen) { ret->full_screen = True; + } else if (opt.default_zoom) { + ret->zoom = 0.01 * opt.default_zoom; + ret->w *= ret->zoom; + ret->h *= ret->zoom; + } winwidget_create_window(ret, ret->w, ret->h); - winwidget_render_image(ret, 1, 1); + winwidget_render_image(ret, 1, 0); } - D_RETURN(4, ret); + return(ret); } void winwidget_create_window(winwidget ret, int w, int h) { + XWindowAttributes window_attr; XSetWindowAttributes attr; XEvent ev; XClassHint *xch; MWMHints mwmhints; Atom prop = None; + pid_t pid; int x = 0; int y = 0; + char *tmpname; +#ifdef HOST_NAME_MAX + char hostname[HOST_NAME_MAX]; +#else /* ! HOST_NAME_MAX */ + long host_name_max; + char *hostname; +#endif /* HOST_NAME_MAX */ - D_ENTER(4); + D(("winwidget_create_window %dx%d\n", w, h)); if (ret->full_screen) { w = scr->width; @@ -162,6 +181,8 @@ void winwidget_create_window(winwidget ret, int w, int h) if (opt.xinerama && xinerama_screens) { w = xinerama_screens[xinerama_screen].width; h = xinerama_screens[xinerama_screen].height; + x = xinerama_screens[xinerama_screen].x_org; + y = xinerama_screens[xinerama_screen].y_org; } #endif /* HAVE_LIBXINERAMA */ } else if (opt.geom_flags) { @@ -201,6 +222,12 @@ void winwidget_create_window(winwidget ret, int w, int h) #endif /* HAVE_LIBXINERAMA */ } + if (opt.paused) { + tmpname = estrjoin(" ", ret->name, "[Paused]", NULL); + free(ret->name); + ret->name = tmpname; + } + ret->x = x; ret->y = y; ret->w = w; @@ -219,6 +246,7 @@ void winwidget_create_window(winwidget ret, int w, int h) KeyPressMask | KeyReleaseMask | ButtonMotionMask | ExposureMask | FocusChangeMask | PropertyChangeMask | VisibilityChangeMask; + memset(&mwmhints, 0, sizeof(mwmhints)); if (opt.borderless || ret->full_screen) { prop = XInternAtom(disp, "_MOTIF_WM_HINTS", True); if (prop == None) { @@ -231,14 +259,33 @@ void winwidget_create_window(winwidget ret, int w, int h) mwmhints.flags = MWM_HINTS_DECORATIONS; mwmhints.decorations = 0; } - } else - mwmhints.flags = 0; + } - ret->win = - XCreateWindow(disp, DefaultRootWindow(disp), x, y, w, h, 0, - depth, InputOutput, vis, - CWOverrideRedirect | CWSaveUnder | CWBackingStore - | CWColormap | CWBackPixel | CWBorderPixel | CWEventMask, &attr); + if (opt.x11_windowid == 0) { + ret->win = + XCreateWindow(disp, DefaultRootWindow(disp), x, y, w, h, 0, + depth, InputOutput, vis, + CWOverrideRedirect | CWSaveUnder | CWBackingStore + | CWColormap | CWBackPixel | CWBorderPixel | CWEventMask, + &attr); + } else { + /* x11_windowid is a pointer to the window */ + ret->win = (Window) opt.x11_windowid; + + /* set our attributes on the window */ + XChangeWindowAttributes(disp, ret->win, + CWOverrideRedirect | CWSaveUnder | CWBackingStore + | CWColormap | CWBackPixel | CWBorderPixel + | CWEventMask, &attr); + + /* determine the size and visibility of the window */ + XGetWindowAttributes(disp, ret->win, &window_attr); + ret->visible = (window_attr.map_state == IsViewable); + ret->x = window_attr.x; + ret->y = window_attr.y; + ret->w = window_attr.width; + ret->h = window_attr.height; + } if (mwmhints.flags) { XChangeProperty(disp, ret->win, prop, prop, 32, @@ -254,18 +301,43 @@ void winwidget_create_window(winwidget ret, int w, int h) ev.xclient.display = disp; ev.xclient.window = ret->win; ev.xclient.format = 32; - ev.xclient.data.l[0] = (ret->full_screen ? 1 : 0); + ev.xclient.data.l[0] = 1; ev.xclient.data.l[1] = prop_fs; XChangeProperty(disp, ret->win, prop_state, XA_ATOM, 32, PropModeReplace, (unsigned char *) &prop_fs, 1); } + pid = getpid(); + prop = XInternAtom(disp, "_NET_WM_PID", False); + XChangeProperty(disp, ret->win, prop, XA_CARDINAL, sizeof(pid_t) * 8, + PropModeReplace, (const unsigned char *)&pid, 1); + +#ifdef HOST_NAME_MAX + if (gethostname(hostname, HOST_NAME_MAX) == 0) { + hostname[HOST_NAME_MAX-1] = '\0'; + prop = XInternAtom(disp, "WM_CLIENT_MACHINE", False); + XChangeProperty(disp, ret->win, prop, XA_STRING, sizeof(char) * 8, + PropModeReplace, (unsigned char *)hostname, strlen(hostname)); + } +#else /* ! HOST_NAME_MAX */ + if ((host_name_max = sysconf(_SC_HOST_NAME_MAX)) != -1 ) { + if ((hostname = calloc(1, host_name_max + 1)) != NULL ) { + if (gethostname(hostname, host_name_max) == 0) { + prop = XInternAtom(disp, "WM_CLIENT_MACHINE", False); + XChangeProperty(disp, ret->win, prop, XA_STRING, sizeof(char) * 8, + PropModeReplace, (unsigned char *)hostname, strlen(hostname)); + } + free(hostname); + } + } +#endif /* HOST_NAME_MAX */ + XSetWMProtocols(disp, ret->win, &wmDeleteWindow, 1); winwidget_update_title(ret); xch = XAllocClassHint(); xch->res_name = "feh"; - xch->res_class = "feh"; + xch->res_class = opt.x11_class ? opt.x11_class : "feh"; XSetClassHint(disp, ret->win, xch); XFree(xch); @@ -288,40 +360,89 @@ void winwidget_create_window(winwidget ret, int w, int h) XSetCommand(disp, ret->win, cmdargv, cmdargc); winwidget_register(ret); - D_RETURN_(4); + + /* do not scale down a thumbnail list window, only those created from it */ + if (opt.geom_enabled && (ret->type != WIN_TYPE_THUMBNAIL)) { + opt.geom_w = w; + opt.geom_h = h; + opt.geom_flags |= WidthValue | HeightValue; + } + + return; } void winwidget_update_title(winwidget ret) { char *name; + Atom prop_name = XInternAtom(disp, "_NET_WM_NAME", False); + Atom prop_icon = XInternAtom(disp, "_NET_WM_ICON_NAME", False); + Atom prop_utf8 = XInternAtom(disp, "UTF8_STRING", False); - D_ENTER(4); - D(4, ("winwid->name = %s\n", ret->name)); + D(("winwid->name = %s\n", ret->name)); name = ret->name ? ret->name : "feh"; XStoreName(disp, ret->win, name); XSetIconName(disp, ret->win, name); - /* XFlush(disp); */ - D_RETURN_(4); + + XChangeProperty(disp, ret->win, prop_name, prop_utf8, 8, + PropModeReplace, (const unsigned char *)name, strlen(name)); + + XChangeProperty(disp, ret->win, prop_icon, prop_utf8, 8, + PropModeReplace, (const unsigned char *)name, strlen(name)); + + return; } -void winwidget_setup_pixmaps(winwidget winwid) +void winwidget_update_caption(winwidget winwid) { - D_ENTER(4); + if (opt.caption_path) { + /* TODO: Does someone understand the caching here. Is this the right + * approach now that I have broken this out into a separate function. -lsmith */ + /* cache bg pixmap. during caption entry, multiple redraws are done + * because the caption overlay changes - the image doesn't though, so re- + * rendering that is a waste of time */ + if (winwid->caption_entry) { + GC gc; + if (winwid->bg_pmap_cache) + XFreePixmap(disp, winwid->bg_pmap_cache); + winwid->bg_pmap_cache = XCreatePixmap(disp, winwid->win, winwid->w, winwid->h, depth); + gc = XCreateGC(disp, winwid->win, 0, NULL); + XCopyArea(disp, winwid->bg_pmap, winwid->bg_pmap_cache, gc, 0, 0, winwid->w, winwid->h, 0, 0); + XFreeGC(disp, gc); + } + feh_draw_caption(winwid); + } + return; +} + +void winwidget_setup_pixmaps(winwidget winwid) +{ if (winwid->full_screen) { if (!(winwid->bg_pmap)) { if (winwid->gc == None) { XGCValues gcval; - gcval.foreground = BlackPixel(disp, DefaultScreen(disp)); - winwid->gc = XCreateGC(disp, winwid->win, GCForeground, &gcval); + if (!opt.image_bg || !strcmp(opt.image_bg, "default")) { + gcval.foreground = BlackPixel(disp, DefaultScreen(disp)); + winwid->gc = XCreateGC(disp, winwid->win, GCForeground, &gcval); + } else if (!strcmp(opt.image_bg, "checks")) { + gcval.tile = feh_create_checks(); + gcval.fill_style = FillTiled; + winwid->gc = XCreateGC(disp, winwid->win, GCTile | GCFillStyle, &gcval); + } else { + XColor color; + Colormap cmap = DefaultColormap(disp, DefaultScreen(disp)); + XAllocNamedColor(disp, cmap, (char*) opt.image_bg, &color, &color); + gcval.foreground = color.pixel; + winwid->gc = XCreateGC(disp, winwid->win, GCForeground, &gcval); + } } winwid->bg_pmap = XCreatePixmap(disp, winwid->win, scr->width, scr->height, depth); } XFillRectangle(disp, winwid->bg_pmap, winwid->gc, 0, 0, scr->width, scr->height); } else { if (!winwid->bg_pmap || winwid->had_resize) { - D(4, ("recreating background pixmap (%dx%d)\n", winwid->w, winwid->h)); + D(("recreating background pixmap (%dx%d)\n", winwid->w, winwid->h)); if (winwid->bg_pmap) XFreePixmap(disp, winwid->bg_pmap); @@ -333,130 +454,79 @@ void winwidget_setup_pixmaps(winwidget winwid) winwid->had_resize = 0; } } - D_RETURN_(4); + return; } -void winwidget_render_image(winwidget winwid, int resize, int alias) +void winwidget_render_image(winwidget winwid, int resize, int force_alias) { int sx, sy, sw, sh, dx, dy, dw, dh; int calc_w, calc_h; - - D_ENTER(4); + int antialias = 0; if (!winwid->full_screen && resize) { - winwidget_resize(winwid, winwid->im_w, winwid->im_h); + if (opt.default_zoom) { + winwid->zoom = 0.01 * opt.default_zoom; + winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom, 0); + } else { + winwidget_resize(winwid, winwid->im_w, winwid->im_h, 0); + } winwidget_reset_image(winwid); } - /* bounds checks for panning */ - if (winwid->im_x > winwid->w) - winwid->im_x = winwid->w; - if (winwid->im_y > winwid->h) - winwid->im_y = winwid->h; + D(("winwidget_render_image resize %d force_alias %d im %dx%d\n", + resize, force_alias, winwid->im_w, winwid->im_h)); - winwidget_setup_pixmaps(winwid); + /* winwidget_setup_pixmaps(winwid) resets the winwid->had_resize flag */ + int had_resize = winwid->had_resize || resize; - if (!winwid->full_screen && ((gib_imlib_image_has_alpha(winwid->im)) || (opt.geom_flags) - || (winwid->im_x || winwid->im_y) || (winwid->zoom != 1.0) - || (winwid->w > winwid->im_w || winwid->h > winwid->im_h) - || (winwid->has_rotated))) - feh_draw_checks(winwid); + winwidget_setup_pixmaps(winwid); - if (!winwid->full_screen && opt.scale_down && ((winwid->w < winwid->im_w) - || (winwid->h < winwid->im_h))) { - D(2, ("scaling down image\n")); + if (had_resize && !opt.keep_zoom_vp && (winwid->type != WIN_TYPE_THUMBNAIL)) { + double required_zoom = 1.0; + feh_calc_needed_zoom(&required_zoom, winwid->im_w, winwid->im_h, winwid->w, winwid->h); - feh_calc_needed_zoom(&(winwid->zoom), winwid->im_w, winwid->im_h, winwid->w, winwid->h); - winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom); - } + winwid->zoom = opt.default_zoom ? (0.01 * opt.default_zoom) : 1.0; - if (resize && (winwid->full_screen || opt.geom_flags)) { - int smaller; /* Is the image smaller than screen? */ - int max_w = 0, max_h = 0; + if ((opt.scale_down || (winwid->full_screen && !opt.default_zoom)) + && winwid->zoom > required_zoom) + winwid->zoom = required_zoom; + else if ((opt.zoom_mode && required_zoom > 1) + && (!opt.default_zoom || required_zoom < winwid->zoom)) + winwid->zoom = required_zoom; - if (winwid->full_screen) { - max_w = scr->width; - max_h = scr->height; -#ifdef HAVE_LIBXINERAMA - if (opt.xinerama && xinerama_screens) { - max_w = xinerama_screens[xinerama_screen].width; - max_h = xinerama_screens[xinerama_screen].height; + if (opt.offset_flags & XValue) { + if (opt.offset_flags & XNegative) { + winwid->im_x = winwid->w - (winwid->im_w * winwid->zoom) - opt.offset_x; + } else { + winwid->im_x = - opt.offset_x * winwid->zoom; } -#endif /* HAVE_LIBXINERAMA */ } else { - if (opt.geom_flags & WidthValue) { - max_w = opt.geom_w; - } - if (opt.geom_flags & HeightValue) { - max_h = opt.geom_h; - } + winwid->im_x = (int) (winwid->w - (winwid->im_w * winwid->zoom)) >> 1; } - - D(4, ("Calculating for fullscreen/fixed geom render\n")); - smaller = ((winwid->im_w < max_w) - && (winwid->im_h < max_h)); - - if (!smaller || opt.auto_zoom) { - double ratio = 0.0; - - /* Image is larger than the screen (so wants shrinking), or it's - smaller but wants expanding to fill it */ - ratio = feh_calc_needed_zoom(&(winwid->zoom), winwid->im_w, winwid->im_h, max_w, max_h); - - /* contributed by Jens Laas <jens.laas@data.slu.se> - * What it does: - * zooms images by a fixed amount but never larger than the screen. - * - * Why: - * This is nice if you got a collection of images where some - * are small and can stand a small zoom. Large images are unaffected. - * - * When does it work, and how? - * You have to be in fullscreen mode _and_ have auto-zoom turned on. - * "feh -FZ --zoom 130 imagefile" will do the trick. - * -zoom percent - the new switch. - * 100 = orignal size, - * 130 is 30% larger. - */ - if (opt.default_zoom) { - double old_zoom = winwid->zoom; - - winwid->zoom = 0.01 * opt.default_zoom; - if ((winwid->im_h * winwid->zoom) > max_h) - winwid->zoom = old_zoom; - if ((winwid->im_w * winwid->zoom) > max_w) - winwid->zoom = old_zoom; - - winwid->im_x = ((int) - (max_w - (winwid->im_w * winwid->zoom))) >> 1; - winwid->im_y = ((int) - (max_h - (winwid->im_h * winwid->zoom))) >> 1; + if (opt.offset_flags & YValue) { + if (opt.offset_flags & YNegative) { + winwid->im_y = winwid->h - (winwid->im_h * winwid->zoom) - opt.offset_y; } else { - if (ratio > 1.0) { - /* height is the factor */ - winwid->im_x = 0; - winwid->im_y = ((int) - (max_h - (winwid->im_h * winwid->zoom))) >> 1; - } else { - /* width is the factor */ - winwid->im_x = ((int) - (max_w - (winwid->im_w * winwid->zoom))) >> 1; - winwid->im_y = 0; - } + winwid->im_y = - opt.offset_y * winwid->zoom; } } else { - /* my modification to jens hack, allow --zoom without auto-zoom mode */ - if (opt.default_zoom) { - winwid->zoom = 0.01 * opt.default_zoom; - } else { - winwid->zoom = 1.0; - } - /* Just center the image in the window */ - winwid->im_x = (int) (max_w - (winwid->im_w * winwid->zoom)) >> 1; - winwid->im_y = (int) (max_h - (winwid->im_h * winwid->zoom)) >> 1; + winwid->im_y = (int) (winwid->h - (winwid->im_h * winwid->zoom)) >> 1; } } + winwid->had_resize = 0; + + if (opt.keep_zoom_vp) + winwidget_sanitise_offsets(winwid); + + if (!winwid->full_screen && ((gib_imlib_image_has_alpha(winwid->im)) + || (opt.geom_flags & (WidthValue | HeightValue)) + || (winwid->im_x || winwid->im_y) + || (winwid->w > winwid->im_w * winwid->zoom) + || (winwid->h > winwid->im_h * winwid->zoom) + || (winwid->has_rotated))) + feh_draw_checks(winwid); + /* Now we ensure only to render the area we're looking at */ dx = winwid->im_x; dy = winwid->im_y; @@ -466,71 +536,85 @@ void winwidget_render_image(winwidget winwid, int resize, int alias) dy = 0; if (winwid->im_x < 0) - sx = 0 - (winwid->im_x / winwid->zoom); + sx = 0 - lround(winwid->im_x / winwid->zoom); else sx = 0; if (winwid->im_y < 0) - sy = 0 - (winwid->im_y / winwid->zoom); + sy = 0 - lround(winwid->im_y / winwid->zoom); else sy = 0; - calc_w = winwid->im_w * winwid->zoom; - calc_h = winwid->im_h * winwid->zoom; + calc_w = lround(winwid->im_w * winwid->zoom); + calc_h = lround(winwid->im_h * winwid->zoom); dw = (winwid->w - winwid->im_x); dh = (winwid->h - winwid->im_y); - if (calc_w < dw) + + D(("sx: %4d sy: %4d sw: sh: dx: %4d dy: %4d dw: %4d dh: %4d zoom: %f\n", + sx, sy, sw, sh, dx, dy, dw, dh, winwid->zoom)); + + if (calc_w < dw) { dw = calc_w; - if (calc_h < dh) + } + if (calc_h < dh) { dh = calc_h; + } if (dw > winwid->w) dw = winwid->w; if (dh > winwid->h) dh = winwid->h; - sw = dw / winwid->zoom; - sh = dh / winwid->zoom; + sw = lround(dw / winwid->zoom); + sh = lround(dh / winwid->zoom); - D(5, - ("sx: %d sy: %d sw: %d sh: %d dx: %d dy: %d dw: %d dh: %d zoom: %f\n", + D(("sx: %4d sy: %4d sw: %4d sh: %4d dx: %4d dy: %4d dw: %4d dh: %4d zoom: %f\n", sx, sy, sw, sh, dx, dy, dw, dh, winwid->zoom)); - D(5, ("winwidget_render(): winwid->im_angle = %f\n", winwid->im_angle)); + if ((winwid->zoom != 1.0 || winwid->has_rotated) && !force_alias && !winwid->force_aliasing) + antialias = 1; + + D(("winwidget_render(): winwid->im_angle = %f\n", winwid->im_angle)); if (winwid->has_rotated) gib_imlib_render_image_part_on_drawable_at_size_with_rotation - (winwid->bg_pmap, winwid->im, sx, sy, sw, sh, dx, dy, dw, dh, winwid->im_angle, 1, 1, alias); + (winwid->bg_pmap, winwid->im, sx, sy, sw, sh, dx, dy, dw, dh, + winwid->im_angle, 1, 1, antialias); else gib_imlib_render_image_part_on_drawable_at_size(winwid->bg_pmap, winwid->im, sx, sy, sw, sh, dx, dy, dw, dh, 1, - gib_imlib_image_has_alpha(winwid->im), alias); - if (opt.caption_path) { - /* cache bg pixmap. during caption entry, multiple redraws are done - * because the caption overlay changes - the image doesn't though, so re- - * rendering that is a waste of time */ - if (winwid->caption_entry) { - GC gc; - if (winwid->bg_pmap_cache) - XFreePixmap(disp, winwid->bg_pmap_cache); - winwid->bg_pmap_cache = XCreatePixmap(disp, winwid->win, winwid->w, winwid->h, depth); - gc = XCreateGC(disp, winwid->win, 0, NULL); - XCopyArea(disp, winwid->bg_pmap, winwid->bg_pmap_cache, gc, 0, 0, winwid->w, winwid->h, 0, 0); - XFreeGC(disp, gc); + gib_imlib_image_has_alpha(winwid->im), + antialias); + + if (opt.mode == MODE_NORMAL) { + if (opt.caption_path) + winwidget_update_caption(winwid); + if (opt.draw_filename) + feh_draw_filename(winwid); +#ifdef HAVE_LIBEXIF + if (opt.draw_exif) + feh_draw_exif(winwid); +#endif + if (opt.draw_actions) + feh_draw_actions(winwid); + if (opt.draw_info && opt.info_cmd) + feh_draw_info(winwid); + if (winwid->errstr) + feh_draw_errstr(winwid); + if (winwid->file != NULL) { + if (opt.title && winwid->type != WIN_TYPE_THUMBNAIL_VIEWER) { + winwidget_rename(winwid, feh_printf(opt.title, FEH_FILE(winwid->file->data), winwid)); + } else if (opt.thumb_title && winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) { + winwidget_rename(winwid, feh_printf(opt.thumb_title, FEH_FILE(winwid->file->data), winwid)); + } } - feh_draw_caption(winwid); - } - - if (opt.draw_filename) - feh_draw_filename(winwid); - if (opt.draw_actions) - feh_draw_actions(winwid); - if ((opt.mode == MODE_ZOOM) && !alias) + } else if ((opt.mode == MODE_ZOOM) && !antialias) feh_draw_zoom(winwid); + XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap); XClearWindow(disp, winwid->win); - D_RETURN_(4); + return; } void winwidget_render_image_cached(winwidget winwid) @@ -548,6 +632,8 @@ void winwidget_render_image_cached(winwidget winwid) feh_draw_filename(winwid); if (opt.draw_actions) feh_draw_actions(winwid); + if (opt.draw_info && opt.info_cmd) + feh_draw_info(winwid); XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap); XClearWindow(disp, winwid->win); } @@ -556,16 +642,17 @@ double feh_calc_needed_zoom(double *zoom, int orig_w, int orig_h, int dest_w, in { double ratio = 0.0; - D_ENTER(4); - ratio = ((double) orig_w / orig_h) / ((double) dest_w / dest_h); + if (opt.zoom_mode == ZOOM_MODE_FILL) + ratio = 1.0 / ratio; + if (ratio > 1.0) *zoom = ((double) dest_w / orig_w); else *zoom = ((double) dest_h / orig_h); - D_RETURN(4, ratio); + return(ratio); } Pixmap feh_create_checks(void) @@ -573,47 +660,28 @@ Pixmap feh_create_checks(void) static Pixmap checks_pmap = None; Imlib_Image checks = NULL; - D_ENTER(4); if (checks_pmap == None) { - int onoff, x, y; - checks = imlib_create_image(16, 16); if (!checks) eprintf("Unable to create a teeny weeny imlib image. I detect problems"); - if (strncmp(opt.image_bg, "white", 5) == 0) - gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, 255, 255, 255, 255); - else if (strncmp(opt.image_bg, "black", 5) == 0) - gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, 0, 0, 0, 255); - else { - for (y = 0; y < 16; y += 8) { - onoff = (y / 8) & 0x1; - for (x = 0; x < 16; x += 8) { - if (onoff) - gib_imlib_image_fill_rectangle(checks, x, y, 8, 8, 144, 144, 144, 255); - else - gib_imlib_image_fill_rectangle(checks, x, y, 8, 8, 100, 100, 100, 255); - onoff++; - if (onoff == 2) - onoff = 0; - } - } + if (!opt.image_bg || !strcmp(opt.image_bg, "default") || !strcmp(opt.image_bg, "checks")) { + gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, 144, 144, 144, 255); + gib_imlib_image_fill_rectangle(checks, 0, 0, 8, 8, 100, 100, 100, 255); + gib_imlib_image_fill_rectangle(checks, 8, 8, 8, 8, 100, 100, 100, 255); + } else { + XColor color; + Colormap cmap = DefaultColormap(disp, DefaultScreen(disp)); + XAllocNamedColor(disp, cmap, (char*) opt.image_bg, &color, &color); + gib_imlib_image_fill_rectangle(checks, 0, 0, 16, 16, color.red, color.green, color.blue, 255); } checks_pmap = XCreatePixmap(disp, root, 16, 16, depth); gib_imlib_render_image_on_drawable(checks_pmap, checks, 0, 0, 1, 0, 0); gib_imlib_free_image_and_decache(checks); } - D_RETURN(4, checks_pmap); -} - -void winwidget_clear_background(winwidget w) -{ - D_ENTER(4); - XSetWindowBackgroundPixmap(disp, w->win, feh_create_checks()); - /* XClearWindow(disp, w->win); */ - D_RETURN_(4); + return(checks_pmap); } void feh_draw_checks(winwidget win) @@ -621,19 +689,17 @@ void feh_draw_checks(winwidget win) static GC gc = None; XGCValues gcval; - D_ENTER(4); if (gc == None) { gcval.tile = feh_create_checks(); gcval.fill_style = FillTiled; gc = XCreateGC(disp, win->win, GCTile | GCFillStyle, &gcval); } XFillRectangle(disp, win->bg_pmap, gc, 0, 0, win->w, win->h); - D_RETURN_(4); + return; } void winwidget_destroy_xwin(winwidget winwid) { - D_ENTER(4); if (winwid->win) { winwidget_unregister(winwid); XDestroyWindow(disp, winwid->win); @@ -642,177 +708,295 @@ void winwidget_destroy_xwin(winwidget winwid) XFreePixmap(disp, winwid->bg_pmap); winwid->bg_pmap = None; } - D_RETURN_(4); + return; } void winwidget_destroy(winwidget winwid) { - D_ENTER(4); +#ifdef HAVE_INOTIFY + winwidget_inotify_remove(winwid); +#endif + if (opt.reload > 0 && opt.multiwindow) { + feh_remove_timer_by_data(winwid); + } winwidget_destroy_xwin(winwid); if (winwid->name) free(winwid->name); - if ((winwid->type == WIN_TYPE_ABOUT) && winwid->file) { - feh_file_free(FEH_FILE(winwid->file->data)); - free(winwid->file); - } if (winwid->gc) XFreeGC(disp, winwid->gc); if (winwid->im) gib_imlib_free_image_and_decache(winwid->im); free(winwid); - D_RETURN_(4); + return; +} + +#ifdef HAVE_INOTIFY +void winwidget_inotify_remove(winwidget winwid) +{ + if (winwid->inotify_wd >= 0) { + D(("Removing inotify watch\n")); + if (inotify_rm_watch(opt.inotify_fd, winwid->inotify_wd)) + weprintf("inotify_rm_watch failed:"); + winwid->inotify_wd = -1; + } +} +#endif + +#ifdef HAVE_INOTIFY +void winwidget_inotify_add(winwidget winwid, feh_file * file) +{ + if (opt.auto_reload && !path_is_url(file->filename)) { + D(("Adding inotify watch for %s\n", file->filename)); + char dir[PATH_MAX]; + feh_file_dirname(dir, file, PATH_MAX); + + /* + * Handle files without directory part, e.g. "feh somefile.jpg". + * These always reside in the current directory. + */ + if (dir[0] == '\0') { + dir[0] = '.'; + dir[1] = '\0'; + } + winwid->inotify_wd = inotify_add_watch(opt.inotify_fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO); + if (winwid->inotify_wd < 0) + weprintf("inotify_add_watch failed:"); + } } +#endif + +#ifdef HAVE_INOTIFY +#define INOTIFY_BUFFER_LEN (1024 * (sizeof (struct inotify_event)) + 16) +void feh_event_handle_inotify(void) +{ + D(("Received inotify events\n")); + char buf[INOTIFY_BUFFER_LEN]; + int i = 0; + int len = read (opt.inotify_fd, buf, INOTIFY_BUFFER_LEN); + if (len < 0) { + if (errno != EINTR) + eprintf("inotify event read failed"); + } else if (!len) + eprintf("inotify event read failed"); + while (i < len) { + struct inotify_event *event; + event = (struct inotify_event *) &buf[i]; + for (int j = 0; j < window_num; j++) { + if(windows[j]->inotify_wd == event->wd) { + if (event->mask & IN_IGNORED) { + D(("inotify watch was implicitly removed\n")); + windows[j]->inotify_wd = -1; + } else if (event->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) { + if (strcmp(event->name, FEH_FILE(windows[j]->file->data)->name) == 0) { + D(("inotify says file changed\n")); + feh_reload_image(windows[j], 0, 0); + } + } + break; + } + } + i += sizeof(struct inotify_event) + event->len; + } +} +#endif void winwidget_destroy_all(void) { int i; - D_ENTER(4); /* Have to DESCEND the list here, 'cos of the way _unregister works */ for (i = window_num - 1; i >= 0; i--) winwidget_destroy(windows[i]); - D_RETURN_(4); + return; } -void winwidget_rerender_all(int resize, int alias) +void winwidget_rerender_all(int resize) { int i; - D_ENTER(4); /* Have to DESCEND the list here, 'cos of the way _unregister works */ for (i = window_num - 1; i >= 0; i--) - winwidget_render_image(windows[i], resize, alias); - D_RETURN_(4); + winwidget_render_image(windows[i], resize, 0); + return; } winwidget winwidget_get_first_window_of_type(unsigned int type) { int i; - D_ENTER(4); for (i = 0; i < window_num; i++) if (windows[i]->type == type) - D_RETURN(4, windows[i]); - D_RETURN(4, NULL); + return(windows[i]); + return(NULL); } int winwidget_loadimage(winwidget winwid, feh_file * file) { - D_ENTER(4); - D(4, ("filename %s\n", file->filename)); - D_RETURN(4, feh_load_image(&(winwid->im), file)); + D(("filename %s\n", file->filename)); +#ifdef HAVE_INOTIFY + winwidget_inotify_remove(winwid); +#endif + int res = feh_load_image(&(winwid->im), file); +#ifdef HAVE_INOTIFY + if (res) { + winwidget_inotify_add(winwid, file); + } +#endif + return(res); } void winwidget_show(winwidget winwid) { XEvent ev; - D_ENTER(4); - /* feh_debug_print_winwid(winwid); */ if (!winwid->visible) { XMapWindow(disp, winwid->win); if (opt.full_screen) XMoveWindow(disp, winwid->win, 0, 0); /* wait for the window to map */ - D(4, ("Waiting for window to map\n")); + D(("Waiting for window to map\n")); XMaskEvent(disp, StructureNotifyMask, &ev); - D(4, ("Window mapped\n")); + winwidget_get_geometry(winwid, NULL); + + /* Unfortunately, StructureNotifyMask does not only mask + * the events of type MapNotify (which we want to mask here) + * but also such of type ConfigureNotify (and others, see + * https://tronche.com/gui/x/xlib/events/processing-overview.html), + * which should be handled, especially on tiling wm's. To + * remedy this, the handler is executed explicitly: + */ + if (ev.type == ConfigureNotify) + feh_event_handle_ConfigureNotify(&ev); + D(("Window mapped\n")); winwid->visible = 1; } - D_RETURN_(4); -} - -int winwidget_count(void) -{ - D_ENTER(4); - D_RETURN(4, window_num); + return; } void winwidget_move(winwidget winwid, int x, int y) { - D_ENTER(4); if (winwid && ((winwid->x != x) || (winwid->y != y))) { - winwid->x = x; - winwid->y = y; winwid->x = (x > scr->width) ? scr->width : x; winwid->y = (y > scr->height) ? scr->height : y; XMoveWindow(disp, winwid->win, winwid->x, winwid->y); XFlush(disp); } else { - D(4, ("No move actually needed\n")); + D(("No move actually needed\n")); } - D_RETURN_(4); + return; } -void winwidget_resize(winwidget winwid, int w, int h) +void winwidget_resize(winwidget winwid, int w, int h, int force_resize) { - Window ignored_window; XWindowAttributes attributes; - int tc_x, tc_y; + int tc_x, tc_y, px, py; + int scr_width = scr->width; + int scr_height = scr->height; + + /* discarded */ + Window dw; + int di, i; + unsigned int du; + + XGetWindowAttributes(disp, winwid->win, &attributes); + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + xinerama_screen = 0; + XQueryPointer(disp, root, &dw, &dw, &px, &py, &di, &di, &du); + for (i = 0; i < num_xinerama_screens; i++) { + if (XY_IN_RECT(px, py, + xinerama_screens[i].x_org, + xinerama_screens[i].y_org, + xinerama_screens[i].width, + xinerama_screens[i].height)) { + xinerama_screen = i; + break; + } + + } + if (opt.xinerama_index >= 0) + xinerama_screen = opt.xinerama_index; + + scr_width = xinerama_screens[xinerama_screen].width; + scr_height = xinerama_screens[xinerama_screen].height; + } +#endif + + + D((" x %d y %d w %d h %d\n", attributes.x, attributes.y, winwid->w, + winwid->h)); - D_ENTER(4); - if (opt.geom_flags) { + if ((opt.geom_flags & (WidthValue | HeightValue)) && !force_resize) { winwid->had_resize = 1; return; } if (winwid && ((winwid->w != w) || (winwid->h != h))) { - D(4, ("Really doing a resize\n")); - /* winwidget_clear_background(winwid); */ if (opt.screen_clip) { - winwid->w = (w > scr->width) ? scr->width : w; - winwid->h = (h > scr->height) ? scr->height : h; + double required_zoom = winwid->zoom; + if (opt.scale_down && !opt.keep_zoom_vp) { + int max_w = (w > scr_width) ? scr_width : w; + int max_h = (h > scr_height) ? scr_height : h; + feh_calc_needed_zoom(&required_zoom, winwid->im_w, winwid->im_h, max_w, max_h); + } + int desired_w = winwid->im_w * required_zoom; + int desired_h = winwid->im_h * required_zoom; + winwid->w = (desired_w > scr_width) ? scr_width : desired_w; + winwid->h = (desired_h > scr_height) ? scr_height : desired_h; } - /* XResizeWindow(disp, winwid->win, winwid->w, winwid->h); */ - XGetWindowAttributes(disp, winwid->win, &attributes); - XTranslateCoordinates(disp, winwid->win, attributes.root, - -attributes.border_width - - attributes.x, - -attributes.border_width - attributes.y, &tc_x, &tc_y, &ignored_window); - winwid->x = tc_x; - winwid->y = tc_y; - XMoveResizeWindow(disp, winwid->win, tc_x, tc_y, winwid->w, winwid->h); + if (winwid->full_screen) { + XTranslateCoordinates(disp, winwid->win, attributes.root, + -attributes.border_width - attributes.x, + -attributes.border_width - attributes.y, &tc_x, &tc_y, &dw); + winwid->x = tc_x; + winwid->y = tc_y; + XMoveResizeWindow(disp, winwid->win, tc_x, tc_y, winwid->w, winwid->h); + } else + XResizeWindow(disp, winwid->win, winwid->w, winwid->h); winwid->had_resize = 1; XFlush(disp); -#ifdef HAVE_LIBXINERAMA - if (opt.xinerama && xinerama_screens) { - int i; - - for (i = 0; i < num_xinerama_screens; i++) { - xinerama_screen = 0; - if (XY_IN_RECT(winwid->x, winwid->y, - xinerama_screens[i].x_org, - xinerama_screens[i].y_org, - xinerama_screens[i].width, xinerama_screens[i].height)) { - xinerama_screen = i; - break; - } - - } + /* + * Note: + * While calling winwidget_get_geometry(winwid, NULL); at this point + * would help alleviate flashing issues that can occur when feh has + * to render a window two times in a row, or renders the initial image + * with a resolution that differs from the one that is needed to + * accomodate the resize. + * + * However, it would also break --scale-down in floating setups. As + * flashing is less annoying, we do not call winwidget_get_geometry. + * here. + */ + + if (force_resize && (opt.geom_flags & (WidthValue | HeightValue)) + && (winwid->type != WIN_TYPE_THUMBNAIL)) { + opt.geom_w = winwid->w; + opt.geom_h = winwid->h; } -#endif /* HAVE_LIBXINERAMA */ + + D(("-> x %d y %d w %d h %d\n", winwid->x, winwid->y, winwid->w, + winwid->h)); } else { - D(4, ("No resize actually needed\n")); + D(("No resize actually needed\n")); } - D_RETURN_(4); + return; } void winwidget_hide(winwidget winwid) { - D_ENTER(4); XUnmapWindow(disp, winwid->win); winwid->visible = 0; - D_RETURN_(4); + return; } static void winwidget_register(winwidget win) { - D_ENTER(4); - D(5, ("window %p\n", win)); + D(("window %p\n", win)); window_num++; if (windows) windows = erealloc(windows, window_num * sizeof(winwidget)); @@ -821,14 +1005,13 @@ static void winwidget_register(winwidget win) windows[window_num - 1] = win; XSaveContext(disp, win->win, xid_context, (XPointer) win); - D_RETURN_(4); + return; } static void winwidget_unregister(winwidget win) { int i, j; - D_ENTER(4); for (i = 0; i < window_num; i++) { if (windows[i] == win) { for (j = i; j < window_num - 1; j++) @@ -843,17 +1026,16 @@ static void winwidget_unregister(winwidget win) } } XDeleteContext(disp, win->win, xid_context); - D_RETURN_(4); + return; } winwidget winwidget_get_from_window(Window win) { winwidget ret = NULL; - D_ENTER(4); if (XFindContext(disp, win, xid_context, (XPointer *) & ret) != XCNOENT) - D_RETURN(4, ret); - D_RETURN(4, NULL); + return(ret); + return(NULL); } void winwidget_rename(winwidget winwid, char *newname) @@ -861,7 +1043,6 @@ void winwidget_rename(winwidget winwid, char *newname) /* newname == NULL -> update current title */ char *p_str; - D_ENTER(4); if (newname == NULL) newname = estrdup(winwid->name ? winwid->name : ""); if (winwid->name) @@ -881,18 +1062,18 @@ void winwidget_rename(winwidget winwid, char *newname) *p_str = '\0'; winwidget_update_title(winwid); - D_RETURN_(4); + return; } void winwidget_free_image(winwidget w) { - D_ENTER(4); - if (w->im) - gib_imlib_free_image_and_decache(w->im); + if (w->im) { + gib_imlib_free_image(w->im); + } w->im = NULL; w->im_w = 0; w->im_h = 0; - D_RETURN_(4); + return; } void feh_debug_print_winwid(winwidget w) @@ -912,13 +1093,44 @@ void feh_debug_print_winwid(winwidget w) void winwidget_reset_image(winwidget winwid) { - D_ENTER(4); - winwid->zoom = 1.0; - winwid->im_x = 0; - winwid->im_y = 0; + if (!opt.keep_zoom_vp) { + winwid->zoom = 1.0; + winwid->old_zoom = 1.0; + winwid->im_x = 0; + winwid->im_y = 0; + } winwid->im_angle = 0.0; winwid->has_rotated = 0; - D_RETURN_(4); + return; +} + +void winwidget_center_image(winwidget winwid) +{ + int scr_width, scr_height; + + scr_width = scr->width; + scr_height = scr->height; + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + scr_width = xinerama_screens[xinerama_screen].width; + scr_height = xinerama_screens[xinerama_screen].height; + } +#endif /* HAVE_LIBXINERAMA */ + + if (winwid->full_screen) { + winwid->im_x = (scr_width - lround(winwid->im_w * winwid->zoom)) >> 1; + winwid->im_y = (scr_height - lround(winwid->im_h * winwid->zoom)) >> 1; + } else { + if (opt.geom_flags & WidthValue) + winwid->im_x = ((int)opt.geom_w - lround(winwid->im_w * winwid->zoom)) >> 1; + else + winwid->im_x = (int) (winwid->w - lround(winwid->im_w * winwid->zoom)) >> 1; + if (opt.geom_flags & HeightValue) + winwid->im_y = ((int)opt.geom_h - lround(winwid->im_h * winwid->zoom)) >> 1; + else + winwid->im_y = (int) (winwid->h - lround(winwid->im_h * winwid->zoom)) >> 1; + } } void winwidget_sanitise_offsets(winwidget winwid) @@ -926,8 +1138,6 @@ void winwidget_sanitise_offsets(winwidget winwid) int far_left, far_top; int min_x, max_x, max_y, min_y; - D_ENTER(4); - far_left = winwid->w - (winwid->im_w * winwid->zoom); far_top = winwid->h - (winwid->im_h * winwid->zoom); @@ -954,16 +1164,15 @@ void winwidget_sanitise_offsets(winwidget winwid) if (winwid->im_y < min_y) winwid->im_y = min_y; - D_RETURN_(4); + return; } void winwidget_size_to_image(winwidget winwid) { - D_ENTER(4); - winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom); + winwidget_resize(winwid, winwid->im_w * winwid->zoom, winwid->im_h * winwid->zoom, 1); winwid->im_x = winwid->im_y = 0; - winwidget_render_image(winwid, 0, 1); - D_RETURN_(4); + winwidget_render_image(winwid, 0, 0); + return; } void winwidget_set_pointer(winwidget winwid, int visible) @@ -987,26 +1196,26 @@ void winwidget_set_pointer(winwidget winwid, int visible) int winwidget_get_width(winwidget winwid) { int rect[4]; - D_ENTER(4); winwidget_get_geometry(winwid, rect); - D_RETURN(4, rect[2]); + return(rect[2]); } int winwidget_get_height(winwidget winwid) { int rect[4]; - D_ENTER(4); winwidget_get_geometry(winwid, rect); - D_RETURN(4, rect[3]); + return(rect[3]); } void winwidget_get_geometry(winwidget winwid, int *rect) { unsigned int bw, bp; Window child; - D_ENTER(4); + + int inner_rect[4]; + if (!rect) - return; + rect = inner_rect; XGetGeometry(disp, winwid->win, &root, &(rect[0]), &(rect[1]), (unsigned int *)&(rect[2]), (unsigned int *)&(rect[3]), &bw, &bp); @@ -1018,7 +1227,7 @@ void winwidget_get_geometry(winwidget winwid, int *rect) winwid->y = rect[1]; winwid->w = rect[2]; winwid->h = rect[3]; - D_RETURN_(4); + return; } void winwidget_show_menu(winwidget winwid) @@ -1028,11 +1237,7 @@ void winwidget_show_menu(winwidget winwid) Window r; XQueryPointer(disp, winwid->win, &r, &r, &x, &y, &b, &b, &c); - if (winwid->type == WIN_TYPE_ABOUT) { - if (!menu_about_win) - feh_menu_init_about_win(); - feh_menu_show_at_xy(menu_about_win, winwid, x, y); - } else if (winwid->type == WIN_TYPE_SINGLE) { + if (winwid->type == WIN_TYPE_SINGLE) { if (!menu_single_win) feh_menu_init_single_win(); feh_menu_show_at_xy(menu_single_win, winwid, x, y); |