From 21c4996c71d76b01a4333ef9905589b3fffa687d Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Fri, 5 Feb 2010 17:52:34 +0100 Subject: Initial commit (upstream 1.3.4) --- src/Makefile.am | 43 + src/Makefile.in | 496 +++++++++++ src/about.png | Bin 0 -> 61171 bytes src/collage.c | 240 ++++++ src/config.h.in | 37 + src/debug.h | 115 +++ src/events.c | 688 +++++++++++++++ src/events.h | 37 + src/feh.h | 178 ++++ src/feh_png.c | 214 +++++ src/feh_png.h | 39 + src/filelist.c | 611 +++++++++++++ src/filelist.h | 89 ++ src/getopt.c | 1045 ++++++++++++++++++++++ src/getopt.h | 136 +++ src/getopt1.c | 187 ++++ src/imlib.c | 1292 ++++++++++++++++++++++++++++ src/index.c | 611 +++++++++++++ src/ipc.c | 94 ++ src/ipc.h | 57 ++ src/jpegint.h | 392 +++++++++ src/keyevents.c | 445 ++++++++++ src/list.c | 116 +++ src/main.c | 219 +++++ src/md5.c | 392 +++++++++ src/md5.h | 94 ++ src/menu.c | 2144 ++++++++++++++++++++++++++++++++++++++++++++++ src/menu.h | 192 +++++ src/menubg_aluminium.png | Bin 0 -> 1862 bytes src/menubg_aqua.png | Bin 0 -> 5325 bytes src/menubg_black.png | Bin 0 -> 1716 bytes src/menubg_britney.png | Bin 0 -> 34250 bytes src/menubg_brushed.png | Bin 0 -> 8649 bytes src/menubg_chrome.png | Bin 0 -> 10995 bytes src/menubg_default.png | Bin 0 -> 209 bytes src/menubg_pastel.png | Bin 0 -> 3676 bytes src/menubg_sky.png | Bin 0 -> 1928 bytes src/menubg_wood.png | Bin 0 -> 7873 bytes src/multiwindow.c | 78 ++ src/options.c | 1203 ++++++++++++++++++++++++++ src/options.h | 137 +++ src/slideshow.c | 582 +++++++++++++ src/stamp-h.in | 1 + src/structs.h | 37 + src/support.c | 658 ++++++++++++++ src/support.h | 56 ++ src/thumbnail.c | 992 +++++++++++++++++++++ src/thumbnail.h | 86 ++ src/timers.c | 172 ++++ src/timers.h | 49 ++ src/transupp.c | 1233 ++++++++++++++++++++++++++ src/transupp.h | 135 +++ src/ttfonts/Makefile.am | 13 + src/ttfonts/Makefile.in | 216 +++++ src/ttfonts/black.style | 4 + src/ttfonts/menu.style | 4 + src/ttfonts/yudit.ttf | Bin 0 -> 57708 bytes src/utils.c | 248 ++++++ src/utils.h | 90 ++ src/winwidget.c | 1058 +++++++++++++++++++++++ src/winwidget.h | 158 ++++ 61 files changed, 17413 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/about.png create mode 100644 src/collage.c create mode 100644 src/config.h.in create mode 100644 src/debug.h create mode 100644 src/events.c create mode 100644 src/events.h create mode 100644 src/feh.h create mode 100644 src/feh_png.c create mode 100644 src/feh_png.h create mode 100644 src/filelist.c create mode 100644 src/filelist.h create mode 100644 src/getopt.c create mode 100644 src/getopt.h create mode 100644 src/getopt1.c create mode 100644 src/imlib.c create mode 100644 src/index.c create mode 100644 src/ipc.c create mode 100644 src/ipc.h create mode 100644 src/jpegint.h create mode 100644 src/keyevents.c create mode 100644 src/list.c create mode 100644 src/main.c create mode 100644 src/md5.c create mode 100644 src/md5.h create mode 100644 src/menu.c create mode 100644 src/menu.h create mode 100644 src/menubg_aluminium.png create mode 100644 src/menubg_aqua.png create mode 100644 src/menubg_black.png create mode 100644 src/menubg_britney.png create mode 100644 src/menubg_brushed.png create mode 100644 src/menubg_chrome.png create mode 100644 src/menubg_default.png create mode 100644 src/menubg_pastel.png create mode 100644 src/menubg_sky.png create mode 100644 src/menubg_wood.png create mode 100644 src/multiwindow.c create mode 100644 src/options.c create mode 100644 src/options.h create mode 100644 src/slideshow.c create mode 100644 src/stamp-h.in create mode 100644 src/structs.h create mode 100644 src/support.c create mode 100644 src/support.h create mode 100644 src/thumbnail.c create mode 100644 src/thumbnail.h create mode 100644 src/timers.c create mode 100644 src/timers.h create mode 100644 src/transupp.c create mode 100644 src/transupp.h create mode 100644 src/ttfonts/Makefile.am create mode 100644 src/ttfonts/Makefile.in create mode 100644 src/ttfonts/black.style create mode 100644 src/ttfonts/menu.style create mode 100644 src/ttfonts/yudit.ttf create mode 100644 src/utils.c create mode 100644 src/utils.h create mode 100644 src/winwidget.c create mode 100644 src/winwidget.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..5b78eec --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,43 @@ +# A list of all the files in the current directory which can be regenerated +MAINTAINERCLEANFILES = Makefile.in + +LDFLAGS = -L/usr/X11R6/lib -L/usr/local/lib -lz -lpng +INCLUDES = -I/usr/X11R6/include $(X_CFLAGS) \ + -I$(prefix)/include -I$(includedir) \ + -I. -I$(top_srcdir)/src \ + -I/usr/local/include/libpng \ + -DPREFIX=\""$(prefix)"\" \ + @IMLIB_CFLAGS@ @GIBLIB_CFLAGS@ +LIBOBJS = @LIBOBJS@ + +bin_PROGRAMS = feh +feh_SOURCES = main.c getopt.c getopt1.c getopt.h feh.h \ +options.c options.h winwidget.c winwidget.h menu.c menu.h structs.h \ +filelist.c filelist.h multiwindow.c imlib.c index.c slideshow.c \ +utils.c utils.h keyevents.c timers.c timers.h list.c collage.c debug.h \ +events.c events.h support.c support.h transupp.c transupp.h \ +thumbnail.c thumbnail.h ipc.c ipc.h md5.c md5.h feh_png.c feh_png.h \ +jpegint.h + +feh_LDADD = -lX11 -lz -lpng @IMLIB_LIBS@ @GIBLIB_LIBS@ + +images_DATA = about.png menubg_default.png menubg_sky.png \ +menubg_chrome.png menubg_brushed.png \ +menubg_pastel.png menubg_aluminium.png menubg_aqua.png \ +menubg_wood.png menubg_britney.png menubg_black.png + +imagesdir=$(prefix)/share/feh/images +EXTRA_DIST = $(images_DATA) + +test: + @for i in test[0-9]* ; do \ + if test -x $$i ; then \ + echo "Executing test script $$i..." ; \ + ./$$i ; \ + fi ; \ + done + +testclean: + -rm -f _test*.jpg + +SUBDIRS = ttfonts diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..7cf64b2 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,496 @@ +# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# A list of all the files in the current directory which can be regenerated + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +CC = @CC@ +GIBLIB_CFLAGS = @GIBLIB_CFLAGS@ +GIBLIB_LIBS = @GIBLIB_LIBS@ +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_CONFIG = @GTK_CONFIG@ +GTK_LIBS = @GTK_LIBS@ +HAVE_LIB = @HAVE_LIB@ +HAVE_XINERAMA = @HAVE_XINERAMA@ +IMLIB_CFLAGS = @IMLIB_CFLAGS@ +IMLIB_LIBS = @IMLIB_LIBS@ +LIB = @LIB@ +LTLIB = @LTLIB@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +UP = @UP@ +VERSION = @VERSION@ +XINERAMA_LIBS = @XINERAMA_LIBS@ + +MAINTAINERCLEANFILES = Makefile.in + +LDFLAGS = -L/usr/X11R6/lib -L/usr/local/lib -lz -lpng +INCLUDES = -I/usr/X11R6/include $(X_CFLAGS) -I$(prefix)/include -I$(includedir) -I. -I$(top_srcdir)/src -I/usr/local/include/libpng -DPREFIX=\""$(prefix)"\" @IMLIB_CFLAGS@ @GIBLIB_CFLAGS@ + +LIBOBJS = @LIBOBJS@ + +bin_PROGRAMS = feh +feh_SOURCES = main.c getopt.c getopt1.c getopt.h feh.h options.c options.h winwidget.c winwidget.h menu.c menu.h structs.h filelist.c filelist.h multiwindow.c imlib.c index.c slideshow.c utils.c utils.h keyevents.c timers.c timers.h list.c collage.c debug.h events.c events.h support.c support.h transupp.c transupp.h thumbnail.c thumbnail.h ipc.c ipc.h md5.c md5.h feh_png.c feh_png.h jpegint.h + + +feh_LDADD = -lX11 -lz -lpng @IMLIB_LIBS@ @GIBLIB_LIBS@ + +images_DATA = about.png menubg_default.png menubg_sky.png menubg_chrome.png menubg_brushed.png menubg_pastel.png menubg_aluminium.png menubg_aqua.png menubg_wood.png menubg_britney.png menubg_black.png + + +imagesdir = $(prefix)/share/feh/images +EXTRA_DIST = $(images_DATA) + +SUBDIRS = ttfonts +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +PROGRAMS = $(bin_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) -I. +CPPFLAGS = @CPPFLAGS@ +LIBS = @LIBS@ +feh_OBJECTS = main.o getopt.o getopt1.o options.o winwidget.o menu.o \ +filelist.o multiwindow.o imlib.o index.o slideshow.o utils.o \ +keyevents.o timers.o list.o collage.o events.o support.o transupp.o \ +thumbnail.o ipc.o md5.o feh_png.o +feh_DEPENDENCIES = +feh_LDFLAGS = +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DATA = $(images_DATA) + +DIST_COMMON = ./stamp-h.in Makefile.am Makefile.in config.h.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(feh_SOURCES) +OBJECTS = $(feh_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.ac $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --foreign --include-deps src/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +config.h: stamp-h + @if test ! -f $@; then \ + rm -f stamp-h; \ + $(MAKE) stamp-h; \ + else :; fi +stamp-h: $(srcdir)/config.h.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES= CONFIG_HEADERS=src/config.h \ + $(SHELL) ./config.status + @echo timestamp > stamp-h 2> /dev/null +$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@$(srcdir)/stamp-h.in + @if test ! -f $@; then \ + rm -f $(srcdir)/stamp-h.in; \ + $(MAKE) $(srcdir)/stamp-h.in; \ + else :; fi +$(srcdir)/stamp-h.in: $(top_srcdir)/configure.ac $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOHEADER) + @echo timestamp > $(srcdir)/stamp-h.in 2> /dev/null + +mostlyclean-hdr: + +clean-hdr: + +distclean-hdr: + -rm -f config.h + +maintainer-clean-hdr: + +mostlyclean-binPROGRAMS: + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +distclean-binPROGRAMS: + +maintainer-clean-binPROGRAMS: + +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(bin_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +feh: $(feh_OBJECTS) $(feh_DEPENDENCIES) + @rm -f feh + $(LINK) $(feh_LDFLAGS) $(feh_OBJECTS) $(feh_LDADD) $(LIBS) + +install-imagesDATA: $(images_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(imagesdir) + @list='$(images_DATA)'; for p in $$list; do \ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(imagesdir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(imagesdir)/$$p; \ + else if test -f $$p; then \ + echo " $(INSTALL_DATA) $$p $(DESTDIR)$(imagesdir)/$$p"; \ + $(INSTALL_DATA) $$p $(DESTDIR)$(imagesdir)/$$p; \ + fi; fi; \ + done + +uninstall-imagesDATA: + @$(NORMAL_UNINSTALL) + list='$(images_DATA)'; for p in $$list; do \ + rm -f $(DESTDIR)$(imagesdir)/$$p; \ + done + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. + +@SET_MAKE@ + +all-recursive install-data-recursive install-exec-recursive \ +installdirs-recursive install-recursive uninstall-recursive \ +check-recursive installcheck-recursive info-recursive dvi-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ + rev="$$subdir $$rev"; \ + test "$$subdir" != "." || dot_seen=yes; \ + done; \ + test "$$dot_seen" = "no" && rev=". $$rev"; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)config.h.in$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags -o $$here/TAGS $(ETAGS_ARGS) $$tags config.h.in $$unique $(LISP)) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + chmod 777 $(distdir)/$$subdir; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(top_distdir) distdir=../$(distdir)/$$subdir distdir) \ + || exit 1; \ + fi; \ + done +collage.o: collage.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h winwidget.h filelist.h options.h +events.o: events.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h filelist.h winwidget.h timers.h options.h \ + events.h thumbnail.h +feh_png.o: feh_png.c feh_png.h feh.h config.h getopt.h structs.h menu.h \ + ipc.h utils.h debug.h +filelist.o: filelist.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h filelist.h options.h +getopt.o: getopt.c config.h +getopt1.o: getopt1.c config.h getopt.h +imlib.o: imlib.c feh.h config.h getopt.h structs.h menu.h ipc.h utils.h \ + debug.h filelist.h winwidget.h options.h transupp.h +index.o: index.c feh.h config.h getopt.h structs.h menu.h ipc.h utils.h \ + debug.h filelist.h winwidget.h options.h +ipc.o: ipc.c feh.h config.h getopt.h structs.h menu.h ipc.h utils.h \ + debug.h options.h +keyevents.o: keyevents.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h thumbnail.h filelist.h winwidget.h options.h +list.o: list.c feh.h config.h getopt.h structs.h menu.h ipc.h utils.h \ + debug.h filelist.h options.h +main.o: main.c feh.h config.h getopt.h structs.h menu.h ipc.h utils.h \ + debug.h filelist.h winwidget.h timers.h options.h events.h \ + support.h +md5.o: md5.c md5.h +menu.o: menu.c feh.h config.h getopt.h structs.h menu.h ipc.h utils.h \ + debug.h support.h thumbnail.h filelist.h winwidget.h options.h +multiwindow.o: multiwindow.c feh.h config.h getopt.h structs.h menu.h \ + ipc.h utils.h debug.h winwidget.h timers.h filelist.h options.h +options.o: options.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h filelist.h options.h +slideshow.o: slideshow.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h filelist.h timers.h winwidget.h options.h +support.o: support.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h filelist.h options.h support.h +thumbnail.o: thumbnail.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h filelist.h winwidget.h options.h thumbnail.h \ + md5.h feh_png.h +timers.o: timers.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h options.h timers.h +transupp.o: transupp.c transupp.h +utils.o: utils.c feh.h config.h getopt.h structs.h menu.h ipc.h utils.h \ + debug.h options.h +winwidget.o: winwidget.c feh.h config.h getopt.h structs.h menu.h ipc.h \ + utils.h debug.h filelist.h winwidget.h options.h + +info-am: +info: info-recursive +dvi-am: +dvi: dvi-recursive +check-am: all-am +check: check-recursive +installcheck-am: +installcheck: installcheck-recursive +all-recursive-am: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +install-exec-am: install-binPROGRAMS +install-exec: install-exec-recursive + +install-data-am: install-imagesDATA +install-data: install-data-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-recursive +uninstall-am: uninstall-binPROGRAMS uninstall-imagesDATA +uninstall: uninstall-recursive +all-am: Makefile $(PROGRAMS) $(DATA) config.h +all-redirect: all-recursive-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: installdirs-recursive +installdirs-am: + $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(imagesdir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +mostlyclean-am: mostlyclean-hdr mostlyclean-binPROGRAMS \ + mostlyclean-compile mostlyclean-tags \ + mostlyclean-generic + +mostlyclean: mostlyclean-recursive + +clean-am: clean-hdr clean-binPROGRAMS clean-compile clean-tags \ + clean-generic mostlyclean-am + +clean: clean-recursive + +distclean-am: distclean-hdr distclean-binPROGRAMS distclean-compile \ + distclean-tags distclean-generic clean-am + +distclean: distclean-recursive + +maintainer-clean-am: maintainer-clean-hdr maintainer-clean-binPROGRAMS \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-recursive + +.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \ +mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \ +maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile uninstall-imagesDATA install-imagesDATA \ +install-data-recursive uninstall-data-recursive install-exec-recursive \ +uninstall-exec-recursive installdirs-recursive uninstalldirs-recursive \ +all-recursive check-recursive installcheck-recursive info-recursive \ +dvi-recursive mostlyclean-recursive distclean-recursive clean-recursive \ +maintainer-clean-recursive tags tags-recursive mostlyclean-tags \ +distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ +dvi-am dvi check check-am installcheck-am installcheck all-recursive-am \ +install-exec-am install-exec install-data-am install-data install-am \ +install uninstall-am uninstall all-redirect all-am all installdirs-am \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +test: + @for i in test[0-9]* ; do \ + if test -x $$i ; then \ + echo "Executing test script $$i..." ; \ + ./$$i ; \ + fi ; \ + done + +testclean: + -rm -f _test*.jpg + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/about.png b/src/about.png new file mode 100644 index 0000000..5aaaf17 Binary files /dev/null and b/src/about.png differ diff --git a/src/collage.c b/src/collage.c new file mode 100644 index 0000000..3377e84 --- /dev/null +++ b/src/collage.c @@ -0,0 +1,240 @@ +/* collage.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "winwidget.h" +#include "filelist.h" +#include "options.h" + +void +init_collage_mode(void) +{ + Imlib_Image im_main; + Imlib_Image im_temp; + int ww, hh, www, hhh, xxx, yyy; + int w = 800, h = 600; + int bg_w = 0, bg_h = 0; + winwidget winwid = NULL; + Imlib_Image bg_im = NULL, im_thumb = NULL; + feh_file *file = NULL; + unsigned char trans_bg = 0; + gib_list *l, *last = NULL; + char *s; + + D_ENTER(4); + + mode = "collage"; + + /* Use bg image dimensions for default size */ + if (opt.bg && opt.bg_file) + { + if (!strcmp(opt.bg_file, "trans")) + trans_bg = 1; + else + { + + D(4,("Time to apply a background to blend onto\n")); + if (feh_load_image_char(&bg_im, opt.bg_file) != 0) + { + bg_w = gib_imlib_image_get_width(bg_im); + bg_h = gib_imlib_image_get_height(bg_im); + } + } + } + + if (!opt.limit_w || !opt.limit_h) + { + if (bg_im) + { + if (opt.verbose) + fprintf(stdout, + PACKAGE " - No size restriction specified for collage.\n" + " You did specify a background however, so the\n" + " collage size has defaulted to the size of the image\n"); + opt.limit_w = bg_w; + opt.limit_h = bg_h; + } + else + { + if (opt.verbose) + fprintf(stdout, + PACKAGE " - No size restriction specified for collage.\n" + " - For collage mode, you need to specify width and height.\n" + " Using defaults (width 800, height 600)\n"); + opt.limit_w = 800; + opt.limit_h = 600; + } + } + + w = opt.limit_w; + h = opt.limit_h; + D(4,("Limiting width to %d and height to %d\n", w, h)); + + im_main = imlib_create_image(w, h); + + if (!im_main) + eprintf("Imlib error creating image"); + + if (bg_im) + gib_imlib_blend_image_onto_image(im_main, bg_im, + gib_imlib_image_has_alpha(bg_im), 0, 0, + bg_w, bg_h, 0, 0, w, h, 1, 0, 0); + else if (trans_bg) + { + gib_imlib_image_fill_rectangle(im_main, 0, 0, w, h, 0, 0, 0, 0); + gib_imlib_image_set_has_alpha(im_main, 1); + } + else + { + /* Colour the background */ + gib_imlib_image_fill_rectangle(im_main, 0, 0, w, h, 0, 0, 0, 255); + } + + /* Create the title string */ + + if (!opt.title) + s = estrdup(PACKAGE " [collage mode]"); + else + s = estrdup(feh_printf(opt.title, NULL)); + + if (opt.display) + { + winwid = + winwidget_create_from_image(im_main, s, WIN_TYPE_SINGLE); + winwidget_show(winwid); + } + + + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + if (last) + { + filelist = feh_file_remove_from_list(filelist, last); + filelist_len--; + last = NULL; + } + D(3,("About to load image %s\n", file->filename)); + if (feh_load_image(&im_temp, file) != 0) + { + D(3,("Successfully loaded %s\n", file->filename)); + if (opt.verbose) + feh_display_status('.'); + www = opt.thumb_w; + hhh = opt.thumb_h; + ww = gib_imlib_image_get_width(im_temp); + hh = gib_imlib_image_get_height(im_temp); + + if (opt.aspect) + { + double ratio = 0.0; + + /* Keep the aspect ratio for the thumbnail */ + ratio = ((double) ww / hh) / ((double) www / hhh); + + if (ratio > 1.0) + hhh = opt.thumb_h / ratio; + else if (ratio != 1.0) + www = opt.thumb_w * ratio; + } + + if ((!opt.stretch) && ((www > ww) || (hhh > hh))) + { + /* Don't make the image larger unless stretch is specified */ + www = ww; + hhh = hh; + } + + /* pick random coords for thumbnail */ + xxx = ((w - www) * ((double) rand() / RAND_MAX)); + yyy = ((h - hhh) * ((double) rand() / RAND_MAX)); + D(5,("image going on at x=%d, y=%d\n", xxx, yyy)); + + im_thumb = + gib_imlib_create_cropped_scaled_image(im_temp, 0, 0, ww, hh, www, + hhh, 1); + gib_imlib_free_image_and_decache(im_temp); + + if (opt.alpha) + { + DATA8 atab[256]; + + D(4,("Applying alpha options\n")); + gib_imlib_image_set_has_alpha(im_thumb, 1); + memset(atab, opt.alpha_level, sizeof(atab)); + gib_imlib_apply_color_modifier_to_rectangle(im_thumb, 0, 0, www, + hhh, NULL, NULL, NULL, + atab); + } + gib_imlib_blend_image_onto_image(im_main, im_thumb, + gib_imlib_image_has_alpha(im_thumb), + 0, 0, www, hhh, xxx, yyy, www, hhh, + 1, + gib_imlib_image_has_alpha(im_thumb), + 0); + gib_imlib_free_image_and_decache(im_thumb); + } + else + { + last = l; + if (opt.verbose) + feh_display_status('x'); + } + if (opt.display) + { + winwidget_render_image(winwid, 0, 0); + if (!feh_main_iteration(0)) + exit(0); + } + } + if (opt.verbose) + fprintf(stdout, "\n"); + + if (opt.output && opt.output_file) + { + char output_buf[1024]; + if (opt.output_dir) + snprintf(output_buf,1024,"%s/%s", opt.output_dir, opt.output_file); + else + strncpy(output_buf,opt.output_file, 1024); + gib_imlib_save_image(im_main, output_buf); + if (opt.verbose) + { + int tw, th; + + tw = gib_imlib_image_get_width(im_main); + th = gib_imlib_image_get_height(im_main); + fprintf(stdout, PACKAGE " - File saved as %s\n", output_buf); + fprintf(stdout, + " - Image is %dx%d pixels and contains %d thumbnails\n", + tw, th, (tw / opt.thumb_w) * (th / opt.thumb_h)); + } + } + + if (!opt.display) + gib_imlib_free_image_and_decache(im_main); + free(s); + D_RETURN_(4); +} diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 0000000..75777c7 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,37 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `jpeg' library (-ljpeg). */ +#undef HAVE_LIBJPEG + +/* Define to 1 if you have the `Xext' library (-lXext). */ +#undef HAVE_LIBXEXT + +/* Define to 1 if you have the `Xinerama' library (-lXinerama). */ +#undef HAVE_LIBXINERAMA + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Version number of package */ +#undef VERSION + +/* Define if using the dmalloc debugging malloc package */ +#undef WITH_DMALLOC + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..3585cd7 --- /dev/null +++ b/src/debug.h @@ -0,0 +1,115 @@ +/* debug.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef DEBUG_H +#define DEBUG_H + +/* #define DEBUG */ + +#ifdef WITH_DMALLOC +#include +#define emalloc(a) malloc(a) +#define estrdup(a) strdup(a) +#define erealloc(a,b) realloc(a,b) +#else +#define emalloc(a) _emalloc(a) +#define estrdup(a) _estrdup(a) +#define erealloc(a,b) _erealloc(a,b) +#endif + +#ifdef DEBUG +#ifdef __GNUC__ +#define D(i, a) \ +{ \ + if(i <= opt.debug_level) \ + { \ + printf("%s +%u %s() %s ",__FILE__,__LINE__,__FUNCTION__, stroflen(' ', call_level)); \ + printf a; \ + fflush(stdout); \ + } \ + } +#define D_ENTER(i) \ + { \ + call_level++; \ + if(i <= opt.debug_level) \ + { \ + printf("%s +%u %s() %s ENTER\n",__FILE__,__LINE__,__FUNCTION__, stroflen('>', call_level)); \ + fflush(stdout); \ + } \ + } +#define D_RETURN(i, a) \ +{ \ + if(i <= opt.debug_level) \ + { \ + printf("%s +%u %s() %s LEAVE\n",__FILE__,__LINE__,__FUNCTION__, stroflen('<', call_level)); \ + fflush(stdout); \ + } \ + call_level--; \ + return (a); \ + } +#define D_RETURN_(i) \ +{ \ + if(i <= opt.debug_level) \ + { \ + printf("%s +%u %s() %s LEAVE\n",__FILE__,__LINE__,__FUNCTION__, stroflen('<', call_level)); \ + fflush(stdout); \ + } \ + call_level--; \ + return; \ + } +#else +#define D(i, a) \ +{ \ + if(i <= opt.debug_level) \ + { \ + printf("%s +%u : ",__FILE__,__LINE__); \ + printf a; \ + } \ + fflush(stdout); \ + } +#define D_ENTER(a) +#define D_RETURN(i, a) \ + { \ + return(a); \ + } +#define D_RETURN_(i) \ + { \ + return; \ + } +#endif +#else +#define D(i,a) +#define D_ENTER(a) +#define D_RETURN(i, a) \ + { \ + return (a); \ + } +#define D_RETURN_(i) \ + { \ + return; \ + } +#endif + +#endif diff --git a/src/events.c b/src/events.c new file mode 100644 index 0000000..ad78fbe --- /dev/null +++ b/src/events.c @@ -0,0 +1,688 @@ +/* events.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "winwidget.h" +#include "timers.h" +#include "options.h" +#include "events.h" +#include "thumbnail.h" + +feh_event_handler *ev_handler[LASTEvent]; + +static void feh_event_handle_ButtonPress(XEvent * ev); +static void feh_event_handle_ButtonRelease(XEvent * ev); +static void feh_event_handle_ConfigureNotify(XEvent * ev); +static void feh_event_handle_EnterNotify(XEvent * ev); +static void feh_event_handle_LeaveNotify(XEvent * ev); +static void feh_event_handle_MotionNotify(XEvent * ev); +static void feh_event_handle_ClientMessage(XEvent * ev); + +void +feh_event_init(void) +{ + int i; + + D_ENTER(4); + for (i = 0; i < LASTEvent; i++) + ev_handler[i] = NULL; + + ev_handler[KeyPress] = feh_event_handle_keypress; + ev_handler[ButtonPress] = feh_event_handle_ButtonPress; + ev_handler[ButtonRelease] = feh_event_handle_ButtonRelease; + ev_handler[ConfigureNotify] = feh_event_handle_ConfigureNotify; + ev_handler[EnterNotify] = feh_event_handle_EnterNotify; + ev_handler[LeaveNotify] = feh_event_handle_LeaveNotify; + ev_handler[MotionNotify] = feh_event_handle_MotionNotify; + ev_handler[ClientMessage] = feh_event_handle_ClientMessage; + + D_RETURN_(4); +} + +static void +feh_event_handle_ButtonPress(XEvent * ev) +{ + winwidget winwid = NULL; + int scr_width, scr_height; + + D_ENTER(4); + + /* get the heck out if it's a mouse-click on the + cover, we'll hide the menus on release */ + if (ev->xbutton.window == menu_cover) { + D_RETURN_(4); + } + + 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 */ + + winwid = winwidget_get_from_window(ev->xbutton.window); + if (winwid && winwid->caption_entry) { + D_RETURN_(4); + } + + if (!opt.no_menus && EV_IS_MENU_BUTTON(ev)) { + D(3, ("Menu Button Press event\n")); + if (winwid != NULL) { + winwidget_show_menu(winwid); + } + } + else if ((ev->xbutton.button == opt.rotate_button) + && ((opt.no_rotate_ctrl_mask) + || (ev->xbutton.state & ControlMask))) + { + if (winwid != NULL) + { + opt.mode = MODE_ROTATE; + winwid->mode = MODE_ROTATE; + D(3, ("rotate starting at %d, %d\n", ev->xbutton.x, ev->xbutton.y)); + } + } + else if ((ev->xbutton.button == opt.blur_button) + && ((opt.no_blur_ctrl_mask) || (ev->xbutton.state & ControlMask))) + { + if (winwid != NULL) + { + opt.mode = MODE_BLUR; + winwid->mode = MODE_BLUR; + D(3, ("blur starting at %d, %d\n", ev->xbutton.x, ev->xbutton.y)); + } + } + else if (ev->xbutton.button == opt.next_button) + { + D(3, ("Next Button Press event\n")); + if (winwid != NULL) + { + D(3, ("Next button, but could be pan mode\n")); + opt.mode = MODE_NEXT; + winwid->mode = MODE_NEXT; + D(3, ("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); + winwid->click_offset_x = ev->xbutton.x - winwid->im_x; + winwid->click_offset_y = ev->xbutton.y - winwid->im_y; + } + } + else if (ev->xbutton.button == opt.zoom_button) + { + D(3, ("Zoom Button Press event\n")); + if (winwid != NULL) + { + D(3, ("Zoom mode baby!\n")); + opt.mode = MODE_ZOOM; + winwid->mode = MODE_ZOOM; + D(3, ("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y)); + winwid->click_offset_x = ev->xbutton.x - winwid->im_x; + winwid->click_offset_y = ev->xbutton.y - winwid->im_y; + winwid->im_click_offset_x = winwid->click_offset_x / winwid->zoom; + winwid->im_click_offset_y = winwid->click_offset_y / winwid->zoom; + winwid->zoom = 1.0; + if (winwid->full_screen) + { + winwid->im_x = (scr_width - winwid->im_w) >> 1; + winwid->im_y = (scr_height - winwid->im_h) >> 1; + } + else + { + if (opt.geom_flags & WidthValue) { + winwid->im_x = (opt.geom_w - winwid->im_w) >> 1; + } else { + winwid->im_x = 0; + } + if (opt.geom_flags & HeightValue) { + winwid->im_y = (opt.geom_h - winwid->im_h) >> 1; + } else { + winwid->im_y = 0; + } + } + if (winwid->im_click_offset_x < 30) + winwid->im_click_offset_x = 30; + if (winwid->im_click_offset_y < 0) + winwid->im_click_offset_y = 0; + if (winwid->im_click_offset_x > winwid->im_w) + winwid->im_click_offset_x = winwid->im_w; + if (winwid->im_click_offset_y > winwid->im_h) + winwid->im_click_offset_y = winwid->im_h; + + if (winwid->click_offset_x < 30) + winwid->click_offset_x = 30; + if (winwid->click_offset_y < 0) + winwid->click_offset_y = 0; + if (winwid->click_offset_x > winwid->w) + winwid->click_offset_x = winwid->w; + if (winwid->click_offset_y > winwid->h) + winwid->click_offset_y = winwid->h; + + winwidget_render_image(winwid, 0, 0); + } + } + else if (ev->xbutton.button == opt.reload_button) + { + D(3, ("Reload Button Press event\n")); + if (winwid != NULL) + feh_reload_image(winwid, 0, 0); + } + else if (ev->xbutton.button == 4 /* this is bad */ ) + { + D(3, ("Button 4 Press event\n")); + if ((winwid != NULL) && (winwid->type == WIN_TYPE_SLIDESHOW)) + slideshow_change_image(winwid, SLIDE_PREV); + } + else if (ev->xbutton.button == 5 /* this is bad */ ) + { + D(3, ("Button 5 Press event\n")); + if ((winwid != NULL) && (winwid->type == WIN_TYPE_SLIDESHOW)) + slideshow_change_image(winwid, SLIDE_NEXT); + } + else + { + D(3, ("Received other ButtonPress event\n")); + } + D_RETURN_(4); +} + +static void +feh_event_handle_ButtonRelease(XEvent * ev) +{ + winwidget winwid = NULL; + + D_ENTER(4); + if (menu_root) + { + /* if menus are open, close them, and execute action if needed */ + + if (ev->xbutton.window == menu_cover) { + feh_menu_hide(menu_root, True); + } else if (menu_root) { + feh_menu *m; + + if ((m = feh_menu_get_from_window(ev->xbutton.window))) + { + feh_menu_item *i = NULL; + + i = feh_menu_find_selected(m); + feh_menu_item_activate(m, i); + } + } + D_RETURN_(4); + } + + winwid = winwidget_get_from_window(ev->xbutton.window); + if (winwid && winwid->caption_entry) { + D_RETURN_(4); + } + + if ((ev->xbutton.button == opt.menu_button) + && + (((!opt.menu_ctrl_mask) + && ((!(ev->xbutton.state & ControlMask)) + || ((ev->xbutton.state & ControlMask) && (opt.menu_ctrl_mask))))) + && (opt.no_menus)) + winwidget_destroy_all(); + else if (ev->xbutton.button == opt.next_button) + { + if (opt.mode == MODE_PAN) + { + if (winwid != NULL) + { + D(3, ("Disabling pan mode\n")); + opt.mode = MODE_NORMAL; + winwid->mode = MODE_NORMAL; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 1); + } + } + else + { + opt.mode = MODE_NORMAL; + if(winwid != NULL) + winwid->mode = MODE_NORMAL; + if ((winwid != NULL) && (winwid->type == WIN_TYPE_SLIDESHOW)) + { + slideshow_change_image(winwid, SLIDE_NEXT); + } + else if ((winwid != NULL) && (winwid->type == WIN_TYPE_THUMBNAIL)) + { + feh_file *thumbfile; + winwidget thumbwin = NULL; + int x, y; + char *s; + + x = ev->xbutton.x; + y = ev->xbutton.y; + x -= winwid->im_x; + y -= winwid->im_y; + x /= winwid->zoom; + y /= winwid->zoom; + thumbfile = feh_thumbnail_get_file_from_coords(x, y); + if (thumbfile) + { + if (!opt.thumb_title) + s = thumbfile->name; + else + s = feh_printf(opt.thumb_title, thumbfile); + thumbwin = + winwidget_get_first_window_of_type + (WIN_TYPE_THUMBNAIL_VIEWER); + if (!thumbwin) + { + thumbwin = + winwidget_create_from_file(gib_list_add_front + (NULL, thumbfile), s, + WIN_TYPE_THUMBNAIL_VIEWER); + winwidget_show(thumbwin); + } + else if (FEH_FILE(thumbwin->file->data) != thumbfile) + { + free(thumbwin->file); + thumbwin->file = gib_list_add_front(NULL, thumbfile); + winwidget_rename(thumbwin, s); + feh_reload_image(thumbwin, 1, 0); + } + } + } + } + } + else if ((ev->xbutton.button == opt.rotate_button) + || (ev->xbutton.button == opt.zoom_button)) + { + D(3, ("Mode-based Button Release event\n")); + if (winwid != NULL) + { + D(3, ("Disabling mode\n")); + opt.mode = MODE_NORMAL; + winwid->mode = MODE_NORMAL; + winwidget_sanitise_offsets(winwid); + winwidget_render_image(winwid, 0, 1); + } + } + else if ((ev->xbutton.button == opt.blur_button) + && ((opt.no_blur_ctrl_mask) || (ev->xbutton.state & ControlMask))) + { + D(3, ("Blur Button Release event\n")); + if (winwid != NULL) + { + D(3, ("Disabling Blur mode\n")); + opt.mode = MODE_NORMAL; + winwid->mode = MODE_NORMAL; + } + } + D_RETURN_(4); +} + +static void +feh_event_handle_ConfigureNotify(XEvent * ev) +{ + D_ENTER(4); + while (XCheckTypedWindowEvent + (disp, ev->xconfigure.window, ConfigureNotify, ev)); + if (!menu_root) + { + winwidget w = winwidget_get_from_window(ev->xconfigure.window); + + if (w) + { + D(3, + ("configure size %dx%d\n", ev->xconfigure.width, + ev->xconfigure.height)); + if ((w->w != ev->xconfigure.width) + || (w->h != ev->xconfigure.height)) + { + D(3, ("assigning size and rerendering\n")); + w->w = ev->xconfigure.width; + w->h = ev->xconfigure.height; + w->had_resize = 1; + if (opt.geom_flags & WidthValue || opt.geom_flags & HeightValue) + { + opt.geom_w = w->w; + opt.geom_h = w->h; + } + winwidget_render_image(w, 0, 1); + } + } + } + + D_RETURN_(4); +} + +static void +feh_event_handle_EnterNotify(XEvent * ev) +{ + D_ENTER(4); + D_RETURN_(4); + ev = NULL; +} + +static void +feh_event_handle_LeaveNotify(XEvent * ev) +{ + D_ENTER(4); + if ((menu_root) && (ev->xcrossing.window == menu_root->win)) + { + feh_menu_item *ii; + + D(4, ("It is for a menu\n")); + for (ii = menu_root->items; ii; ii = ii->next) + { + if (MENU_ITEM_IS_SELECTED(ii)) + { + D(4, ("Unselecting menu\n")); + MENU_ITEM_SET_NORMAL(ii); + menu_root->updates = + imlib_update_append_rect(menu_root->updates, ii->x, ii->y, + ii->w, ii->h); + menu_root->needs_redraw = 1; + } + } + feh_raise_all_menus(); + } + + D_RETURN_(4); +} + +static void +feh_event_handle_MotionNotify(XEvent * ev) +{ + winwidget winwid = NULL; + int dx, dy; + 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 */ + + D_ENTER(5); + if (menu_root) + { + feh_menu *m; + feh_menu_item *selected_item, *mouseover_item; + + D(3, ("motion notify with menus open\n")); + while (XCheckTypedWindowEvent + (disp, ev->xmotion.window, MotionNotify, ev)); + + if (ev->xmotion.window == menu_cover) + { + D_RETURN_(5); + } + else if ((m = feh_menu_get_from_window(ev->xmotion.window))) + { + selected_item = feh_menu_find_selected(m); + mouseover_item = + feh_menu_find_at_xy(m, ev->xmotion.x, ev->xmotion.y); + + if (selected_item != mouseover_item) + { + D(4, ("selecting a menu item\n")); + if (selected_item) + feh_menu_deselect_selected(m); + if ((mouseover_item) + && ((mouseover_item->func) || (mouseover_item->submenu) + || (mouseover_item->func_gen_sub))) + feh_menu_select(m, mouseover_item); + } + /* check if we are close to the right and/or the bottom edge of the + * screen. If so, and if the menu we are currently over is partially + * hidden, slide the menu to the left and/or up until it is + * fully visible */ + + /* FIXME: get this working nicely with xinerama screen edges -- + * at the moment it does really funky stuff with + * scr_{width,height} instead of scr->{width,height} -- pabs*/ + if (mouseover_item + && ((scr->width - (ev->xmotion.x + m->x)) < m->w + || (scr->height - (ev->xmotion.y + m->y)) < m->w)) + { + dx = scr_width - (m->x + m->w); + dy = scr_height - (m->y + m->h); + dx = dx < 0 ? dx : 0; + dy = dy < 0 ? dy : 0; + if (dx || dy) + feh_menu_slide_all_menus_relative(dx, dy); + } + /* if a submenu is open we want to see that also */ + if (mouseover_item && m->next + && ((scr->width - (ev->xmotion.x + m->next->x)) < m->next->w + || (scr->height - (ev->xmotion.y + m->next->y)) < m->next->w)) + { + dx = scr->width - (m->next->x + m->next->w); + dy = scr->height - (m->next->y + m->next->h); + dx = dx < 0 ? dx : 0; + dy = dy < 0 ? dy : 0; + if (dx || dy) + feh_menu_slide_all_menus_relative(dx, dy); + } + } + } + else if (opt.mode == MODE_ZOOM) + { + while (XCheckTypedWindowEvent + (disp, ev->xmotion.window, MotionNotify, ev)); + + winwid = winwidget_get_from_window(ev->xmotion.window); + if (winwid) + { + winwid->zoom = + ((double) ev->xmotion.x - (double) winwid->click_offset_x) / 64.0; + if (winwid->zoom < 0) + winwid->zoom = + 1.0 + + ((winwid->zoom * 64.0) / + ((double) (winwid->click_offset_x + 1))); + else + winwid->zoom += 1.0; + + if (winwid->zoom < 0.01) + winwid->zoom = 0.01; + + /* calculate change in zoom and move im_x and im_y respectively to + enable zooming to the clicked spot... */ + /* for now, center around im_click_offset_x and im_click_offset_y */ + winwid->im_x = + (winwid->w / 2) - (winwid->im_click_offset_x * winwid->zoom); + winwid->im_y = + (winwid->h / 2) - (winwid->im_click_offset_y * winwid->zoom); + + winwidget_render_image(winwid, 0, 0); + } + } + else if ((opt.mode == MODE_PAN) || (opt.mode == MODE_NEXT)) + { + int orig_x, orig_y; + + while (XCheckTypedWindowEvent + (disp, ev->xmotion.window, MotionNotify, ev)); + winwid = winwidget_get_from_window(ev->xmotion.window); + if (winwid) + { + if (opt.mode == MODE_NEXT) + { + opt.mode = MODE_PAN; + winwid->mode = MODE_PAN; + } + D(5, ("Panning\n")); + orig_x = winwid->im_x; + orig_y = winwid->im_y; + + winwid->im_x = ev->xmotion.x - winwid->click_offset_x; + winwid->im_y = ev->xmotion.y - winwid->click_offset_y; + + winwidget_sanitise_offsets(winwid); + + if ((winwid->im_x != orig_x) || (winwid->im_y != orig_y)) + winwidget_render_image(winwid, 0, 0); + } + } + else if (opt.mode == MODE_ROTATE) + { + while (XCheckTypedWindowEvent + (disp, ev->xmotion.window, MotionNotify, ev)); + winwid = winwidget_get_from_window(ev->xmotion.window); + if (winwid) + { + D(5, ("Rotating\n")); + if (!winwid->has_rotated) + { + Imlib_Image temp; + + temp = gib_imlib_create_rotated_image(winwid->im, 0.0); + winwid->im_w = gib_imlib_image_get_width(temp); + winwid->im_h = gib_imlib_image_get_height(temp); + gib_imlib_free_image_and_decache(temp); + if (!winwid->full_screen && !opt.geom_flags) + winwidget_resize(winwid, winwid->im_w, winwid->im_h); + winwid->has_rotated = 1; + } + winwid->im_angle = + (ev->xmotion.x - + winwid->w / 2) / ((double) winwid->w / 2) * 3.1415926535; + D(5, ("angle: %f\n", winwid->im_angle)); + winwidget_render_image(winwid, 0, 0); + } + } + else if (opt.mode == MODE_BLUR) + { + while (XCheckTypedWindowEvent + (disp, ev->xmotion.window, MotionNotify, ev)); + winwid = winwidget_get_from_window(ev->xmotion.window); + if (winwid) + { + Imlib_Image temp, ptr; + signed int blur_radius; + + D(5, ("Blurring\n")); + + temp = gib_imlib_clone_image(winwid->im); + blur_radius = (((double) ev->xmotion.x / winwid->w) * 20) - 10; + D(5, ("angle: %d\n", blur_radius)); + if (blur_radius > 0) + gib_imlib_image_sharpen(temp, blur_radius); + else + gib_imlib_image_blur(temp, 0 - blur_radius); + ptr = winwid->im; + winwid->im = temp; + winwidget_render_image(winwid, 0, 0); + gib_imlib_free_image_and_decache(winwid->im); + winwid->im = ptr; + } + } + else + { + while (XCheckTypedWindowEvent + (disp, ev->xmotion.window, MotionNotify, ev)); + winwid = winwidget_get_from_window(ev->xmotion.window); + if (winwid != NULL) + { + if (winwid->type == WIN_TYPE_ABOUT) + { + Imlib_Image orig_im; + int x, y; + + x = ev->xmotion.x - winwid->im_x; + y = ev->xmotion.y - winwid->im_y; + orig_im = winwid->im; + winwid->im = gib_imlib_clone_image(orig_im); + imlib_context_set_image(winwid->im); + imlib_apply_filter("bump_map_point(x=[],y=[],map=" PREFIX + "/share/feh/images/about.png);", &x, &y); + winwidget_render_image(winwid, 0, 1); + gib_imlib_free_image_and_decache(winwid->im); + winwid->im = orig_im; + } + else if (winwid->type == WIN_TYPE_THUMBNAIL) + { + static feh_thumbnail *last_thumb = NULL; + feh_thumbnail *thumbnail; + int x, y; + + x = (ev->xbutton.x - winwid->im_x) / winwid->zoom; + y = (ev->xbutton.y - winwid->im_y) / winwid->zoom; + thumbnail = feh_thumbnail_get_thumbnail_from_coords(x, y); + if (thumbnail != last_thumb) + { + if (thumbnail) + { + Imlib_Image origwin; + + origwin = winwid->im; + winwid->im = gib_imlib_clone_image(origwin); + gib_imlib_image_fill_rectangle(winwid->im, thumbnail->x, + thumbnail->y, thumbnail->w, + thumbnail->h, 50, 50, 255, + 100); + gib_imlib_image_draw_rectangle(winwid->im, thumbnail->x, + thumbnail->y, thumbnail->w, + thumbnail->h, 255, 255, 255, + 255); + gib_imlib_image_draw_rectangle(winwid->im, thumbnail->x + 1, + thumbnail->y + 1, + thumbnail->w - 2, + thumbnail->h - 2, 0, 0, 0, + 255); + gib_imlib_image_draw_rectangle(winwid->im, thumbnail->x + 2, + thumbnail->y + 2, + thumbnail->w - 4, + thumbnail->h - 4, 255, 255, + 255, 255); + winwidget_render_image(winwid, 0, 1); + gib_imlib_free_image_and_decache(winwid->im); + winwid->im = origwin; + } + else + winwidget_render_image(winwid, 0, 1); + } + last_thumb = thumbnail; + } + } + } + D_RETURN_(5); +} + +static void +feh_event_handle_ClientMessage(XEvent * ev) +{ + winwidget winwid = NULL; + + D_ENTER(4); + if (ev->xclient.format == 32 + && ev->xclient.data.l[0] == (signed) wmDeleteWindow) + { + winwid = winwidget_get_from_window(ev->xclient.window); + if (winwid) + winwidget_destroy(winwid); + } + + D_RETURN_(4); +} diff --git a/src/events.h b/src/events.h new file mode 100644 index 0000000..22abaf7 --- /dev/null +++ b/src/events.h @@ -0,0 +1,37 @@ +/* events.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef EVENTS_H +#define EVENTS_H + +typedef void (feh_event_handler) (XEvent * ev); + +extern feh_event_handler *ev_handler[]; + +void feh_event_init(void); + +#define EV_IS_MENU_BUTTON(ev) ((((ev)->xbutton.button == opt.menu_button) || (opt.menu_button == 0)) && (((!opt.menu_ctrl_mask) && (!((ev)->xbutton.state & ControlMask))) || (((ev)->xbutton.state & ControlMask) && (opt.menu_ctrl_mask)))) + +#endif diff --git a/src/feh.h b/src/feh.h new file mode 100644 index 0000000..ea8657c --- /dev/null +++ b/src/feh.h @@ -0,0 +1,178 @@ +/* feh.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef FEH_H +#define FEH_H + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBXINERAMA +#include +#include +#endif /* HAVE_LIBXINERAMA */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "structs.h" +#include "menu.h" +#include "ipc.h" + +#include "utils.h" +#include "getopt.h" + + +#include "debug.h" + +#define SLIDESHOW_RELOAD_MAX 4096 + +#ifndef TRUE +#define FALSE 0 +#define TRUE !FALSE +#endif + +#ifndef __GNUC__ +# define __attribute__(x) +#endif + +#define XY_IN_RECT(x, y, rx, ry, rw, rh) \ +(((x) >= (rx)) && ((y) >= (ry)) && ((x) < ((rx) + (rw))) && ((y) < ((ry) + (rh)))) + +#define DEFAULT_FONT "yudit/11" +#define DEFAULT_MENU_FONT "yudit/10" +#define DEFAULT_FONT_BIG "yudit/12" +#define DEFAULT_FONT_TITLE "yudit/14" + +enum mode_type +{ MODE_NORMAL = 0, MODE_PAN, MODE_ZOOM, MODE_ROTATE, MODE_BLUR, MODE_NEXT }; + +enum bgmode_type +{ BG_MODE_NONE = 0, BG_MODE_TILE, BG_MODE_CENTER, BG_MODE_SEAMLESS, BG_MODE_SCALE }; + +enum slide_change +{ SLIDE_NEXT, SLIDE_PREV, SLIDE_FIRST, SLIDE_LAST, SLIDE_JUMP_FWD, + SLIDE_JUMP_BACK +}; + +typedef void (*sighandler_t) (int); + +void show_usage(void); +void show_version(void); +int feh_main_iteration(int block); +void feh_handle_event(XEvent * ev); +void init_x_and_imlib(void); +#ifdef HAVE_LIBXINERAMA +void init_xinerama(void); +#endif /* HAVE_LIBXINERAMA */ +void init_multiwindow_mode(void); +void init_thumbnail_mode(void); +void init_collage_mode(void); +void init_index_mode(void); +void init_slideshow_mode(void); +void init_list_mode(void); +void init_loadables_mode(void); +void init_unloadables_mode(void); +void feh_clean_exit(void); +int feh_load_image(Imlib_Image * im, feh_file * file); +void show_mini_usage(void); +void slideshow_change_image(winwidget winwid, int change); +void slideshow_pause_toggle(winwidget w); +char *slideshow_create_name(feh_file * file); +char *chop_file_from_full_path(char *str); +void feh_event_handle_keypress(XEvent * ev); +void feh_action_run(feh_file * file, char *action); +char *feh_printf(char *str, feh_file * file); +void feh_draw_zoom(winwidget w); +void feh_draw_checks(winwidget win); +void cb_slide_timer(void *data); +void cb_reload_timer(void *data); +char *feh_http_load_image(char *url); +int feh_load_image_char(Imlib_Image * im, char *filename); +void feh_draw_filename(winwidget w); +void feh_draw_caption(winwidget w); +void feh_display_status(char stat); +void real_loadables_mode(int loadable); +void feh_reload_image(winwidget w, int resize, int force_new); +void feh_filelist_image_remove(winwidget winwid, char do_delete); +char *feh_strip_hostname(char *url); +struct hostent *feh_gethostbyname(const char *name); +void slideshow_save_image(winwidget win); +void feh_edit_inplace_orient(winwidget w, int orientation); +void feh_edit_inplace_lossless_rotate(winwidget w, int orientation); +gib_list * feh_wrap_string(char *text, int wrap_width, int max_height, Imlib_Font fn, gib_style * style); +char *build_caption_filename(feh_file *file); +gib_list * feh_list_jump(gib_list * root, gib_list * l, int direction, int num); + + +/* Imlib stuff */ +extern Display *disp; +extern Visual *vis; +extern Colormap cm; +extern int depth; +extern Atom wmDeleteWindow; + +#ifdef HAVE_LIBXINERAMA +extern int num_xinerama_screens; +extern XineramaScreenInfo *xinerama_screens; +extern int xinerama_screen; +#endif /* HAVE_LIBXINERAMA */ + +/* Thumbnail sizes */ +extern int cmdargc; +extern char **cmdargv; +extern Window root; +extern XContext xid_context; +extern Screen *scr; +extern unsigned char reset_output; +extern int call_level; +extern feh_menu *menu_main; +extern feh_menu *menu_close; +extern char *mode; /* label for the current mode */ + +#endif diff --git a/src/feh_png.c b/src/feh_png.c new file mode 100644 index 0000000..7654880 --- /dev/null +++ b/src/feh_png.c @@ -0,0 +1,214 @@ +/* feh_png.c + +Copyright (C) 2004 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh_png.h" + +#include + +#include +#include + +#define FEH_PNG_COMPRESSION 3 +#define FEH_PNG_NUM_COMMENTS 2 /* only Thumb::URI and Thumb::MTime for now */ + +gib_hash* +feh_png_read_comments(char *file) +{ + gib_hash *hash = NULL; + + FILE *fp; + int i, sig_bytes, comments = 0; + + png_structp png_ptr; + png_infop info_ptr; + png_textp text_ptr; + + if (!(fp = fopen(file, "rb"))) + return hash; + + if (!(sig_bytes = feh_png_file_is_png(fp))) { + fclose(fp); + return hash; + } + + /* initialize data structures */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + fclose(fp); + return hash; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); + fclose(fp); + return hash; + } + + if (setjmp(png_ptr->jmpbuf)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + return hash; + } + + /* initialize reading */ + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, sig_bytes); + + png_read_info(png_ptr, info_ptr); + +#ifdef PNG_TEXT_SUPPORTED + png_get_text(png_ptr, info_ptr, &text_ptr, &comments); + if (comments > 0) { + hash = gib_hash_new(); + for (i = 0; i < comments; i++) + gib_hash_set(hash, text_ptr[i].key, estrdup(text_ptr[i].text)); + } +#endif /* PNG_TEXT_SUPPORTED */ + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(fp); + + return hash; +} + +/* grab image data from image and write info file with comments ... */ +int +feh_png_write_png(Imlib_Image image, char *file, ...) +{ + FILE *fp; + int i, w, h; + + png_structp png_ptr; + png_infop info_ptr; + png_color_8 sig_bit; + + DATA32 *ptr; + +#ifdef PNG_TEXT_SUPPORTED + va_list args; + png_text text[FEH_PNG_NUM_COMMENTS]; + char *pair_key, *pair_text; +#endif /* PNG_TEXT_SUPPORTED */ + + if (!(fp = fopen(file, "wb"))) + return 0; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + return 0; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + return 0; + } + + if (setjmp(png_ptr->jmpbuf)) + { + fclose(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); + png_destroy_info_struct(png_ptr, &info_ptr); + return 0; + } + + w = gib_imlib_image_get_width(image); + h = gib_imlib_image_get_height(image); + + png_init_io(png_ptr, fp); + + png_set_IHDR(png_ptr, info_ptr, w, h, 8, PNG_COLOR_TYPE_RGB_ALPHA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + +#ifdef WORDS_BIGENDIAN + png_set_swap_alpha(png_ptr); +#else /* !WORDS_BIGENDIAN */ + png_set_bgr(png_ptr); +#endif /* WORDS_BIGENDIAN */ + + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + sig_bit.alpha = 8; + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + +#ifdef PNG_TEXT_SUPPORTED + va_start(args, file); + for (i = 0; i < FEH_PNG_NUM_COMMENTS; i++) + { + if ((pair_key = va_arg(args, char *)) && + (pair_text = va_arg(args, char *))) + { + /* got a complete pair, add to info structure */ + text[i].key = pair_key; + text[i].text = pair_text; + text[i].compression = PNG_TEXT_COMPRESSION_NONE; + } + else + break; + } + va_end(args); + + if (i > 0) + png_set_text(png_ptr, info_ptr, text, i); +#endif /* PNG_TEXT_SUPPORTED */ + + png_set_compression_level(png_ptr, FEH_PNG_COMPRESSION); + png_write_info(png_ptr, info_ptr); + png_set_shift(png_ptr, &sig_bit); + png_set_packing(png_ptr); + + /* write image data */ + imlib_context_set_image(image); + ptr = imlib_image_get_data(); + for (i = 0; i < h; i++, ptr += w) + png_write_row(png_ptr, (png_bytep) ptr); + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + png_destroy_info_struct(png_ptr, &info_ptr); + + fclose(fp); + + return 0; +} + +/* check PNG signature */ +int +feh_png_file_is_png(FILE *fp) +{ + unsigned char buf[8]; + + fread(buf, 1, 8, fp); + if (png_sig_cmp(buf, 0, 8)) { + return 0; + } + + return 8; +} diff --git a/src/feh_png.h b/src/feh_png.h new file mode 100644 index 0000000..458f6c5 --- /dev/null +++ b/src/feh_png.h @@ -0,0 +1,39 @@ +/* feh_png.h + +Copyright (C) 2004 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef FEH_PNG_H +#define FEH_PNG_H + +#include "feh.h" + +#include +#include + +gib_hash *feh_png_read_comments(char *file); +int feh_png_write_png(Imlib_Image image, char *file, ...); + +int feh_png_file_is_png(FILE *fp); + +#endif /* FEH_PNG_H */ diff --git a/src/filelist.c b/src/filelist.c new file mode 100644 index 0000000..dcbab54 --- /dev/null +++ b/src/filelist.c @@ -0,0 +1,611 @@ +/* filelist.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "options.h" + +gib_list *filelist = NULL; +int filelist_len = 0; +gib_list *current_file = NULL; +extern int errno; + +static gib_list *rm_filelist = NULL; + +feh_file * +feh_file_new(char *filename) +{ + feh_file *newfile; + char *s; + + D_ENTER(4); + + newfile = (feh_file *) emalloc(sizeof(feh_file)); + newfile->caption = NULL; + newfile->filename = estrdup(filename); + s = strrchr(filename, '/'); + if (s) + newfile->name = estrdup(s + 1); + else + newfile->name = estrdup(filename); + newfile->info = NULL; + D_RETURN(4, newfile); +} + +void +feh_file_free(feh_file * file) +{ + D_ENTER(4); + if (!file) + D_RETURN_(4); + if (file->filename) + free(file->filename); + if (file->name) + free(file->name); + if (file->caption) + free(file->caption); + if (file->info) + feh_file_info_free(file->info); + free(file); + D_RETURN_(4); +} + +feh_file_info * +feh_file_info_new(void) +{ + feh_file_info *info; + + D_ENTER(4); + + info = (feh_file_info *) emalloc(sizeof(feh_file_info)); + + info->width = 0; + info->height = 0; + info->size = 0; + info->pixels = 0; + info->has_alpha = 0; + info->format = NULL; + info->extension = NULL; + + D_RETURN(4, info); +} + +void +feh_file_info_free(feh_file_info * info) +{ + D_ENTER(4); + if (!info) + D_RETURN_(4); + if (info->format) + free(info->format); + if (info->extension) + free(info->extension); + free(info); + D_RETURN_(4); +} + +gib_list * +feh_file_rm_and_free(gib_list * list, gib_list * l) +{ + D_ENTER(4); + unlink(FEH_FILE(l->data)->filename); + D_RETURN(4, feh_file_remove_from_list(list, l)); +} + +gib_list * +feh_file_remove_from_list(gib_list * list, gib_list * l) +{ + D_ENTER(4); + feh_file_free(FEH_FILE(l->data)); + D_RETURN(4, gib_list_remove(list, l)); +} + +/* Recursive */ +void +add_file_to_filelist_recursively(char *origpath, unsigned char level) +{ + struct stat st; + char *path; + + D_ENTER(5); + if (!origpath) + D_RETURN_(5); + + path = estrdup(origpath); + D(4, ("file is %s\n", path)); + + if (level == FILELIST_FIRST) + { + /* First time through, sort out pathname */ + int len = 0; + + len = strlen(path); + if (path[len - 1] == '/') + path[len - 1] = '\0'; + + if ((!strncmp(path, "http://", 7)) || + (!strncmp(path, "https://", 8)) || + (!strncmp(path, "ftp://", 6))) + { + /* Its a url */ + D(3, ("Adding url %s to filelist\n", path)); + filelist = gib_list_add_front(filelist, feh_file_new(path)); + /* We'll download it later... */ + free(path); + D_RETURN_(5); + } + else if (opt.filelistfile) + { + char *newpath = feh_absolute_path(path); + + free(path); + path = newpath; + } + } + + errno = 0; + if (stat(path, &st)) + { + /* Display useful error message */ + switch (errno) + { + case ENOENT: + case ENOTDIR: + if (!opt.quiet) + weprintf("%s does not exist - skipping", path); + break; + case ELOOP: + if (!opt.quiet) + weprintf("%s - too many levels of symbolic links - skipping", + path); + break; + case EACCES: + if (!opt.quiet) + weprintf("you don't have permission to open %s - skipping", + path); + break; + default: + if (!opt.quiet) + weprintf("couldn't open %s", path); + break; + } + free(path); + D_RETURN_(5); + } + + if ((S_ISDIR(st.st_mode)) && (level != FILELIST_LAST)) + { + struct dirent *de; + DIR *dir; + + D(4, ("It is a directory\n")); + + if ((dir = opendir(path)) == NULL) + { + if (!opt.quiet) + weprintf("couldn't open directory %s:", path); + free(path); + D_RETURN_(5); + } + de = readdir(dir); + while (de != NULL) + { + if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) + { + char *newfile; + + newfile = estrjoin("", path, "/", de->d_name, NULL); + + /* This ensures we go down one level even if not fully recursive + - this way "feh some_dir" expands to some_dir's contents */ + if (opt.recursive) + add_file_to_filelist_recursively(newfile, FILELIST_CONTINUE); + else + add_file_to_filelist_recursively(newfile, FILELIST_LAST); + + free(newfile); + } + de = readdir(dir); + } + closedir(dir); + } + else if (S_ISREG(st.st_mode)) + { + D(5, ("Adding regular file %s to filelist\n", path)); + filelist = gib_list_add_front(filelist, feh_file_new(path)); + } + free(path); + D_RETURN_(5); +} + +void +add_file_to_rm_filelist(char *file) +{ + D_ENTER(4); + rm_filelist = gib_list_add_front(rm_filelist, feh_file_new(file)); + D_RETURN_(4); +} + +void +delete_rm_files(void) +{ + gib_list *l; + + D_ENTER(4); + for (l = rm_filelist; l; l = l->next) + unlink(FEH_FILE(l->data)->filename); + D_RETURN_(4); +} + +gib_list * +feh_file_info_preload(gib_list * list) +{ + gib_list *l; + feh_file *file = NULL; + gib_list *remove_list = NULL; + + D_ENTER(4); + if (opt.verbose) + fprintf(stdout, PACKAGE " - preloading...\n"); + + for (l = list; l; l = l->next) + { + file = FEH_FILE(l->data); + D(5, + ("file %p, file->next %p, file->name %s\n", l, l->next, file->name)); + if (feh_file_info_load(file, NULL)) + { + D(3, ("Failed to load file %p\n", file)); + remove_list = gib_list_add_front(remove_list, l); + if (opt.verbose) + feh_display_status('x'); + } + else if (opt.verbose) + feh_display_status('.'); + } + if (opt.verbose) + fprintf(stdout, "\n"); + + if (remove_list) + { + for (l = remove_list; l; l = l->next) + filelist = list = gib_list_remove(list, (gib_list *) l->data); + + gib_list_free(remove_list); + } + + D_RETURN(4, list); +} + +int +feh_file_info_load(feh_file * file, Imlib_Image im) +{ + struct stat st; + int need_free = 1; + Imlib_Image im1; + + D_ENTER(4); + + D(4, ("im is %p\n", im)); + + if (im) + need_free = 0; + + errno = 0; + if (stat(file->filename, &st)) + { + /* Display useful error message */ + switch (errno) + { + case ENOENT: + case ENOTDIR: + if (!opt.quiet) + weprintf("%s does not exist - skipping", file->filename); + break; + case ELOOP: + if (!opt.quiet) + weprintf("%s - too many levels of symbolic links - skipping", + file->filename); + break; + case EACCES: + if (!opt.quiet) + weprintf("you don't have permission to open %s - skipping", + file->filename); + break; + default: + if (!opt.quiet) + weprintf("couldn't open %s ", file->filename); + break; + } + D_RETURN(4, 1); + } + + if (im) + im1 = im; + else if (!feh_load_image(&im1, file)) + D_RETURN(4, 1); + + if (!im1) + D_RETURN(4, 1); + + file->info = feh_file_info_new(); + + file->info->width = gib_imlib_image_get_width(im1); + file->info->height = gib_imlib_image_get_height(im1); + + file->info->has_alpha = gib_imlib_image_has_alpha(im1); + + file->info->pixels = file->info->width * file->info->height; + + file->info->format = estrdup(gib_imlib_image_format(im1)); + + file->info->size = st.st_size; + + if (need_free && im1) + gib_imlib_free_image_and_decache(im1); + D_RETURN(4, 0); +} + +int +feh_cmp_filename(void *file1, void *file2) +{ + D_ENTER(4); + D_RETURN(4, strcmp(FEH_FILE(file1)->filename, FEH_FILE(file2)->filename)); +} + +int +feh_cmp_name(void *file1, void *file2) +{ + D_ENTER(4); + D_RETURN(4, strcmp(FEH_FILE(file1)->name, FEH_FILE(file2)->name)); +} + +int +feh_cmp_width(void *file1, void *file2) +{ + D_ENTER(4); + D_RETURN(4, (FEH_FILE(file1)->info->width - FEH_FILE(file2)->info->width)); +} + +int +feh_cmp_height(void *file1, void *file2) +{ + D_ENTER(4); + D_RETURN(4, + (FEH_FILE(file1)->info->height - FEH_FILE(file2)->info->height)); +} + +int +feh_cmp_pixels(void *file1, void *file2) +{ + D_ENTER(4); + D_RETURN(4, + (FEH_FILE(file1)->info->pixels - FEH_FILE(file2)->info->pixels)); +} + +int +feh_cmp_size(void *file1, void *file2) +{ + D_ENTER(4); + D_RETURN(4, (FEH_FILE(file1)->info->size - FEH_FILE(file2)->info->size)); +} + +int +feh_cmp_format(void *file1, void *file2) +{ + D_ENTER(4); + D_RETURN(4, + strcmp(FEH_FILE(file1)->info->format, + FEH_FILE(file2)->info->format)); +} + +void +feh_prepare_filelist(void) +{ + D_ENTER(4); + if (opt.list || opt.customlist || (opt.sort > SORT_FILENAME) + || opt.preload) + { + /* For these sort options, we have to preload images */ + filelist = feh_file_info_preload(filelist); + if (!gib_list_length(filelist)) + show_mini_usage(); + } + + D(3, ("sort mode requested is: %d\n", opt.sort)); + switch (opt.sort) + { + case SORT_NONE: + if (opt.randomize) + { + /* Randomize the filename order */ + filelist = gib_list_randomize(filelist); + } + else if (!opt.reverse) + { + /* Let's reverse the list. Its back-to-front right now ;) */ + filelist = gib_list_reverse(filelist); + } + break; + case SORT_NAME: + filelist = gib_list_sort(filelist, feh_cmp_name); + break; + case SORT_FILENAME: + filelist = gib_list_sort(filelist, feh_cmp_filename); + break; + case SORT_WIDTH: + filelist = gib_list_sort(filelist, feh_cmp_width); + break; + case SORT_HEIGHT: + filelist = gib_list_sort(filelist, feh_cmp_height); + break; + case SORT_PIXELS: + filelist = gib_list_sort(filelist, feh_cmp_pixels); + break; + case SORT_SIZE: + filelist = gib_list_sort(filelist, feh_cmp_size); + break; + case SORT_FORMAT: + filelist = gib_list_sort(filelist, feh_cmp_format); + break; + default: + break; + } + + /* no point reversing a random list */ + if (opt.reverse && (opt.sort != SORT_NONE)) + { + D(3, ("Reversing filelist as requested\n")); + filelist = gib_list_reverse(filelist); + } + + D_RETURN_(4); +} + +int +feh_write_filelist(gib_list * list, char *filename) +{ + FILE *fp; + gib_list *l; + + D_ENTER(4); + + if (!list || !filename) + D_RETURN(4, 0); + + errno = 0; + if ((fp = fopen(filename, "w")) == NULL) + { + weprintf("can't write filelist %s:", filename); + D_RETURN(4, 0); + } + + for (l = list; l; l = l->next) + fprintf(fp, "%s\n", (FEH_FILE(l->data)->filename)); + + fclose(fp); + + D_RETURN(4, 1); +} + +gib_list * +feh_read_filelist(char *filename) +{ + FILE *fp; + gib_list *list = NULL; + char s[1024], s1[1024]; + Imlib_Image im1; + + D_ENTER(4); + + if (!filename) + D_RETURN(4, NULL); + + /* try and load the given filelist as an image, cowardly refuse to + * overwrite an image with a filelist. (requested by user who did feh -df * + * when he meant feh -dF *, as it overwrote the first image with the + * filelist). + */ + if (feh_load_image_char(&im1, filename)) { + weprintf("The file you specified as a filelist to read - %s - appears to be an image. Ignoring it (this is a common mistake).\n", filename); + opt.filelistfile = NULL; + D_RETURN(4, NULL); + } + + errno = 0; + if ((fp = fopen(filename, "r")) == NULL) + { + /* return quietly, as it's okay to specify a filelist file that doesn't + exist. In that case we create it on exit. */ + D_RETURN(4, NULL); + } + + for (; fgets(s, sizeof(s), fp);) + { + D(5, ("Got line '%s'\n", s)); + s1[0] = '\0'; + sscanf(s, "%[^\n]", (char *) &s1); + if (!(*s1) || (*s1 == '\n')) + continue; + D(5, ("Got filename %s from filelist file\n", s1)); + /* Add it to the new list */ + list = gib_list_add_front(list, feh_file_new(s1)); + } + fclose(fp); + + D_RETURN(4, list); +} + +char * +feh_absolute_path(char *path) +{ + char cwd[PATH_MAX]; + char fullpath[PATH_MAX]; + char temp[PATH_MAX]; + char *ret; + + D_ENTER(4); + + if (!path) + D_RETURN(4, NULL); + if (path[0] == '/') + D_RETURN(4, estrdup(path)); + /* This path is not relative. We're gonna convert it, so that a + filelist file can be saved anywhere and feh will still find the + images */ + D(4, ("Need to convert %s to an absolute form\n", path)); + /* I SHOULD be able to just use a simple realpath() here, but dumb * + old Solaris's realpath doesn't return an absolute path if the + path you give it is relative. Linux and BSD get this right... */ + getcwd(cwd, sizeof(cwd)); + snprintf(temp, sizeof(temp), "%s/%s", cwd, path); + if (realpath(temp, fullpath) != NULL) + { + ret = estrdup(fullpath); + } + else + { + ret = estrdup(temp); + } + D(4, ("Converted path to %s\n", ret)); + D_RETURN(4, ret); +} + +void feh_save_filelist() +{ + char *tmpname; + + D_ENTER(4); + + tmpname = + feh_unique_filename("", "filelist"); + + if(!opt.quiet) + printf("saving filelist to filename '%s'\n", tmpname); + + feh_write_filelist(filelist, tmpname); + free(tmpname); + D_RETURN_(4); +} diff --git a/src/filelist.h b/src/filelist.h new file mode 100644 index 0000000..a292bb4 --- /dev/null +++ b/src/filelist.h @@ -0,0 +1,89 @@ +/* filelist.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef FILELIST_H +#define FILELIST_H + +struct __feh_file +{ + char *filename; + char *caption; + char *name; + + /* info stuff */ + feh_file_info *info; /* only set when needed */ +}; + +struct __feh_file_info +{ + int width; + int height; + int size; + int pixels; + unsigned char has_alpha; + char *format; + char *extension; +}; + +#define FEH_FILE(l) ((feh_file *) l) + +enum filelist_recurse +{ FILELIST_FIRST, FILELIST_CONTINUE, FILELIST_LAST }; + +enum sort_type +{ SORT_NONE, SORT_NAME, SORT_FILENAME, SORT_WIDTH, SORT_HEIGHT, SORT_PIXELS, + SORT_SIZE, SORT_FORMAT +}; + +feh_file *feh_file_new(char *filename); +void feh_file_free(feh_file * file); +feh_file_info *feh_file_info_new(void); +void feh_file_info_free(feh_file_info * info); +gib_list *feh_file_rm_and_free(gib_list * list, gib_list * file); +void add_file_to_filelist_recursively(char *origpath, unsigned char level); +void add_file_to_rm_filelist(char *file); +void delete_rm_files(void); +gib_list *feh_file_info_preload(gib_list * list); +int feh_file_info_load(feh_file * file, Imlib_Image im); +void feh_prepare_filelist(void); +int feh_write_filelist(gib_list * list, char *filename); +gib_list *feh_read_filelist(char *filename); +char *feh_absolute_path(char *path); +gib_list *feh_file_remove_from_list(gib_list * list, gib_list * l); +void feh_save_filelist(); + +int feh_cmp_name(void *file1, void *file2); +int feh_cmp_filename(void *file1, void *file2); +int feh_cmp_width(void *file1, void *file2); +int feh_cmp_height(void *file1, void *file2); +int feh_cmp_pixels(void *file1, void *file2); +int feh_cmp_size(void *file1, void *file2); +int feh_cmp_format(void *file1, void *file2); + +extern gib_list *filelist; +extern int filelist_len; +extern gib_list *current_file; + +#endif diff --git a/src/getopt.c b/src/getopt.c new file mode 100644 index 0000000..4503953 --- /dev/null +++ b/src/getopt.c @@ -0,0 +1,1045 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 + Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#include +#endif /* GNU C library. */ + +#ifdef VMS +#include +#if HAVE_STRING_H - 0 +#include +#endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +#ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +#else +# define _(msgid) (msgid) +#endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} +ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv(); + +static char * +my_index(str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen(const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void __attribute__ ((unused)) store_args_and_env(int argc, + char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so that + we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} + +# ifdef text_set_element +text_set_element(__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined (__STDC__) && __STDC__ +static void exchange(char **); +#endif + +static void +exchange(argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. It leaves the + longer segment in the right place overall, but it consists of two parts + that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' string + can work normally. Our top argument must be in the range of the + string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and presents + new arguments. */ + char *new_str = malloc(top + 1); + + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset(__mempcpy + (new_str, __getopt_nonoption_flags, nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS(bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS(bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined (__STDC__) && __STDC__ +static const char *_getopt_initialize(int, char *const *, const char *); +#endif +static const char * +_getopt_initialize(argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 is + the program name); the sequence of previously skipped non-option + ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL && argc == original_argc + && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen(orig_str); + + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc(nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset(__mempcpy(__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal(argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the + program name. */ + optstring = _getopt_initialize(argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. Either it + does not have option syntax, or there is an environment flag from the + shell indicating it is not an option. The later information is only + used when the used in the GNU libc. */ +#ifdef _LIBC +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some + non-options, exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options and extend the range of + non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. Skip + it like a null option, then exchange with previous non-options as if + it were an option, then skip everything else like a non-option. */ + + if (optind != argc && !strcmp(argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan and back over + any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options that we + previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, either stop + the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. Skip the initial + punctuation. */ + + nextchar = + (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is a valid + short option, don't consider it an abbreviated form of a long option + that starts with f. Otherwise there would be no way to give the -f + short option. + + On the other hand, if there's a long option "fubar" and the + ARGV-element is "-fu", do consider that an abbreviation of the long + option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only + && (argv[optind][2] + || !my_index(optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match or abbreviated matches. + */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp(p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == + (unsigned int) strlen(p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf(stderr, _("%s: option `%s' is ambiguous\n"), argv[0], + argv[optind]); + nextchar += strlen(nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf(stderr, + _ + ("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf(stderr, + _ + ("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen(nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf(stderr, _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen(nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen(nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, or + the option starts with '--' or is not a valid short option, then + it's an error. Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index(optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf(stderr, _("%s: unrecognized option `--%s'\n"), argv[0], + nextchar); + else + /* +option or -option */ + fprintf(stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], + argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index(optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf(stderr, _("%s: illegal option -- %c\n"), argv[0], c); + else + fprintf(stderr, _("%s: invalid option -- %c\n"), argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, we + must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf(stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; increment it again when + taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the table of longopts. + */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; + nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match or abbreviated + matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp(p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen(p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen(nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf(stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); + + nextchar += strlen(nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf(stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen(nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen(nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. + */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf(stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; increment it again + when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt(argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal(argc, argv, optstring, (const struct option *) 0, + (int *) 0, 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt(argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf("option %c\n", c); + break; + + case 'a': + printf("option a\n"); + break; + + case 'b': + printf("option b\n"); + break; + + case 'c': + printf("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + } + + exit(0); +} + +#endif /* TEST */ diff --git a/src/getopt.h b/src/getopt.h new file mode 100644 index 0000000..73ec1b7 --- /dev/null +++ b/src/getopt.h @@ -0,0 +1,136 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + + extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + + extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + + extern int opterr; + +/* Set to an option character which was unrecognized. */ + + extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + + struct option + { +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about type + mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; + }; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ + extern int getopt(int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +#ifndef __cplusplus +/* C++ is more pedantic, and demands a full prototype, not this. + Hope that stdlib.h has a prototype for `getopt'. */ + extern int getopt(); +#endif /* __cplusplus */ +#endif /* __GNU_LIBRARY__ */ + extern int getopt_long(int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); + extern int getopt_long_only(int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ + extern int _getopt_internal(int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ + extern int getopt(); + extern int getopt_long(); + extern int getopt_long_only(); + + extern int _getopt_internal(); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* getopt.h */ diff --git a/src/getopt1.c b/src/getopt1.c new file mode 100644 index 0000000..24dd347 --- /dev/null +++ b/src/getopt1.c @@ -0,0 +1,187 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "getopt.h" + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long(argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal(argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only(argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal(argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = + getopt_long(argc, argv, "abc:d:0123456789", long_options, + &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf("option %s", long_options[option_index].name); + if (optarg) + printf(" with arg %s", optarg); + printf("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf("option %c\n", c); + break; + + case 'a': + printf("option a\n"); + break; + + case 'b': + printf("option b\n"); + break; + + case 'c': + printf("option c with value `%s'\n", optarg); + break; + + case 'd': + printf("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + } + + exit(0); +} + +#endif /* TEST */ diff --git a/src/imlib.c b/src/imlib.c new file mode 100644 index 0000000..fc4dcf0 --- /dev/null +++ b/src/imlib.c @@ -0,0 +1,1292 @@ +/* imlib.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "winwidget.h" +#include "options.h" + +#include +#include +#include +#include +#include +#include + +#include "transupp.h" + +Display *disp = NULL; +Visual *vis = NULL; +Screen *scr = NULL; +Colormap cm; +int depth; +Atom wmDeleteWindow; +XContext xid_context = 0; +Window root = 0; + +/* Xinerama support */ +#ifdef HAVE_LIBXINERAMA +XineramaScreenInfo *xinerama_screens = NULL; +int xinerama_screen; +int num_xinerama_screens; +#endif /* HAVE_LIBXINERAMA */ + +#ifdef HAVE_LIBXINERAMA +void +init_xinerama(void) +{ + if (opt.xinerama && XineramaIsActive(disp)) { + int major, minor; + xinerama_screen = 0; + XineramaQueryVersion(disp, &major, &minor); + xinerama_screens = XineramaQueryScreens(disp, &num_xinerama_screens); + } +} +#endif /* HAVE_LIBXINERAMA */ + +void +init_x_and_imlib(void) +{ + D_ENTER(4); + + disp = XOpenDisplay(NULL); + if (!disp) + eprintf("Can't open X display. It *is* running, yeah?"); + vis = DefaultVisual(disp, DefaultScreen(disp)); + depth = DefaultDepth(disp, DefaultScreen(disp)); + cm = DefaultColormap(disp, DefaultScreen(disp)); + root = RootWindow(disp, DefaultScreen(disp)); + scr = ScreenOfDisplay(disp, DefaultScreen(disp)); + xid_context = XUniqueContext(); + +#ifdef HAVE_LIBXINERAMA + init_xinerama(); +#endif /* HAVE_LIBXINERAMA */ + + imlib_context_set_display(disp); + imlib_context_set_visual(vis); + imlib_context_set_colormap(cm); + imlib_context_set_color_modifier(NULL); + imlib_context_set_progress_function(NULL); + imlib_context_set_operation(IMLIB_OP_COPY); + wmDeleteWindow = XInternAtom(disp, "WM_DELETE_WINDOW", False); + + /* Initialise random numbers */ + srand(getpid() * time(NULL) % ((unsigned int) -1)); + + /* Set up the font stuff */ + imlib_add_path_to_font_path("."); + imlib_add_path_to_font_path(PREFIX "/share/feh/fonts"); + imlib_add_path_to_font_path("./ttfonts"); + + D_RETURN_(4); +} + +int +feh_load_image_char(Imlib_Image * im, char *filename) +{ + feh_file *file; + int i; + + D_ENTER(4); + file = feh_file_new(filename); + i = feh_load_image(im, file); + feh_file_free(file); + D_RETURN(4, i); +} + +int +feh_load_image(Imlib_Image * im, feh_file * file) +{ + Imlib_Load_Error err; + + D_ENTER(4); + D(3, ("filename is %s, image is %p\n", file->filename, im)); + + if (!file || !file->filename) + D_RETURN(4, 0); + + /* Handle URLs */ + if ((!strncmp(file->filename, "http://", 7)) || + (!strncmp(file->filename, "https://", 8)) || + (!strncmp(file->filename, "ftp://", 6))) + { + char *tmpname = NULL; + char *tempcpy; + + tmpname = feh_http_load_image(file->filename); + if (tmpname == NULL) + D_RETURN(4, 0); + *im = imlib_load_image_with_error_return(tmpname, &err); + if (im) + { + /* load the info now, in case it's needed after we delete the + temporary image file */ + tempcpy = file->filename; + file->filename = tmpname; + feh_file_info_load(file, *im); + file->filename = tempcpy; + } + if ((opt.slideshow) && (opt.reload == 0)) + { + /* Http, no reload, slideshow. Let's keep this image on hand... */ + free(file->filename); + file->filename = estrdup(tmpname); + } + else + { + /* Don't cache the image if we're doing reload + http (webcams etc) */ + if (!opt.keep_http) + unlink(tmpname); + } + if (!opt.keep_http) + add_file_to_rm_filelist(tmpname); + free(tmpname); + } + else + { + *im = imlib_load_image_with_error_return(file->filename, &err); + } + + if ((err) || (!im)) + { + if (opt.verbose && !opt.quiet) + { + fprintf(stdout, "\n"); + reset_output = 1; + } + /* Check error code */ + switch (err) + { + case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST: + if (!opt.quiet) + weprintf("%s - File does not exist", file->filename); + break; + case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY: + if (!opt.quiet) + weprintf("%s - Directory specified for image filename", + file->filename); + break; + case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ: + if (!opt.quiet) + weprintf("%s - No read access to directory", file->filename); + break; + case IMLIB_LOAD_ERROR_UNKNOWN: + case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT: + if (!opt.quiet) + weprintf("%s - No Imlib2 loader for that file format", + file->filename); + break; + case IMLIB_LOAD_ERROR_PATH_TOO_LONG: + if (!opt.quiet) + weprintf("%s - Path specified is too long", file->filename); + break; + case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT: + if (!opt.quiet) + weprintf("%s - Path component does not exist", file->filename); + break; + case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY: + if (!opt.quiet) + weprintf("%s - Path component is not a directory", + file->filename); + break; + case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE: + if (!opt.quiet) + weprintf("%s - Path points outside address space", + file->filename); + break; + case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS: + if (!opt.quiet) + weprintf("%s - Too many levels of symbolic links", + file->filename); + break; + case IMLIB_LOAD_ERROR_OUT_OF_MEMORY: + if (!opt.quiet) + weprintf("While loading %s - Out of memory", file->filename); + break; + case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS: + eprintf("While loading %s - Out of file descriptors", + file->filename); + break; + case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE: + if (!opt.quiet) + weprintf("%s - Cannot write to directory", file->filename); + break; + case IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE: + if (!opt.quiet) + weprintf("%s - Cannot write - out of disk space", + file->filename); + break; + default: + if (!opt.quiet) + weprintf + ("While loading %s - Unknown error (%d). Attempting to continue", + file->filename, err); + break; + } + D(3, ("Load *failed*\n")); + D_RETURN(4, 0); + } + + D(3, ("Loaded ok\n")); + D_RETURN(4, 1); +} + +char * +feh_http_load_image(char *url) +{ + char *tmpname; + char *tmpname_timestamper = NULL; + char *basename; + char *newurl = NULL; + char randnum[20]; + int rnum; + char *path = NULL; + + D_ENTER(4); + + if (opt.keep_http) + { + if (opt.output_dir) + path = opt.output_dir; + else + path = ""; + } + else + path = "/tmp/"; + + basename = strrchr(url, '/') + 1; + tmpname = feh_unique_filename(path, basename); + + if (opt.wget_timestamp) + { + char cppid[10]; + pid_t ppid; + + ppid = getpid(); + snprintf(cppid, sizeof(cppid), "%06ld", (long)ppid); + tmpname_timestamper = + estrjoin("", "/tmp/feh_", cppid, "_", basename, NULL); + } + + if (opt.wget_timestamp) + { + newurl = estrdup(url); + } + else + { + rnum = rand(); + snprintf(randnum, sizeof(randnum), "%d", rnum); + newurl = estrjoin("?", url, randnum, NULL); + } + D(3, ("newurl: %s\n", newurl)); + + if (opt.builtin_http) + { + /* state for HTTP header parser */ +#define SAW_NONE 1 +#define SAW_ONE_CM 2 +#define SAW_ONE_CJ 3 +#define SAW_TWO_CM 4 +#define IN_BODY 5 + +#define OUR_BUF_SIZE 1024 +#define EOL "\015\012" + + int sockno = 0; + int size; + int body = SAW_NONE; + struct sockaddr_in addr; + struct hostent *hptr; + char *hostname; + char *get_string; + char *host_string; + char *query_string; + char *get_url; + static char buf[OUR_BUF_SIZE]; + char ua_string[] = "User-Agent: feh image viewer"; + char accept_string[] = "Accept: image/*"; + FILE *fp; + + D(4, ("using builtin http collection\n")); + fp = fopen(tmpname, "w"); + if (!fp) + { + weprintf("couldn't write to file %s:", tmpname); + free(tmpname); + D_RETURN(4, NULL); + } + + hostname = feh_strip_hostname(newurl); + if (!hostname) + { + weprintf("couldn't work out hostname from %s:", newurl); + free(tmpname); + free(newurl); + D_RETURN(4, NULL); + } + + D(4, ("trying hostname %s\n", hostname)); + + if (!(hptr = feh_gethostbyname(hostname))) + { + weprintf("error resolving host %s:", hostname); + free(hostname); + free(tmpname); + free(newurl); + D_RETURN(4, NULL); + } + + /* Copy the address of the host to socket description. */ + memcpy(&addr.sin_addr, hptr->h_addr, hptr->h_length); + + /* Set port and protocol */ + addr.sin_family = AF_INET; + addr.sin_port = htons(80); + + if ((sockno = socket(PF_INET, SOCK_STREAM, 0)) == -1) + { + weprintf("error opening socket:"); + free(tmpname); + free(hostname); + free(newurl); + D_RETURN(4, NULL); + } + if (connect(sockno, (struct sockaddr *) &addr, sizeof(addr)) == -1) + { + weprintf("error connecting socket:"); + free(tmpname); + free(hostname); + free(newurl); + D_RETURN(4, NULL); + } + + get_url = strchr(newurl, '/') + 2; + get_url = strchr(get_url, '/'); + + get_string = estrjoin(" ", "GET", get_url, "HTTP/1.0", NULL); + host_string = estrjoin(" ", "Host:", hostname, NULL); + query_string = + estrjoin(EOL, get_string, host_string, accept_string, ua_string, "", + "", NULL); + /* At this point query_string looks something like + ** + ** GET /dir/foo.jpg?123456 HTTP/1.0^M^J + ** Host: www.example.com^M^J + ** Accept: image/ *^M^J + ** User-Agent: feh image viewer^M^J + ** ^M^J + ** + ** Host: is required by HTTP/1.1 and very important for some sites, + ** even with HTTP/1.0 + ** + ** -- BEG + */ + if ((send(sockno, query_string, strlen(query_string), 0)) == -1) + { + free(get_string); + free(host_string); + free(query_string); + free(tmpname); + free(hostname); + free(newurl); + weprintf("error sending over socket:"); + D_RETURN(4, NULL); + } + free(get_string); + free(host_string); + free(query_string); + free(hostname); + free(newurl); + + while ((size = read(sockno, &buf, OUR_BUF_SIZE))) + { + if (body == IN_BODY) + { + fwrite(buf, 1, size, fp); + } + else + { + int i; + + for (i = 0; i < size; i++) + { + /* We are looking for ^M^J^M^J, but will accept + ** ^J^J from broken servers. Stray ^Ms will be + ** ignored. + ** + ** TODO: + ** Checking the headers for a + ** Content-Type: image/ * + ** header would help detect problems with results. + ** Maybe look at the response code too? But there is + ** no fundamental reason why a 4xx or 5xx response + ** could not return an image, it is just the 3xx + ** series we need to worry about. + ** + ** Also, grabbing the size from the Content-Length + ** header and killing the connection after that + ** many bytes where read would speed up closing the + ** socket. + ** -- BEG + */ + + switch (body) + { + + case IN_BODY: + fwrite(buf + i, 1, size - i, fp); + i = size; + break; + + case SAW_ONE_CM: + if (buf[i] == '\012') + { + body = SAW_ONE_CJ; + } + else + { + body = SAW_NONE; + } + break; + + case SAW_ONE_CJ: + if (buf[i] == '\015') + { + body = SAW_TWO_CM; + } + else + { + if (buf[i] == '\012') + { + body = IN_BODY; + } + else + { + body = SAW_NONE; + } + } + break; + + case SAW_TWO_CM: + if (buf[i] == '\012') + { + body = IN_BODY; + } + else + { + body = SAW_NONE; + } + break; + + case SAW_NONE: + if (buf[i] == '\015') + { + body = SAW_ONE_CM; + } + else + { + if (buf[i] == '\012') + { + body = SAW_ONE_CJ; + } + } + break; + + } /* switch */ + } /* for i */ + } + } /* while read */ + close(sockno); + fclose(fp); + } + else + { + int pid; + int status; + + if ((pid = fork()) < 0) + { + weprintf("open url: fork failed:"); + free(tmpname); + free(newurl); + D_RETURN(4, NULL); + } + else if (pid == 0) + { + char *quiet = NULL; + + if (!opt.verbose) + quiet = estrdup("-q"); + + if (opt.wget_timestamp) + { + execlp("wget", "wget", "-N", "-O", tmpname_timestamper, newurl, + quiet, (char*) NULL); + } + else + { + execlp("wget", "wget", "--cache", "0", newurl, "-O", tmpname, + quiet, (char*) NULL); + } + eprintf("url: exec failed: wget:"); + } + else + { + waitpid(pid, &status, 0); + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + { + weprintf("url: wget failed to load URL %s\n", url); + free(tmpname); + free(newurl); + D_RETURN(4, NULL); + } + if (opt.wget_timestamp) + { + char cmd[2048]; + + snprintf(cmd, sizeof(cmd), "/bin/cp %s %s", tmpname_timestamper, + tmpname); + system(cmd); + } + } + } + + free(newurl); + D_RETURN(4, tmpname); +} + +struct hostent * +feh_gethostbyname(const char *name) +{ + struct hostent *hp; + unsigned long addr; + + D_ENTER(3); + addr = (unsigned long) inet_addr(name); + if ((int) addr != -1) + hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); + else + hp = gethostbyname(name); + D_RETURN(3, hp); +} + +char * +feh_strip_hostname(char *url) +{ + char *ret; + char *start; + char *finish; + int len; + + D_ENTER(3); + + start = strchr(url, '/'); + if (!start) + D_RETURN(3, NULL); + + start += 2; + + finish = strchr(start, '/'); + if (!finish) + D_RETURN(3, NULL); + + len = finish - start; + + ret = emalloc(len + 1); + strncpy(ret, start, len); + ret[len] = '\0'; + D_RETURN(3, ret); +} + +void +feh_draw_zoom(winwidget w) +{ + static Imlib_Font fn = NULL; + int tw = 0, th = 0; + Imlib_Image im = NULL; + char buf[100]; + static DATA8 atab[256]; + + D_ENTER(4); + + if (!w->im) + D_RETURN_(4); + + if (!fn) { + fn = gib_imlib_load_font(DEFAULT_FONT); + memset(atab, 0, sizeof(atab)); + } + + if (!fn) + { + weprintf("Couldn't load font for zoom printing"); + D_RETURN_(4); + } + + snprintf(buf, sizeof(buf), "%.0f%%, %dx%d", w->zoom * 100, + (int) (w->im_w * w->zoom), (int) (w->im_h * w->zoom)); + + /* Work out how high the font is */ + gib_imlib_get_text_size(fn, buf, NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); + + tw += 3; + th += 3; + im = imlib_create_image(tw, th); + if (!im) + eprintf("Couldn't create image. Out of memory?"); + + gib_imlib_image_set_has_alpha(im, 1); + gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, th, + NULL, NULL, NULL, atab); + gib_imlib_image_fill_rectangle(im, 0, 0, tw, th, 0, 0, 0, 0); + + gib_imlib_text_draw(im, fn, NULL, 2, 2, buf, IMLIB_TEXT_TO_RIGHT, + 0, 0, 0, 255); + gib_imlib_text_draw(im, fn, NULL, 1, 1, buf, IMLIB_TEXT_TO_RIGHT, + 255, 255, 255, 255); + gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, w->h - th, 1, 1, 0); + gib_imlib_free_image_and_decache(im); + D_RETURN_(4); +} + +void +feh_draw_filename(winwidget w) +{ + static Imlib_Font fn = NULL; + int tw = 0, th = 0; + Imlib_Image im = NULL; + static DATA8 atab[256]; + char *s = NULL; + int len = 0; + + D_ENTER(4); + + if ((!w->file) || (!FEH_FILE(w->file->data)) + || (!FEH_FILE(w->file->data)->filename)) + D_RETURN_(4); + + if (!fn) + { + memset(atab, 0, sizeof(atab)); + if (w->full_screen) + fn = gib_imlib_load_font(DEFAULT_FONT_BIG); + else + fn = gib_imlib_load_font(DEFAULT_FONT); + } + + if (!fn) + { + weprintf("Couldn't load font for filename printing"); + D_RETURN_(4); + } + + /* Work out how high the font is */ + gib_imlib_get_text_size(fn, FEH_FILE(w->file->data)->filename, NULL, &tw, &th, + IMLIB_TEXT_TO_RIGHT); + + /* tw is no longer correct, if the filename is shorter than + * the string "%d of %d" used below in fullscreen mode */ + tw += 3; + th += 3; + im = imlib_create_image(tw, 2*th); + if (!im) + eprintf("Couldn't create image. Out of memory?"); + + gib_imlib_image_set_has_alpha(im, 1); + gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, 2*th, + NULL, NULL, NULL, atab); + gib_imlib_image_fill_rectangle(im, 0, 0, tw, 2*th, 0, 0, 0, 0); + + gib_imlib_text_draw(im, fn, NULL, 2, 2, FEH_FILE(w->file->data)->filename, + IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); + gib_imlib_text_draw(im, fn, NULL, 1, 1, FEH_FILE(w->file->data)->filename, + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + /* Print the position in the filelist, if we are in fullscreen and the + * list has more than one element */ + if (w->full_screen && (gib_list_length(filelist)-1)) + { + /* sic! */ + len = snprintf(NULL, 0, "%d of %d", gib_list_length(filelist), gib_list_length(filelist))+1; + s = emalloc(len); + snprintf(s, len, "%d of %d", + gib_list_num(filelist, current_file) + 1, + gib_list_length(filelist)); + /* This should somehow be right-aligned */ + gib_imlib_text_draw(im, fn, NULL, 2, th+1, s, IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); + gib_imlib_text_draw(im, fn, NULL, 1, th, s, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + free(s); + } + + gib_imlib_render_image_on_drawable(w->bg_pmap, im, 0, 0, 1, 1, 0); + + gib_imlib_free_image_and_decache(im); + D_RETURN_(4); +} + +char *build_caption_filename(feh_file *file) { + char *caption_filename; + char *s, *dir; + s = strrchr(file->filename, '/'); + if (s) { + dir = estrdup(file->filename); + s = strrchr(dir, '/'); + *s = '\0'; + } else { + dir = estrdup("."); + } + caption_filename = estrjoin("", + dir, + "/", + opt.caption_path, + "/", + file->name, + ".txt", + NULL); + free(dir); + return caption_filename; +} + +void +feh_draw_caption(winwidget w) +{ + static Imlib_Font fn = NULL; + int tw = 0, th = 0, ww, hh; + int x, y; + Imlib_Image im = NULL; + static DATA8 atab[256]; + char *p; + gib_list *lines, *l; + static gib_style *caption_style = NULL; + feh_file *file; + + D_ENTER(4); + + if (!w->file) { + D_RETURN_(4); + } + file = FEH_FILE(w->file->data); + if (!file->filename) { + D_RETURN_(4); + } + + if (!file->caption) { + char *caption_filename; + caption_filename = build_caption_filename(file); + /* read caption from file */ + file->caption = ereadfile(caption_filename); + free(caption_filename); + } + + if (file->caption == NULL) { + /* caption file is not there, we want to cache that, otherwise we'll stat + * the damn file every time we render the image. Reloading an image will + * always cause the caption to be reread though so we're safe to do so. + * (Before this bit was added, when zooming a captionless image with + * captions enabled, the captions file would be stat()d like 30 times a + * second) - don't forget this function is called from + * winwidget_render_image(). + */ + file->caption = estrdup(""); + D_RETURN_(4); + } + + if (file->caption == '\0') { + D_RETURN_(4); + } + + if (!caption_style) { + caption_style = gib_style_new("caption"); + caption_style->bits = gib_list_add_front(caption_style->bits, + gib_style_bit_new(0,0,0,0,0,0)); + caption_style->bits = gib_list_add_front(caption_style->bits, + gib_style_bit_new(1,1,0,0,0,255)); + } + + if (!fn) + { + memset(atab, 0, sizeof(atab)); + if (w->full_screen) + fn = gib_imlib_load_font(DEFAULT_FONT_BIG); + else + fn = gib_imlib_load_font(DEFAULT_FONT); + } + + if (!fn) + { + weprintf("Couldn't load font for caption printing"); + D_RETURN_(4); + } + + lines = feh_wrap_string(file->caption, w->w, w->h, fn, NULL); + if (!lines) + D_RETURN_(4); + + /* Work out how high/wide the caption is */ + l = lines; + while (l) { + p = (char *) l->data; + gib_imlib_get_text_size(fn, p, caption_style, &ww, &hh, IMLIB_TEXT_TO_RIGHT); + if (ww > tw) + tw = ww; + th += hh; + if (l->next) + th += 1; /* line spacing */ + l = l->next; + } + + /* we don't want the caption overlay larger than our window */ + if (th > w->h) + th = w->h; + if (tw > w->w) + tw = w->w; + + im = imlib_create_image(tw, th); + if (!im) + eprintf("Couldn't create image. Out of memory?"); + + gib_imlib_image_set_has_alpha(im, 1); + gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, th, + NULL, NULL, NULL, atab); + gib_imlib_image_fill_rectangle(im, 0, 0, tw, th, 0, 0, 0, 0); + + l = lines; + x = 0; + y = 0; + while (l) { + p = (char *) l->data; + gib_imlib_get_text_size(fn, p, caption_style, &ww, &hh, IMLIB_TEXT_TO_RIGHT); + x = (tw - ww) / 2; + if (w->caption_entry) { + gib_imlib_text_draw(im, fn, caption_style, x, y, p, IMLIB_TEXT_TO_RIGHT, + 255, 255, 0, 255); + } else { + gib_imlib_text_draw(im, fn, caption_style, x, y, p, IMLIB_TEXT_TO_RIGHT, + 255, 255, 255, 255); + } + + y += hh + 1; /* line spacing */ + l = l->next; + } + + gib_imlib_render_image_on_drawable(w->bg_pmap, im, (w->w - tw) / 2, w->h - th, 1, 1, 0); + gib_imlib_free_image_and_decache(im); + gib_list_free_and_data(lines); + D_RETURN_(4); +} + + +unsigned char reset_output = 0; + +void +feh_display_status(char stat) +{ + static int i = 0; + static int init_len = 0; + int j = 0; + + D_ENTER(5); + + D(5, ("filelist %p, filelist->next %p\n", filelist, filelist->next)); + + if (!init_len) + init_len = gib_list_length(filelist); + + if (i) + { + if (reset_output) + { + /* There's just been an error message. Unfortunate ;) */ + for (j = 0; j < (((i % 50) + ((i % 50) / 10)) + 7); j++) + fprintf(stdout, " "); + } + + if (!(i % 50)) + { + int len; + char buf[50]; + + len = gib_list_length(filelist); + snprintf(buf, sizeof(buf), " %5d/%d (%d)\n[%3d%%] ", i, init_len, + len, ((int) ((float) i / init_len * 100))); + fprintf(stdout, buf); + } + else if ((!(i % 10)) && (!reset_output)) + fprintf(stdout, " "); + + reset_output = 0; + } + else + fprintf(stdout, "[ 0%%] "); + + fprintf(stdout, "%c", stat); + fflush(stdout); + i++; + D_RETURN_(5); +} + +void feh_edit_inplace_orient(winwidget w, int orientation) { + int ret; + Imlib_Image old; + D_ENTER(4); + if(!w->file + || !w->file->data + || !FEH_FILE(w->file->data)->filename) + D_RETURN_(4); + + if (!strcmp(gib_imlib_image_format(w->im), "jpeg")) { + feh_edit_inplace_lossless_rotate(w, orientation); + feh_reload_image(w, 1, 1); + D_RETURN_(4); + } + + ret = feh_load_image(&old, FEH_FILE(w->file->data)); + if(ret) { + gib_imlib_image_orientate(old, orientation); + gib_imlib_save_image(old, FEH_FILE(w->file->data)->filename); + gib_imlib_free_image(old); + feh_reload_image(w, 1, 1); + } else { + weprintf("failed to load image from disk to edit it in place\n"); + } + + D_RETURN_(4); +} + + +/* TODO max_height is ignored... Could use a function which generates a + * transparent text overlay image, with wrapping and all. Would be useful */ +gib_list * +feh_wrap_string(char *text, int wrap_width, int max_height, Imlib_Font fn, gib_style * style) +{ + gib_list *ll, *lines = NULL, *list = NULL, *words; + gib_list *l = NULL; + char delim[2] = { '\n', '\0' }; + int w, line_width; + int tw, th; + char *p, *pp; + char *line = NULL; + char *temp; + int space_width = 0, m_width = 0, t_width = 0, new_width = 0; + + lines = gib_string_split(text, delim); + + if (wrap_width) + { + gib_imlib_get_text_size(fn, "M M", style, &t_width, NULL, + IMLIB_TEXT_TO_RIGHT); + gib_imlib_get_text_size(fn, "M", style, &m_width, NULL, + IMLIB_TEXT_TO_RIGHT); + space_width = t_width - (2 * m_width); + w = wrap_width; + l = lines; + while (l) + { + line_width = 0; + p = (char *) l->data; + /* quick check to see if whole line fits okay */ + gib_imlib_get_text_size(fn, p, style, &tw, &th, IMLIB_TEXT_TO_RIGHT); + if (tw <= w) { + list = gib_list_add_end(list, estrdup(p)); + } else if (strlen(p) == 0) { + list = gib_list_add_end(list, estrdup("")); + } else if (!strcmp(p, " ")) { + list = gib_list_add_end(list, estrdup(" ")); + } else { + words = gib_string_split(p, " "); + if (words) { + ll = words; + while (ll) { + pp = (char *) ll->data; + if (strcmp(pp, " ")) { + gib_imlib_get_text_size(fn, pp, style, &tw, &th, + IMLIB_TEXT_TO_RIGHT); + if (line_width == 0) + new_width = tw; + else + new_width = line_width + space_width + tw; + if (new_width <= w) { + /* add word to line */ + if (line) { + int len; + + len = strlen(line) + strlen(pp) + 2; + temp = emalloc(len); + snprintf(temp, len, "%s %s", line, pp); + free(line); + line = temp; + } else { + line = estrdup(pp); + } + line_width = new_width; + } else if (line_width == 0) { + /* can't fit single word in :/ + increase width limit to width of word + and jam the bastard in anyhow */ + w = tw; + line = estrdup(pp); + line_width = new_width; + } else { + /* finish this line, start next and add word there */ + if (line) { + list = gib_list_add_end(list, estrdup(line)); + free(line); + line = NULL; + } + line = estrdup(pp); + line_width = tw; + } + } + ll = ll->next; + } + if (line) { + /* finish last line */ + list = gib_list_add_end(list, estrdup(line)); + free(line); + line = NULL; + line_width = 0; + } + gib_list_free_and_data(words); + } + } + l = l->next; + } + gib_list_free_and_data(lines); + lines = list; + } + return lines; +} + +void feh_edit_inplace_lossless_rotate(winwidget w, int orientation) { + FILE *input_file; + FILE *output_file; + struct jpeg_decompress_struct srcinfo; + struct jpeg_compress_struct dstinfo; + struct jpeg_error_mgr jsrcerr, jdsterr; + jvirt_barray_ptr * src_coef_arrays; + jvirt_barray_ptr * dst_coef_arrays; + JCOPY_OPTION copyoption; + jpeg_transform_info transformoption; + int len; + char *outfilename; + char *infilename = FEH_FILE(w->file->data)->filename; + + copyoption = JCOPYOPT_ALL; + transformoption.transform = JXFORM_NONE; + transformoption.trim = FALSE; + transformoption.force_grayscale = FALSE; + + if (orientation == 1) { + transformoption.transform = JXFORM_ROT_90; + } else if (orientation == 2) { + transformoption.transform = JXFORM_ROT_180; + } else { + transformoption.transform = JXFORM_ROT_270; + } + + if ((input_file = fopen(infilename, "rb")) == NULL) { + weprintf("couldn't open file for reading: %s\n", infilename); + D_RETURN_(4); + } + len = strlen(infilename) + sizeof(".tmp") + 1; + outfilename = emalloc(len); + snprintf(outfilename, len, "%s.tmp", infilename); + + if ((output_file = fopen(outfilename, "wb")) == NULL) { + weprintf("couldn't open file for writing: %s\n", outfilename); + free(outfilename); + fclose(input_file); + D_RETURN_(4); + } + + /* Initialize the JPEG decompression object with default error handling. */ + srcinfo.err = jpeg_std_error(&jsrcerr); + jpeg_create_decompress(&srcinfo); + + /* Initialize the JPEG compression object with default error handling. */ + dstinfo.err = jpeg_std_error(&jdsterr); + jpeg_create_compress(&dstinfo); + jsrcerr.trace_level = jdsterr.trace_level; + + /* Specify data source for decompression */ + jpeg_stdio_src(&srcinfo, input_file); + + /* Enable saving of extra markers that we want to copy */ + jcopy_markers_setup(&srcinfo, copyoption); + + /* Read file header */ + (void) jpeg_read_header(&srcinfo, TRUE); + + /* Any space needed by a transform option must be requested before + * jpeg_read_coefficients so that memory allocation will be done right. + */ + jtransform_request_workspace(&srcinfo, &transformoption); + + /* Read source file as DCT coefficients */ + src_coef_arrays = jpeg_read_coefficients(&srcinfo); + + /* Initialize destination compression parameters from source values */ + jpeg_copy_critical_parameters(&srcinfo, &dstinfo); + + /* Adjust destination parameters if required by transform options; + * also find out which set of coefficient arrays will hold the output. + */ + dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, + src_coef_arrays, + &transformoption); + + /* Specify data destination for compression */ + jpeg_stdio_dest(&dstinfo, output_file); + + /* Start compressor (note no image data is actually written here) */ + jpeg_write_coefficients(&dstinfo, dst_coef_arrays); + + /* Copy to the output file any extra markers that we want to preserve */ + jcopy_markers_execute(&srcinfo, &dstinfo, copyoption); + + /* Execute image transformation */ + jtransform_execute_transformation(&srcinfo, &dstinfo, + src_coef_arrays, + &transformoption); + + /* Finish compression and release memory */ + jpeg_finish_compress(&dstinfo); + jpeg_destroy_compress(&dstinfo); + + (void) jpeg_finish_decompress(&srcinfo); + jpeg_destroy_decompress(&srcinfo); + + fclose(input_file); + fclose(output_file); + + /* TODO fix EXIF tags (orientation, width, height) */ + + /* rename outfilename to infilename.. if it worked */ + if (jsrcerr.num_warnings > 0) { + weprintf("got errors from libjpeg (%d), not replacing file\n", jsrcerr.num_warnings); + } else { + if (rename(outfilename, infilename)) { + weprintf("failed to replace file %s with %s\n", infilename, outfilename); + } + } + free(outfilename); +} + +void +feh_draw_actions(winwidget w) +{ + static Imlib_Font fn = NULL; + int tw = 0, th = 0; + int max_tw = 0; + int line_th = 0; + Imlib_Image im = NULL; + static DATA8 atab[256]; + int i = 0; + int num_actions = 0; + + D_ENTER(4); + +// count the number of defined actions + for (num_actions=0;opt.actions[num_actions];num_actions++) + ; + if (num_actions == 0) + return; + + if ((!w->file) || (!FEH_FILE(w->file->data)) + || (!FEH_FILE(w->file->data)->filename)) + D_RETURN_(4); + + if (!fn) + { + memset(atab, 0, sizeof(atab)); + if (w->full_screen) + fn = gib_imlib_load_font(DEFAULT_FONT_BIG); + else + fn = gib_imlib_load_font(DEFAULT_FONT); + } + + if (!fn) + { + weprintf("Couldn't load font for actions printing"); + D_RETURN_(4); + } + + + gib_imlib_get_text_size(fn, "defined actions:", NULL, &tw, &th, + IMLIB_TEXT_TO_RIGHT); +// Check for the widest line + max_tw = tw; + for (i=0;opt.actions[i];i++) { + gib_imlib_get_text_size(fn, opt.actions[i], NULL, &tw, &th, + IMLIB_TEXT_TO_RIGHT); + if (tw>max_tw) { + max_tw = tw; + } + } + + tw = max_tw; + tw += 3; + th += 3; + line_th = th; + th = (th*num_actions)+line_th; + + im = imlib_create_image(tw, th); + if (!im) + eprintf("Couldn't create image. Out of memory?"); + + gib_imlib_image_set_has_alpha(im, 1); + gib_imlib_apply_color_modifier_to_rectangle(im, 0, 0, tw, th, + NULL, NULL, NULL, atab); + gib_imlib_image_fill_rectangle(im, 0, 0, tw, th, 0, 0, 0, 0); + + gib_imlib_text_draw(im, fn, NULL, 1, 1, "defined actions:", + IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); + gib_imlib_text_draw(im, fn, NULL, 2, 2, "defined actions:", + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + + for(i=0;ibg_pmap, im, 0, 0, 1, 1, 0); + + gib_imlib_free_image_and_decache(im); + D_RETURN_(4); +} diff --git a/src/index.c b/src/index.c new file mode 100644 index 0000000..90f34f8 --- /dev/null +++ b/src/index.c @@ -0,0 +1,611 @@ +/* index.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "winwidget.h" +#include "options.h" + +static char *create_index_dimension_string(int w, int h); +static char *create_index_size_string(char *file); +static char *create_index_title_string(int num, int w, int h); + +/* TODO Break this up a bit ;) */ +/* TODO s/bit/lot */ +void +init_index_mode(void) +{ + Imlib_Image im_main; + Imlib_Image im_temp; + int w = 800, h = 600, ww = 0, hh = 0, www, hhh, xxx, yyy; + int x = 0, y = 0; + int bg_w = 0, bg_h = 0; + winwidget winwid = NULL; + Imlib_Image bg_im = NULL, im_thumb = NULL; + int tot_thumb_h; + int text_area_h = 50; + int title_area_h = 0; + Imlib_Font fn = NULL; + Imlib_Font title_fn = NULL; + int text_area_w = 0; + int tw = 0, th = 0; + int fw_name, fw_size, fw_dim, fw, fh; + int vertical = 0; + int max_column_w = 0; + int thumbnailcount = 0; + gib_list *l = NULL, *last = NULL; + feh_file *file = NULL; + int lines; + unsigned char trans_bg = 0; + int index_image_width, index_image_height; + int x_offset_name = 0, x_offset_dim = 0, x_offset_size = 0; + char *s; + + D_ENTER(3); + + if (opt.montage) { + mode = "montage"; + } else { + mode = "index"; + } + + fn = gib_imlib_load_font(opt.font); + + if (opt.title_font) + { + int fh, fw; + + title_fn = gib_imlib_load_font(opt.title_font); + if (!fn) + title_fn = gib_imlib_load_font(DEFAULT_FONT_TITLE); + gib_imlib_get_text_size(title_fn, "W", NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + title_area_h = fh + 4; + } + else + title_fn = gib_imlib_load_font(DEFAULT_FONT_TITLE); + + if ((!fn) || (!title_fn)) + eprintf("Error loading fonts"); + + /* Work out how tall the font is */ + gib_imlib_get_text_size(fn, "W", NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); + /* For now, allow room for the right number of lines with small gaps */ + text_area_h = + ((th + 2) * (opt.index_show_name + opt.index_show_size + + opt.index_show_dim)) + 5; + + /* This includes the text area for index data */ + tot_thumb_h = opt.thumb_h + text_area_h; + + /* Use bg image dimensions for default size */ + if (opt.bg && opt.bg_file) + { + if (!strcmp(opt.bg_file, "trans")) + trans_bg = 1; + else + { + D(3, ("Time to apply a background to blend onto\n")); + if (feh_load_image_char(&bg_im, opt.bg_file) != 0) + { + bg_w = gib_imlib_image_get_width(bg_im); + bg_h = gib_imlib_image_get_height(bg_im); + } + } + } + + if (!opt.limit_w && !opt.limit_h) + { + if (bg_im) + { + if (opt.verbose) + fprintf(stdout, + PACKAGE " - No size restriction specified for index.\n" + " You did specify a background however, so the\n" + " index size has defaulted to the size of the image\n"); + opt.limit_w = bg_w; + opt.limit_h = bg_h; + } + else + { + if (opt.verbose) + fprintf(stdout, + PACKAGE " - No size restriction specified for index.\n" + " Using defaults (width limited to 800)\n"); + opt.limit_w = 800; + } + } + + + /* Here we need to whiz through the files, and look at the filenames and + info in the selected font, work out how much space we need, and + calculate the size of the image we will require */ + + if (opt.limit_w && opt.limit_h) + { + int rec_h = 0; + + w = opt.limit_w; + h = opt.limit_h; + + /* Work out if this is big enough, and give a warning if not */ + + /* Pretend we are limiting width by that specified, loop through, and + see it we fit in the height specified. If not, continue the loop, + and recommend the final value instead. Carry on and make the index + anyway. */ + + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + text_area_w = opt.thumb_w; + if (opt.index_show_name) + { + gib_imlib_get_text_size(fn, file->name, NULL, &fw, &fh, + IMLIB_TEXT_TO_RIGHT); + if (fw > text_area_w) + text_area_w = fw; + } + if (opt.index_show_dim) + { + gib_imlib_get_text_size(fn, + create_index_dimension_string(1000, 1000), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > text_area_w) + text_area_w = fw; + } + if (opt.index_show_size) + { + gib_imlib_get_text_size(fn, + create_index_size_string(file->filename), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > text_area_w) + text_area_w = fw; + } + if (text_area_w > opt.thumb_w) + text_area_w += 5; + + if ((x > w - text_area_w)) + { + x = 0; + y += tot_thumb_h; + } + + x += text_area_w; + } + rec_h = y + tot_thumb_h; + + if (h < rec_h) + { + weprintf("The image size you specified (%d by %d) is not large\n" + "enough to hold all the thumnails you specified (%d). To fit all\n" + "the thumnails, either decrease their size, choose a smaller font,\n" + "or use a larger image (may I recommend %d by %d?)", + opt.limit_w, opt.limit_h, filelist_len, opt.limit_w, rec_h); + } + } + else if (opt.limit_h) + { + vertical = 1; + h = opt.limit_h; + /* calc w */ + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + text_area_w = opt.thumb_w; + /* Calc width of text */ + if (opt.index_show_name) + { + gib_imlib_get_text_size(fn, file->name, NULL, &fw, &fh, + IMLIB_TEXT_TO_RIGHT); + if (fw > text_area_w) + text_area_w = fw; + } + if (opt.index_show_dim) + { + gib_imlib_get_text_size(fn, + create_index_dimension_string(1000, 1000), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > text_area_w) + text_area_w = fw; + } + if (opt.index_show_size) + { + gib_imlib_get_text_size(fn, + create_index_size_string(file->filename), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > text_area_w) + text_area_w = fw; + } + if (text_area_w > opt.thumb_w) + text_area_w += 5; + + if (text_area_w > max_column_w) + max_column_w = text_area_w; + + if ((y > h - tot_thumb_h)) + { + y = 0; + x += max_column_w; + max_column_w = 0; + } + + y += tot_thumb_h; + } + w = x + text_area_w; + max_column_w = 0; + } + else if (opt.limit_w) + { + w = opt.limit_w; + /* calc h */ + + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + text_area_w = opt.thumb_w; + if (opt.index_show_name) + { + gib_imlib_get_text_size(fn, file->name, NULL, &fw, &fh, + IMLIB_TEXT_TO_RIGHT); + if (fw > text_area_w) + text_area_w = fw; + } + if (opt.index_show_dim) + { + gib_imlib_get_text_size(fn, + create_index_dimension_string(1000, 1000), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > text_area_w) + text_area_w = fw; + } + if (opt.index_show_size) + { + gib_imlib_get_text_size(fn, + create_index_size_string(file->filename), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > text_area_w) + text_area_w = fw; + } + + if (text_area_w > opt.thumb_w) + text_area_w += 5; + + if ((x > w - text_area_w)) + { + x = 0; + y += tot_thumb_h; + } + + x += text_area_w; + } + h = y + tot_thumb_h; + } + + x = y = 0; + + index_image_width = w; + index_image_height = h + title_area_h; + im_main = imlib_create_image(index_image_width, index_image_height); + + if (!im_main) + eprintf("Imlib error creating index image, are you low on RAM?"); + + if (bg_im) + gib_imlib_blend_image_onto_image(im_main, bg_im, + gib_imlib_image_has_alpha(bg_im), 0, 0, + bg_w, bg_h, 0, 0, w, h, 1, 0, 0); + else if (trans_bg) + { + gib_imlib_image_fill_rectangle(im_main, 0, 0, w, h + title_area_h, 0, 0, + 0, 0); + gib_imlib_image_set_has_alpha(im_main, 1); + } + else + { + /* Colour the background */ + gib_imlib_image_fill_rectangle(im_main, 0, 0, w, h + title_area_h, 0, 0, + 0, 255); + } + + /* Create the window title at this point */ + + if (!opt.title) + s = estrdup(PACKAGE " [index mode]"); + else + s = estrdup(feh_printf(opt.title, NULL)); + + if (opt.display) + { + winwid = winwidget_create_from_image(im_main, s, WIN_TYPE_SINGLE); + winwidget_show(winwid); + } + + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + if (last) + { + filelist = feh_file_remove_from_list(filelist, last); + filelist_len--; + last = NULL; + } + D(4, ("About to load image %s\n", file->filename)); + if (feh_load_image(&im_temp, file) != 0) + { + if (opt.verbose) + feh_display_status('.'); + D(4, ("Successfully loaded %s\n", file->filename)); + www = opt.thumb_w; + hhh = opt.thumb_h; + ww = gib_imlib_image_get_width(im_temp); + hh = gib_imlib_image_get_height(im_temp); + thumbnailcount++; + + if (opt.aspect) + { + double ratio = 0.0; + + /* Keep the aspect ratio for the thumbnail */ + ratio = ((double) ww / hh) / ((double) www / hhh); + + if (ratio > 1.0) + hhh = opt.thumb_h / ratio; + else if (ratio != 1.0) + www = opt.thumb_w * ratio; + } + + if ((!opt.stretch) && ((www > ww) || (hhh > hh))) + { + /* Don't make the image larger unless stretch is specified */ + www = ww; + hhh = hh; + } + + im_thumb = + gib_imlib_create_cropped_scaled_image(im_temp, 0, 0, ww, hh, www, + hhh, 1); + gib_imlib_free_image_and_decache(im_temp); + + if (opt.alpha) + { + DATA8 atab[256]; + + D(3, ("Applying alpha options\n")); + gib_imlib_image_set_has_alpha(im_thumb, 1); + memset(atab, opt.alpha_level, sizeof(atab)); + gib_imlib_apply_color_modifier_to_rectangle(im_thumb, 0, 0, www, + hhh, NULL, NULL, NULL, + atab); + } + + text_area_w = opt.thumb_w; + /* Now draw on the info text */ + if (opt.index_show_name) + { + gib_imlib_get_text_size(fn, file->name, NULL, &fw_name, &fh, + IMLIB_TEXT_TO_RIGHT); + if (fw_name > text_area_w) + text_area_w = fw_name; + } + if (opt.index_show_dim) + { + gib_imlib_get_text_size(fn, create_index_dimension_string(ww, hh), + NULL, &fw_dim, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw_dim > text_area_w) + text_area_w = fw_dim; + } + if (opt.index_show_size) + { + gib_imlib_get_text_size(fn, + create_index_size_string(file->filename), + NULL, &fw_size, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw_size > text_area_w) + text_area_w = fw_size; + } + if (text_area_w > opt.thumb_w) + text_area_w += 5; + + /* offsets for centering text */ + x_offset_name = (text_area_w - fw_name) / 2; + x_offset_dim = (text_area_w - fw_dim) / 2; + x_offset_size = (text_area_w - fw_size) / 2; + + if (vertical) + { + if (text_area_w > max_column_w) + max_column_w = text_area_w; + if (y > h - tot_thumb_h) + { + y = 0; + x += max_column_w; + max_column_w = 0; + } + if (x > w - text_area_w) + break; + } + else + { + if (x > w - text_area_w) + { + x = 0; + y += tot_thumb_h; + } + if (y > h - tot_thumb_h) + break; + } + + if (opt.aspect) + { + xxx = x + ((opt.thumb_w - www) / 2); + yyy = y + ((opt.thumb_h - hhh) / 2); + } + else + { + /* Ignore the aspect ratio and squash the image in */ + xxx = x; + yyy = y; + } + + /* Draw now */ + gib_imlib_blend_image_onto_image(im_main, im_thumb, + gib_imlib_image_has_alpha(im_thumb), + 0, 0, www, hhh, xxx, yyy, www, hhh, + 1, + gib_imlib_image_has_alpha(im_thumb), + 0); + + gib_imlib_free_image_and_decache(im_thumb); + + lines = 0; + if (opt.index_show_name) + gib_imlib_text_draw(im_main, fn, NULL, x + x_offset_name, + y + opt.thumb_h + (lines++ * (th + 2)) + 2, + file->name, IMLIB_TEXT_TO_RIGHT, 255, 255, + 255, 255); + if (opt.index_show_dim) + gib_imlib_text_draw(im_main, fn, NULL, x + x_offset_dim, + y + opt.thumb_h + (lines++ * (th + 2)) + 2, + create_index_dimension_string(ww, hh), + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + if (opt.index_show_size) + gib_imlib_text_draw(im_main, fn, NULL, x + x_offset_size, + y + opt.thumb_h + (lines++ * (th + 2)) + 2, + create_index_size_string(file->filename), + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + + if (vertical) + y += tot_thumb_h; + else + x += text_area_w; + + } + else + { + if (opt.verbose) + feh_display_status('x'); + last = l; + } + if (opt.display) + { + winwidget_render_image(winwid, 0, 0); + if (!feh_main_iteration(0)) + exit(0); + } + } + if (opt.verbose) + fprintf(stdout, "\n"); + + if (opt.title_font) + { + int fw, fh, fx, fy; + char *s; + + s = create_index_title_string(thumbnailcount, w, h); + gib_imlib_get_text_size(title_fn, s, NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + fx = (index_image_width - fw) >> 1; + fy = index_image_height - fh - 2; + gib_imlib_text_draw(im_main, title_fn, NULL, fx, fy, s, IMLIB_TEXT_TO_RIGHT, + 255, 255, 255, 255); + } + + if (opt.output && opt.output_file) + { + char output_buf[1024]; + + if (opt.output_dir) + snprintf(output_buf, 1024, "%s/%s", opt.output_dir, opt.output_file); + else + strncpy(output_buf, opt.output_file, 1024); + + gib_imlib_save_image(im_main, output_buf); + if (opt.verbose) + { + int tw, th; + + tw = gib_imlib_image_get_width(im_main); + th = gib_imlib_image_get_height(im_main); + fprintf(stdout, PACKAGE " - File saved as %s\n", output_buf); + fprintf(stdout, + " - Image is %dx%d pixels and contains %d thumbnails\n", + tw, th, thumbnailcount); + } + } + + if (!opt.display) + gib_imlib_free_image_and_decache(im_main); + + free(s); + D_RETURN_(3); +} + + +char * +chop_file_from_full_path(char *str) +{ + D_ENTER(4); + D_RETURN(4, strrchr(str, '/') + 1); +} + +static char * +create_index_size_string(char *file) +{ + static char str[50]; + int size = 0; + double kbs = 0.0; + struct stat st; + + D_ENTER(4); + if (stat(file, &st)) + kbs = 0.0; + else + { + size = st.st_size; + kbs = (double) size / 1000; + } + + snprintf(str, sizeof(str), "%.2fKb", kbs); + D_RETURN(4, str); +} + +static char * +create_index_dimension_string(int w, int h) +{ + static char str[50]; + + D_ENTER(4); + snprintf(str, sizeof(str), "%dx%d", w, h); + D_RETURN(4, str); +} + +static char * +create_index_title_string(int num, int w, int h) +{ + static char str[50]; + + D_ENTER(4); + snprintf(str, sizeof(str), + PACKAGE " index - %d thumbnails, %d by %d pixels", num, w, h); + D_RETURN(4, str); +} diff --git a/src/ipc.c b/src/ipc.c new file mode 100644 index 0000000..4cc38dc --- /dev/null +++ b/src/ipc.c @@ -0,0 +1,94 @@ +/* ipc.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "debug.h" +#include "options.h" + +static int session_id = 0; +static char *socket_name; +static int socket_fd = 0; + +int feh_ipc_create_socket(void) { + struct sockaddr_un saddr; + int i; + + if ((socket_fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) { + for (i = 0; ; i++) { + saddr.sun_family = AF_UNIX; + snprintf(saddr.sun_path, 108, "%s/feh_%s.%d", feh_get_tmp_dir(), feh_get_user_name(), i); +/* + if (!feh_remote_is_running(i)) { + if ((unlink(saddr.sun_path) == -1) && errno != ENOENT) { + close(socket_fd); + eprintf("feh_ipc_create_socket: failed to unlink %s:", saddr.sun_path); + } + } else { + continue; + } +*/ + if (bind(socket_fd, (struct sockaddr *) &saddr, sizeof(saddr)) != -1) { + session_id = i; + socket_name = estrdup(saddr.sun_path); + listen(socket_fd, 50); + break; + } else { + close(socket_fd); + eprintf("feh_ipc_create_socket: failed to bind %s to a socket:", saddr.sun_path); + } + } + } else { + eprintf("feh_ipc_create_socket: failed to open socket:"); + } +} + +int feh_ipc_get_session_id(void) { + return session_id; +} + +void feh_ipc_cleanup(void) { + close(socket_fd); + unlink(socket_name); + free(socket_name); +} + +static void feh_ipc_write_packet(int fd, void *data, int length) { + feh_ipc_server_header header; + header.version = FEH_IPC_VERSION; + header.data_length = length; + if (data && length > 0) { + write(fd, data, length); + } +} + +static void feh_ipc_write_int(int fd, int val) { + feh_ipc_write_packet(fd, &val, sizeof(int)); +} + +static void feh_ipc_write_string(int fd, char *string) { + feh_ipc_write_packet(fd, &string, string ? strlen(string) + 1 : 0); +} + + diff --git a/src/ipc.h b/src/ipc.h new file mode 100644 index 0000000..63f819e --- /dev/null +++ b/src/ipc.h @@ -0,0 +1,57 @@ +/* ipc.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef IPC_H +#define IPC_H + +#define FEH_IPC_VERSION 0x100; + +int feh_ipc_create_socket(void); +int feh_ipc_get_session_id(void); + +typedef struct +{ + unsigned int version; + unsigned int command; + unsigned int data_length; +} +feh_ipc_client_header; + +typedef struct +{ + unsigned int version; + unsigned int data_length; +} +feh_ipc_server_header; + +enum { + IPC_CMD_QUIT, + IPC_CMD_FILELIST_NEXT, + IPC_CMD_FILELIST_PREV, +}; + +#endif + + diff --git a/src/jpegint.h b/src/jpegint.h new file mode 100644 index 0000000..95b00d4 --- /dev/null +++ b/src/jpegint.h @@ -0,0 +1,392 @@ +/* + * jpegint.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + /* These routines are exported to allow insertion of extra markers */ + /* Probably only COM and APPn markers should be written this way */ + JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker, + unsigned int datalen)); + JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val)); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + + /* This is here to share code between baseline and progressive decoders; */ + /* other modules probably should not use it */ + boolean insufficient_data; /* set TRUE after emitting warning */ +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_phuff_encoder jIPHEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_phuff_decoder jIPHDecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Compression module initialization routines */ +EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN(long) jdiv_round_up JPP((long a, long b)); +EXTERN(long) jround_up JPP((long a, long b)); +EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +#if 0 /* This table is not actually needed in v6a */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +#endif +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ diff --git a/src/keyevents.c b/src/keyevents.c new file mode 100644 index 0000000..803b71c --- /dev/null +++ b/src/keyevents.c @@ -0,0 +1,445 @@ +/* keyevents.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "thumbnail.h" +#include "filelist.h" +#include "winwidget.h" +#include "options.h" + +void +feh_event_invoke_action(winwidget winwid, char *action) +{ + D_ENTER(4); + D(4, ("action is '%s'\n", action)); + D(4, ("winwid is '%p'\n", winwid)); + if (action) + { + if (opt.slideshow) + { + feh_action_run(FEH_FILE(winwid->file->data),action); + slideshow_change_image(winwid, SLIDE_NEXT); + } + else if ((winwid->type == WIN_TYPE_SINGLE) + || (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER)) + { + feh_action_run(FEH_FILE(winwid->file->data),action); + winwidget_destroy(winwid); + } + else if (winwid->type == WIN_TYPE_THUMBNAIL) + { + printf("actions from the main thumb window aren't currentl supported!\n"); + printf("For now, open the image to perform the action on it.\n"); + } + } + D_RETURN_(4); +} + +void +feh_event_handle_keypress(XEvent * ev) +{ + int len; + char kbuf[20]; + KeySym keysym; + XKeyEvent *kev; + winwidget winwid = NULL; + int curr_screen = 0; + feh_menu_item *selected_item; + feh_menu *selected_menu; + + D_ENTER(4); + + winwid = winwidget_get_from_window(ev->xkey.window); + + /* nuke dupe events, unless we're typing text */ + if (winwid && !winwid->caption_entry) { + while (XCheckTypedWindowEvent(disp, ev->xkey.window, KeyPress, ev)); + } + + kev = (XKeyEvent *) ev; + len = XLookupString(&ev->xkey, (char *) kbuf, sizeof(kbuf), &keysym, NULL); + + /* menus are showing, so this is a menu control keypress */ + if (ev->xbutton.window == menu_cover) { + selected_item = feh_menu_find_selected_r(menu_root, &selected_menu); + switch (keysym) { + case XK_Escape: + feh_menu_hide(menu_root, True); + break; + case XK_Left: + feh_menu_select_parent(selected_menu, selected_item); + break; + case XK_Down: + feh_menu_select_next(selected_menu, selected_item); + break; + case XK_Up: + feh_menu_select_prev(selected_menu, selected_item); + break; + case XK_Right: + feh_menu_select_submenu(selected_menu, selected_item); + break; + case XK_Return: + feh_menu_item_activate(selected_menu, selected_item); + break; + default: + break; + } + if (len <= 0 || len > (int) sizeof(kbuf)) + D_RETURN_(4); + kbuf[len] = '\0'; + + switch (*kbuf) + { + case 'h': + feh_menu_select_parent(selected_menu, selected_item); + break; + case 'j': + feh_menu_select_next(selected_menu, selected_item); + break; + case 'k': + feh_menu_select_prev(selected_menu, selected_item); + break; + case 'l': + feh_menu_select_submenu(selected_menu, selected_item); + break; + case ' ': + feh_menu_item_activate(selected_menu, selected_item); + break; + default: + break; + } + + D_RETURN_(4); + } + + if (winwid == NULL) + D_RETURN_(4); + + if (winwid->caption_entry) { + switch(keysym) { + case XK_Return: + if (kev->state & ControlMask) { + /* insert actual newline */ + ESTRAPPEND(FEH_FILE(winwid->file->data)->caption, "\n"); + winwidget_render_image_cached(winwid); + } else { + /* finish caption entry, write to captions file */ + FILE *fp; + char *caption_filename; + caption_filename = build_caption_filename(FEH_FILE(winwid->file->data)); + winwid->caption_entry = 0; + winwidget_render_image_cached(winwid); + XFreePixmap(disp, winwid->bg_pmap_cache); + winwid->bg_pmap_cache = 0; + fp = fopen(caption_filename, "w"); + if (!fp) { + weprintf("couldn't write to captions file %s:", caption_filename); + D_RETURN_(4); + } + fprintf(fp, "%s", FEH_FILE(winwid->file->data)->caption); + free(caption_filename); + fclose(fp); + } + break; + case XK_Escape: + /* cancel, revert caption */ + winwid->caption_entry = 0; + free(FEH_FILE(winwid->file->data)->caption); + FEH_FILE(winwid->file->data)->caption = NULL; + winwidget_render_image_cached(winwid); + XFreePixmap(disp, winwid->bg_pmap_cache); + winwid->bg_pmap_cache = 0; + break; + case XK_BackSpace: + /* backspace */ + ESTRTRUNC(FEH_FILE(winwid->file->data)->caption, 1); + winwidget_render_image_cached(winwid); + break; + default: + if(isascii(keysym)) { + /* append to caption */ + ESTRAPPEND_CHAR(FEH_FILE(winwid->file->data)->caption, keysym); + winwidget_render_image_cached(winwid); + } + break; + } + D_RETURN_(4); + } + + + switch (keysym) + { + case XK_Left: + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_PREV); + break; + case XK_Right: + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_NEXT); + break; + case XK_Page_Up: + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_JUMP_BACK); + break; + case XK_Escape: + winwidget_destroy_all(); + break; + case XK_Page_Down: + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_JUMP_FWD); + break; + case XK_Delete: + /* Holding ctrl gets you a filesystem deletion and removal from the * + filelist. Just DEL gets you filelist removal only. */ + if (kev->state & ControlMask) + { + if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) + feh_thumbnail_mark_removed(FEH_FILE(winwid->file->data), 1); + feh_filelist_image_remove(winwid, 1); + } + else + { + if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) + feh_thumbnail_mark_removed(FEH_FILE(winwid->file->data), 0); + feh_filelist_image_remove(winwid, 0); + } + break; + case XK_Home: + case XK_KP_Home: + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_FIRST); + break; + case XK_End: + case XK_KP_End: + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_LAST); + break; + case XK_Tab: + if (opt.draw_actions) + { + opt.draw_actions = 0; + winwidget_rerender_all(0, 1); + } + else + { + opt.draw_actions = 1; + winwidget_rerender_all(0, 1); + } + break; + case XK_Return: + feh_event_invoke_action(winwid,opt.actions[0]); + break; + case XK_0: + feh_event_invoke_action(winwid,opt.actions[0]); + break; + case XK_1: + feh_event_invoke_action(winwid,opt.actions[1]); + break; + case XK_2: + feh_event_invoke_action(winwid,opt.actions[2]); + break; + case XK_3: + feh_event_invoke_action(winwid,opt.actions[3]); + break; + case XK_4: + feh_event_invoke_action(winwid,opt.actions[4]); + break; + case XK_5: + feh_event_invoke_action(winwid,opt.actions[5]); + break; + case XK_6: + feh_event_invoke_action(winwid,opt.actions[6]); + break; + case XK_7: + feh_event_invoke_action(winwid,opt.actions[7]); + break; + case XK_8: + feh_event_invoke_action(winwid,opt.actions[8]); + break; + case XK_9: + feh_event_invoke_action(winwid,opt.actions[9]); + break; + case XK_KP_Left: + winwid->im_x = winwid->im_x - 10; + winwidget_render_image(winwid, 0, 0); + break; + case XK_KP_Right: + winwid->im_x = winwid->im_x + 10; + winwidget_render_image(winwid, 0, 0); + break; + case XK_KP_Up: + winwid->im_y = winwid->im_y - 10; + winwidget_render_image(winwid, 0, 0); + break; + case XK_KP_Down: + winwid->im_y = winwid->im_y + 10; + winwidget_render_image(winwid, 0, 0); + break; + case XK_KP_Add: + winwid->zoom = winwid->zoom * 1.25; + winwidget_render_image(winwid, 0, 0); + break; + case XK_KP_Subtract: + winwid->zoom = winwid->zoom * 0.75; + winwidget_render_image(winwid, 0, 0); + break; + case XK_KP_Multiply: + winwid->zoom = 1; + winwidget_render_image(winwid, 0, 0); + break; + case XK_KP_Divide: + feh_calc_needed_zoom(&winwid->zoom, winwid->im_w, winwid->im_h, winwid->w, winwid->h); + winwidget_render_image(winwid, 0, 0); + break; + default: + break; + } + + if (len <= 0 || len > (int) sizeof(kbuf)) + D_RETURN_(4); + kbuf[len] = '\0'; + + switch (*kbuf) + { + case 'n': + case 'N': + case ' ': + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_NEXT); + break; + case 'p': + case 'P': + case '\b': + if (opt.slideshow) + slideshow_change_image(winwid, SLIDE_PREV); + break; + case 'q': + case 'Q': + winwidget_destroy_all(); + break; + case 'c': + case 'C': + if (opt.caption_path) + winwid->caption_entry = 1; + winwidget_render_image(winwid, 0, 0); + break; + case 'r': + case 'R': + feh_reload_image(winwid, 0, 0); + break; + case 'h': + case 'H': + slideshow_pause_toggle(winwid); + break; + case 's': + case 'S': + slideshow_save_image(winwid); + break; + case 'f': + case 'F': + feh_save_filelist(); + break; + case 'w': + case 'W': + winwidget_size_to_image(winwid); + break; + case 'm': + case 'M': + winwidget_show_menu(winwid); + break; + case 'x': + case 'X': + winwidget_destroy(winwid); + break; + case '>': + feh_edit_inplace_orient(winwid, 1); + break; + case '<': + feh_edit_inplace_orient(winwid, 3); + break; + case 'v': + case 'V': +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + int i, rect[4]; + + /* FIXME: this doesn't do what it should; XGetGeometry always + * returns x,y == 0,0. I think that's due to the hints being passed + * (or more specifically, a missing hint) to X in winwidget_create + */ + winwidget_get_geometry(winwid, rect); + /* printf("window: (%d, %d)\n", rect[0], rect[1]); + printf("found %d screens.\n", num_xinerama_screens); */ + for (i = 0; i < num_xinerama_screens; i++) { + xinerama_screen = 0; + /* printf("%d: [%d, %d, %d, %d] (%d, %d)\n", + i, + xinerama_screens[i].x_org, xinerama_screens[i].y_org, + xinerama_screens[i].width, xinerama_screens[i].height, + rect[0], rect[1]);*/ + if (XY_IN_RECT(rect[0], rect[1], + xinerama_screens[i].x_org, xinerama_screens[i].y_org, + xinerama_screens[i].width, xinerama_screens[i].height)) { + curr_screen = xinerama_screen = i; + break; + } + } + } +#endif /* HAVE_LIBXINERAMA */ + winwid->full_screen = !winwid->full_screen; + winwidget_destroy_xwin(winwid); + winwidget_create_window(winwid, winwid->im_w, winwid->im_h); + winwidget_render_image(winwid, 1, 1); + winwidget_show(winwid); +#ifdef HAVE_LIBXINERAMA + /* if we have xinerama and we're using it, then full screen the window + * on the head that the window was active on */ + if (winwid->full_screen == TRUE && + opt.xinerama && xinerama_screens) { + xinerama_screen = curr_screen; + winwidget_move(winwid, + xinerama_screens[curr_screen].x_org, + xinerama_screens[curr_screen].y_org); + } +#endif /* HAVE_LIBXINERAMA */ + case '=': + case '+': + if (opt.reload < SLIDESHOW_RELOAD_MAX) + opt.reload++; + else if (opt.verbose) + weprintf("Cannot set RELOAD higher than %d seconds.", opt.reload); + break; + case '-': + case '_': + if (opt.reload > 1) + opt.reload--; + else if (opt.verbose) + weprintf("Cannot set RELOAD lower than 1 second."); + break; + default: + break; + } + D_RETURN_(4); +} diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..98f9fe7 --- /dev/null +++ b/src/list.c @@ -0,0 +1,116 @@ +/* list.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "options.h" + +void +init_list_mode(void) +{ + gib_list *l; + feh_file *file = NULL; + int j = 0; + + D_ENTER(4); + + mode = "list"; + + if (!opt.customlist) + printf + ("NUM\tFORMAT\tWIDTH\tHEIGHT\tPIXELS\tSIZE(bytes)\tALPHA\tFILENAME\n"); + + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + if (opt.customlist) + printf("%s\n", feh_printf(opt.customlist, file)); + else + printf("%d\t%s\t%d\t%d\t%d\t%d\t\t%c\t%s\n", ++j, file->info->format, + file->info->width, file->info->height, file->info->pixels, + file->info->size, file->info->has_alpha ? 'X' : '-', + file->filename); + + feh_action_run(file,opt.actions[0]); + } + exit(0); +} + +void +init_loadables_mode(void) +{ + D_ENTER(4); + mode = "loadables"; + real_loadables_mode(1); + D_RETURN_(4); +} + +void +init_unloadables_mode(void) +{ + D_ENTER(4); + mode = "unloadables"; + real_loadables_mode(0); + D_RETURN_(4); +} + + +void +real_loadables_mode(int loadable) +{ + feh_file *file; + gib_list *l; + + D_ENTER(4); + opt.quiet = 1; + + for (l = filelist; l; l = l->next) + { + Imlib_Image im = NULL; + + file = FEH_FILE(l->data); + + if (feh_load_image(&im, file)) + { + /* loaded ok */ + if (loadable) + { + fprintf(stdout, "%s\n", file->filename); + feh_action_run(file,opt.actions[0]); + } + gib_imlib_free_image_and_decache(im); + } + else + { + /* Oh dear. */ + if (!loadable) + { + fprintf(stdout, "%s\n", file->filename); + feh_action_run(file,opt.actions[0]); + } + } + } + exit(0); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..745fdf9 --- /dev/null +++ b/src/main.c @@ -0,0 +1,219 @@ +/* main.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "winwidget.h" +#include "timers.h" +#include "options.h" +#include "events.h" +#include "support.h" + +char **cmdargv = NULL; +int cmdargc = 0; +int call_level = 0; +char *mode = NULL; + +int +main(int argc, char **argv) +{ + D_ENTER(4); + atexit(feh_clean_exit); + + init_parse_options(argc, argv); + + init_x_and_imlib(); + + feh_event_init(); + + if (opt.index) + init_index_mode(); + else if (opt.collage) + init_collage_mode(); + else if (opt.multiwindow) + init_multiwindow_mode(); + else if (opt.list || opt.customlist) + init_list_mode(); + else if (opt.loadables) + init_loadables_mode(); + else if (opt.unloadables) + init_unloadables_mode(); + else if (opt.thumbs) + init_thumbnail_mode(); + else if(opt.bgmode) + { + feh_wm_set_bg_file(opt.output_file, opt.bgmode); + exit(0); + } +/* else if (opt.fmmode) + { + fmmode(); + opt.slideshow = 1; + init_slideshow_mode(); + } + */ + else + { + /* Slideshow mode is the default. Because it's spiffy */ + opt.slideshow = 1; + init_slideshow_mode(); + } + + /* main event loop */ + while (feh_main_iteration(1)); + + D_RETURN(4,0); +} + + +/* Return 0 to stop iterating, 1 if ok to continue. */ +int +feh_main_iteration(int block) +{ + static int first = 1; + static int xfd = 0; + static int fdsize = 0; + static double pt = 0.0; + XEvent ev; + struct timeval tval; + fd_set fdset; + int count = 0; + double t1 = 0.0, t2 = 0.0; + fehtimer ft; + + D_ENTER(5); + + if (window_num == 0) + D_RETURN(5,0); + + if (first) + { + /* Only need to set these up the first time */ + xfd = ConnectionNumber(disp); + fdsize = xfd + 1; + pt = feh_get_time(); + first = 0; + } + + /* Timers */ + t1 = feh_get_time(); + t2 = t1 - pt; + pt = t1; + while (XPending(disp)) + { + XNextEvent(disp, &ev); + if (ev_handler[ev.type]) + (*(ev_handler[ev.type])) (&ev); + + if (window_num == 0) + D_RETURN(5,0); + } + XFlush(disp); + + feh_redraw_menus(); + + FD_ZERO(&fdset); + FD_SET(xfd, &fdset); + + /* Timers */ + ft = first_timer; + /* Don't do timers if we're zooming/panning/etc or if we are paused*/ + if (ft && (opt.mode == MODE_NORMAL) && !opt.paused) + { + D(5,("There are timers in the queue\n")); + if (ft->just_added) + { + D(5,("The first timer has just been added\n")); + D(5,("ft->in = %f\n", ft->in)); + ft->just_added = 0; + t1 = ft->in; + } + else + { + D(5,("The first timer was not just added\n")); + t1 = ft->in - t2; + if (t1 < 0.0) + t1 = 0.0; + ft->in = t1; + } + + XSync(disp, False); + D(5,("I next need to action a timer in %f seconds\n", t1)); + /* Only do a blocking select if there's a timer due, or no events + waiting */ + if (t1 == 0.0 || (block && !XPending(disp))) + { + tval.tv_sec = (long) t1; + tval.tv_usec = (long) ((t1 - ((double) tval.tv_sec)) * 1000000); + if (tval.tv_sec < 0) + tval.tv_sec = 0; + if (tval.tv_usec <= 1000) + tval.tv_usec = 1000; + errno = 0; + D(5,("Performing blocking select - waiting for timer or event\n")); + count = select(fdsize, &fdset, NULL, NULL, &tval); + if ((count < 0) + && ((errno == ENOMEM) || (errno == EINVAL) || (errno == EBADF))) + eprintf("Connection to X display lost"); + if ((ft) && (count == 0)) + { + /* This means the timer is due to be executed. If count was > 0, + that would mean an X event had woken us, we're not interested + in that */ + feh_handle_timer(); + } + } + } + else + { + /* Don't block if there are events in the queue. That's a bit rude ;-) */ + if (block && !XPending(disp)) + { + errno = 0; + D(5,("Performing blocking select - no timers, or zooming\n")); + count = select(fdsize, &fdset, NULL, NULL, NULL); + if ((count < 0) + && ((errno == ENOMEM) || (errno == EINVAL) || (errno == EBADF))) + eprintf("Connection to X display lost"); + } + } + if (window_num == 0) + D_RETURN(5,0); + D_RETURN(5,1); +} + + +void +feh_clean_exit(void) +{ + D_ENTER(4); + + delete_rm_files(); + + if (opt.filelistfile) + feh_write_filelist(filelist, opt.filelistfile); + + D_RETURN_(4); +} diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..d6f78b8 --- /dev/null +++ b/src/md5.c @@ -0,0 +1,392 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/*$Id: md5.c $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" + +#ifdef TEST +/* + * Compile with -DTEST to create a self-contained executable test program. + * The test program should print out the same values as given in section + * A.5 of RFC 1321, reproduced below. + */ +#include +main() +{ + static const char *const test[7] = { + "", /*d41d8cd98f00b204e9800998ecf8427e*/ + "a", /*0cc175b9c0f1b6a831c399e269772661*/ + "abc", /*900150983cd24fb0d6963f7d28e17f72*/ + "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/ + "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + /*d174ab98d277d9f5a5611c2c9f419d9f*/ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/ + }; + int i; + + for (i = 0; i < 7; ++i) { + md5_state_t state; + md5_byte_t digest[16]; + int di; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i])); + md5_finish(&state, digest); + printf("MD5 (\"%s\") = ", test[i]); + for (di = 0; di < 16; ++di) + printf("%02x", digest[di]); + printf("\n"); + } + return 0; +} +#endif /* TEST */ + + +/* + * For reference, here is the program that computed the T values. + */ +#if 0 +#include +main() +{ + int i; + for (i = 1; i <= 64; ++i) { + unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i))); + printf("#define T%d 0x%08lx\n", i, v); + } + return 0; +} +#endif +/* + * End of T computation program. + */ +#define T1 0xd76aa478 +#define T2 0xe8c7b756 +#define T3 0x242070db +#define T4 0xc1bdceee +#define T5 0xf57c0faf +#define T6 0x4787c62a +#define T7 0xa8304613 +#define T8 0xfd469501 +#define T9 0x698098d8 +#define T10 0x8b44f7af +#define T11 0xffff5bb1 +#define T12 0x895cd7be +#define T13 0x6b901122 +#define T14 0xfd987193 +#define T15 0xa679438e +#define T16 0x49b40821 +#define T17 0xf61e2562 +#define T18 0xc040b340 +#define T19 0x265e5a51 +#define T20 0xe9b6c7aa +#define T21 0xd62f105d +#define T22 0x02441453 +#define T23 0xd8a1e681 +#define T24 0xe7d3fbc8 +#define T25 0x21e1cde6 +#define T26 0xc33707d6 +#define T27 0xf4d50d87 +#define T28 0x455a14ed +#define T29 0xa9e3e905 +#define T30 0xfcefa3f8 +#define T31 0x676f02d9 +#define T32 0x8d2a4c8a +#define T33 0xfffa3942 +#define T34 0x8771f681 +#define T35 0x6d9d6122 +#define T36 0xfde5380c +#define T37 0xa4beea44 +#define T38 0x4bdecfa9 +#define T39 0xf6bb4b60 +#define T40 0xbebfbc70 +#define T41 0x289b7ec6 +#define T42 0xeaa127fa +#define T43 0xd4ef3085 +#define T44 0x04881d05 +#define T45 0xd9d4d039 +#define T46 0xe6db99e5 +#define T47 0x1fa27cf8 +#define T48 0xc4ac5665 +#define T49 0xf4292244 +#define T50 0x432aff97 +#define T51 0xab9423a7 +#define T52 0xfc93a039 +#define T53 0x655b59c3 +#define T54 0x8f0ccc92 +#define T55 0xffeff47d +#define T56 0x85845dd1 +#define T57 0x6fa87e4f +#define T58 0xfe2ce6e0 +#define T59 0xa3014314 +#define T60 0x4e0811a1 +#define T61 0xf7537e82 +#define T62 0xbd3af235 +#define T63 0x2ad7d2bb +#define T64 0xeb86d391 + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; + +#ifndef ARCH_IS_BIG_ENDIAN +# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */ +#endif +#if ARCH_IS_BIG_ENDIAN + + /* + * On big-endian machines, we must arrange the bytes in the right + * order. (This also works on machines of unknown byte order.) + */ + md5_word_t X[16]; + const md5_byte_t *xp = data; + int i; + + for (i = 0; i < 16; ++i, xp += 4) + X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + +#else /* !ARCH_IS_BIG_ENDIAN */ + + /* + * On little-endian machines, we can process properly aligned data + * without copying it. + */ + md5_word_t xbuf[16]; + const md5_word_t *X; + + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } +#endif + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = 0xefcdab89; + pms->abcd[2] = 0x98badcfe; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..0193f5d --- /dev/null +++ b/src/md5.h @@ -0,0 +1,94 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/*$Id: md5.h $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This code has some adaptations for the Ghostscript environment, but it + * will compile and run correctly in any environment with 8-bit chars and + * 32-bit ints. Specifically, it assumes that if the following are + * defined, they have the same meaning as in Ghostscript: P1, P2, P3, + * ARCH_IS_BIG_ENDIAN. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +#ifdef P1 +void md5_init(P1(md5_state_t *pms)); +#else +void md5_init(md5_state_t *pms); +#endif + +/* Append a string to the message. */ +#ifdef P3 +void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes)); +#else +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); +#endif + +/* Finish the message and return the digest. */ +#ifdef P2 +void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16])); +#else +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); +#endif + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/src/menu.c b/src/menu.c new file mode 100644 index 0000000..34a236a --- /dev/null +++ b/src/menu.c @@ -0,0 +1,2144 @@ +/* menu.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "support.h" +#include "thumbnail.h" +#include "winwidget.h" +#include "filelist.h" +#include "options.h" + +Window menu_cover = 0; +feh_menu *menu_root = NULL; +feh_menu *menu_main = NULL; +feh_menu *menu_single_win = NULL; +feh_menu *menu_about_win = NULL; +feh_menu *menu_thumbnail_viewer = NULL; +feh_menu *menu_thumbnail_win = NULL; +feh_menu *menu_bg = NULL; +static feh_menu_list *menus = NULL; +static int common_menus = 0; + +static void feh_menu_cb_about(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_close(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_exit(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_reload(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_remove(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_delete(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_reset(feh_menu * m, + feh_menu_item * i, + void *data); + +static void feh_menu_cb_remove_thumb(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_delete_thumb(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_background_set_tiled(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_background_set_scaled(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_background_set_seamless(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_background_set_centered(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_background_set_tiled_no_file(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_background_set_scaled_no_file(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_background_set_centered_no_file(feh_menu * m, + feh_menu_item * i, + void *data); + +static void feh_menu_cb_sort_filename(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_sort_imagename(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_sort_filesize(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_sort_randomize(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_jump_to(feh_menu * m, + feh_menu_item * i, + void *data); +static feh_menu *feh_menu_func_gen_jump(feh_menu * m, + feh_menu_item * i, + void *data); +static feh_menu *feh_menu_func_gen_info(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_func_free_info(feh_menu * m, + void *data); +static void feh_menu_cb_save_image(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_save_filelist(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_fit(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_opt_draw_filename(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_opt_keep_http(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_opt_freeze_window(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_opt_fullscreen(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_func_free_options(feh_menu * m, + void *data); +static feh_menu *feh_menu_func_gen_options(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_edit_rotate(feh_menu * m, + feh_menu_item * i, + void *data); +static void feh_menu_cb_opt_auto_zoom(feh_menu * m, + feh_menu_item * i, + void *data); +#ifdef HAVE_LIBXINERAMA +static void feh_menu_cb_opt_xinerama(feh_menu * m, + feh_menu_item * i, + void *data); +#endif /* HAVE_LIBXINERAMA */ + + +feh_menu * +feh_menu_new(void) +{ + feh_menu *m; + XSetWindowAttributes attr; + feh_menu_list *l; + static Imlib_Image bg = NULL; + static Imlib_Border border; + + D_ENTER(4); + + m = (feh_menu *) emalloc(sizeof(feh_menu)); + + attr.backing_store = NotUseful; + attr.override_redirect = True; + attr.colormap = cm; + attr.border_pixel = 0; + attr.background_pixmap = None; + attr.save_under = False; + attr.do_not_propagate_mask = True; + + m->win = + XCreateWindow(disp, root, 1, 1, 1, 1, 0, depth, InputOutput, vis, + CWOverrideRedirect | CWSaveUnder | CWBackingStore | + CWColormap | CWBackPixmap | CWBorderPixel | CWDontPropagate, + &attr); + XSelectInput(disp, m->win, + ButtonPressMask | ButtonReleaseMask | EnterWindowMask | + LeaveWindowMask | PointerMotionMask | ButtonMotionMask); + + m->name = NULL; + m->fehwin = NULL; + m->pmap = 0; + m->x = 0; + m->y = 0; + m->w = 0; + m->h = 0; + m->visible = 0; + m->items = NULL; + m->next = NULL; + m->prev = NULL; + m->updates = NULL; + m->needs_redraw = 1; + m->func_free = NULL; + m->data = NULL; + m->calc = 0; + m->bg = NULL; + + l = emalloc(sizeof(feh_menu_list)); + l->menu = m; + l->next = menus; + menus = l; + + if (!bg) { + feh_load_image_char(&bg, opt.menu_bg); + if (bg) { + border.left = opt.menu_border; + border.right = opt.menu_border; + border.top = opt.menu_border; + border.bottom = opt.menu_border; + imlib_context_set_image(bg); + imlib_image_set_border(&border); + } + } + + if (bg) + m->bg = gib_imlib_clone_image(bg); + + D_RETURN(4, m); +} + +void +feh_menu_free(feh_menu * m) +{ + feh_menu_item *i; + feh_menu_list *l, *pl = NULL; + + D_ENTER(4); + + if (m->name) + free(m->name); + XDestroyWindow(disp, m->win); + if (m->pmap) + XFreePixmap(disp, m->pmap); + if (m->updates) + imlib_updates_free(m->updates); + for (i = m->items; i;) { + feh_menu_item *ii; + + ii = i; + i = i->next; + if (ii->func_free) + (ii->func_free) (ii->data); + if (ii->text) + free(ii->text); + if (ii->submenu) + free(ii->submenu); + free(ii); + } + + for (l = menus; l; l = l->next) { + if (l->menu == m) { + if (pl) + pl->next = l->next; + else + menus = l->next; + free(l); + break; + } + pl = l; + } + free(m); + + D_RETURN_(4); +} + +feh_menu_item * +feh_menu_find_selected(feh_menu * m) +{ + feh_menu_item *i; + + D_ENTER(4); + + D(5, ("menu %p\n", m)); + + for (i = m->items; i; i = i->next) { + if (MENU_ITEM_IS_SELECTED(i)) + D_RETURN(4, i); + } + D_RETURN(4, NULL); +} + +feh_menu_item * +feh_menu_find_selected_r(feh_menu * m, feh_menu **parent) +{ + feh_menu_item *i, *ii; + feh_menu *mm; + + D_ENTER(4); + + D(5, ("menu %p\n", m)); + + for (i = m->items; i; i = i->next) { + if (MENU_ITEM_IS_SELECTED(i)) { + if (parent) + *parent = m; + D_RETURN(4, i); + } else if (i->submenu) { + mm = feh_menu_find(i->submenu); + if (mm) { + ii = feh_menu_find_selected_r(mm, parent); + if (ii) + D_RETURN(4, ii); + } + } + } + if (parent) + *parent = m; + D_RETURN(4, NULL); +} + +void +feh_menu_select_next(feh_menu *selected_menu, feh_menu_item *selected_item) { + feh_menu_item *i; + if (!selected_item) { + /* jump to first item, select it */ + feh_menu_select(selected_menu, selected_menu->items); + } else { + i = selected_item; + while (1) { + i = i->next; + if (!i) + i = selected_menu->items; + if (i->func || i->submenu || i->func_gen_sub || i->text) { + break; + } + } + feh_menu_deselect_selected(selected_menu); + feh_menu_select(selected_menu, i); + } +} + +void +feh_menu_select_prev(feh_menu *selected_menu, feh_menu_item *selected_item) { + feh_menu_item *i, *ii; + if (!selected_item) { + /* jump to last item, select it */ + for (i = selected_menu->items; i->next; i = i->next); + feh_menu_select(selected_menu, i); + } else { + i = selected_item; + while (1) { + i = i->prev; + if (!i) { + i = selected_menu->items; + for (ii = selected_menu->items; ii->next; ii = ii->next); + i = ii; + } + if (i->func || i->submenu || i->func_gen_sub || i->text) { + break; + } + } + feh_menu_deselect_selected(selected_menu); + feh_menu_select(selected_menu, i); + } +} + +void +feh_menu_select_parent(feh_menu *selected_menu, feh_menu_item *selected_item) { + feh_menu *m; + feh_menu_item *i; + /* find the parent menu's item which refers to this menu's name */ + if (selected_menu->prev) { + m = selected_menu->prev; + for (i = m->items; i; i = i->next) { + if(i->submenu && !strcmp(i->submenu, selected_menu->name)) + break; + } + /* shouldn't ever happen */ + if (i == NULL) + i = m->items; + feh_menu_deselect_selected(selected_menu); + feh_menu_select(m, i); + } +} + +void +feh_menu_select_submenu(feh_menu *selected_menu, feh_menu_item *selected_item) { + if (selected_menu->next) { + feh_menu_deselect_selected(selected_menu); + feh_menu_select(selected_menu->next, selected_menu->next->items); + } +} + +void feh_menu_item_activate(feh_menu *m, + feh_menu_item *i) { + /* watch out for this. I put it this way around so the menu + goes away *before* we perform the action, if we start + freeing menus on hiding, it will break ;-) */ + if ((i) && (i->func)) { + feh_menu_hide(menu_root, False); + feh_main_iteration(0); + (i->func) (m, i, i->data); + if(m->func_free) + m->func_free(m, m->data); + } +} + +feh_menu_item * +feh_menu_find_at_xy(feh_menu * m, + int x, + int y) +{ + feh_menu_item *i; + + D_ENTER(4); + D(4, ("looking for menu item at %d,%d\n", x, y)); + for (i = m->items; i; i = i->next) { + if (XY_IN_RECT(x, y, i->x, i->y, i->w, i->h)) { + D(4, ("Found an item\n")); + D_RETURN(4, i); + } + } + D(4, ("didn't find an item\n")); + D_RETURN(4, NULL); +} + +void +feh_menu_deselect_selected(feh_menu * m) +{ + feh_menu_item *i; + + D_ENTER(4); + + if (!m) + D_RETURN_(4); + + i = feh_menu_find_selected(m); + if (i) { + D(4, ("found a selected menu, deselecting it\n")); + MENU_ITEM_SET_NORMAL(i); + m->updates = imlib_update_append_rect(m->updates, i->x, i->y, i->w, i->h); + m->needs_redraw = 1; + } + D_RETURN_(4); +} + +void +feh_menu_select(feh_menu * m, + feh_menu_item * i) +{ + D_ENTER(4); + MENU_ITEM_SET_SELECTED(i); + m->updates = imlib_update_append_rect(m->updates, i->x, i->y, i->w, i->h); + m->needs_redraw = 1; + if (m->next) { + m->next->prev = NULL; + feh_menu_hide(m->next, TRUE); + m->next = NULL; + } + if (i->submenu) { + feh_menu *mm; + + mm = feh_menu_find(i->submenu); + if (mm) + feh_menu_show_at_submenu(mm, m, i); + else if (i->func_gen_sub) + feh_menu_show_at_submenu(i->func_gen_sub(m, i, i->data), m, i); + } + D_RETURN_(4); +} + +void +feh_menu_show_at(feh_menu * m, int x, int y) +{ + D_ENTER(4); + + if (m->calc) + feh_menu_calc_size(m); + if (!menu_cover) { + XSetWindowAttributes attr; + + D(4, ("creating menu cover window\n")); + attr.override_redirect = True; + attr.do_not_propagate_mask = True; + menu_cover = + XCreateWindow(disp, root, 0, 0, scr->width, scr->height, 0, 0, + InputOnly, vis, CWOverrideRedirect | CWDontPropagate, + &attr); + XSelectInput(disp, menu_cover, + KeyPressMask | ButtonPressMask | ButtonReleaseMask | + EnterWindowMask | LeaveWindowMask | PointerMotionMask | + ButtonMotionMask); + + XRaiseWindow(disp, menu_cover); + XMapWindow(disp, menu_cover); + menu_root = m; + XUngrabPointer(disp, CurrentTime); + XSetInputFocus(disp, menu_cover, RevertToPointerRoot, CurrentTime); + } + m->visible = 1; + XMoveWindow(disp, m->win, x, y); + m->x = x; + m->y = y; + XRaiseWindow(disp, m->win); + feh_menu_redraw(m); + XMapWindow(disp, m->win); + D_RETURN_(4); +} + +void +feh_menu_show_at_xy(feh_menu * m, + winwidget winwid, + int x, + int y) +{ + D_ENTER(4); + + if (!m) + D_RETURN_(4); + + if (m->calc) + feh_menu_calc_size(m); + m->fehwin = winwid; + if ((x + m->w) > scr->width) + x = scr->width - m->w; + if ((y + m->h) > scr->height) + y = scr->height - m->h; + +#if 0 +/* #ifdef HAVE_LIBXINERAMA */ +/* this doesn't work correctly :( -- pabs */ + if (opt.xinerama && xinerama_screens) { + if ((x + m->w) > xinerama_screens[xinerama_screen].width) + x = xinerama_screens[xinerama_screen].width - m->w; + if ((y + m->h) > xinerama_screens[xinerama_screen].height) + y = xinerama_screens[xinerama_screen].height - m->h; + + } +#endif /* HAVE_LIBXINERAMA */ + + if (x < 0) + x = 0; + if (y < 0) + y = 0; + feh_menu_move(m, x, y); + feh_menu_show(m); + D_RETURN_(4); +} + +void +feh_menu_show_at_submenu(feh_menu * m, + feh_menu * parent_m, + feh_menu_item * i) +{ + int mx, my; + + D_ENTER(4); + + if (!m) + D_RETURN_(4); + + if (m->calc) + feh_menu_calc_size(m); + mx = parent_m->x + parent_m->w; + my = parent_m->y + i->y - FEH_MENU_PAD_TOP; + m->fehwin = parent_m->fehwin; + parent_m->next = m; + m->prev = parent_m; + feh_menu_move(m, mx, my); + feh_menu_show(m); + D_RETURN_(4); +} + +void +feh_menu_move(feh_menu * m, + int x, + int y) +{ + int dx, dy; + + D_ENTER(4); + + if (!m) + D_RETURN_(4); + dx = x - m->x; + dy = y - m->y; + if (m->visible) + XMoveWindow(disp, m->win, x, y); + m->x = x; + m->y = y; + D_RETURN_(4); +} + +void +feh_menu_slide_all_menus_relative(int dx, + int dy) +{ + int i; + feh_menu_list *m; + double vector_len = 0; + int stepx = 0; + int stepy = 0; + + D_ENTER(4); + vector_len = sqrt(dx * dx + dy * dy); + if (vector_len) { + if (dx) + stepx = rint(dx / vector_len); + + if (dy) + stepy = rint(dy / vector_len); + + } + for (i = 0; i < vector_len; i++) { + for (m = menus; m; m = m->next) { + if (m->menu->visible) + feh_menu_move(m->menu, m->menu->x + stepx, m->menu->y + stepy); + + } + XWarpPointer(disp, None, None, 0, 0, 0, 0, stepx, stepy); + } + D_RETURN_(4); +} + +void +feh_menu_hide(feh_menu * m, + int func_free) +{ + D_ENTER(4); + + if (!m->visible) + D_RETURN_(4); + if (m->next) { + m->next->prev = NULL; + feh_menu_hide(m->next, func_free); + m->next = NULL; + } + if (m == menu_root) { + if (menu_cover) { + D(4, ("DESTROYING menu cover\n")); + XDestroyWindow(disp, menu_cover); + menu_cover = 0; + } + menu_root = NULL; + } + m->visible = 0; + XUnmapWindow(disp, m->win); + if (func_free && m->func_free) + m->func_free(m, m->data); + else + feh_menu_deselect_selected(m); + D_RETURN_(4); +} + +void +feh_menu_show(feh_menu * m) +{ + D_ENTER(4); + if (!m) + D_RETURN_(4); + feh_menu_show_at(m, m->x, m->y); + D_RETURN_(4); +} + +feh_menu_item * +feh_menu_add_toggle_entry(feh_menu * m, + char *text, + Imlib_Image icon, + char *submenu, + menu_func func, + void *data, + void (*func_free) (void *data), + int setting) +{ + feh_menu_item *mi; + + D_ENTER(4); + mi = feh_menu_add_entry(m, text, icon, submenu, func, data, func_free); + mi->is_toggle = TRUE; + MENU_ITEM_TOGGLE_SET(mi, setting); + D_RETURN(4, mi); +} + +feh_menu_item * +feh_menu_add_entry(feh_menu * m, + char *text, + Imlib_Image icon, + char *submenu, + menu_func func, + void *data, + void (*func_free) (void *data)) +{ + feh_menu_item *mi, *ptr; + + D_ENTER(4); + + mi = (feh_menu_item *) emalloc(sizeof(feh_menu_item)); + mi->state = MENU_ITEM_STATE_NORMAL; + mi->icon = icon; + mi->is_toggle = FALSE; + if (text) + mi->text = estrdup(text); + else + mi->text = NULL; + if (submenu) + mi->submenu = estrdup(submenu); + else + mi->submenu = NULL; + mi->func = func; + mi->func_free = func_free; + mi->data = data; + mi->func_gen_sub = NULL; + mi->next = NULL; + mi->prev = NULL; + + if (!m->items) + m->items = mi; + else { + for (ptr = m->items; ptr; ptr = ptr->next) { + if (!ptr->next) { + ptr->next = mi; + mi->prev = ptr; + break; + } + } + } + m->calc = 1; + D_RETURN(4, mi); +} + + +void +feh_menu_entry_get_size(feh_menu * m, + feh_menu_item * i, + int *w, + int *h) +{ + int tw, th; + + D_ENTER(4); + + if (i->text) { + gib_imlib_get_text_size(opt.menu_fn, i->text, opt.menu_style_l, &tw, &th, + IMLIB_TEXT_TO_RIGHT); + *w = + tw + FEH_MENUITEM_PAD_LEFT + FEH_MENUITEM_PAD_RIGHT; + *h = + th + FEH_MENUITEM_PAD_TOP + FEH_MENUITEM_PAD_BOTTOM; + } else { + *w = FEH_MENUITEM_PAD_LEFT + FEH_MENUITEM_PAD_RIGHT; + *h = FEH_MENUITEM_PAD_TOP + FEH_MENUITEM_PAD_BOTTOM; + } + + D_RETURN_(4); + m = NULL; +} + +void +feh_menu_calc_size(feh_menu * m) +{ + int prev_w, prev_h; + feh_menu_item *i; + int j = 0, count = 0, max_w = 0, max_h = 0, icon_w = 0, next_w = 0; + int toggle_w = 0; + + D_ENTER(4); + + prev_w = m->w; + prev_h = m->h; + m->calc = 0; + + for (i = m->items; i; i = i->next) { + int w, h; + + feh_menu_entry_get_size(m, i, &w, &h); + if (w > max_w) + max_w = w; + if (h > max_h) + max_h = h; + if (i->submenu) { + next_w = FEH_MENU_SUBMENU_W; + if (FEH_MENU_SUBMENU_H > max_h) + max_h = FEH_MENU_SUBMENU_H; + } + if (i->is_toggle) { + toggle_w = FEH_MENU_TOGGLE_W + FEH_MENU_TOGGLE_PAD; + if (FEH_MENU_TOGGLE_H > max_h) + max_h = FEH_MENU_TOGGLE_H; + } + count++; + } + + for (i = m->items; i; i = i->next) { + if (i->icon) { + Imlib_Image im; + + im = i->icon; + if (im) { + int iw, ih, ow, oh; + + iw = gib_imlib_image_get_width(im); + ih = gib_imlib_image_get_height(im); + if (ih <= max_h) { + ow = iw; + oh = ih; + } else { + ow = (iw * max_h) / ih; + oh = max_h; + } + if (ow > icon_w) + icon_w = ow; + } + } + } + m->h = FEH_MENU_PAD_TOP; + for (i = m->items; i; i = i->next) { + i->x = FEH_MENU_PAD_LEFT; + i->y = m->h; + i->w = max_w + icon_w + toggle_w + next_w; + i->icon_x = FEH_MENUITEM_PAD_LEFT; + i->toggle_x = i->icon_x + icon_w; + i->text_x = i->toggle_x + toggle_w; + i->sub_x = i->text_x + max_w; + if (i->text) + i->h = max_h; + else + i->h = FEH_MENU_SEP_MAX_H; + m->h += i->h; + j++; + } + m->h += FEH_MENU_PAD_BOTTOM; + m->w = + next_w + toggle_w + icon_w + max_w + FEH_MENU_PAD_LEFT + + FEH_MENU_PAD_RIGHT; + + if ((prev_w != m->w) || (prev_h != m->h)) { + if (m->pmap) + XFreePixmap(disp, m->pmap); + m->pmap = 0; + m->needs_redraw = 1; + XResizeWindow(disp, m->win, m->w, m->h); + m->updates = imlib_update_append_rect(m->updates, 0, 0, m->w, m->h); + } + D(4, ("menu size calculated. w=%d h=%d\n", m->w, m->h)); + + /* Make sure bg is same size */ + if (m->bg) { + int bg_w, bg_h; + + bg_w = gib_imlib_image_get_width(m->bg); + bg_h = gib_imlib_image_get_height(m->bg); + + if (m->w != bg_w || m->h != bg_h) { + Imlib_Image newim = imlib_create_image(m->w, m->h); + + D(3, ("resizing bg to %dx%d\n", m->w, m->h)); + + gib_imlib_blend_image_onto_image(newim, m->bg, 0, 0, 0, bg_w, bg_h, 0, + 0, m->w, m->h, 0, 0, 1); + gib_imlib_free_image_and_decache(m->bg); + m->bg = newim; + } + } + + D_RETURN_(4); +} + +void +feh_menu_draw_item(feh_menu * m, + feh_menu_item * i, + Imlib_Image im, + int ox, + int oy) +{ + D_ENTER(5); + + D(5, + ("drawing item %p (text %s) on menu %p (name %s)\n", i, i->text, m, + m->name)); + + if (i->text) { + D(5, ("text item\n")); + if (MENU_ITEM_IS_SELECTED(i)) { + D(5, ("selected item\n")); + /* draw selected image */ + feh_menu_item_draw_at(i->x, i->y, i->w, i->h, im, ox, oy, 1); + } else { + D(5, ("unselected item\n")); + /* draw unselected image */ + feh_menu_item_draw_at(i->x, i->y, i->w, i->h, im, ox, oy, 0); + } + + /* draw text */ + gib_imlib_text_draw(im, opt.menu_fn, opt.menu_style_l, + i->x - ox + i->text_x, + i->y - oy + FEH_MENUITEM_PAD_TOP, i->text, + IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); + if (i->icon) { + Imlib_Image im2; + + D(5, ("icon item\n")); + + im2 = i->icon; + if (im2) { + int iw, ih, ow, oh; + + iw = gib_imlib_image_get_width(im2); + ih = gib_imlib_image_get_height(im2); + if (ih <= (i->h - FEH_MENUITEM_PAD_TOP - FEH_MENUITEM_PAD_BOTTOM)) { + ow = iw; + oh = ih; + } else { + ow = + (iw * (i->h - FEH_MENUITEM_PAD_TOP - FEH_MENUITEM_PAD_BOTTOM)) / + ih; + oh = i->h - FEH_MENUITEM_PAD_TOP - FEH_MENUITEM_PAD_BOTTOM; + } + gib_imlib_blend_image_onto_image(im, im2, 0, 0, 0, iw, ih, + i->x + i->icon_x - ox, + i->y + FEH_MENUITEM_PAD_TOP + + (((i->h - FEH_MENUITEM_PAD_TOP - + FEH_MENUITEM_PAD_BOTTOM) - + oh) / 2) - oy, ow, oh, 1, 1, 1); + gib_imlib_free_image(im2); + } + } + if (i->submenu) { + D(5, ("submenu item\n")); + feh_menu_draw_submenu_at(i->x + i->sub_x, + i->y + FEH_MENUITEM_PAD_TOP + + ((i->h - FEH_MENUITEM_PAD_TOP - + FEH_MENUITEM_PAD_BOTTOM - + FEH_MENU_SUBMENU_H) / 2), FEH_MENU_SUBMENU_W, + FEH_MENU_SUBMENU_H, im, ox, oy, + MENU_ITEM_IS_SELECTED(i)); + } + if (i->is_toggle) { + D(5, ("toggleable item\n")); + feh_menu_draw_toggle_at(i->x + i->toggle_x, + i->y + FEH_MENUITEM_PAD_TOP + + ((i->h - FEH_MENUITEM_PAD_TOP - + FEH_MENUITEM_PAD_BOTTOM - + FEH_MENU_TOGGLE_H) / 2), FEH_MENU_TOGGLE_W, + FEH_MENU_TOGGLE_H, im, ox, oy, + MENU_ITEM_IS_ON(i)); + } + } else { + D(5, ("separator item\n")); + feh_menu_draw_separator_at(i->x, i->y, i->w, i->h, im, ox, oy); + } + D_RETURN_(5); + m = NULL; +} + +void +feh_menu_redraw(feh_menu * m) +{ + Imlib_Updates u, uu; + + D_ENTER(5); + + if ((!m->needs_redraw) || (!m->visible) || (!m->updates)) + D_RETURN_(5); + m->needs_redraw = 0; + if (!m->pmap) + m->pmap = XCreatePixmap(disp, m->win, m->w, m->h, depth); + XSetWindowBackgroundPixmap(disp, m->win, m->pmap); + + u = imlib_updates_merge_for_rendering(m->updates, m->w, m->h); + m->updates = NULL; + if (u) { + D(5, ("I have updates to render\n")); + for (uu = u; u; u = imlib_updates_get_next(u)) { + int x, y, w, h; + Imlib_Image im; + + imlib_updates_get_coordinates(u, &x, &y, &w, &h); + D(5, ("update coords %d,%d %d*%d\n", x, y, w, h)); + im = imlib_create_image(w, h); + gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 0, 0, 0, 0); + if (im) { + feh_menu_draw_to_buf(m, im, x, y); + gib_imlib_render_image_on_drawable(m->pmap, im, x, y, 1, 0, 0); + gib_imlib_free_image(im); + XClearArea(disp, m->win, x, y, w, h, False); + } + } + imlib_updates_free(uu); + } + D_RETURN_(5); +} + +feh_menu * +feh_menu_find(char *name) +{ + feh_menu_list *l; + + D_ENTER(4); + for (l = menus; l; l = l->next) { + if ((l->menu->name) && (!strcmp(l->menu->name, name))) + D_RETURN(4, l->menu); + } + D_RETURN(4, NULL); +} + +void +feh_menu_draw_to_buf(feh_menu * m, + Imlib_Image im, + int ox, + int oy) +{ + feh_menu_item *i; + int w, h; + + D_ENTER(5); + w = gib_imlib_image_get_width(im); + h = gib_imlib_image_get_height(im); + + feh_menu_draw_menu_bg(m, im, ox, oy); + + for (i = m->items; i; i = i->next) { + if (RECTS_INTERSECT(i->x, i->y, i->w, i->h, ox, oy, w, h)) + feh_menu_draw_item(m, i, im, ox, oy); + } + D_RETURN_(5); +} + +void +feh_menu_draw_menu_bg(feh_menu * m, + Imlib_Image im, + int ox, + int oy) +{ + int w, h; + + D_ENTER(5); + + w = gib_imlib_image_get_width(im); + h = gib_imlib_image_get_height(im); + + if (m->bg) + gib_imlib_blend_image_onto_image(im, m->bg, 0, ox, oy, w, h, 0, 0, w, h, + 0, 0, 0); + else + gib_imlib_image_fill_rectangle(im, 0, 0, w, h, 205, 203, 176, 255); + + D_RETURN_(5); +} + +void +feh_menu_draw_toggle_at(int x, + int y, + int w, + int h, + Imlib_Image dst, + int ox, + int oy, + int on) +{ + D_ENTER(5); + x -= ox; + y -= oy; + if (on) + gib_imlib_image_fill_rectangle(dst, x, y, w, h, 0, 0, 0, 255); + else + gib_imlib_image_draw_rectangle(dst, x, y, w, h, 0, 0, 0, 255); + D_RETURN_(5); +} + +void +feh_menu_draw_submenu_at(int x, + int y, + int w, + int h, + Imlib_Image dst, + int ox, + int oy, + int selected) +{ + ImlibPolygon poly; + + D_ENTER(5); + + x -= ox; + y -= oy; + + imlib_context_set_image(dst); + poly = imlib_polygon_new(); + imlib_polygon_add_point(poly, x + 2, y + 5); + imlib_polygon_add_point(poly, x + 5, y + 7); + imlib_polygon_add_point(poly, x + 2, y + 11); + imlib_context_set_color(0, 0, 0, 60); + imlib_image_fill_polygon(poly); + imlib_polygon_free(poly); + + poly = imlib_polygon_new(); + imlib_polygon_add_point(poly, x, y + 3); + imlib_polygon_add_point(poly, x + 3, y + 6); + imlib_polygon_add_point(poly, x, y + 9); + imlib_context_set_color(0, 0, 0, 255); + imlib_image_fill_polygon(poly); + imlib_polygon_free(poly); + + D_RETURN_(5); + selected = 0; +} + + +void +feh_menu_draw_separator_at(int x, + int y, + int w, + int h, + Imlib_Image dst, + int ox, + int oy) +{ + D_ENTER(5); + gib_imlib_image_fill_rectangle(dst, x - ox + 2, y - oy + 2, w - 4, h - 4, 0, + 0, 0, 255); + D_RETURN_(5); +} + +void +feh_menu_item_draw_at(int x, + int y, + int w, + int h, + Imlib_Image dst, + int ox, + int oy, + int selected) +{ + D_ENTER(5); + imlib_context_set_image(dst); + if (selected) + gib_imlib_image_fill_rectangle(dst, x - ox, y - oy, w, h, 255, 255, 255, + 178); + D_RETURN_(5); +} + + +void +feh_raise_all_menus(void) +{ + feh_menu_list *l; + + D_ENTER(5); + + for (l = menus; l; l = l->next) { + if (l->menu->visible) + XRaiseWindow(disp, l->menu->win); + } + D_RETURN_(5); +} + +void +feh_redraw_menus(void) +{ + feh_menu_list *l; + + D_ENTER(5); + + for (l = menus; l; l = l->next) { + if (l->menu->needs_redraw) + feh_menu_redraw(l->menu); + } + + D_RETURN_(5); +} + +feh_menu * +feh_menu_get_from_window(Window win) +{ + feh_menu_list *l; + + D_ENTER(5); + for (l = menus; l; l = l->next) + if (l->menu->win == win) + D_RETURN(5, l->menu); + D_RETURN(5, NULL); +} + +void +feh_menu_init_main(void) +{ + feh_menu *m; + feh_menu_item *mi; + + D_ENTER(4); + if (!common_menus) + feh_menu_init_common(); + + menu_main = feh_menu_new(); + menu_main->name = estrdup("MAIN"); + + feh_menu_add_entry(menu_main, "File", NULL, "FILE", NULL, NULL, NULL); + if (opt.slideshow || opt.multiwindow) { +#if 0 + feh_menu_item *mi; + + mi = + feh_menu_add_entry(menu_main, "Jump to", NULL, "JUMP", NULL, NULL, + NULL); + mi->func_gen_sub = feh_menu_func_gen_jump; +#endif + + feh_menu_add_entry(menu_main, "Sort List", NULL, "SORT", NULL, NULL, + NULL); + mi = + feh_menu_add_entry(menu_main, "Image Info", NULL, "INFO", NULL, NULL, + NULL); + mi->func_gen_sub = feh_menu_func_gen_info; + feh_menu_add_entry(menu_main, NULL, NULL, NULL, NULL, NULL, NULL); + } + mi = + feh_menu_add_entry(menu_main, "Options", NULL, "OPTIONS", NULL, NULL, + NULL); + mi->func_gen_sub = feh_menu_func_gen_options; + + if (!opt.full_screen) + feh_menu_add_entry(menu_main, "About " PACKAGE, NULL, NULL, + feh_menu_cb_about, NULL, NULL); + if (opt.multiwindow) + feh_menu_add_entry(menu_main, "Close", NULL, NULL, feh_menu_cb_close, + NULL, NULL); + feh_menu_add_entry(menu_main, "Exit", NULL, NULL, feh_menu_cb_exit, NULL, + NULL); + + m = feh_menu_new(); + m->name = estrdup("FILE"); + feh_menu_add_entry(m, "Reset", NULL, NULL, feh_menu_cb_reset, NULL, NULL); + feh_menu_add_entry(m, "Resize Window", NULL, NULL, feh_menu_cb_fit, NULL, + NULL); + feh_menu_add_entry(m, "Reload", NULL, NULL, feh_menu_cb_reload, NULL, NULL); + feh_menu_add_entry(m, "Save Image", NULL, NULL, feh_menu_cb_save_image, + NULL, NULL); + feh_menu_add_entry(m, "Save List", NULL, NULL, + feh_menu_cb_save_filelist, NULL, NULL); + feh_menu_add_entry(m, "Edit in Place", NULL, "EDIT", NULL, NULL, NULL); + feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", NULL, NULL, NULL); + feh_menu_add_entry(m, NULL, NULL, NULL, NULL, NULL, NULL); + feh_menu_add_entry(m, "Hide", NULL, NULL, feh_menu_cb_remove, NULL, NULL); + feh_menu_add_entry(m, "Delete", NULL, "CONFIRM", NULL, NULL, NULL); + + D_RETURN_(4); +} + + +void +feh_menu_init_common() +{ + int num_desks, i; + char buf[30]; + feh_menu *m; + + D_ENTER(4); + + if (!opt.menu_fn) { + opt.menu_fn = gib_imlib_load_font(opt.menu_font); + if (!opt.menu_fn) + eprintf + ("couldn't load menu font %s, did you make install?\nAre you specifying a nonexistant font?\nDid you tell feh where to find it with --fontpath?", + opt.menu_font); + } + if (!opt.menu_style_l) { + opt.menu_style_l = gib_style_new_from_ascii(opt.menu_style); + if (!opt.menu_style_l) { + weprintf + ("couldn't load style file for menu fonts, (%s).\nDid you make install? Menus will look boring without the style file.", + opt.menu_style); + } + } + + m = feh_menu_new(); + m->name = estrdup("SORT"); + + feh_menu_add_entry(m, "By File Name", NULL, NULL, feh_menu_cb_sort_filename, + NULL, NULL); + feh_menu_add_entry(m, "By Image Name", NULL, NULL, + feh_menu_cb_sort_imagename, NULL, NULL); + if (opt.preload || (opt.sort > SORT_FILENAME)) + feh_menu_add_entry(m, "By File Size", NULL, NULL, + feh_menu_cb_sort_filesize, NULL, NULL); + feh_menu_add_entry(m, "Randomize", NULL, NULL, feh_menu_cb_sort_randomize, + NULL, NULL); + + m = feh_menu_new(); + m->name = estrdup("CONFIRM"); + feh_menu_add_entry(m, "Confirm", NULL, NULL, feh_menu_cb_delete, NULL, + NULL); + + m = feh_menu_new(); + m->name = estrdup("EDIT"); + feh_menu_add_entry(m, "Rotate 90 CW", NULL, NULL, feh_menu_cb_edit_rotate, + (void *) 1, NULL); + feh_menu_add_entry(m, "Rotate 180", NULL, NULL, feh_menu_cb_edit_rotate, + (void *) 2, NULL); + feh_menu_add_entry(m, "Rotate 90 CCW", NULL, NULL, feh_menu_cb_edit_rotate, + (void *) 3, NULL); + + menu_bg = feh_menu_new(); + menu_bg->name = estrdup("BACKGROUND"); + + num_desks = feh_wm_get_num_desks(); + if (num_desks > 1) { + feh_menu_add_entry(menu_bg, "Set Tiled", NULL, "TILED", NULL, NULL, NULL); + feh_menu_add_entry(menu_bg, "Set Seamless", NULL, "SEAMLESS", NULL, NULL, + NULL); + feh_menu_add_entry(menu_bg, "Set Scaled", NULL, "SCALED", NULL, NULL, + NULL); + feh_menu_add_entry(menu_bg, "Set Centered", NULL, "CENTERED", NULL, NULL, + NULL); + + m = feh_menu_new(); + m->name = estrdup("TILED"); + for (i = 0; i < num_desks; i++) { + snprintf(buf, sizeof(buf), "Desktop %d", i + 1); + if (opt.slideshow || opt.multiwindow) + feh_menu_add_entry(m, buf, NULL, NULL, + feh_menu_cb_background_set_tiled, (void *) i, + NULL); + else + feh_menu_add_entry(m, buf, NULL, NULL, + feh_menu_cb_background_set_tiled_no_file, + (void *) i, NULL); + } + + m = feh_menu_new(); + m->name = estrdup("SEAMLESS"); + for (i = 0; i < num_desks; i++) { + snprintf(buf, sizeof(buf), "Desktop %d", i + 1); + feh_menu_add_entry(m, buf, NULL, NULL, + feh_menu_cb_background_set_seamless, (void *) i, + NULL); + } + + + m = feh_menu_new(); + m->name = estrdup("SCALED"); + for (i = 0; i < num_desks; i++) { + snprintf(buf, sizeof(buf), "Desktop %d", i + 1); + + if (opt.slideshow || opt.multiwindow) + feh_menu_add_entry(m, buf, NULL, NULL, + feh_menu_cb_background_set_scaled, (void *) i, + NULL); + else + feh_menu_add_entry(m, buf, NULL, NULL, + feh_menu_cb_background_set_scaled_no_file, + (void *) i, NULL); + } + + m = feh_menu_new(); + m->name = estrdup("CENTERED"); + for (i = 0; i < num_desks; i++) { + snprintf(buf, sizeof(buf), "Desktop %d", i + 1); + if (opt.slideshow || opt.multiwindow) + feh_menu_add_entry(m, buf, NULL, NULL, + feh_menu_cb_background_set_centered, (void *) i, + NULL); + else + feh_menu_add_entry(m, buf, NULL, NULL, + feh_menu_cb_background_set_centered_no_file, + (void *) i, NULL); + } + } else { + if (opt.slideshow || opt.multiwindow) { + feh_menu_add_entry(menu_bg, "Set Tiled", NULL, NULL, + feh_menu_cb_background_set_tiled, NULL, NULL); + feh_menu_add_entry(menu_bg, "Set Seamless", NULL, NULL, + feh_menu_cb_background_set_seamless, NULL, NULL); + feh_menu_add_entry(menu_bg, "Set Scaled", NULL, NULL, + feh_menu_cb_background_set_scaled, NULL, NULL); + feh_menu_add_entry(menu_bg, "Set Centered", NULL, NULL, + feh_menu_cb_background_set_centered, NULL, NULL); + } else { + feh_menu_add_entry(menu_bg, "Set Tiled", NULL, NULL, + feh_menu_cb_background_set_tiled_no_file, NULL, + NULL); + feh_menu_add_entry(menu_bg, "Set Seamless", NULL, NULL, + feh_menu_cb_background_set_seamless, NULL, NULL); + feh_menu_add_entry(menu_bg, "Set Scaled", NULL, NULL, + feh_menu_cb_background_set_scaled_no_file, NULL, + NULL); + feh_menu_add_entry(menu_bg, "Set Centered", NULL, NULL, + feh_menu_cb_background_set_centered_no_file, NULL, + NULL); + } + } + common_menus = 1; + + D_RETURN_(4); +} + +void +feh_menu_init_about_win(void) +{ + D_ENTER(4); + + menu_about_win = feh_menu_new(); + menu_about_win->name = estrdup("ABOUTWIN"); + + feh_menu_add_entry(menu_about_win, "Close", NULL, NULL, feh_menu_cb_close, + NULL, NULL); + feh_menu_add_entry(menu_about_win, "Exit", NULL, NULL, feh_menu_cb_exit, + NULL, NULL); + + D_RETURN_(4); +} + +void +feh_menu_init_single_win(void) +{ + feh_menu *m; + feh_menu_item *mi; + + D_ENTER(4); + if (!common_menus) + feh_menu_init_common(); + + menu_single_win = feh_menu_new(); + menu_single_win->name = estrdup("SINGLEWIN"); + + feh_menu_add_entry(menu_single_win, "File", NULL, "SINGLEWIN_FILE", NULL, + NULL, NULL); + m = feh_menu_new(); + m->name = estrdup("SINGLEWIN_FILE"); + feh_menu_add_entry(m, "Reset", NULL, NULL, feh_menu_cb_reset, NULL, NULL); + feh_menu_add_entry(m, "Resize Window", NULL, NULL, feh_menu_cb_fit, NULL, + NULL); + feh_menu_add_entry(m, "Reload", NULL, NULL, feh_menu_cb_reload, NULL, NULL); + feh_menu_add_entry(m, "Save Image", NULL, NULL, feh_menu_cb_save_image, + NULL, NULL); + feh_menu_add_entry(m, "Save List", NULL, NULL, + feh_menu_cb_save_filelist, NULL, NULL); + feh_menu_add_entry(m, "Edit in Place", NULL, "EDIT", NULL, NULL, NULL); + feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", NULL, NULL, NULL); + if (opt.multiwindow || opt.slideshow) { + feh_menu_add_entry(m, NULL, NULL, NULL, NULL, NULL, NULL); + feh_menu_add_entry(m, "Hide", NULL, NULL, feh_menu_cb_remove, NULL, NULL); + feh_menu_add_entry(m, "Delete", NULL, "CONFIRM", NULL, NULL, NULL); + } + + mi = + feh_menu_add_entry(menu_single_win, "Image Info", NULL, "INFO", NULL, + NULL, NULL); + mi->func_gen_sub = feh_menu_func_gen_info; + feh_menu_add_entry(menu_single_win, NULL, NULL, NULL, NULL, NULL, NULL); + mi = + feh_menu_add_entry(menu_single_win, "Options", NULL, "OPTIONS", NULL, + NULL, NULL); + mi->func_gen_sub = feh_menu_func_gen_options; + feh_menu_add_entry(menu_single_win, "About " PACKAGE, NULL, NULL, + feh_menu_cb_about, NULL, NULL); + feh_menu_add_entry(menu_single_win, "Close", NULL, NULL, feh_menu_cb_close, + NULL, NULL); + feh_menu_add_entry(menu_single_win, "Exit", NULL, NULL, feh_menu_cb_exit, + NULL, NULL); + + D_RETURN_(4); +} + +void +feh_menu_init_thumbnail_win(void) +{ + feh_menu *m; + feh_menu_item *mi; + + D_ENTER(4); + if (!common_menus) + feh_menu_init_common(); + + menu_thumbnail_win = feh_menu_new(); + menu_thumbnail_win->name = estrdup("THUMBWIN"); + + feh_menu_add_entry(menu_thumbnail_win, "File", NULL, "THUMBWIN_FILE", NULL, + NULL, NULL); + m = feh_menu_new(); + m->name = estrdup("THUMBWIN_FILE"); + feh_menu_add_entry(m, "Reset", NULL, NULL, feh_menu_cb_reset, NULL, NULL); + feh_menu_add_entry(m, "Resize Window", NULL, NULL, feh_menu_cb_fit, NULL, + NULL); + feh_menu_add_entry(m, "Save Image", NULL, NULL, feh_menu_cb_save_image, + NULL, NULL); + feh_menu_add_entry(m, "Save List", NULL, NULL, + feh_menu_cb_save_filelist, NULL, NULL); + feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", NULL, NULL, NULL); + feh_menu_add_entry(menu_thumbnail_win, NULL, NULL, NULL, NULL, NULL, NULL); + mi = + feh_menu_add_entry(menu_thumbnail_win, "Options", NULL, "OPTIONS", NULL, + NULL, NULL); + mi->func_gen_sub = feh_menu_func_gen_options; + feh_menu_add_entry(menu_thumbnail_win, "About " PACKAGE, NULL, NULL, + feh_menu_cb_about, NULL, NULL); + feh_menu_add_entry(menu_thumbnail_win, "Close", NULL, NULL, + feh_menu_cb_close, NULL, NULL); + feh_menu_add_entry(menu_thumbnail_win, "Exit", NULL, NULL, feh_menu_cb_exit, + NULL, NULL); + D_RETURN_(4); +} + + +void +feh_menu_init_thumbnail_viewer(void) +{ + feh_menu *m; + feh_menu_item *mi; + + D_ENTER(4); + if (!common_menus) + feh_menu_init_common(); + + menu_thumbnail_viewer = feh_menu_new(); + menu_thumbnail_viewer->name = estrdup("THUMBVIEW"); + + feh_menu_add_entry(menu_thumbnail_viewer, "File", NULL, "THUMBVIEW_FILE", + NULL, NULL, NULL); + m = feh_menu_new(); + m->name = estrdup("THUMBVIEW_FILE"); + feh_menu_add_entry(m, "Reset", NULL, NULL, feh_menu_cb_reset, NULL, NULL); + feh_menu_add_entry(m, "Resize Window", NULL, NULL, feh_menu_cb_fit, NULL, + NULL); + feh_menu_add_entry(m, "Reload", NULL, NULL, feh_menu_cb_reload, NULL, NULL); + feh_menu_add_entry(m, "Save Image", NULL, NULL, feh_menu_cb_save_image, + NULL, NULL); + feh_menu_add_entry(m, "Save List", NULL, NULL, + feh_menu_cb_save_filelist, NULL, NULL); + feh_menu_add_entry(m, "Edit in Place", NULL, "EDIT", NULL, NULL, NULL); + feh_menu_add_entry(m, "Background", NULL, "BACKGROUND", NULL, NULL, NULL); + feh_menu_add_entry(m, NULL, NULL, NULL, NULL, NULL, NULL); + feh_menu_add_entry(m, "Hide", NULL, NULL, feh_menu_cb_remove_thumb, NULL, + NULL); + feh_menu_add_entry(m, "Delete", NULL, "THUMBVIEW_CONFIRM", NULL, NULL, + NULL); + mi = + feh_menu_add_entry(menu_thumbnail_viewer, "Image Info", NULL, "INFO", + NULL, NULL, NULL); + mi->func_gen_sub = feh_menu_func_gen_info; + feh_menu_add_entry(menu_thumbnail_viewer, NULL, NULL, NULL, NULL, NULL, + NULL); + mi = + feh_menu_add_entry(menu_thumbnail_viewer, "Options", NULL, "OPTIONS", + NULL, NULL, NULL); + mi->func_gen_sub = feh_menu_func_gen_options; + feh_menu_add_entry(menu_thumbnail_viewer, "About " PACKAGE, NULL, NULL, + feh_menu_cb_about, NULL, NULL); + feh_menu_add_entry(menu_thumbnail_viewer, "Close", NULL, NULL, + feh_menu_cb_close, NULL, NULL); + feh_menu_add_entry(menu_thumbnail_viewer, "Exit", NULL, NULL, + feh_menu_cb_exit, NULL, NULL); + m = feh_menu_new(); + m->name = estrdup("THUMBVIEW_CONFIRM"); + feh_menu_add_entry(m, "Confirm", NULL, NULL, feh_menu_cb_delete_thumb, NULL, + NULL); + D_RETURN_(4); +} + +static void +feh_menu_cb_background_set_tiled(feh_menu * m, + feh_menu_item * i, + void *data) +{ + char *path; + + D_ENTER(4); + path = feh_absolute_path(FEH_FILE(m->fehwin->file->data)->filename); + feh_wm_set_bg(path, m->fehwin->im, 0, 0, (int) data, 1); + free(path); + D_RETURN_(4); + i = NULL; +} + +static void +feh_menu_cb_background_set_seamless(feh_menu * m, + feh_menu_item * i, + void *data) +{ + Imlib_Image im; + + D_ENTER(4); + im = gib_imlib_clone_image(m->fehwin->im); + gib_imlib_image_tile(im); + feh_wm_set_bg(NULL, im, 0, 0, (int) data, 1); + gib_imlib_free_image_and_decache(im); + D_RETURN_(4); + i = NULL; +} + +static void +feh_menu_cb_background_set_scaled(feh_menu * m, + feh_menu_item * i, + void *data) +{ + char *path; + + D_ENTER(4); + path = feh_absolute_path(FEH_FILE(m->fehwin->file->data)->filename); + feh_wm_set_bg(path, m->fehwin->im, 0, 1, (int) data, 1); + free(path); + D_RETURN_(4); + i = NULL; +} + +static void +feh_menu_cb_background_set_centered(feh_menu * m, + feh_menu_item * i, + void *data) +{ + char *path; + + D_ENTER(4); + path = feh_absolute_path(FEH_FILE(m->fehwin->file->data)->filename); + feh_wm_set_bg(path, m->fehwin->im, 1, 0, (int) data, 1); + free(path); + D_RETURN_(4); + i = NULL; +} + +static void +feh_menu_cb_background_set_tiled_no_file(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + feh_wm_set_bg(NULL, m->fehwin->im, 0, 0, (int) data, 1); + D_RETURN_(4); + i = NULL; +} + +static void +feh_menu_cb_background_set_scaled_no_file(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + feh_wm_set_bg(NULL, m->fehwin->im, 0, 1, (int) data, 1); + D_RETURN_(4); + i = NULL; +} + +static void +feh_menu_cb_background_set_centered_no_file(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + feh_wm_set_bg(NULL, m->fehwin->im, 1, 0, (int) data, 1); + D_RETURN_(4); + i = NULL; +} + +static void +feh_menu_cb_about(feh_menu * m, + feh_menu_item * i, + void *data) +{ + Imlib_Image im; + winwidget winwid; + + D_ENTER(4); + if (feh_load_image_char(&im, PREFIX "/share/feh/images/about.png") != 0) { + winwid = + winwidget_create_from_image(im, "About " PACKAGE, WIN_TYPE_ABOUT); + winwid->file = + gib_list_add_front(NULL, + feh_file_new(PREFIX "/share/feh/images/about.png")); + winwidget_show(winwid); + } + D_RETURN_(4); + m = NULL; + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_close(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + winwidget_destroy(m->fehwin); + D_RETURN_(4); + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_exit(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + winwidget_destroy_all(); + D_RETURN_(4); + m = NULL; + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_reset(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + if (m->fehwin->has_rotated) { + m->fehwin->im_w = gib_imlib_image_get_width(m->fehwin->im); + m->fehwin->im_h = gib_imlib_image_get_height(m->fehwin->im); + winwidget_resize(m->fehwin, m->fehwin->im_w, m->fehwin->im_h); + } + winwidget_reset_image(m->fehwin); + winwidget_render_image(m->fehwin, 1, 1); + D_RETURN_(4); + i = NULL; + data = NULL; +} + + +static void +feh_menu_cb_reload(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + feh_reload_image(m->fehwin, 0, 0); + D_RETURN_(4); + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_remove(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + feh_filelist_image_remove(m->fehwin, 0); + D_RETURN_(4); + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_delete(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + feh_filelist_image_remove(m->fehwin, 1); + D_RETURN_(4); + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_remove_thumb(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + feh_thumbnail_mark_removed(FEH_FILE(m->fehwin->file->data), 0); + feh_filelist_image_remove(m->fehwin, 0); + D_RETURN_(4); + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_delete_thumb(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + feh_thumbnail_mark_removed(FEH_FILE(m->fehwin->file->data), 1); + feh_filelist_image_remove(m->fehwin, 1); + D_RETURN_(4); + i = NULL; + data = NULL; +} + + +static void +feh_menu_cb_sort_filename(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + filelist = gib_list_sort(filelist, feh_cmp_filename); + if(!opt.no_jump_on_resort){ + slideshow_change_image(m->fehwin, SLIDE_FIRST); + }; + D_RETURN_(4); + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_sort_imagename(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + filelist = gib_list_sort(filelist, feh_cmp_name); + if(!opt.no_jump_on_resort){ + slideshow_change_image(m->fehwin, SLIDE_FIRST); + }; + D_RETURN_(4); + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_sort_filesize(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + filelist = gib_list_sort(filelist, feh_cmp_size); + if(!opt.no_jump_on_resort){ + slideshow_change_image(m->fehwin, SLIDE_FIRST); + }; + D_RETURN_(4); + i = NULL; + data = NULL; +} + +static void +feh_menu_cb_sort_randomize(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + filelist = gib_list_randomize(filelist); + if(!opt.no_jump_on_resort){ + slideshow_change_image(m->fehwin, SLIDE_FIRST); + }; + D_RETURN_(4); + i = NULL; + data = NULL; +} + +static feh_menu * +feh_menu_func_gen_jump(feh_menu * m, + feh_menu_item * i, + void *data) +{ + feh_menu *mm; + gib_list *l; + + D_ENTER(4); + mm = feh_menu_new(); + mm->name = estrdup("JUMP"); + for (l = filelist; l; l = l->next) { + feh_menu_add_entry(mm, FEH_FILE(l->data)->name, NULL, NULL, + feh_menu_cb_jump_to, l, NULL); + } + D_RETURN(4, mm); + m = NULL; + i = NULL; + data = NULL; +} + +static feh_menu * +feh_menu_func_gen_info(feh_menu * m, + feh_menu_item * i, + void *data) +{ + Imlib_Image im; + feh_menu *mm; + feh_file *file; + char buffer[400]; + + D_ENTER(4); + if (!m->fehwin->file) + D_RETURN(4, NULL); + file = FEH_FILE(m->fehwin->file->data); + im = m->fehwin->im; + if (!im) + D_RETURN(4, NULL); + mm = feh_menu_new(); + mm->name = estrdup("INFO"); + snprintf(buffer, sizeof(buffer), "Filename: %s", file->name); + feh_menu_add_entry(mm, buffer, NULL, NULL, NULL, NULL, NULL); + if (!file->info) + feh_file_info_load(file, im); + if (file->info) { + snprintf(buffer, sizeof(buffer), "Size: %dKb", file->info->size / 1024); + feh_menu_add_entry(mm, buffer, NULL, NULL, NULL, NULL, NULL); + snprintf(buffer, sizeof(buffer), "Dimensions: %dx%d", file->info->width, + file->info->height); + feh_menu_add_entry(mm, buffer, NULL, NULL, NULL, NULL, NULL); + snprintf(buffer, sizeof(buffer), "Type: %s", file->info->format); + feh_menu_add_entry(mm, buffer, NULL, NULL, NULL, NULL, NULL); + } + + mm->func_free = feh_menu_func_free_info; + D_RETURN(4, mm); + i = NULL; + data = NULL; +} + +static void +feh_menu_func_free_info(feh_menu * m, + void *data) +{ + D_ENTER(4); + feh_menu_free(m); + D_RETURN_(4); + data = NULL; +} + + +static feh_menu * +feh_menu_func_gen_options(feh_menu * m, + feh_menu_item * i, + void *data) +{ + feh_menu *mm; + + D_ENTER(4); + mm = feh_menu_new(); + mm->name = estrdup("OPTIONS"); + mm->fehwin = m->fehwin; + feh_menu_add_toggle_entry(mm, "Auto-Zoom", NULL, NULL, + feh_menu_cb_opt_auto_zoom, NULL, NULL, + opt.auto_zoom); + feh_menu_add_toggle_entry(mm, "Freeze Window Size", NULL, NULL, + feh_menu_cb_opt_freeze_window, NULL, NULL, + opt.geom_flags); + feh_menu_add_toggle_entry(mm, "Fullscreen", NULL, NULL, + feh_menu_cb_opt_fullscreen, NULL, NULL, + m->fehwin->full_screen); +#ifdef HAVE_LIBXINERAMA + feh_menu_add_toggle_entry(mm, "Use Xinerama", NULL, NULL, + feh_menu_cb_opt_xinerama, NULL, NULL, + opt.xinerama); +#endif /* HAVE_LIBXINERAMA */ + feh_menu_add_entry(mm, NULL, NULL, NULL, NULL, NULL, NULL); + feh_menu_add_toggle_entry(mm, "Draw Filename", NULL, NULL, + feh_menu_cb_opt_draw_filename, NULL, NULL, + opt.draw_filename); + feh_menu_add_toggle_entry(mm, "Keep HTTP Files", NULL, NULL, + feh_menu_cb_opt_keep_http, NULL, NULL, + opt.keep_http); + mm->func_free = feh_menu_func_free_options; + D_RETURN(4, mm); + i = NULL; + data = NULL; +} + +static void +feh_menu_func_free_options(feh_menu * m, + void *data) +{ + D_ENTER(4); + feh_menu_free(m); + D_RETURN_(4); + data = NULL; +} + +static void +feh_menu_cb_jump_to(feh_menu * m, + feh_menu_item * i, + void *data) +{ + gib_list *l; + + D_ENTER(4); + l = (gib_list *) data; + if (l->prev) { + current_file = l->prev; + slideshow_change_image(m->fehwin, SLIDE_NEXT); + } else if (l->next) { + current_file = l->next; + slideshow_change_image(m->fehwin, SLIDE_PREV); + } + + D_RETURN_(4); + i = NULL; + m = NULL; +} + +static void +feh_menu_cb_fit(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + winwidget_size_to_image(m->fehwin); + D_RETURN_(4); + data = NULL; +} + +static void +feh_menu_cb_edit_rotate(feh_menu * m, + feh_menu_item * i, + void *data) +{ + D_ENTER(4); + feh_edit_inplace_orient(m->fehwin, (int) data); + D_RETURN_(4); +} + +static void +feh_menu_cb_save_image(feh_menu * m, + feh_menu_item * i, + void *data) +{ + slideshow_save_image(m->fehwin); +} + +static void +feh_menu_cb_save_filelist(feh_menu * m, + feh_menu_item * i, + void *data) +{ + feh_save_filelist(); +} + +static void +feh_menu_cb_opt_draw_filename(feh_menu * m, + feh_menu_item * i, + void *data) +{ + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) + opt.draw_filename = TRUE; + else + opt.draw_filename = FALSE; + winwidget_rerender_all(0, 1); +} + +static void +feh_menu_cb_opt_keep_http(feh_menu * m, + feh_menu_item * i, + void *data) +{ + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) + opt.keep_http = TRUE; + else + opt.keep_http = FALSE; +} + +static void +feh_menu_cb_opt_freeze_window(feh_menu * m, + feh_menu_item * i, + void *data) +{ + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) { + opt.geom_flags = (WidthValue | HeightValue); + opt.geom_w = m->fehwin->w; + opt.geom_h = m->fehwin->h; + } else { + opt.geom_flags = 0; + } +} + +static void +feh_menu_cb_opt_fullscreen(feh_menu * m, + feh_menu_item * i, + void *data) +{ + int curr_screen = 0; + + MENU_ITEM_TOGGLE(i); + if (MENU_ITEM_IS_ON(i)) + m->fehwin->full_screen = TRUE; + else + m->fehwin->full_screen = FALSE; + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + int i, rect[4]; + + /* FIXME: this doesn't do what it should; XGetGeometry always + * returns x,y == 0,0. I think that's due to the hints being passed + * (or more specifically, a missing hint) to X in winwidget_create + */ + winwidget_get_geometry(m->fehwin, rect); + /* printf("window: (%d, %d)\n", rect[0], rect[1]); + printf("found %d screens.\n", num_xinerama_screens); */ + for (i = 0; i < num_xinerama_screens; i++) { + xinerama_screen = 0; + /* printf("%d: [%d, %d, %d, %d] (%d, %d)\n", + i, + xinerama_screens[i].x_org, xinerama_screens[i].y_org, + xinerama_screens[i].width, xinerama_screens[i].height, + rect[0], rect[1]);*/ + if (XY_IN_RECT(rect[0], rect[1], + xinerama_screens[i].x_org, xinerama_screens[i].y_org, + xinerama_screens[i].width, xinerama_screens[i].height)) { + curr_screen = xinerama_screen = i; + break; + } + + } + } +#endif /* HAVE_LIBXINERAMA */ + + winwidget_destroy_xwin(m->fehwin); + winwidget_create_window(m->fehwin, m->fehwin->im_w, m->fehwin->im_h); + + winwidget_render_image(m->fehwin, 1, 1); + winwidget_show(m->fehwin); + +#ifdef HAVE_LIBXINERAMA + /* if we have xinerama and we're using it, then full screen the window + * on the head that the window was active on */ + if (m->fehwin->full_screen == TRUE && opt.xinerama && xinerama_screens) { + xinerama_screen = curr_screen; + winwidget_move(m->fehwin, + xinerama_screens[curr_screen].x_org, + xinerama_screens[curr_screen].y_org); + } +#endif /* HAVE_LIBXINERAMA */ +} + +static void +feh_menu_cb_opt_auto_zoom(feh_menu * m, + feh_menu_item * i, + void *data) +{ + MENU_ITEM_TOGGLE(i); + opt.auto_zoom = MENU_ITEM_IS_ON(i) ? 1 : 0; + winwidget_rerender_all(1, 1); +} + +#ifdef HAVE_LIBXINERAMA +static void +feh_menu_cb_opt_xinerama(feh_menu * m, + feh_menu_item * i, + void *data) +{ + MENU_ITEM_TOGGLE(i); + opt.xinerama = MENU_ITEM_IS_ON(i) ? 1 : 0; + + if (opt.xinerama) { + init_xinerama(); + } else { + XFree(xinerama_screens); + xinerama_screens = NULL; + } + winwidget_rerender_all(1, 1); +} +#endif /* HAVE_LIBXINERAMA */ diff --git a/src/menu.h b/src/menu.h new file mode 100644 index 0000000..214e8a2 --- /dev/null +++ b/src/menu.h @@ -0,0 +1,192 @@ +/* menu.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef MENU_H +#define MENU_H + +typedef struct _feh_menu feh_menu; +typedef struct _feh_menu_item feh_menu_item; +typedef struct _feh_menu_list feh_menu_list; + + +#define MENU_ITEM_STATE_NORMAL 0x00 +#define MENU_ITEM_STATE_SELECTED 0x01 +#define MENU_ITEM_STATE_ON 0x02 + +#define MENU_ITEM_IS_SELECTED(item) \ +((item)->state & MENU_ITEM_STATE_SELECTED) +#define MENU_ITEM_SET_NORMAL(item) \ +(item)->state = (item)->state & (~MENU_ITEM_STATE_SELECTED) +#define MENU_ITEM_SET_SELECTED(item) \ +(item)->state = (item)->state | MENU_ITEM_STATE_SELECTED + +#define MENU_ITEM_IS_ON(item) \ +((item)->state & MENU_ITEM_STATE_ON) +#define MENU_ITEM_TOGGLE_ON(item) \ +((item)->state |= MENU_ITEM_STATE_ON) +#define MENU_ITEM_TOGGLE_OFF(item) \ +((item)->state &= ~(MENU_ITEM_STATE_ON)) +#define MENU_ITEM_TOGGLE_SET(item, setting) \ +((setting) ? MENU_ITEM_TOGGLE_ON(item) : MENU_ITEM_TOGGLE_OFF(item)) +#define MENU_ITEM_TOGGLE(item) \ +(((item)->state & MENU_ITEM_STATE_ON) ? MENU_ITEM_TOGGLE_OFF(item) : MENU_ITEM_TOGGLE_ON(item)) + + +#define RECTS_INTERSECT(x, y, w, h, xx, yy, ww, hh) \ +((SPANS_COMMON((x), (w), (xx), (ww))) && (SPANS_COMMON((y), (h), (yy), (hh)))) +#define SPANS_COMMON(x1, w1, x2, w2) \ +(!((((x2) + (w2)) <= (x1)) || ((x2) >= ((x1) + (w1))))) + + +#define FEH_MENU_PAD_LEFT 3 +#define FEH_MENU_PAD_RIGHT 3 +#define FEH_MENU_PAD_TOP 3 +#define FEH_MENU_PAD_BOTTOM 3 + +#define FEH_MENUITEM_PAD_LEFT 2 +#define FEH_MENUITEM_PAD_RIGHT 2 +#define FEH_MENUITEM_PAD_TOP 1 +#define FEH_MENUITEM_PAD_BOTTOM 1 + +#define FEH_MENU_SEP_MAX_H 5 + +#define FEH_MENU_SUBMENU_H 14 +#define FEH_MENU_SUBMENU_W 9 + +#define FEH_MENU_TOGGLE_H 7 +#define FEH_MENU_TOGGLE_W 7 +#define FEH_MENU_TOGGLE_PAD 3 + +typedef void (*menu_func) (feh_menu * m, feh_menu_item * i, void *data); +typedef feh_menu *(*menuitem_func_gen) (feh_menu * m, feh_menu_item * i, + void *data); + +struct _feh_menu_list +{ + feh_menu *menu; + feh_menu_list *next; +}; + +struct _feh_menu_item +{ + int state; + Imlib_Image icon; + char *text; + char *submenu; + menu_func func; + void (*func_free) (void *data); + void *data; + feh_menu_item *next; + feh_menu_item *prev; + unsigned char is_toggle; + int text_x, icon_x, sub_x, toggle_x; + int x, y, w, h; + menuitem_func_gen func_gen_sub; +}; + +struct _feh_menu +{ + char *name; + winwidget fehwin; + Window win; + Pixmap pmap; + int x, y, w, h; + int item_w, item_h; + int visible; + feh_menu_item *items; + feh_menu *next; + feh_menu *prev; + Imlib_Updates updates; + Imlib_Image bg; + int needs_redraw; + void *data; + int calc; + void (*func_free) (feh_menu * m, void *data); +}; + +feh_menu *feh_menu_new(void); + +feh_menu_item *feh_menu_find_selected(feh_menu * m); +feh_menu_item *feh_menu_find_at_xy(feh_menu * m, int x, int y); +void feh_menu_deselect_selected(feh_menu * m); +void feh_menu_select(feh_menu * m, feh_menu_item * i); +void feh_menu_show_at(feh_menu * m, int x, int y); +void feh_menu_show_at_xy(feh_menu * m, winwidget winwin, int x, int y); +void feh_menu_show_at_submenu(feh_menu * m, feh_menu * parent_m, + feh_menu_item * i); +void feh_menu_hide(feh_menu * m, int func_free); +void feh_menu_show(feh_menu * m); +feh_menu_item *feh_menu_add_entry(feh_menu * m, char *text, Imlib_Image icon, + char *submenu, menu_func func, void *data, + void (*func_free) (void *data)); +feh_menu_item * +feh_menu_add_toggle_entry(feh_menu * m, char *text, Imlib_Image icon, + char *submenu, menu_func func, void *data, + void (*func_free) (void *data), int setting); +void feh_menu_entry_get_size(feh_menu * m, feh_menu_item * i, int *w, int *h); +void feh_menu_calc_size(feh_menu * m); +void feh_menu_draw_item(feh_menu * m, feh_menu_item * i, Imlib_Image im, + int ox, int oy); +void feh_menu_redraw(feh_menu * m); +void feh_menu_move(feh_menu * m, int x, int y); +void feh_menu_slide_all_menus_relative(int dx, int dy); +void feh_menu_init_main(void); +void feh_menu_init_single_win(void); +void feh_menu_init_about_win(void); +void feh_menu_init_common(void); +void feh_menu_init_thumbnail_viewer(void); +void feh_menu_init_thumbnail_win(void); +void feh_menu_draw_to_buf(feh_menu * m, Imlib_Image im, int ox, int oy); +void feh_menu_draw_menu_bg(feh_menu * m, Imlib_Image im, int ox, int oy); +void feh_menu_draw_submenu_at(int x, int y, int w, int h, Imlib_Image dst, + int ox, int oy, int selected); +void feh_menu_draw_separator_at(int x, int y, int w, int h, Imlib_Image dst, + int ox, int oy); +void feh_menu_item_draw_at(int x, int y, int w, int h, Imlib_Image dst, + int ox, int oy, int selected); +void feh_menu_draw_toggle_at(int x, int y, int w, int h, Imlib_Image dst, + int ox, int oy, int on); +void feh_redraw_menus(void); +feh_menu *feh_menu_find(char *name); +void feh_redraw_menus(void); +feh_menu *feh_menu_get_from_window(Window win); +void feh_raise_all_menus(void); +void feh_menu_free(feh_menu * m); +feh_menu_item *feh_menu_find_selected_r(feh_menu *m, feh_menu **parent); +void feh_menu_select_prev(feh_menu *selected_menu, feh_menu_item *selected_item); +void feh_menu_select_next(feh_menu *selected_menu, feh_menu_item *selected_item); +void feh_menu_item_activate(feh_menu *selected_menu, + feh_menu_item *selected_item); +void feh_menu_select_parent(feh_menu *selected_menu, feh_menu_item *selected_item); +void feh_menu_select_submenu(feh_menu *selected_menu, feh_menu_item *selected_item); + +extern feh_menu *menu_root; +extern feh_menu *menu_single_win; +extern feh_menu *menu_about_win; +extern feh_menu *menu_thumbnail_viewer; +extern feh_menu *menu_thumbnail_win; +extern Window menu_cover; + +#endif diff --git a/src/menubg_aluminium.png b/src/menubg_aluminium.png new file mode 100644 index 0000000..eed00f1 Binary files /dev/null and b/src/menubg_aluminium.png differ diff --git a/src/menubg_aqua.png b/src/menubg_aqua.png new file mode 100644 index 0000000..3a72590 Binary files /dev/null and b/src/menubg_aqua.png differ diff --git a/src/menubg_black.png b/src/menubg_black.png new file mode 100644 index 0000000..08b4c2b Binary files /dev/null and b/src/menubg_black.png differ diff --git a/src/menubg_britney.png b/src/menubg_britney.png new file mode 100644 index 0000000..1b1b94e Binary files /dev/null and b/src/menubg_britney.png differ diff --git a/src/menubg_brushed.png b/src/menubg_brushed.png new file mode 100644 index 0000000..32fad47 Binary files /dev/null and b/src/menubg_brushed.png differ diff --git a/src/menubg_chrome.png b/src/menubg_chrome.png new file mode 100644 index 0000000..24f3025 Binary files /dev/null and b/src/menubg_chrome.png differ diff --git a/src/menubg_default.png b/src/menubg_default.png new file mode 100644 index 0000000..dd21188 Binary files /dev/null and b/src/menubg_default.png differ diff --git a/src/menubg_pastel.png b/src/menubg_pastel.png new file mode 100644 index 0000000..ec96566 Binary files /dev/null and b/src/menubg_pastel.png differ diff --git a/src/menubg_sky.png b/src/menubg_sky.png new file mode 100644 index 0000000..e0be8ca Binary files /dev/null and b/src/menubg_sky.png differ diff --git a/src/menubg_wood.png b/src/menubg_wood.png new file mode 100644 index 0000000..4dc7440 Binary files /dev/null and b/src/menubg_wood.png differ diff --git a/src/multiwindow.c b/src/multiwindow.c new file mode 100644 index 0000000..60d5660 --- /dev/null +++ b/src/multiwindow.c @@ -0,0 +1,78 @@ +/* multiwindow.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "winwidget.h" +#include "timers.h" +#include "filelist.h" +#include "options.h" + +void +init_multiwindow_mode(void) +{ + winwidget w = NULL; + gib_list *l; + feh_file *file = NULL; + + D_ENTER(2); + + mode = "multiwindow"; + + for (l = filelist; l; l = l->next) + { + char *s = NULL; + int len = 0; + file = FEH_FILE(l->data); + current_file = l; + + if (!opt.title) + { + len = strlen(PACKAGE " - ") + strlen(file->filename) + 1; + s = emalloc(len); + snprintf(s, len, PACKAGE " - %s", file->filename); + } + else + { + s = estrdup(feh_printf(opt.title, file)); + } + + if ((w = winwidget_create_from_file(l, s, WIN_TYPE_SINGLE)) != NULL) + { + winwidget_show(w); + if (opt.reload > 0) + feh_add_unique_timer(cb_reload_timer, w, opt.reload); + if (!feh_main_iteration(0)) + exit(0); + } + else + { + D(3, + ("EEEK. Couldn't load image in multiwindow mode. " + "I 'm not sure if this is a problem\n")); + } + free(s); + } + D_RETURN_(2); +} diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..d9d803d --- /dev/null +++ b/src/options.c @@ -0,0 +1,1203 @@ +/* options.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "options.h" + +static void check_options(void); +static void feh_parse_option_array(int argc, char **argv); +static void feh_parse_environment_options(void); +static void feh_check_theme_options(int arg, char **argv); +static void feh_parse_options_from_string(char *opts); +static void feh_load_options_for_theme(char *theme); +static char *theme; + +fehoptions opt; + +void +init_parse_options(int argc, char **argv) +{ + D_ENTER(4); + + /* For setting the command hint on X windows */ + cmdargc = argc; + cmdargv = argv; + + /* Set default options */ + memset(&opt, 0, sizeof(fehoptions)); + opt.display = 1; + opt.aspect = 1; + opt.slideshow_delay = -1.0; + opt.thumb_w = 60; + opt.thumb_h = 60; + opt.menu_font = estrdup(DEFAULT_MENU_FONT); + opt.font = estrdup(DEFAULT_FONT); + opt.menu_bg = estrdup(PREFIX "/share/feh/images/menubg_default.png"); + opt.menu_style = estrdup(PREFIX "/share/feh/fonts/menu.style"); + opt.menu_border = 4; + + opt.next_button = 1; + opt.zoom_button = 2; + opt.menu_button = 3; + opt.menu_ctrl_mask = 0; + opt.reload_button = 0; + + opt.rotate_button = 2; + opt.no_rotate_ctrl_mask = 0; + opt.blur_button = 1; + opt.no_blur_ctrl_mask = 0; + + opt.no_jump_on_resort = 0; + + opt.builtin_http = 0; + + opt.xinerama = 0; + opt.screen_clip = 1; +#ifdef HAVE_LIBXINERAMA + /* if we're using xinerama, then enable it by default */ + opt.xinerama = 1; +#endif /* HAVE_LIBXINERAMA */ + + opt.fmmode = 0; + + D(3, ("About to parse env options (if any)\n")); + /* Check for and parse any options in FEH_OPTIONS */ + feh_parse_environment_options(); + + D(3, ("About to parse commandline options\n")); + /* Parse the cmdline args */ + feh_parse_option_array(argc, argv); + + D(3, ("About to check for theme configuration\n")); + feh_check_theme_options(argc, argv); + + /* If we have a filelist to read, do it now */ + if (opt.filelistfile) + { + /* joining two reverse-sorted lists in this manner works nicely for us + here, as files specified on the commandline end up at the *end* of + the combined filelist, in the specified order. */ + D(3, ("About to load filelist from file\n")); + filelist = gib_list_cat(filelist, feh_read_filelist(opt.filelistfile)); + } + + D(4, ("Options parsed\n")); + + if(opt.bgmode) + D_RETURN_(4); + + filelist_len = gib_list_length(filelist); + if (!filelist_len) + show_mini_usage(); + + check_options(); + + feh_prepare_filelist(); + D_RETURN_(4); +} + +static void +feh_check_theme_options(int arg, char **argv) +{ + D_ENTER(4); + if (!theme) + { + /* This prevents screw up when running src/feh or ./feh */ + char *pos = strrchr(argv[0], '/'); + + if (pos) + theme = estrdup(pos + 1); + else + theme = estrdup(argv[0]); + } + D(3, ("Theme name is %s\n", theme)); + + feh_load_options_for_theme(theme); + + free(theme); + D_RETURN_(4); + arg = 0; +} + +static void +feh_load_options_for_theme(char *theme) +{ + FILE *fp = NULL; + char *home; + char *rcpath = NULL; + char s[1024], s1[1024], s2[1024]; + + D_ENTER(4); + + if (opt.rcfile) + { + if ((fp = fopen(opt.rcfile, "r")) == NULL) + { + weprintf("couldn't load the specified rcfile %s\n", opt.rcfile); + D_RETURN_(4); + } + } + else + { + home = getenv("HOME"); + if (!home) + eprintf("D'oh! Please define HOME in your environment!" + "It would really help me out...\n"); + rcpath = estrjoin("/", home, ".fehrc", NULL); + D(3, ("Trying %s for config\n", rcpath)); + fp = fopen(rcpath, "r"); + + if (!fp && ((fp = fopen("/etc/fehrc", "r")) == NULL)) + { + feh_create_default_config(rcpath); + + if ((fp = fopen(rcpath, "r")) == NULL) + D_RETURN_(4); + } + + free(rcpath); + } + + /* Oooh. We have an options file :) */ + for (; fgets(s, sizeof(s), fp);) + { + s1[0] = '\0'; + s2[0] = '\0'; + sscanf(s, "%s %[^\n]\n", (char *) &s1, (char *) &s2); + if (!(*s1) || (!*s2) || (*s1 == '\n') || (*s1 == '#')) + continue; + D(5, ("Got theme/options pair %s/%s\n", s1, s2)); + if (!strcmp(s1, theme)) + { + D(4, ("A match. Using options %s\n", s2)); + feh_parse_options_from_string(s2); + break; + } + } + fclose(fp); + D_RETURN_(4); +} + +static void +feh_parse_environment_options(void) +{ + char *opts; + + D_ENTER(4); + + if ((opts = getenv("FEH_OPTIONS")) == NULL) + D_RETURN_(4); + + weprintf + ("The FEH_OPTIONS configuration method is depreciated and will soon die.\n" + "Use the .fehrc configuration file instead."); + + /* We definitely have some options to parse */ + feh_parse_options_from_string(opts); + D_RETURN_(4); +} + +/* FIXME This function is a crufty bitch ;) */ +static void +feh_parse_options_from_string(char *opts) +{ + char **list = NULL; + int num = 0; + char *s; + char *t; + char last = 0; + int inquote = 0; + int i = 0; + + D_ENTER(4); + + /* So we don't reinvent the wheel (not again, anyway), we use the + getopt_long function to do this parsing as well. This means it has to + look like the real argv ;) */ + + list = malloc(sizeof(char *)); + + list[num++] = estrdup(PACKAGE); + + for (s = opts, t = opts;; t++) + { + if ((*t == ' ') && !(inquote)) + { + *t = '\0'; + num++; + list = erealloc(list, sizeof(char *) * num); + + list[num - 1] = feh_string_normalize(s); + s = t + 1; + } + else if (*t == '\0') + { + num++; + list = erealloc(list, sizeof(char *) * num); + + list[num - 1] = feh_string_normalize(s); + break; + } + else if (*t == '\"' && last != '\\') + inquote = !(inquote); + last = *t; + } + + feh_parse_option_array(num, list); + + for (i = 0; i < num; i++) + if (list[i]) + free(list[i]); + if (list) + free(list); + D_RETURN_(4); +} + +char * +feh_string_normalize(char *str) +{ + char ret[4096]; + char *s; + int i = 0; + char last = 0; + + D_ENTER(4); + D(4, ("normalizing %s\n", str)); + ret[0] = '\0'; + + for (s = str;; s++) + { + if (*s == '\0') + break; + else if ((*s == '\"') && (last == '\\')) + ret[i++] = '\"'; + else if ((*s == '\"') && (last == 0)) + ; + else if ((*s == ' ') && (last == '\\')) + ret[i++] = ' '; + else + ret[i++] = *s; + + last = *s; + } + if (i && ret[i - 1] == '\"') + ret[i - 1] = '\0'; + else + ret[i] = '\0'; + D(4, ("normalized to %s\n", ret)); + + D_RETURN(4, estrdup(ret)); +} + +static void +feh_parse_option_array(int argc, char **argv) +{ + static char stropts[] = + "a:A:b:BcC:dD:e:E:f:Fg:GhH:iIj:klL:mM:nNo:O:pqQrR:sS:tT:uUvVwW:xXy:zZ1:2:4:56:78:90:.@:^:~:):|:_:+:"; + static struct option lopts[] = { + /* actions */ + {"help", 0, 0, 'h'}, /* okay */ + {"version", 0, 0, 'v'}, /* okay */ + /* toggles */ + {"montage", 0, 0, 'm'}, /* okay */ + {"collage", 0, 0, 'c'}, /* okay */ + {"index", 0, 0, 'i'}, /* okay */ + {"fullindex", 0, 0, 'I'}, /* okay */ + {"verbose", 0, 0, 'V'}, /* okay */ + {"borderless", 0, 0, 'x'}, /* okay */ + {"keep-http", 0, 0, 'k'}, /* okay */ + {"stretch", 0, 0, 's'}, /* okay */ + {"multiwindow", 0, 0, 'w'}, /* okay */ + {"recursive", 0, 0, 'r'}, /* okay */ + {"randomize", 0, 0, 'z'}, /* okay */ + {"list", 0, 0, 'l'}, /* okay */ + {"quiet", 0, 0, 'q'}, /* okay */ + {"loadables", 0, 0, 'U'}, /* okay */ + {"unloadables", 0, 0, 'u'}, /* okay */ + {"no-menus", 0, 0, 'N'}, + {"full-screen", 0, 0, 'F'}, + {"auto-zoom", 0, 0, 'Z'}, + {"ignore-aspect", 0, 0, 'X'}, + {"draw-filename", 0, 0, 'd'}, + {"preload", 0, 0, 'p'}, + {"reverse", 0, 0, 'n'}, + {"thumbnails", 0, 0, 't'}, + {"wget-timestamp", 0, 0, 'G'}, + {"builtin", 0, 0, 'Q'}, + {"menu-ctrl-mask", 0, 0, '5'}, /* okay */ + {"scale-down", 0, 0, '.'}, /* okay */ + {"no-rotate-ctrl-mask", 0, 0, '7'}, + {"no-blur-ctrl-mask", 0, 0, '9'}, + {"no-jump-on-resort",0,0,220}, + {"hide-pointer",0,0,221}, + /* options with values */ + {"output", 1, 0, 'o'}, /* okay */ + {"output-only", 1, 0, 'O'}, /* okay */ + {"action", 1, 0, 'A'}, /* okay */ + {"limit-width", 1, 0, 'W'}, /* okay */ + {"limit-height", 1, 0, 'H'}, /* okay */ + {"reload", 1, 0, 'R'}, /* okay */ + {"alpha", 1, 0, 'a'}, /* okay */ + {"sort", 1, 0, 'S'}, /* okay */ + {"theme", 1, 0, 'T'}, /* okay */ + {"filelist", 1, 0, 'f'}, /* okay */ + {"customlist", 1, 0, 'L'}, /* okay */ + {"geometry", 1, 0, 'g'}, /* okay */ + {"menu-font", 1, 0, 'M'}, + {"thumb-width", 1, 0, 'y'}, + {"thumb-height", 1, 0, 'E'}, + {"slideshow-delay", 1, 0, 'D'}, + {"font", 1, 0, 'e'}, + {"title-font", 1, 0, '@'}, + {"title", 1, 0, '^'}, + {"thumb-title", 1, 0, '~'}, + {"bg", 1, 0, 'b'}, + {"fontpath", 1, 0, 'C'}, + {"menu-bg", 1, 0, ')'}, + {"next-button", 1, 0, '1'}, + {"zoom-button", 1, 0, '2'}, + {"menu-button", 1, 0, '4'}, + {"rotate-button", 1, 0, '6'}, + {"blur-button", 1, 0, '8'}, + {"reload-button", 1, 0, '0'}, + {"start-at", 1, 0, '|'}, + {"rcfile", 1, 0, '_'}, + {"debug-level", 1, 0, '+'}, + {"output-dir", 1, 0, 'j'}, + {"bg-tile", 1, 0, 200}, + {"bg-center", 1, 0, 201}, + {"bg-scale", 1, 0, 202}, + {"bg-seamless", 1, 0, 203}, + {"menu-style", 1, 0, 204}, + {"zoom", 1, 0, 205}, + {"xinerama", 1, 0, 206}, + {"screen-clip", 1, 0, 207}, + {"menu-border", 1, 0, 208}, + {"caption-path", 1, 0, 209}, + {"action1", 1, 0, 210}, + {"action2", 1, 0, 211}, + {"action3", 1, 0, 212}, + {"action4", 1, 0, 213}, + {"action5", 1, 0, 214}, + {"action6", 1, 0, 215}, + {"action7", 1, 0, 216}, + {"action8", 1, 0, 217}, + {"action9", 1, 0, 218}, + {"fmmode", 0, 0, 219}, + {"draw-actions", 0, 0, 222}, + {"cache-thumbnails", 0, 0, 223}, + {"cycle-once", 0, 0, 224}, + {0, 0, 0, 0} + }; + int optch = 0, cmdx = 0, i = 0; + int geomret; + + D_ENTER(4); + + /* Now to pass some optionarinos */ + while ((optch = getopt_long(argc, argv, stropts, lopts, &cmdx)) != EOF) + { + D(5, ("Got option, getopt calls it %d, or %c\n", optch, optch)); + switch (optch) + { + case 0: + break; + case 'h': + show_usage(); + break; + case 'v': + show_version(); + break; + case 'm': + opt.index = 1; + opt.index_show_name = 0; + opt.index_show_size = 0; + opt.index_show_dim = 0; + break; + case 'c': + opt.collage = 1; + break; + case 'i': + opt.index = 1; + opt.index_show_name = 1; + opt.index_show_size = 0; + opt.index_show_dim = 0; + break; + case '.': + opt.scale_down = 1; + break; + case 'I': + opt.index = 1; + opt.index_show_name = 1; + opt.index_show_size = 1; + opt.index_show_dim = 1; + break; + case 'l': + opt.list = 1; + break; + case 'G': + opt.wget_timestamp = 1; + break; + case 'Q': + opt.builtin_http = 1; + break; + case 'L': + opt.customlist = estrdup(optarg); + break; + case 'M': + free(opt.menu_font); + opt.menu_font = estrdup(optarg); + break; + case '+': + opt.debug_level = atoi(optarg); + break; + case 'n': + opt.reverse = 1; + break; + case 'g': + opt.geom_flags = XParseGeometry(optarg, &opt.geom_x, &opt.geom_y, &opt.geom_w, &opt.geom_h); + break; + case 'N': + opt.no_menus = 1; + break; + case 'V': + opt.verbose = 1; + break; + case 'q': + opt.quiet = 1; + break; + case 'x': + opt.borderless = 1; + break; + case 'k': + opt.keep_http = 1; + break; + case 's': + opt.stretch = 1; + break; + case 'w': + opt.multiwindow = 1; + break; + case 'r': + opt.recursive = 1; + break; + case 'z': + opt.randomize = 1; + break; + case 'd': + opt.draw_filename = 1; + break; + case 'F': + opt.full_screen = 1; + break; + case 'Z': + opt.auto_zoom = 1; + break; + case 'U': + opt.loadables = 1; + break; + case 'u': + opt.unloadables = 1; + break; + case 'p': + opt.preload = 1; + break; + case 'X': + opt.aspect = 0; + break; + case 'S': + if (!strcasecmp(optarg, "name")) + opt.sort = SORT_NAME; + else if (!strcasecmp(optarg, "filename")) + opt.sort = SORT_FILENAME; + else if (!strcasecmp(optarg, "width")) + opt.sort = SORT_WIDTH; + else if (!strcasecmp(optarg, "height")) + opt.sort = SORT_HEIGHT; + else if (!strcasecmp(optarg, "pixels")) + opt.sort = SORT_PIXELS; + else if (!strcasecmp(optarg, "size")) + opt.sort = SORT_SIZE; + else if (!strcasecmp(optarg, "format")) + opt.sort = SORT_FORMAT; + else + { + weprintf + ("Unrecognised sort mode \"%s\". Defaulting to sort by filename", + optarg); + opt.sort = SORT_FILENAME; + } + break; + case 'o': + opt.output = 1; + opt.output_file = estrdup(optarg); + break; + case 'O': + opt.output = 1; + opt.output_file = estrdup(optarg); + opt.display = 0; + break; + case 'T': + theme = estrdup(optarg); + break; + case 'C': + D(3, ("adding fontpath %s\n", optarg)); + imlib_add_path_to_font_path(optarg); + break; + case 'e': + opt.font = estrdup(optarg); + break; + case '@': + opt.title_font = estrdup(optarg); + break; + case '^': + opt.title = estrdup(optarg); + break; + case '~': + opt.thumb_title = estrdup(optarg); + break; + case 'b': + opt.bg = 1; + opt.bg_file = estrdup(optarg); + break; + case '_': + opt.rcfile = estrdup(optarg); + break; + case 'A': + opt.actions[0] = estrdup(optarg); + break; + case 'W': + opt.limit_w = atoi(optarg); + break; + case 'H': + opt.limit_h = atoi(optarg); + break; + case 'y': + opt.thumb_w = atoi(optarg); + break; + case 'E': + opt.thumb_h = atoi(optarg); + break; + case ')': + free(opt.menu_bg); + opt.menu_bg = estrdup(optarg); + break; + case 'D': + opt.slideshow_delay = atof(optarg); + break; + case 'R': + opt.reload = atoi(optarg); + break; + case 'a': + opt.alpha = 1; + opt.alpha_level = 255 - atoi(optarg); + break; + case 'f': + opt.filelistfile = estrdup(optarg); + break; + case '1': + opt.next_button = atoi(optarg); + break; + case '2': + opt.zoom_button = atoi(optarg); + break; + case '4': + opt.menu_button = atoi(optarg); + break; + case '5': + opt.menu_ctrl_mask = 1; + break; + case '6': + opt.rotate_button = atoi(optarg); + break; + case '7': + opt.no_rotate_ctrl_mask = 1; + break; + case '8': + opt.blur_button = atoi(optarg); + break; + case '9': + opt.no_blur_ctrl_mask = 1; + break; + case '|': + opt.start_list_at = atoi(optarg); + break; + case '0': + opt.reload_button = atoi(optarg); + break; + case 't': + opt.thumbs = 1; + opt.index_show_name = 1; + opt.index_show_size = 0; + opt.index_show_dim = 0; + break; + case 'j': + opt.output_dir = estrdup(optarg); + break; + case 200: + opt.bgmode = BG_MODE_TILE; + opt.output_file = estrdup(optarg); + break; + case 201: + opt.bgmode = BG_MODE_CENTER; + opt.output_file = estrdup(optarg); + break; + case 202: + opt.bgmode = BG_MODE_SCALE; + opt.output_file = estrdup(optarg); + break; + case 203: + opt.bgmode = BG_MODE_SEAMLESS; + opt.output_file = estrdup(optarg); + break; + case 204: + free(opt.menu_style); + opt.menu_style = estrdup(optarg); + break; + case 205: + opt.default_zoom = atoi(optarg); + break; + case 206: + opt.xinerama = atoi(optarg); + break; + case 207: + opt.screen_clip = atoi(optarg); + break; + case 208: + opt.menu_border = atoi(optarg); + break; + case 209: + opt.caption_path = estrdup(optarg); + break; + case 210: + opt.actions[1] = estrdup(optarg); + break; + case 211: + opt.actions[2] = estrdup(optarg); + break; + case 212: + opt.actions[3] = estrdup(optarg); + break; + case 213: + opt.actions[4] = estrdup(optarg); + break; + case 214: + opt.actions[5] = estrdup(optarg); + break; + case 215: + opt.actions[6] = estrdup(optarg); + break; + case 216: + opt.actions[7] = estrdup(optarg); + break; + case 217: + opt.actions[8] = estrdup(optarg); + break; + case 218: + opt.actions[9] = estrdup(optarg); + break; + case 220: + opt.no_jump_on_resort = 1; + break; + case 221: + opt.hide_pointer = 1; + break; + case 219: + opt.fmmode = 1; + opt.sort = SORT_FILENAME; + break; + case 222: + opt.draw_actions = 1; + break; + case 223: + opt.cache_thumbnails = 1; + break; + case 224: + opt.cycle_once = 1; + break; + default: + break; + } + } + + /* Now the leftovers, which must be files */ + if (optind < argc) + { + while (optind < argc) + { + /* If recursive is NOT set, but the only argument is a directory + name, we grab all the files in there, but not subdirs */ + add_file_to_filelist_recursively(argv[optind++], FILELIST_FIRST); + } + } + + /* So that we can safely be called again */ + optind = 1; + D_RETURN_(4); +} + + +static void +check_options(void) +{ + D_ENTER(4); + if ((opt.index + opt.collage) > 1) + { + weprintf("you can't use collage mode and index mode together.\n" + " I'm going with index"); + opt.collage = 0; + } + + if (opt.full_screen && opt.multiwindow) + { + weprintf + ("you shouldn't combine multiwindow mode with full-screen mode,\n" + " Multiwindow mode has been disabled."); + opt.multiwindow = 0; + } + + if (opt.list && (opt.multiwindow || opt.index || opt.collage)) + { + weprintf("list mode can't be combined with other processing modes,\n" + " list mode disabled."); + opt.list = 0; + } + + if (opt.sort && opt.randomize) + { + weprintf("You cant sort AND randomize the filelist...\n" + "randomize mode has been unset\n"); + opt.randomize = 0; + } + + if (opt.loadables && opt.unloadables) + { + weprintf("You cant show loadables AND unloadables...\n" + "you might as well use ls ;)\n" + "loadables only will be shown\n"); + opt.unloadables = 0; + } + + if (opt.thumb_title && (!opt.thumbs)) + { + weprintf("Doesn't make sense to set thumbnail title when not in\n" + "thumbnail mode.\n"); + free(opt.thumb_title); + opt.thumb_title = NULL; + } + D_RETURN_(4); +} + +void +show_version(void) +{ + printf(PACKAGE " version " VERSION "\n"); + exit(0); +} + +void +show_mini_usage(void) +{ + fprintf(stdout, + PACKAGE " - No loadable images specified.\nUse " PACKAGE + " --help for detailed usage information\n"); + exit(0); +} + +void +show_usage(void) +{ + fprintf(stdout, +"Usage : " PACKAGE " [OPTIONS]... FILES...\n" +" Where a FILE is an image file or a directory.\n" +" Multiple files are supported.\n" +" Urls are supported. They must begin with http:// or ftp:// and you must\n" +" have wget installed to download the files for viewing.\n" +" Options can also be specified in the in the feh configuration file. See\n" +" man feh for more details\n" +" -h, --help display this help and exit\n" +" -v, --version output version information and exit\n" +" -V, --verbose output useful information, progress bars, etc\n" +" -q, --quiet Don't report non-fatal errors for failed loads\n" +" Verbose and quiet modes are not mutually exclusive,\n" +" the first controls informational messages, the\n" +" second only errors.\n" +" -T, --theme THEME Load options from config file with name THEME\n" +" see man feh for more info.\n" +" --rcfile FILE Use FILE to parse themes and options from,\n" +" instead of the default ~/.fehrc, /etc/fehrc files.\n" +" -r, --recursive Recursively expand any directories in FILE to\n" +" the content of those directories. (Take it easy)\n" +" -z, --randomize When viewing multiple files in a slideshow,\n" +" randomise the file list before displaying\n" +" --no-jump-on-resort Don't jump to the first image when the filelist\n" +" is resorted.\n" +" -g, --geometry STRING Limit (and don't change) the window size. Takes\n" +" an X-style geometry string like 640x480.\n" +" Note that larger images will be zoomed out to fit\n" +" but you can see them at 1:1 by clicking the zoom\n" +" button.\n" +" -f, --filelist FILE This option is similar to the playlists used by\n" +" music software. If FILE exists, it will be read\n" +" for a list of files to load, in the order they\n" +" appear. The format is a list of image filenames,\n" +" absolute or relative to the current directory,\n" +" one filename per line.\n" +" If FILE doesn't exist, it will be created from the\n" +" internal filelist at the end of a viewing session.\n" +" This is best used to store the results of complex\n" +" sorts (-Spixels for example) for later viewing.\n" +" Any changes to the internal filelist (such as\n" +" deleting a file or it being pruned for being\n" +" unloadable) will be saved to FILE when feh exits.\n" +" You can add files to filelists by specifying them\n" +" on the commandline when also specifying the list.\n" +" -p, --preload Preload images. This doesn't mean hold them in\n" +" RAM, it means run through and eliminate unloadable\n" +" images first. Otherwise they will be removed as you\n" +" flick through.\n" +" --scale-down Automatically scale down images too big for the\n" +" screen. Currently only works with -P\n" +" -F, --full-screen Make the window fullscreen\n" +" -Z, --auto-zoom Zoom picture to screen size in fullscreen mode,\n" +" is affected by the option --stretch\n" +" --zoom PERCENT Zooms images by a PERCENT, when in full screen\n" +" mode or when window geometry is fixed. If combined\n" +" with --auto-zoom, zooming will be limited to the\n" +" the size.\n" +" -w, --multiwindow Disable slideshow mode. With this setting,\n" +" instead of opening multiple files in slideshow\n" +" mode, multiple windows will be opened.\n" +" -x, --borderless Create borderless windows\n" +" -d, --draw-filename Draw the filename at the top-left of the image.\n" +" --title TITLE Use TITLE as the window title in slideshow mode.\n" +" -D, --slideshow-delay NUM For slideshow mode, specifies time delay (seconds,\n" +" can be a decimal) between automatically changing\n" +" slides.\n" +" --cycle-once exit feh after one loop through a slideshow\n" +" -R, --reload NUM Use this option to tell feh to reload an image\n" +" after NUM seconds. Useful for viewing webcams\n" +" via http, or even on your local machine.\n" +" -Q, --builtin Use builtin http grabber to grab remote files\n" +" instead of wget.\n" +" mechanism, useful if don't have wget.\n" +" -k, --keep-http When viewing files using http, feh normally\n" +" deletes the local copies after viewing, or,\n" +" if caching, on exit. This option prevents this\n" +" so that you get to keep the local copies.\n" +" They will be in the current working directory\n" +" with \"feh\" in the name.\n" +" --caption-path PATH Path to directory containing image captions.\n" +" This turns on caption viewing, and if captions\n" +" are found in PATH, which is relative to the\n" +" directory of each image, they are overlayed\n" +" on the displayed image.\n" +" e.g with caption path \"captions\", and viewing\n" +" image images/foo.jpg, caption will be looked for\n" +" as \"images/captions/foo.jpg.txt\"\n" +" -j, --output-dir Output directory for saved files. Really only\n" +" useful with the -k flag.\n" +" -G, --wget-timestamp When viewing http images with reload set (eg\n" +" webcams), try to only reload the image if the\n" +" remote file has changed.\n" +" -l, --list Don't display images. Analyse them and display an\n" +" 'ls' style listing. Useful in scripts hunt out\n" +" images of a certain size/resolution/type etc.\n" +" -L, --customlist FORMAT Use FORMAT as the format specifier for list\n" +" output. FORMAT is a printf-like string containing\n" +" image info specifiers. See FORMAT SPECIFIERS.\n" +" -U, --loadable Don't display images. Just print out their name\n" +" if imlib2 can successfully load them.\n" +" -u, --unloadable Don't display images. Just print out their name\n" +" if imlib2 can NOT successfully load them.\n" +" -S, --sort SORT_TYPE The file list may be sorted according to image\n" +" parameters. Allowed sort types are: name,\n" +" filename, width, height, pixels, size, format.\n" +" For sort modes other than name or filename, a\n" +" preload run will be necessary, causing a delay\n" +" proportional to the number of images in the list\n" +" -n, --reverse Reverse the sort order. Use this to invert the order\n" +" of the filelist. Eg to sort in reverse width order,\n" +" use -nSwidth\n" +" -A, --action ACTION Specify a string as an action to perform on the\n" +" image. In slideshow or multiwindow modes, the action\n" +" in list mode, or loadable|unloadable modes, the\n" +" action will be run for each file.\n" +" The action will be executed by /bin/sh. Use\n" +" format specifiers to refer to image info. See\n" +" FORMAT SPECIFIERS for examples\n" +" Eg. -A \"mv %%f ~/images/%%n\"\n" +" In slideshow mode, the next image will be shown\n" +" after running the command, in multiwindow mode,\n" +" the window will be closed.\n" +" --action1 ACTION These extra action options allow you to specify\n" +" --action2 ACTION multiple additional actions which can be invoked\n" +" ... using the appropriate number key 1-9\n" +" --action9 ACTION\n" +" -m, --montage Enable montage mode. Montage mode creates a new\n" +" image consisting of a grid of thumbnails of the\n" +" images specified using FILE... When montage mode\n" +" is selected, certain other options become\n" +" available. See MONTAGE MODE OPTIONS\n" +" -c, --collage Same as montage mode, but the thumbnails are\n" +" distributed randomly. You must specify width and\n" +" height or supply a background image or both\n" +" -i, --index Enable Index mode. Index mode is similar to\n" +" montage mode, and accepts the same options. It\n" +" creates an index print of thumbails, printing the\n" +" images name beneath each thumbnail. Index mode\n" +" enables certain other options, see INDEX MODE\n" +" OPTIONS\n" +" -t, --thumbnails As --index, but clicking an image will open it in\n" +" a new viewing window\n" +" --cache-thumbnails Enable thumbnail caching\n" +" -I, --fullindex Same as index mode, but below each thumbnail you\n" +" get image name, size and dimensions\n" +" --bg-tile FILE\n" +" --bg-center FILE\n" +" --bg-scale FILE\n" +" --bg-seamless FILE Set your desktop background to FILE. Feh can\n" +" use enlightenment IPC if you are running it,\n" +" or will fall back to X methods.\n" +" Feh stores the commandline necessary to restore\n" +" the background you chose in ~/.fehbg. So to have\n" +" feh-set backgrounds restored when you restart X,\n" +" add the line \"eval `cat $HOME/.fehbg`\" to your\n" +" X startup script (e.g. ~/.xsession). Note that\n" +" you only need to do this for non E window\n" +" managers.\n" +" --fontpath PATH Specify an extra directory to look in for fonts,\n" +" can be used multiple times to add multiple paths.\n" +" -M, --menu-font FONT Use FONT for the font in menus.\n" +" --menu-style FILE Use FILE as the style descriptor for menu text.\n" +" --menu-bg BG Use BG for the background image in menus.\n" +" --menu-border INT Specify number of pixels that define the menu\n" +" background's border. Borders are not stretched\n" +" when images are scaled.\n" +" -N, --no-menus Don't load or show any menus.\n" +" -1, --next-button B Use button B to advance to the next image in any\n" +" mode (defaults to 1, usually the left button).\n" +" -2, --zoom-button B Use button B to zoom the current image in any\n" +" mode (defaults to 2, usually the middle button).\n" +" -4, --menu-button B Use CTRL+Button B to activate the menu in any\n" +" mode. Set to 0 for any button. This option\n" +" is disabled if the -N or --no-menus option is set\n" +" (defaults to 3, usually the right button).\n" +" -5, --menu-ctrl-mask Require CTRL+Button for menu activation in\n" +" any mode (default=off).\n" +" -6, --rotate-button B Use CTRL+Button B to rotate the current image in\n" +" any mode (default=2).\n" +" -7, --no-rotate-ctrl-mask Don't require CTRL+Button for rotation in\n" +" any mode -- just use the button (default=off).\n" +" -8, --blur-button B Use CTRL+Button B to blur the current image in\n" +" any mode (default=1).\n" +" -9, --no-blur-ctrl-mask Don't require CTRL+Button for blurring in\n" +" any mode -- just use the button (default=off).\n" +" --xinerama [0|1] Enable/disable Xinerama support. Has no effect\n" +" unless you have an Xinerama compiled in.\n" +" --screen-clip [0|1] Enable/disable window clipping based on screen\n" +" size. WARNING: with this option disabled,\n" +" image windows could become very large, making\n" +" them unmanageable in certain window managers.\n" +" --hide-pointer In full screen mode, hide the X mouse pointer.\n" +" FORMAT SPECIFIERS\n" +" %%f image path/filename\n" +" %%n image name\n" +" %%s image size (bytes)\n" +" %%p image pixel size\n" +" %%w image width\n" +" %%h image height\n" +" %%t image format\n" +" %%P prints feh\n" +" %%v prints the version\n" +" %%m prints the mode (slideshow, multiwindow...)\n" +" %%l prints the total number of files in the filelist\n" +" %%u prints the current file number\n" +" \\n prints a newline\n" +" Eg. feh -A \"mv %%f ~/images/%%n\" *\n" +" MONTAGE MODE OPTIONS\n" +" -X, --ignore-aspect By default, the montage thumbnails will retain\n" +" their aspect ratios, while fitting in --thumb-width\n" +" and --thumb-height. This option will force them to\n" +" be the size set by --thumb-width and --thumb-height\n" +" This will prevent any whitespace in the final\n" +" montage\n" +" -s, --stretch Normally, if an image is smaller than the specified\n" +" thumbnail size, it will not be enlarged. If this\n" +" option is set, the image will be scaled up to fit\n" +" the thumbnail size. (Aspect ratio will be maintained\n" +" unless --ignore-aspect is specified)\n" +" -y, --thumb-width NUM Set thumbnail width in pixels\n" +" -E, --thumb-height NUM Set thumbnail height in pixels\n" +" Thumbnails default to 20x20 pixels\n" +" -W, --limit-width NUM Limit the width of the montage in pixels\n" +" -H, --limit-height NUM Limit the height of the montage in pixels\n" +" These options can be used together (to define the\n" +" image size exactly), or separately. If only one is\n" +" specified, theother is calculated from the number\n" +" of files specified and the size of the thumbnails.\n" +" The default is to limit width to 800 pixels and\n" +" calculate the height\n" +" -b, --bg FILE|trans Use FILE as a background for your montage. With\n" +" this option specified, the size of the montage will\n" +" default to the size of FILE if no size restrictions\n" +" are specified. Alternatively, if FILE is \"trans\",\n" +" make the background transparent.\n" +" -a, --alpha NUM When drawing thumbnails onto the background, apply\n" +" them with a transparency level of NUM (0-255).\n" +" -o FILE Save the created montage to FILE\n" +" -O FILE Just save the created montage to FILE\n" +" WITHOUT displaying it (use in scripts)\n" +" INDEX MODE OPTIONS\n" +" -e FONT Use FONT to print the information under each\n" +" thumbnail. FONT should be defined in the form\n" +" fontname/size(points). eg -e myfont/12\n" +" -t, --title-font FONT Use FONT to print a title on the index, if no\n" +" font is specified, a title will not be printed\n" +" SLIDESHOW KEYS\n" +" The default mode for viewing mulitple images is Slideshow mode\n" +" When viewing a slideshow, the following keys may be used:\n" +" p, P, , Goto previous slide\n" +" n, N, , Goto next slide\n" +" r, R Reload image (good for webcams)\n" +" v, V Toggle fullscreen\n" +" m, M Show popup menu\n" +" c, C Caption entry mode. If --caption-path has been\n" +" specified, then this enables caption editing.\n" +" The caption will turn yellow and be editable,\n" +" hit enter to confirm and save the caption, or\n" +" hit escape to cancel and revert the caption.\n" +" w, W Size window to current image dimensions\n" +" h, H Pause the slideshow (only useful when using\n" +" s, S Save current image to unique filename\n" +" f, F Save current filelist to unique filename\n" +" timed reloading or image changes)\n" +" <, > In place editing, rotate 90 degrees right/left\n" +" Goto first slide\n" +" Goto last slide\n" +" Quit the slideshow\n" +" +, = Increase reload delay\n" +" -, _ Decrease reload delay\n" +" Remove the currently viewed file from the filelist\n" +" Delete the currently viewed file and remove it\n" +" from the filelist\n" +" x, X Close current window\n" +" q, Q Quit the slideshow\n" +" Move the image to the left\n" +" Move the image to the right\n" +" Zoom in\n" +" Zoom out\n" +" Zoom to 100%%\n" +" Zoom to fit the window\n" +" ,0 Run action specified by --action option\n" +" 1-9 Run action 1-9 specified by --action[1-9] options\n" +"\n" +" MOUSE ACTIONS\n" +" When viewing an image, a click of mouse button 1 moves to the next image\n" +" (slideshow mode only), a drag of mouse button 1 pans the image, if the\n" +" viewable window is smaller than the image, button 2 zooms (click and drag\n" +" left->right to zoom in, right->left to zoom out, click once to restore\n" +" 1x zoom), and mouse button 3 pans.\n" +" Ctrl+button 1 blurs or sharpens the image (drag left to blur and right to\n" +" sharpen). Ctrl+button 2 rotates the image around the center point.\n" +" Button 3 activates the context-sensitive menu. Buttons can be redefined\n" +" with the -1 through -9 (or --*-button) cmdline flags. All you people\n" +" with million button mice can remove the ctrl mask with the --no-*-ctrl-mask\n" +" options.\n" "\n" "See 'man feh' for more detailed information\n" +"\n" +"This program is free software see the file COPYING for licensing info.\n" +"Copyright Tom Gilbert (and various contributors) 1999-2003\n" +"Email bugs to \n"); + exit(0); +} + +void +feh_create_default_config(char *rcfile) +{ + FILE *fp; + + D_ENTER(4); + + if ((fp = fopen(rcfile, "w")) == NULL) + { + weprintf("Unable to create default config file %s\n", rcfile); + D_RETURN_(4); + } + + fprintf(fp, + "# Feh configuration file.\n" + "# Lines starting with # are comments. Don't use comments mid-line.\n" + "\n" "# Feh expects to find this as ~/.fehrc or /etc/fehrc\n" + "# If both are available, ~/.fehrc will be used\n" "\n" + "# Options defined in theme_name/options pairs.\n" + "# Separate themename and options by whitespace.\n" "\n" + "# There are two ways of specifying the theme. Either use feh -Tthemename,\n" + "# or use a symbolic link to feh with the name of the theme. eg\n" + "# ln -s `which feh` ~/bin/mkindex\n" + "# Now when you run 'mkindex', feh will load the config specified for the\n" + "# mkindex theme.\n" "\n" "# ======================\n" + "# Some examples of usage\n" "# ======================\n" "\n" + "# Set the default feh options to be recursive and verbose\n" + "# feh -rV\n" "\n" + "# Multiple options can of course be used. They should all be on one line\n" + "# imagemap -rV --quiet -W 400 -H 300 --thumb-width 40 --thumb-height 30\n" + "\n" "# ================================================\n" + "# Here I set some useful themes for you to try out\n" + "# ================================================\n" "\n" + "# Webcam mode, simply specify the url(s).\n" + "# e.g. feh -Twebcam http://cam1 http://cam2\n" + "webcam --multiwindow --reload 20\n" "\n" + "# Create an index of the current directory. This version uses . as the\n" + "# current dir, so you don't even need any commandline arguments.\n" + "mkindex -iVO index.jpg .\n" "\n" "# More ambitious version...\n" + "imgidx -iVO .fehindex.jpg --limit-width 1200 --thumb-width 90 --thumb-height 90 .\n" + "\n" "# Show a presentation\n" + "present --full-screen --sort name\n" + "\n" + "# Booth mode ;-)\n" + "booth --full-screen --hide-pointer --slideshow-delay 20\n" + "\n" + "# Screw xscreensaver, use feh =)\n" + "screensave --full-screen --randomize --slideshow-delay 5\n" "\n" + "# Add tags to your html with ease :-)\n" + "newimg -q -L \"\\\"%%n\\\"\"\n" + "\n" + "# Different menus\n" + "chrome --menu-bg " PREFIX" /share/feh/images/menubg_chrome.png\n" + "brushed --menu-bg " PREFIX "/share/feh/images/menubg_brushed.png\n" + "pastel --menu-bg " PREFIX "/share/feh/images/menubg_pastel.png\n" + "aluminium --menu-bg " PREFIX "/share/feh/images/menubg_aluminium.png\n" + "wood --menu-bg " PREFIX "/share/feh/images/menubg_wood.png\n" + "aqua --menu-bg " PREFIX "/share/feh/images/menubg_aqua.png\n" + "sky --menu-bg " PREFIX "/share/feh/images/menubg_sky.png\n" + "orange --menu-bg " PREFIX "/share/feh/images/menubg_orange.png\n" + "light --menu-bg " PREFIX "/share/feh/images/menubg_light.png\n" + "black --menu-bg " PREFIX "/share/feh/images/menubg_black.png --menu-style " PREFIX "/share/feh/fonts/black.style\n" + "britney --menu-bg " PREFIX "/share/feh/images/menubg_britney.png\n"); + fclose(fp); + + D_RETURN_(4); +} diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..0101885 --- /dev/null +++ b/src/options.h @@ -0,0 +1,137 @@ +/* options.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef OPTIONS_H +#define OPTIONS_H + +struct __fehoptions +{ + unsigned char multiwindow; + unsigned char montage; + unsigned char collage; + unsigned char index; + unsigned char index_show_name; + unsigned char index_show_dim; + unsigned char index_show_size; + unsigned char thumbs; + unsigned char slideshow; + unsigned char recursive; + unsigned char output; + unsigned char verbose; + unsigned char display; + unsigned char bg; + unsigned char alpha; + unsigned char alpha_level; + unsigned char aspect; + unsigned char stretch; + unsigned char keep_http; + unsigned char borderless; + unsigned char randomize; + unsigned char no_jump_on_resort; + unsigned char full_screen; + unsigned char auto_zoom; + unsigned char draw_filename; + unsigned char list; + unsigned char quiet; + unsigned char preload; + unsigned char loadables; + unsigned char unloadables; + unsigned char reverse; + unsigned char no_menus; + unsigned char scale_down; + unsigned char builtin_http; + unsigned char wget_timestamp; + unsigned char bgmode; + unsigned char xinerama; + unsigned char screen_clip; + unsigned char hide_pointer; + unsigned char fmmode; + unsigned char draw_actions; + unsigned char cache_thumbnails; + unsigned char cycle_once; + + char *output_file; + char *output_dir; + char *bg_file; + char *font; + char *title_font; + char *title; + char *thumb_title; + char *actions[10]; + char *fontpath; + char *filelistfile; + char *menu_font; + char *customlist; + char *menu_bg; + char *rcfile; + char *menu_style; + char *caption_path; + + gib_style *menu_style_l; + + unsigned char next_button; + unsigned char zoom_button; + unsigned char menu_button; + unsigned char menu_ctrl_mask; + + unsigned char rotate_button; + unsigned char blur_button; + unsigned char reload_button; + unsigned char no_rotate_ctrl_mask; + unsigned char no_blur_ctrl_mask; + unsigned char no_pan_ctrl_mask; + + int thumb_w; + int thumb_h; + int limit_w; + int limit_h; + int reload; + int sort; + int debug_level; + int geom_flags; + int geom_x; + int geom_y; + int geom_w; + int geom_h; + int default_zoom; + int menu_border; + unsigned char adjust_reload; + unsigned int start_list_at; + + unsigned char mode; + unsigned char paused; + + double slideshow_delay; + + Imlib_Font menu_fn; +}; + +void init_parse_options(int argc, char **argv); +char *feh_string_normalize(char *str); +void feh_create_default_config(char *rcfile); + +extern fehoptions opt; + +#endif diff --git a/src/slideshow.c b/src/slideshow.c new file mode 100644 index 0000000..2ec05a8 --- /dev/null +++ b/src/slideshow.c @@ -0,0 +1,582 @@ +/* slideshow.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "timers.h" +#include "winwidget.h" +#include "options.h" + +void +init_slideshow_mode(void) +{ + winwidget w = NULL; + int success = 0; + char *s = NULL; + gib_list *l = NULL, *last = NULL; + feh_file *file = NULL; + + D_ENTER(3); + + mode = "slideshow"; + if (opt.start_list_at) + { + l = gib_list_nth(filelist, opt.start_list_at); + opt.start_list_at = 0; /* for next time */ + } + else + { + l = filelist; + } + for (; l; l = l->next) + { + file = FEH_FILE(l->data); + if (last) + { + filelist = feh_file_remove_from_list(filelist, last); + filelist_len--; + last = NULL; + } + current_file = l; + s = slideshow_create_name(file); + if ((w = winwidget_create_from_file(l, s, WIN_TYPE_SLIDESHOW)) != NULL) + { + free(s); + success = 1; + winwidget_show(w); + if (opt.slideshow_delay >= 0.0) + feh_add_timer(cb_slide_timer, w, opt.slideshow_delay, + "SLIDE_CHANGE"); + else if (opt.reload > 0) + feh_add_unique_timer(cb_reload_timer, w, opt.reload); + break; + } + else + { + free(s); + last = l; + } + } + if (!success) + show_mini_usage(); + D_RETURN_(3); +} + +void +cb_slide_timer(void *data) +{ + D_ENTER(4); + slideshow_change_image((winwidget) data, SLIDE_NEXT); + D_RETURN_(4); +} + +void +cb_reload_timer(void *data) +{ + winwidget w = (winwidget) data; + + D_ENTER(4); + feh_reload_image(w, 0, 0); + feh_add_unique_timer(cb_reload_timer, w, opt.reload); + D_RETURN_(4); +} + +void +feh_reload_image(winwidget w, int resize, int force_new) +{ + char *title, *new_title; + int len; + Imlib_Image tmp; + + D_ENTER(4); + + if (!w->file) { + weprintf("couldn't reload, this image has no file associated with it."); + D_RETURN_(4); + } + + free(FEH_FILE(w->file->data)->caption); + FEH_FILE(w->file->data)->caption = NULL; + + len = strlen(w->name) + sizeof("Reloading: ") + 1; + new_title = emalloc(len); + snprintf(new_title, len, "Reloading: %s", w->name); + title = estrdup(w->name); + winwidget_rename(w, new_title); + + + /* force imlib2 not to cache */ + if (force_new) { + winwidget_free_image(w); + } + + /* if the image has changed in dimensions - we gotta resize */ + if ((feh_load_image(&tmp, FEH_FILE(w->file->data))) == 0) { + if (force_new) { + eprintf("failed to reload image\n"); + } else { + weprintf("Couldn't reload image. Is it still there?"); + } + winwidget_rename(w, title); + free(title); + free(new_title); + D_RETURN_(4); + } + if (force_new) { + w->im = tmp; + resize = 1; + winwidget_reset_image(w); + } else { + if ((gib_imlib_image_get_width(w->im) != gib_imlib_image_get_width(tmp)) || (gib_imlib_image_get_height(w->im) != gib_imlib_image_get_height(tmp))) { + resize = 1; + winwidget_reset_image(w); + } + winwidget_free_image(w); + w->im = tmp; + } + + w->mode = MODE_NORMAL; + if ((w->im_w != gib_imlib_image_get_width(w->im)) + || (w->im_h != gib_imlib_image_get_height(w->im))) + w->had_resize = 1; + if (w->has_rotated) + { + Imlib_Image temp; + + temp = gib_imlib_create_rotated_image(w->im, 0.0); + w->im_w = gib_imlib_image_get_width(temp); + w->im_h = gib_imlib_image_get_height(temp); + gib_imlib_free_image_and_decache(temp); + } + else + { + w->im_w = gib_imlib_image_get_width(w->im); + w->im_h = gib_imlib_image_get_height(w->im); + } + winwidget_render_image(w, resize, 1); + + winwidget_rename(w, title); + free(title); + free(new_title); + + D_RETURN_(4); +} + + +void +slideshow_change_image(winwidget winwid, int change) +{ + int success = 0; + gib_list *last = NULL; + int i = 0; + int jmp = 1; + char *s; + + D_ENTER(4); + + /* Without this, clicking a one-image slideshow reloads it. Not very * + intelligent behaviour :-) */ + if (filelist_len < 2) + D_RETURN_(4); + + /* Ok. I do this in such an odd way to ensure that if the last or first * + image is not loadable, it will go through in the right direction to * + find the correct one. Otherwise SLIDE_LAST would try the last file, * + then loop forward to find a loadable one. */ + if (change == SLIDE_FIRST) + { + current_file = gib_list_last(filelist); + change = SLIDE_NEXT; + } + else if (change == SLIDE_LAST) + { + current_file = filelist; + change = SLIDE_PREV; + } + + /* The for loop prevents us looping infinitely */ + for (i = 0; i < filelist_len; i++) + { + winwidget_free_image(winwid); + switch (change) + { + case SLIDE_NEXT: + current_file = feh_list_jump(filelist, current_file, FORWARD, 1); + break; + case SLIDE_PREV: + current_file = feh_list_jump(filelist, current_file, BACK, 1); + break; + case SLIDE_JUMP_FWD: + if (filelist_len < 5) + jmp = 1; + else if (filelist_len < 40) + jmp = 2; + else + jmp = filelist_len / 20; + if (!jmp) + jmp = 2; + current_file = feh_list_jump(filelist, current_file, FORWARD, jmp); + /* important. if the load fails, we only want to step on ONCE to + try the next file, not another jmp */ + change = SLIDE_NEXT; + break; + case SLIDE_JUMP_BACK: + if (filelist_len < 5) + jmp = 1; + else if (filelist_len < 40) + jmp = 2; + else + jmp = filelist_len / 20; + if (!jmp) + jmp = 2; + current_file = feh_list_jump(filelist, current_file, BACK, jmp); + /* important. if the load fails, we only want to step back ONCE to + try the previous file, not another jmp */ + change = SLIDE_NEXT; + break; + default: + eprintf("BUG!\n"); + break; + } + + if (last) + { + filelist = feh_file_remove_from_list(filelist, last); + filelist_len--; + last = NULL; + } + s = slideshow_create_name(FEH_FILE(current_file->data)); + + winwidget_rename(winwid, s); + free(s); + + if ((winwidget_loadimage(winwid, FEH_FILE(current_file->data))) != 0) + { + success = 1; + winwid->mode = MODE_NORMAL; + winwid->file = current_file; + if ((winwid->im_w != gib_imlib_image_get_width(winwid->im)) + || (winwid->im_h != gib_imlib_image_get_height(winwid->im))) + winwid->had_resize = 1; + winwidget_reset_image(winwid); + winwid->im_w = gib_imlib_image_get_width(winwid->im); + winwid->im_h = gib_imlib_image_get_height(winwid->im); + winwidget_render_image(winwid, 1, 1); + break; + } + else + last = current_file; + } + if (!success) + { + /* We didn't manage to load any files. Maybe the last one in the show + was deleted? */ + eprintf("No more slides in show"); + } + if (opt.slideshow_delay >= 0.0) + feh_add_timer(cb_slide_timer, winwid, opt.slideshow_delay, + "SLIDE_CHANGE"); + D_RETURN_(4); +} + +void +slideshow_pause_toggle(winwidget w) +{ + char *title, *new_title; + int len; + + if (!opt.paused) + { + opt.paused = 1; + + len = strlen(w->name) + sizeof(" [Paused]") + 1; + new_title = emalloc(len); + snprintf(new_title, len, "%s [Paused]", w->name); + title = estrdup(w->name); + winwidget_rename(w, new_title); + } + else + { + opt.paused = 0; + } +} + +char * +slideshow_create_name(feh_file * file) +{ + char *s = NULL; + int len = 0; + + D_ENTER(4); + if (!opt.title) + { + len = + strlen(PACKAGE " [slideshow mode] - ") + strlen(file->filename) + 1; + s = emalloc(len); + snprintf(s, len, PACKAGE " [%d of %d] - %s", + gib_list_num(filelist, current_file) + 1, + gib_list_length(filelist), file->filename); + } + else + { + s = estrdup(feh_printf(opt.title, file)); + } + + D_RETURN(4, s); +} + +void +feh_action_run(feh_file * file, char *action) +{ + D_ENTER(4); + if (action) + { + D(3, ("Running action %s\n", action)); + char *sys; + sys = feh_printf(action, file); + + if (opt.verbose && !opt.list && !opt.customlist) + fprintf(stderr, "Running action -->%s<--\n", sys); + system(sys); + } + D_RETURN_(4); +} + +char * +feh_printf(char *str, feh_file * file) +{ + char *c; + char buf[20]; + static char ret[4096]; + + D_ENTER(4); + + ret[0] = '\0'; + + for (c = str; *c != '\0'; c++) + { + if (*c == '%') + { + c++; + switch (*c) + { + case 'f': + if (file) + strcat(ret, file->filename); + break; + case 'n': + if (file) + strcat(ret, file->name); + break; + case 'w': + if (file) + { + if (!file->info) + feh_file_info_load(file, NULL); + snprintf(buf, sizeof(buf), "%d", file->info->width); + strcat(ret, buf); + } + break; + case 'h': + if (file) + { + if (!file->info) + feh_file_info_load(file, NULL); + snprintf(buf, sizeof(buf), "%d", file->info->height); + strcat(ret, buf); + } + break; + case 's': + if (file) + { + if (!file->info) + feh_file_info_load(file, NULL); + snprintf(buf, sizeof(buf), "%d", file->info->size); + strcat(ret, buf); + } + break; + case 'p': + if (file) + { + if (!file->info) + feh_file_info_load(file, NULL); + snprintf(buf, sizeof(buf), "%d", file->info->pixels); + strcat(ret, buf); + } + break; + case 't': + if (file) + { + if (!file->info) + feh_file_info_load(file, NULL); + strcat(ret, file->info->format); + } + break; + case 'P': + strcat(ret, PACKAGE); + break; + case 'v': + strcat(ret, VERSION); + break; + case 'm': + strcat(ret, mode); + break; + case 'l': + snprintf(buf, sizeof(buf), "%d", gib_list_length(filelist)); + strcat(ret, buf); + break; + case 'u': + snprintf(buf, sizeof(buf), "%d", + current_file != NULL ? gib_list_num(filelist, + current_file) + + 1 : 0); + strcat(ret, buf); + break; + default: + strncat(ret, c, 1); + break; + } + } + else if (*c == '\\') + { + c++; + switch (*c) + { + case 'n': + strcat(ret, "\n"); + break; + default: + strncat(ret, c, 1); + break; + } + } + else + strncat(ret, c, 1); + } + D_RETURN(4, ret); +} + +void +feh_filelist_image_remove(winwidget winwid, char do_delete) +{ + if (winwid->type == WIN_TYPE_SLIDESHOW) + { + char *s; + gib_list *doomed; + + doomed = current_file; + slideshow_change_image(winwid, SLIDE_NEXT); + filelist_len--; + if (do_delete) + filelist = feh_file_rm_and_free(filelist, doomed); + else + filelist = feh_file_remove_from_list(filelist, doomed); + if (!filelist) + { + /* No more images. Game over ;-) */ + winwidget_destroy(winwid); + return; + } + s = slideshow_create_name(FEH_FILE(winwid->file->data)); + winwidget_rename(winwid, s); + free(s); + } + else if ((winwid->type == WIN_TYPE_SINGLE) + || (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER)) + { + filelist_len--; + if (do_delete) + filelist = feh_file_rm_and_free(filelist, winwid->file); + else + filelist = feh_file_remove_from_list(filelist, winwid->file); + winwidget_destroy(winwid); + } +} + +void slideshow_save_image(winwidget win) +{ + char *tmpname; + + D_ENTER(4); + if(win->file) { + tmpname = feh_unique_filename("", FEH_FILE(win->file->data)->name); + } else if(mode) { + char *tmp; + tmp = estrjoin(".", mode, "png", NULL); + tmpname = feh_unique_filename("", tmp); + free(tmp); + } else { + tmpname = feh_unique_filename("", "noname.png"); + } + + if(!opt.quiet) + printf("saving image to filename '%s'\n", tmpname); + + gib_imlib_save_image(win->im, tmpname); + free(tmpname); + D_RETURN_(4); +} + +gib_list * +feh_list_jump(gib_list * root, gib_list * l, int direction, int num) +{ + int i; + gib_list *ret = NULL; + + if (!root) + return (NULL); + if (!l) + return (root); + + ret = l; + + for (i = 0; i < num; i++) + { + if (direction == FORWARD) + { + if (ret->next) + { + ret = ret->next; + } + else + { + if (opt.cycle_once) + { + exit(0); + } + ret = root; + } + } + else + { + if (ret->prev) + ret = ret->prev; + else + ret = gib_list_last(ret); + } + } + return (ret); +} + diff --git a/src/stamp-h.in b/src/stamp-h.in new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/src/stamp-h.in @@ -0,0 +1 @@ +timestamp diff --git a/src/structs.h b/src/structs.h new file mode 100644 index 0000000..a2d3527 --- /dev/null +++ b/src/structs.h @@ -0,0 +1,37 @@ +/* structs.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef STRUCTS_H +#define STRUCTS_H + +typedef struct __fehtimer _fehtimer; +typedef _fehtimer *fehtimer; +typedef struct __feh_file feh_file; +typedef struct __feh_file_info feh_file_info; +typedef struct __winwidget _winwidget; +typedef _winwidget *winwidget; +typedef struct __fehoptions fehoptions; + +#endif diff --git a/src/support.c b/src/support.c new file mode 100644 index 0000000..7f38f27 --- /dev/null +++ b/src/support.c @@ -0,0 +1,658 @@ +/* support.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "options.h" +#include "support.h" +Window ipc_win = None; +Window my_ipc_win = None; +Atom ipc_atom = None; +static unsigned char timeout = 0; + +/* + * This is a boolean indicating + * That while we seem to see E16 IPC + * it's actually E17 faking it + * -- richlowe 2005-06-22 + */ +static char e17_fake_ipc = 0; + +void +feh_wm_set_bg_file(char *file, unsigned char bgmode) +{ + Imlib_Image im; + feh_file *fil; + + fil = feh_file_new(file); + if (fil) + { + if (feh_load_image(&im, fil) == 0) + eprintf("Couldn't load image in order to set bg"); + switch (bgmode) + { + case BG_MODE_SEAMLESS: + gib_imlib_image_tile(im); + feh_wm_set_bg(NULL, im, 0, 0, 0, 1); + break; + case BG_MODE_TILE: + feh_wm_set_bg(file, im, 0, 0, 0, 1); + break; + case BG_MODE_SCALE: + feh_wm_set_bg(file, im, 0, 1, 0, 1); + break; + default: + feh_wm_set_bg(file, im, 1, 0, 0, 1); + break; + } + gib_imlib_free_image_and_decache(im); + feh_file_free(fil); + } +} + +void +feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled, + int desktop, int set) +{ + char bgname[20]; + int num = (int) rand(); + char bgfil[4096]; + char sendbuf[4096]; + + D_ENTER(4); + + snprintf(bgname, sizeof(bgname), "FEHBG_%d", num); + + if (!fil) + { + snprintf(bgfil, sizeof(bgfil), "%s/.%s.png", getenv("HOME"), bgname); + imlib_context_set_image(im); + imlib_image_set_format("png"); + gib_imlib_save_image(im, bgfil); + D(3, ("bg saved as %s\n", bgfil)); + fil = bgfil; + } + D(3, ("Setting bg %s\n", fil)); + + if (feh_wm_get_wm_is_e() && (enl_ipc_get_win() != None)) + { + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.file %s", bgname, + fil); + enl_ipc_send(sendbuf); + + if (scaled) + { + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.solid 0 0 0", + bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.tile 0", + bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.xjust 512", + bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.yjust 512", + bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.xperc 1024", + bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.yperc 1024", + bgname); + enl_ipc_send(sendbuf); + } + else if (centered) + { + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.solid 0 0 0", + bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.tile 0", + bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.xjust 512", + bgname); + enl_ipc_send(sendbuf); + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.yjust 512", + bgname); + enl_ipc_send(sendbuf); + } + else + { + snprintf(sendbuf, sizeof(sendbuf), "background %s bg.tile 1", + bgname); + enl_ipc_send(sendbuf); + } + + if (set) + { + snprintf(sendbuf, sizeof(sendbuf), "use_bg %s %d", bgname, desktop); + enl_ipc_send(sendbuf); + } + enl_ipc_sync(); + } + else + { + Atom prop_root, prop_esetroot, type; + int format; + unsigned long length, after; + unsigned char *data_root, *data_esetroot; + Pixmap pmap_d1, pmap_d2; + /* string for sticking in ~/.fehbg */ + char *fehbg = NULL; + char *home; + + /* local display to set closedownmode on */ + Display *disp2; + Window root2; + int depth2; + XGCValues gcvalues; + GC gc; + int w, h; + + D(3, ("Falling back to XSetRootWindowPixmap\n")); + + if (scaled) { + w = scr->width; + h = scr->height; + +/* disable xinerama check for setting background */ +#if 0 +/* #ifdef HAVE_LIBXINERAMA */ + if (opt.xinerama && xinerama_screens) { + w = xinerama_screens[xinerama_screen].width; + h = xinerama_screens[xinerama_screen].height; + } +#endif /* HAVE_LIBXINERAMA */ + + pmap_d1 = XCreatePixmap(disp, root, w, h, depth); + gib_imlib_render_image_on_drawable_at_size(pmap_d1, im, 0, 0, w, h, + 1, 0, 1); + fehbg = estrjoin(" ", "feh --bg-scale", fil, NULL); + } else if (centered) { + XGCValues gcval; + GC gc; + int x, y; + + D(3, ("centering\n")); + w = scr->width; + h = scr->height; + +/* disable xinerama check for setting background */ +#if 0 +/* #ifdef HAVE_LIBXINERAMA */ + if (opt.xinerama && xinerama_screens) { + w = xinerama_screens[xinerama_screen].width; + h = xinerama_screens[xinerama_screen].height; + } +#endif /* HAVE_LIBXINERAMA */ + + pmap_d1 = XCreatePixmap(disp, root, w, h, depth); + gcval.foreground = BlackPixel(disp, DefaultScreen(disp)); + gc = XCreateGC(disp, root, GCForeground, &gcval); + XFillRectangle(disp, pmap_d1, gc, 0, 0, w, h); + x = (w - gib_imlib_image_get_width(im)) >> 1; + y = (h - gib_imlib_image_get_height(im)) >> 1; + gib_imlib_render_image_on_drawable(pmap_d1, im, x, y, 1, 0, 0); + XFreeGC(disp, gc); + fehbg = estrjoin(" ", "feh --bg-center", fil, NULL); + } else { + w = gib_imlib_image_get_width(im); + h = gib_imlib_image_get_height(im); + pmap_d1 = + XCreatePixmap(disp, root, w, h, depth); + gib_imlib_render_image_on_drawable(pmap_d1, im, 0, 0, 1, 0, 0); + fehbg = estrjoin(" ", "feh --bg-tile", fil, NULL); + } + + if (fehbg) { + home = getenv("HOME"); + if(home) { + FILE *fp; + char *path; + path = estrjoin("/", home, ".fehbg", NULL); + if ((fp = fopen(path, "w")) == NULL) { + wprintf("Can't open %s for write", path); + } else { + fprintf(fp, "%s\n", fehbg); + fclose(fp); + } + free(path); + } + free(fehbg); + } + + /* create new display, copy pixmap to new display */ + disp2 = XOpenDisplay(NULL); + if (!disp2) + eprintf("Can't reopen X display."); + root2 = RootWindow(disp2, DefaultScreen(disp2)); + depth2 = DefaultDepth(disp2, DefaultScreen(disp2)); + XSync(disp, False); + pmap_d2 = XCreatePixmap(disp2, root2, scr->width, scr->height, depth2); + gcvalues.fill_style = FillTiled; + gcvalues.tile = pmap_d1; + gc = XCreateGC(disp2, pmap_d2, GCFillStyle|GCTile, &gcvalues); + XFillRectangle(disp2, pmap_d2, gc, 0, 0, scr->width, scr->height); + XFreeGC(disp2, gc); + XSync(disp2, False); + XSync(disp, False); + XFreePixmap(disp, pmap_d1); + + prop_root = XInternAtom(disp2, "_XROOTPMAP_ID", True); + prop_esetroot = XInternAtom(disp2, "ESETROOT_PMAP_ID", True); + + if (prop_root != None && prop_esetroot != None) + { + XGetWindowProperty(disp2, root2, prop_root, 0L, 1L, False, + AnyPropertyType, &type, &format, &length, &after, + &data_root); + if (type == XA_PIXMAP) + { + XGetWindowProperty(disp2, root2, prop_esetroot, 0L, 1L, False, + AnyPropertyType, &type, &format, &length, + &after, &data_esetroot); + if (data_root && data_esetroot) + { + if (type == XA_PIXMAP + && *((Pixmap *) data_root) == *((Pixmap *) data_esetroot)) + { + XKillClient(disp2, *((Pixmap *) data_root)); + } + } + } + } + /* This will locate the property, creating it if it doesn't exist */ + prop_root = XInternAtom(disp2, "_XROOTPMAP_ID", False); + prop_esetroot = XInternAtom(disp2, "ESETROOT_PMAP_ID", False); + + if (prop_root == None || prop_esetroot == None) + weprintf("creation of pixmap property failed."); + + XChangeProperty(disp2, root2, prop_root, XA_PIXMAP, 32, PropModeReplace, + (unsigned char *) &pmap_d2, 1); + XChangeProperty(disp2, root2, prop_esetroot, XA_PIXMAP, 32, + PropModeReplace, (unsigned char *) &pmap_d2, 1); + + XSetWindowBackgroundPixmap(disp2, root2, pmap_d2); + XClearWindow(disp2, root2); + XFlush(disp2); + XSetCloseDownMode(disp2, RetainPermanent); + XCloseDisplay(disp2); + } + D_RETURN_(4); +} + +signed char +feh_wm_get_wm_is_e(void) +{ + static signed char e = -1; + + D_ENTER(4); + + /* check if E is actually running */ + if (e == -1) + { + /* XXX: This only covers E17 prior to 6/22/05 */ + if ((XInternAtom(disp, "ENLIGHTENMENT_COMMS", True) != None) && + (XInternAtom(disp, "ENLIGHTENMENT_VERSION", True) != None)) + { + D(3, ("Enlightenment detected.\n")); + e = 1; + } + else + { + D(3, ("Enlightenment not detected.\n")); + e = 0; + } + } + D_RETURN(4, e); +} + +int +feh_wm_get_num_desks(void) +{ + char *buf, *ptr; + int desks; + + D_ENTER(4); + + if (!feh_wm_get_wm_is_e()) + D_RETURN(4, -1); + + buf = enl_send_and_wait("num_desks ?"); + if (buf == IPC_FAKE) /* Fake E17 IPC */ + D_RETURN(4, -1); + D(3, ("Got from E IPC: %s\n", buf)); + ptr = buf; + while (ptr && !isdigit(*ptr)) + ptr++; + desks = atoi(ptr); + +D_RETURN(4, desks)} + +Window +enl_ipc_get_win(void) +{ + + unsigned char *str = NULL; + Atom prop, prop2, ever; + unsigned long num, after; + int format; + Window dummy_win; + int dummy_int; + unsigned int dummy_uint; + + D_ENTER(4); + + D(3, ("Searching for IPC window.\n")); + + /* + * Shortcircuit this entire func + * if we already know it's an e17 fake + */ + if (e17_fake_ipc) + D_RETURN(4, ipc_win) + + prop = XInternAtom(disp, "ENLIGHTENMENT_COMMS", True); + if (prop == None) + { + D(3, ("Enlightenment is not running.\n")); + D_RETURN(4, None); + } else { + /* XXX: This will only work with E17 prior to 6/22/2005 */ + ever = XInternAtom(disp, "ENLIGHTENMENT_VERSION", True); + if (ever == None) { + /* This is an E without ENLIGHTENMENT_VERSION */ + D(3, ("E16 IPC Protocol not supported")); + D_RETURN(4, None); + } + } + XGetWindowProperty(disp, root, prop, 0, 14, False, AnyPropertyType, &prop2, + &format, &num, &after, &str); + if (str) + { + sscanf((char *) str, "%*s %x", (unsigned int *) &ipc_win); + XFree(str); + } + if (ipc_win != None) + { + if (!XGetGeometry + (disp, ipc_win, &dummy_win, &dummy_int, &dummy_int, &dummy_uint, + &dummy_uint, &dummy_uint, &dummy_uint)) + { + D(3, + (" -> IPC Window property is valid, but the window doesn't exist.\n")); + ipc_win = None; + } + str = NULL; + if (ipc_win != None) + { + XGetWindowProperty(disp, ipc_win, prop, 0, 14, False, + AnyPropertyType, &prop2, &format, &num, &after, + &str); + if (str) + { + XFree(str); + } + else + { + D(3, + (" -> IPC Window lacks the proper atom. I can't talk to fake IPC windows....\n")); + ipc_win = None; + } + } + } + if (ipc_win != None) + { + + XGetWindowProperty(disp, ipc_win, ever, 0, 14, False, AnyPropertyType, + &prop2, &format, &num, &after, &str); + if (str) { + /* + * This is E17's way of telling us it's only pretending + * as a workaround for a bug related to the way java handles + * Window Managers. + * (Only valid after date of this comment) + * -- richlowe 2005-06-22 + */ + XFree(str); + D(3,(" -> Found a fake E17 IPC window, ignoring")); + ipc_win = None; + e17_fake_ipc = 1; + D_RETURN(4, ipc_win); + } + + D(3, + (" -> IPC Window found and verified as 0x%08x. Registering feh as an IPC client.\n", + (int) ipc_win)); + XSelectInput(disp, ipc_win, + StructureNotifyMask | SubstructureNotifyMask); + enl_ipc_send("set clientname " PACKAGE); + enl_ipc_send("set version " VERSION); + enl_ipc_send("set email tom@linuxbrit.co.uk"); + enl_ipc_send("set web http://www.linuxbrit.co.uk"); + enl_ipc_send("set info Feh - be pr0n or be dead"); + } + if (my_ipc_win == None) + { + my_ipc_win = XCreateSimpleWindow(disp, root, -2, -2, 1, 1, 0, 0, 0); + } + D_RETURN(4, ipc_win); +} + +void +enl_ipc_send(char *str) +{ + + static char *last_msg = NULL; + char buff[21]; + register unsigned short i; + register unsigned char j; + unsigned short len; + XEvent ev; + + D_ENTER(4); + if (str == NULL) + { + if (last_msg == NULL) + eprintf("eeek"); + str = last_msg; + D(4, ("Resending last message \"%s\" to Enlightenment.\n", str)); + } + else + { + if (last_msg != NULL) + { + free(last_msg); + } + last_msg = estrdup(str); + D(4, ("Sending \"%s\" to Enlightenment.\n", str)); + } + if (ipc_win == None) + { + if ((ipc_win = enl_ipc_get_win()) == None) + { + D(3, + ("Hrm. Enlightenment doesn't seem to be running. No IPC window, no IPC.\n")); + D_RETURN_(4); + } + } + len = strlen(str); + ipc_atom = XInternAtom(disp, "ENL_MSG", False); + if (ipc_atom == None) + { + D(3, ("IPC error: Unable to find/create ENL_MSG atom.\n")); + D_RETURN_(4); + } + for (; XCheckTypedWindowEvent(disp, my_ipc_win, ClientMessage, &ev);); /* Discard any out-of-sync messages */ + ev.xclient.type = ClientMessage; + ev.xclient.serial = 0; + ev.xclient.send_event = True; + ev.xclient.window = ipc_win; + ev.xclient.message_type = ipc_atom; + ev.xclient.format = 8; + + for (i = 0; i < len + 1; i += 12) + { + sprintf(buff, "%8x", (int) my_ipc_win); + for (j = 0; j < 12; j++) + { + buff[8 + j] = str[i + j]; + if (!str[i + j]) + { + break; + } + } + buff[20] = 0; + for (j = 0; j < 20; j++) + { + ev.xclient.data.b[j] = buff[j]; + } + XSendEvent(disp, ipc_win, False, 0, (XEvent *) & ev); + } + D_RETURN_(4); +} + +static sighandler_t * +enl_ipc_timeout(int sig) +{ + timeout = 1; + D_RETURN(4, (sighandler_t *) sig); + sig = 0; +} + +char * +enl_wait_for_reply(void) +{ + + XEvent ev; + static char msg_buffer[20]; + register unsigned char i; + + D_ENTER(4); + + alarm(2); + for (; + !XCheckTypedWindowEvent(disp, my_ipc_win, ClientMessage, &ev) + && !timeout;); + alarm(0); + if (ev.xany.type != ClientMessage) + { + D_RETURN(4, IPC_TIMEOUT); + } + for (i = 0; i < 20; i++) + { + msg_buffer[i] = ev.xclient.data.b[i]; + } + D_RETURN(4, msg_buffer + 8); +} + +char * +enl_ipc_get(const char *msg_data) +{ + + static char *message = NULL; + static unsigned short len = 0; + char buff[13], *ret_msg = NULL; + register unsigned char i; + unsigned char blen; + + D_ENTER(4); + + if (msg_data == IPC_TIMEOUT) + { + D_RETURN(4, IPC_TIMEOUT); + } + for (i = 0; i < 12; i++) + { + buff[i] = msg_data[i]; + } + buff[12] = 0; + blen = strlen(buff); + if (message != NULL) + { + len += blen; + message = (char *) erealloc(message, len + 1); + strcat(message, buff); + } + else + { + len = blen; + message = (char *) emalloc(len + 1); + strcpy(message, buff); + } + if (blen < 12) + { + ret_msg = message; + message = NULL; + D(4, ("Received complete reply: \"%s\"\n", ret_msg)); + } + D_RETURN(4, ret_msg); +} + +char * +enl_send_and_wait(char *msg) +{ + char *reply = IPC_TIMEOUT; + sighandler_t old_alrm; + + D_ENTER(4); + + /* + * Shortcut this func and return IPC_FAKE + * If the IPC Window is the E17 fake + */ + if (e17_fake_ipc) + return IPC_FAKE; + + if (ipc_win == None) + { + /* The IPC window is missing. Wait for it to return or feh to be killed. */ + /* Only called once in the E17 case */ + for (; enl_ipc_get_win() == None;) + { + if (e17_fake_ipc) + return IPC_FAKE; + else + sleep(1); + } + } + old_alrm = (sighandler_t) signal(SIGALRM, (sighandler_t) enl_ipc_timeout); + for (; reply == IPC_TIMEOUT;) + { + timeout = 0; + enl_ipc_send(msg); + for (; !(reply = enl_ipc_get(enl_wait_for_reply()));); + if (reply == IPC_TIMEOUT) + { + /* We timed out. The IPC window must be AWOL. Reset and resend message. */ + D(3, ("IPC timed out. IPC window has gone. Clearing ipc_win.\n")); + XSelectInput(disp, ipc_win, None); + ipc_win = None; + } + } + signal(SIGALRM, old_alrm); + D_RETURN(4, reply); +} diff --git a/src/support.h b/src/support.h new file mode 100644 index 0000000..a8f81a1 --- /dev/null +++ b/src/support.h @@ -0,0 +1,56 @@ +/* support.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef SUPPORT_H +#define SUPPORT_H + +#include +#include /* Xlib, Xutil, Xresource, Xfuncproto */ + +#define IPC_TIMEOUT ((char *) 1) +#define IPC_FAKE ((char *) 2) /* Faked IPC */ + +#define enl_ipc_sync() do { \ + char *reply = enl_send_and_wait("nop"); \ + if ((reply != IPC_FAKE) && (reply != IPC_TIMEOUT)) \ + free(reply); \ + } while (0) + +extern Window ipc_win; +extern Atom ipc_atom; + +_XFUNCPROTOBEGIN extern Window enl_ipc_get_win(void); +extern void enl_ipc_send(char *); +extern char *enl_wait_for_reply(void); +extern char *enl_ipc_get(const char *); +extern char *enl_send_and_wait(char *); +extern void feh_wm_set_bg(char *fil, Imlib_Image im, int centered, int scaled, + int desktop, int set); +extern int feh_wm_get_num_desks(void); +extern signed char feh_wm_get_wm_is_e(void); +void feh_wm_set_bg_file(char *file, unsigned char bgmode); + +_XFUNCPROTOEND +#endif diff --git a/src/thumbnail.c b/src/thumbnail.c new file mode 100644 index 0000000..f85e434 --- /dev/null +++ b/src/thumbnail.c @@ -0,0 +1,992 @@ +/* thumbnail.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "winwidget.h" +#include "options.h" +#include "thumbnail.h" +#include "md5.h" +#include "feh_png.h" + +static char *create_index_dimension_string(int w, int h); +static char *create_index_size_string(char *file); +static char *create_index_title_string(int num, int w, int h); +static gib_list *thumbnails = NULL; + +static thumbmode_data td; + +/* TODO Break this up a bit ;) */ +/* TODO s/bit/lot */ +void +init_thumbnail_mode(void) +{ + /* moved to thumbnail_data: + Imlib_Image im_main; + Imlib_Image bg_im = NULL; + Imlib_Font fn = NULL; + Imlib_Font title_fn = NULL; + + int w = 800, h = 600; + int bg_w = 0, bg_h = 0; + + int text_area_w, text_area_h; + int max_column_w = 0; + */ + + + Imlib_Image im_temp; + int ww = 0, hh = 0, www, hhh, xxx, yyy; + int x = 0, y = 0; + winwidget winwid = NULL; + Imlib_Image im_thumb = NULL; + unsigned char trans_bg = 0; + int title_area_h = 0; + int tw = 0, th = 0; + int fw_name, fw_size, fw_dim, fh; + int thumbnailcount = 0; + feh_file *file = NULL; + gib_list *l, *last = NULL; + int lines; + int index_image_width, index_image_height; + int x_offset_name = 0, x_offset_dim = 0, x_offset_size = 0; + char *s; + + /* initialize thumbnail mode data */ + td.im_main = NULL; + td.im_bg = NULL; + td.font_main = NULL; + td.font_title = NULL; + + td.w = 640; + td.h = 480; + td.bg_w = 0; + td.bg_h = 0; + td.thumb_tot_h = 0; + td.text_area_w = 0; + td.text_area_h = 0; + + td.vertical = 0; + td.max_column_w = 0; + + D_ENTER(3); + + mode = "thumbnail"; + + td.font_main = gib_imlib_load_font(opt.font); + + if (opt.title_font) + { + int fh, fw; + + td.font_title = gib_imlib_load_font(opt.title_font); + gib_imlib_get_text_size(td.font_title, "W", NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + title_area_h = fh + 4; + } + else + td.font_title = imlib_load_font(DEFAULT_FONT_TITLE); + + if ((!td.font_main) || (!td.font_title)) + eprintf("Error loading fonts"); + + /* Work out how tall the font is */ + gib_imlib_get_text_size(td.font_main, "W", NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); + /* For now, allow room for the right number of lines with small gaps */ + td.text_area_h = + ((th + 2) * (opt.index_show_name + opt.index_show_size + + opt.index_show_dim)) + 5; + + /* This includes the text area for index data */ + td.thumb_tot_h = opt.thumb_h + td.text_area_h; + + /* Use bg image dimensions for default size */ + if (opt.bg && opt.bg_file) + { + if (!strcmp(opt.bg_file, "trans")) + trans_bg = 1; + else + { + + D(3, ("Time to apply a background to blend onto\n")); + if (feh_load_image_char(&td.im_bg, opt.bg_file) != 0) + { + td.bg_w = gib_imlib_image_get_width(td.im_bg); + td.bg_h = gib_imlib_image_get_height(td.im_bg); + } + } + } + + /* figure out geometry for the main window and entries */ + feh_thumbnail_calculate_geometry(); + + index_image_width = td.w; + index_image_height = td.h + title_area_h; + td.im_main = imlib_create_image(index_image_width, index_image_height); + + if (!td.im_main) + eprintf("Imlib error creating index image, are you low on RAM?"); + + if (td.im_bg) + gib_imlib_blend_image_onto_image(td.im_main, td.im_bg, + gib_imlib_image_has_alpha(td.im_bg), 0, 0, + td.bg_w, td.bg_h, 0, 0, td.w, td.h, 1, 0, 0); + else if (trans_bg) + { + gib_imlib_image_fill_rectangle(td.im_main, 0, 0, td.w, td.h + title_area_h, 0, 0, + 0, 0); + gib_imlib_image_set_has_alpha(td.im_main, 1); + } + else + { + /* Colour the background */ + gib_imlib_image_fill_rectangle(td.im_main, 0, 0, td.w, td.h + title_area_h, 0, 0, + 0, 255); + } + + /* Create title now */ + + if (!opt.title) + s = estrdup(PACKAGE " [thumbnail mode]"); + else + s = estrdup(feh_printf(opt.title, NULL)); + + if (opt.display) + { + winwid = winwidget_create_from_image(td.im_main, s, WIN_TYPE_THUMBNAIL); + winwidget_show(winwid); + } + + /* make sure we have an ~/.thumbnails/normal directory for storing + permanent thumbnails */ + td.cache_thumbnails = feh_thumbnail_setup_thumbnail_dir(); + td.cache_thumbnails = opt.cache_thumbnails; + + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + if (last) + { + filelist = feh_file_remove_from_list(filelist, last); + filelist_len--; + last = NULL; + } + D(4, ("About to load image %s\n", file->filename)); + /* if (feh_load_image(&im_temp, file) != 0) */ + if (feh_thumbnail_get_thumbnail(&im_temp, file) != 0) + { + if (opt.verbose) + feh_display_status('.'); + D(4, ("Successfully loaded %s\n", file->filename)); + www = opt.thumb_w; + hhh = opt.thumb_h; + ww = gib_imlib_image_get_width(im_temp); + hh = gib_imlib_image_get_height(im_temp); + thumbnailcount++; + if (gib_imlib_image_has_alpha(im_temp)) + imlib_context_set_blend(1); + else + imlib_context_set_blend(0); + + if (opt.aspect) + { + double ratio = 0.0; + + /* Keep the aspect ratio for the thumbnail */ + ratio = ((double) ww / hh) / ((double) www / hhh); + + if (ratio > 1.0) + hhh = opt.thumb_h / ratio; + else if (ratio != 1.0) + www = opt.thumb_w * ratio; + } + + if ((!opt.stretch) && ((www > ww) || (hhh > hh))) + { + /* Don't make the image larger unless stretch is specified */ + www = ww; + hhh = hh; + } + + im_thumb = + gib_imlib_create_cropped_scaled_image(im_temp, 0, 0, ww, hh, www, + hhh, 1); + gib_imlib_free_image_and_decache(im_temp); + + if (opt.alpha) + { + DATA8 atab[256]; + + D(3, ("Applying alpha options\n")); + gib_imlib_image_set_has_alpha(im_thumb, 1); + memset(atab, opt.alpha_level, sizeof(atab)); + gib_imlib_apply_color_modifier_to_rectangle(im_thumb, 0, 0, www, + hhh, NULL, NULL, NULL, + atab); + } + + td.text_area_w = opt.thumb_w; + /* Now draw on the info text */ + if (opt.index_show_name) + { + gib_imlib_get_text_size(td.font_main, file->name, NULL, &fw_name, &fh, + IMLIB_TEXT_TO_RIGHT); + if (fw_name > td.text_area_w) + td.text_area_w = fw_name; + } + if (opt.index_show_dim) + { + gib_imlib_get_text_size(td.font_main, create_index_dimension_string(ww, hh), + NULL, &fw_dim, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw_dim > td.text_area_w) + td.text_area_w = fw_dim; + } + if (opt.index_show_size) + { + gib_imlib_get_text_size(td.font_main, + create_index_size_string(file->filename), + NULL, &fw_size, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw_size > td.text_area_w) + td.text_area_w = fw_size; + } + if (td.text_area_w > opt.thumb_w) + td.text_area_w += 5; + + /* offsets for centering text */ + x_offset_name = (td.text_area_w - fw_name) / 2; + x_offset_dim = (td.text_area_w - fw_dim) / 2; + x_offset_size = (td.text_area_w - fw_size) / 2; + + if (td.vertical) + { + if (td.text_area_w > td.max_column_w) + td.max_column_w = td.text_area_w; + if (y > td.h - td.thumb_tot_h) + { + y = 0; + x += td.max_column_w; + td.max_column_w = 0; + } + if (x > td.w - td.text_area_w) + break; + } + else + { + if (x > td.w - td.text_area_w) + { + x = 0; + y += td.thumb_tot_h; + } + if (y > td.h - td.thumb_tot_h) + break; + } + + if (opt.aspect) + { + xxx = x + ((opt.thumb_w - www) / 2); + yyy = y + ((opt.thumb_h - hhh) / 2); + } + else + { + /* Ignore the aspect ratio and squash the image in */ + xxx = x; + yyy = y; + } + + /* Draw now */ + gib_imlib_blend_image_onto_image(td.im_main, im_thumb, + gib_imlib_image_has_alpha(im_thumb), + 0, 0, www, hhh, xxx, yyy, www, hhh, + 1, + gib_imlib_image_has_alpha(im_thumb), + 0); + + thumbnails = + gib_list_add_front(thumbnails, + feh_thumbnail_new(file, xxx, yyy, www, hhh)); + + gib_imlib_free_image_and_decache(im_thumb); + + lines = 0; + if (opt.index_show_name) + gib_imlib_text_draw(td.im_main, td.font_main, NULL, x + x_offset_name, + y + opt.thumb_h + (lines++ * (th + 2)) + 2, + file->name, IMLIB_TEXT_TO_RIGHT, 255, 255, + 255, 255); + if (opt.index_show_dim) + gib_imlib_text_draw(td.im_main, td.font_main, NULL, x + x_offset_dim, + y + opt.thumb_h + (lines++ * (th + 2)) + 2, + create_index_dimension_string(ww, hh), + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + if (opt.index_show_size) + gib_imlib_text_draw(td.im_main, td.font_main, NULL, x + x_offset_size, + y + opt.thumb_h + (lines++ * (th + 2)) + 2, + create_index_size_string(file->filename), + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + + if (td.vertical) + y += td.thumb_tot_h; + else + x += td.text_area_w; + } + else + { + if (opt.verbose) + feh_display_status('x'); + last = l; + } + if (opt.display) + { + winwidget_render_image(winwid, 0, 0); + if (!feh_main_iteration(0)) + exit(0); + } + } + if (opt.verbose) + fprintf(stdout, "\n"); + + if (opt.title_font) + { + int fw, fh, fx, fy; + char *s; + + s = create_index_title_string(thumbnailcount, td.w, td.h); + gib_imlib_get_text_size(td.font_title, s, NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + fx = (index_image_width - fw) >> 1; + fy = index_image_height - fh - 2; + gib_imlib_text_draw(td.im_main, td.font_title, NULL, fx, fy, s, IMLIB_TEXT_TO_RIGHT, + 255, 255, 255, 255); + } + + if (opt.output && opt.output_file) + { + char output_buf[1024]; + + if (opt.output_dir) + snprintf(output_buf, 1024, "%s/%s", opt.output_dir, opt.output_file); + else + strncpy(output_buf, opt.output_file, 1024); + gib_imlib_save_image(td.im_main, output_buf); + if (opt.verbose) + { + int tw, th; + + tw = gib_imlib_image_get_width(td.im_main); + th = gib_imlib_image_get_height(td.im_main); + fprintf(stdout, PACKAGE " - File saved as %s\n", output_buf); + fprintf(stdout, + " - Image is %dx%d pixels and contains %d thumbnails\n", + tw, th, thumbnailcount); + } + } + + if (!opt.display) + gib_imlib_free_image_and_decache(td.im_main); + + free(s); + D_RETURN_(3); +} + + +static char * +create_index_size_string(char *file) +{ + static char str[50]; + int size = 0; + double kbs = 0.0; + struct stat st; + + D_ENTER(4); + if (stat(file, &st)) + kbs = 0.0; + else + { + size = st.st_size; + kbs = (double) size / 1000; + } + + snprintf(str, sizeof(str), "%.2fKb", kbs); + D_RETURN(4, str); +} + +static char * +create_index_dimension_string(int w, int h) +{ + static char str[50]; + + D_ENTER(4); + snprintf(str, sizeof(str), "%dx%d", w, h); + D_RETURN(4, str); +} + +static char * +create_index_title_string(int num, int w, int h) +{ + static char str[50]; + + D_ENTER(4); + snprintf(str, sizeof(str), + PACKAGE " index - %d thumbnails, %d by %d pixels", num, w, h); + D_RETURN(4, str); +} + +feh_thumbnail * +feh_thumbnail_new(feh_file * file, int x, int y, int w, int h) +{ + feh_thumbnail *thumb; + + D_ENTER(4); + + thumb = (feh_thumbnail *) emalloc(sizeof(feh_thumbnail)); + thumb->x = x; + thumb->y = y; + thumb->w = w; + thumb->h = h; + thumb->file = file; + thumb->exists = 1; + + D_RETURN(4, thumb); +} + +feh_file * +feh_thumbnail_get_file_from_coords(int x, int y) +{ + gib_list *l; + feh_thumbnail *thumb; + + D_ENTER(4); + + for (l = thumbnails; l; l = l->next) + { + thumb = FEH_THUMB(l->data); + if (XY_IN_RECT(x, y, thumb->x, thumb->y, thumb->w, thumb->h)) + { + if (thumb->exists) + { + D_RETURN(4, thumb->file); + } + } + } + D(4, ("No matching %d %d\n", x, y)); + D_RETURN(4, NULL); +} + +feh_thumbnail * +feh_thumbnail_get_thumbnail_from_coords(int x, int y) +{ + gib_list *l; + feh_thumbnail *thumb; + + D_ENTER(4); + + for (l = thumbnails; l; l = l->next) + { + thumb = FEH_THUMB(l->data); + if (XY_IN_RECT(x, y, thumb->x, thumb->y, thumb->w, thumb->h)) + { + if (thumb->exists) + { + D_RETURN(4, thumb); + } + } + } + D(4, ("No matching %d %d\n", x, y)); + D_RETURN(4, NULL); +} + +feh_thumbnail * +feh_thumbnail_get_from_file(feh_file * file) +{ + gib_list *l; + feh_thumbnail *thumb; + + D_ENTER(4); + + for (l = thumbnails; l; l = l->next) + { + thumb = FEH_THUMB(l->data); + if (thumb->file == file) + { + if (thumb->exists) + { + D_RETURN(4, thumb); + } + } + } + D(4, ("No match\n")); + D_RETURN(4, NULL); +} + + +void +feh_thumbnail_mark_removed(feh_file * file, int deleted) +{ + feh_thumbnail *thumb; + winwidget w; + + D_ENTER(4); + + thumb = feh_thumbnail_get_from_file(file); + if (thumb) + { + w = winwidget_get_first_window_of_type(WIN_TYPE_THUMBNAIL); + if (w) + { + td.font_main = imlib_load_font(DEFAULT_FONT_TITLE); + if (deleted) + gib_imlib_image_fill_rectangle(w->im, thumb->x, thumb->y, + thumb->w, thumb->h, 255, 0, 0, + 150); + else + gib_imlib_image_fill_rectangle(w->im, thumb->x, thumb->y, + thumb->w, thumb->h, 0, 0, 255, + 150); + if (td.font_main) + { + int tw, th; + + gib_imlib_get_text_size(td.font_main, "X", NULL, &tw, &th, IMLIB_TEXT_TO_RIGHT); + gib_imlib_text_draw(w->im, td.font_main, NULL, thumb->x + ((thumb->w - tw) / 2), + thumb->y + ((thumb->h - th) / 2), "X", + IMLIB_TEXT_TO_RIGHT, 205, 205, 50, 255); + } + else + weprintf(DEFAULT_FONT_TITLE); + winwidget_render_image(w, 0, 1); + } + thumb->exists = 0; + } + D_RETURN_(4); +} + +void +feh_thumbnail_calculate_geometry(void) +{ + gib_list *l; + feh_file *file; + + int x = 0, y = 0; + int fw, fh; + + if (!opt.limit_w && !opt.limit_h) + { + if (td.im_bg) + { + if (opt.verbose) + fprintf(stdout, + PACKAGE " - No size restriction specified for index.\n" + " You did specify a background however, so the\n" + " index size has defaulted to the size of the image\n"); + opt.limit_w = td.bg_w; + opt.limit_h = td.bg_h; + } + else + { + if (opt.verbose) + fprintf(stdout, + PACKAGE " - No size restriction specified for index.\n" + " Using defaults (width limited to 640)\n"); + opt.limit_w = 640; + } + } + + + /* Here we need to whiz through the files, and look at the filenames and + info in the selected font, work out how much space we need, and + calculate the size of the image we will require */ + + if (opt.limit_w && opt.limit_h) + { + int rec_h = 0; + + td.w = opt.limit_w; + td.h = opt.limit_h; + + /* Work out if this is big enough, and give a warning if not */ + + /* Pretend we are limiting width by that specified, loop through, and + see it we fit in the height specified. If not, continue the loop, + and recommend the final value instead. Carry on and make the index + anyway. */ + + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + td.text_area_w = opt.thumb_w; + if (opt.index_show_name) + { + gib_imlib_get_text_size(td.font_main, file->name, NULL, &fw, &fh, + IMLIB_TEXT_TO_RIGHT); + if (fw > td.text_area_w) + td.text_area_w = fw; + } + if (opt.index_show_dim) + { + gib_imlib_get_text_size(td.font_main, + create_index_dimension_string(1000, 1000), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > td.text_area_w) + td.text_area_w = fw; + } + if (opt.index_show_size) + { + gib_imlib_get_text_size(td.font_main, + create_index_size_string(file->filename), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > td.text_area_w) + td.text_area_w = fw; + } + if (td.text_area_w > opt.thumb_w) + td.text_area_w += 5; + + if ((x > td.w - td.text_area_w)) + { + x = 0; + y += td.thumb_tot_h; + } + + x += td.text_area_w; + } + rec_h = y + td.thumb_tot_h; + + if (td.h < rec_h) + { + weprintf("The image size you specified (%d by %d) is not large\n" + "enough to hold all the thumnails you specified (%d). To fit all\n" + "the thumnails, either decrease their size, choose a smaller font,\n" + "or use a larger image (may I recommend %d by %d?)", + opt.limit_w, opt.limit_h, filelist_len, opt.limit_w, rec_h); + } + } + else if (opt.limit_h) + { + td.vertical = 1; + td.h = opt.limit_h; + /* calc w */ + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + td.text_area_w = opt.thumb_w; + /* Calc width of text */ + if (opt.index_show_name) + { + gib_imlib_get_text_size(td.font_main, file->name, NULL, &fw, &fh, + IMLIB_TEXT_TO_RIGHT); + if (fw > td.text_area_w) + td.text_area_w = fw; + } + if (opt.index_show_dim) + { + gib_imlib_get_text_size(td.font_main, + create_index_dimension_string(1000, 1000), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > td.text_area_w) + td.text_area_w = fw; + } + if (opt.index_show_size) + { + gib_imlib_get_text_size(td.font_main, + create_index_size_string(file->filename), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > td.text_area_w) + td.text_area_w = fw; + } + if (td.text_area_w > opt.thumb_w) + td.text_area_w += 5; + + if (td.text_area_w > td.max_column_w) + td.max_column_w = td.text_area_w; + + if ((y > td.h - td.thumb_tot_h)) + { + y = 0; + x += td.max_column_w; + td.max_column_w = 0; + } + + y += td.thumb_tot_h; + } + td.w = x + td.text_area_w; + td.max_column_w = 0; + } + else if (opt.limit_w) + { + td.w = opt.limit_w; + /* calc h */ + + for (l = filelist; l; l = l->next) + { + file = FEH_FILE(l->data); + td.text_area_w = opt.thumb_w; + if (opt.index_show_name) + { + gib_imlib_get_text_size(td.font_main, file->name, NULL, &fw, &fh, + IMLIB_TEXT_TO_RIGHT); + if (fw > td.text_area_w) + td.text_area_w = fw; + } + if (opt.index_show_dim) + { + gib_imlib_get_text_size(td.font_main, + create_index_dimension_string(1000, 1000), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > td.text_area_w) + td.text_area_w = fw; + } + if (opt.index_show_size) + { + gib_imlib_get_text_size(td.font_main, + create_index_size_string(file->filename), + NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT); + if (fw > td.text_area_w) + td.text_area_w = fw; + } + + if (td.text_area_w > opt.thumb_w) + td.text_area_w += 5; + + if ((x > td.w - td.text_area_w)) + { + x = 0; + y += td.thumb_tot_h; + } + + x += td.text_area_w; + } + td.h = y + td.thumb_tot_h; + } +} + +int +feh_thumbnail_get_thumbnail(Imlib_Image *image, feh_file *file) +{ + int status = 0; + char *thumb_file = NULL, *uri = NULL; + + if (!file || !file->filename) + return(0); + + if (td.cache_thumbnails) + { + uri = feh_thumbnail_get_name_uri(file->filename); + thumb_file = feh_thumbnail_get_name(uri); + status = feh_thumbnail_get_generated(image, file, thumb_file, uri); + + if (!status) + status = feh_thumbnail_generate(image, file, thumb_file, uri); + + printf("uri is %s, thumb_file is %s\n", uri, thumb_file); + free(uri); + free(thumb_file); + } + else + status = feh_load_image(image, file); + + return status; +} + +char* +feh_thumbnail_get_name(char *uri) +{ + char *home = NULL, *thumb_file = NULL, *md5_name = NULL; + + /* FIXME: make sure original file isn't under ~/.thumbnails */ + + + + md5_name = feh_thumbnail_get_name_md5(uri); + + home = getenv("HOME"); + if (home) + { + thumb_file = estrjoin("/", home, ".thumbnails/normal", md5_name, NULL); + } + + free(md5_name); + + return thumb_file; +} + +char* +feh_thumbnail_get_name_uri(char *name) +{ + char *cwd, *uri = NULL; + + /* FIXME: what happends with http, https, and ftp? MTime etc */ + if ((strncmp(name, "http://", 7) != 0) && + (strncmp(name, "https://", 8) != 0) && + (strncmp(name, "ftp://", 6) != 0) && + (strncmp(name, "file://", 7) != 0)) + { + + /* make sure it's an absoulte path */ + /* FIXME: add support for ~, need to investigate if it's expanded + somewhere else before adding (unecessary) code */ + if (name[0] != '/') { + cwd = getcwd(NULL, 0); + uri = estrjoin("/", "file:/", cwd, name, NULL); + free(cwd); + } else { + uri = estrjoin(NULL, "file://", name, NULL); + } + } + else + uri = estrdup(name); + + return uri; +} + +char* +feh_thumbnail_get_name_md5(char *uri) +{ + int i; + char *pos, *md5_name; + md5_state_t pms; + md5_byte_t digest[16]; + + /* generate the md5 sum */ + md5_init(&pms); + md5_append(&pms, uri, strlen(uri)); + md5_finish(&pms, digest); + + /* print the md5 as hex to a string */ + md5_name = emalloc(32 + 4 + 1 * sizeof(char)); /* md5 + .png + '\0' */ + for (i = 0, pos = md5_name; i < 16; i++, pos += 2) + { + sprintf(pos, "%02x", digest[i]); + } + sprintf(pos, ".png"); + + return md5_name; +} + +int +feh_thumbnail_generate(Imlib_Image *image, feh_file *file, char *thumb_file, char *uri) +{ + int w, h, thumb_w, thumb_h; + char *c_mtime; + Imlib_Image im_temp; + struct stat sb; + + if (feh_load_image(&im_temp, file) != 0) + { + w = gib_imlib_image_get_width(im_temp); + h = gib_imlib_image_get_height(im_temp); + thumb_w = 128; + thumb_h = 128; + + if ((w > 128) || (h > 128)) + { + double ratio = (double) w / h; + if (ratio > 1.0) + thumb_h = 128 / ratio; + else if (ratio != 1.0) + thumb_w = 128 * ratio; + } + + *image = gib_imlib_create_cropped_scaled_image(im_temp, 0, 0, w, h, + thumb_w, thumb_h, 1); + + if (!stat(file->filename, &sb)) + { + char c_mtime[256]; + sprintf(c_mtime, "%d", sb.st_mtime); + feh_png_write_png(*image, thumb_file, + "Thumb::URI", uri, "Thumb::MTime", c_mtime); + } + + gib_imlib_free_image_and_decache(im_temp); + + return(1); + } + + return(0); +} + +int +feh_thumbnail_get_generated(Imlib_Image *image, feh_file *file, char *thumb_file, char *uri) +{ + struct stat sb; + char *c_mtime; + time_t mtime = 0; + gib_hash *hash; + + if (!stat(file->filename, &sb)) + { + hash = feh_png_read_comments(thumb_file); + if (hash != NULL) + { + c_mtime = (char*) gib_hash_get(hash, "Thumb::MTime"); + if (c_mtime != NULL) + mtime = (time_t) strtol(c_mtime, NULL,10); + gib_hash_free_and_data(hash); + } + + /* FIXME: should we bother about Thumb::URI? */ + if (mtime == sb.st_mtime) + { + feh_load_image_char(image, thumb_file); + + return(1); + } + } + + return(0); +} + +int +feh_thumbnail_setup_thumbnail_dir(void) +{ + int status = 0; + struct stat sb; + char *dir, *dir_thumbnails, *home; + + home = getenv("HOME"); + if (home != NULL) + { + dir = estrjoin("/", home, ".thumbnails/normal", NULL); + + if (!stat(dir, &sb)) { + if (S_ISDIR(sb.st_mode)) + status = 1; + else + weprintf("%s should be a directory", dir); + } else { + dir_thumbnails = estrjoin("/", home, ".thumbnails", NULL); + + if (stat(dir_thumbnails, &sb) != 0) + { + if (mkdir(dir_thumbnails, 0700) == -1) + weprintf("unable to create %s directory", dir_thumbnails); + } + + if (mkdir(dir, 0700) == -1) + weprintf("unable to create %s directory", dir); + else + status = 1; + } + } + + return status; +} diff --git a/src/thumbnail.h b/src/thumbnail.h new file mode 100644 index 0000000..bc986de --- /dev/null +++ b/src/thumbnail.h @@ -0,0 +1,86 @@ +/* thumbnail.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef THIMBNAIL_H +#define THIMBNAIL_H + + +#include "feh.h" +#include "filelist.h" +#include "winwidget.h" + +#define FEH_THUMB(l) ((feh_thumbnail *) l) + +typedef struct thumbnail +{ + int x; + int y; + int w; + int h; + feh_file *file; + unsigned char exists; + struct feh_thumbnail *next; +} +feh_thumbnail; + +typedef struct thumbmode_data +{ + /* FIXME: text_area_h not really needed, remove? */ + + Imlib_Image im_main; /* base image which all thumbnails are rendered on */ + Imlib_Image im_bg; /* background for the thumbnails */ + + Imlib_Font font_main; /* font used for file info */ + Imlib_Font font_title; /* font used for title */ + + int w, h, bg_w, bg_h; /* dimensions of the window and bg image */ + + int thumb_tot_h; /* total space needed for a thumbnail including description */ + int text_area_w, text_area_h; /* space needed for thumbnail description */ + + int max_column_w; /* FIXME: description */ + int vertical; /* FIXME: vertical in what way? */ + int cache_thumbnails; /* use cached thumbnails from ~/.thumbnails/normal */ +} +thumbmode_data; + +feh_thumbnail *feh_thumbnail_new(feh_file * fil, int x, int y, int w, int h); +feh_file *feh_thumbnail_get_file_from_coords(int x, int y); +feh_thumbnail *feh_thumbnail_get_thumbnail_from_coords(int x, int y); +feh_thumbnail *feh_thumbnail_get_from_file(feh_file * file); +void feh_thumbnail_mark_removed(feh_file * file, int deleted); + +void feh_thumbnail_calculate_geometry(void); + +int feh_thumbnail_get_thumbnail(Imlib_Image *image, feh_file *file); +int feh_thumbnail_generate(Imlib_Image *image, feh_file *file, char *thumb_file, char *uri); +int feh_thumbnail_get_generated(Imlib_Image *image, feh_file *file, char *thumb_file, char *uri); +char *feh_thumbnail_get_name(char *uri); +char *feh_thumbnail_get_name_uri(char *name); +char *feh_thumbnail_get_name_md5(char *uri); + +int feh_thumbnail_setup_thumbnail_dir(void); + +#endif diff --git a/src/timers.c b/src/timers.c new file mode 100644 index 0000000..5db92f0 --- /dev/null +++ b/src/timers.c @@ -0,0 +1,172 @@ +/* timers.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "options.h" +#include "timers.h" + +fehtimer first_timer = NULL; + +void +feh_handle_timer(void) +{ + fehtimer ft; + + D_ENTER(4); + if (!first_timer) + { + D(3,("No timer to handle, returning\n")); + D_RETURN_(4); + } + ft = first_timer; + first_timer = first_timer->next; + D(3,("Executing timer function now\n")); + (*(ft->func)) (ft->data); + D(3,("Freeing the timer\n")); + if (ft && ft->name) + free(ft->name); + if (ft) + free(ft); + D_RETURN_(4); +} + +double +feh_get_time(void) +{ + struct timeval timev; + + D_ENTER(4); + gettimeofday(&timev, NULL); + D_RETURN(4,(double) timev.tv_sec + (((double) timev.tv_usec) / 1000000)); +} + +void +feh_remove_timer(char *name) +{ + fehtimer ft, ptr, pptr; + + D_ENTER(4); + D(3,("removing %s\n", name)); + pptr = NULL; + ptr = first_timer; + while (ptr) + { + D(3,("Stepping through event list\n")); + ft = ptr; + if (!strcmp(ft->name, name)) + { + D(3,("Found it. Removing\n")); + if (pptr) + pptr->next = ft->next; + else + first_timer = ft->next; + if (ft->next) + ft->next->in += ft->in; + if (ft->name) + free(ft->name); + if (ft) + free(ft); + D_RETURN_(4); + } + pptr = ptr; + ptr = ptr->next; + } + D_RETURN_(4); +} + +void +feh_add_timer(void (*func) (void *data), void *data, double in, char *name) +{ + fehtimer ft, ptr, pptr; + double tally; + + D_ENTER(4); + D(3,("adding timer %s for %f seconds time\n", name, in)); + feh_remove_timer(name); + ft = malloc(sizeof(_fehtimer)); + ft->next = NULL; + ft->func = func; + ft->data = data; + ft->name = estrdup(name); + ft->just_added = 1; + ft->in = in; + D(3,("ft->in = %f\n", ft->in)); + tally = 0.0; + if (!first_timer) + { + D(3,("No first timer\n")); + first_timer = ft; + } + else + { + D(3,("There is a first timer\n")); + pptr = NULL; + ptr = first_timer; + tally = 0.0; + while (ptr) + { + tally += ptr->in; + if (tally > in) + { + tally -= ptr->in; + ft->next = ptr; + if (pptr) + pptr->next = ft; + else + first_timer = ft; + ft->in -= tally; + if (ft->next) + ft->next->in -= ft->in; + D_RETURN_(4); + } + pptr = ptr; + ptr = ptr->next; + } + if (pptr) + pptr->next = ft; + else + first_timer = ft; + ft->in -= tally; + } + D(3,("ft->in = %f\n", ft->in)); + D_RETURN_(4); +} + +void +feh_add_unique_timer(void (*func) (void *data), void *data, double in) +{ + static long i = 0; + char evname[20]; + + D_ENTER(4); + snprintf(evname, sizeof(evname), "T_%ld", i); + D(4,("adding timer with unique name %s\n", evname)); + feh_add_timer(func, data, in, evname); + i++; + /* Mega paranoia ;) */ + if (i > 1000000) + i = 0; + D_RETURN_(4); +} diff --git a/src/timers.h b/src/timers.h new file mode 100644 index 0000000..5cce160 --- /dev/null +++ b/src/timers.h @@ -0,0 +1,49 @@ +/* timers.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef TIMERS_H +#define TIMERS_H + +struct __fehtimer +{ + char *name; + void (*func) (void *data); + void *data; + double in; + char just_added; + fehtimer next; +}; + +void feh_handle_timer(void); +double feh_get_time(void); +void feh_remove_timer(char *name); +void feh_add_timer(void (*func) (void *data), void *data, double in, + + char *name); +void feh_add_unique_timer(void (*func) (void *data), void *data, double in); + +extern fehtimer first_timer; + +#endif diff --git a/src/transupp.c b/src/transupp.c new file mode 100644 index 0000000..7b6d7ee --- /dev/null +++ b/src/transupp.c @@ -0,0 +1,1233 @@ +/* + * transupp.c + * + * Copyright (C) 1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains image transformation routines and other utility code + * used by the jpegtran sample application. These are NOT part of the core + * JPEG library. But we keep these routines separate from jpegtran.c to + * ease the task of maintaining jpegtran-like programs that have other user + * interfaces. + */ + +/* Although this file really shouldn't have access to the library internals, + * it's helpful to let it call jround_up() and jcopy_block_row(). + */ +#define JPEG_INTERNALS + +#include +#include +#include +#include + +#include +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) +#define SIZEOF(object) ((size_t) sizeof(object)) +#define JFREAD(file,buf,sizeofbuf) \ + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +#define JFWRITE(file,buf,sizeofbuf) \ + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) + + +#include "jpeglib.h" +#include "transupp.h" /* My own external interface */ + + +#if TRANSFORMS_SUPPORTED + +/* + * Lossless image transformation routines. These routines work on DCT + * coefficient arrays and thus do not require any lossy decompression + * or recompression of the image. + * Thanks to Guido Vollbeding for the initial design and code of this feature. + * + * Horizontal flipping is done in-place, using a single top-to-bottom + * pass through the virtual source array. It will thus be much the + * fastest option for images larger than main memory. + * + * The other routines require a set of destination virtual arrays, so they + * need twice as much memory as jpegtran normally does. The destination + * arrays are always written in normal scan order (top to bottom) because + * the virtual array manager expects this. The source arrays will be scanned + * in the corresponding order, which means multiple passes through the source + * arrays for most of the transforms. That could result in much thrashing + * if the image is larger than main memory. + * + * Some notes about the operating environment of the individual transform + * routines: + * 1. Both the source and destination virtual arrays are allocated from the + * source JPEG object, and therefore should be manipulated by calling the + * source's memory manager. + * 2. The destination's component count should be used. It may be smaller + * than the source's when forcing to grayscale. + * 3. Likewise the destination's sampling factors should be used. When + * forcing to grayscale the destination's sampling factors will be all 1, + * and we may as well take that as the effective iMCU size. + * 4. When "trim" is in effect, the destination's dimensions will be the + * trimmed values but the source's will be untrimmed. + * 5. All the routines assume that the source and destination buffers are + * padded out to a full iMCU boundary. This is true, although for the + * source buffer it is an undocumented property of jdcoefct.c. + * Notes 2,3,4 boil down to this: generally we should use the destination's + * dimensions and ignore the source's. + */ + + +LOCAL(void) +do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays) +/* Horizontal flip; done in-place, so no separate dest array is required */ +{ + JDIMENSION MCU_cols, comp_width, blk_x, blk_y; + int ci, k, offset_y; + JBLOCKARRAY buffer; + JCOEFPTR ptr1, ptr2; + JCOEF temp1, temp2; + jpeg_component_info *compptr; + + /* Horizontal mirroring of DCT blocks is accomplished by swapping + * pairs of blocks in-place. Within a DCT block, we perform horizontal + * mirroring by changing the signs of odd-numbered columns. + * Partial iMCUs at the right edge are left untouched. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (blk_y = 0; blk_y < compptr->height_in_blocks; + blk_y += compptr->v_samp_factor) { + buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) { + ptr1 = buffer[offset_y][blk_x]; + ptr2 = buffer[offset_y][comp_width - blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *ptr2++ = -temp1; + } + } + } + } + } +} + + +LOCAL(void) +do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Vertical flip */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge blocks will be copied verbatim. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } + } +} + + +LOCAL(void) +do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transpose source into destination */ +{ + JDIMENSION dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } +} + + +LOCAL(void) +do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 90 degree rotation is equivalent to + * 1. Transposing the image; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* Edge blocks are transposed but not mirrored. */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 270 degree rotation is equivalent to + * 1. Horizontal mirroring; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (dst_blk_y < comp_height) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Edge blocks are transposed but not mirrored. */ + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 180 degree rotation is equivalent to + * 1. Vertical mirroring; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (dst_blk_y < comp_height) { + /* Row is within the vertically mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge rows are only mirrored horizontally. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + /* Process the blocks that can be mirrored both ways. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE; i += 2) { + /* For even row, negate every odd column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + /* For odd row, negate every even column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = - *src_ptr++; + *dst_ptr++ = *src_ptr++; + } + } + } + /* Any remaining right-edge blocks are only mirrored vertically. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Remaining rows are just mirrored horizontally. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[offset_y]; + /* Process the blocks that can be mirrored. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE2; i += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + } + /* Any remaining right-edge blocks are only copied. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE2; i++) + *dst_ptr++ = *src_ptr++; + } + } + } + } + } +} + + +LOCAL(void) +do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transverse transpose is equivalent to + * 1. 180 degree rotation; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + if (dst_blk_y < comp_height) { + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + i++; + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Right-edge blocks are mirrored in y only */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } + } else { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Bottom-edge blocks are mirrored in x only */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } + } +} + + +/* Request any required workspace. + * + * We allocate the workspace virtual arrays from the source decompression + * object, so that all the arrays (both the original data and the workspace) + * will be taken into account while making memory management decisions. + * Hence, this routine must be called after jpeg_read_header (which reads + * the image dimensions) and before jpeg_read_coefficients (which realizes + * the source's virtual arrays). + */ + +GLOBAL(void) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays = NULL; + jpeg_component_info *compptr; + int ci; + + if (info->force_grayscale && + srcinfo->jpeg_color_space == JCS_YCbCr && + srcinfo->num_components == 3) { + /* We'll only process the first component */ + info->num_components = 1; + } else { + /* Process all the components */ + info->num_components = srcinfo->num_components; + } + + switch (info->transform) { + case JXFORM_NONE: + case JXFORM_FLIP_H: + /* Don't need a workspace array */ + break; + case JXFORM_FLIP_V: + case JXFORM_ROT_180: + /* Need workspace arrays having same dimensions as source image. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + } + break; + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + /* Need workspace arrays having transposed dimensions. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->h_samp_factor); + } + break; + } + info->workspace_coef_arrays = coef_arrays; +} + + +/* Transpose destination image parameters */ + +LOCAL(void) +transpose_critical_parameters (j_compress_ptr dstinfo) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + JDIMENSION dtemp; + UINT16 qtemp; + + /* Transpose basic image dimensions */ + dtemp = dstinfo->image_width; + dstinfo->image_width = dstinfo->image_height; + dstinfo->image_height = dtemp; + + /* Transpose sampling factors */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + + +/* Trim off any partial iMCUs on the indicated destination edge */ + +LOCAL(void) +trim_right_edge (j_compress_ptr dstinfo) +{ + int ci, max_h_samp_factor; + JDIMENSION MCU_cols; + + /* We have to compute max_h_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_h_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int h_samp_factor = dstinfo->comp_info[ci].h_samp_factor; + max_h_samp_factor = MAX(max_h_samp_factor, h_samp_factor); + } + MCU_cols = dstinfo->image_width / (max_h_samp_factor * DCTSIZE); + if (MCU_cols > 0) /* can't trim to 0 pixels */ + dstinfo->image_width = MCU_cols * (max_h_samp_factor * DCTSIZE); +} + +LOCAL(void) +trim_bottom_edge (j_compress_ptr dstinfo) +{ + int ci, max_v_samp_factor; + JDIMENSION MCU_rows; + + /* We have to compute max_v_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_v_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int v_samp_factor = dstinfo->comp_info[ci].v_samp_factor; + max_v_samp_factor = MAX(max_v_samp_factor, v_samp_factor); + } + MCU_rows = dstinfo->image_height / (max_v_samp_factor * DCTSIZE); + if (MCU_rows > 0) /* can't trim to 0 pixels */ + dstinfo->image_height = MCU_rows * (max_v_samp_factor * DCTSIZE); +} + +LOCAL(void) +set_exif_orientation (JOCTET FAR * data, unsigned int length, + unsigned char new_orient) +{ + boolean is_motorola; /* Flag for byte order */ + unsigned int number_of_tags, tagnum; + unsigned int firstoffset, offset; + + if (length < 12) return; /* Length of an IFD entry */ + + /* Discover byte order */ + if (GETJOCTET(data[0]) == 0x49 && GETJOCTET(data[1]) == 0x49) + is_motorola = FALSE; + else if (GETJOCTET(data[0]) == 0x4D && GETJOCTET(data[1]) == 0x4D) + is_motorola = TRUE; + else + return; + + /* Check Tag Mark */ + if (is_motorola) { + if (GETJOCTET(data[2]) != 0) return; + if (GETJOCTET(data[3]) != 0x2A) return; + } else { + if (GETJOCTET(data[3]) != 0) return; + if (GETJOCTET(data[2]) != 0x2A) return; + } + + /* Get first IFD offset (offset to IFD0) */ + if (is_motorola) { + if (GETJOCTET(data[4]) != 0) return; + if (GETJOCTET(data[5]) != 0) return; + firstoffset = GETJOCTET(data[6]); + firstoffset <<= 8; + firstoffset += GETJOCTET(data[7]); + } else { + if (GETJOCTET(data[7]) != 0) return; + if (GETJOCTET(data[6]) != 0) return; + firstoffset = GETJOCTET(data[5]); + firstoffset <<= 8; + firstoffset += GETJOCTET(data[4]); + } + if (firstoffset > length - 2) return; /* check end of data segment */ + + /* Get the number of directory entries contained in this IFD */ + if (is_motorola) { + number_of_tags = GETJOCTET(data[firstoffset]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[firstoffset+1]); + } else { + number_of_tags = GETJOCTET(data[firstoffset+1]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[firstoffset]); + } + if (number_of_tags == 0) return; + firstoffset += 2; + + /* Search for Orientation offset Tag in IFD0 */ + for (;;) { + if (firstoffset > length - 12) return; /* check end of data segment */ + /* Get Tag number */ + if (is_motorola) { + tagnum = GETJOCTET(data[firstoffset]); + tagnum <<= 8; + tagnum += GETJOCTET(data[firstoffset+1]); + } else { + tagnum = GETJOCTET(data[firstoffset+1]); + tagnum <<= 8; + tagnum += GETJOCTET(data[firstoffset]); + } + if (tagnum == 0x0112) break; /* found Orientation Tag */ + if (--number_of_tags == 0) return; + firstoffset += 12; + } + + if (is_motorola) { + data[firstoffset+2] = 0; /* Format = unsigned short (2 octets) */ + data[firstoffset+3] = 3; + data[firstoffset+4] = 0; /* Number Of Components = 1 */ + data[firstoffset+5] = 0; + data[firstoffset+6] = 0; + data[firstoffset+7] = 1; + data[firstoffset+8] = 0; + data[firstoffset+9] = (unsigned char) new_orient; + data[firstoffset+10] = 0; + data[firstoffset+11] = 0; + } else { + data[firstoffset+2] = 3; /* Format = unsigned short (2 octets) */ + data[firstoffset+3] = 0; + data[firstoffset+4] = 1; /* Number Of Components = 1 */ + data[firstoffset+5] = 0; + data[firstoffset+6] = 0; + data[firstoffset+7] = 0; + data[firstoffset+8] = (unsigned char) new_orient; + data[firstoffset+9] = 0; + data[firstoffset+10] = 0; + data[firstoffset+11] = 0; + } +} + +/* Adjust Exif image parameters. + * + * We try to adjust the Tags ExifImageWidth and ExifImageHeight if possible. + */ + +LOCAL(void) +adjust_exif_parameters (JOCTET FAR * data, unsigned int length, + JDIMENSION new_width, JDIMENSION new_height) +{ + boolean is_motorola; /* Flag for byte order */ + unsigned int number_of_tags, tagnum; + unsigned int firstoffset, offset; + unsigned int new_orient; + JDIMENSION new_value; + + if (length < 12) return; /* Length of an IFD entry */ + + /* Discover byte order */ + if (GETJOCTET(data[0]) == 0x49 && GETJOCTET(data[1]) == 0x49) + is_motorola = FALSE; + else if (GETJOCTET(data[0]) == 0x4D && GETJOCTET(data[1]) == 0x4D) + is_motorola = TRUE; + else + return; + + /* Check Tag Mark */ + if (is_motorola) { + if (GETJOCTET(data[2]) != 0) return; + if (GETJOCTET(data[3]) != 0x2A) return; + } else { + if (GETJOCTET(data[3]) != 0) return; + if (GETJOCTET(data[2]) != 0x2A) return; + } + + /* Get first IFD offset (offset to IFD0) */ + if (is_motorola) { + if (GETJOCTET(data[4]) != 0) return; + if (GETJOCTET(data[5]) != 0) return; + firstoffset = GETJOCTET(data[6]); + firstoffset <<= 8; + firstoffset += GETJOCTET(data[7]); + } else { + if (GETJOCTET(data[7]) != 0) return; + if (GETJOCTET(data[6]) != 0) return; + firstoffset = GETJOCTET(data[5]); + firstoffset <<= 8; + firstoffset += GETJOCTET(data[4]); + } + if (firstoffset > length - 2) return; /* check end of data segment */ + + /* Get the number of directory entries contained in this IFD */ + if (is_motorola) { + number_of_tags = GETJOCTET(data[firstoffset]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[firstoffset+1]); + } else { + number_of_tags = GETJOCTET(data[firstoffset+1]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[firstoffset]); + } + if (number_of_tags == 0) return; + firstoffset += 2; + + /* Search for ExifSubIFD offset Tag in IFD0 */ + for (;;) { + if (firstoffset > length - 12) return; /* check end of data segment */ + /* Get Tag number */ + if (is_motorola) { + tagnum = GETJOCTET(data[firstoffset]); + tagnum <<= 8; + tagnum += GETJOCTET(data[firstoffset+1]); + } else { + tagnum = GETJOCTET(data[firstoffset+1]); + tagnum <<= 8; + tagnum += GETJOCTET(data[firstoffset]); + } + if (tagnum == 0x8769) break; /* found ExifSubIFD offset Tag */ + if (--number_of_tags == 0) return; + firstoffset += 12; + } + + /* Get the ExifSubIFD offset */ + if (is_motorola) { + if (GETJOCTET(data[firstoffset+8]) != 0) return; + if (GETJOCTET(data[firstoffset+9]) != 0) return; + offset = GETJOCTET(data[firstoffset+10]); + offset <<= 8; + offset += GETJOCTET(data[firstoffset+11]); + } else { + if (GETJOCTET(data[firstoffset+11]) != 0) return; + if (GETJOCTET(data[firstoffset+10]) != 0) return; + offset = GETJOCTET(data[firstoffset+9]); + offset <<= 8; + offset += GETJOCTET(data[firstoffset+8]); + } + if (offset > length - 2) return; /* check end of data segment */ + + /* Get the number of directory entries contained in this SubIFD */ + if (is_motorola) { + number_of_tags = GETJOCTET(data[offset]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[offset+1]); + } else { + number_of_tags = GETJOCTET(data[offset+1]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[offset]); + } + if (number_of_tags < 2) return; + offset += 2; + + /* Search for ExifImageWidth and ExifImageHeight Tags in this SubIFD */ + do { + if (offset > length - 12) return; /* check end of data segment */ + /* Get Tag number */ + if (is_motorola) { + tagnum = GETJOCTET(data[offset]); + tagnum <<= 8; + tagnum += GETJOCTET(data[offset+1]); + } else { + tagnum = GETJOCTET(data[offset+1]); + tagnum <<= 8; + tagnum += GETJOCTET(data[offset]); + } + if (tagnum == 0xA002 || tagnum == 0xA003) { + if (tagnum == 0xA002) { + new_value = new_width; /* ExifImageWidth Tag */ + } else { + new_value = new_height; /* ExifImageHeight Tag */ + } + if (is_motorola) { + data[offset+2] = 0; /* Format = unsigned long (4 octets) */ + data[offset+3] = 4; + data[offset+4] = 0; /* Number Of Components = 1 */ + data[offset+5] = 0; + data[offset+6] = 0; + data[offset+7] = 1; + data[offset+8] = 0; + data[offset+9] = 0; + data[offset+10] = (JOCTET)((new_value >> 8) & 0xFF); + data[offset+11] = (JOCTET)(new_value & 0xFF); + } else { + data[offset+2] = 4; /* Format = unsigned long (4 octets) */ + data[offset+3] = 0; + data[offset+4] = 1; /* Number Of Components = 1 */ + data[offset+5] = 0; + data[offset+6] = 0; + data[offset+7] = 0; + data[offset+8] = (JOCTET)(new_value & 0xFF); + data[offset+9] = (JOCTET)((new_value >> 8) & 0xFF); + data[offset+10] = 0; + data[offset+11] = 0; + } + } + offset += 12; + } while (--number_of_tags); +} + + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +GLOBAL(jvirt_barray_ptr *) +jtransform_adjust_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + jpeg_saved_marker_ptr marker; + /* If force-to-grayscale is requested, adjust destination parameters */ + if (info->force_grayscale) { + /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed + * properly. Among other things, the target h_samp_factor & v_samp_factor + * will get set to 1, which typically won't match the source. + * In fact we do this even if the source is already grayscale; that + * provides an easy way of coercing a grayscale JPEG with funny sampling + * factors to the customary 1,1. (Some decoders fail on other factors.) + */ + if ((dstinfo->jpeg_color_space == JCS_YCbCr && + dstinfo->num_components == 3) || + (dstinfo->jpeg_color_space == JCS_GRAYSCALE && + dstinfo->num_components == 1)) { + /* We have to preserve the source's quantization table number. */ + int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no; + jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE); + dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no; + } else { + /* Sorry, can't do it */ + ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); + } + } + + /* Correct the destination's image dimensions etc if necessary */ + switch (info->transform) { + case JXFORM_NONE: + /* Nothing to do */ + break; + case JXFORM_FLIP_H: + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_FLIP_V: + if (info->trim) + trim_bottom_edge(dstinfo); + break; + case JXFORM_TRANSPOSE: + transpose_critical_parameters(dstinfo); + /* transpose does NOT have to trim anything */ + break; + case JXFORM_TRANSVERSE: + transpose_critical_parameters(dstinfo); + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_90: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_ROT_180: + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_270: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_bottom_edge(dstinfo); + break; + } + + for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker->marker != JPEG_APP0+1) + continue; + /* Adjust Exif properties */ + if (marker->marker == JPEG_APP0+1 && + marker->data_length >= 6 && + GETJOCTET(marker->data[0]) == 0x45 && + GETJOCTET(marker->data[1]) == 0x78 && + GETJOCTET(marker->data[2]) == 0x69 && + GETJOCTET(marker->data[3]) == 0x66 && + GETJOCTET(marker->data[4]) == 0 && + GETJOCTET(marker->data[5]) == 0) { + /* Suppress output of JFIF marker */ + dstinfo->write_JFIF_header = FALSE; + /* Adjust Exif image parameters */ + if (dstinfo->image_width != srcinfo->image_width || + dstinfo->image_height != srcinfo->image_height) + /* Align data segment to start of TIFF structure for parsing */ + adjust_exif_parameters(marker->data + 6, + marker->data_length - 6, + dstinfo->image_width, dstinfo->image_height); + /* I'm honestly not sure what the right thing to do is here.. The + * existing orientation tag may be incorrect, so making a change based + * on the previous value seems like the wrong thing to do. For now, I'm + * going to assume that the user is always "fixing" the orientation, + * i.e. putting the image the "right way up". In this case, we want to + * set the orientation to "top left". + */ + set_exif_orientation(marker->data + 6, + marker->data_length - 6, + 1); + } + } + + /* Return the appropriate output data set */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +GLOBAL(void) +jtransform_execute_transformation (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays; + + switch (info->transform) { + case JXFORM_NONE: + break; + case JXFORM_FLIP_H: + do_flip_h(srcinfo, dstinfo, src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSVERSE: + do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + do_rot_90(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_180: + do_rot_180(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + do_rot_270(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + } +} + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* Setup decompression object to save desired markers in memory. + * This must be called before jpeg_read_header() to have the desired effect. + */ + +GLOBAL(void) +jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * if the encoder library already wrote one. + */ + for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { + if (dstinfo->write_JFIF_header && + marker->marker == JPEG_APP0 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x4A && + GETJOCTET(marker->data[1]) == 0x46 && + GETJOCTET(marker->data[2]) == 0x49 && + GETJOCTET(marker->data[3]) == 0x46 && + GETJOCTET(marker->data[4]) == 0) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} diff --git a/src/transupp.h b/src/transupp.h new file mode 100644 index 0000000..5c2d32a --- /dev/null +++ b/src/transupp.h @@ -0,0 +1,135 @@ +/* + * transupp.h + * + * Copyright (C) 1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for image transformation routines and + * other utility code used by the jpegtran sample application. These are + * NOT part of the core JPEG library. But we keep these routines separate + * from jpegtran.c to ease the task of maintaining jpegtran-like programs + * that have other user interfaces. + * + * NOTE: all the routines declared here have very specific requirements + * about when they are to be executed during the reading and writing of the + * source and destination files. See the comments in transupp.c, or see + * jpegtran.c for an example of correct usage. + */ + +/* If you happen not to want the image transform support, disable it here */ +#ifndef TRANSFORMS_SUPPORTED +#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */ +#endif + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jtransform_request_workspace jTrRequest +#define jtransform_adjust_parameters jTrAdjust +#define jtransform_execute_transformation jTrExec +#define jcopy_markers_setup jCMrkSetup +#define jcopy_markers_execute jCMrkExec +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * Codes for supported types of image transformations. + */ + +typedef enum { + JXFORM_NONE, /* no transformation */ + JXFORM_FLIP_H, /* horizontal flip */ + JXFORM_FLIP_V, /* vertical flip */ + JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */ + JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */ + JXFORM_ROT_90, /* 90-degree clockwise rotation */ + JXFORM_ROT_180, /* 180-degree rotation */ + JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */ +} JXFORM_CODE; + +/* + * Although rotating and flipping data expressed as DCT coefficients is not + * hard, there is an asymmetry in the JPEG format specification for images + * whose dimensions aren't multiples of the iMCU size. The right and bottom + * image edges are padded out to the next iMCU boundary with junk data; but + * no padding is possible at the top and left edges. If we were to flip + * the whole image including the pad data, then pad garbage would become + * visible at the top and/or left, and real pixels would disappear into the + * pad margins --- perhaps permanently, since encoders & decoders may not + * bother to preserve DCT blocks that appear to be completely outside the + * nominal image area. So, we have to exclude any partial iMCUs from the + * basic transformation. + * + * Transpose is the only transformation that can handle partial iMCUs at the + * right and bottom edges completely cleanly. flip_h can flip partial iMCUs + * at the bottom, but leaves any partial iMCUs at the right edge untouched. + * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched. + * The other transforms are defined as combinations of these basic transforms + * and process edge blocks in a way that preserves the equivalence. + * + * The "trim" option causes untransformable partial iMCUs to be dropped; + * this is not strictly lossless, but it usually gives the best-looking + * result for odd-size images. Note that when this option is active, + * the expected mathematical equivalences between the transforms may not hold. + * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim + * followed by -rot 180 -trim trims both edges.) + * + * We also offer a "force to grayscale" option, which simply discards the + * chrominance channels of a YCbCr image. This is lossless in the sense that + * the luminance channel is preserved exactly. It's not the same kind of + * thing as the rotate/flip transformations, but it's convenient to handle it + * as part of this package, mainly because the transformation routines have to + * be aware of the option to know how many components to work on. + */ + +typedef struct { + /* Options: set by caller */ + JXFORM_CODE transform; /* image transform operator */ + boolean trim; /* if TRUE, trim partial MCUs as needed */ + boolean force_grayscale; /* if TRUE, convert color image to grayscale */ + + /* Internal workspace: caller should not touch these */ + int num_components; /* # of components in workspace */ + jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */ +} jpeg_transform_info; + + +#if TRANSFORMS_SUPPORTED + +/* Request any required workspace */ +EXTERN(void) jtransform_request_workspace + JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info)); +/* Adjust output image parameters */ +EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); +/* Execute the actual transformation, if any */ +EXTERN(void) jtransform_execute_transformation + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* + * Support for copying optional markers from source to destination file. + */ + +typedef enum { + JCOPYOPT_NONE, /* copy no optional markers */ + JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */ + JCOPYOPT_ALL /* copy all optional markers */ +} JCOPY_OPTION; + +#define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */ + +/* Setup decompression object to save desired markers in memory */ +EXTERN(void) jcopy_markers_setup + JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); +/* Copy markers saved in the given source object to the destination object */ +EXTERN(void) jcopy_markers_execute + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option)); diff --git a/src/ttfonts/Makefile.am b/src/ttfonts/Makefile.am new file mode 100644 index 0000000..7df955a --- /dev/null +++ b/src/ttfonts/Makefile.am @@ -0,0 +1,13 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = 1.4 foreign + +# A list of all the files in the current directory which can be regenerated +MAINTAINERCLEANFILES = Makefile.in + +testfonts_DATA = \ +menu.style black.style yudit.ttf + +testfontsdir=$(prefix)/share/feh/fonts + +EXTRA_DIST = $(testfonts_DATA) diff --git a/src/ttfonts/Makefile.in b/src/ttfonts/Makefile.in new file mode 100644 index 0000000..ae93e14 --- /dev/null +++ b/src/ttfonts/Makefile.in @@ -0,0 +1,216 @@ +# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +CC = @CC@ +GIBLIB_CFLAGS = @GIBLIB_CFLAGS@ +GIBLIB_LIBS = @GIBLIB_LIBS@ +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_CONFIG = @GTK_CONFIG@ +GTK_LIBS = @GTK_LIBS@ +HAVE_LIB = @HAVE_LIB@ +HAVE_XINERAMA = @HAVE_XINERAMA@ +IMLIB_CFLAGS = @IMLIB_CFLAGS@ +IMLIB_LIBS = @IMLIB_LIBS@ +LIB = @LIB@ +LTLIB = @LTLIB@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +UP = @UP@ +VERSION = @VERSION@ +XINERAMA_LIBS = @XINERAMA_LIBS@ + +AUTOMAKE_OPTIONS = 1.4 foreign + +# A list of all the files in the current directory which can be regenerated +MAINTAINERCLEANFILES = Makefile.in + +testfonts_DATA = menu.style black.style yudit.ttf + + +testfontsdir = $(prefix)/share/feh/fonts + +EXTRA_DIST = $(testfonts_DATA) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../src/config.h +CONFIG_CLEAN_FILES = +DATA = $(testfonts_DATA) + +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.ac $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --foreign --include-deps src/ttfonts/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +install-testfontsDATA: $(testfonts_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(testfontsdir) + @list='$(testfonts_DATA)'; for p in $$list; do \ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(testfontsdir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(testfontsdir)/$$p; \ + else if test -f $$p; then \ + echo " $(INSTALL_DATA) $$p $(DESTDIR)$(testfontsdir)/$$p"; \ + $(INSTALL_DATA) $$p $(DESTDIR)$(testfontsdir)/$$p; \ + fi; fi; \ + done + +uninstall-testfontsDATA: + @$(NORMAL_UNINSTALL) + list='$(testfonts_DATA)'; for p in $$list; do \ + rm -f $(DESTDIR)$(testfontsdir)/$$p; \ + done +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src/ttfonts + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: install-testfontsDATA +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-testfontsDATA +uninstall: uninstall-am +all-am: Makefile $(DATA) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(testfontsdir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +mostlyclean-am: mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: uninstall-testfontsDATA install-testfontsDATA tags distdir \ +info-am info dvi-am dvi check check-am installcheck-am installcheck \ +install-exec-am install-exec install-data-am install-data install-am \ +install uninstall-am uninstall all-redirect all-am all installdirs \ +mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/ttfonts/black.style b/src/ttfonts/black.style new file mode 100644 index 0000000..d2a86a9 --- /dev/null +++ b/src/ttfonts/black.style @@ -0,0 +1,4 @@ +#Style +#NAME Menu +255 255 255 64 2 2 +255 255 255 255 0 0 diff --git a/src/ttfonts/menu.style b/src/ttfonts/menu.style new file mode 100644 index 0000000..8557360 --- /dev/null +++ b/src/ttfonts/menu.style @@ -0,0 +1,4 @@ +#Style +#NAME Menu +0 0 0 64 2 2 +0 0 0 0 0 0 diff --git a/src/ttfonts/yudit.ttf b/src/ttfonts/yudit.ttf new file mode 100644 index 0000000..8099e63 Binary files /dev/null and b/src/ttfonts/yudit.ttf differ diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..7183b66 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,248 @@ +/* utils.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "debug.h" +#include "options.h" + +static char *feh_user_name = NULL; +static char *feh_tmp_dir = NULL; + +/* eprintf: print error message and exit */ +void +eprintf(char *fmt, ...) +{ + va_list args; + + fflush(stdout); + fprintf(stderr, "%s ERROR: ", PACKAGE); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + if (fmt[0] != '\0' && fmt[strlen(fmt) - 1] == ':') + fprintf(stderr, " %s", strerror(errno)); + fprintf(stderr, "\n"); + exit(2); +} + +/* weprintf: print warning message and continue */ +void +weprintf(char *fmt, ...) +{ + va_list args; + + fflush(stdout); + fprintf(stderr, "%s WARNING: ", PACKAGE); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + if (fmt[0] != '\0' && fmt[strlen(fmt) - 1] == ':') + fprintf(stderr, " %s", strerror(errno)); + fprintf(stderr, "\n"); +} + +/* estrdup: duplicate a string, report if error */ +char * +_estrdup(char *s) +{ + char *t; + + if (!s) + return NULL; + + t = (char *) malloc(strlen(s) + 1); + if (t == NULL) + eprintf("estrdup(\"%.20s\") failed:", s); + strcpy(t, s); + return t; +} + +/* emalloc: malloc and report if error */ +void * +_emalloc(size_t n) +{ + void *p; + + p = malloc(n); + if (p == NULL) + eprintf("malloc of %u bytes failed:", n); + return p; +} + +/* erealloc: realloc and report if error */ +void * +_erealloc(void *ptr, size_t n) +{ + void *p; + + p = realloc(ptr, n); + if (p == NULL) + eprintf("realloc of %p by %u bytes failed:", ptr, n); + return p; +} + +char * +estrjoin(const char *separator, ...) +{ + char *string, *s; + va_list args; + int len; + int separator_len; + + if (separator == NULL) + separator = ""; + + separator_len = strlen(separator); + va_start(args, separator); + s = va_arg(args, char *); + + if (s) + { + len = strlen(s); + s = va_arg(args, char *); + + while (s) + { + len += separator_len + strlen(s); + s = va_arg(args, char *); + } + va_end(args); + string = malloc(sizeof(char) * (len + 1)); + + *string = 0; + va_start(args, separator); + s = va_arg(args, char *); + + strcat(string, s); + s = va_arg(args, char *); + + while (s) + { + strcat(string, separator); + strcat(string, s); + s = va_arg(args, char *); + } + } + else + string = estrdup(""); + va_end(args); + + return string; +} + +char * +stroflen(char c, int l) +{ + static char buf[1024]; + int i = 0; + + buf[0] = '\0'; + while (l--) + buf[i++] = c; + buf[i] = '\0'; + return buf; +} + +/* free the result please */ +char *feh_unique_filename(char *path, char *basename) +{ + char *tmpname; + char num[10]; + char cppid[10]; + static long int i = 1; + struct stat st; + pid_t ppid; + + D_ENTER(4); + /* Massive paranoia ;) */ + if (i > 999998) + i = 1; + + ppid = getpid(); + snprintf(cppid, sizeof(cppid), "%06ld", (long)ppid); + + /* make sure file doesn't exist */ + do + { + snprintf(num, sizeof(num), "%06ld", i++); + tmpname = + estrjoin("", path, "feh_", cppid, "_", num, "_", + basename, NULL); + } + while (stat(tmpname, &st) == 0); + D_RETURN(4, tmpname); +} + +/* reads file into a string, but limits o 4095 chars and ensures a \0 */ +char *ereadfile(char *path) { + char buffer[4096]; + FILE *fp; + int count; + + fp = fopen(path, "r"); + if (!fp) + return NULL; + + count = fread(buffer, sizeof(char), sizeof(buffer) - 1, fp); + if (buffer[count - 1] == '\n') + buffer[count - 1] = '\0'; + else + buffer[count] = '\0'; + + fclose(fp); + + return estrdup(buffer); +} + +char *feh_get_tmp_dir(void) { + char *tmp; + if (feh_tmp_dir) + return feh_tmp_dir; + tmp = getenv("TMPDIR"); + if (!tmp) + tmp = getenv("TMP"); + if (!tmp) + tmp = getenv("TEMP"); + if (!tmp) + tmp = "/tmp"; + feh_tmp_dir = estrdup(tmp); + return feh_tmp_dir; +} + +char *feh_get_user_name(void) { + struct passwd *pw = NULL; + + if (feh_user_name) + return feh_user_name; + setpwent (); + pw = getpwuid (getuid ()); + endpwent (); + feh_user_name = estrdup(pw->pw_name); + return feh_user_name; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..221319c --- /dev/null +++ b/src/utils.h @@ -0,0 +1,90 @@ +/* utils.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef UTILS_H +#define UTILS_H + +#ifndef __GNUC__ +# define __attribute__(x) +#endif + +void eprintf(char *fmt, ...) __attribute__ ((noreturn)); +void weprintf(char *fmt, ...); +char *_estrdup(char *s); +void *_emalloc(size_t n); +void *_erealloc(void *ptr, size_t n); +char *estrjoin(const char *separator, ...); +char *stroflen(char, int); +char *feh_unique_filename(char *path, char *basename); +char *ereadfile(char *path); +char *feh_get_tmp_dir(void); +char *feh_get_user_name(void); + +#define ESTRAPPEND(a,b) \ + {\ + char *____newstr;\ + if (!(a)) {\ + a = estrdup(b);\ + } else {\ + ____newstr = emalloc(strlen(a) + strlen(b) + 1);\ + strcpy(____newstr, (a));\ + strcat(____newstr, (b));\ + free(a);\ + (a) = ____newstr;\ + }\ + } + +#define ESTRAPPEND_CHAR(a,b) \ + {\ + char *____newstr;\ + int ____len;\ + if (!(a)) {\ + (a) = emalloc(2);\ + (a)[0] = (b);\ + (a)[1] = '\0';\ + } else {\ + ____len = strlen((a));\ + ____newstr = emalloc(____len + 2);\ + strcpy(____newstr, (a));\ + ____newstr[____len] = (b);\ + ____newstr[____len+1] = '\0';\ + free(a);\ + (a) = ____newstr;\ + }\ + } + +#define ESTRTRUNC(string,chars) \ + {\ + int ____len;\ + if (string) {\ + ____len = strlen(string);\ + if (____len >= (chars)) {\ + (string)[strlen(string) - chars] = '\0';\ + }\ + }\ + } + + +#endif diff --git a/src/winwidget.c b/src/winwidget.c new file mode 100644 index 0000000..e3b9d35 --- /dev/null +++ b/src/winwidget.c @@ -0,0 +1,1058 @@ +/* winwidget.c + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "feh.h" +#include "filelist.h" +#include "winwidget.h" +#include "options.h" + +static void winwidget_unregister(winwidget win); +static void winwidget_register(winwidget win); +static winwidget winwidget_allocate(void); + +static char bm_no_data[] = { 0,0,0,0, 0,0,0,0 }; +int window_num = 0; /* For window list */ +winwidget *windows = NULL; /* List of windows to loop though */ + +static winwidget +winwidget_allocate(void) +{ + winwidget ret = NULL; + + D_ENTER(4); + ret = emalloc(sizeof(_winwidget)); + + ret->win = 0; + ret->w = 0; + ret->h = 0; + ret->full_screen = 0; + ret->im_w = 0; + ret->im_h = 0; + ret->im_angle = 0; + ret->bg_pmap = 0; + ret->bg_pmap_cache = 0; + ret->im = NULL; + ret->name = NULL; + ret->file = NULL; + ret->type = WIN_TYPE_UNSET; + ret->visible = 0; + ret->caption_entry = 0; + + /* Zoom stuff */ + ret->mode = MODE_NORMAL; + + ret->gc = None; + + /* New stuff */ + ret->im_x = 0; + ret->im_y = 0; + ret->zoom = 1.0; + + ret->click_offset_x = 0; + ret->click_offset_y = 0; + ret->im_click_offset_x = 0; + ret->im_click_offset_y = 0; + ret->has_rotated = 0; + + D_RETURN(4, ret); +} + +winwidget +winwidget_create_from_image(Imlib_Image im, + char *name, + char type) +{ + winwidget ret = NULL; + + D_ENTER(4); + + if (im == NULL) + D_RETURN(4, NULL); + + ret = winwidget_allocate(); + ret->type = type; + + ret->im = im; + 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) + ret->full_screen = True; + winwidget_create_window(ret, ret->w, ret->h); + winwidget_render_image(ret, 1, 1); + + D_RETURN(4, ret); +} + +winwidget +winwidget_create_from_file(gib_list * list, + char *name, + char type) +{ + winwidget ret = NULL; + feh_file *file = FEH_FILE(list->data); + + D_ENTER(4); + + if (!file || !file->filename) + D_RETURN(4, 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) { + winwidget_destroy(ret); + D_RETURN(4, 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) + ret->full_screen = True; + winwidget_create_window(ret, ret->w, ret->h); + winwidget_render_image(ret, 1, 1); + } + + D_RETURN(4, ret); +} + +void +winwidget_create_window(winwidget ret, + int w, + int h) +{ + XSetWindowAttributes attr; + XClassHint *xch; + MWMHints mwmhints; + Atom prop = None; + int x = 0; + int y = 0; + + D_ENTER(4); + + if (ret->full_screen) { + w = scr->width; + h = scr->height; + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + w = xinerama_screens[xinerama_screen].width; + h = xinerama_screens[xinerama_screen].height; + } +#endif /* HAVE_LIBXINERAMA */ + } else if (opt.geom_flags) { + if (opt.geom_flags & WidthValue) { + w = opt.geom_w; + } + if (opt.geom_flags & HeightValue) { + h = opt.geom_h; + } + if (opt.geom_flags & XValue) { + if (opt.geom_flags & XNegative) { + x = scr->width - opt.geom_x; + } else { + x = opt.geom_x; + } + } + if (opt.geom_flags & YValue) { + if (opt.geom_flags & YNegative) { + y = scr->height - opt.geom_y; + } else { + y = opt.geom_y; + } + } + } else if (opt.screen_clip) { + if (w > scr->width) + w = scr->width; + if (h > scr->height) + h = scr->height; + +#ifdef HAVE_LIBXINERAMA + if (opt.xinerama && xinerama_screens) { + if (w > xinerama_screens[xinerama_screen].width) + w = xinerama_screens[xinerama_screen].width; + if (h > xinerama_screens[xinerama_screen].height) + h = xinerama_screens[xinerama_screen].height; + } +#endif /* HAVE_LIBXINERAMA */ + } + + ret->x = x; + ret->y = y; + ret->w = w; + ret->h = h; + ret->visible = False; + + attr.backing_store = NotUseful; + attr.override_redirect = False; + attr.colormap = cm; + attr.border_pixel = 0; + attr.background_pixel = 0; + attr.save_under = False; + attr.event_mask = + StructureNotifyMask | ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | + KeyReleaseMask | ButtonMotionMask | ExposureMask | FocusChangeMask | + PropertyChangeMask | VisibilityChangeMask; + + if (opt.borderless || ret->full_screen) { + prop = XInternAtom(disp, "_MOTIF_WM_HINTS", True); + if (prop == None) { + weprintf("Window Manager does not support MWM hints. " + "To get a borderless window I have to bypass your wm."); + attr.override_redirect = True; + mwmhints.flags = 0; + } else { + 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 (mwmhints.flags) { + XChangeProperty(disp, ret->win, prop, prop, 32, PropModeReplace, + (unsigned char *) &mwmhints, PROP_MWM_HINTS_ELEMENTS); + } + + XSetWMProtocols(disp, ret->win, &wmDeleteWindow, 1); + winwidget_update_title(ret); + xch = XAllocClassHint(); + xch->res_name = "feh"; + xch->res_class = "feh"; + XSetClassHint(disp, ret->win, xch); + XFree(xch); + + /* Size hints */ + if (ret->full_screen || opt.geom_flags) { + XSizeHints xsz; + + xsz.flags = USPosition; + xsz.x = x; + xsz.y = y; + XSetWMNormalHints(disp, ret->win, &xsz); + XMoveWindow(disp, ret->win, x, y); + } + if (ret->full_screen && opt.hide_pointer) { + Cursor no_ptr; + XColor black, dummy; + Pixmap bm_no; + bm_no = XCreateBitmapFromData(disp, ret->win, bm_no_data, 8, 8); + XAllocNamedColor(disp, DefaultColormapOfScreen(DefaultScreenOfDisplay(disp)), "black", &black, &dummy); + + no_ptr = XCreatePixmapCursor(disp, bm_no, bm_no, &black, &black, 0, 0); + XDefineCursor(disp, ret->win, no_ptr); + } + + /* set the icon name property */ + XSetIconName(disp, ret->win, "feh"); + /* set the command hint */ + XSetCommand(disp, ret->win, cmdargv, cmdargc); + + winwidget_register(ret); + D_RETURN_(4); +} + +void +winwidget_update_title(winwidget ret) +{ + char *name; + + D_ENTER(4); + D(4, ("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); +} + +void +winwidget_setup_pixmaps(winwidget winwid) +{ + D_ENTER(4); + + 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); + } + 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)); + if (winwid->bg_pmap) + XFreePixmap(disp, winwid->bg_pmap); + + if (winwid->w == 0) + winwid->w = 1; + if (winwid->h == 0) + winwid->h = 1; + winwid->bg_pmap = + XCreatePixmap(disp, winwid->win, winwid->w, winwid->h, depth); + winwid->had_resize = 0; + } + } + D_RETURN_(4); +} + +void +winwidget_render_image(winwidget winwid, + int resize, + int alias) +{ + int sx, sy, sw, sh, dx, dy, dw, dh; + int calc_w, calc_h; + + D_ENTER(4); + + if (!winwid->full_screen && resize) { + winwidget_resize(winwid, winwid->im_w, winwid->im_h); + 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; + + winwidget_setup_pixmaps(winwid); + + 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); + + if (!winwid->full_screen && opt.scale_down + && ((winwid->w < winwid->im_w) || (winwid->h < winwid->im_h))) { + D(2, ("scaling down image\n")); + + 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); + } + + if (resize && (winwid->full_screen || opt.geom_flags)) { + int smaller; /* Is the image smaller than screen? */ + int max_w, max_h; + + 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; + } +#endif /* HAVE_LIBXINERAMA */ + } else { + if (opt.geom_flags & WidthValue) { + max_w = opt.geom_w; + } + if (opt.geom_flags & HeightValue) { + max_h = opt.geom_h; + } + } + + 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 + * 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; + } 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; + } + } + } 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; + } + } + + /* Now we ensure only to render the area we're looking at */ + dx = winwid->im_x; + dy = winwid->im_y; + if (dx < 0) + dx = 0; + if (dy < 0) + dy = 0; + + if (winwid->im_x < 0) + sx = 0 - (winwid->im_x / winwid->zoom); + else + sx = 0; + + if (winwid->im_y < 0) + sy = 0 - (winwid->im_y / winwid->zoom); + else + sy = 0; + + calc_w = winwid->im_w * winwid->zoom; + calc_h = winwid->im_h * winwid->zoom; + dw = (winwid->w - winwid->im_x); + dh = (winwid->h - winwid->im_y); + if (calc_w < dw) + dw = calc_w; + 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; + + D(5, + ("sx: %d sy: %d sw: %d sh: %d dx: %d dy: %d dw: %d dh: %d 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->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); + 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); + } + 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) + feh_draw_zoom(winwid); + XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap); + XClearWindow(disp, winwid->win); + D_RETURN_(4); +} + +void winwidget_render_image_cached(winwidget winwid) { + static GC gc = None; + + if (gc == None) { + gc = XCreateGC(disp, winwid->win, 0, NULL); + } + XCopyArea(disp, winwid->bg_pmap_cache, winwid->bg_pmap, gc, 0, 0, winwid->w, winwid->h, 0, 0); + + if (opt.caption_path) + feh_draw_caption(winwid); + if (opt.draw_filename) + feh_draw_filename(winwid); + if (opt.draw_actions) + feh_draw_actions(winwid); + XSetWindowBackgroundPixmap(disp, winwid->win, winwid->bg_pmap); + XClearWindow(disp, winwid->win); +} + +double +feh_calc_needed_zoom(double *zoom, + int orig_w, + int orig_h, + int dest_w, + int dest_h) +{ + double ratio = 0.0; + + D_ENTER(4); + + ratio = ((double) orig_w / orig_h) / ((double) dest_w / dest_h); + + if (ratio > 1.0) + *zoom = ((double) dest_w / orig_w); + else + *zoom = ((double) dest_h / orig_h); + + D_RETURN(4, ratio); +} + +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"); + + 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; + } + } + 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); +} + +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); +} + +void +winwidget_destroy_xwin(winwidget winwid) +{ + D_ENTER(4); + if (winwid->win) { + winwidget_unregister(winwid); + XDestroyWindow(disp, winwid->win); + } + if (winwid->bg_pmap) { + XFreePixmap(disp, winwid->bg_pmap); + winwid->bg_pmap = None; + } + D_RETURN_(4); +} + +void +winwidget_destroy(winwidget winwid) +{ + D_ENTER(4); + 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); +} + +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); +} + +void +winwidget_rerender_all(int resize, + int alias) +{ + 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 +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); +} + +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)); +} + +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")); + XMaskEvent(disp, StructureNotifyMask, &ev); + D(4, ("Window mapped\n")); + winwid->visible = 1; + } + D_RETURN_(4); +} + +int +winwidget_count(void) +{ + D_ENTER(4); + D_RETURN(4, window_num); +} + +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_RETURN_(4); +} + +void +winwidget_resize(winwidget winwid, + int w, + int h) +{ + D_ENTER(4); + if (opt.geom_flags) { + 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; + } + 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; + } + + } + } +#endif /* HAVE_LIBXINERAMA */ + + } else { + D(4, ("No resize actually needed\n")); + } + + D_RETURN_(4); +} + +void +winwidget_hide(winwidget winwid) +{ + D_ENTER(4); + XUnmapWindow(disp, winwid->win); + winwid->visible = 0; + D_RETURN_(4); +} + +static void +winwidget_register(winwidget win) +{ + D_ENTER(4); + D(5, ("window %p\n", win)); + window_num++; + if (windows) + windows = erealloc(windows, window_num * sizeof(winwidget)); + else + windows = emalloc(window_num * sizeof(winwidget)); + windows[window_num - 1] = win; + + XSaveContext(disp, win->win, xid_context, (XPointer) win); + D_RETURN_(4); +} + +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++) + windows[j] = windows[j + 1]; + window_num--; + if (window_num > 0) + windows = erealloc(windows, window_num * sizeof(winwidget)); + else { + free(windows); + windows = NULL; + } + } + } + XDeleteContext(disp, win->win, xid_context); + D_RETURN_(4); +} + +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); +} + +void +winwidget_rename(winwidget winwid, + char *newname) +{ + D_ENTER(4); + if (winwid->name) + free(winwid->name); + winwid->name = estrdup(newname); + winwidget_update_title(winwid); + D_RETURN_(4); +} + +void +winwidget_free_image(winwidget w) +{ + D_ENTER(4); + if (w->im) + gib_imlib_free_image_and_decache(w->im); + w->im = NULL; + w->im_w = 0; + w->im_h = 0; + D_RETURN_(4); +} + +void +feh_debug_print_winwid(winwidget w) +{ + printf("winwid_debug:\n" "winwid = %p\n" "win = %ld\n" "w = %d\n" "h = %d\n" + "im_w = %d\n" "im_h = %d\n" "im_angle = %f\n" "type = %d\n" + "had_resize = %d\n" "im = %p\n" "GC = %p\n" "pixmap = %ld\n" + "name = %s\n" "file = %p\n" "mode = %d\n" "im_x = %d\n" "im_y = %d\n" + "zoom = %f\n" "click_offset_x = %d\n" "click_offset_y = %d\n" + "im_click_offset_x = %d\n" "im_click_offset_y = %d\n" + "has_rotated = %d\n", w, w->win, w->w, w->h, w->im_w, w->im_h, + w->im_angle, w->type, w->had_resize, w->im, w->gc, w->bg_pmap, + w->name, w->file, w->mode, w->im_x, w->im_y, w->zoom, + w->click_offset_x, w->click_offset_y, w->im_click_offset_x, + w->im_click_offset_y, w->has_rotated); +} + +void +winwidget_reset_image(winwidget winwid) +{ + D_ENTER(4); + winwid->zoom = 1.0; + winwid->im_x = 0; + winwid->im_y = 0; + winwid->im_angle = 0.0; + winwid->has_rotated = 0; + D_RETURN_(4); +} + +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); + + if ((winwid->im_w * winwid->zoom) > winwid->w) { + min_x = far_left; + max_x = 0; + } else { + min_x = 0; + max_x = far_left; + } + if ((winwid->im_h * winwid->zoom) > winwid->h) { + min_y = far_top; + max_y = 0; + } else { + min_y = 0; + max_y = far_top; + } + if (winwid->im_x > max_x) + winwid->im_x = max_x; + if (winwid->im_x < min_x) + winwid->im_x = min_x; + if (winwid->im_y > max_y) + winwid->im_y = max_y; + if (winwid->im_y < min_y) + winwid->im_y = min_y; + + D_RETURN_(4); +} + + +void +winwidget_size_to_image(winwidget winwid) +{ + D_ENTER(4); + winwidget_resize(winwid, winwid->im_w * winwid->zoom, + winwid->im_h * winwid->zoom); + winwid->im_x = winwid->im_y = 0; + winwidget_render_image(winwid, 0, 1); + D_RETURN_(4); +} + +int winwidget_get_width(winwidget winwid) { + int rect[4]; + D_ENTER(4); + winwidget_get_geometry(winwid, rect); + D_RETURN(4, rect[2]); +} + +int winwidget_get_height(winwidget winwid) { + int rect[4]; + D_ENTER(4); + winwidget_get_geometry(winwid, rect); + D_RETURN(4, rect[3]); +} + +void winwidget_get_geometry(winwidget winwid, int *rect) { + int bw, bp; + D_ENTER(4); + if (!rect) + return; + + XGetGeometry(disp, winwid->win, &root, + &(rect[0]), &(rect[1]), &(rect[2]), &(rect[3]), &bw, &bp); + + /* update the window geometry (in case it's inaccurate) */ + winwid->x = rect[0]; + winwid->y = rect[1]; + winwid->w = rect[2]; + winwid->h = rect[3]; + D_RETURN_(4); +} + +void winwidget_show_menu(winwidget winwid) { + int x, y, b; + unsigned int c; + 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 (!menu_single_win) + feh_menu_init_single_win(); + feh_menu_show_at_xy(menu_single_win, winwid, x, y); + } + else if (winwid->type == WIN_TYPE_THUMBNAIL) + { + if (!menu_thumbnail_win) + feh_menu_init_thumbnail_win(); + feh_menu_show_at_xy(menu_thumbnail_win, winwid, x, y); + } + else if (winwid->type == WIN_TYPE_THUMBNAIL_VIEWER) + { + if (!menu_single_win) + feh_menu_init_thumbnail_viewer(); + feh_menu_show_at_xy(menu_thumbnail_viewer, winwid, x, y); + } + else + { + if (!menu_main) + feh_menu_init_main(); + feh_menu_show_at_xy(menu_main, winwid, x, y); + } +} diff --git a/src/winwidget.h b/src/winwidget.h new file mode 100644 index 0000000..de08ec9 --- /dev/null +++ b/src/winwidget.h @@ -0,0 +1,158 @@ +/* winwidget.h + +Copyright (C) 1999-2003 Tom Gilbert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies of the Software and its documentation and acknowledgment shall be +given in the documentation and software packages that this Software was +used. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef WINWIDGET_H +#define WINWIDGET_H + +/* This MWM stuff pinched from Eterm/src/command.h */ + +# include +# include + +/* Motif window hints */ +#define MWM_HINTS_FUNCTIONS (1L << 0) +#define MWM_HINTS_DECORATIONS (1L << 1) +#define MWM_HINTS_INPUT_MODE (1L << 2) +#define MWM_HINTS_STATUS (1L << 3) +/* bit definitions for MwmHints.functions */ +#define MWM_FUNC_ALL (1L << 0) +#define MWM_FUNC_RESIZE (1L << 1) +#define MWM_FUNC_MOVE (1L << 2) +#define MWM_FUNC_MINIMIZE (1L << 3) +#define MWM_FUNC_MAXIMIZE (1L << 4) +#define MWM_FUNC_CLOSE (1L << 5) +/* bit definitions for MwmHints.decorations */ +#define MWM_DECOR_ALL (1L << 0) +#define MWM_DECOR_BORDER (1L << 1) +#define MWM_DECOR_RESIZEH (1L << 2) +#define MWM_DECOR_TITLE (1L << 3) +#define MWM_DECOR_MENU (1L << 4) +#define MWM_DECOR_MINIMIZE (1L << 5) +#define MWM_DECOR_MAXIMIZE (1L << 6) +/* bit definitions for MwmHints.inputMode */ +#define MWM_INPUT_MODELESS 0 +#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 +#define MWM_INPUT_SYSTEM_MODAL 2 +#define MWM_INPUT_FULL_APPLICATION_MODAL 3 +#define PROP_MWM_HINTS_ELEMENTS 5 + +/* Motif window hints */ +typedef struct _mwmhints +{ + CARD32 flags; + CARD32 functions; + CARD32 decorations; + INT32 input_mode; + CARD32 status; +} +MWMHints; + +enum win_type +{ + WIN_TYPE_UNSET, WIN_TYPE_SLIDESHOW, WIN_TYPE_SINGLE, WIN_TYPE_ABOUT, + WIN_TYPE_THUMBNAIL, WIN_TYPE_THUMBNAIL_VIEWER +}; + +struct __winwidget +{ + Window win; + int x; + int y; + int w; + int h; + int im_w; + int im_h; + double im_angle; + enum win_type type; + unsigned char had_resize, full_screen; + Imlib_Image im; + GC gc; + Pixmap bg_pmap; + Pixmap bg_pmap_cache; + char *name; + gib_list *file; + unsigned char visible; + + /* Stuff for zooming */ + unsigned char mode; + + unsigned char caption_entry; /* are we in caption entry mode? */ + + /* New stuff */ + int im_x; /* image offset from window top left */ + int im_y; /* image offset from window top left */ + double zoom; /* From 0 (not visible) to 100 (actual size) + all the way up to INT_MAX (ouch) */ + int click_offset_x; + int click_offset_y; + int im_click_offset_x; + int im_click_offset_y; + + unsigned char has_rotated; +}; + +int winwidget_loadimage(winwidget winwid, feh_file * filename); +void winwidget_show(winwidget winwid); +void winwidget_show_menu(winwidget winwid); +void winwidget_hide(winwidget winwid); +void winwidget_destroy_all(void); +void winwidget_free_image(winwidget w); +void winwidget_render_image(winwidget winwid, int resize, int alias); +void winwidget_rotate_image(winwidget winid, double angle); +void winwidget_move(winwidget winwid, int x, int y); +void winwidget_resize(winwidget winwid, int w, int h); +void winwidget_setup_pixmaps(winwidget winwid); +void winwidget_update_title(winwidget ret); +void winwidget_rerender_all(int resize, int alias); +void winwidget_destroy_xwin(winwidget winwid); +int winwidget_count(void); + +void winwidget_get_geometry(winwidget winwid, int *rect); +int winwidget_get_width(winwidget winwid); +int winwidget_get_height(winwidget winwid); +winwidget winwidget_get_from_window(Window win); +winwidget winwidget_create_from_file(gib_list * filename, char *name, + + char type); +winwidget winwidget_create_from_image(Imlib_Image im, char *name, char type); +void winwidget_rename(winwidget winwid, char *newname); +void winwidget_destroy(winwidget winwid); +void winwidget_create_window(winwidget ret, int w, int h); +void winwidget_clear_background(winwidget w); +Pixmap feh_create_checks(void); +double feh_calc_needed_zoom(double *zoom, int orig_w, int orig_h, int dest_w, + + int dest_h); +void feh_debug_print_winwid(winwidget winwid); +winwidget winwidget_get_first_window_of_type(unsigned int type); +void winwidget_reset_image(winwidget winwid); +void winwidget_sanitise_offsets(winwidget winwid); +void winwidget_size_to_image(winwidget winwid); +void winwidget_render_image_cached(winwidget winwid); + +extern int window_num; /* For window list */ +extern winwidget *windows; /* List of windows to loop though */ + +#endif -- cgit v1.2.3