X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/383d6ffc5612a0113ffdac23a7e3dcfceb5100bd..f1d2ce7f2f7067d538ac590df4e9f4f0d2738dab:/src/xfns.c diff --git a/src/xfns.c b/src/xfns.c index 8ed9b7d98d..6e8931b42d 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -1,5 +1,5 @@ /* Functions for the X window system. - Copyright (C) 1989, 92, 93, 94, 95, 96, 1997, 1998, 1999 + Copyright (C) 1989, 92, 93, 94, 95, 96, 1997, 1998, 1999, 2000 Free Software Foundation. This file is part of GNU Emacs. @@ -39,6 +39,7 @@ Boston, MA 02111-1307, USA. */ #include "blockinput.h" #include #include "charset.h" +#include "coding.h" #include "fontset.h" #include "systime.h" #include "termhooks.h" @@ -123,7 +124,7 @@ extern double atof (); int gray_bitmap_width = gray_width; int gray_bitmap_height = gray_height; -unsigned char *gray_bitmap_bits = gray_bits; +char *gray_bitmap_bits = gray_bits; /* The name we're using in resource queries. Most often "emacs". */ @@ -148,6 +149,11 @@ Lisp_Object Vx_busy_pointer_shape; Lisp_Object Vx_sensitive_text_pointer_shape; +/* If non-nil, the pointer shape to indicate that windows can be + dragged horizontally. */ + +Lisp_Object Vx_window_horizontal_drag_shape; + /* Color of chars displayed in cursor box. */ Lisp_Object Vx_cursor_fore_pixel; @@ -236,7 +242,8 @@ Lisp_Object Quser_position; Lisp_Object Quser_size; extern Lisp_Object Qdisplay; Lisp_Object Qscroll_bar_foreground, Qscroll_bar_background; -Lisp_Object Qscreen_gamma; +Lisp_Object Qscreen_gamma, Qline_spacing, Qcenter; +Lisp_Object Qcompound_text; /* The below are defined in frame.c. */ @@ -248,6 +255,11 @@ extern Lisp_Object Vwindow_system_version; Lisp_Object Qface_set_after_frame_default; +#if GLYPH_DEBUG +int image_cache_refcount, dpyinfo_refcount; +#endif + + /* Error if we are not connected to X. */ @@ -293,19 +305,21 @@ static struct x_display_info * check_x_display_info (frame) Lisp_Object frame; { + struct x_display_info *dpyinfo = NULL; + if (NILP (frame)) { struct frame *sf = XFRAME (selected_frame); if (FRAME_X_P (sf) && FRAME_LIVE_P (sf)) - return FRAME_X_DISPLAY_INFO (sf); + dpyinfo = FRAME_X_DISPLAY_INFO (sf); else if (x_display_list != 0) - return x_display_list; + dpyinfo = x_display_list; else error ("X windows are not in use or not initialized"); } else if (STRINGP (frame)) - return x_display_info_for_name (frame); + dpyinfo = x_display_info_for_name (frame); else { FRAME_PTR f; @@ -314,8 +328,10 @@ check_x_display_info (frame) f = XFRAME (frame); if (! FRAME_X_P (f)) error ("Non-X frame used"); - return FRAME_X_DISPLAY_INFO (f); + dpyinfo = FRAME_X_DISPLAY_INFO (f); } + + return dpyinfo; } @@ -657,9 +673,6 @@ x_create_bitmap_from_file (f, file) fd = openp (Vx_bitmap_file_path, file, "", &found, 0); if (fd < 0) return -1; - /* XReadBitmapFile won't handle magic file names. */ - if (fd == 0) - return -1; emacs_close (fd); filename = (char *) XSTRING (found)->data; @@ -738,8 +751,13 @@ struct x_frame_parm_table void (*setter) P_ ((struct frame *, Lisp_Object, Lisp_Object)); }; +static Lisp_Object unwind_create_frame P_ ((Lisp_Object)); +static Lisp_Object unwind_create_tip_frame P_ ((Lisp_Object)); +static void x_change_window_heights P_ ((Lisp_Object, int)); +static void x_disable_image P_ ((struct frame *, struct image *)); static void x_create_im P_ ((struct frame *)); void x_set_foreground_color P_ ((struct frame *, Lisp_Object, Lisp_Object)); +static void x_set_line_spacing P_ ((struct frame *, Lisp_Object, Lisp_Object)); void x_set_background_color P_ ((struct frame *, Lisp_Object, Lisp_Object)); void x_set_mouse_color P_ ((struct frame *, Lisp_Object, Lisp_Object)); void x_set_cursor_color P_ ((struct frame *, Lisp_Object, Lisp_Object)); @@ -772,33 +790,43 @@ static Lisp_Object x_default_scroll_bar_color_parameter P_ ((struct frame *, char *, char *, int)); static void x_set_screen_gamma P_ ((struct frame *, Lisp_Object, Lisp_Object)); +static void x_edge_detection P_ ((struct frame *, struct image *, Lisp_Object, + Lisp_Object)); +static void init_color_table P_ ((void)); +static void free_color_table P_ ((void)); +static unsigned long *colors_in_color_table P_ ((int *n)); +static unsigned long lookup_rgb_color P_ ((struct frame *f, int r, int g, int b)); +static unsigned long lookup_pixel_color P_ ((struct frame *f, unsigned long p)); + + static struct x_frame_parm_table x_frame_parms[] = { - "auto-raise", x_set_autoraise, - "auto-lower", x_set_autolower, - "background-color", x_set_background_color, - "border-color", x_set_border_color, - "border-width", x_set_border_width, - "cursor-color", x_set_cursor_color, - "cursor-type", x_set_cursor_type, - "font", x_set_font, - "foreground-color", x_set_foreground_color, - "icon-name", x_set_icon_name, - "icon-type", x_set_icon_type, - "internal-border-width", x_set_internal_border_width, - "menu-bar-lines", x_set_menu_bar_lines, - "mouse-color", x_set_mouse_color, - "name", x_explicitly_set_name, - "scroll-bar-width", x_set_scroll_bar_width, - "title", x_set_title, - "unsplittable", x_set_unsplittable, - "vertical-scroll-bars", x_set_vertical_scroll_bars, - "visibility", x_set_visibility, - "tool-bar-lines", x_set_tool_bar_lines, - "scroll-bar-foreground", x_set_scroll_bar_foreground, - "scroll-bar-background", x_set_scroll_bar_background, - "screen-gamma", x_set_screen_gamma + "auto-raise", x_set_autoraise, + "auto-lower", x_set_autolower, + "background-color", x_set_background_color, + "border-color", x_set_border_color, + "border-width", x_set_border_width, + "cursor-color", x_set_cursor_color, + "cursor-type", x_set_cursor_type, + "font", x_set_font, + "foreground-color", x_set_foreground_color, + "icon-name", x_set_icon_name, + "icon-type", x_set_icon_type, + "internal-border-width", x_set_internal_border_width, + "menu-bar-lines", x_set_menu_bar_lines, + "mouse-color", x_set_mouse_color, + "name", x_explicitly_set_name, + "scroll-bar-width", x_set_scroll_bar_width, + "title", x_set_title, + "unsplittable", x_set_unsplittable, + "vertical-scroll-bars", x_set_vertical_scroll_bars, + "visibility", x_set_visibility, + "tool-bar-lines", x_set_tool_bar_lines, + "scroll-bar-foreground", x_set_scroll_bar_foreground, + "scroll-bar-background", x_set_scroll_bar_background, + "screen-gamma", x_set_screen_gamma, + "line-spacing", x_set_line_spacing }; /* Attach the `x-frame-parameter' properties to @@ -815,8 +843,10 @@ init_x_parm_symbols () } /* Change the parameters of frame F as specified by ALIST. - If a parameter is not specially recognized, do nothing; - otherwise call the `x_set_...' function for that parameter. */ + If a parameter is not specially recognized, do nothing special; + otherwise call the `x_set_...' function for that parameter. + Except for certain geometry properties, always call store_frame_param + to store the new value in the parameter alist. */ void x_set_frame_parameters (f, alist) @@ -1098,7 +1128,7 @@ x_real_positions (f, xptr, yptr) #endif Window tmp_root_window; Window *tmp_children; - int tmp_nchildren; + unsigned int tmp_nchildren; while (1) { @@ -1282,12 +1312,33 @@ x_decode_color (f, color_name, mono_color) if (x_defined_color (f, XSTRING (color_name)->data, &cdef, 1)) return cdef.pixel; - Fsignal (Qerror, Fcons (build_string ("undefined color"), + Fsignal (Qerror, Fcons (build_string ("Undefined color"), Fcons (color_name, Qnil))); + return 0; } +/* Change the `line-spacing' frame parameter of frame F. OLD_VALUE is + the previous value of that parameter, NEW_VALUE is the new value. */ + +static void +x_set_line_spacing (f, new_value, old_value) + struct frame *f; + Lisp_Object new_value, old_value; +{ + if (NILP (new_value)) + f->extra_line_spacing = 0; + else if (NATNUMP (new_value)) + f->extra_line_spacing = XFASTINT (new_value); + else + Fsignal (Qerror, Fcons (build_string ("Invalid line-spacing"), + Fcons (new_value, Qnil))); + if (FRAME_VISIBLE_P (f)) + redraw_frame (f); +} + + /* Change the `screen-gamma' frame parameter of frame F. OLD_VALUE is the previous value of that parameter, NEW_VALUE is the new value. */ @@ -1302,7 +1353,7 @@ x_set_screen_gamma (f, new_value, old_value) /* The value 0.4545 is the normal viewing gamma. */ f->gamma = 1.0 / (0.4545 * XFLOATINT (new_value)); else - Fsignal (Qerror, Fcons (build_string ("Illegal screen-gamma"), + Fsignal (Qerror, Fcons (build_string ("Invalid screen-gamma"), Fcons (new_value, Qnil))); clear_face_cache (0); @@ -1388,7 +1439,7 @@ x_set_mouse_color (f, arg, oldval) Lisp_Object arg, oldval; { Cursor cursor, nontext_cursor, mode_cursor, cross_cursor; - Cursor busy_cursor; + Cursor busy_cursor, horizontal_drag_cursor; int count; unsigned long pixel = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); unsigned long mask_color = f->output_data.x->background_pixel; @@ -1456,6 +1507,17 @@ x_set_mouse_color (f, arg, oldval) else cross_cursor = XCreateFontCursor (FRAME_X_DISPLAY (f), XC_crosshair); + if (!NILP (Vx_window_horizontal_drag_shape)) + { + CHECK_NUMBER (Vx_window_horizontal_drag_shape, 0); + horizontal_drag_cursor + = XCreateFontCursor (FRAME_X_DISPLAY (f), + XINT (Vx_window_horizontal_drag_shape)); + } + else + horizontal_drag_cursor + = XCreateFontCursor (FRAME_X_DISPLAY (f), XC_sb_h_double_arrow); + /* Check and report errors with the above calls. */ x_check_errors (FRAME_X_DISPLAY (f), "can't set cursor shape: %s"); x_uncatch_errors (FRAME_X_DISPLAY (f), count); @@ -1464,11 +1526,10 @@ x_set_mouse_color (f, arg, oldval) XColor fore_color, back_color; fore_color.pixel = f->output_data.x->mouse_pixel; + x_query_color (f, &fore_color); back_color.pixel = mask_color; - XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), - &fore_color); - XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), - &back_color); + x_query_color (f, &back_color); + XRecolorCursor (FRAME_X_DISPLAY (f), cursor, &fore_color, &back_color); XRecolorCursor (FRAME_X_DISPLAY (f), nontext_cursor, @@ -1476,15 +1537,18 @@ x_set_mouse_color (f, arg, oldval) XRecolorCursor (FRAME_X_DISPLAY (f), mode_cursor, &fore_color, &back_color); XRecolorCursor (FRAME_X_DISPLAY (f), cross_cursor, - &fore_color, &back_color); + &fore_color, &back_color); XRecolorCursor (FRAME_X_DISPLAY (f), busy_cursor, &fore_color, &back_color); + XRecolorCursor (FRAME_X_DISPLAY (f), horizontal_drag_cursor, + &fore_color, &back_color); } if (FRAME_X_WINDOW (f) != 0) XDefineCursor (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), cursor); - if (cursor != f->output_data.x->text_cursor && f->output_data.x->text_cursor != 0) + if (cursor != f->output_data.x->text_cursor + && f->output_data.x->text_cursor != 0) XFreeCursor (FRAME_X_DISPLAY (f), f->output_data.x->text_cursor); f->output_data.x->text_cursor = cursor; @@ -1508,6 +1572,11 @@ x_set_mouse_color (f, arg, oldval) XFreeCursor (FRAME_X_DISPLAY (f), f->output_data.x->cross_cursor); f->output_data.x->cross_cursor = cross_cursor; + if (horizontal_drag_cursor != f->output_data.x->horizontal_drag_cursor + && f->output_data.x->horizontal_drag_cursor != 0) + XFreeCursor (FRAME_X_DISPLAY (f), f->output_data.x->horizontal_drag_cursor); + f->output_data.x->horizontal_drag_cursor = horizontal_drag_cursor; + XFlush (FRAME_X_DISPLAY (f)); UNBLOCK_INPUT; @@ -1520,26 +1589,49 @@ x_set_cursor_color (f, arg, oldval) Lisp_Object arg, oldval; { unsigned long fore_pixel, pixel; + int fore_pixel_allocated_p = 0, pixel_allocated_p = 0; - if (!EQ (Vx_cursor_fore_pixel, Qnil)) - fore_pixel = x_decode_color (f, Vx_cursor_fore_pixel, - WHITE_PIX_DEFAULT (f)); + if (!NILP (Vx_cursor_fore_pixel)) + { + fore_pixel = x_decode_color (f, Vx_cursor_fore_pixel, + WHITE_PIX_DEFAULT (f)); + fore_pixel_allocated_p = 1; + } else fore_pixel = f->output_data.x->background_pixel; + pixel = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); + pixel_allocated_p = 1; /* Make sure that the cursor color differs from the background color. */ if (pixel == f->output_data.x->background_pixel) { + if (pixel_allocated_p) + { + x_free_colors (f, &pixel, 1); + pixel_allocated_p = 0; + } + pixel = f->output_data.x->mouse_pixel; if (pixel == fore_pixel) - fore_pixel = f->output_data.x->background_pixel; + { + if (fore_pixel_allocated_p) + { + x_free_colors (f, &fore_pixel, 1); + fore_pixel_allocated_p = 0; + } + fore_pixel = f->output_data.x->background_pixel; + } } unload_color (f, f->output_data.x->cursor_foreground_pixel); + if (!fore_pixel_allocated_p) + fore_pixel = x_copy_color (f, fore_pixel); f->output_data.x->cursor_foreground_pixel = fore_pixel; unload_color (f, f->output_data.x->cursor_pixel); + if (!pixel_allocated_p) + pixel = x_copy_color (f, pixel); f->output_data.x->cursor_pixel = pixel; if (FRAME_X_WINDOW (f) != 0) @@ -1609,31 +1701,51 @@ x_set_border_pixel (f, pix) } } -void -x_set_cursor_type (f, arg, oldval) - FRAME_PTR f; - Lisp_Object arg, oldval; + +/* Value is the internal representation of the specified cursor type + ARG. If type is BAR_CURSOR, return in *WIDTH the specified width + of the bar cursor. */ + +enum text_cursor_kinds +x_specified_cursor_type (arg, width) + Lisp_Object arg; + int *width; { + enum text_cursor_kinds type; + if (EQ (arg, Qbar)) { - FRAME_DESIRED_CURSOR (f) = BAR_CURSOR; - f->output_data.x->cursor_width = 2; + type = BAR_CURSOR; + *width = 2; } else if (CONSP (arg) && EQ (XCAR (arg), Qbar) && INTEGERP (XCDR (arg)) && XINT (XCDR (arg)) >= 0) { - FRAME_DESIRED_CURSOR (f) = BAR_CURSOR; - f->output_data.x->cursor_width = XINT (XCDR (arg)); + type = BAR_CURSOR; + *width = XINT (XCDR (arg)); } else if (NILP (arg)) - FRAME_DESIRED_CURSOR (f) = NO_CURSOR; + type = NO_CURSOR; else /* Treat anything unknown as "box cursor". It was bad to signal an error; people have trouble fixing .Xdefaults with Emacs, when it has something bad in it. */ - FRAME_DESIRED_CURSOR (f) = FILLED_BOX_CURSOR; + type = FILLED_BOX_CURSOR; + + return type; +} + +void +x_set_cursor_type (f, arg, oldval) + FRAME_PTR f; + Lisp_Object arg, oldval; +{ + int width; + + FRAME_DESIRED_CURSOR (f) = x_specified_cursor_type (arg, &width); + f->output_data.x->cursor_width = width; /* Make sure the cursor gets redrawn. This is overkill, but how often do people change cursor types? */ @@ -1832,9 +1944,12 @@ x_set_visibility (f, value, oldval) else Fmake_frame_visible (frame); } + +/* Change window heights in windows rooted in WINDOW by N lines. */ + static void -x_set_menu_bar_lines_1 (window, n) +x_change_window_heights (window, n) Lisp_Object window; int n; { @@ -1850,13 +1965,13 @@ x_set_menu_bar_lines_1 (window, n) /* Handle just the top child in a vertical split. */ if (!NILP (w->vchild)) - x_set_menu_bar_lines_1 (w->vchild, n); + x_change_window_heights (w->vchild, n); /* Adjust all children in a horizontal split. */ for (window = w->hchild; !NILP (window); window = w->next) { w = XWINDOW (window); - x_set_menu_bar_lines_1 (window, n); + x_change_window_heights (window, n); } } @@ -1904,7 +2019,7 @@ x_set_menu_bar_lines (f, value, oldval) } #else /* not USE_X_TOOLKIT */ FRAME_MENU_BAR_LINES (f) = nlines; - x_set_menu_bar_lines_1 (f->root_window, nlines - olines); + x_change_window_heights (f->root_window, nlines - olines); #endif /* not USE_X_TOOLKIT */ adjust_glyphs (f); } @@ -1921,7 +2036,8 @@ x_set_tool_bar_lines (f, value, oldval) struct frame *f; Lisp_Object value, oldval; { - int delta, nlines; + int delta, nlines, root_height; + Lisp_Object root_window; /* Use VALUE only if an integer >= 0. */ if (INTEGERP (value) && XINT (value) >= 0) @@ -1933,9 +2049,48 @@ x_set_tool_bar_lines (f, value, oldval) ++windows_or_buffers_changed; delta = nlines - FRAME_TOOL_BAR_LINES (f); + + /* Don't resize the tool-bar to more than we have room for. */ + root_window = FRAME_ROOT_WINDOW (f); + root_height = XINT (XWINDOW (root_window)->height); + if (root_height - delta < 1) + { + delta = root_height - 1; + nlines = FRAME_TOOL_BAR_LINES (f) + delta; + } + FRAME_TOOL_BAR_LINES (f) = nlines; - x_set_menu_bar_lines_1 (FRAME_ROOT_WINDOW (f), delta); + x_change_window_heights (root_window, delta); adjust_glyphs (f); + + /* We also have to make sure that the internal border at the top of + the frame, below the menu bar or tool bar, is redrawn when the + tool bar disappears. This is so because the internal border is + below the tool bar if one is displayed, but is below the menu bar + if there isn't a tool bar. The tool bar draws into the area + below the menu bar. */ + if (FRAME_X_WINDOW (f) && FRAME_TOOL_BAR_LINES (f) == 0) + { + updating_frame = f; + clear_frame (); + clear_current_matrices (f); + updating_frame = NULL; + } + + /* If the tool bar gets smaller, the internal border below it + has to be cleared. It was formerly part of the display + of the larger tool bar, and updating windows won't clear it. */ + if (delta < 0) + { + int height = FRAME_INTERNAL_BORDER_WIDTH (f); + int width = PIXEL_WIDTH (f); + int y = nlines * CANON_Y_UNIT (f); + + BLOCK_INPUT; + XClearArea (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + 0, y, width, height, False); + UNBLOCK_INPUT; + } } @@ -2008,6 +2163,60 @@ x_set_scroll_bar_background (f, value, oldval) } } + +/* Encode Lisp string STRING as a text in a format appropriate for + XICCC (X Inter Client Communication Conventions). + + If STRING contains only ASCII characters, do no conversion and + return the string data of STRING. Otherwise, encode the text by + CODING_SYSTEM, and return a newly allocated memory area which + should be freed by `xfree' by a caller. + + Store the byte length of resulting text in *TEXT_BYTES. + + If the text contains only ASCII and Latin-1, store 1 in *STRING_P, + which means that the `encoding' of the result can be `STRING'. + Otherwise store 0 in *STRINGP, which means that the `encoding' of + the result should be `COMPOUND_TEXT'. */ + +unsigned char * +x_encode_text (string, coding_system, text_bytes, stringp) + Lisp_Object string, coding_system; + int *text_bytes, *stringp; +{ + unsigned char *str = XSTRING (string)->data; + int chars = XSTRING (string)->size; + int bytes = STRING_BYTES (XSTRING (string)); + int charset_info; + int bufsize; + unsigned char *buf; + struct coding_system coding; + + charset_info = find_charset_in_text (str, chars, bytes, NULL, Qnil); + if (charset_info == 0) + { + /* No multibyte character in OBJ. We need not encode it. */ + *text_bytes = bytes; + *stringp = 1; + return str; + } + + setup_coding_system (coding_system, &coding); + coding.src_multibyte = 1; + coding.dst_multibyte = 0; + coding.mode |= CODING_MODE_LAST_BLOCK; + if (coding.type == coding_type_iso2022) + coding.flags |= CODING_FLAG_ISO_SAFE; + /* We suppress producing escape sequences for composition. */ + coding.composing = COMPOSITION_DISABLED; + bufsize = encoding_buffer_size (&coding, bytes); + buf = (unsigned char *) xmalloc (bufsize); + encode_coding (&coding, str, buf, bytes, bufsize); + *text_bytes = coding.produced; + *stringp = (charset_info == 1 || !EQ (coding_system, Qcompound_text)); + return buf; +} + /* Change the name of frame F to NAME. If NAME is nil, set F's name to x_id_name. @@ -2070,19 +2279,31 @@ x_set_name (f, name, explicit) #ifdef HAVE_X11R4 { XTextProperty text, icon; - Lisp_Object icon_name; - - text.value = XSTRING (name)->data; - text.encoding = XA_STRING; + int bytes, stringp; + Lisp_Object coding_system; + + coding_system = Vlocale_coding_system; + if (NILP (coding_system)) + coding_system = Qcompound_text; + text.value = x_encode_text (name, coding_system, &bytes, &stringp); + text.encoding = (stringp ? XA_STRING + : FRAME_X_DISPLAY_INFO (f)->Xatom_COMPOUND_TEXT); text.format = 8; - text.nitems = STRING_BYTES (XSTRING (name)); + text.nitems = bytes; - icon_name = (!NILP (f->icon_name) ? f->icon_name : name); - - icon.value = XSTRING (icon_name)->data; - icon.encoding = XA_STRING; - icon.format = 8; - icon.nitems = STRING_BYTES (XSTRING (icon_name)); + if (NILP (f->icon_name)) + { + icon = text; + } + else + { + icon.value = x_encode_text (f->icon_name, coding_system, + &bytes, &stringp); + icon.encoding = (stringp ? XA_STRING + : FRAME_X_DISPLAY_INFO (f)->Xatom_COMPOUND_TEXT); + icon.format = 8; + icon.nitems = bytes; + } #ifdef USE_X_TOOLKIT XSetWMName (FRAME_X_DISPLAY (f), XtWindow (f->output_data.x->widget), &text); @@ -2092,6 +2313,11 @@ x_set_name (f, name, explicit) XSetWMName (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &text); XSetWMIconName (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &icon); #endif /* not USE_X_TOOLKIT */ + if (!NILP (f->icon_name) + && icon.value != XSTRING (f->icon_name)->data) + xfree (icon.value); + if (text.value != XSTRING (name)->data) + xfree (text.value); } #else /* not HAVE_X11R4 */ XSetIconName (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), @@ -2160,19 +2386,31 @@ x_set_title (f, name, old_name) #ifdef HAVE_X11R4 { XTextProperty text, icon; - Lisp_Object icon_name; - - text.value = XSTRING (name)->data; - text.encoding = XA_STRING; + int bytes, stringp; + Lisp_Object coding_system; + + coding_system = Vlocale_coding_system; + if (NILP (coding_system)) + coding_system = Qcompound_text; + text.value = x_encode_text (name, coding_system, &bytes, &stringp); + text.encoding = (stringp ? XA_STRING + : FRAME_X_DISPLAY_INFO (f)->Xatom_COMPOUND_TEXT); text.format = 8; - text.nitems = STRING_BYTES (XSTRING (name)); - - icon_name = (!NILP (f->icon_name) ? f->icon_name : name); + text.nitems = bytes; - icon.value = XSTRING (icon_name)->data; - icon.encoding = XA_STRING; - icon.format = 8; - icon.nitems = STRING_BYTES (XSTRING (icon_name)); + if (NILP (f->icon_name)) + { + icon = text; + } + else + { + icon.value = x_encode_text (f->icon_name, coding_system, + &bytes, &stringp); + icon.encoding = (stringp ? XA_STRING + : FRAME_X_DISPLAY_INFO (f)->Xatom_COMPOUND_TEXT); + icon.format = 8; + icon.nitems = bytes; + } #ifdef USE_X_TOOLKIT XSetWMName (FRAME_X_DISPLAY (f), XtWindow (f->output_data.x->widget), &text); @@ -2182,6 +2420,11 @@ x_set_title (f, name, old_name) XSetWMName (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &text); XSetWMIconName (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &icon); #endif /* not USE_X_TOOLKIT */ + if (!NILP (f->icon_name) + && icon.value != XSTRING (f->icon_name)->data) + xfree (icon.value); + if (text.value != XSTRING (name)->data) + xfree (text.value); } #else /* not HAVE_X11R4 */ XSetIconName (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), @@ -3093,22 +3336,39 @@ create_frame_xic (f) base_fontname = "-*-*-*-r-normal--14-*-*-*-*-*-*-*"; else { - struct fontset_info *fontsetp; - int len = 0; + /* Determine the base fontname from the ASCII font name of + FONTSET. */ + char *ascii_font = (char *) XSTRING (fontset_ascii (fontset))->data; + char *p = ascii_font; int i; - - fontsetp = FRAME_FONTSET_DATA (f)->fontset_table[fontset]; - for (i = 0; i <= MAX_CHARSET; i++) - if (fontsetp->fontname[i]) - len += strlen (fontsetp->fontname[i]) + 1; - base_fontname = alloca (len); - strcpy (base_fontname, fontsetp->fontname[CHARSET_ASCII]); - for (i = MIN_CHARSET_OFFICIAL_DIMENSION1; i <= MAX_CHARSET; i++) - if (fontsetp->fontname[i]) - { - strcat (base_fontname, ","); - strcat (base_fontname, fontsetp->fontname[i]); - } + + for (i = 0; *p; p++) + if (*p == '-') i++; + if (i != 14) + /* As the font name doesn't conform to XLFD, we can't + modify it to get a suitable base fontname for the + frame. */ + base_fontname = "-*-*-*-r-normal--14-*-*-*-*-*-*-*"; + else + { + int len = strlen (ascii_font) + 1; + char *p1 = NULL; + + for (i = 0, p = ascii_font; i < 8; p++) + { + if (*p == '-') + { + i++; + if (i == 3) + p1 = p + 1; + } + } + base_fontname = (char *) alloca (len); + bzero (base_fontname, len); + strcpy (base_fontname, "-*-*-"); + bcopy (p1, base_fontname + 5, p - p1); + strcat (base_fontname, "*-*-*-*-*-*-*"); + } } xfs = xic_create_xfontset (f, base_fontname); @@ -3438,7 +3698,9 @@ x_window (f, window_prompting, minibuffer_only) #ifdef HAVE_X_I18N FRAME_XIC (f) = NULL; +#ifdef USE_XIM create_frame_xic (f); +#endif #endif f->output_data.x->wm_hints.input = True; @@ -3541,6 +3803,7 @@ x_window (f) attribute_mask, &attributes); #ifdef HAVE_X_I18N +#ifdef USE_XIM create_frame_xic (f); if (FRAME_XIC (f)) { @@ -3552,6 +3815,7 @@ x_window (f) XChangeWindowAttributes (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), attribute_mask, &attributes); } +#endif #endif /* HAVE_X_I18N */ validate_x_resource_name (); @@ -3651,7 +3915,7 @@ x_icon (f, parms) UNBLOCK_INPUT; } -/* Make the GC's needed for this window, setting the +/* Make the GCs needed for this window, setting the background, border and mouse colors; also create the mouse cursor and the gray border tile. */ @@ -3671,7 +3935,7 @@ x_make_gc (f) BLOCK_INPUT; - /* Create the GC's of this frame. + /* Create the GCs of this frame. Note that many default values are used. */ /* Normal video */ @@ -3679,20 +3943,20 @@ x_make_gc (f) gc_values.foreground = f->output_data.x->foreground_pixel; gc_values.background = f->output_data.x->background_pixel; gc_values.line_width = 0; /* Means 1 using fast algorithm. */ - f->output_data.x->normal_gc = XCreateGC (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - GCLineWidth | GCFont - | GCForeground | GCBackground, - &gc_values); + f->output_data.x->normal_gc + = XCreateGC (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + GCLineWidth | GCFont | GCForeground | GCBackground, + &gc_values); /* Reverse video style. */ gc_values.foreground = f->output_data.x->background_pixel; gc_values.background = f->output_data.x->foreground_pixel; - f->output_data.x->reverse_gc = XCreateGC (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - GCFont | GCForeground | GCBackground - | GCLineWidth, - &gc_values); + f->output_data.x->reverse_gc + = XCreateGC (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + GCFont | GCForeground | GCBackground | GCLineWidth, + &gc_values); /* Cursor has cursor-color background, background-color foreground. */ gc_values.foreground = f->output_data.x->background_pixel; @@ -3727,6 +3991,74 @@ x_make_gc (f) UNBLOCK_INPUT; } + +/* Free what was was allocated in x_make_gc. */ + +void +x_free_gcs (f) + struct frame *f; +{ + Display *dpy = FRAME_X_DISPLAY (f); + + BLOCK_INPUT; + + if (f->output_data.x->normal_gc) + { + XFreeGC (dpy, f->output_data.x->normal_gc); + f->output_data.x->normal_gc = 0; + } + + if (f->output_data.x->reverse_gc) + { + XFreeGC (dpy, f->output_data.x->reverse_gc); + f->output_data.x->reverse_gc = 0; + } + + if (f->output_data.x->cursor_gc) + { + XFreeGC (dpy, f->output_data.x->cursor_gc); + f->output_data.x->cursor_gc = 0; + } + + if (f->output_data.x->border_tile) + { + XFreePixmap (dpy, f->output_data.x->border_tile); + f->output_data.x->border_tile = 0; + } + + UNBLOCK_INPUT; +} + + +/* Handler for signals raised during x_create_frame and + x_create_top_frame. FRAME is the frame which is partially + constructed. */ + +static Lisp_Object +unwind_create_frame (frame) + Lisp_Object frame; +{ + struct frame *f = XFRAME (frame); + + /* If frame is ``official'', nothing to do. */ + if (!CONSP (Vframe_list) || !EQ (XCAR (Vframe_list), frame)) + { +#if GLYPH_DEBUG + struct x_display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f); +#endif + + x_free_frame_resources (f); + + /* Check that reference counts are indeed correct. */ + xassert (dpyinfo->reference_count == dpyinfo_refcount); + xassert (dpyinfo->image_cache->refcount == image_cache_refcount); + return Qt; + } + + return Qnil; +} + + DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, 1, 1, 0, "Make a new X window, which is called a \"frame\" in Emacs terms.\n\ @@ -3747,7 +4079,7 @@ This function is an internal primitive--use `make-frame' instead.") int minibuffer_only = 0; long window_prompting = 0; int width, height; - int count = specpdl_ptr - specpdl; + int count = BINDING_STACK_SIZE (); struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; Lisp_Object display; struct x_display_info *dpyinfo = NULL; @@ -3817,6 +4149,7 @@ This function is an internal primitive--use `make-frame' instead.") f->output_data.x->fontset = -1; f->output_data.x->scroll_bar_foreground_pixel = -1; f->output_data.x->scroll_bar_background_pixel = -1; + record_unwind_protect (unwind_create_frame, frame); f->icon_name = x_get_arg (dpyinfo, parms, Qicon_name, "iconName", "Title", @@ -3825,6 +4158,10 @@ This function is an internal primitive--use `make-frame' instead.") f->icon_name = Qnil; FRAME_X_DISPLAY_INFO (f) = dpyinfo; +#if GLYPH_DEBUG + image_cache_refcount = FRAME_X_IMAGE_CACHE (f)->refcount; + dpyinfo_refcount = dpyinfo->reference_count; +#endif /* GLYPH_DEBUG */ #ifdef MULTI_KBOARD FRAME_KBOARD (f) = kb; #endif @@ -3880,10 +4217,6 @@ This function is an internal primitive--use `make-frame' instead.") specbind (Qx_resource_name, name); } - /* Create fontsets from `global_fontset_alist' before handling fonts. */ - for (tem = Vglobal_fontset_alist; CONSP (tem); tem = XCDR (tem)) - fs_register_fontset (f, XCAR (tem)); - /* Extract the window parameters from the supplied values that are needed to determine window geometry. */ { @@ -3966,6 +4299,8 @@ This function is an internal primitive--use `make-frame' instead.") "borderColor", "BorderColor", RES_TYPE_STRING); x_default_parameter (f, parms, Qscreen_gamma, Qnil, "screenGamma", "ScreenGamma", RES_TYPE_FLOAT); + x_default_parameter (f, parms, Qline_spacing, Qnil, + "lineSpacing", "LineSpacing", RES_TYPE_NUMBER); x_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_foreground, "scrollBarForeground", @@ -3984,7 +4319,7 @@ This function is an internal primitive--use `make-frame' instead.") x_default_parameter (f, parms, Qmenu_bar_lines, make_number (1), "menuBar", "MenuBar", RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qtool_bar_lines, make_number (0), + x_default_parameter (f, parms, Qtool_bar_lines, make_number (1), "toolBar", "ToolBar", RES_TYPE_NUMBER); x_default_parameter (f, parms, Qbuffer_predicate, Qnil, "bufferPredicate", "BufferPredicate", @@ -4060,7 +4395,11 @@ This function is an internal primitive--use `make-frame' instead.") SET_FRAME_WIDTH (f, 0); change_frame_size (f, height, width, 1, 0, 0); - /* Set up faces after all frame parameters are known. */ + /* Set up faces after all frame parameters are known. This call + also merges in face attributes specified for new frames. If we + don't do this, the `menu' face for instance won't have the right + colors, and the menu bar won't appear in the specified colors for + new frames. */ call1 (Qface_set_after_frame_default, frame); #ifdef USE_X_TOOLKIT @@ -4111,6 +4450,7 @@ This function is an internal primitive--use `make-frame' instead.") return unbind_to (count, frame); } + /* FRAME is used only to get a handle on the X display. We don't pass the display info directly because we're called from frame.c, which doesn't know about that structure. */ @@ -4407,21 +4747,28 @@ If omitted or nil, that stands for the selected frame's display.") Lisp_Object display; { struct x_display_info *dpyinfo = check_x_display_info (display); + Lisp_Object result; switch (DoesBackingStore (dpyinfo->screen)) { case Always: - return intern ("always"); + result = intern ("always"); + break; case WhenMapped: - return intern ("when-mapped"); + result = intern ("when-mapped"); + break; case NotUseful: - return intern ("not-useful"); + result = intern ("not-useful"); + break; default: error ("Strange value for BackingStore parameter of screen"); + result = Qnil; } + + return result; } DEFUN ("x-display-visual-class", Fx_display_visual_class, @@ -4436,18 +4783,34 @@ If omitted or nil, that stands for the selected frame's display.") Lisp_Object display; { struct x_display_info *dpyinfo = check_x_display_info (display); + Lisp_Object result; switch (dpyinfo->visual->class) { - case StaticGray: return (intern ("static-gray")); - case GrayScale: return (intern ("gray-scale")); - case StaticColor: return (intern ("static-color")); - case PseudoColor: return (intern ("pseudo-color")); - case TrueColor: return (intern ("true-color")); - case DirectColor: return (intern ("direct-color")); + case StaticGray: + result = intern ("static-gray"); + break; + case GrayScale: + result = intern ("gray-scale"); + break; + case StaticColor: + result = intern ("static-color"); + break; + case PseudoColor: + result = intern ("pseudo-color"); + break; + case TrueColor: + result = intern ("true-color"); + break; + case DirectColor: + result = intern ("direct-color"); + break; default: error ("Display has an unknown visual class"); + result = Qnil; } + + return result; } DEFUN ("x-display-save-under", Fx_display_save_under, @@ -4622,20 +4985,7 @@ select_visual (dpyinfo) if (n_visuals != 1) fatal ("Can't get proper X visual info"); - if ((1 << vinfo->depth) == vinfo->colormap_size) - dpyinfo->n_planes = vinfo->depth; - else - { - int i = 0; - int n = vinfo->colormap_size - 1; - while (n) - { - n = n >> 1; - i++; - } - dpyinfo->n_planes = i; - } - + dpyinfo->n_planes = vinfo->depth; XFree ((char *) vinfo); } } @@ -4752,9 +5102,9 @@ If DISPLAY is nil, that stands for the selected frame's display.") for (i = 0; i < dpyinfo->n_fonts; i++) if (dpyinfo->font_table[i].name) { + if (dpyinfo->font_table[i].name != dpyinfo->font_table[i].full_name) + xfree (dpyinfo->font_table[i].full_name); xfree (dpyinfo->font_table[i].name); - /* Don't free the full_name string; - it is always shared with something else. */ XFreeFont (dpyinfo->display, dpyinfo->font_table[i].font); } @@ -4830,10 +5180,6 @@ x_sync (f) static struct image_type *image_types; -/* A list of symbols, one for each supported image type. */ - -Lisp_Object Vimage_types; - /* The symbol `image' which is the car of the lists used to represent images in Lisp. */ @@ -4849,11 +5195,11 @@ extern Lisp_Object QCwidth, QCheight, QCforeground, QCbackground, QCfile; extern Lisp_Object QCdata; Lisp_Object QCtype, QCascent, QCmargin, QCrelief; Lisp_Object QCalgorithm, QCcolor_symbols, QCheuristic_mask; -Lisp_Object QCindex; +Lisp_Object QCindex, QCmatrix, QCcolor_adjustment, QCmask; /* Other symbols. */ -Lisp_Object Qlaplace; +Lisp_Object Qlaplace, Qemboss, Qedge_detection, Qheuristic; /* Time in seconds after which images should be removed from the cache if not displayed. */ @@ -4866,6 +5212,7 @@ static void define_image_type P_ ((struct image_type *type)); static struct image_type *lookup_image_type P_ ((Lisp_Object symbol)); static void image_error P_ ((char *format, Lisp_Object, Lisp_Object)); static void x_laplace P_ ((struct frame *, struct image *)); +static void x_emboss P_ ((struct frame *, struct image *)); static int x_build_heuristic_mask P_ ((struct frame *, struct image *, Lisp_Object)); @@ -4957,6 +5304,7 @@ enum image_value_type IMAGE_SYMBOL_VALUE, IMAGE_POSITIVE_INTEGER_VALUE, IMAGE_NON_NEGATIVE_INTEGER_VALUE, + IMAGE_ASCENT_VALUE, IMAGE_INTEGER_VALUE, IMAGE_FUNCTION_VALUE, IMAGE_NUMBER_VALUE, @@ -5059,6 +5407,15 @@ parse_image_spec (spec, keywords, nkeywords, type) return 0; break; + case IMAGE_ASCENT_VALUE: + if (SYMBOLP (value) && EQ (value, Qcenter)) + break; + else if (INTEGERP (value) + && XINT (value) >= 0 + && XINT (value) <= 100) + break; + return 0; + case IMAGE_NON_NEGATIVE_INTEGER_VALUE: if (!INTEGERP (value) || XINT (value) < 0) return 0; @@ -5139,6 +5496,63 @@ image_spec_value (spec, key, found) } +DEFUN ("image-size", Fimage_size, Simage_size, 1, 3, 0, + "Return the size of image SPEC as pair (WIDTH . HEIGHT).\n\ +PIXELS non-nil means return the size in pixels, otherwise return the\n\ +size in canonical character units.\n\ +FRAME is the frame on which the image will be displayed. FRAME nil\n\ +or omitted means use the selected frame.") + (spec, pixels, frame) + Lisp_Object spec, pixels, frame; +{ + Lisp_Object size; + + size = Qnil; + if (valid_image_p (spec)) + { + struct frame *f = check_x_frame (frame); + int id = lookup_image (f, spec); + struct image *img = IMAGE_FROM_ID (f, id); + int width = img->width + 2 * img->margin; + int height = img->height + 2 * img->margin; + + if (NILP (pixels)) + size = Fcons (make_float ((double) width / CANON_X_UNIT (f)), + make_float ((double) height / CANON_Y_UNIT (f))); + else + size = Fcons (make_number (width), make_number (height)); + } + else + error ("Invalid image specification"); + + return size; +} + + +DEFUN ("image-mask-p", Fimage_mask_p, Simage_mask_p, 1, 2, 0, + "Return t if image SPEC has a mask bitmap.\n\ +FRAME is the frame on which the image will be displayed. FRAME nil\n\ +or omitted means use the selected frame.") + (spec, frame) + Lisp_Object spec, frame; +{ + Lisp_Object mask; + + mask = Qnil; + if (valid_image_p (spec)) + { + struct frame *f = check_x_frame (frame); + int id = lookup_image (f, spec); + struct image *img = IMAGE_FROM_ID (f, id); + if (img->mask) + mask = Qt; + } + else + error ("Invalid image specification"); + + return mask; +} + /*********************************************************************** @@ -5216,49 +5630,99 @@ prepare_image_for_display (f, img) /* If IMG doesn't have a pixmap yet, load it now, using the image type dependent loader function. */ - if (img->pixmap == 0 && !img->load_failed_p) + if (img->pixmap == None && !img->load_failed_p) img->load_failed_p = img->type->load (f, img) == 0; } +/* Value is the number of pixels for the ascent of image IMG when + drawn in face FACE. */ + +int +image_ascent (img, face) + struct image *img; + struct face *face; +{ + int height = img->height + img->margin; + int ascent; + + if (img->ascent == CENTERED_IMAGE_ASCENT) + { + if (face->font) + /* This expression is arranged so that if the image can't be + exactly centered, it will be moved slightly up. This is + because a typical font is `top-heavy' (due to the presence + uppercase letters), so the image placement should err towards + being top-heavy too. It also just generally looks better. */ + ascent = (height + face->font->ascent - face->font->descent + 1) / 2; + else + ascent = height / 2; + } + else + ascent = height * img->ascent / 100.0; + + return ascent; +} + + /*********************************************************************** Helper functions for X image types ***********************************************************************/ +static void x_clear_image_1 P_ ((struct frame *, struct image *, int, + int, int)); static void x_clear_image P_ ((struct frame *f, struct image *img)); static unsigned long x_alloc_image_color P_ ((struct frame *f, struct image *img, Lisp_Object color_name, unsigned long dflt)); -/* Free X resources of image IMG which is used on frame F. */ + +/* Clear X resources of image IMG on frame F. PIXMAP_P non-zero means + free the pixmap if any. MASK_P non-zero means clear the mask + pixmap if any. COLORS_P non-zero means free colors allocated for + the image, if any. */ static void -x_clear_image (f, img) +x_clear_image_1 (f, img, pixmap_p, mask_p, colors_p) struct frame *f; struct image *img; + int pixmap_p, mask_p, colors_p; { - if (img->pixmap) + if (pixmap_p && img->pixmap) { - BLOCK_INPUT; XFreePixmap (FRAME_X_DISPLAY (f), img->pixmap); - img->pixmap = 0; - UNBLOCK_INPUT; + img->pixmap = None; + } + + if (mask_p && img->mask) + { + XFreePixmap (FRAME_X_DISPLAY (f), img->mask); + img->mask = None; } - if (img->ncolors) + if (colors_p && img->ncolors) { - BLOCK_INPUT; x_free_colors (f, img->colors, img->ncolors); - UNBLOCK_INPUT; - xfree (img->colors); img->colors = NULL; img->ncolors = 0; } } +/* Free X resources of image IMG which is used on frame F. */ + +static void +x_clear_image (f, img) + struct frame *f; + struct image *img; +{ + BLOCK_INPUT; + x_clear_image_1 (f, img, 1, 1, 1); + UNBLOCK_INPUT; +} + /* Allocate color COLOR_NAME for image IMG on frame F. If color cannot be allocated, use DFLT. Add a newly allocated color to @@ -5340,8 +5804,8 @@ free_image_cache (f) for (i = 0; i < c->used; ++i) free_image (f, c->images[i]); xfree (c->images); - xfree (c); xfree (c->buckets); + xfree (c); FRAME_X_IMAGE_CACHE (f) = NULL; } } @@ -5365,20 +5829,23 @@ clear_image_cache (f, force_p) { EMACS_TIME t; unsigned long old; - int i, any_freed_p = 0; + int i, nfreed; EMACS_GET_TIME (t); old = EMACS_SECS (t) - XFASTINT (Vimage_cache_eviction_delay); + + /* Block input so that we won't be interrupted by a SIGIO + while being in an inconsistent state. */ + BLOCK_INPUT; - for (i = 0; i < c->used; ++i) + for (i = nfreed = 0; i < c->used; ++i) { struct image *img = c->images[i]; if (img != NULL - && (force_p - || (img->timestamp > old))) + && (force_p || img->timestamp < old)) { free_image (f, img); - any_freed_p = 1; + ++nfreed; } } @@ -5386,11 +5853,22 @@ clear_image_cache (f, force_p) Emacs was iconified for a longer period of time. In that case, current matrices may still contain references to images freed above. So, clear these matrices. */ - if (any_freed_p) + if (nfreed) { - clear_current_matrices (f); + Lisp_Object tail, frame; + + FOR_EACH_FRAME (tail, frame) + { + struct frame *f = XFRAME (frame); + if (FRAME_X_P (f) + && FRAME_X_IMAGE_CACHE (f) == c) + clear_current_matrices (f); + } + ++windows_or_buffers_changed; } + + UNBLOCK_INPUT; } } @@ -5451,10 +5929,10 @@ lookup_image (f, spec) /* If not found, create a new image and cache it. */ if (img == NULL) { + BLOCK_INPUT; img = make_image (spec, hash); cache_image (f, img); img->load_failed_p = img->type->load (f, img) == 0; - xassert (!interrupt_input_blocked); /* If we can't load the image, and we don't have a width and height, use some arbitrary width and height so that we can @@ -5473,13 +5951,15 @@ lookup_image (f, spec) else { /* Handle image type independent image attributes - `:ascent PERCENT', `:margin MARGIN', `:relief RELIEF'. */ - Lisp_Object ascent, margin, relief, algorithm, heuristic_mask; + `:ascent ASCENT', `:margin MARGIN', `:relief RELIEF'. */ + Lisp_Object ascent, margin, relief; Lisp_Object file; ascent = image_spec_value (spec, QCascent, NULL); if (INTEGERP (ascent)) img->ascent = XFASTINT (ascent); + else if (EQ (ascent, Qcenter)) + img->ascent = CENTERED_IMAGE_ASCENT; margin = image_spec_value (spec, QCmargin, NULL); if (INTEGERP (margin) && XINT (margin) >= 0) @@ -5492,16 +5972,75 @@ lookup_image (f, spec) img->margin += abs (img->relief); } - /* Should we apply a Laplace edge-detection algorithm? */ - algorithm = image_spec_value (spec, QCalgorithm, NULL); - if (img->pixmap && EQ (algorithm, Qlaplace)) - x_laplace (f, img); - - /* Should we built a mask heuristically? */ - heuristic_mask = image_spec_value (spec, QCheuristic_mask, NULL); - if (img->pixmap && !img->mask && !NILP (heuristic_mask)) - x_build_heuristic_mask (f, img, heuristic_mask); + /* Manipulation of the image's mask. */ + if (img->pixmap) + { + /* `:heuristic-mask t' + `:mask heuristic' + means build a mask heuristically. + `:heuristic-mask (R G B)' + `:mask (heuristic (R G B))' + means build a mask from color (R G B) in the + image. + `:mask nil' + means remove a mask, if any. */ + + Lisp_Object mask; + + mask = image_spec_value (spec, QCheuristic_mask, NULL); + if (!NILP (mask)) + x_build_heuristic_mask (f, img, mask); + else + { + int found_p; + + mask = image_spec_value (spec, QCmask, &found_p); + + if (EQ (mask, Qheuristic)) + x_build_heuristic_mask (f, img, Qt); + else if (CONSP (mask) + && EQ (XCAR (mask), Qheuristic)) + { + if (CONSP (XCDR (mask))) + x_build_heuristic_mask (f, img, XCAR (XCDR (mask))); + else + x_build_heuristic_mask (f, img, XCDR (mask)); + } + else if (NILP (mask) && found_p && img->mask) + { + XFreePixmap (FRAME_X_DISPLAY (f), img->mask); + img->mask = None; + } + } + } + + /* Should we apply an image transformation algorithm? */ + if (img->pixmap) + { + Lisp_Object algorithm; + + algorithm = image_spec_value (spec, QCalgorithm, NULL); + if (EQ (algorithm, Qdisabled)) + x_disable_image (f, img); + else if (EQ (algorithm, Qlaplace)) + x_laplace (f, img); + else if (EQ (algorithm, Qemboss)) + x_emboss (f, img); + else if (CONSP (algorithm) + && EQ (XCAR (algorithm), Qedge_detection)) + { + Lisp_Object tem; + tem = XCDR (algorithm); + if (CONSP (tem)) + x_edge_detection (f, img, + Fplist_get (tem, QCmatrix), + Fplist_get (tem, QCcolor_adjustment)); + } + } } + + UNBLOCK_INPUT; + xassert (!interrupt_input_blocked); } /* We're using IMG, so set its timestamp to `now'. */ @@ -5622,7 +6161,7 @@ x_create_x_image_and_pixmap (f, width, height, depth, ximg, pixmap) /* Allocate a pixmap of the same size. */ *pixmap = XCreatePixmap (display, window, width, height, depth); - if (*pixmap == 0) + if (*pixmap == None) { x_destroy_x_image (*ximg); *ximg = NULL; @@ -5670,10 +6209,12 @@ x_put_x_image (f, ximg, pixmap, width, height) /*********************************************************************** - Searching files + File Handling ***********************************************************************/ static Lisp_Object x_find_image_file P_ ((Lisp_Object)); +static char *slurp_file P_ ((char *, int *)); + /* Find image file FILE. Look in data-directory, then x-bitmap-file-path. Value is the full name of the file found, or @@ -5694,7 +6235,7 @@ x_find_image_file (file) /* Try to find FILE in data-directory, then x-bitmap-file-path. */ fd = openp (search_path, file, "", &file_found, 0); - if (fd < 0) + if (fd == -1) file_found = Qnil; else close (fd); @@ -5704,17 +6245,55 @@ x_find_image_file (file) } +/* Read FILE into memory. Value is a pointer to a buffer allocated + with xmalloc holding FILE's contents. Value is null if an error + occurred. *SIZE is set to the size of the file. */ + +static char * +slurp_file (file, size) + char *file; + int *size; +{ + FILE *fp = NULL; + char *buf = NULL; + struct stat st; + + if (stat (file, &st) == 0 + && (fp = fopen (file, "r")) != NULL + && (buf = (char *) xmalloc (st.st_size), + fread (buf, 1, st.st_size, fp) == st.st_size)) + { + *size = st.st_size; + fclose (fp); + } + else + { + if (fp) + fclose (fp); + if (buf) + { + xfree (buf); + buf = NULL; + } + } + + return buf; +} + + /*********************************************************************** XBM images ***********************************************************************/ +static int xbm_scan P_ ((char **, char *, char *, int *)); static int xbm_load P_ ((struct frame *f, struct image *img)); -static int xbm_load_image_from_file P_ ((struct frame *f, struct image *img, - Lisp_Object file)); +static int xbm_load_image P_ ((struct frame *f, struct image *img, + char *, char *)); static int xbm_image_p P_ ((Lisp_Object object)); -static int xbm_read_bitmap_file_data P_ ((char *, int *, int *, - unsigned char **)); +static int xbm_read_bitmap_data P_ ((char *, char *, int *, int *, + unsigned char **)); +static int xbm_file_p P_ ((Lisp_Object)); /* Indices of image specification fields in xbm_format, below. */ @@ -5733,6 +6312,7 @@ enum xbm_keyword_index XBM_RELIEF, XBM_ALGORITHM, XBM_HEURISTIC_MASK, + XBM_MASK, XBM_LAST }; @@ -5748,11 +6328,12 @@ static struct image_keyword xbm_format[XBM_LAST] = {":data", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, {":foreground", IMAGE_STRING_VALUE, 0}, {":background", IMAGE_STRING_VALUE, 0}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":ascent", IMAGE_ASCENT_VALUE, 0}, {":margin", IMAGE_POSITIVE_INTEGER_VALUE, 0}, {":relief", IMAGE_INTEGER_VALUE, 0}, {":algorithm", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, - {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} + {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, + {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} }; /* Structure describing the image type XBM. */ @@ -5795,10 +6376,14 @@ enum xbm_token 3. a vector of strings or bool-vectors, one for each line of the bitmap. + 4. A string containing an in-memory XBM file. WIDTH and HEIGHT + may not be specified in this case because they are defined in the + XBM file. + Both the file and data forms may contain the additional entries `:background COLOR' and `:foreground COLOR'. If not present, foreground and background of the frame on which the image is - displayed, is used. */ + displayed is used. */ static int xbm_image_p (object) @@ -5817,6 +6402,12 @@ xbm_image_p (object) if (kw[XBM_WIDTH].count || kw[XBM_HEIGHT].count || kw[XBM_DATA].count) return 0; } + else if (kw[XBM_DATA].count && xbm_file_p (kw[XBM_DATA].value)) + { + /* In-memory XBM file. */ + if (kw[XBM_WIDTH].count || kw[XBM_HEIGHT].count || kw[XBM_FILE].count) + return 0; + } else { Lisp_Object data; @@ -5878,11 +6469,6 @@ xbm_image_p (object) return 0; } - /* Baseline must be a value between 0 and 100 (a percentage). */ - if (kw[XBM_ASCENT].count - && XFASTINT (kw[XBM_ASCENT].value) > 100) - return 0; - return 1; } @@ -5894,30 +6480,33 @@ xbm_image_p (object) scanning a number, store its value in *IVAL. */ static int -xbm_scan (fp, sval, ival) - FILE *fp; +xbm_scan (s, end, sval, ival) + char **s, *end; char *sval; int *ival; { int c; + + loop: /* Skip white space. */ - while ((c = fgetc (fp)) != EOF && isspace (c)) + while (*s < end && (c = *(*s)++, isspace (c))) ; - if (c == EOF) + if (*s >= end) c = 0; else if (isdigit (c)) { int value = 0, digit; - if (c == '0') + if (c == '0' && *s < end) { - c = fgetc (fp); + c = *(*s)++; if (c == 'x' || c == 'X') { - while ((c = fgetc (fp)) != EOF) + while (*s < end) { + c = *(*s)++; if (isdigit (c)) digit = c - '0'; else if (c >= 'a' && c <= 'f') @@ -5932,53 +6521,66 @@ xbm_scan (fp, sval, ival) else if (isdigit (c)) { value = c - '0'; - while ((c = fgetc (fp)) != EOF - && isdigit (c)) + while (*s < end + && (c = *(*s)++, isdigit (c))) value = 8 * value + c - '0'; } } else { value = c - '0'; - while ((c = fgetc (fp)) != EOF - && isdigit (c)) + while (*s < end + && (c = *(*s)++, isdigit (c))) value = 10 * value + c - '0'; } - if (c != EOF) - ungetc (c, fp); + if (*s < end) + *s = *s - 1; *ival = value; c = XBM_TK_NUMBER; } else if (isalpha (c) || c == '_') { *sval++ = c; - while ((c = fgetc (fp)) != EOF - && (isalnum (c) || c == '_')) + while (*s < end + && (c = *(*s)++, (isalnum (c) || c == '_'))) *sval++ = c; *sval = 0; - if (c != EOF) - ungetc (c, fp); + if (*s < end) + *s = *s - 1; c = XBM_TK_IDENT; } + else if (c == '/' && **s == '*') + { + /* C-style comment. */ + ++*s; + while (**s && (**s != '*' || *(*s + 1) != '/')) + ++*s; + if (**s) + { + *s += 2; + goto loop; + } + } return c; } /* Replacement for XReadBitmapFileData which isn't available under old - X versions. FILE is the name of the bitmap file to read. Set - *WIDTH and *HEIGHT to the width and height of the image. Return in - *DATA the bitmap data allocated with xmalloc. Value is non-zero if - successful. */ + X versions. CONTENTS is a pointer to a buffer to parse; END is the + buffer's end. Set *WIDTH and *HEIGHT to the width and height of + the image. Return in *DATA the bitmap data allocated with xmalloc. + Value is non-zero if successful. DATA null means just test if + CONTENTS looks like an in-memory XBM file. */ static int -xbm_read_bitmap_file_data (file, width, height, data) - char *file; +xbm_read_bitmap_data (contents, end, width, height, data) + char *contents, *end; int *width, *height; unsigned char **data; { - FILE *fp; + char *s = contents; char buffer[BUFSIZ]; int padding_p = 0; int v10 = 0; @@ -5988,7 +6590,7 @@ xbm_read_bitmap_file_data (file, width, height, data) int LA1; #define match() \ - LA1 = xbm_scan (fp, buffer, &value) + LA1 = xbm_scan (&s, end, buffer, &value) #define expect(TOKEN) \ if (LA1 != (TOKEN)) \ @@ -6002,13 +6604,10 @@ xbm_read_bitmap_file_data (file, width, height, data) else \ goto failure - fp = fopen (file, "r"); - if (fp == NULL) - return 0; - *width = *height = -1; - *data = NULL; - LA1 = xbm_scan (fp, buffer, &value); + if (data) + *data = NULL; + LA1 = xbm_scan (&s, end, buffer, &value); /* Parse defines for width, height and hot-spots. */ while (LA1 == '#') @@ -6031,6 +6630,8 @@ xbm_read_bitmap_file_data (file, width, height, data) if (*width < 0 || *height < 0) goto failure; + else if (data == NULL) + goto success; /* Parse bits. Must start with `static'. */ expect_ident ("static"); @@ -6068,7 +6669,6 @@ xbm_read_bitmap_file_data (file, width, height, data) if (v10) { - for (i = 0; i < nbytes; i += 2) { int val = value; @@ -6100,13 +6700,12 @@ xbm_read_bitmap_file_data (file, width, height, data) } } - fclose (fp); + success: return 1; failure: - fclose (fp); - if (*data) + if (data && *data) { xfree (*data); *data = NULL; @@ -6119,35 +6718,21 @@ xbm_read_bitmap_file_data (file, width, height, data) } -/* Load XBM image IMG which will be displayed on frame F from file - SPECIFIED_FILE. Value is non-zero if successful. */ +/* Load XBM image IMG which will be displayed on frame F from buffer + CONTENTS. END is the end of the buffer. Value is non-zero if + successful. */ static int -xbm_load_image_from_file (f, img, specified_file) +xbm_load_image (f, img, contents, end) struct frame *f; struct image *img; - Lisp_Object specified_file; + char *contents, *end; { int rc; unsigned char *data; int success_p = 0; - Lisp_Object file; - struct gcpro gcpro1; - xassert (STRINGP (specified_file)); - file = Qnil; - GCPRO1 (file); - - file = x_find_image_file (specified_file); - if (!STRINGP (file)) - { - image_error ("Cannot find image file `%s'", specified_file, Qnil); - UNGCPRO; - return 0; - } - - rc = xbm_read_bitmap_file_data (XSTRING (file)->data, &img->width, - &img->height, &data); + rc = xbm_read_bitmap_data (contents, end, &img->width, &img->height, &data); if (rc) { int depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); @@ -6166,7 +6751,6 @@ xbm_load_image_from_file (f, img, specified_file) if (!NILP (value)) background = x_alloc_image_color (f, img, value, background); - BLOCK_INPUT; img->pixmap = XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), @@ -6176,24 +6760,36 @@ xbm_load_image_from_file (f, img, specified_file) depth); xfree (data); - if (img->pixmap == 0) + if (img->pixmap == None) { x_clear_image (f, img); - image_error ("Unable to create X pixmap for `%s'", file, Qnil); + image_error ("Unable to create X pixmap for `%s'", img->spec, Qnil); } else success_p = 1; - - UNBLOCK_INPUT; } else image_error ("Error loading XBM image `%s'", img->spec, Qnil); - UNGCPRO; return success_p; } +/* Value is non-zero if DATA looks like an in-memory XBM file. */ + +static int +xbm_file_p (data) + Lisp_Object data; +{ + int w, h; + return (STRINGP (data) + && xbm_read_bitmap_data (XSTRING (data)->data, + (XSTRING (data)->data + + STRING_BYTES (XSTRING (data))), + &w, &h, NULL)); +} + + /* Fill image IMG which is used on frame F with pixmap data. Value is non-zero if successful. */ @@ -6210,31 +6806,60 @@ xbm_load (f, img) /* If IMG->spec specifies a file name, create a non-file spec from it. */ file_name = image_spec_value (img->spec, QCfile, NULL); if (STRINGP (file_name)) - success_p = xbm_load_image_from_file (f, img, file_name); + { + Lisp_Object file; + char *contents; + int size; + struct gcpro gcpro1; + + file = x_find_image_file (file_name); + GCPRO1 (file); + if (!STRINGP (file)) + { + image_error ("Cannot find image file `%s'", file_name, Qnil); + UNGCPRO; + return 0; + } + + contents = slurp_file (XSTRING (file)->data, &size); + if (contents == NULL) + { + image_error ("Error loading XBM image `%s'", img->spec, Qnil); + UNGCPRO; + return 0; + } + + success_p = xbm_load_image (f, img, contents, contents + size); + UNGCPRO; + } else { struct image_keyword fmt[XBM_LAST]; Lisp_Object data; + unsigned char *bitmap_data; int depth; unsigned long foreground = FRAME_FOREGROUND_PIXEL (f); unsigned long background = FRAME_BACKGROUND_PIXEL (f); char *bits; - int parsed_p; + int parsed_p, height, width; + int in_memory_file_p = 0; + + /* See if data looks like an in-memory XBM file. */ + data = image_spec_value (img->spec, QCdata, NULL); + in_memory_file_p = xbm_file_p (data); - /* Parse the list specification. */ + /* Parse the image specification. */ bcopy (xbm_format, fmt, sizeof fmt); parsed_p = parse_image_spec (img->spec, fmt, XBM_LAST, Qxbm); xassert (parsed_p); /* Get specified width, and height. */ - img->width = XFASTINT (fmt[XBM_WIDTH].value); - img->height = XFASTINT (fmt[XBM_HEIGHT].value); - xassert (img->width > 0 && img->height > 0); - - BLOCK_INPUT; - - if (fmt[XBM_ASCENT].count) - img->ascent = XFASTINT (fmt[XBM_ASCENT].value); + if (!in_memory_file_p) + { + img->width = XFASTINT (fmt[XBM_WIDTH].value); + img->height = XFASTINT (fmt[XBM_HEIGHT].value); + xassert (img->width > 0 && img->height > 0); + } /* Get foreground and background colors, maybe allocate colors. */ if (fmt[XBM_FOREGROUND].count) @@ -6244,48 +6869,51 @@ xbm_load (f, img) background = x_alloc_image_color (f, img, fmt[XBM_BACKGROUND].value, background); - /* Set bits to the bitmap image data. */ - data = fmt[XBM_DATA].value; - if (VECTORP (data)) + if (in_memory_file_p) + success_p = xbm_load_image (f, img, XSTRING (data)->data, + (XSTRING (data)->data + + STRING_BYTES (XSTRING (data)))); + else { - int i; - char *p; - int nbytes = (img->width + BITS_PER_CHAR - 1) / BITS_PER_CHAR; + if (VECTORP (data)) + { + int i; + char *p; + int nbytes = (img->width + BITS_PER_CHAR - 1) / BITS_PER_CHAR; - p = bits = (char *) alloca (nbytes * img->height); - for (i = 0; i < img->height; ++i, p += nbytes) + p = bits = (char *) alloca (nbytes * img->height); + for (i = 0; i < img->height; ++i, p += nbytes) + { + Lisp_Object line = XVECTOR (data)->contents[i]; + if (STRINGP (line)) + bcopy (XSTRING (line)->data, p, nbytes); + else + bcopy (XBOOL_VECTOR (line)->data, p, nbytes); + } + } + else if (STRINGP (data)) + bits = XSTRING (data)->data; + else + bits = XBOOL_VECTOR (data)->data; + + /* Create the pixmap. */ + depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); + img->pixmap + = XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + bits, + img->width, img->height, + foreground, background, + depth); + if (img->pixmap) + success_p = 1; + else { - Lisp_Object line = XVECTOR (data)->contents[i]; - if (STRINGP (line)) - bcopy (XSTRING (line)->data, p, nbytes); - else - bcopy (XBOOL_VECTOR (line)->data, p, nbytes); + image_error ("Unable to create pixmap for XBM image `%s'", + img->spec, Qnil); + x_clear_image (f, img); } } - else if (STRINGP (data)) - bits = XSTRING (data)->data; - else - bits = XBOOL_VECTOR (data)->data; - - /* Create the pixmap. */ - depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); - img->pixmap - = XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - bits, - img->width, img->height, - foreground, background, - depth); - if (img->pixmap) - success_p = 1; - else - { - image_error ("Unable to create pixmap for XBM image `%s'", - img->spec, Qnil); - x_clear_image (f, img); - } - - UNBLOCK_INPUT; } return success_p; @@ -6321,6 +6949,7 @@ enum xpm_keyword_index XPM_RELIEF, XPM_ALGORITHM, XPM_HEURISTIC_MASK, + XPM_MASK, XPM_COLOR_SYMBOLS, XPM_LAST }; @@ -6333,11 +6962,12 @@ static struct image_keyword xpm_format[XPM_LAST] = {":type", IMAGE_SYMBOL_VALUE, 1}, {":file", IMAGE_STRING_VALUE, 0}, {":data", IMAGE_STRING_VALUE, 0}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":ascent", IMAGE_ASCENT_VALUE, 0}, {":margin", IMAGE_POSITIVE_INTEGER_VALUE, 0}, {":relief", IMAGE_INTEGER_VALUE, 0}, {":algorithm", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, + {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, {":color-symbols", IMAGE_DONT_CHECK_VALUE_TYPE, 0} }; @@ -6353,6 +6983,207 @@ static struct image_type xpm_type = }; +/* Define ALLOC_XPM_COLORS if we can use Emacs' own color allocation + functions for allocating image colors. Our own functions handle + color allocation failures more gracefully than the ones on the XPM + lib. */ + +#if defined XpmAllocColor && defined XpmFreeColors && defined XpmColorClosure +#define ALLOC_XPM_COLORS +#endif + +#ifdef ALLOC_XPM_COLORS + +static void xpm_init_color_cache P_ ((struct frame *, XpmAttributes *)); +static void xpm_free_color_cache P_ ((void)); +static int xpm_lookup_color P_ ((struct frame *, char *, XColor *)); +static int xpm_color_bucket P_ ((char *)); +static struct xpm_cached_color *xpm_cache_color P_ ((struct frame *, char *, + XColor *, int)); + +/* An entry in a hash table used to cache color definitions of named + colors. This cache is necessary to speed up XPM image loading in + case we do color allocations ourselves. Without it, we would need + a call to XParseColor per pixel in the image. */ + +struct xpm_cached_color +{ + /* Next in collision chain. */ + struct xpm_cached_color *next; + + /* Color definition (RGB and pixel color). */ + XColor color; + + /* Color name. */ + char name[1]; +}; + +/* The hash table used for the color cache, and its bucket vector + size. */ + +#define XPM_COLOR_CACHE_BUCKETS 1001 +struct xpm_cached_color **xpm_color_cache; + +/* Initialize the color cache. */ + +static void +xpm_init_color_cache (f, attrs) + struct frame *f; + XpmAttributes *attrs; +{ + size_t nbytes = XPM_COLOR_CACHE_BUCKETS * sizeof *xpm_color_cache; + xpm_color_cache = (struct xpm_cached_color **) xmalloc (nbytes); + memset (xpm_color_cache, 0, nbytes); + init_color_table (); + + if (attrs->valuemask & XpmColorSymbols) + { + int i; + XColor color; + + for (i = 0; i < attrs->numsymbols; ++i) + if (XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), + attrs->colorsymbols[i].value, &color)) + { + color.pixel = lookup_rgb_color (f, color.red, color.green, + color.blue); + xpm_cache_color (f, attrs->colorsymbols[i].name, &color, -1); + } + } +} + + +/* Free the color cache. */ + +static void +xpm_free_color_cache () +{ + struct xpm_cached_color *p, *next; + int i; + + for (i = 0; i < XPM_COLOR_CACHE_BUCKETS; ++i) + for (p = xpm_color_cache[i]; p; p = next) + { + next = p->next; + xfree (p); + } + + xfree (xpm_color_cache); + xpm_color_cache = NULL; + free_color_table (); +} + + +/* Return the bucket index for color named COLOR_NAME in the color + cache. */ + +static int +xpm_color_bucket (color_name) + char *color_name; +{ + unsigned h = 0; + char *s; + + for (s = color_name; *s; ++s) + h = (h << 2) ^ *s; + return h %= XPM_COLOR_CACHE_BUCKETS; +} + + +/* On frame F, cache values COLOR for color with name COLOR_NAME. + BUCKET, if >= 0, is a precomputed bucket index. Value is the cache + entry added. */ + +static struct xpm_cached_color * +xpm_cache_color (f, color_name, color, bucket) + struct frame *f; + char *color_name; + XColor *color; + int bucket; +{ + size_t nbytes; + struct xpm_cached_color *p; + + if (bucket < 0) + bucket = xpm_color_bucket (color_name); + + nbytes = sizeof *p + strlen (color_name); + p = (struct xpm_cached_color *) xmalloc (nbytes); + strcpy (p->name, color_name); + p->color = *color; + p->next = xpm_color_cache[bucket]; + xpm_color_cache[bucket] = p; + return p; +} + + +/* Look up color COLOR_NAME for frame F in the color cache. If found, + return the cached definition in *COLOR. Otherwise, make a new + entry in the cache and allocate the color. Value is zero if color + allocation failed. */ + +static int +xpm_lookup_color (f, color_name, color) + struct frame *f; + char *color_name; + XColor *color; +{ + struct xpm_cached_color *p; + int h = xpm_color_bucket (color_name); + + for (p = xpm_color_cache[h]; p; p = p->next) + if (strcmp (p->name, color_name) == 0) + break; + + if (p != NULL) + *color = p->color; + else if (XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), + color_name, color)) + { + color->pixel = lookup_rgb_color (f, color->red, color->green, + color->blue); + p = xpm_cache_color (f, color_name, color, h); + } + + return p != NULL; +} + + +/* Callback for allocating color COLOR_NAME. Called from the XPM lib. + CLOSURE is a pointer to the frame on which we allocate the + color. Return in *COLOR the allocated color. Value is non-zero + if successful. */ + +static int +xpm_alloc_color (dpy, cmap, color_name, color, closure) + Display *dpy; + Colormap cmap; + char *color_name; + XColor *color; + void *closure; +{ + return xpm_lookup_color ((struct frame *) closure, color_name, color); +} + + +/* Callback for freeing NPIXELS colors contained in PIXELS. CLOSURE + is a pointer to the frame on which we allocate the color. Value is + non-zero if successful. */ + +static int +xpm_free_colors (dpy, cmap, pixels, npixels, closure) + Display *dpy; + Colormap cmap; + Pixel *pixels; + int npixels; + void *closure; +{ + return 1; +} + +#endif /* ALLOC_XPM_COLORS */ + + /* Value is non-zero if COLOR_SYMBOLS is a valid color symbols list for XPM images. Such a list must consist of conses whose car and cdr are strings. */ @@ -6389,9 +7220,7 @@ xpm_image_p (object) /* Either no `:color-symbols' or it's a list of conses whose car and cdr are strings. */ && (fmt[XPM_COLOR_SYMBOLS].count == 0 - || xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)) - && (fmt[XPM_ASCENT].count == 0 - || XFASTINT (fmt[XPM_ASCENT].value) < 100)); + || xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value))); } @@ -6414,14 +7243,25 @@ xpm_load (f, img) attrs.colormap = FRAME_X_COLORMAP (f); attrs.valuemask |= XpmVisual; attrs.valuemask |= XpmColormap; + +#ifdef ALLOC_XPM_COLORS + /* Allocate colors with our own functions which handle + failing color allocation more gracefully. */ + attrs.color_closure = f; + attrs.alloc_color = xpm_alloc_color; + attrs.free_colors = xpm_free_colors; + attrs.valuemask |= XpmAllocColor | XpmFreeColors | XpmColorClosure; +#else /* not ALLOC_XPM_COLORS */ + /* Let the XPM lib allocate colors. */ attrs.valuemask |= XpmReturnAllocPixels; #ifdef XpmAllocCloseColors attrs.alloc_close_colors = 1; attrs.valuemask |= XpmAllocCloseColors; -#else +#else /* not XpmAllocCloseColors */ attrs.closeness = 600; attrs.valuemask |= XpmCloseness; -#endif +#endif /* not XpmAllocCloseColors */ +#endif /* ALLOC_XPM_COLORS */ /* If image specification contains symbolic color definitions, add these to `attrs'. */ @@ -6461,7 +7301,10 @@ xpm_load (f, img) /* Create a pixmap for the image, either from a file, or from a string buffer containing data in the same format as an XPM file. */ - BLOCK_INPUT; +#ifdef ALLOC_XPM_COLORS + xpm_init_color_cache (f, &attrs); +#endif + specified_file = image_spec_value (img->spec, QCfile, NULL); if (STRINGP (specified_file)) { @@ -6469,7 +7312,6 @@ xpm_load (f, img) if (!STRINGP (file)) { image_error ("Cannot find image file `%s'", specified_file, Qnil); - UNBLOCK_INPUT; return 0; } @@ -6485,25 +7327,30 @@ xpm_load (f, img) &img->pixmap, &img->mask, &attrs); } - UNBLOCK_INPUT; if (rc == XpmSuccess) { - /* Remember allocated colors. */ +#ifdef ALLOC_XPM_COLORS + img->colors = colors_in_color_table (&img->ncolors); +#else /* not ALLOC_XPM_COLORS */ img->ncolors = attrs.nalloc_pixels; img->colors = (unsigned long *) xmalloc (img->ncolors * sizeof *img->colors); for (i = 0; i < attrs.nalloc_pixels; ++i) - img->colors[i] = attrs.alloc_pixels[i]; + { + img->colors[i] = attrs.alloc_pixels[i]; +#ifdef DEBUG_X_COLORS + register_color (img->colors[i]); +#endif + } +#endif /* not ALLOC_XPM_COLORS */ img->width = attrs.width; img->height = attrs.height; xassert (img->width > 0 && img->height > 0); /* The call to XpmFreeAttributes below frees attrs.alloc_pixels. */ - BLOCK_INPUT; XpmFreeAttributes (&attrs); - UNBLOCK_INPUT; } else { @@ -6531,6 +7378,9 @@ xpm_load (f, img) } } +#ifdef ALLOC_XPM_COLORS + xpm_free_color_cache (); +#endif return rc == XpmSuccess; } @@ -6568,15 +7418,6 @@ struct ct_color **ct_table; int ct_colors_allocated; -/* Function prototypes. */ - -static void init_color_table P_ ((void)); -static void free_color_table P_ ((void)); -static unsigned long *colors_in_color_table P_ ((int *n)); -static unsigned long lookup_rgb_color P_ ((struct frame *f, int r, int g, int b)); -static unsigned long lookup_pixel_color P_ ((struct frame *f, unsigned long p)); - - /* Initialize the color table. */ static void @@ -6637,10 +7478,8 @@ lookup_rgb_color (f, r, g, b) color.green = g; color.blue = b; - BLOCK_INPUT; cmap = FRAME_X_COLORMAP (f); rc = x_alloc_nearest_color (f, cmap, &color); - UNBLOCK_INPUT; if (rc) { @@ -6683,13 +7522,10 @@ lookup_pixel_color (f, pixel) Colormap cmap; int rc; - BLOCK_INPUT; - cmap = FRAME_X_COLORMAP (f); color.pixel = pixel; - XQueryColor (FRAME_X_DISPLAY (f), cmap, &color); + x_query_color (f, &color); rc = x_alloc_nearest_color (f, cmap, &color); - UNBLOCK_INPUT; if (rc) { @@ -6751,135 +7587,314 @@ static void x_laplace_write_row P_ ((struct frame *, long *, int, XImage *, int)); static void x_laplace_read_row P_ ((struct frame *, Colormap, XColor *, int, XImage *, int)); +static XColor *x_to_xcolors P_ ((struct frame *, struct image *, int)); +static void x_from_xcolors P_ ((struct frame *, struct image *, XColor *)); +static void x_detect_edges P_ ((struct frame *, struct image *, int[9], int)); +/* Non-zero means draw a cross on images having `:algorithm + disabled'. */ -/* Fill COLORS with RGB colors from row Y of image XIMG. F is the - frame we operate on, CMAP is the color-map in effect, and WIDTH is - the width of one row in the image. */ +int cross_disabled_images; + +/* Edge detection matrices for different edge-detection + strategies. */ + +static int emboss_matrix[9] = { + /* x - 1 x x + 1 */ + 2, -1, 0, /* y - 1 */ + -1, 0, 1, /* y */ + 0, 1, -2 /* y + 1 */ +}; + +static int laplace_matrix[9] = { + /* x - 1 x x + 1 */ + 1, 0, 0, /* y - 1 */ + 0, 0, 0, /* y */ + 0, 0, -1 /* y + 1 */ +}; + +/* Value is the intensity of the color whose red/green/blue values + are R, G, and B. */ + +#define COLOR_INTENSITY(R, G, B) ((2 * (R) + 3 * (G) + (B)) / 6) + + +/* On frame F, return an array of XColor structures describing image + IMG->pixmap. Each XColor structure has its pixel color set. RGB_P + non-zero means also fill the red/green/blue members of the XColor + structures. Value is a pointer to the array of XColors structures, + allocated with xmalloc; it must be freed by the caller. */ + +static XColor * +x_to_xcolors (f, img, rgb_p) + struct frame *f; + struct image *img; + int rgb_p; +{ + int x, y; + XColor *colors, *p; + XImage *ximg; + + colors = (XColor *) xmalloc (img->width * img->height * sizeof *colors); + + /* Get the X image IMG->pixmap. */ + ximg = XGetImage (FRAME_X_DISPLAY (f), img->pixmap, + 0, 0, img->width, img->height, ~0, ZPixmap); + + /* Fill the `pixel' members of the XColor array. I wished there + were an easy and portable way to circumvent XGetPixel. */ + p = colors; + for (y = 0; y < img->height; ++y) + { + XColor *row = p; + + for (x = 0; x < img->width; ++x, ++p) + p->pixel = XGetPixel (ximg, x, y); + + if (rgb_p) + x_query_colors (f, row, img->width); + } + + XDestroyImage (ximg); + return colors; +} + + +/* Create IMG->pixmap from an array COLORS of XColor structures, whose + RGB members are set. F is the frame on which this all happens. + COLORS will be freed; an existing IMG->pixmap will be freed, too. */ static void -x_laplace_read_row (f, cmap, colors, width, ximg, y) +x_from_xcolors (f, img, colors) struct frame *f; - Colormap cmap; + struct image *img; XColor *colors; - int width; - XImage *ximg; - int y; { - int x; + int x, y; + XImage *oimg; + Pixmap pixmap; + XColor *p; + + init_color_table (); + + x_create_x_image_and_pixmap (f, img->width, img->height, 0, + &oimg, &pixmap); + p = colors; + for (y = 0; y < img->height; ++y) + for (x = 0; x < img->width; ++x, ++p) + { + unsigned long pixel; + pixel = lookup_rgb_color (f, p->red, p->green, p->blue); + XPutPixel (oimg, x, y, pixel); + } - for (x = 0; x < width; ++x) - colors[x].pixel = XGetPixel (ximg, x, y); + xfree (colors); + x_clear_image_1 (f, img, 1, 0, 1); - XQueryColors (FRAME_X_DISPLAY (f), cmap, colors, width); + x_put_x_image (f, oimg, pixmap, img->width, img->height); + x_destroy_x_image (oimg); + img->pixmap = pixmap; + img->colors = colors_in_color_table (&img->ncolors); + free_color_table (); } -/* Write row Y of image XIMG. PIXELS is an array of WIDTH longs - containing the pixel colors to write. F is the frame we are - working on. */ +/* On frame F, perform edge-detection on image IMG. + + MATRIX is a nine-element array specifying the transformation + matrix. See emboss_matrix for an example. + + COLOR_ADJUST is a color adjustment added to each pixel of the + outgoing image. */ static void -x_laplace_write_row (f, pixels, width, ximg, y) +x_detect_edges (f, img, matrix, color_adjust) struct frame *f; - long *pixels; - int width; - XImage *ximg; - int y; + struct image *img; + int matrix[9], color_adjust; { - int x; + XColor *colors = x_to_xcolors (f, img, 1); + XColor *new, *p; + int x, y, i, sum; + + for (i = sum = 0; i < 9; ++i) + sum += abs (matrix[i]); + +#define COLOR(A, X, Y) ((A) + (Y) * img->width + (X)) + + new = (XColor *) xmalloc (img->width * img->height * sizeof *new); + + for (y = 0; y < img->height; ++y) + { + p = COLOR (new, 0, y); + p->red = p->green = p->blue = 0xffff/2; + p = COLOR (new, img->width - 1, y); + p->red = p->green = p->blue = 0xffff/2; + } - for (x = 0; x < width; ++x) - XPutPixel (ximg, x, y, pixels[x]); + for (x = 1; x < img->width - 1; ++x) + { + p = COLOR (new, x, 0); + p->red = p->green = p->blue = 0xffff/2; + p = COLOR (new, x, img->height - 1); + p->red = p->green = p->blue = 0xffff/2; + } + + for (y = 1; y < img->height - 1; ++y) + { + p = COLOR (new, 1, y); + + for (x = 1; x < img->width - 1; ++x, ++p) + { + int r, g, b, y1, x1; + + r = g = b = i = 0; + for (y1 = y - 1; y1 < y + 2; ++y1) + for (x1 = x - 1; x1 < x + 2; ++x1, ++i) + if (matrix[i]) + { + XColor *t = COLOR (colors, x1, y1); + r += matrix[i] * t->red; + g += matrix[i] * t->green; + b += matrix[i] * t->blue; + } + + r = (r / sum + color_adjust) & 0xffff; + g = (g / sum + color_adjust) & 0xffff; + b = (b / sum + color_adjust) & 0xffff; + p->red = p->green = p->blue = COLOR_INTENSITY (r, g, b); + } + } + + xfree (colors); + x_from_xcolors (f, img, new); + +#undef COLOR } -/* Transform image IMG which is used on frame F with a Laplace - edge-detection algorithm. The result is an image that can be used - to draw disabled buttons, for example. */ +/* Perform the pre-defined `emboss' edge-detection on image IMG + on frame F. */ static void -x_laplace (f, img) +x_emboss (f, img) struct frame *f; struct image *img; { - Colormap cmap = FRAME_X_COLORMAP (f); - XImage *ximg, *oimg; - XColor *in[3]; - long *out; - Pixmap pixmap; - int x, y, i; - long pixel; - int in_y, out_y, rc; - int mv2 = 45000; + x_detect_edges (f, img, emboss_matrix, 0xffff / 2); +} - BLOCK_INPUT; - /* Get the X image IMG->pixmap. */ - ximg = XGetImage (FRAME_X_DISPLAY (f), img->pixmap, - 0, 0, img->width, img->height, ~0, ZPixmap); +/* Perform the pre-defined `laplace' edge-detection on image IMG + on frame F. */ + +static void +x_laplace (f, img) + struct frame *f; + struct image *img; +{ + x_detect_edges (f, img, laplace_matrix, 45000); +} - /* Allocate 3 input rows, and one output row of colors. */ - for (i = 0; i < 3; ++i) - in[i] = (XColor *) alloca (img->width * sizeof (XColor)); - out = (long *) alloca (img->width * sizeof (long)); - /* Create an X image for output. */ - rc = x_create_x_image_and_pixmap (f, img->width, img->height, 0, - &oimg, &pixmap); +/* Perform edge-detection on image IMG on frame F, with specified + transformation matrix MATRIX and color-adjustment COLOR_ADJUST. - /* Fill first two rows. */ - x_laplace_read_row (f, cmap, in[0], img->width, ximg, 0); - x_laplace_read_row (f, cmap, in[1], img->width, ximg, 1); - in_y = 2; + MATRIX must be either - /* Write first row, all zeros. */ - init_color_table (); - pixel = lookup_rgb_color (f, 0, 0, 0); - for (x = 0; x < img->width; ++x) - out[x] = pixel; - x_laplace_write_row (f, out, img->width, oimg, 0); - out_y = 1; + - a list of at least 9 numbers in row-major form + - a vector of at least 9 numbers - for (y = 2; y < img->height; ++y) + COLOR_ADJUST nil means use a default; otherwise it must be a + number. */ + +static void +x_edge_detection (f, img, matrix, color_adjust) + struct frame *f; + struct image *img; + Lisp_Object matrix, color_adjust; +{ + int i = 0; + int trans[9]; + + if (CONSP (matrix)) { - int rowa = y % 3; - int rowb = (y + 2) % 3; + for (i = 0; + i < 9 && CONSP (matrix) && NUMBERP (XCAR (matrix)); + ++i, matrix = XCDR (matrix)) + trans[i] = XFLOATINT (XCAR (matrix)); + } + else if (VECTORP (matrix) && ASIZE (matrix) >= 9) + { + for (i = 0; i < 9 && NUMBERP (AREF (matrix, i)); ++i) + trans[i] = XFLOATINT (AREF (matrix, i)); + } + + if (NILP (color_adjust)) + color_adjust = make_number (0xffff / 2); + + if (i == 9 && NUMBERP (color_adjust)) + x_detect_edges (f, img, trans, (int) XFLOATINT (color_adjust)); +} - x_laplace_read_row (f, cmap, in[rowa], img->width, ximg, in_y++); - for (x = 0; x < img->width - 2; ++x) +/* Transform image IMG on frame F so that it looks disabled. */ + +static void +x_disable_image (f, img) + struct frame *f; + struct image *img; +{ + struct x_display_info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + + if (dpyinfo->n_planes >= 2) + { + /* Color (or grayscale). Convert to gray, and equalize. Just + drawing such images with a stipple can look very odd, so + we're using this method instead. */ + XColor *colors = x_to_xcolors (f, img, 1); + XColor *p, *end; + const int h = 15000; + const int l = 30000; + + for (p = colors, end = colors + img->width * img->height; + p < end; + ++p) { - int r = in[rowa][x].red + mv2 - in[rowb][x + 2].red; - int g = in[rowa][x].green + mv2 - in[rowb][x + 2].green; - int b = in[rowa][x].blue + mv2 - in[rowb][x + 2].blue; - - out[x + 1] = lookup_rgb_color (f, r & 0xffff, g & 0xffff, - b & 0xffff); + int i = COLOR_INTENSITY (p->red, p->green, p->blue); + int i2 = (0xffff - h - l) * i / 0xffff + l; + p->red = p->green = p->blue = i2; } - x_laplace_write_row (f, out, img->width, oimg, out_y++); + x_from_xcolors (f, img, colors); } - /* Write last line, all zeros. */ - for (x = 0; x < img->width; ++x) - out[x] = pixel; - x_laplace_write_row (f, out, img->width, oimg, out_y); - - /* Free the input image, and free resources of IMG. */ - XDestroyImage (ximg); - x_clear_image (f, img); - - /* Put the output image into pixmap, and destroy it. */ - x_put_x_image (f, oimg, pixmap, img->width, img->height); - x_destroy_x_image (oimg); + /* Draw a cross over the disabled image, if we must or if we + should. */ + if (dpyinfo->n_planes < 2 || cross_disabled_images) + { + Display *dpy = FRAME_X_DISPLAY (f); + GC gc; - /* Remember new pixmap and colors in IMG. */ - img->pixmap = pixmap; - img->colors = colors_in_color_table (&img->ncolors); - free_color_table (); + gc = XCreateGC (dpy, img->pixmap, 0, NULL); + XSetForeground (dpy, gc, BLACK_PIX_DEFAULT (f)); + XDrawLine (dpy, img->pixmap, gc, 0, 0, + img->width - 1, img->height - 1); + XDrawLine (dpy, img->pixmap, gc, 0, img->height - 1, + img->width - 1, 0); + XFreeGC (dpy, gc); - UNBLOCK_INPUT; + if (img->mask) + { + gc = XCreateGC (dpy, img->mask, 0, NULL); + XSetForeground (dpy, gc, WHITE_PIX_DEFAULT (f)); + XDrawLine (dpy, img->mask, gc, 0, 0, + img->width - 1, img->height - 1); + XDrawLine (dpy, img->mask, gc, 0, img->height - 1, + img->width - 1, 0); + XFreeGC (dpy, gc); + } + } } @@ -6899,18 +7914,19 @@ x_build_heuristic_mask (f, img, how) Display *dpy = FRAME_X_DISPLAY (f); XImage *ximg, *mask_img; int x, y, rc, look_at_corners_p; - unsigned long bg; + unsigned long bg = 0; + + if (img->mask) + { + XFreePixmap (FRAME_X_DISPLAY (f), img->mask); + img->mask = None; + } - BLOCK_INPUT; - /* Create an image and pixmap serving as mask. */ rc = x_create_x_image_and_pixmap (f, img->width, img->height, 1, &mask_img, &img->mask); if (!rc) - { - UNBLOCK_INPUT; - return 0; - } + return 0; /* Get the X image of IMG->pixmap. */ ximg = XGetImage (dpy, img->pixmap, 0, 0, img->width, img->height, @@ -6986,7 +8002,6 @@ x_build_heuristic_mask (f, img, how) x_destroy_x_image (mask_img); XDestroyImage (ximg); - UNBLOCK_INPUT; return 1; } @@ -7016,6 +8031,9 @@ enum pbm_keyword_index PBM_RELIEF, PBM_ALGORITHM, PBM_HEURISTIC_MASK, + PBM_MASK, + PBM_FOREGROUND, + PBM_BACKGROUND, PBM_LAST }; @@ -7027,11 +8045,14 @@ static struct image_keyword pbm_format[PBM_LAST] = {":type", IMAGE_SYMBOL_VALUE, 1}, {":file", IMAGE_STRING_VALUE, 0}, {":data", IMAGE_STRING_VALUE, 0}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":ascent", IMAGE_ASCENT_VALUE, 0}, {":margin", IMAGE_POSITIVE_INTEGER_VALUE, 0}, {":relief", IMAGE_INTEGER_VALUE, 0}, {":algorithm", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, - {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} + {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, + {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, + {":foreground", IMAGE_STRING_VALUE, 0}, + {":background", IMAGE_STRING_VALUE, 0} }; /* Structure describing the image type `pbm'. */ @@ -7056,9 +8077,7 @@ pbm_image_p (object) bcopy (pbm_format, fmt, sizeof fmt); - if (!parse_image_spec (object, fmt, PBM_LAST, Qpbm) - || (fmt[PBM_ASCENT].count - && XFASTINT (fmt[PBM_ASCENT].value) > 100)) + if (!parse_image_spec (object, fmt, PBM_LAST, Qpbm)) return 0; /* Must specify either :data or :file. */ @@ -7074,7 +8093,7 @@ static int pbm_scan_number (s, end) unsigned char **s, *end; { - int c, val = -1; + int c = 0, val = -1; while (*s < end) { @@ -7104,42 +8123,6 @@ pbm_scan_number (s, end) } -/* Read FILE into memory. Value is a pointer to a buffer allocated - with xmalloc holding FILE's contents. Value is null if an error - occured. *SIZE is set to the size of the file. */ - -static char * -pbm_read_file (file, size) - Lisp_Object file; - int *size; -{ - FILE *fp = NULL; - char *buf = NULL; - struct stat st; - - if (stat (XSTRING (file)->data, &st) == 0 - && (fp = fopen (XSTRING (file)->data, "r")) != NULL - && (buf = (char *) xmalloc (st.st_size), - fread (buf, 1, st.st_size, fp) == st.st_size)) - { - *size = st.st_size; - fclose (fp); - } - else - { - if (fp) - fclose (fp); - if (buf) - { - xfree (buf); - buf = NULL; - } - } - - return buf; -} - - /* Load PBM image IMG for use on frame F. */ static int @@ -7171,7 +8154,7 @@ pbm_load (f, img) return 0; } - contents = pbm_read_file (file, &size); + contents = slurp_file (XSTRING (file)->data, &size); if (contents == NULL) { image_error ("Error reading `%s'", file, Qnil); @@ -7248,13 +8231,9 @@ pbm_load (f, img) || (type != PBM_MONO && max_color_idx < 0)) goto error; - BLOCK_INPUT; if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) - { - UNBLOCK_INPUT; - goto error; - } + goto error; /* Initialize the color hash table. */ init_color_table (); @@ -7262,6 +8241,19 @@ pbm_load (f, img) if (type == PBM_MONO) { int c = 0, g; + struct image_keyword fmt[PBM_LAST]; + unsigned long fg = FRAME_FOREGROUND_PIXEL (f); + unsigned long bg = FRAME_BACKGROUND_PIXEL (f); + + /* Parse the image specification. */ + bcopy (pbm_format, fmt, sizeof fmt); + parse_image_spec (img->spec, fmt, PBM_LAST, Qpbm); + + /* Get foreground and background colors, maybe allocate colors. */ + if (fmt[PBM_FOREGROUND].count) + fg = x_alloc_image_color (f, img, fmt[PBM_FOREGROUND].value, fg); + if (fmt[PBM_BACKGROUND].count) + bg = x_alloc_image_color (f, img, fmt[PBM_BACKGROUND].value, bg); for (y = 0; y < height; ++y) for (x = 0; x < width; ++x) @@ -7276,9 +8268,7 @@ pbm_load (f, img) else g = pbm_scan_number (&p, end); - XPutPixel (ximg, x, y, (g - ? FRAME_FOREGROUND_PIXEL (f) - : FRAME_BACKGROUND_PIXEL (f))); + XPutPixel (ximg, x, y, g ? fg : bg); } } else @@ -7308,7 +8298,6 @@ pbm_load (f, img) xfree (ximg->data); ximg->data = NULL; XDestroyImage (ximg); - UNBLOCK_INPUT; image_error ("Invalid pixel value in image `%s'", img->spec, Qnil); goto error; @@ -7331,7 +8320,6 @@ pbm_load (f, img) /* Put the image into a pixmap. */ x_put_x_image (f, ximg, img->pixmap, width, height); x_destroy_x_image (ximg); - UNBLOCK_INPUT; img->width = width; img->height = height; @@ -7372,6 +8360,7 @@ enum png_keyword_index PNG_RELIEF, PNG_ALGORITHM, PNG_HEURISTIC_MASK, + PNG_MASK, PNG_LAST }; @@ -7383,11 +8372,12 @@ static struct image_keyword png_format[PNG_LAST] = {":type", IMAGE_SYMBOL_VALUE, 1}, {":data", IMAGE_STRING_VALUE, 0}, {":file", IMAGE_STRING_VALUE, 0}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":ascent", IMAGE_ASCENT_VALUE, 0}, {":margin", IMAGE_POSITIVE_INTEGER_VALUE, 0}, {":relief", IMAGE_INTEGER_VALUE, 0}, {":algorithm", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, - {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} + {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, + {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} }; /* Structure describing the image type `png'. */ @@ -7411,9 +8401,7 @@ png_image_p (object) struct image_keyword fmt[PNG_LAST]; bcopy (png_format, fmt, sizeof fmt); - if (!parse_image_spec (object, fmt, PNG_LAST, Qpng) - || (fmt[PNG_ASCENT].count - && XFASTINT (fmt[PNG_ASCENT].value) > 100)) + if (!parse_image_spec (object, fmt, PNG_LAST, Qpng)) return 0; /* Must specify either the :data or :file keyword. */ @@ -7489,10 +8477,10 @@ png_load (f, img) struct gcpro gcpro1; png_struct *png_ptr = NULL; png_info *info_ptr = NULL, *end_info = NULL; - FILE *fp = NULL; + FILE *volatile fp = NULL; png_byte sig[8]; - png_byte *pixels = NULL; - png_byte **rows = NULL; + png_byte * volatile pixels = NULL; + png_byte ** volatile rows = NULL; png_uint_32 width, height; int bit_depth, color_type, interlace_type; png_byte channels; @@ -7676,11 +8664,9 @@ png_load (f, img) Colormap cmap; png_color_16 frame_background; - BLOCK_INPUT; cmap = FRAME_X_COLORMAP (f); color.pixel = FRAME_BACKGROUND_PIXEL (f); - XQueryColor (FRAME_X_DISPLAY (f), cmap, &color); - UNBLOCK_INPUT; + x_query_color (f, &color); bzero (&frame_background, sizeof frame_background); frame_background.red = color.red; @@ -7721,15 +8707,10 @@ png_load (f, img) fp = NULL; } - BLOCK_INPUT; - /* Create the X image and pixmap. */ if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) - { - UNBLOCK_INPUT; - goto error; - } + goto error; /* Create an image and pixmap serving as mask if the PNG image contains an alpha channel. */ @@ -7740,8 +8721,7 @@ png_load (f, img) { x_destroy_x_image (ximg); XFreePixmap (FRAME_X_DISPLAY (f), img->pixmap); - img->pixmap = 0; - UNBLOCK_INPUT; + img->pixmap = None; goto error; } @@ -7809,7 +8789,6 @@ png_load (f, img) x_destroy_x_image (mask_img); } - UNBLOCK_INPUT; UNGCPRO; return 1; } @@ -7858,6 +8837,7 @@ enum jpeg_keyword_index JPEG_RELIEF, JPEG_ALGORITHM, JPEG_HEURISTIC_MASK, + JPEG_MASK, JPEG_LAST }; @@ -7869,11 +8849,12 @@ static struct image_keyword jpeg_format[JPEG_LAST] = {":type", IMAGE_SYMBOL_VALUE, 1}, {":data", IMAGE_STRING_VALUE, 0}, {":file", IMAGE_STRING_VALUE, 0}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":ascent", IMAGE_ASCENT_VALUE, 0}, {":margin", IMAGE_POSITIVE_INTEGER_VALUE, 0}, {":relief", IMAGE_INTEGER_VALUE, 0}, {":algorithm", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, - {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} + {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, + {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} }; /* Structure describing the image type `jpeg'. */ @@ -7898,9 +8879,7 @@ jpeg_image_p (object) bcopy (jpeg_format, fmt, sizeof fmt); - if (!parse_image_spec (object, fmt, JPEG_LAST, Qjpeg) - || (fmt[JPEG_ASCENT].count - && XFASTINT (fmt[JPEG_ASCENT].value) > 100)) + if (!parse_image_spec (object, fmt, JPEG_LAST, Qjpeg)) return 0; /* Must specify either the :data or :file keyword. */ @@ -7914,6 +8893,7 @@ struct my_jpeg_error_mgr jmp_buf setjmp_buffer; }; + static void my_error_exit (cinfo) j_common_ptr cinfo; @@ -7922,6 +8902,7 @@ my_error_exit (cinfo) longjmp (mgr->setjmp_buffer, 1); } + /* Init source method for JPEG data source manager. Called by jpeg_read_header() before any data is actually read. See libjpeg.doc from the JPEG lib distribution. */ @@ -8030,7 +9011,7 @@ jpeg_load (f, img) struct my_jpeg_error_mgr mgr; Lisp_Object file, specified_file; Lisp_Object specified_data; - FILE *fp = NULL; + FILE * volatile fp = NULL; JSAMPARRAY buffer; int row_stride, x, y; XImage *ximg = NULL; @@ -8066,8 +9047,8 @@ jpeg_load (f, img) /* Customize libjpeg's error handling to call my_error_exit when an error is detected. This function will perform a longjmp. */ - mgr.pub.error_exit = my_error_exit; cinfo.err = jpeg_std_error (&mgr.pub); + mgr.pub.error_exit = my_error_exit; if ((rc = setjmp (mgr.setjmp_buffer)) != 0) { @@ -8082,18 +9063,15 @@ jpeg_load (f, img) /* Close the input file and destroy the JPEG object. */ if (fp) - fclose (fp); + fclose ((FILE *) fp); jpeg_destroy_decompress (&cinfo); - BLOCK_INPUT; - /* If we already have an XImage, free that. */ x_destroy_x_image (ximg); /* Free pixmap and colors. */ x_clear_image (f, img); - UNBLOCK_INPUT; UNGCPRO; return 0; } @@ -8103,7 +9081,7 @@ jpeg_load (f, img) jpeg_create_decompress (&cinfo); if (NILP (specified_data)) - jpeg_stdio_src (&cinfo, fp); + jpeg_stdio_src (&cinfo, (FILE *) fp); else jpeg_memory_src (&cinfo, XSTRING (specified_data)->data, STRING_BYTES (XSTRING (specified_data))); @@ -8117,14 +9095,9 @@ jpeg_load (f, img) width = img->width = cinfo.output_width; height = img->height = cinfo.output_height; - BLOCK_INPUT; - /* Create X image and pixmap. */ if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) - { - UNBLOCK_INPUT; - longjmp (mgr.setjmp_buffer, 2); - } + longjmp (mgr.setjmp_buffer, 2); /* Allocate colors. When color quantization is used, cinfo.actual_number_of_colors has been set with the number of @@ -8179,12 +9152,11 @@ jpeg_load (f, img) jpeg_finish_decompress (&cinfo); jpeg_destroy_decompress (&cinfo); if (fp) - fclose (fp); + fclose ((FILE *) fp); /* Put the image into the pixmap. */ x_put_x_image (f, ximg, img->pixmap, width, height); x_destroy_x_image (ximg); - UNBLOCK_INPUT; UNGCPRO; return 1; } @@ -8220,6 +9192,7 @@ enum tiff_keyword_index TIFF_RELIEF, TIFF_ALGORITHM, TIFF_HEURISTIC_MASK, + TIFF_MASK, TIFF_LAST }; @@ -8231,11 +9204,12 @@ static struct image_keyword tiff_format[TIFF_LAST] = {":type", IMAGE_SYMBOL_VALUE, 1}, {":data", IMAGE_STRING_VALUE, 0}, {":file", IMAGE_STRING_VALUE, 0}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":ascent", IMAGE_ASCENT_VALUE, 0}, {":margin", IMAGE_POSITIVE_INTEGER_VALUE, 0}, {":relief", IMAGE_INTEGER_VALUE, 0}, {":algorithm", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, - {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} + {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, + {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} }; /* Structure describing the image type `tiff'. */ @@ -8259,9 +9233,7 @@ tiff_image_p (object) struct image_keyword fmt[TIFF_LAST]; bcopy (tiff_format, fmt, sizeof fmt); - if (!parse_image_spec (object, fmt, TIFF_LAST, Qtiff) - || (fmt[TIFF_ASCENT].count - && XFASTINT (fmt[TIFF_ASCENT].value) > 100)) + if (!parse_image_spec (object, fmt, TIFF_LAST, Qtiff)) return 0; /* Must specify either the :data or :file keyword. */ @@ -8286,6 +9258,7 @@ typedef struct } tiff_memory_source; + static size_t tiff_read_from_memory (data, buf, size) thandle_t data; @@ -8301,6 +9274,7 @@ tiff_read_from_memory (data, buf, size) return size; } + static size_t tiff_write_from_memory (data, buf, size) thandle_t data; @@ -8310,6 +9284,7 @@ tiff_write_from_memory (data, buf, size) return (size_t) -1; } + static toff_t tiff_seek_in_memory (data, off, whence) thandle_t data; @@ -8344,6 +9319,7 @@ tiff_seek_in_memory (data, off, whence) return src->index; } + static int tiff_close_memory (data) thandle_t data; @@ -8352,6 +9328,7 @@ tiff_close_memory (data) return 0; } + static int tiff_mmap_memory (data, pbase, psize) thandle_t data; @@ -8362,6 +9339,7 @@ tiff_mmap_memory (data, pbase, psize) return 0; } + static void tiff_unmap_memory (data, base, size) thandle_t data; @@ -8371,6 +9349,7 @@ tiff_unmap_memory (data, base, size) /* We don't need to do this. */ } + static toff_t tiff_size_of_memory (data) thandle_t data; @@ -8378,6 +9357,7 @@ tiff_size_of_memory (data) return ((tiff_memory_source *) data)->len; } + /* Load TIFF image IMG for use on frame F. Value is non-zero if successful. */ @@ -8461,12 +9441,9 @@ tiff_load (f, img) return 0; } - BLOCK_INPUT; - /* Create the X image and pixmap. */ if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) { - UNBLOCK_INPUT; xfree (buf); UNGCPRO; return 0; @@ -8498,7 +9475,6 @@ tiff_load (f, img) x_put_x_image (f, ximg, img->pixmap, width, height); x_destroy_x_image (ximg); xfree (buf); - UNBLOCK_INPUT; img->width = width; img->height = height; @@ -8538,6 +9514,7 @@ enum gif_keyword_index GIF_RELIEF, GIF_ALGORITHM, GIF_HEURISTIC_MASK, + GIF_MASK, GIF_IMAGE, GIF_LAST }; @@ -8550,11 +9527,12 @@ static struct image_keyword gif_format[GIF_LAST] = {":type", IMAGE_SYMBOL_VALUE, 1}, {":data", IMAGE_STRING_VALUE, 0}, {":file", IMAGE_STRING_VALUE, 0}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":ascent", IMAGE_ASCENT_VALUE, 0}, {":margin", IMAGE_POSITIVE_INTEGER_VALUE, 0}, {":relief", IMAGE_INTEGER_VALUE, 0}, {":algorithm", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, + {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, {":image", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0} }; @@ -8569,6 +9547,7 @@ static struct image_type gif_type = NULL }; + /* Return non-zero if OBJECT is a valid GIF image specification. */ static int @@ -8578,15 +9557,14 @@ gif_image_p (object) struct image_keyword fmt[GIF_LAST]; bcopy (gif_format, fmt, sizeof fmt); - if (!parse_image_spec (object, fmt, GIF_LAST, Qgif) - || (fmt[GIF_ASCENT].count - && XFASTINT (fmt[GIF_ASCENT].value) > 100)) + if (!parse_image_spec (object, fmt, GIF_LAST, Qgif)) return 0; /* Must specify either the :data or :file keyword. */ return fmt[GIF_FILE].count + fmt[GIF_DATA].count == 1; } + /* Reading a GIF image from memory Based on the PNG memory stuff to a certain extent. */ @@ -8598,6 +9576,7 @@ typedef struct } gif_memory_source; + /* Make the current memory source available to gif_read_from_memory. It's done this way because not all versions of libungif support a UserData field in the GifFileType structure. */ @@ -8706,12 +9685,9 @@ gif_load (f, img) width = img->width = gif->SWidth; height = img->height = gif->SHeight; - BLOCK_INPUT; - /* Create the X image and pixmap. */ if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) { - UNBLOCK_INPUT; DGifCloseFile (gif); UNGCPRO; return 0; @@ -8809,7 +9785,6 @@ gif_load (f, img) /* Put the image into the pixmap, then free the X image and its buffer. */ x_put_x_image (f, ximg, img->pixmap, width, height); x_destroy_x_image (ximg); - UNBLOCK_INPUT; UNGCPRO; return 1; @@ -8850,6 +9825,7 @@ enum gs_keyword_index GS_RELIEF, GS_ALGORITHM, GS_HEURISTIC_MASK, + GS_MASK, GS_LAST }; @@ -8864,11 +9840,12 @@ static struct image_keyword gs_format[GS_LAST] = {":file", IMAGE_STRING_VALUE, 1}, {":loader", IMAGE_FUNCTION_VALUE, 0}, {":bounding-box", IMAGE_DONT_CHECK_VALUE_TYPE, 1}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":ascent", IMAGE_ASCENT_VALUE, 0}, {":margin", IMAGE_POSITIVE_INTEGER_VALUE, 0}, {":relief", IMAGE_INTEGER_VALUE, 0}, {":algorithm", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, - {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} + {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, + {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0} }; /* Structure describing the image type `ghostscript'. */ @@ -8909,9 +9886,7 @@ gs_image_p (object) bcopy (gs_format, fmt, sizeof fmt); - if (!parse_image_spec (object, fmt, GS_LAST, Qpostscript) - || (fmt[GS_ASCENT].count - && XFASTINT (fmt[GS_ASCENT].value) > 100)) + if (!parse_image_spec (object, fmt, GS_LAST, Qpostscript)) return 0; /* Bounding box must be a list or vector containing 4 integers. */ @@ -8966,12 +9941,10 @@ gs_load (f, img) img->height = in_height * FRAME_X_DISPLAY_INFO (f)->resy; /* Create the pixmap. */ - BLOCK_INPUT; - xassert (img->pixmap == 0); + xassert (img->pixmap == None); img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), img->width, img->height, DefaultDepthOfScreen (FRAME_X_SCREEN (f))); - UNBLOCK_INPUT; if (!img->pixmap) { @@ -9295,31 +10268,41 @@ show_busy_cursor (timer) BLOCK_INPUT; FOR_EACH_FRAME (rest, frame) - if (FRAME_X_P (XFRAME (frame))) - { - struct frame *f = XFRAME (frame); - - f->output_data.x->busy_p = 1; + { + struct frame *f = XFRAME (frame); + + if (FRAME_LIVE_P (f) && FRAME_X_P (f) && FRAME_X_DISPLAY (f)) + { + Display *dpy = FRAME_X_DISPLAY (f); + +#ifdef USE_X_TOOLKIT + if (f->output_data.x->widget) +#else + if (FRAME_OUTER_WINDOW (f)) +#endif + { + f->output_data.x->busy_p = 1; - if (!f->output_data.x->busy_window) - { - unsigned long mask = CWCursor; - XSetWindowAttributes attrs; + if (!f->output_data.x->busy_window) + { + unsigned long mask = CWCursor; + XSetWindowAttributes attrs; - attrs.cursor = f->output_data.x->busy_cursor; + attrs.cursor = f->output_data.x->busy_cursor; - f->output_data.x->busy_window - = XCreateWindow (FRAME_X_DISPLAY (f), - FRAME_OUTER_WINDOW (f), - 0, 0, 32000, 32000, 0, 0, - InputOnly, - CopyFromParent, - mask, &attrs); - } + f->output_data.x->busy_window + = XCreateWindow (dpy, FRAME_OUTER_WINDOW (f), + 0, 0, 32000, 32000, 0, 0, + InputOnly, + CopyFromParent, + mask, &attrs); + } - XMapRaised (FRAME_X_DISPLAY (f), f->output_data.x->busy_window); - XFlush (FRAME_X_DISPLAY (f)); - } + XMapRaised (dpy, f->output_data.x->busy_window); + XFlush (dpy); + } + } + } busy_cursor_shown_p = 1; UNBLOCK_INPUT; @@ -9367,9 +10350,9 @@ hide_busy_cursor () static Lisp_Object x_create_tip_frame P_ ((struct x_display_info *, Lisp_Object)); -/* The frame of a currently visible tooltip, or null. */ +/* The frame of a currently visible tooltip. */ -struct frame *tip_frame; +Lisp_Object tip_frame; /* If non-nil, a timer started that hides the last tooltip when it fires. */ @@ -9377,8 +10360,31 @@ struct frame *tip_frame; Lisp_Object tip_timer; Window tip_window; + +static Lisp_Object +unwind_create_tip_frame (frame) + Lisp_Object frame; +{ + Lisp_Object deleted; + + deleted = unwind_create_frame (frame); + if (EQ (deleted, Qt)) + { + tip_window = None; + tip_frame = Qnil; + } + + return deleted; +} + + /* Create a frame for a tooltip on the display described by DPYINFO. - PARMS is a list of frame parameters. Value is the frame. */ + PARMS is a list of frame parameters. Value is the frame. + + Note that functions called here, esp. x_default_parameter can + signal errors, for instance when a specified color name is + undefined. We have to make sure that we're in a consistent state + when this happens. */ static Lisp_Object x_create_tip_frame (dpyinfo, parms) @@ -9390,7 +10396,7 @@ x_create_tip_frame (dpyinfo, parms) Lisp_Object name; long window_prompting = 0; int width, height; - int count = specpdl_ptr - specpdl; + int count = BINDING_STACK_SIZE (); struct gcpro gcpro1, gcpro2, gcpro3; struct kboard *kb; @@ -9416,23 +10422,57 @@ x_create_tip_frame (dpyinfo, parms) frame = Qnil; GCPRO3 (parms, name, frame); - tip_frame = f = make_frame (1); + f = make_frame (1); XSETFRAME (frame, f); FRAME_CAN_HAVE_SCROLL_BARS (f) = 0; + record_unwind_protect (unwind_create_tip_frame, frame); + /* By setting the output method, we're essentially saying that + the frame is live, as per FRAME_LIVE_P. If we get a signal + from this point on, x_destroy_window might screw up reference + counts etc. */ f->output_method = output_x_window; f->output_data.x = (struct x_output *) xmalloc (sizeof (struct x_output)); bzero (f->output_data.x, sizeof (struct x_output)); f->output_data.x->icon_bitmap = -1; f->output_data.x->fontset = -1; + f->output_data.x->scroll_bar_foreground_pixel = -1; + f->output_data.x->scroll_bar_background_pixel = -1; f->icon_name = Qnil; FRAME_X_DISPLAY_INFO (f) = dpyinfo; +#if GLYPH_DEBUG + image_cache_refcount = FRAME_X_IMAGE_CACHE (f)->refcount; + dpyinfo_refcount = dpyinfo->reference_count; +#endif /* GLYPH_DEBUG */ #ifdef MULTI_KBOARD FRAME_KBOARD (f) = kb; #endif f->output_data.x->parent_desc = FRAME_X_DISPLAY_INFO (f)->root_window; f->output_data.x->explicit_parent = 0; + /* These colors will be set anyway later, but it's important + to get the color reference counts right, so initialize them! */ + { + Lisp_Object black; + struct gcpro gcpro1; + + black = build_string ("black"); + GCPRO1 (black); + f->output_data.x->foreground_pixel + = x_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.x->background_pixel + = x_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.x->cursor_pixel + = x_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.x->cursor_foreground_pixel + = x_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.x->border_pixel + = x_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.x->mouse_pixel + = x_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + UNGCPRO; + } + /* Set the name; the functions to which we pass f expect the name to be set. */ if (EQ (name, Qunbound) || NILP (name)) @@ -9448,12 +10488,8 @@ x_create_tip_frame (dpyinfo, parms) specbind (Qx_resource_name, name); } - /* Create fontsets from `global_fontset_alist' before handling fonts. */ - for (tem = Vglobal_fontset_alist; CONSP (tem); tem = XCDR (tem)) - fs_register_fontset (f, XCAR (tem)); - - /* Extract the window parameters from the supplied values - that are needed to determine window geometry. */ + /* Extract the window parameters from the supplied values that are + needed to determine window geometry. */ { Lisp_Object font; @@ -9557,7 +10593,10 @@ x_create_tip_frame (dpyinfo, parms) unsigned long mask; BLOCK_INPUT; - mask = CWBackPixel | CWOverrideRedirect | CWSaveUnder | CWEventMask; + mask = CWBackPixel | CWOverrideRedirect | CWEventMask; + if (DoesSaveUnders (dpyinfo->screen)) + mask |= CWSaveUnder; + /* Window managers look at the override-redirect flag to determine whether or net to give windows a decoration (Xlib spec, chapter 3.2.8). */ @@ -9605,31 +10644,43 @@ x_create_tip_frame (dpyinfo, parms) below. And the frame needs to be on Vframe_list or making it visible won't work. */ Vframe_list = Fcons (frame, Vframe_list); + tip_frame = frame; /* Now that the frame is official, it counts as a reference to its display. */ FRAME_X_DISPLAY_INFO (f)->reference_count++; + /* Discard the unwind_protect. */ return unbind_to (count, frame); } -DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 4, 0, +DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, "Show STRING in a \"tooltip\" window on frame FRAME.\n\ -A tooltip window is a small X window displaying STRING at\n\ -the current mouse position.\n\ +A tooltip window is a small X window displaying a string.\n\ +\n\ FRAME nil or omitted means use the selected frame.\n\ +\n\ PARMS is an optional list of frame parameters which can be\n\ used to change the tooltip's appearance.\n\ +\n\ Automatically hide the tooltip after TIMEOUT seconds.\n\ -TIMEOUT nil means use the default timeout of 5 seconds.") - (string, frame, parms, timeout) - Lisp_Object string, frame, parms, timeout; +TIMEOUT nil means use the default timeout of 5 seconds.\n\ +\n\ +If the list of frame parameters PARAMS contains a `left' parameters,\n\ +the tooltip is displayed at that x-position. Otherwise it is\n\ +displayed at the mouse position, with offset DX added (default is 5 if\n\ +DX isn't specified). Likewise for the y-position; if a `top' frame\n\ +parameter is specified, it determines the y-position of the tooltip\n\ +window, otherwise it is displayed at the mouse position, with offset\n\ +DY added (default is -10).") + (string, frame, parms, timeout, dx, dy) + Lisp_Object string, frame, parms, timeout, dx, dy; { struct frame *f; struct window *w; Window root, child; - Lisp_Object buffer; + Lisp_Object buffer, top, left; struct buffer *old_buffer; struct text_pos pos; int i, width, height; @@ -9649,6 +10700,16 @@ TIMEOUT nil means use the default timeout of 5 seconds.") timeout = make_number (5); else CHECK_NATNUM (timeout, 2); + + if (NILP (dx)) + dx = make_number (5); + else + CHECK_NUMBER (dx, 5); + + if (NILP (dy)) + dy = make_number (-10); + else + CHECK_NUMBER (dy, 6); /* Hide a previous tip, if any. */ Fx_hide_tip (); @@ -9669,15 +10730,15 @@ TIMEOUT nil means use the default timeout of 5 seconds.") /* Create a frame for the tooltip, and record it in the global variable tip_frame. */ frame = x_create_tip_frame (FRAME_X_DISPLAY_INFO (f), parms); - tip_frame = f = XFRAME (frame); + f = XFRAME (frame); /* Set up the frame's root window. Currently we use a size of 80 columns x 40 lines. If someone wants to show a larger tip, he will loose. I don't think this is a realistic case. */ w = XWINDOW (FRAME_ROOT_WINDOW (f)); w->left = w->top = make_number (0); - w->width = 80; - w->height = 40; + w->width = make_number (80); + w->height = make_number (40); adjust_glyphs (f); w->pseudo_window_p = 1; @@ -9687,7 +10748,7 @@ TIMEOUT nil means use the default timeout of 5 seconds.") old_buffer = current_buffer; set_buffer_internal_1 (XBUFFER (buffer)); Ferase_buffer (); - Finsert (make_number (1), &string); + Finsert (1, &string); clear_glyph_matrix (w->desired_matrix); clear_glyph_matrix (w->current_matrix); SET_TEXT_POS (pos, BEGV, BEGV_BYTE); @@ -9708,7 +10769,7 @@ TIMEOUT nil means use the default timeout of 5 seconds.") /* Let the row go over the full width of the frame. */ row->full_width_p = 1; - /* There's a glyph at the end of rows that is use to place + /* There's a glyph at the end of rows that is used to place the cursor there. Don't include the width of this glyph. */ if (row->used[TEXT_AREA]) { @@ -9727,13 +10788,28 @@ TIMEOUT nil means use the default timeout of 5 seconds.") height += 2 * FRAME_INTERNAL_BORDER_WIDTH (f); width += 2 * FRAME_INTERNAL_BORDER_WIDTH (f); + /* User-specified position? */ + left = Fcdr (Fassq (Qleft, parms)); + top = Fcdr (Fassq (Qtop, parms)); + /* Move the tooltip window where the mouse pointer is. Resize and show it. */ BLOCK_INPUT; XQueryPointer (FRAME_X_DISPLAY (f), FRAME_X_DISPLAY_INFO (f)->root_window, &root, &child, &root_x, &root_y, &win_x, &win_y, &pmask); + UNBLOCK_INPUT; + + root_x += XINT (dx); + root_y += XINT (dy); + + if (INTEGERP (left)) + root_x = XINT (left); + if (INTEGERP (top)) + root_y = XINT (top); + + BLOCK_INPUT; XMoveResizeWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - root_x + 5, root_y - height - 5, width, height); + root_x, root_y - height, width, height); XMapRaised (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); UNBLOCK_INPUT; @@ -9759,28 +10835,53 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0, Value is t is tooltip was open, nil otherwise.") () { - int count = specpdl_ptr - specpdl; - int deleted_p = 0; + int count; + Lisp_Object deleted, frame, timer; + struct gcpro gcpro1, gcpro2; + + /* Return quickly if nothing to do. */ + if (NILP (tip_timer) && NILP (tip_frame)) + return Qnil; + frame = tip_frame; + timer = tip_timer; + GCPRO2 (frame, timer); + tip_frame = tip_timer = deleted = Qnil; + + count = BINDING_STACK_SIZE (); specbind (Qinhibit_redisplay, Qt); + specbind (Qinhibit_quit, Qt); - if (!NILP (tip_timer)) - { - call1 (intern ("cancel-timer"), tip_timer); - tip_timer = Qnil; - } + if (!NILP (timer)) + call1 (intern ("cancel-timer"), timer); - if (tip_frame) + if (FRAMEP (frame)) { - Lisp_Object frame; - - XSETFRAME (frame, tip_frame); - Fdelete_frame (frame, Qt); - tip_frame = NULL; - deleted_p = 1; + Fdelete_frame (frame, Qnil); + deleted = Qt; + +#ifdef USE_LUCID + /* Bloodcurdling hack alert: The Lucid menu bar widget's + redisplay procedure is not called when a tip frame over menu + items is unmapped. Redisplay the menu manually... */ + { + struct frame *f = SELECTED_FRAME (); + Widget w = f->output_data.x->menubar_widget; + extern void xlwmenu_redisplay P_ ((Widget)); + + if (!DoesSaveUnders (FRAME_X_DISPLAY_INFO (f)->screen) + && w != None) + { + BLOCK_INPUT; + xlwmenu_redisplay (w); + UNBLOCK_INPUT; + } + } +#endif /* USE_LUCID */ } - return unbind_to (count, deleted_p ? Qt : Qnil); + UNGCPRO; + return unbind_to (count, deleted); } @@ -9907,6 +11008,15 @@ selection dialog's entry field, if MUSTMATCH is non-nil.") XmListSetPos (list, item_pos); } +#ifdef HAVE_MOTIF_2_1 + + /* Process events until the user presses Cancel or OK. */ + result = 0; + while (result == 0 || XtAppPending (Xt_app_con)) + XtAppProcessEvent (Xt_app_con, XtIMAll); + +#else /* not HAVE_MOTIF_2_1 */ + /* Process all events until the user presses Cancel or OK. */ for (result = 0; result == 0;) { @@ -9922,20 +11032,22 @@ selection dialog's entry field, if MUSTMATCH is non-nil.") parent = widget; while (parent && parent != dialog) parent = XtParent (parent); - + if (parent == dialog || (event.type == Expose && !process_expose_from_menu (event))) XtDispatchEvent (&event); } +#endif /* not HAVE_MOTIF_2_1 */ + /* Get the result. */ if (result == XmCR_OK) { XmString text; String data; - XtVaGetValues (dialog, XmNtextString, &text, 0); + XtVaGetValues (dialog, XmNtextString, &text, NULL); XmStringGetLtoR (text, XmFONTLIST_DEFAULT_TAG, &data); XmStringFree (text); file = build_string (data); @@ -9959,37 +11071,68 @@ selection dialog's entry field, if MUSTMATCH is non-nil.") #endif /* USE_MOTIF */ + /*********************************************************************** - Tests + Keyboard ***********************************************************************/ -#if GLYPH_DEBUG +#ifdef HAVE_XKBGETKEYBOARD +#include +#include +#endif -DEFUN ("imagep", Fimagep, Simagep, 1, 1, 0, - "Value is non-nil if SPEC is a valid image specification.") - (spec) - Lisp_Object spec; +DEFUN ("x-backspace-delete-keys-p", Fx_backspace_delete_keys_p, + Sx_backspace_delete_keys_p, 0, 1, 0, + "Check if both Backspace and Delete keys are on the keyboard of FRAME.\n\ +FRAME nil means use the selected frame.\n\ +Value is t if we know that both keys are present, and are mapped to the\n\ +usual X keysyms.") + (frame) + Lisp_Object frame; { - return valid_image_p (spec) ? Qt : Qnil; -} +#ifdef HAVE_XKBGETKEYBOARD + XkbDescPtr kb; + struct frame *f = check_x_frame (frame); + Display *dpy = FRAME_X_DISPLAY (f); + Lisp_Object have_keys; + have_keys = Qnil; -DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0, "") - (spec) - Lisp_Object spec; -{ - int id = -1; - - if (valid_image_p (spec)) - id = lookup_image (SELECTED_FRAME (), spec); + BLOCK_INPUT; + kb = XkbGetKeyboard (dpy, XkbAllComponentsMask, XkbUseCoreKbd); + if (kb) + { + int delete_keycode = 0, backspace_keycode = 0, i; + + for (i = kb->min_key_code; + (i < kb->max_key_code + && (delete_keycode == 0 || backspace_keycode == 0)); + ++i) + { + /* The XKB symbolic key names can be seen most easily + in the PS file generated by `xkbprint -label name $DISPLAY'. */ + if (bcmp ("DELE", kb->names->keys[i].name, 4) == 0) + delete_keycode = i; + else if (bcmp ("BKSP", kb->names->keys[i].name, 4) == 0) + backspace_keycode = i; + } - debug_print (spec); - return make_number (id); + XkbFreeKeyboard (kb, XkbAllComponentsMask, True); + + if (delete_keycode + && backspace_keycode + && XKeysymToKeycode (dpy, XK_Delete) == delete_keycode + && XKeysymToKeycode (dpy, XK_BackSpace) == backspace_keycode) + have_keys = Qt; + } + UNBLOCK_INPUT; + return have_keys; +#else /* not HAVE_XKBGETKEYBOARD */ + return Qnil; +#endif /* not HAVE_XKBGETKEYBOARD */ } -#endif /* GLYPH_DEBUG != 0 */ - /*********************************************************************** @@ -10071,6 +11214,12 @@ syms_of_xfns () staticpro (&Qscroll_bar_background); Qscreen_gamma = intern ("screen-gamma"); staticpro (&Qscreen_gamma); + Qline_spacing = intern ("line-spacing"); + staticpro (&Qline_spacing); + Qcenter = intern ("center"); + staticpro (&Qcenter); + Qcompound_text = intern ("compound-text"); + staticpro (&Qcompound_text); /* This is the end of symbol initialization. */ /* Text property `display' should be nonsticky by default. */ @@ -10080,7 +11229,19 @@ syms_of_xfns () Qlaplace = intern ("laplace"); staticpro (&Qlaplace); - + Qemboss = intern ("emboss"); + staticpro (&Qemboss); + Qedge_detection = intern ("edge-detection"); + staticpro (&Qedge_detection); + Qheuristic = intern ("heuristic"); + staticpro (&Qheuristic); + QCmatrix = intern (":matrix"); + staticpro (&QCmatrix); + QCcolor_adjustment = intern (":color-adjustment"); + staticpro (&QCcolor_adjustment); + QCmask = intern (":mask"); + staticpro (&QCmask); + Qface_set_after_frame_default = intern ("face-set-after-frame-default"); staticpro (&Qface_set_after_frame_default); @@ -10091,6 +11252,12 @@ syms_of_xfns () init_x_parm_symbols (); + DEFVAR_BOOL ("cross-disabled-images", &cross_disabled_images, + "Non-nil means always draw a cross over disabled images.\n\ +Disabled images are those having an `:algorithm disabled' property.\n\ +A cross is always drawn on black & white displays."); + cross_disabled_images = 0; + DEFVAR_LISP ("x-bitmap-file-path", &Vx_bitmap_file_path, "List of directories to search for bitmap files for X."); Vx_bitmap_file_path = decode_env_path ((char *) 0, PATH_BITMAPS); @@ -10162,6 +11329,13 @@ This variable takes effect when you create a new frame\n\ or when you set the mouse color."); Vx_sensitive_text_pointer_shape = Qnil; + DEFVAR_LISP ("x-window-horizontal-drag-cursor", + &Vx_window_horizontal_drag_shape, + "Pointer shape to use for indicating a window can be dragged horizontally.\n\ +This variable takes effect when you create a new frame\n\ +or when you set the mouse color."); + Vx_window_horizontal_drag_shape = Qnil; + DEFVAR_LISP ("x-cursor-fore-pixel", &Vx_cursor_fore_pixel, "A string indicating the foreground color of the cursor box."); Vx_cursor_fore_pixel = Qnil; @@ -10191,11 +11365,6 @@ from the image cache. Value must be an integer or nil with nil\n\ meaning don't clear the cache."); Vimage_cache_eviction_delay = make_number (30 * 60); - DEFVAR_LISP ("image-types", &Vimage_types, - "List of supported image types.\n\ -Each element of the list is a symbol for a supported image type."); - Vimage_types = Qnil; - #ifdef USE_X_TOOLKIT Fprovide (intern ("x-toolkit")); #endif @@ -10234,7 +11403,8 @@ Each element of the list is a symbol for a supported image type."); defsubr (&Sx_display_list); defsubr (&Sx_synchronize); defsubr (&Sx_focus_frame); - + defsubr (&Sx_backspace_delete_keys_p); + /* Setting callback functions for fontset handler. */ get_font_info_func = x_get_font_info; @@ -10307,19 +11477,18 @@ Each element of the list is a symbol for a supported image type."); #endif defsubr (&Sclear_image_cache); - -#if GLYPH_DEBUG - defsubr (&Simagep); - defsubr (&Slookup_image); -#endif + defsubr (&Simage_size); + defsubr (&Simage_mask_p); busy_cursor_atimer = NULL; busy_cursor_shown_p = 0; defsubr (&Sx_show_tip); defsubr (&Sx_hide_tip); - staticpro (&tip_timer); tip_timer = Qnil; + staticpro (&tip_timer); + tip_frame = Qnil; + staticpro (&tip_frame); #ifdef USE_MOTIF defsubr (&Sx_file_dialog);