diff options
Diffstat (limited to 'src/imlib.c')
-rw-r--r-- | src/imlib.c | 1292 |
1 files changed, 1292 insertions, 0 deletions
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 <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <jpeglib.h> + +#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;i<num_actions;i++) + { +// compose a line containing an index, a colon followed by the +// action. + char index[1]; + char line[strlen(opt.actions[i])+5]; + sprintf(index, "%d", i); + strcpy(line, index); + strcat(line, ": "); + strcat(line, opt.actions[i]); + + gib_imlib_text_draw(im, fn, NULL, 2, ((i+1)*line_th)+2, line, + IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255); + gib_imlib_text_draw(im, fn, NULL, 1, ((i+1)*line_th)+1, line, + IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255); + free(line); + } + + 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); +} |