X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/a744a2ec4d7db4ceea7bd4e1476b0d71ed3af1c2..f1d2ce7f2f7067d538ac590df4e9f4f0d2738dab:/src/xfns.c diff --git a/src/xfns.c b/src/xfns.c index 2f06de41ff..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. @@ -19,14 +19,6 @@ along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* Image support (XBM, XPM, PBM, JPEG, TIFF, GIF, PNG, GS). tooltips, - tool-bars, busy-cursor, file selection dialog added by Gerd - Moellmann . */ - -/* Completely rewritten by Richard Stallman. */ - -/* Rewritten for X11 by Joseph Arceneaux */ - #include #include #include @@ -47,21 +39,17 @@ Boston, MA 02111-1307, USA. */ #include "blockinput.h" #include #include "charset.h" +#include "coding.h" #include "fontset.h" #include "systime.h" #include "termhooks.h" +#include "atimer.h" #ifdef HAVE_X_WINDOWS #include - -/* On some systems, the character-composition stuff is broken in X11R5. */ - -#if defined (HAVE_X11R5) && ! defined (HAVE_X11R6) -#ifdef X11R5_INHIBIT_I18N -#define X_I18N_INHIBITED -#endif -#endif +#include +#include #ifndef VMS #if 1 /* Used to be #ifdef EMACS_BITMAP_FILES, but this should always work. */ @@ -136,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". */ @@ -161,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; @@ -249,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. */ @@ -261,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. */ @@ -306,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; @@ -327,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; } @@ -353,6 +356,8 @@ x_window_to_frame (dpyinfo, wdesc) f = XFRAME (frame); if (!FRAME_X_P (f) || FRAME_X_DISPLAY_INFO (f) != dpyinfo) continue; + if (f->output_data.x->busy_window == wdesc) + return f; #ifdef USE_X_TOOLKIT if ((f->output_data.x->edit_widget && XtWindow (f->output_data.x->edit_widget) == wdesc) @@ -380,34 +385,40 @@ x_any_window_to_frame (dpyinfo, wdesc) int wdesc; { Lisp_Object tail, frame; - struct frame *f; + struct frame *f, *found; struct x_output *x; - for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail)) + found = NULL; + for (tail = Vframe_list; GC_CONSP (tail) && !found; tail = XCDR (tail)) { frame = XCAR (tail); if (!GC_FRAMEP (frame)) continue; + f = XFRAME (frame); - if (!FRAME_X_P (f) || FRAME_X_DISPLAY_INFO (f) != dpyinfo) - continue; - x = f->output_data.x; - /* This frame matches if the window is any of its widgets. */ - if (x->widget) + if (FRAME_X_P (f) && FRAME_X_DISPLAY_INFO (f) == dpyinfo) { - if (wdesc == XtWindow (x->widget) - || wdesc == XtWindow (x->column_widget) - || wdesc == XtWindow (x->edit_widget)) - return f; - /* Match if the window is this frame's menubar. */ - if (lw_window_is_in_menubar (wdesc, x->menubar_widget)) - return f; + /* This frame matches if the window is any of its widgets. */ + x = f->output_data.x; + if (x->busy_window == wdesc) + found = f; + else if (x->widget) + { + if (wdesc == XtWindow (x->widget) + || wdesc == XtWindow (x->column_widget) + || wdesc == XtWindow (x->edit_widget)) + found = f; + /* Match if the window is this frame's menubar. */ + else if (lw_window_is_in_menubar (wdesc, x->menubar_widget)) + found = f; + } + else if (FRAME_X_WINDOW (f) == wdesc) + /* A tooltip frame. */ + found = f; } - else if (FRAME_X_WINDOW (f) == wdesc) - /* A tooltip frame. */ - return f; } - return 0; + + return found; } /* Likewise, but exclude the menu bar widget. */ @@ -431,7 +442,9 @@ x_non_menubar_window_to_frame (dpyinfo, wdesc) continue; x = f->output_data.x; /* This frame matches if the window is any of its widgets. */ - if (x->widget) + if (x->busy_window == wdesc) + return f; + else if (x->widget) { if (wdesc == XtWindow (x->widget) || wdesc == XtWindow (x->column_widget) @@ -660,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; @@ -741,7 +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)); @@ -774,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 @@ -817,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) @@ -1100,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) { @@ -1226,131 +1254,91 @@ gamma_correct (f, color) } -/* Decide if color named COLOR is valid for the display associated with - the selected frame; if so, return the rgb values in COLOR_DEF. - If ALLOC is nonzero, allocate a new colormap cell. */ +/* Decide if color named COLOR_NAME is valid for use on frame F. If + so, return the RGB values in COLOR. If ALLOC_P is non-zero, + allocate the color. Value is zero if COLOR_NAME is invalid, or + no color could be allocated. */ int -x_defined_color (f, color, color_def, alloc) - FRAME_PTR f; - char *color; - XColor *color_def; - int alloc; +x_defined_color (f, color_name, color, alloc_p) + struct frame *f; + char *color_name; + XColor *color; + int alloc_p; { - register int status; - Colormap screen_colormap; - Display *display = FRAME_X_DISPLAY (f); + int success_p; + Display *dpy = FRAME_X_DISPLAY (f); + Colormap cmap = FRAME_X_COLORMAP (f); BLOCK_INPUT; - screen_colormap = DefaultColormap (display, XDefaultScreen (display)); - - status = XParseColor (display, screen_colormap, color, color_def); - if (status && alloc) - { - /* Apply gamma correction. */ - gamma_correct (f, color_def); - - status = XAllocColor (display, screen_colormap, color_def); - if (!status) - { - /* If we got to this point, the colormap is full, so we're - going to try and get the next closest color. - The algorithm used is a least-squares matching, which is - what X uses for closest color matching with StaticColor visuals. */ - - XColor *cells; - int no_cells; - int nearest; - long nearest_delta, trial_delta; - int x; - - no_cells = XDisplayCells (display, XDefaultScreen (display)); - cells = (XColor *) alloca (sizeof (XColor) * no_cells); - - for (x = 0; x < no_cells; x++) - cells[x].pixel = x; - - XQueryColors (display, screen_colormap, cells, no_cells); - nearest = 0; - /* I'm assuming CSE so I'm not going to condense this. */ - nearest_delta = ((((color_def->red >> 8) - (cells[0].red >> 8)) - * ((color_def->red >> 8) - (cells[0].red >> 8))) - + - (((color_def->green >> 8) - (cells[0].green >> 8)) - * ((color_def->green >> 8) - (cells[0].green >> 8))) - + - (((color_def->blue >> 8) - (cells[0].blue >> 8)) - * ((color_def->blue >> 8) - (cells[0].blue >> 8)))); - for (x = 1; x < no_cells; x++) - { - trial_delta = ((((color_def->red >> 8) - (cells[x].red >> 8)) - * ((color_def->red >> 8) - (cells[x].red >> 8))) - + - (((color_def->green >> 8) - (cells[x].green >> 8)) - * ((color_def->green >> 8) - (cells[x].green >> 8))) - + - (((color_def->blue >> 8) - (cells[x].blue >> 8)) - * ((color_def->blue >> 8) - (cells[x].blue >> 8)))); - if (trial_delta < nearest_delta) - { - XColor temp; - temp.red = cells[x].red; - temp.green = cells[x].green; - temp.blue = cells[x].blue; - status = XAllocColor (display, screen_colormap, &temp); - if (status) - { - nearest = x; - nearest_delta = trial_delta; - } - } - } - color_def->red = cells[nearest].red; - color_def->green = cells[nearest].green; - color_def->blue = cells[nearest].blue; - status = XAllocColor (display, screen_colormap, color_def); - } - } + success_p = XParseColor (dpy, cmap, color_name, color); + if (success_p && alloc_p) + success_p = x_alloc_nearest_color (f, cmap, color); UNBLOCK_INPUT; - if (status) - return 1; - else - return 0; + return success_p; } -/* Given a string ARG naming a color, compute a pixel value from it - suitable for screen F. - If F is not a color screen, return DEF (default) regardless of what - ARG says. */ + +/* Return the pixel color value for color COLOR_NAME on frame F. If F + is a monochrome frame, return MONO_COLOR regardless of what ARG says. + Signal an error if color can't be allocated. */ int -x_decode_color (f, arg, def) +x_decode_color (f, color_name, mono_color) FRAME_PTR f; - Lisp_Object arg; - int def; + Lisp_Object color_name; + int mono_color; { XColor cdef; - CHECK_STRING (arg, 0); + CHECK_STRING (color_name, 0); - if (strcmp (XSTRING (arg)->data, "black") == 0) +#if 0 /* Don't do this. It's wrong when we're not using the default + colormap, it makes freeing difficult, and it's probably not + an important optimization. */ + if (strcmp (XSTRING (color_name)->data, "black") == 0) return BLACK_PIX_DEFAULT (f); - else if (strcmp (XSTRING (arg)->data, "white") == 0) + else if (strcmp (XSTRING (color_name)->data, "white") == 0) return WHITE_PIX_DEFAULT (f); +#endif + /* Return MONO_COLOR for monochrome frames. */ if (FRAME_X_DISPLAY_INFO (f)->n_planes == 1) - return def; + return mono_color; /* x_defined_color is responsible for coping with failures by looking for a near-miss. */ - if (x_defined_color (f, XSTRING (arg)->data, &cdef, 1)) + if (x_defined_color (f, XSTRING (color_name)->data, &cdef, 1)) return cdef.pixel; - Fsignal (Qerror, Fcons (build_string ("undefined color"), - Fcons (arg, Qnil))); + 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. */ @@ -1365,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); @@ -1451,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; @@ -1519,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); @@ -1527,15 +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), - DefaultColormap (FRAME_X_DISPLAY (f), - DefaultScreen (FRAME_X_DISPLAY (f))), - &fore_color); - XQueryColor (FRAME_X_DISPLAY (f), - DefaultColormap (FRAME_X_DISPLAY (f), - DefaultScreen (FRAME_X_DISPLAY (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, @@ -1543,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; @@ -1575,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; @@ -1587,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) @@ -1676,27 +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))) + 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)) + 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? */ @@ -1895,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; { @@ -1906,15 +1958,20 @@ x_set_menu_bar_lines_1 (window, n) XSETFASTINT (w->top, XFASTINT (w->top) + n); XSETFASTINT (w->height, XFASTINT (w->height) - n); + if (INTEGERP (w->orig_top)) + XSETFASTINT (w->orig_top, XFASTINT (w->orig_top) + n); + if (INTEGERP (w->orig_height)) + XSETFASTINT (w->orig_height, XFASTINT (w->orig_height) - 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); } } @@ -1962,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); } @@ -1979,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) @@ -1991,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; + } } @@ -2066,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. @@ -2128,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); @@ -2150,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), @@ -2218,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); @@ -2240,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), @@ -2509,8 +2694,6 @@ display_x_get_resource (dpyinfo, attribute, class, component, subclass) char *name_key; char *class_key; - check_x (); - CHECK_STRING (attribute, 0); CHECK_STRING (class, 0); @@ -3049,106 +3232,414 @@ hack_wm_protocols (f, widget) UNBLOCK_INPUT; } #endif - -#ifdef USE_X_TOOLKIT -/* Create and set up the X widget for frame F. */ -static void -x_window (f, window_prompting, minibuffer_only) - struct frame *f; - long window_prompting; - int minibuffer_only; -{ - XClassHint class_hints; - XSetWindowAttributes attributes; - unsigned long attribute_mask; + +/* Support routines for XIC (X Input Context). */ - Widget shell_widget; - Widget pane_widget; - Widget frame_widget; - Arg al [25]; - int ac; +#ifdef HAVE_X_I18N - BLOCK_INPUT; +static XFontSet xic_create_xfontset P_ ((struct frame *, char *)); +static XIMStyle best_xim_style P_ ((XIMStyles *, XIMStyles *)); - /* Use the resource name as the top-level widget name - for looking up resources. Make a non-Lisp copy - for the window manager, so GC relocation won't bother it. - Elsewhere we specify the window name for the window manager. */ - - { - char *str = (char *) XSTRING (Vx_resource_name)->data; - f->namebuf = (char *) xmalloc (strlen (str) + 1); - strcpy (f->namebuf, str); - } +/* Supported XIM styles, ordered by preferenc. */ - ac = 0; - XtSetArg (al[ac], XtNallowShellResize, 1); ac++; - XtSetArg (al[ac], XtNinput, 1); ac++; - XtSetArg (al[ac], XtNmappedWhenManaged, 0); ac++; - XtSetArg (al[ac], XtNborderWidth, f->output_data.x->border_width); ac++; - shell_widget = XtAppCreateShell (f->namebuf, EMACS_CLASS, - applicationShellWidgetClass, - FRAME_X_DISPLAY (f), al, ac); +static XIMStyle supported_xim_styles[] = +{ + XIMPreeditPosition | XIMStatusArea, + XIMPreeditPosition | XIMStatusNothing, + XIMPreeditPosition | XIMStatusNone, + XIMPreeditNothing | XIMStatusArea, + XIMPreeditNothing | XIMStatusNothing, + XIMPreeditNothing | XIMStatusNone, + XIMPreeditNone | XIMStatusArea, + XIMPreeditNone | XIMStatusNothing, + XIMPreeditNone | XIMStatusNone, + 0, +}; - f->output_data.x->widget = shell_widget; - /* maybe_set_screen_title_format (shell_widget); */ - pane_widget = lw_create_widget ("main", "pane", widget_id_tick++, - (widget_value *) NULL, - shell_widget, False, - (lw_callback) NULL, - (lw_callback) NULL, - (lw_callback) NULL); +/* Create an X fontset on frame F with base font name + BASE_FONTNAME.. */ - f->output_data.x->column_widget = pane_widget; +static XFontSet +xic_create_xfontset (f, base_fontname) + struct frame *f; + char *base_fontname; +{ + XFontSet xfs; + char **missing_list; + int missing_count; + char *def_string; + + xfs = XCreateFontSet (FRAME_X_DISPLAY (f), + base_fontname, &missing_list, + &missing_count, &def_string); + if (missing_list) + XFreeStringList (missing_list); + + /* No need to free def_string. */ + return xfs; +} - /* mappedWhenManaged to false tells to the paned window to not map/unmap - the emacs screen when changing menubar. This reduces flickering. */ - ac = 0; - XtSetArg (al[ac], XtNmappedWhenManaged, 0); ac++; - XtSetArg (al[ac], XtNshowGrip, 0); ac++; - XtSetArg (al[ac], XtNallowResize, 1); ac++; - XtSetArg (al[ac], XtNresizeToPreferred, 1); ac++; - XtSetArg (al[ac], XtNemacsFrame, f); ac++; - frame_widget = XtCreateWidget (f->namebuf, - emacsFrameClass, - pane_widget, al, ac); - - f->output_data.x->edit_widget = frame_widget; - - XtManageChild (frame_widget); +/* Value is the best input style, given user preferences USER (already + checked to be supported by Emacs), and styles supported by the + input method XIM. */ - /* Do some needed geometry management. */ - { - int len; - char *tem, shell_position[32]; - Arg al[2]; - int ac = 0; - int extra_borders = 0; - int menubar_size - = (f->output_data.x->menubar_widget - ? (f->output_data.x->menubar_widget->core.height - + f->output_data.x->menubar_widget->core.border_width) - : 0); +static XIMStyle +best_xim_style (user, xim) + XIMStyles *user; + XIMStyles *xim; +{ + int i, j; -#if 0 /* Experimentally, we now get the right results - for -geometry -0-0 without this. 24 Aug 96, rms. */ - if (FRAME_EXTERNAL_MENU_BAR (f)) - { - Dimension ibw = 0; - XtVaGetValues (pane_widget, XtNinternalBorderWidth, &ibw, NULL); - menubar_size += ibw; - } -#endif + for (i = 0; i < user->count_styles; ++i) + for (j = 0; j < xim->count_styles; ++j) + if (user->supported_styles[i] == xim->supported_styles[j]) + return user->supported_styles[i]; - f->output_data.x->menubar_height = menubar_size; + /* Return the default style. */ + return XIMPreeditNothing | XIMStatusNothing; +} -#ifndef USE_LUCID - /* Motif seems to need this amount added to the sizes +/* Create XIC for frame F. */ + +void +create_frame_xic (f) + struct frame *f; +{ + XIM xim; + XIC xic = NULL; + XFontSet xfs = NULL; + static XIMStyle xic_style; + + if (FRAME_XIC (f)) + return; + + xim = FRAME_X_XIM (f); + if (xim) + { + XRectangle s_area; + XPoint spot; + XVaNestedList preedit_attr; + XVaNestedList status_attr; + char *base_fontname; + int fontset; + + s_area.x = 0; s_area.y = 0; s_area.width = 1; s_area.height = 1; + spot.x = 0; spot.y = 1; + /* Create X fontset. */ + fontset = FRAME_FONTSET (f); + if (fontset < 0) + base_fontname = "-*-*-*-r-normal--14-*-*-*-*-*-*-*"; + else + { + /* 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; + + 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); + + /* Determine XIC style. */ + if (xic_style == 0) + { + XIMStyles supported_list; + supported_list.count_styles = (sizeof supported_xim_styles + / sizeof supported_xim_styles[0]); + supported_list.supported_styles = supported_xim_styles; + xic_style = best_xim_style (&supported_list, + FRAME_X_XIM_STYLES (f)); + } + + preedit_attr = XVaCreateNestedList (0, + XNFontSet, xfs, + XNForeground, + FRAME_FOREGROUND_PIXEL (f), + XNBackground, + FRAME_BACKGROUND_PIXEL (f), + (xic_style & XIMPreeditPosition + ? XNSpotLocation + : NULL), + &spot, + NULL); + status_attr = XVaCreateNestedList (0, + XNArea, + &s_area, + XNFontSet, + xfs, + XNForeground, + FRAME_FOREGROUND_PIXEL (f), + XNBackground, + FRAME_BACKGROUND_PIXEL (f), + NULL); + + xic = XCreateIC (xim, + XNInputStyle, xic_style, + XNClientWindow, FRAME_X_WINDOW(f), + XNFocusWindow, FRAME_X_WINDOW(f), + XNStatusAttributes, status_attr, + XNPreeditAttributes, preedit_attr, + NULL); + XFree (preedit_attr); + XFree (status_attr); + } + + FRAME_XIC (f) = xic; + FRAME_XIC_STYLE (f) = xic_style; + FRAME_XIC_FONTSET (f) = xfs; +} + + +/* Destroy XIC and free XIC fontset of frame F, if any. */ + +void +free_frame_xic (f) + struct frame *f; +{ + if (FRAME_XIC (f) == NULL) + return; + + XDestroyIC (FRAME_XIC (f)); + if (FRAME_XIC_FONTSET (f)) + XFreeFontSet (FRAME_X_DISPLAY (f), FRAME_XIC_FONTSET (f)); + + FRAME_XIC (f) = NULL; + FRAME_XIC_FONTSET (f) = NULL; +} + + +/* Place preedit area for XIC of window W's frame to specified + pixel position X/Y. X and Y are relative to window W. */ + +void +xic_set_preeditarea (w, x, y) + struct window *w; + int x, y; +{ + struct frame *f = XFRAME (w->frame); + XVaNestedList attr; + XPoint spot; + + spot.x = WINDOW_TO_FRAME_PIXEL_X (w, x); + spot.y = WINDOW_TO_FRAME_PIXEL_Y (w, y) + FONT_BASE (FRAME_FONT (f)); + attr = XVaCreateNestedList (0, XNSpotLocation, &spot, NULL); + XSetICValues (FRAME_XIC (f), XNPreeditAttributes, attr, NULL); + XFree (attr); +} + + +/* Place status area for XIC in bottom right corner of frame F.. */ + +void +xic_set_statusarea (f) + struct frame *f; +{ + XIC xic = FRAME_XIC (f); + XVaNestedList attr; + XRectangle area; + XRectangle *needed; + + /* Negotiate geometry of status area. If input method has existing + status area, use its current size. */ + area.x = area.y = area.width = area.height = 0; + attr = XVaCreateNestedList (0, XNAreaNeeded, &area, NULL); + XSetICValues (xic, XNStatusAttributes, attr, NULL); + XFree (attr); + + attr = XVaCreateNestedList (0, XNAreaNeeded, &needed, NULL); + XGetICValues (xic, XNStatusAttributes, attr, NULL); + XFree (attr); + + if (needed->width == 0) /* Use XNArea instead of XNAreaNeeded */ + { + attr = XVaCreateNestedList (0, XNArea, &needed, NULL); + XGetICValues (xic, XNStatusAttributes, attr, NULL); + XFree (attr); + } + + area.width = needed->width; + area.height = needed->height; + area.x = PIXEL_WIDTH (f) - area.width - FRAME_INTERNAL_BORDER_WIDTH (f); + area.y = (PIXEL_HEIGHT (f) - area.height + - FRAME_MENUBAR_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f)); + XFree (needed); + + attr = XVaCreateNestedList (0, XNArea, &area, NULL); + XSetICValues(xic, XNStatusAttributes, attr, NULL); + XFree (attr); +} + + +/* Set X fontset for XIC of frame F, using base font name + BASE_FONTNAME. Called when a new Emacs fontset is chosen. */ + +void +xic_set_xfontset (f, base_fontname) + struct frame *f; + char *base_fontname; +{ + XVaNestedList attr; + XFontSet xfs; + + xfs = xic_create_xfontset (f, base_fontname); + + attr = XVaCreateNestedList (0, XNFontSet, xfs, NULL); + if (FRAME_XIC_STYLE (f) & XIMPreeditPosition) + XSetICValues (FRAME_XIC (f), XNPreeditAttributes, attr, NULL); + if (FRAME_XIC_STYLE (f) & XIMStatusArea) + XSetICValues (FRAME_XIC (f), XNStatusAttributes, attr, NULL); + XFree (attr); + + if (FRAME_XIC_FONTSET (f)) + XFreeFontSet (FRAME_X_DISPLAY (f), FRAME_XIC_FONTSET (f)); + FRAME_XIC_FONTSET (f) = xfs; +} + +#endif /* HAVE_X_I18N */ + + + +#ifdef USE_X_TOOLKIT + +/* Create and set up the X widget for frame F. */ + +static void +x_window (f, window_prompting, minibuffer_only) + struct frame *f; + long window_prompting; + int minibuffer_only; +{ + XClassHint class_hints; + XSetWindowAttributes attributes; + unsigned long attribute_mask; + Widget shell_widget; + Widget pane_widget; + Widget frame_widget; + Arg al [25]; + int ac; + + BLOCK_INPUT; + + /* Use the resource name as the top-level widget name + for looking up resources. Make a non-Lisp copy + for the window manager, so GC relocation won't bother it. + + Elsewhere we specify the window name for the window manager. */ + + { + char *str = (char *) XSTRING (Vx_resource_name)->data; + f->namebuf = (char *) xmalloc (strlen (str) + 1); + strcpy (f->namebuf, str); + } + + ac = 0; + XtSetArg (al[ac], XtNallowShellResize, 1); ac++; + XtSetArg (al[ac], XtNinput, 1); ac++; + XtSetArg (al[ac], XtNmappedWhenManaged, 0); ac++; + XtSetArg (al[ac], XtNborderWidth, f->output_data.x->border_width); ac++; + XtSetArg (al[ac], XtNvisual, FRAME_X_VISUAL (f)); ac++; + XtSetArg (al[ac], XtNdepth, FRAME_X_DISPLAY_INFO (f)->n_planes); ac++; + XtSetArg (al[ac], XtNcolormap, FRAME_X_COLORMAP (f)); ac++; + shell_widget = XtAppCreateShell (f->namebuf, EMACS_CLASS, + applicationShellWidgetClass, + FRAME_X_DISPLAY (f), al, ac); + + f->output_data.x->widget = shell_widget; + /* maybe_set_screen_title_format (shell_widget); */ + + pane_widget = lw_create_widget ("main", "pane", widget_id_tick++, + (widget_value *) NULL, + shell_widget, False, + (lw_callback) NULL, + (lw_callback) NULL, + (lw_callback) NULL, + (lw_callback) NULL); + + ac = 0; + XtSetArg (al[ac], XtNvisual, FRAME_X_VISUAL (f)); ac++; + XtSetArg (al[ac], XtNdepth, FRAME_X_DISPLAY_INFO (f)->n_planes); ac++; + XtSetArg (al[ac], XtNcolormap, FRAME_X_COLORMAP (f)); ac++; + XtSetValues (pane_widget, al, ac); + f->output_data.x->column_widget = pane_widget; + + /* mappedWhenManaged to false tells to the paned window to not map/unmap + the emacs screen when changing menubar. This reduces flickering. */ + + ac = 0; + XtSetArg (al[ac], XtNmappedWhenManaged, 0); ac++; + XtSetArg (al[ac], XtNshowGrip, 0); ac++; + XtSetArg (al[ac], XtNallowResize, 1); ac++; + XtSetArg (al[ac], XtNresizeToPreferred, 1); ac++; + XtSetArg (al[ac], XtNemacsFrame, f); ac++; + XtSetArg (al[ac], XtNvisual, FRAME_X_VISUAL (f)); ac++; + XtSetArg (al[ac], XtNdepth, FRAME_X_DISPLAY_INFO (f)->n_planes); ac++; + XtSetArg (al[ac], XtNcolormap, FRAME_X_COLORMAP (f)); ac++; + frame_widget = XtCreateWidget (f->namebuf, emacsFrameClass, pane_widget, + al, ac); + + f->output_data.x->edit_widget = frame_widget; + + XtManageChild (frame_widget); + + /* Do some needed geometry management. */ + { + int len; + char *tem, shell_position[32]; + Arg al[2]; + int ac = 0; + int extra_borders = 0; + int menubar_size + = (f->output_data.x->menubar_widget + ? (f->output_data.x->menubar_widget->core.height + + f->output_data.x->menubar_widget->core.border_width) + : 0); + +#if 0 /* Experimentally, we now get the right results + for -geometry -0-0 without this. 24 Aug 96, rms. */ + if (FRAME_EXTERNAL_MENU_BAR (f)) + { + Dimension ibw = 0; + XtVaGetValues (pane_widget, XtNinternalBorderWidth, &ibw, NULL); + menubar_size += ibw; + } +#endif + + f->output_data.x->menubar_height = menubar_size; + +#ifndef USE_LUCID + /* Motif seems to need this amount added to the sizes specified for the shell widget. The Athena/Lucid widgets don't. Both conclusions reached experimentally. -- rms. */ XtVaGetValues (f->output_data.x->edit_widget, XtNinternalBorderWidth, @@ -3206,35 +3697,11 @@ x_window (f, window_prompting, minibuffer_only) XSetClassHint (FRAME_X_DISPLAY (f), XtWindow (shell_widget), &class_hints); #ifdef HAVE_X_I18N -#ifndef X_I18N_INHIBITED - { - XIM xim; - XIC xic = NULL; - - xim = XOpenIM (FRAME_X_DISPLAY (f), NULL, NULL, NULL); - - if (xim) - { - xic = XCreateIC (xim, - XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, FRAME_X_WINDOW(f), - XNFocusWindow, FRAME_X_WINDOW(f), - NULL); - - if (xic == 0) - { - XCloseIM (xim); - xim = NULL; - } - } - FRAME_XIM (f) = xim; - FRAME_XIC (f) = xic; - } -#else /* X_I18N_INHIBITED */ - FRAME_XIM (f) = 0; - FRAME_XIC (f) = 0; -#endif /* X_I18N_INHIBITED */ -#endif /* HAVE_X_I18N */ + FRAME_XIC (f) = NULL; +#ifdef USE_XIM + create_frame_xic (f); +#endif +#endif f->output_data.x->wm_hints.input = True; f->output_data.x->wm_hints.flags |= InputHint; @@ -3256,8 +3723,19 @@ x_window (f, window_prompting, minibuffer_only) XA_ATOM, 32, PropModeAppend, (unsigned char*) NULL, 0); - /* Make all the standard events reach the Emacs frame. */ + /* Make all the standard events reach the Emacs frame. */ attributes.event_mask = STANDARD_EVENT_SET; + +#ifdef HAVE_X_I18N + if (FRAME_XIC (f)) + { + /* XIM server might require some X events. */ + unsigned long fevent = NoEventMask; + XGetICValues(FRAME_XIC (f), XNFilterEvents, &fevent, NULL); + attributes.event_mask |= fevent; + } +#endif /* HAVE_X_I18N */ + attribute_mask = CWEventMask; XChangeWindowAttributes (XtDisplay (shell_widget), XtWindow (shell_widget), attribute_mask, &attributes); @@ -3307,11 +3785,9 @@ x_window (f) attributes.backing_store = NotUseful; attributes.save_under = True; attributes.event_mask = STANDARD_EVENT_SET; - attribute_mask = (CWBackPixel | CWBorderPixel | CWBitGravity -#if 0 - | CWBackingStore | CWSaveUnder -#endif - | CWEventMask); + attributes.colormap = FRAME_X_COLORMAP (f); + attribute_mask = (CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask + | CWColormap); BLOCK_INPUT; FRAME_X_WINDOW (f) @@ -3323,40 +3799,25 @@ x_window (f) f->output_data.x->border_width, CopyFromParent, /* depth */ InputOutput, /* class */ - FRAME_X_DISPLAY_INFO (f)->visual, + FRAME_X_VISUAL (f), attribute_mask, &attributes); -#ifdef HAVE_X_I18N -#ifndef X_I18N_INHIBITED - { - XIM xim; - XIC xic = NULL; - - xim = XOpenIM (FRAME_X_DISPLAY(f), NULL, NULL, NULL); - - if (xim) - { - xic = XCreateIC (xim, - XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, FRAME_X_WINDOW(f), - XNFocusWindow, FRAME_X_WINDOW(f), - NULL); - if (!xic) - { - XCloseIM (xim); - xim = NULL; - } - } - - FRAME_XIM (f) = xim; - FRAME_XIC (f) = xic; - } -#else /* X_I18N_INHIBITED */ - FRAME_XIM (f) = 0; - FRAME_XIC (f) = 0; -#endif /* X_I18N_INHIBITED */ +#ifdef HAVE_X_I18N +#ifdef USE_XIM + create_frame_xic (f); + if (FRAME_XIC (f)) + { + /* XIM server might require some X events. */ + unsigned long fevent = NoEventMask; + XGetICValues(FRAME_XIC (f), XNFilterEvents, &fevent, NULL); + attributes.event_mask |= fevent; + attribute_mask = CWEventMask; + XChangeWindowAttributes (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + attribute_mask, &attributes); + } +#endif #endif /* HAVE_X_I18N */ - + validate_x_resource_name (); class_hints.res_name = (char *) XSTRING (Vx_resource_name)->data; @@ -3454,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. */ @@ -3474,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 */ @@ -3482,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; @@ -3530,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\ @@ -3550,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; @@ -3620,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", @@ -3628,10 +4158,37 @@ 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 + /* 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; + } + /* Specify the parent under which to make this X window. */ if (!NILP (parent)) @@ -3660,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. */ { @@ -3746,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", @@ -3764,11 +4319,8 @@ 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, Qscroll_bar_width, Qnil, - "scrollBarWidth", "ScrollBarWidth", - RES_TYPE_NUMBER); x_default_parameter (f, parms, Qbuffer_predicate, Qnil, "bufferPredicate", "BufferPredicate", RES_TYPE_SYMBOL); @@ -3830,6 +4382,9 @@ This function is an internal primitive--use `make-frame' instead.") "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN); x_default_parameter (f, parms, Qcursor_type, Qbox, "cursorType", "CursorType", RES_TYPE_SYMBOL); + x_default_parameter (f, parms, Qscroll_bar_width, Qnil, + "scrollBarWidth", "ScrollBarWidth", + RES_TYPE_NUMBER); /* Dimensions, especially f->height, must be done via change_frame_size. Change will not be effected unless different from the current @@ -3840,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 @@ -3891,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. */ @@ -3908,6 +4468,36 @@ x_get_focus_frame (frame) return xfocus; } + +/* In certain situations, when the window manager follows a + click-to-focus policy, there seems to be no way around calling + XSetInputFocus to give another frame the input focus . + + In an ideal world, XSetInputFocus should generally be avoided so + that applications don't interfere with the window manager's focus + policy. But I think it's okay to use when it's clearly done + following a user-command. */ + +DEFUN ("x-focus-frame", Fx_focus_frame, Sx_focus_frame, 1, 1, 0, + "Set the input focus to FRAME.\n\ +FRAME nil means use the selected frame.") + (frame) + Lisp_Object frame; +{ + struct frame *f = check_x_frame (frame); + Display *dpy = FRAME_X_DISPLAY (f); + int count; + + BLOCK_INPUT; + count = x_catch_errors (dpy); + XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + RevertToParent, CurrentTime); + x_uncatch_errors (dpy, count); + UNBLOCK_INPUT; + + return Qnil; +} + DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0, "Internal function called by `color-defined-p', which see.") @@ -4121,1110 +4711,286 @@ If omitted or nil, that stands for the selected frame's display.") } DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 0, - "Returns the height in millimeters of the X display DISPLAY.\n\ -The optional argument DISPLAY specifies which display to ask about.\n\ -DISPLAY should be either a frame or a display name (a string).\n\ -If omitted or nil, that stands for the selected frame's display.") - (display) - Lisp_Object display; -{ - struct x_display_info *dpyinfo = check_x_display_info (display); - - return make_number (HeightMMOfScreen (dpyinfo->screen)); -} - -DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0, - "Returns the width in millimeters of the X display DISPLAY.\n\ -The optional argument DISPLAY specifies which display to ask about.\n\ -DISPLAY should be either a frame or a display name (a string).\n\ -If omitted or nil, that stands for the selected frame's display.") - (display) - Lisp_Object display; -{ - struct x_display_info *dpyinfo = check_x_display_info (display); - - return make_number (WidthMMOfScreen (dpyinfo->screen)); -} - -DEFUN ("x-display-backing-store", Fx_display_backing_store, - Sx_display_backing_store, 0, 1, 0, - "Returns an indication of whether X display DISPLAY does backing store.\n\ -The value may be `always', `when-mapped', or `not-useful'.\n\ -The optional argument DISPLAY specifies which display to ask about.\n\ -DISPLAY should be either a frame or a display name (a string).\n\ -If omitted or nil, that stands for the selected frame's display.") - (display) - Lisp_Object display; -{ - struct x_display_info *dpyinfo = check_x_display_info (display); - - switch (DoesBackingStore (dpyinfo->screen)) - { - case Always: - return intern ("always"); - - case WhenMapped: - return intern ("when-mapped"); - - case NotUseful: - return intern ("not-useful"); - - default: - error ("Strange value for BackingStore parameter of screen"); - } -} - -DEFUN ("x-display-visual-class", Fx_display_visual_class, - Sx_display_visual_class, 0, 1, 0, - "Returns the visual class of the X display DISPLAY.\n\ -The value is one of the symbols `static-gray', `gray-scale',\n\ -`static-color', `pseudo-color', `true-color', or `direct-color'.\n\n\ -The optional argument DISPLAY specifies which display to ask about.\n\ -DISPLAY should be either a frame or a display name (a string).\n\ -If omitted or nil, that stands for the selected frame's display.") - (display) - Lisp_Object display; -{ - struct x_display_info *dpyinfo = check_x_display_info (display); - - 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")); - default: - error ("Display has an unknown visual class"); - } -} - -DEFUN ("x-display-save-under", Fx_display_save_under, - Sx_display_save_under, 0, 1, 0, - "Returns t if the X display DISPLAY supports the save-under feature.\n\ -The optional argument DISPLAY specifies which display to ask about.\n\ -DISPLAY should be either a frame or a display name (a string).\n\ -If omitted or nil, that stands for the selected frame's display.") - (display) - Lisp_Object display; -{ - struct x_display_info *dpyinfo = check_x_display_info (display); - - if (DoesSaveUnders (dpyinfo->screen) == True) - return Qt; - else - return Qnil; -} - -int -x_pixel_width (f) - register struct frame *f; -{ - return PIXEL_WIDTH (f); -} - -int -x_pixel_height (f) - register struct frame *f; -{ - return PIXEL_HEIGHT (f); -} - -int -x_char_width (f) - register struct frame *f; -{ - return FONT_WIDTH (f->output_data.x->font); -} - -int -x_char_height (f) - register struct frame *f; -{ - return f->output_data.x->line_height; -} - -int -x_screen_planes (f) - register struct frame *f; -{ - return FRAME_X_DISPLAY_INFO (f)->n_planes; -} - -#if 0 /* These no longer seem like the right way to do things. */ - -/* Draw a rectangle on the frame with left top corner including - the character specified by LEFT_CHAR and TOP_CHAR. The rectangle is - CHARS by LINES wide and long and is the color of the cursor. */ - -void -x_rectangle (f, gc, left_char, top_char, chars, lines) - register struct frame *f; - GC gc; - register int top_char, left_char, chars, lines; -{ - int width; - int height; - int left = (left_char * FONT_WIDTH (f->output_data.x->font) - + f->output_data.x->internal_border_width); - int top = (top_char * f->output_data.x->line_height - + f->output_data.x->internal_border_width); - - if (chars < 0) - width = FONT_WIDTH (f->output_data.x->font) / 2; - else - width = FONT_WIDTH (f->output_data.x->font) * chars; - if (lines < 0) - height = f->output_data.x->line_height / 2; - else - height = f->output_data.x->line_height * lines; - - XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - gc, left, top, width, height); -} - -DEFUN ("x-draw-rectangle", Fx_draw_rectangle, Sx_draw_rectangle, 5, 5, 0, - "Draw a rectangle on FRAME between coordinates specified by\n\ -numbers X0, Y0, X1, Y1 in the cursor pixel.") - (frame, X0, Y0, X1, Y1) - register Lisp_Object frame, X0, X1, Y0, Y1; -{ - register int x0, y0, x1, y1, top, left, n_chars, n_lines; - - CHECK_LIVE_FRAME (frame, 0); - CHECK_NUMBER (X0, 0); - CHECK_NUMBER (Y0, 1); - CHECK_NUMBER (X1, 2); - CHECK_NUMBER (Y1, 3); - - x0 = XINT (X0); - x1 = XINT (X1); - y0 = XINT (Y0); - y1 = XINT (Y1); - - if (y1 > y0) - { - top = y0; - n_lines = y1 - y0 + 1; - } - else - { - top = y1; - n_lines = y0 - y1 + 1; - } - - if (x1 > x0) - { - left = x0; - n_chars = x1 - x0 + 1; - } - else - { - left = x1; - n_chars = x0 - x1 + 1; - } - - BLOCK_INPUT; - x_rectangle (XFRAME (frame), XFRAME (frame)->output_data.x->cursor_gc, - left, top, n_chars, n_lines); - UNBLOCK_INPUT; - - return Qt; -} - -DEFUN ("x-erase-rectangle", Fx_erase_rectangle, Sx_erase_rectangle, 5, 5, 0, - "Draw a rectangle drawn on FRAME between coordinates\n\ -X0, Y0, X1, Y1 in the regular background-pixel.") - (frame, X0, Y0, X1, Y1) - register Lisp_Object frame, X0, Y0, X1, Y1; -{ - register int x0, y0, x1, y1, top, left, n_chars, n_lines; - - CHECK_LIVE_FRAME (frame, 0); - CHECK_NUMBER (X0, 0); - CHECK_NUMBER (Y0, 1); - CHECK_NUMBER (X1, 2); - CHECK_NUMBER (Y1, 3); - - x0 = XINT (X0); - x1 = XINT (X1); - y0 = XINT (Y0); - y1 = XINT (Y1); - - if (y1 > y0) - { - top = y0; - n_lines = y1 - y0 + 1; - } - else - { - top = y1; - n_lines = y0 - y1 + 1; - } - - if (x1 > x0) - { - left = x0; - n_chars = x1 - x0 + 1; - } - else - { - left = x1; - n_chars = x0 - x1 + 1; - } - - BLOCK_INPUT; - x_rectangle (XFRAME (frame), XFRAME (frame)->output_data.x->reverse_gc, - left, top, n_chars, n_lines); - UNBLOCK_INPUT; - - return Qt; -} - -/* Draw lines around the text region beginning at the character position - TOP_X, TOP_Y and ending at BOTTOM_X and BOTTOM_Y. GC specifies the - pixel and line characteristics. */ - -#define line_len(line) (FRAME_CURRENT_GLYPHS (f)->used[(line)]) - -static void -outline_region (f, gc, top_x, top_y, bottom_x, bottom_y) - register struct frame *f; - GC gc; - int top_x, top_y, bottom_x, bottom_y; -{ - register int ibw = f->output_data.x->internal_border_width; - register int font_w = FONT_WIDTH (f->output_data.x->font); - register int font_h = f->output_data.x->line_height; - int y = top_y; - int x = line_len (y); - XPoint *pixel_points - = (XPoint *) alloca (((bottom_y - top_y + 2) * 4) * sizeof (XPoint)); - register XPoint *this_point = pixel_points; - - /* Do the horizontal top line/lines */ - if (top_x == 0) - { - this_point->x = ibw; - this_point->y = ibw + (font_h * top_y); - this_point++; - if (x == 0) - this_point->x = ibw + (font_w / 2); /* Half-size for newline chars. */ - else - this_point->x = ibw + (font_w * x); - this_point->y = (this_point - 1)->y; - } - else - { - this_point->x = ibw; - this_point->y = ibw + (font_h * (top_y + 1)); - this_point++; - this_point->x = ibw + (font_w * top_x); - this_point->y = (this_point - 1)->y; - this_point++; - this_point->x = (this_point - 1)->x; - this_point->y = ibw + (font_h * top_y); - this_point++; - this_point->x = ibw + (font_w * x); - this_point->y = (this_point - 1)->y; - } - - /* Now do the right side. */ - while (y < bottom_y) - { /* Right vertical edge */ - this_point++; - this_point->x = (this_point - 1)->x; - this_point->y = ibw + (font_h * (y + 1)); - this_point++; - - y++; /* Horizontal connection to next line */ - x = line_len (y); - if (x == 0) - this_point->x = ibw + (font_w / 2); - else - this_point->x = ibw + (font_w * x); - - this_point->y = (this_point - 1)->y; - } - - /* Now do the bottom and connect to the top left point. */ - this_point->x = ibw + (font_w * (bottom_x + 1)); - - this_point++; - this_point->x = (this_point - 1)->x; - this_point->y = ibw + (font_h * (bottom_y + 1)); - this_point++; - this_point->x = ibw; - this_point->y = (this_point - 1)->y; - this_point++; - this_point->x = pixel_points->x; - this_point->y = pixel_points->y; - - XDrawLines (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - gc, pixel_points, - (this_point - pixel_points + 1), CoordModeOrigin); -} - -DEFUN ("x-contour-region", Fx_contour_region, Sx_contour_region, 1, 1, 0, - "Highlight the region between point and the character under the mouse\n\ -selected frame.") - (event) - register Lisp_Object event; -{ - register int x0, y0, x1, y1; - register struct frame *f = selected_frame; - struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f)); - register int p1, p2; - - CHECK_CONS (event, 0); - - BLOCK_INPUT; - x0 = XINT (Fcar (Fcar (event))); - y0 = XINT (Fcar (Fcdr (Fcar (event)))); - - /* If the mouse is past the end of the line, don't that area. */ - /* ReWrite this... */ - - /* Where the cursor is. */ - x1 = WINDOW_TO_FRAME_PIXEL_X (w, w->cursor.x); - y1 = WINDOW_TO_FRAME_PIXEL_Y (w, w->cursor.y); - - if (y1 > y0) /* point below mouse */ - outline_region (f, f->output_data.x->cursor_gc, - x0, y0, x1, y1); - else if (y1 < y0) /* point above mouse */ - outline_region (f, f->output_data.x->cursor_gc, - x1, y1, x0, y0); - else /* same line: draw horizontal rectangle */ - { - if (x1 > x0) - x_rectangle (f, f->output_data.x->cursor_gc, - x0, y0, (x1 - x0 + 1), 1); - else if (x1 < x0) - x_rectangle (f, f->output_data.x->cursor_gc, - x1, y1, (x0 - x1 + 1), 1); - } - - XFlush (FRAME_X_DISPLAY (f)); - UNBLOCK_INPUT; - - return Qnil; -} - -DEFUN ("x-uncontour-region", Fx_uncontour_region, Sx_uncontour_region, 1, 1, 0, - "Erase any highlighting of the region between point and the character\n\ -at X, Y on the selected frame.") - (event) - register Lisp_Object event; -{ - register int x0, y0, x1, y1; - register struct frame *f = selected_frame; - struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f)); - - BLOCK_INPUT; - x0 = XINT (Fcar (Fcar (event))); - y0 = XINT (Fcar (Fcdr (Fcar (event)))); - x1 = WINDOW_TO_FRAME_PIXEL_X (w, w->cursor.x); - y1 = WINDOW_TO_FRAME_PIXEL_Y (w, w->cursor.y); - - if (y1 > y0) /* point below mouse */ - outline_region (f, f->output_data.x->reverse_gc, - x0, y0, x1, y1); - else if (y1 < y0) /* point above mouse */ - outline_region (f, f->output_data.x->reverse_gc, - x1, y1, x0, y0); - else /* same line: draw horizontal rectangle */ - { - if (x1 > x0) - x_rectangle (f, f->output_data.x->reverse_gc, - x0, y0, (x1 - x0 + 1), 1); - else if (x1 < x0) - x_rectangle (f, f->output_data.x->reverse_gc, - x1, y1, (x0 - x1 + 1), 1); - } - UNBLOCK_INPUT; - - return Qnil; -} - -#if 0 -int contour_begin_x, contour_begin_y; -int contour_end_x, contour_end_y; -int contour_npoints; - -/* Clip the top part of the contour lines down (and including) line Y_POS. - If X_POS is in the middle (rather than at the end) of the line, drop - down a line at that character. */ - -static void -clip_contour_top (y_pos, x_pos) -{ - register XPoint *begin = contour_lines[y_pos].top_left; - register XPoint *end; - register int npoints; - register struct display_line *line = selected_frame->phys_lines[y_pos + 1]; - - if (x_pos >= line->len - 1) /* Draw one, straight horizontal line. */ - { - end = contour_lines[y_pos].top_right; - npoints = (end - begin + 1); - XDrawLines (x_current_display, contour_window, - contour_erase_gc, begin_erase, npoints, CoordModeOrigin); - - bcopy (end, begin + 1, contour_last_point - end + 1); - contour_last_point -= (npoints - 2); - XDrawLines (x_current_display, contour_window, - contour_erase_gc, begin, 2, CoordModeOrigin); - XFlush (x_current_display); - - /* Now, update contour_lines structure. */ - } - /* ______. */ - else /* |________*/ - { - register XPoint *p = begin + 1; - end = contour_lines[y_pos].bottom_right; - npoints = (end - begin + 1); - XDrawLines (x_current_display, contour_window, - contour_erase_gc, begin_erase, npoints, CoordModeOrigin); - - p->y = begin->y; - p->x = ibw + (font_w * (x_pos + 1)); - p++; - p->y = begin->y + font_h; - p->x = (p - 1)->x; - bcopy (end, begin + 3, contour_last_point - end + 1); - contour_last_point -= (npoints - 5); - XDrawLines (x_current_display, contour_window, - contour_erase_gc, begin, 4, CoordModeOrigin); - XFlush (x_current_display); - - /* Now, update contour_lines structure. */ - } -} - -/* Erase the top horizontal lines of the contour, and then extend - the contour upwards. */ - -static void -extend_contour_top (line) -{ -} - -static void -clip_contour_bottom (x_pos, y_pos) - int x_pos, y_pos; -{ -} - -static void -extend_contour_bottom (x_pos, y_pos) -{ -} - -DEFUN ("x-select-region", Fx_select_region, Sx_select_region, 1, 1, "e", - "") - (event) - Lisp_Object event; -{ - register struct frame *f = selected_frame; - struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f)); - register int point_x = WINDOW_TO_FRAME_PIXEL_X (w, w->cursor.x); - register int point_y = WINDOW_TO_FRAME_PIXEL_Y (w, w->cursor.y); - register int mouse_below_point; - register Lisp_Object obj; - register int x_contour_x, x_contour_y; - - x_contour_x = x_mouse_x; - x_contour_y = x_mouse_y; - if (x_contour_y > point_y || (x_contour_y == point_y - && x_contour_x > point_x)) - { - mouse_below_point = 1; - outline_region (f, f->output_data.x->cursor_gc, point_x, point_y, - x_contour_x, x_contour_y); - } - else - { - mouse_below_point = 0; - outline_region (f, f->output_data.x->cursor_gc, x_contour_x, x_contour_y, - point_x, point_y); - } - - while (1) - { - obj = read_char (-1, 0, 0, Qnil, 0); - if (!CONSP (obj)) - break; - - if (mouse_below_point) - { - if (x_mouse_y <= point_y) /* Flipped. */ - { - mouse_below_point = 0; - - outline_region (f, f->output_data.x->reverse_gc, point_x, point_y, - x_contour_x, x_contour_y); - outline_region (f, f->output_data.x->cursor_gc, x_mouse_x, x_mouse_y, - point_x, point_y); - } - else if (x_mouse_y < x_contour_y) /* Bottom clipped. */ - { - clip_contour_bottom (x_mouse_y); - } - else if (x_mouse_y > x_contour_y) /* Bottom extended. */ - { - extend_bottom_contour (x_mouse_y); - } - - x_contour_x = x_mouse_x; - x_contour_y = x_mouse_y; - } - else /* mouse above or same line as point */ - { - if (x_mouse_y >= point_y) /* Flipped. */ - { - mouse_below_point = 1; - - outline_region (f, f->output_data.x->reverse_gc, - x_contour_x, x_contour_y, point_x, point_y); - outline_region (f, f->output_data.x->cursor_gc, point_x, point_y, - x_mouse_x, x_mouse_y); - } - else if (x_mouse_y > x_contour_y) /* Top clipped. */ - { - clip_contour_top (x_mouse_y); - } - else if (x_mouse_y < x_contour_y) /* Top extended. */ - { - extend_contour_top (x_mouse_y); - } - } - } - - unread_command_event = obj; - if (mouse_below_point) - { - contour_begin_x = point_x; - contour_begin_y = point_y; - contour_end_x = x_contour_x; - contour_end_y = x_contour_y; - } - else - { - contour_begin_x = x_contour_x; - contour_begin_y = x_contour_y; - contour_end_x = point_x; - contour_end_y = point_y; - } -} -#endif - -DEFUN ("x-horizontal-line", Fx_horizontal_line, Sx_horizontal_line, 1, 1, "e", - "") - (event) - Lisp_Object event; -{ - register Lisp_Object obj; - struct frame *f = selected_frame; - register struct window *w = XWINDOW (selected_window); - register GC line_gc = f->output_data.x->cursor_gc; - register GC erase_gc = f->output_data.x->reverse_gc; -#if 0 - char dash_list[] = {6, 4, 6, 4}; - int dashes = 4; - XGCValues gc_values; -#endif - register int previous_y; - register int line = (x_mouse_y + 1) * f->output_data.x->line_height - + f->output_data.x->internal_border_width; - register int left = f->output_data.x->internal_border_width - + (WINDOW_LEFT_MARGIN (w) - * FONT_WIDTH (f->output_data.x->font)); - register int right = left + (w->width - * FONT_WIDTH (f->output_data.x->font)) - - f->output_data.x->internal_border_width; - -#if 0 - BLOCK_INPUT; - gc_values.foreground = f->output_data.x->cursor_pixel; - gc_values.background = f->output_data.x->background_pixel; - gc_values.line_width = 1; - gc_values.line_style = LineOnOffDash; - gc_values.cap_style = CapRound; - gc_values.join_style = JoinRound; - - line_gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - GCLineStyle | GCJoinStyle | GCCapStyle - | GCLineWidth | GCForeground | GCBackground, - &gc_values); - XSetDashes (FRAME_X_DISPLAY (f), line_gc, 0, dash_list, dashes); - gc_values.foreground = f->output_data.x->background_pixel; - gc_values.background = f->output_data.x->foreground_pixel; - erase_gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - GCLineStyle | GCJoinStyle | GCCapStyle - | GCLineWidth | GCForeground | GCBackground, - &gc_values); - XSetDashes (FRAME_X_DISPLAY (f), erase_gc, 0, dash_list, dashes); - UNBLOCK_INPUT; -#endif - - while (1) - { - BLOCK_INPUT; - if (x_mouse_y >= XINT (w->top) - && x_mouse_y < XINT (w->top) + XINT (w->height) - 1) - { - previous_y = x_mouse_y; - line = (x_mouse_y + 1) * f->output_data.x->line_height - + f->output_data.x->internal_border_width; - XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - line_gc, left, line, right, line); - } - XFlush (FRAME_X_DISPLAY (f)); - UNBLOCK_INPUT; - - do - { - obj = read_char (-1, 0, 0, Qnil, 0); - if (!CONSP (obj) - || (! EQ (Fcar (Fcdr (Fcdr (obj))), - Qvertical_scroll_bar)) - || x_mouse_grabbed) - { - BLOCK_INPUT; - XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - erase_gc, left, line, right, line); - unread_command_event = obj; -#if 0 - XFreeGC (FRAME_X_DISPLAY (f), line_gc); - XFreeGC (FRAME_X_DISPLAY (f), erase_gc); -#endif - UNBLOCK_INPUT; - return Qnil; - } - } - while (x_mouse_y == previous_y); - - BLOCK_INPUT; - XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - erase_gc, left, line, right, line); - UNBLOCK_INPUT; - } -} -#endif - -#if 0 -/* These keep track of the rectangle following the pointer. */ -int mouse_track_top, mouse_track_left, mouse_track_width; - -/* Offset in buffer of character under the pointer, or 0. */ -int mouse_buffer_offset; - -DEFUN ("x-track-pointer", Fx_track_pointer, Sx_track_pointer, 0, 0, 0, - "Track the pointer.") - () -{ - static Cursor current_pointer_shape; - FRAME_PTR f = x_mouse_frame; - - BLOCK_INPUT; - if (EQ (Vmouse_frame_part, Qtext_part) - && (current_pointer_shape != f->output_data.x->nontext_cursor)) - { - unsigned char c; - struct buffer *buf; - - current_pointer_shape = f->output_data.x->nontext_cursor; - XDefineCursor (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - current_pointer_shape); - - buf = XBUFFER (XWINDOW (Vmouse_window)->buffer); - c = *(BUF_CHAR_ADDRESS (buf, mouse_buffer_offset)); - } - else if (EQ (Vmouse_frame_part, Qmodeline_part) - && (current_pointer_shape != f->output_data.x->modeline_cursor)) - { - current_pointer_shape = f->output_data.x->modeline_cursor; - XDefineCursor (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - current_pointer_shape); - } - - XFlush (FRAME_X_DISPLAY (f)); - UNBLOCK_INPUT; -} -#endif - -#if 0 -DEFUN ("x-track-pointer", Fx_track_pointer, Sx_track_pointer, 1, 1, "e", - "Draw rectangle around character under mouse pointer, if there is one.") - (event) - Lisp_Object event; -{ - struct window *w = XWINDOW (Vmouse_window); - struct frame *f = XFRAME (WINDOW_FRAME (w)); - struct buffer *b = XBUFFER (w->buffer); - Lisp_Object obj; - - if (! EQ (Vmouse_window, selected_window)) - return Qnil; - - if (EQ (event, Qnil)) - { - int x, y; - - x_read_mouse_position (selected_frame, &x, &y); - } - - BLOCK_INPUT; - mouse_track_width = 0; - mouse_track_left = mouse_track_top = -1; - - do - { - if ((x_mouse_x != mouse_track_left - && (x_mouse_x < mouse_track_left - || x_mouse_x > (mouse_track_left + mouse_track_width))) - || x_mouse_y != mouse_track_top) - { - int hp = 0; /* Horizontal position */ - int len = FRAME_CURRENT_GLYPHS (f)->used[x_mouse_y]; - int p = FRAME_CURRENT_GLYPHS (f)->bufp[x_mouse_y]; - int tab_width = XINT (b->tab_width); - int ctl_arrow_p = !NILP (b->ctl_arrow); - unsigned char c; - int mode_line_vpos = XFASTINT (w->height) + XFASTINT (w->top) - 1; - int in_mode_line = 0; - - if (! FRAME_CURRENT_GLYPHS (f)->enable[x_mouse_y]) - break; - - /* Erase previous rectangle. */ - if (mouse_track_width) - { - x_rectangle (f, f->output_data.x->reverse_gc, - mouse_track_left, mouse_track_top, - mouse_track_width, 1); - - if ((mouse_track_left == f->phys_cursor_x - || mouse_track_left == f->phys_cursor_x - 1) - && mouse_track_top == f->phys_cursor_y) - { - x_display_cursor (f, 1); - } - } - - mouse_track_left = x_mouse_x; - mouse_track_top = x_mouse_y; - mouse_track_width = 0; - - if (mouse_track_left > len) /* Past the end of line. */ - goto draw_or_not; + "Returns the height in millimeters of the X display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame or a display name (a string).\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + struct x_display_info *dpyinfo = check_x_display_info (display); - if (mouse_track_top == mode_line_vpos) - { - in_mode_line = 1; - goto draw_or_not; - } + return make_number (HeightMMOfScreen (dpyinfo->screen)); +} - if (tab_width <= 0 || tab_width > 20) tab_width = 8; - do - { - c = FETCH_BYTE (p); - if (len == f->width && hp == len - 1 && c != '\n') - goto draw_or_not; +DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0, + "Returns the width in millimeters of the X display DISPLAY.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame or a display name (a string).\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + struct x_display_info *dpyinfo = check_x_display_info (display); - switch (c) - { - case '\t': - mouse_track_width = tab_width - (hp % tab_width); - p++; - hp += mouse_track_width; - if (hp > x_mouse_x) - { - mouse_track_left = hp - mouse_track_width; - goto draw_or_not; - } - continue; + return make_number (WidthMMOfScreen (dpyinfo->screen)); +} - case '\n': - mouse_track_width = -1; - goto draw_or_not; +DEFUN ("x-display-backing-store", Fx_display_backing_store, + Sx_display_backing_store, 0, 1, 0, + "Returns an indication of whether X display DISPLAY does backing store.\n\ +The value may be `always', `when-mapped', or `not-useful'.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame or a display name (a string).\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + struct x_display_info *dpyinfo = check_x_display_info (display); + Lisp_Object result; - default: - if (ctl_arrow_p && (c < 040 || c == 0177)) - { - if (p > ZV) - goto draw_or_not; - - mouse_track_width = 2; - p++; - hp +=2; - if (hp > x_mouse_x) - { - mouse_track_left = hp - mouse_track_width; - goto draw_or_not; - } - } - else - { - mouse_track_width = 1; - p++; - hp++; - } - continue; - } - } - while (hp <= x_mouse_x); + switch (DoesBackingStore (dpyinfo->screen)) + { + case Always: + result = intern ("always"); + break; - draw_or_not: - if (mouse_track_width) /* Over text; use text pointer shape. */ - { - XDefineCursor (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - f->output_data.x->text_cursor); - x_rectangle (f, f->output_data.x->cursor_gc, - mouse_track_left, mouse_track_top, - mouse_track_width, 1); - } - else if (in_mode_line) - XDefineCursor (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - f->output_data.x->modeline_cursor); - else - XDefineCursor (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - f->output_data.x->nontext_cursor); - } + case WhenMapped: + result = intern ("when-mapped"); + break; - XFlush (FRAME_X_DISPLAY (f)); - UNBLOCK_INPUT; + case NotUseful: + result = intern ("not-useful"); + break; - obj = read_char (-1, 0, 0, Qnil, 0); - BLOCK_INPUT; + default: + error ("Strange value for BackingStore parameter of screen"); + result = Qnil; } - while (CONSP (obj) /* Mouse event */ - && EQ (Fcar (Fcdr (Fcdr (obj))), Qnil) /* Not scroll bar */ - && EQ (Vmouse_depressed, Qnil) /* Only motion events */ - && EQ (Vmouse_window, selected_window) /* In this window */ - && x_mouse_frame); - unread_command_event = obj; + return result; +} + +DEFUN ("x-display-visual-class", Fx_display_visual_class, + Sx_display_visual_class, 0, 1, 0, + "Returns the visual class of the X display DISPLAY.\n\ +The value is one of the symbols `static-gray', `gray-scale',\n\ +`static-color', `pseudo-color', `true-color', or `direct-color'.\n\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame or a display name (a string).\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + struct x_display_info *dpyinfo = check_x_display_info (display); + Lisp_Object result; - if (mouse_track_width) + switch (dpyinfo->visual->class) { - x_rectangle (f, f->output_data.x->reverse_gc, - mouse_track_left, mouse_track_top, - mouse_track_width, 1); - mouse_track_width = 0; - if ((mouse_track_left == f->phys_cursor_x - || mouse_track_left - 1 == f->phys_cursor_x) - && mouse_track_top == f->phys_cursor_y) - { - x_display_cursor (f, 1); - } + 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; } - XDefineCursor (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), - f->output_data.x->nontext_cursor); - XFlush (FRAME_X_DISPLAY (f)); - UNBLOCK_INPUT; + + return result; +} - return Qnil; +DEFUN ("x-display-save-under", Fx_display_save_under, + Sx_display_save_under, 0, 1, 0, + "Returns t if the X display DISPLAY supports the save-under feature.\n\ +The optional argument DISPLAY specifies which display to ask about.\n\ +DISPLAY should be either a frame or a display name (a string).\n\ +If omitted or nil, that stands for the selected frame's display.") + (display) + Lisp_Object display; +{ + struct x_display_info *dpyinfo = check_x_display_info (display); + + if (DoesSaveUnders (dpyinfo->screen) == True) + return Qt; + else + return Qnil; } -#endif -#if 0 -#include "glyphs.h" - -/* Draw a pixmap specified by IMAGE_DATA of dimensions WIDTH and HEIGHT - on the frame F at position X, Y. */ +int +x_pixel_width (f) + register struct frame *f; +{ + return PIXEL_WIDTH (f); +} -x_draw_pixmap (f, x, y, image_data, width, height) - struct frame *f; - int x, y, width, height; - char *image_data; +int +x_pixel_height (f) + register struct frame *f; { - Pixmap image; + return PIXEL_HEIGHT (f); +} - image = XCreateBitmapFromData (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), image_data, - width, height); - XCopyPlane (FRAME_X_DISPLAY (f), image, FRAME_X_WINDOW (f), - f->output_data.x->normal_gc, 0, 0, width, height, x, y); +int +x_char_width (f) + register struct frame *f; +{ + return FONT_WIDTH (f->output_data.x->font); } -#endif - -#if 0 /* I'm told these functions are superfluous - given the ability to bind function keys. */ - -#ifdef HAVE_X11 -DEFUN ("x-rebind-key", Fx_rebind_key, Sx_rebind_key, 3, 3, 0, -"Rebind X keysym KEYSYM, with MODIFIERS, to generate NEWSTRING.\n\ -KEYSYM is a string which conforms to the X keysym definitions found\n\ -in X11/keysymdef.h, sans the initial XK_. MODIFIERS is nil or a\n\ -list of strings specifying modifier keys such as Control_L, which must\n\ -also be depressed for NEWSTRING to appear.") - (x_keysym, modifiers, newstring) - register Lisp_Object x_keysym; - register Lisp_Object modifiers; - register Lisp_Object newstring; -{ - char *rawstring; - register KeySym keysym; - KeySym modifier_list[16]; - check_x (); - CHECK_STRING (x_keysym, 1); - CHECK_STRING (newstring, 3); +int +x_char_height (f) + register struct frame *f; +{ + return f->output_data.x->line_height; +} - keysym = XStringToKeysym ((char *) XSTRING (x_keysym)->data); - if (keysym == NoSymbol) - error ("Keysym does not exist"); +int +x_screen_planes (f) + register struct frame *f; +{ + return FRAME_X_DISPLAY_INFO (f)->n_planes; +} - if (NILP (modifiers)) - XRebindKeysym (x_current_display, keysym, modifier_list, 0, - XSTRING (newstring)->data, - STRING_BYTES (XSTRING (newstring))); - else - { - register Lisp_Object rest, mod; - register int i = 0; - for (rest = modifiers; !NILP (rest); rest = Fcdr (rest)) - { - if (i == 16) - error ("Can't have more than 16 modifiers"); - - mod = Fcar (rest); - CHECK_STRING (mod, 3); - modifier_list[i] = XStringToKeysym ((char *) XSTRING (mod)->data); -#ifndef HAVE_X11R5 - if (modifier_list[i] == NoSymbol - || !(IsModifierKey (modifier_list[i]) - || ((unsigned)(modifier_list[i]) == XK_Mode_switch) - || ((unsigned)(modifier_list[i]) == XK_Num_Lock))) -#else - if (modifier_list[i] == NoSymbol - || !IsModifierKey (modifier_list[i])) -#endif - error ("Element is not a modifier keysym"); - i++; - } + +/************************************************************************ + X Displays + ************************************************************************/ - XRebindKeysym (x_current_display, keysym, modifier_list, i, - XSTRING (newstring)->data, - STRING_BYTES (XSTRING (newstring))); - } + +/* Mapping visual names to visuals. */ - return Qnil; +static struct visual_class +{ + char *name; + int class; } - -DEFUN ("x-rebind-keys", Fx_rebind_keys, Sx_rebind_keys, 2, 2, 0, - "Rebind KEYCODE to list of strings STRINGS.\n\ -STRINGS should be a list of 16 elements, one for each shift combination.\n\ -nil as element means don't change.\n\ -See the documentation of `x-rebind-key' for more information.") - (keycode, strings) - register Lisp_Object keycode; - register Lisp_Object strings; -{ - register Lisp_Object item; - register unsigned char *rawstring; - KeySym rawkey, modifier[1]; - int strsize; - register unsigned i; +visual_classes[] = +{ + {"StaticGray", StaticGray}, + {"GrayScale", GrayScale}, + {"StaticColor", StaticColor}, + {"PseudoColor", PseudoColor}, + {"TrueColor", TrueColor}, + {"DirectColor", DirectColor}, + NULL +}; + - check_x (); - CHECK_NUMBER (keycode, 1); - CHECK_CONS (strings, 2); - rawkey = (KeySym) ((unsigned) (XINT (keycode))) & 255; - for (i = 0; i <= 15; strings = Fcdr (strings), i++) - { - item = Fcar (strings); - if (!NILP (item)) - { - CHECK_STRING (item, 2); - strsize = STRING_BYTES (XSTRING (item)); - rawstring = (unsigned char *) xmalloc (strsize); - bcopy (XSTRING (item)->data, rawstring, strsize); - modifier[1] = 1 << i; - XRebindKeysym (x_current_display, rawkey, modifier, 1, - rawstring, strsize); - } - } - return Qnil; -} -#endif /* HAVE_X11 */ -#endif /* 0 */ - #ifndef HAVE_XSCREENNUMBEROFSCREEN + +/* Value is the screen number of screen SCR. This is a substitute for + the X function with the same name when that doesn't exist. */ + int XScreenNumberOfScreen (scr) register Screen *scr; { - register Display *dpy; - register Screen *dpyscr; - register int i; - - dpy = scr->display; - dpyscr = dpy->screens; + Display *dpy = scr->display; + int i; - for (i = 0; i < dpy->nscreens; i++, dpyscr++) - if (scr == dpyscr) - return i; + for (i = 0; i < dpy->nscreens; ++i) + if (scr == dpy->screens[i]) + break; - return -1; + return i; } -#endif /* not HAVE_XSCREENNUMBEROFSCREEN */ - -Visual * -select_visual (dpy, screen, depth) - Display *dpy; - Screen *screen; - unsigned int *depth; -{ - Visual *v; - XVisualInfo *vinfo, vinfo_template; - int n_visuals; - v = DefaultVisualOfScreen (screen); +#endif /* not HAVE_XSCREENNUMBEROFSCREEN */ -#ifdef HAVE_X11R4 - vinfo_template.visualid = XVisualIDFromVisual (v); -#else - vinfo_template.visualid = v->visualid; -#endif - vinfo_template.screen = XScreenNumberOfScreen (screen); +/* Select the visual that should be used on display DPYINFO. Set + members of DPYINFO appropriately. Called from x_term_init. */ - vinfo = XGetVisualInfo (dpy, - VisualIDMask | VisualScreenMask, &vinfo_template, - &n_visuals); - if (n_visuals != 1) - fatal ("Can't get proper X visual info"); +void +select_visual (dpyinfo) + struct x_display_info *dpyinfo; +{ + Display *dpy = dpyinfo->display; + Screen *screen = dpyinfo->screen; + Lisp_Object value; - if ((1 << vinfo->depth) == vinfo->colormap_size) - *depth = vinfo->depth; - else + /* See if a visual is specified. */ + value = display_x_get_resource (dpyinfo, + build_string ("visualClass"), + build_string ("VisualClass"), + Qnil, Qnil); + if (STRINGP (value)) { - int i = 0; - int n = vinfo->colormap_size - 1; - while (n) + /* VALUE should be of the form CLASS-DEPTH, where CLASS is one + of `PseudoColor', `TrueColor' etc. and DEPTH is the color + depth, a decimal number. NAME is compared with case ignored. */ + char *s = (char *) alloca (STRING_BYTES (XSTRING (value)) + 1); + char *dash; + int i, class = -1; + XVisualInfo vinfo; + + strcpy (s, XSTRING (value)->data); + dash = index (s, '-'); + if (dash) { - n = n >> 1; - i++; + dpyinfo->n_planes = atoi (dash + 1); + *dash = '\0'; } - *depth = i; + else + /* We won't find a matching visual with depth 0, so that + an error will be printed below. */ + dpyinfo->n_planes = 0; + + /* Determine the visual class. */ + for (i = 0; visual_classes[i].name; ++i) + if (xstricmp (s, visual_classes[i].name) == 0) + { + class = visual_classes[i].class; + break; + } + + /* Look up a matching visual for the specified class. */ + if (class == -1 + || !XMatchVisualInfo (dpy, XScreenNumberOfScreen (screen), + dpyinfo->n_planes, class, &vinfo)) + fatal ("Invalid visual specification `%s'", XSTRING (value)->data); + + dpyinfo->visual = vinfo.visual; } + else + { + int n_visuals; + XVisualInfo *vinfo, vinfo_template; + + dpyinfo->visual = DefaultVisualOfScreen (screen); + +#ifdef HAVE_X11R4 + vinfo_template.visualid = XVisualIDFromVisual (dpyinfo->visual); +#else + vinfo_template.visualid = dpyinfo->visual->visualid; +#endif + vinfo_template.screen = XScreenNumberOfScreen (screen); + vinfo = XGetVisualInfo (dpy, VisualIDMask | VisualScreenMask, + &vinfo_template, &n_visuals); + if (n_visuals != 1) + fatal ("Can't get proper X visual info"); - XFree ((char *) vinfo); - return v; + dpyinfo->n_planes = vinfo->depth; + XFree ((char *) vinfo); + } } + /* Return the X display structure for the display named NAME. Open a new connection if necessary. */ @@ -5267,6 +5033,7 @@ x_display_info_for_name (name) return dpyinfo; } + DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection, 1, 3, 0, "Open a connection to an X server.\n\ DISPLAY is the name of the display to connect to.\n\ @@ -5335,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); } @@ -5413,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. */ @@ -5428,14 +5191,15 @@ Lisp_Object Qxbm; /* Keywords. */ -Lisp_Object QCtype, QCdata, QCascent, QCmargin, QCrelief; 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. */ @@ -5448,8 +5212,9 @@ 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 int x_build_heuristic_mask P_ ((struct frame *, Lisp_Object, - struct image *, Lisp_Object)); +static void x_emboss P_ ((struct frame *, struct image *)); +static int x_build_heuristic_mask P_ ((struct frame *, struct image *, + Lisp_Object)); /* Define a new image type from TYPE. This adds a copy of TYPE to @@ -5539,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, @@ -5641,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; @@ -5721,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; +} + /*********************************************************************** @@ -5798,61 +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 (img->ncolors) + + if (mask_p && img->mask) { - int class = FRAME_X_DISPLAY_INFO (f)->visual->class; - - /* If display has an immutable color map, freeing colors is not - necessary and some servers don't allow it. So don't do it. */ - if (class != StaticColor - && class != StaticGray - && class != TrueColor) - { - Colormap cmap; - BLOCK_INPUT; - cmap = DefaultColormapOfScreen (FRAME_X_DISPLAY_INFO (f)->screen); - XFreeColors (FRAME_X_DISPLAY (f), cmap, img->colors, - img->ncolors, 0); - UNBLOCK_INPUT; - } + XFreePixmap (FRAME_X_DISPLAY (f), img->mask); + img->mask = None; + } + if (colors_p && img->ncolors) + { + x_free_colors (f, img->colors, img->ncolors); 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 @@ -5934,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; } } @@ -5959,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; } } @@ -5980,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; } } @@ -6045,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 @@ -6067,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) @@ -6086,19 +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)) + /* 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) { - file = image_spec_value (spec, QCfile, NULL); - x_build_heuristic_mask (f, file, img, heuristic_mask); + 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'. */ @@ -6178,9 +6120,8 @@ forall_images_in_image_cache (f, fn) X support code ***********************************************************************/ -static int x_create_x_image_and_pixmap P_ ((struct frame *, Lisp_Object, - int, int, int, XImage **, - Pixmap *)); +static int x_create_x_image_and_pixmap P_ ((struct frame *, int, int, int, + XImage **, Pixmap *)); static void x_destroy_x_image P_ ((XImage *)); static void x_put_x_image P_ ((struct frame *, XImage *, Pixmap, int, int)); @@ -6189,13 +6130,11 @@ static void x_put_x_image P_ ((struct frame *, XImage *, Pixmap, int, int)); frame F. Set *XIMG and *PIXMAP to the XImage and Pixmap created. Set (*XIMG)->data to a raster of WIDTH x HEIGHT pixels allocated via xmalloc. Print error messages via image_error if an error - occurs. FILE is the name of an image file being processed, for - error messages. Value is non-zero if successful. */ + occurs. Value is non-zero if successful. */ static int -x_create_x_image_and_pixmap (f, file, width, height, depth, ximg, pixmap) +x_create_x_image_and_pixmap (f, width, height, depth, ximg, pixmap) struct frame *f; - Lisp_Object file; int width, height, depth; XImage **ximg; Pixmap *pixmap; @@ -6213,7 +6152,7 @@ x_create_x_image_and_pixmap (f, file, width, height, depth, ximg, pixmap) depth > 16 ? 32 : depth > 8 ? 16 : 8, 0); if (*ximg == NULL) { - image_error ("Unable to allocate X image for %s", file, Qnil); + image_error ("Unable to allocate X image", Qnil, Qnil); return 0; } @@ -6222,11 +6161,11 @@ x_create_x_image_and_pixmap (f, file, 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; - image_error ("Unable to create pixmap for `%s'", file, Qnil); + image_error ("Unable to create X pixmap", Qnil, Qnil); return 0; } @@ -6270,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 @@ -6294,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); @@ -6304,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. */ @@ -6333,6 +6312,7 @@ enum xbm_keyword_index XBM_RELIEF, XBM_ALGORITHM, XBM_HEURISTIC_MASK, + XBM_MASK, XBM_LAST }; @@ -6348,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. */ @@ -6395,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) @@ -6417,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; @@ -6478,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; } @@ -6494,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') @@ -6532,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; @@ -6588,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)) \ @@ -6602,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 == '#') @@ -6631,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"); @@ -6668,7 +6669,6 @@ xbm_read_bitmap_file_data (file, width, height, data) if (v10) { - for (i = 0; i < nbytes; i += 2) { int val = value; @@ -6700,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; @@ -6719,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)); @@ -6766,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), @@ -6776,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); + 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. */ @@ -6810,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; - /* Parse the list specification. */ + /* 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 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) @@ -6844,112 +6869,319 @@ 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", Qnil, Qnil); - x_clear_image (f, img); - } + return success_p; +} + - UNBLOCK_INPUT; + +/*********************************************************************** + XPM images + ***********************************************************************/ + +#if HAVE_XPM + +static int xpm_image_p P_ ((Lisp_Object object)); +static int xpm_load P_ ((struct frame *f, struct image *img)); +static int xpm_valid_color_symbols_p P_ ((Lisp_Object)); + +#include "X11/xpm.h" + +/* The symbol `xpm' identifying XPM-format images. */ + +Lisp_Object Qxpm; + +/* Indices of image specification fields in xpm_format, below. */ + +enum xpm_keyword_index +{ + XPM_TYPE, + XPM_FILE, + XPM_DATA, + XPM_ASCENT, + XPM_MARGIN, + XPM_RELIEF, + XPM_ALGORITHM, + XPM_HEURISTIC_MASK, + XPM_MASK, + XPM_COLOR_SYMBOLS, + XPM_LAST +}; + +/* Vector of image_keyword structures describing the format + of valid XPM image specifications. */ + +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_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} +}; + +/* Structure describing the image type XBM. */ + +static struct image_type xpm_type = +{ + &Qxpm, + xpm_image_p, + xpm_load, + x_clear_image, + NULL +}; + + +/* 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); + } - return success_p; + xfree (xpm_color_cache); + xpm_color_cache = NULL; + free_color_table (); } - - -/*********************************************************************** - XPM images - ***********************************************************************/ -#if HAVE_XPM +/* Return the bucket index for color named COLOR_NAME in the color + cache. */ -static int xpm_image_p P_ ((Lisp_Object object)); -static int xpm_load P_ ((struct frame *f, struct image *img)); -static int xpm_valid_color_symbols_p P_ ((Lisp_Object)); +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; +} -#include "X11/xpm.h" -/* The symbol `xpm' identifying XPM-format images. */ +/* 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. */ -Lisp_Object Qxpm; +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; +} -/* Indices of image specification fields in xpm_format, below. */ -enum xpm_keyword_index +/* 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; { - XPM_TYPE, - XPM_FILE, - XPM_DATA, - XPM_ASCENT, - XPM_MARGIN, - XPM_RELIEF, - XPM_ALGORITHM, - XPM_HEURISTIC_MASK, - XPM_COLOR_SYMBOLS, - XPM_LAST -}; + struct xpm_cached_color *p; + int h = xpm_color_bucket (color_name); -/* Vector of image_keyword structures describing the format - of valid XPM image specifications. */ + for (p = xpm_color_cache[h]; p; p = p->next) + if (strcmp (p->name, color_name) == 0) + break; -static struct image_keyword xpm_format[XPM_LAST] = + 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; { - {":type", IMAGE_SYMBOL_VALUE, 1}, - {":file", IMAGE_STRING_VALUE, 0}, - {":data", IMAGE_STRING_VALUE, 0}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_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}, - {":color-symbols", IMAGE_DONT_CHECK_VALUE_TYPE, 0} -}; + return xpm_lookup_color ((struct frame *) closure, color_name, color); +} -/* Structure describing the image type XBM. */ -static struct image_type xpm_type = +/* 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; { - &Qxpm, - xpm_image_p, - xpm_load, - x_clear_image, - NULL -}; + return 1; +} + +#endif /* ALLOC_XPM_COLORS */ /* Value is non-zero if COLOR_SYMBOLS is a valid color symbols list @@ -6988,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))); } @@ -7009,16 +7239,29 @@ xpm_load (f, img) /* Configure the XPM lib. Use the visual of frame F. Allocate close colors. Return colors allocated. */ bzero (&attrs, sizeof attrs); - attrs.visual = FRAME_X_DISPLAY_INFO (f)->visual; + attrs.visual = FRAME_X_VISUAL (f); + 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'. */ @@ -7058,15 +7301,17 @@ 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)) { Lisp_Object file = x_find_image_file (specified_file); if (!STRINGP (file)) { - image_error ("Cannot find image file %s", specified_file, Qnil); - UNBLOCK_INPUT; + image_error ("Cannot find image file `%s'", specified_file, Qnil); return 0; } @@ -7082,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 { @@ -7128,6 +7378,9 @@ xpm_load (f, img) } } +#ifdef ALLOC_XPM_COLORS + xpm_free_color_cache (); +#endif return rc == XpmSuccess; } @@ -7165,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 @@ -7234,10 +7478,8 @@ lookup_rgb_color (f, r, g, b) color.green = g; color.blue = b; - BLOCK_INPUT; - cmap = DefaultColormapOfScreen (FRAME_X_SCREEN (f)); + cmap = FRAME_X_COLORMAP (f); rc = x_alloc_nearest_color (f, cmap, &color); - UNBLOCK_INPUT; if (rc) { @@ -7280,13 +7522,10 @@ lookup_pixel_color (f, pixel) Colormap cmap; int rc; - BLOCK_INPUT; - - cmap = DefaultColormapOfScreen (FRAME_X_SCREEN (f)); + 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) { @@ -7348,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'. */ + +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; +} -/* 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. */ +/* 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 = DefaultColormapOfScreen (FRAME_X_SCREEN (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, Qnil, 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)); +} + + +/* Transform image IMG on frame F so that it looks disabled. */ - x_laplace_read_row (f, cmap, in[rowa], img->width, ximg, in_y++); +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 (x = 0; x < img->width - 2; ++x) + 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); + } + } } @@ -7488,27 +7906,27 @@ x_laplace (f, img) heuristically. Value is non-zero if successful. */ static int -x_build_heuristic_mask (f, file, img, how) +x_build_heuristic_mask (f, img, how) struct frame *f; - Lisp_Object file; struct image *img; Lisp_Object 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, file, img->width, img->height, 1, + 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, @@ -7539,7 +7957,7 @@ x_build_heuristic_mask (f, file, img, how) sprintf (color_name, "#%04x%04x%04x", rgb[0], rgb[1], rgb[2]); - cmap = DefaultColormapOfScreen (FRAME_X_SCREEN (f)); + cmap = FRAME_X_COLORMAP (f); if (XLookupColor (dpy, cmap, color_name, &exact, &color)) { bg = color.pixel; @@ -7584,7 +8002,6 @@ x_build_heuristic_mask (f, file, img, how) x_destroy_x_image (mask_img); XDestroyImage (ximg); - UNBLOCK_INPUT; return 1; } @@ -7596,7 +8013,7 @@ x_build_heuristic_mask (f, file, img, how) static int pbm_image_p P_ ((Lisp_Object object)); static int pbm_load P_ ((struct frame *f, struct image *img)); -static int pbm_scan_number P_ ((FILE *fp)); +static int pbm_scan_number P_ ((unsigned char **, unsigned char *)); /* The symbol `pbm' identifying images of this type. */ @@ -7608,11 +8025,15 @@ enum pbm_keyword_index { PBM_TYPE, PBM_FILE, + PBM_DATA, PBM_ASCENT, PBM_MARGIN, PBM_RELIEF, PBM_ALGORITHM, PBM_HEURISTIC_MASK, + PBM_MASK, + PBM_FOREGROUND, + PBM_BACKGROUND, PBM_LAST }; @@ -7622,12 +8043,16 @@ enum pbm_keyword_index static struct image_keyword pbm_format[PBM_LAST] = { {":type", IMAGE_SYMBOL_VALUE, 1}, - {":file", IMAGE_STRING_VALUE, 1}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":file", IMAGE_STRING_VALUE, 0}, + {":data", IMAGE_STRING_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'. */ @@ -7652,40 +8077,41 @@ 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; - return 1; + + /* Must specify either :data or :file. */ + return fmt[PBM_DATA].count + fmt[PBM_FILE].count == 1; } -/* Scan a decimal number from PBM input file FP and return it. Value - is -1 at end of file or if an error occurs. */ +/* Scan a decimal number from *S and return it. Advance *S while + reading the number. END is the end of the string. Value is -1 at + end of input. */ static int -pbm_scan_number (fp) - FILE *fp; +pbm_scan_number (s, end) + unsigned char **s, *end; { - int c, val = -1; + int c = 0, val = -1; - while (!feof (fp)) + while (*s < end) { /* Skip white-space. */ - while ((c = fgetc (fp)) != EOF && isspace (c)) + while (*s < end && (c = *(*s)++, isspace (c))) ; if (c == '#') { /* Skip comment to end of line. */ - while ((c = fgetc (fp)) != EOF && c != '\n') + while (*s < end && (c = *(*s)++, c != '\n')) ; } else if (isdigit (c)) { /* Read decimal number. */ val = c - '0'; - while ((c = fgetc (fp)) != EOF && isdigit (c)) + while (*s < end && (c = *(*s)++, isdigit (c))) val = 10 * val + c - '0'; break; } @@ -7704,50 +8130,60 @@ pbm_load (f, img) struct frame *f; struct image *img; { - FILE *fp; - char magic[2]; int raw_p, x, y; int width, height, max_color_idx = 0; XImage *ximg; Lisp_Object file, specified_file; enum {PBM_MONO, PBM_GRAY, PBM_COLOR} type; struct gcpro gcpro1; + unsigned char *contents = NULL; + unsigned char *end, *p; + int size; specified_file = image_spec_value (img->spec, QCfile, NULL); - file = x_find_image_file (specified_file); - GCPRO1 (file); - if (!STRINGP (file)) - { - image_error ("Cannot find image file %s", specified_file, Qnil); - UNGCPRO; - return 0; - } + file = Qnil; + GCPRO1 (file); - fp = fopen (XSTRING (file)->data, "r"); - if (fp == NULL) + if (STRINGP (specified_file)) { - UNGCPRO; - return 0; - } + file = x_find_image_file (specified_file); + if (!STRINGP (file)) + { + image_error ("Cannot find image file `%s'", specified_file, Qnil); + UNGCPRO; + return 0; + } + + contents = slurp_file (XSTRING (file)->data, &size); + if (contents == NULL) + { + image_error ("Error reading `%s'", file, Qnil); + UNGCPRO; + return 0; + } - /* Read first two characters. */ - if (fread (magic, sizeof *magic, 2, fp) != 2) + p = contents; + end = contents + size; + } + else { - fclose (fp); - image_error ("Not a PBM image file: %s", file, Qnil); - UNGCPRO; - return 0; + Lisp_Object data; + data = image_spec_value (img->spec, QCdata, NULL); + p = XSTRING (data)->data; + end = p + STRING_BYTES (XSTRING (data)); } - if (*magic != 'P') + /* Check magic number. */ + if (end - p < 2 || *p++ != 'P') { - fclose (fp); - image_error ("Not a PBM image file: %s", file, Qnil); + image_error ("Not a PBM image: `%s'", img->spec, Qnil); + error: + xfree (contents); UNGCPRO; return 0; } - switch (magic[1]) + switch (*p++) { case '1': raw_p = 0, type = PBM_MONO; @@ -7774,41 +8210,30 @@ pbm_load (f, img) break; default: - fclose (fp); - image_error ("Not a PBM image file: %s", file, Qnil); - UNGCPRO; - return 0; + image_error ("Not a PBM image: `%s'", img->spec, Qnil); + goto error; } /* Read width, height, maximum color-component. Characters starting with `#' up to the end of a line are ignored. */ - width = pbm_scan_number (fp); - height = pbm_scan_number (fp); + width = pbm_scan_number (&p, end); + height = pbm_scan_number (&p, end); if (type != PBM_MONO) { - max_color_idx = pbm_scan_number (fp); + max_color_idx = pbm_scan_number (&p, end); if (raw_p && max_color_idx > 255) max_color_idx = 255; } - if (width < 0 || height < 0 + if (width < 0 + || height < 0 || (type != PBM_MONO && max_color_idx < 0)) - { - fclose (fp); - UNGCPRO; - return 0; - } + goto error; - BLOCK_INPUT; - if (!x_create_x_image_and_pixmap (f, file, width, height, 0, + if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) - { - fclose (fp); - UNBLOCK_INPUT; - UNGCPRO; - return 0; - } + goto error; /* Initialize the color hash table. */ init_color_table (); @@ -7816,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) @@ -7823,16 +8261,14 @@ pbm_load (f, img) if (raw_p) { if ((x & 7) == 0) - c = fgetc (fp); + c = *p++; g = c & 0x80; c <<= 1; } else - g = pbm_scan_number (fp); + 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 @@ -7843,31 +8279,28 @@ pbm_load (f, img) int r, g, b; if (type == PBM_GRAY) - r = g = b = raw_p ? fgetc (fp) : pbm_scan_number (fp); + r = g = b = raw_p ? *p++ : pbm_scan_number (&p, end); else if (raw_p) { - r = fgetc (fp); - g = fgetc (fp); - b = fgetc (fp); + r = *p++; + g = *p++; + b = *p++; } else { - r = pbm_scan_number (fp); - g = pbm_scan_number (fp); - b = pbm_scan_number (fp); + r = pbm_scan_number (&p, end); + g = pbm_scan_number (&p, end); + b = pbm_scan_number (&p, end); } if (r < 0 || g < 0 || b < 0) { - fclose (fp); xfree (ximg->data); ximg->data = NULL; XDestroyImage (ximg); - UNBLOCK_INPUT; - image_error ("Invalid pixel value in file `%s'", - file, Qnil); - UNGCPRO; - return 0; + image_error ("Invalid pixel value in image `%s'", + img->spec, Qnil); + goto error; } /* RGB values are now in the range 0..max_color_idx. @@ -7879,8 +8312,6 @@ pbm_load (f, img) } } - fclose (fp); - /* Store in IMG->colors the colors allocated for the image, and free the color table. */ img->colors = colors_in_color_table (&img->ncolors); @@ -7889,12 +8320,12 @@ 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; UNGCPRO; + xfree (contents); return 1; } @@ -7922,12 +8353,14 @@ Lisp_Object Qpng; enum png_keyword_index { PNG_TYPE, + PNG_DATA, PNG_FILE, PNG_ASCENT, PNG_MARGIN, PNG_RELIEF, PNG_ALGORITHM, PNG_HEURISTIC_MASK, + PNG_MASK, PNG_LAST }; @@ -7937,12 +8370,14 @@ enum png_keyword_index static struct image_keyword png_format[PNG_LAST] = { {":type", IMAGE_SYMBOL_VALUE, 1}, - {":file", IMAGE_STRING_VALUE, 1}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":data", IMAGE_STRING_VALUE, 0}, + {":file", IMAGE_STRING_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'. */ @@ -7966,11 +8401,11 @@ 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; - return 1; + + /* Must specify either the :data or :file keyword. */ + return fmt[PNG_FILE].count + fmt[PNG_DATA].count == 1; } @@ -7997,6 +8432,35 @@ my_png_warning (png_ptr, msg) image_error ("PNG warning: %s", build_string (msg), Qnil); } +/* Memory source for PNG decoding. */ + +struct png_memory_storage +{ + unsigned char *bytes; /* The data */ + size_t len; /* How big is it? */ + int index; /* Where are we? */ +}; + + +/* Function set as reader function when reading PNG image from memory. + PNG_PTR is a pointer to the PNG control structure. Copy LENGTH + bytes from the input to DATA. */ + +static void +png_read_from_memory (png_ptr, data, length) + png_structp png_ptr; + png_bytep data; + png_size_t length; +{ + struct png_memory_storage *tbr + = (struct png_memory_storage *) png_get_io_ptr (png_ptr); + + if (length > tbr->len - tbr->index) + png_error (png_ptr, "Read error"); + + bcopy (tbr->bytes + tbr->index, data, length); + tbr->index = tbr->index + length; +} /* Load PNG image IMG for use on frame F. Value is non-zero if successful. */ @@ -8007,15 +8471,16 @@ png_load (f, img) struct image *img; { Lisp_Object file, specified_file; + Lisp_Object specified_data; int x, y, i; XImage *ximg, *mask_img = NULL; struct gcpro gcpro1; png_struct *png_ptr = NULL; png_info *info_ptr = NULL, *end_info = NULL; - FILE *fp; + 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; @@ -8024,36 +8489,62 @@ png_load (f, img) char *gamma_str; double screen_gamma, image_gamma; int intent; + struct png_memory_storage tbr; /* Data to be read */ /* Find out what file to load. */ specified_file = image_spec_value (img->spec, QCfile, NULL); - file = x_find_image_file (specified_file); + specified_data = image_spec_value (img->spec, QCdata, NULL); + file = Qnil; GCPRO1 (file); - if (!STRINGP (file)) - { - image_error ("Cannot find image file %s", specified_file, Qnil); - UNGCPRO; - return 0; - } - /* Open the image file. */ - fp = fopen (XSTRING (file)->data, "rb"); - if (!fp) + if (NILP (specified_data)) { - image_error ("Cannot open image file %s", file, Qnil); - UNGCPRO; - fclose (fp); - return 0; - } + file = x_find_image_file (specified_file); + if (!STRINGP (file)) + { + image_error ("Cannot find image file `%s'", specified_file, Qnil); + UNGCPRO; + return 0; + } + + /* Open the image file. */ + fp = fopen (XSTRING (file)->data, "rb"); + if (!fp) + { + image_error ("Cannot open image file `%s'", file, Qnil); + UNGCPRO; + fclose (fp); + return 0; + } - /* Check PNG signature. */ - if (fread (sig, 1, sizeof sig, fp) != sizeof sig - || !png_check_sig (sig, sizeof sig)) + /* Check PNG signature. */ + if (fread (sig, 1, sizeof sig, fp) != sizeof sig + || !png_check_sig (sig, sizeof sig)) + { + image_error ("Not a PNG file: `%s'", file, Qnil); + UNGCPRO; + fclose (fp); + return 0; + } + } + else { - image_error ("Not a PNG file: %s", file, Qnil); - UNGCPRO; - fclose (fp); - return 0; + /* Read from memory. */ + tbr.bytes = XSTRING (specified_data)->data; + tbr.len = STRING_BYTES (XSTRING (specified_data)); + tbr.index = 0; + + /* Check PNG signature. */ + if (tbr.len < sizeof sig + || !png_check_sig (tbr.bytes, sizeof sig)) + { + image_error ("Not a PNG image: `%s'", img->spec, Qnil); + UNGCPRO; + return 0; + } + + /* Need to skip past the signature. */ + tbr.bytes += sizeof (sig); } /* Initialize read and info structs for PNG lib. */ @@ -8061,7 +8552,7 @@ png_load (f, img) my_png_error, my_png_warning); if (!png_ptr) { - fclose (fp); + if (fp) fclose (fp); UNGCPRO; return 0; } @@ -8070,7 +8561,7 @@ png_load (f, img) if (!info_ptr) { png_destroy_read_struct (&png_ptr, NULL, NULL); - fclose (fp); + if (fp) fclose (fp); UNGCPRO; return 0; } @@ -8079,7 +8570,7 @@ png_load (f, img) if (!end_info) { png_destroy_read_struct (&png_ptr, &info_ptr, NULL); - fclose (fp); + if (fp) fclose (fp); UNGCPRO; return 0; } @@ -8093,14 +8584,17 @@ png_load (f, img) png_destroy_read_struct (&png_ptr, &info_ptr, &end_info); xfree (pixels); xfree (rows); - if (fp) - fclose (fp); + if (fp) fclose (fp); UNGCPRO; return 0; } /* Read image info. */ - png_init_io (png_ptr, fp); + if (!NILP (specified_data)) + png_set_read_fn (png_ptr, (void *) &tbr, png_read_from_memory); + else + png_init_io (png_ptr, fp); + png_set_sig_bytes (png_ptr, sizeof sig); png_read_info (png_ptr, info_ptr); png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, @@ -8170,11 +8664,9 @@ png_load (f, img) Colormap cmap; png_color_16 frame_background; - BLOCK_INPUT; - cmap = DefaultColormapOfScreen (FRAME_X_SCREEN (f)); + 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; @@ -8209,30 +8701,27 @@ png_load (f, img) /* Read the entire image. */ png_read_image (png_ptr, rows); png_read_end (png_ptr, info_ptr); - fclose (fp); - fp = NULL; + if (fp) + { + fclose (fp); + fp = NULL; + } - BLOCK_INPUT; - /* Create the X image and pixmap. */ - if (!x_create_x_image_and_pixmap (f, file, width, height, 0, &ximg, + 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. */ if (channels == 4 && !transparent_p - && !x_create_x_image_and_pixmap (f, file, width, height, 1, + && !x_create_x_image_and_pixmap (f, width, height, 1, &mask_img, &img->mask)) { x_destroy_x_image (ximg); XFreePixmap (FRAME_X_DISPLAY (f), img->pixmap); - img->pixmap = 0; - UNBLOCK_INPUT; + img->pixmap = None; goto error; } @@ -8300,7 +8789,6 @@ png_load (f, img) x_destroy_x_image (mask_img); } - UNBLOCK_INPUT; UNGCPRO; return 1; } @@ -8342,12 +8830,14 @@ Lisp_Object Qjpeg; enum jpeg_keyword_index { JPEG_TYPE, + JPEG_DATA, JPEG_FILE, JPEG_ASCENT, JPEG_MARGIN, JPEG_RELIEF, JPEG_ALGORITHM, JPEG_HEURISTIC_MASK, + JPEG_MASK, JPEG_LAST }; @@ -8357,12 +8847,14 @@ enum jpeg_keyword_index static struct image_keyword jpeg_format[JPEG_LAST] = { {":type", IMAGE_SYMBOL_VALUE, 1}, - {":file", IMAGE_STRING_VALUE, 1}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":data", IMAGE_STRING_VALUE, 0}, + {":file", IMAGE_STRING_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'. */ @@ -8387,19 +8879,21 @@ 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; - return 1; + + /* Must specify either the :data or :file keyword. */ + return fmt[JPEG_FILE].count + fmt[JPEG_DATA].count == 1; } + struct my_jpeg_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; + static void my_error_exit (cinfo) j_common_ptr cinfo; @@ -8408,6 +8902,103 @@ 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. */ + +static void +our_init_source (cinfo) + j_decompress_ptr cinfo; +{ +} + + +/* Fill input buffer method for JPEG data source manager. Called + whenever more data is needed. We read the whole image in one step, + so this only adds a fake end of input marker at the end. */ + +static boolean +our_fill_input_buffer (cinfo) + j_decompress_ptr cinfo; +{ + /* Insert a fake EOI marker. */ + struct jpeg_source_mgr *src = cinfo->src; + static JOCTET buffer[2]; + + buffer[0] = (JOCTET) 0xFF; + buffer[1] = (JOCTET) JPEG_EOI; + + src->next_input_byte = buffer; + src->bytes_in_buffer = 2; + return TRUE; +} + + +/* Method to skip over NUM_BYTES bytes in the image data. CINFO->src + is the JPEG data source manager. */ + +static void +our_skip_input_data (cinfo, num_bytes) + j_decompress_ptr cinfo; + long num_bytes; +{ + struct jpeg_source_mgr *src = (struct jpeg_source_mgr *) cinfo->src; + + if (src) + { + if (num_bytes > src->bytes_in_buffer) + ERREXIT (cinfo, JERR_INPUT_EOF); + + src->bytes_in_buffer -= num_bytes; + src->next_input_byte += num_bytes; + } +} + + +/* Method to terminate data source. Called by + jpeg_finish_decompress() after all data has been processed. */ + +static void +our_term_source (cinfo) + j_decompress_ptr cinfo; +{ +} + + +/* Set up the JPEG lib for reading an image from DATA which contains + LEN bytes. CINFO is the decompression info structure created for + reading the image. */ + +static void +jpeg_memory_src (cinfo, data, len) + j_decompress_ptr cinfo; + JOCTET *data; + unsigned int len; +{ + struct jpeg_source_mgr *src; + + if (cinfo->src == NULL) + { + /* First time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof (struct jpeg_source_mgr)); + src = (struct jpeg_source_mgr *) cinfo->src; + src->next_input_byte = data; + } + + src = (struct jpeg_source_mgr *) cinfo->src; + src->init_source = our_init_source; + src->fill_input_buffer = our_fill_input_buffer; + src->skip_input_data = our_skip_input_data; + src->resync_to_restart = jpeg_resync_to_restart; /* Use default method. */ + src->term_source = our_term_source; + src->bytes_in_buffer = len; + src->next_input_byte = data; +} + + /* Load image IMG for use on frame F. Patterned after example.c from the JPEG lib. */ @@ -8419,7 +9010,8 @@ jpeg_load (f, img) struct jpeg_decompress_struct cinfo; struct my_jpeg_error_mgr mgr; Lisp_Object file, specified_file; - FILE *fp; + Lisp_Object specified_data; + FILE * volatile fp = NULL; JSAMPARRAY buffer; int row_stride, x, y; XImage *ximg = NULL; @@ -8430,28 +9022,33 @@ jpeg_load (f, img) /* Open the JPEG file. */ specified_file = image_spec_value (img->spec, QCfile, NULL); - file = x_find_image_file (specified_file); + specified_data = image_spec_value (img->spec, QCdata, NULL); + file = Qnil; GCPRO1 (file); - if (!STRINGP (file)) + + if (NILP (specified_data)) { - image_error ("Cannot find image file %s", specified_file, Qnil); - UNGCPRO; - return 0; - } + file = x_find_image_file (specified_file); + if (!STRINGP (file)) + { + image_error ("Cannot find image file `%s'", specified_file, Qnil); + UNGCPRO; + return 0; + } - fp = fopen (XSTRING (file)->data, "r"); - if (fp == NULL) - { - image_error ("Cannot open `%s'", file, Qnil); - UNGCPRO; - return 0; + fp = fopen (XSTRING (file)->data, "r"); + if (fp == NULL) + { + image_error ("Cannot open `%s'", file, Qnil); + UNGCPRO; + return 0; + } } - /* 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; + /* Customize libjpeg's error handling to call my_error_exit when an + error is detected. This function will perform a longjmp. */ cinfo.err = jpeg_std_error (&mgr.pub); + mgr.pub.error_exit = my_error_exit; if ((rc = setjmp (mgr.setjmp_buffer)) != 0) { @@ -8460,49 +9057,47 @@ jpeg_load (f, img) /* Called from my_error_exit. Display a JPEG error. */ char buffer[JMSG_LENGTH_MAX]; cinfo.err->format_message ((j_common_ptr) &cinfo, buffer); - image_error ("Error reading JPEG file `%s': %s", file, + image_error ("Error reading JPEG image `%s': %s", img->spec, build_string (buffer)); } /* Close the input file and destroy the JPEG object. */ - fclose (fp); + if (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; } /* Create the JPEG decompression object. Let it read from fp. - Read the JPEG image header. */ + Read the JPEG image header. */ jpeg_create_decompress (&cinfo); - jpeg_stdio_src (&cinfo, fp); + + if (NILP (specified_data)) + jpeg_stdio_src (&cinfo, (FILE *) fp); + else + jpeg_memory_src (&cinfo, XSTRING (specified_data)->data, + STRING_BYTES (XSTRING (specified_data))); + jpeg_read_header (&cinfo, TRUE); /* Customize decompression so that color quantization will be used. - Start decompression. */ + Start decompression. */ cinfo.quantize_colors = TRUE; jpeg_start_decompress (&cinfo); 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, file, width, height, 0, &ximg, - &img->pixmap)) - { - UNBLOCK_INPUT; - longjmp (mgr.setjmp_buffer, 2); - } + if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) + longjmp (mgr.setjmp_buffer, 2); /* Allocate colors. When color quantization is used, cinfo.actual_number_of_colors has been set with the number of @@ -8556,12 +9151,12 @@ jpeg_load (f, img) /* Clean up. */ jpeg_finish_decompress (&cinfo); jpeg_destroy_decompress (&cinfo); - fclose (fp); + if (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; } @@ -8590,12 +9185,14 @@ Lisp_Object Qtiff; enum tiff_keyword_index { TIFF_TYPE, + TIFF_DATA, TIFF_FILE, TIFF_ASCENT, TIFF_MARGIN, TIFF_RELIEF, TIFF_ALGORITHM, TIFF_HEURISTIC_MASK, + TIFF_MASK, TIFF_LAST }; @@ -8605,12 +9202,14 @@ enum tiff_keyword_index static struct image_keyword tiff_format[TIFF_LAST] = { {":type", IMAGE_SYMBOL_VALUE, 1}, - {":file", IMAGE_STRING_VALUE, 1}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":data", IMAGE_STRING_VALUE, 0}, + {":file", IMAGE_STRING_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'. */ @@ -8634,11 +9233,128 @@ 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; - return 1; + + /* Must specify either the :data or :file keyword. */ + return fmt[TIFF_FILE].count + fmt[TIFF_DATA].count == 1; +} + + +/* Reading from a memory buffer for TIFF images Based on the PNG + memory source, but we have to provide a lot of extra functions. + Blah. + + We really only need to implement read and seek, but I am not + convinced that the TIFF library is smart enough not to destroy + itself if we only hand it the function pointers we need to + override. */ + +typedef struct +{ + unsigned char *bytes; + size_t len; + int index; +} +tiff_memory_source; + + +static size_t +tiff_read_from_memory (data, buf, size) + thandle_t data; + tdata_t buf; + tsize_t size; +{ + tiff_memory_source *src = (tiff_memory_source *) data; + + if (size > src->len - src->index) + return (size_t) -1; + bcopy (src->bytes + src->index, buf, size); + src->index += size; + return size; +} + + +static size_t +tiff_write_from_memory (data, buf, size) + thandle_t data; + tdata_t buf; + tsize_t size; +{ + return (size_t) -1; +} + + +static toff_t +tiff_seek_in_memory (data, off, whence) + thandle_t data; + toff_t off; + int whence; +{ + tiff_memory_source *src = (tiff_memory_source *) data; + int idx; + + switch (whence) + { + case SEEK_SET: /* Go from beginning of source. */ + idx = off; + break; + + case SEEK_END: /* Go from end of source. */ + idx = src->len + off; + break; + + case SEEK_CUR: /* Go from current position. */ + idx = src->index + off; + break; + + default: /* Invalid `whence'. */ + return -1; + } + + if (idx > src->len || idx < 0) + return -1; + + src->index = idx; + return src->index; +} + + +static int +tiff_close_memory (data) + thandle_t data; +{ + /* NOOP */ + return 0; +} + + +static int +tiff_mmap_memory (data, pbase, psize) + thandle_t data; + tdata_t *pbase; + toff_t *psize; +{ + /* It is already _IN_ memory. */ + return 0; +} + + +static void +tiff_unmap_memory (data, base, size) + thandle_t data; + tdata_t base; + toff_t size; +{ + /* We don't need to do this. */ +} + + +static toff_t +tiff_size_of_memory (data) + thandle_t data; +{ + return ((tiff_memory_source *) data)->len; } @@ -8651,30 +9367,62 @@ tiff_load (f, img) struct image *img; { Lisp_Object file, specified_file; + Lisp_Object specified_data; TIFF *tiff; int width, height, x, y; uint32 *buf; int rc; XImage *ximg; struct gcpro gcpro1; + tiff_memory_source memsrc; specified_file = image_spec_value (img->spec, QCfile, NULL); - file = x_find_image_file (specified_file); + specified_data = image_spec_value (img->spec, QCdata, NULL); + file = Qnil; GCPRO1 (file); - if (!STRINGP (file)) + + if (NILP (specified_data)) { - image_error ("Cannot find image file %s", file, Qnil); - UNGCPRO; - return 0; + /* Read from a file */ + file = x_find_image_file (specified_file); + if (!STRINGP (file)) + { + image_error ("Cannot find image file `%s'", file, Qnil); + UNGCPRO; + return 0; + } + + /* Try to open the image file. */ + tiff = TIFFOpen (XSTRING (file)->data, "r"); + if (tiff == NULL) + { + image_error ("Cannot open `%s'", file, Qnil); + UNGCPRO; + return 0; + } } - - /* Try to open the image file. */ - tiff = TIFFOpen (XSTRING (file)->data, "r"); - if (tiff == NULL) + else { - image_error ("Cannot open `%s'", file, Qnil); - UNGCPRO; - return 0; + /* Memory source! */ + memsrc.bytes = XSTRING (specified_data)->data; + memsrc.len = STRING_BYTES (XSTRING (specified_data)); + memsrc.index = 0; + + tiff = TIFFClientOpen ("memory_source", "r", &memsrc, + (TIFFReadWriteProc) tiff_read_from_memory, + (TIFFReadWriteProc) tiff_write_from_memory, + tiff_seek_in_memory, + tiff_close_memory, + tiff_size_of_memory, + tiff_mmap_memory, + tiff_unmap_memory); + + if (!tiff) + { + image_error ("Cannot open memory source for `%s'", img->spec, Qnil); + UNGCPRO; + return 0; + } } /* Get width and height of the image, and allocate a raster buffer @@ -8687,19 +9435,15 @@ tiff_load (f, img) TIFFClose (tiff); if (!rc) { - image_error ("Error reading `%s'", file, Qnil); + image_error ("Error reading TIFF image `%s'", img->spec, Qnil); xfree (buf); UNGCPRO; return 0; } - BLOCK_INPUT; - /* Create the X image and pixmap. */ - if (!x_create_x_image_and_pixmap (f, file, width, height, 0, &ximg, - &img->pixmap)) + if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) { - UNBLOCK_INPUT; xfree (buf); UNGCPRO; return 0; @@ -8731,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; @@ -8764,12 +9507,14 @@ Lisp_Object Qgif; enum gif_keyword_index { GIF_TYPE, + GIF_DATA, GIF_FILE, GIF_ASCENT, GIF_MARGIN, GIF_RELIEF, GIF_ALGORITHM, GIF_HEURISTIC_MASK, + GIF_MASK, GIF_IMAGE, GIF_LAST }; @@ -8780,12 +9525,14 @@ enum gif_keyword_index static struct image_keyword gif_format[GIF_LAST] = { {":type", IMAGE_SYMBOL_VALUE, 1}, - {":file", IMAGE_STRING_VALUE, 1}, - {":ascent", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}, + {":data", IMAGE_STRING_VALUE, 0}, + {":file", IMAGE_STRING_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} }; @@ -8810,11 +9557,45 @@ 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; - return 1; + + /* 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. */ + +typedef struct +{ + unsigned char *bytes; + size_t len; + int index; +} +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. */ +static gif_memory_source *current_gif_memory_src; + +static int +gif_read_from_memory (file, buf, len) + GifFileType *file; + GifByteType *buf; + int len; +{ + gif_memory_source *src = current_gif_memory_src; + + if (len > src->len - src->index) + return -1; + + bcopy (src->bytes + src->index, buf, len); + src->index += len; + return len; } @@ -8827,6 +9608,7 @@ gif_load (f, img) struct image *img; { Lisp_Object file, specified_file; + Lisp_Object specified_data; int rc, width, height, x, y, i; XImage *ximg; ColorMapObject *gif_color_map; @@ -8835,31 +9617,55 @@ gif_load (f, img) struct gcpro gcpro1; Lisp_Object image; int ino, image_left, image_top, image_width, image_height; + gif_memory_source memsrc; + unsigned char *raster; specified_file = image_spec_value (img->spec, QCfile, NULL); - file = x_find_image_file (specified_file); + specified_data = image_spec_value (img->spec, QCdata, NULL); + file = Qnil; GCPRO1 (file); - if (!STRINGP (file)) + + if (NILP (specified_data)) { - image_error ("Cannot find image file %s", specified_file, Qnil); - UNGCPRO; - return 0; - } + file = x_find_image_file (specified_file); + if (!STRINGP (file)) + { + image_error ("Cannot find image file `%s'", specified_file, Qnil); + UNGCPRO; + return 0; + } - /* Open the GIF file. */ - gif = DGifOpenFileName (XSTRING (file)->data); - if (gif == NULL) + /* Open the GIF file. */ + gif = DGifOpenFileName (XSTRING (file)->data); + if (gif == NULL) + { + image_error ("Cannot open `%s'", file, Qnil); + UNGCPRO; + return 0; + } + } + else { - image_error ("Cannot open `%s'", file, Qnil); - UNGCPRO; - return 0; + /* Read from memory! */ + current_gif_memory_src = &memsrc; + memsrc.bytes = XSTRING (specified_data)->data; + memsrc.len = STRING_BYTES (XSTRING (specified_data)); + memsrc.index = 0; + + gif = DGifOpen(&memsrc, gif_read_from_memory); + if (!gif) + { + image_error ("Cannot open memory source `%s'", img->spec, Qnil); + UNGCPRO; + return 0; + } } /* Read entire contents. */ rc = DGifSlurp (gif); if (rc == GIF_ERROR) { - image_error ("Error reading `%s'", file, Qnil); + image_error ("Error reading `%s'", img->spec, Qnil); DGifCloseFile (gif); UNGCPRO; return 0; @@ -8869,7 +9675,8 @@ gif_load (f, img) ino = INTEGERP (image) ? XFASTINT (image) : 0; if (ino >= gif->ImageCount) { - image_error ("Invalid image number `%s'", image, Qnil); + image_error ("Invalid image number `%s' in image `%s'", + image, img->spec); DGifCloseFile (gif); UNGCPRO; return 0; @@ -8878,13 +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, file, width, height, 0, &ximg, - &img->pixmap)) + if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap)) { - UNBLOCK_INPUT; DGifCloseFile (gif); UNGCPRO; return 0; @@ -8934,7 +9737,11 @@ gif_load (f, img) XPutPixel (ximg, x, y, FRAME_BACKGROUND_PIXEL (f)); } - /* Read the GIF image into the X image. */ + /* Read the GIF image into the X image. We use a local variable + `raster' here because RasterBits below is a char *, and invites + problems with bytes >= 0x80. */ + raster = (unsigned char *) gif->SavedImages[ino].RasterBits; + if (gif->SavedImages[ino].ImageDesc.Interlace) { static int interlace_start[] = {0, 4, 2, 1}; @@ -8955,8 +9762,7 @@ gif_load (f, img) for (x = 0; x < image_width; x++) { - unsigned int i - = gif->SavedImages[ino].RasterBits[(y * image_width) + x]; + int i = raster[(y * image_width) + x]; XPutPixel (ximg, x + image_left, row + image_top, pixel_colors[i]); } @@ -8969,7 +9775,7 @@ gif_load (f, img) for (y = 0; y < image_height; ++y) for (x = 0; x < image_width; ++x) { - unsigned i = gif->SavedImages[ino].RasterBits[y * image_width + x]; + int i = raster[y * image_width + x]; XPutPixel (ximg, x + image_left, y + image_top, pixel_colors[i]); } } @@ -8979,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; @@ -9020,6 +9825,7 @@ enum gs_keyword_index GS_RELIEF, GS_ALGORITHM, GS_HEURISTIC_MASK, + GS_MASK, GS_LAST }; @@ -9034,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'. */ @@ -9079,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. */ @@ -9136,17 +9941,14 @@ 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) { - image_error ("Unable to create pixmap for `%s'", - image_spec_value (img->spec, QCfile, NULL), Qnil); + image_error ("Unable to create pixmap for `%s'", img->spec, Qnil); return 0; } @@ -9210,7 +10012,7 @@ x_kill_gs_process (pixmap, f) /* On displays with a mutable colormap, figure out the colors allocated for the image by looking at the pixels of an XImage for img->pixmap. */ - class = FRAME_X_DISPLAY_INFO (f)->visual->class; + class = FRAME_X_VISUAL (f)->class; if (class != StaticColor && class != StaticGray && class != TrueColor) { XImage *ximg; @@ -9249,16 +10051,12 @@ x_kill_gs_process (pixmap, f) allocated colors on behalf of us. So, to get the reference counts right, free them once. */ if (img->ncolors) - { - Colormap cmap = DefaultColormapOfScreen (FRAME_X_SCREEN (f)); - XFreeColors (FRAME_X_DISPLAY (f), cmap, - img->colors, img->ncolors, 0); - } + x_free_colors (f, img->colors, img->ncolors); #endif } else image_error ("Cannot get X image of `%s'; colors will not be freed", - image_spec_value (img->spec, QCfile, NULL), Qnil); + img->spec, Qnil); UNBLOCK_INPUT; } @@ -9375,108 +10173,172 @@ value.") Busy cursor ***********************************************************************/ -/* The implementation partly follows a patch from - F.Pierresteguy@frcl.bull.fr dated 1994. */ +/* If non-null, an asynchronous timer that, when it expires, displays + a busy cursor on all frames. */ -/* Setting inhibit_busy_cursor to 2 inhibits busy-cursor display until - the next X event is read and we enter XTread_socket again. Setting - it to 1 inhibits busy-cursor display for direct commands. */ +static struct atimer *busy_cursor_atimer; -int inhibit_busy_cursor; +/* Non-zero means a busy cursor is currently shown. */ -/* Incremented with each call to x-display-busy-cursor. - Decremented in x-undisplay-busy-cursor. */ +static int busy_cursor_shown_p; -static int busy_count; +/* Number of seconds to wait before displaying a busy cursor. */ +static Lisp_Object Vbusy_cursor_delay; -DEFUN ("x-show-busy-cursor", Fx_show_busy_cursor, - Sx_show_busy_cursor, 0, 0, 0, - "Show a busy cursor, if not already shown.\n\ -Each call to this function must be matched by a call to\n\ -`x-hide-busy-cursor' to make the busy pointer disappear again.") - () -{ - ++busy_count; - if (busy_count == 1) - { - Lisp_Object rest, frame; +/* Default number of seconds to wait before displaying a busy + cursor. */ - FOR_EACH_FRAME (rest, frame) - if (FRAME_X_P (XFRAME (frame))) - { - struct frame *f = XFRAME (frame); - - BLOCK_INPUT; - f->output_data.x->busy_p = 1; - - if (!f->output_data.x->busy_window) - { - unsigned long mask = CWCursor; - XSetWindowAttributes attrs; - - 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); - } +#define DEFAULT_BUSY_CURSOR_DELAY 1 - XMapRaised (FRAME_X_DISPLAY (f), f->output_data.x->busy_window); - UNBLOCK_INPUT; - } - } +/* Function prototypes. */ - return Qnil; +static void show_busy_cursor P_ ((struct atimer *)); +static void hide_busy_cursor P_ ((void)); + + +/* Cancel a currently active busy-cursor timer, and start a new one. */ + +void +start_busy_cursor () +{ + EMACS_TIME delay; + int secs, usecs = 0; + + cancel_busy_cursor (); + + if (INTEGERP (Vbusy_cursor_delay) + && XINT (Vbusy_cursor_delay) > 0) + secs = XFASTINT (Vbusy_cursor_delay); + else if (FLOATP (Vbusy_cursor_delay) + && XFLOAT_DATA (Vbusy_cursor_delay) > 0) + { + Lisp_Object tem; + tem = Ftruncate (Vbusy_cursor_delay, Qnil); + secs = XFASTINT (tem); + usecs = (XFLOAT_DATA (Vbusy_cursor_delay) - secs) * 1000000; + } + else + secs = DEFAULT_BUSY_CURSOR_DELAY; + + EMACS_SET_SECS_USECS (delay, secs, usecs); + busy_cursor_atimer = start_atimer (ATIMER_RELATIVE, delay, + show_busy_cursor, NULL); } -DEFUN ("x-hide-busy-cursor", Fx_hide_busy_cursor, - Sx_hide_busy_cursor, 0, 1, 0, - "Hide a busy-cursor.\n\ -A busy-cursor will actually be undisplayed when a matching\n\ -`x-hide-busy-cursor' is called for each `x-show-busy-cursor'\n\ -issued. FORCE non-nil means hide the busy-cursor forcibly,\n\ -not counting calls.") - (force) - Lisp_Object force; +/* Cancel the busy cursor timer if active, hide a busy cursor if + shown. */ + +void +cancel_busy_cursor () { - Lisp_Object rest, frame; + if (busy_cursor_atimer) + { + cancel_atimer (busy_cursor_atimer); + busy_cursor_atimer = NULL; + } + + if (busy_cursor_shown_p) + hide_busy_cursor (); +} - if (busy_count == 0) - return Qnil; - if (!NILP (force) && busy_count != 0) - busy_count = 1; +/* Timer function of busy_cursor_atimer. TIMER is equal to + busy_cursor_atimer. - --busy_count; - if (busy_count != 0) - return Qnil; + Display a busy cursor on all frames by mapping the frames' + busy_window. Set the busy_p flag in the frames' output_data.x + structure to indicate that a busy cursor is shown on the + frames. */ + +static void +show_busy_cursor (timer) + struct atimer *timer; +{ + /* The timer implementation will cancel this timer automatically + after this function has run. Set busy_cursor_atimer to null + so that we know the timer doesn't have to be canceled. */ + busy_cursor_atimer = NULL; - FOR_EACH_FRAME (rest, frame) + if (!busy_cursor_shown_p) { - struct frame *f = XFRAME (frame); - - if (FRAME_X_P (f) - /* Watch out for newly created frames. */ - && f->output_data.x->busy_window) + Lisp_Object rest, frame; + + BLOCK_INPUT; + + FOR_EACH_FRAME (rest, frame) { + struct frame *f = XFRAME (frame); - BLOCK_INPUT; - XUnmapWindow (FRAME_X_DISPLAY (f), f->output_data.x->busy_window); - /* Sync here because XTread_socket looks at the busy_p flag - that is reset to zero below. */ - XSync (FRAME_X_DISPLAY (f), False); - UNBLOCK_INPUT; - f->output_data.x->busy_p = 0; + 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; + + attrs.cursor = f->output_data.x->busy_cursor; + + f->output_data.x->busy_window + = XCreateWindow (dpy, FRAME_OUTER_WINDOW (f), + 0, 0, 32000, 32000, 0, 0, + InputOnly, + CopyFromParent, + mask, &attrs); + } + + XMapRaised (dpy, f->output_data.x->busy_window); + XFlush (dpy); + } + } } + + busy_cursor_shown_p = 1; + UNBLOCK_INPUT; } +} - return Qnil; + +/* Hide the busy cursor on all frames, if it is currently shown. */ + +static void +hide_busy_cursor () +{ + if (busy_cursor_shown_p) + { + Lisp_Object rest, frame; + + BLOCK_INPUT; + FOR_EACH_FRAME (rest, frame) + { + struct frame *f = XFRAME (frame); + + if (FRAME_X_P (f) + /* Watch out for newly created frames. */ + && f->output_data.x->busy_window) + { + XUnmapWindow (FRAME_X_DISPLAY (f), f->output_data.x->busy_window); + /* Sync here because XTread_socket looks at the busy_p flag + that is reset to zero below. */ + XSync (FRAME_X_DISPLAY (f), False); + f->output_data.x->busy_p = 0; + } + } + + busy_cursor_shown_p = 0; + UNBLOCK_INPUT; + } } @@ -9488,9 +10350,9 @@ not counting calls.") 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. */ @@ -9498,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) @@ -9511,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; @@ -9537,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)) @@ -9569,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; @@ -9678,9 +10593,12 @@ x_create_tip_frame (dpyinfo, parms) unsigned long mask; BLOCK_INPUT; - mask = CWBackPixel | CWOverrideRedirect | CWSaveUnder | CWEventMask; - /* Window managers looks at the override-redirect flag to - determine whether or net to give windows a decoration (Xlib + 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). */ attrs.override_redirect = True; attrs.save_under = True; @@ -9726,43 +10644,55 @@ 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; int root_x, root_y, win_x, win_y; unsigned pmask; - struct gcpro gcpro1, gcpro2, gcpro3; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; int old_windows_or_buffers_changed = windows_or_buffers_changed; int count = specpdl_ptr - specpdl; specbind (Qinhibit_redisplay, Qt); - GCPRO3 (string, parms, frame); + GCPRO4 (string, parms, frame, timeout); CHECK_STRING (string, 0); f = check_x_frame (frame); @@ -9770,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 (); @@ -9790,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; @@ -9808,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); @@ -9829,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]) { @@ -9848,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; @@ -9880,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); } @@ -10028,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;) { @@ -10043,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); @@ -10080,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 */ - /*********************************************************************** @@ -10192,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. */ @@ -10201,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); @@ -10212,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); @@ -10263,6 +11309,11 @@ or when you set the mouse color."); "Non-zero means Emacs displays a busy cursor on window systems."); display_busy_cursor_p = 1; + DEFVAR_LISP ("busy-cursor-delay", &Vbusy_cursor_delay, + "*Seconds to wait before displaying a busy-cursor.\n\ +Value must be an integer or float."); + Vbusy_cursor_delay = make_number (DEFAULT_BUSY_CURSOR_DELAY); + #if 0 /* This doesn't really do anything. */ DEFVAR_LISP ("x-mode-pointer-shape", &Vx_mode_pointer_shape, "The shape of the pointer when over the mode line.\n\ @@ -10278,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; @@ -10307,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 @@ -10326,12 +11379,6 @@ Each element of the list is a symbol for a supported image type."); defsubr (&Sx_delete_window_property); defsubr (&Sx_window_property); -#if 0 - defsubr (&Sx_draw_rectangle); - defsubr (&Sx_erase_rectangle); - defsubr (&Sx_contour_region); - defsubr (&Sx_uncontour_region); -#endif defsubr (&Sxw_display_color_p); defsubr (&Sx_display_grayscale_p); defsubr (&Sxw_color_defined_p); @@ -10349,23 +11396,15 @@ Each element of the list is a symbol for a supported image type."); defsubr (&Sx_display_visual_class); defsubr (&Sx_display_backing_store); defsubr (&Sx_display_save_under); -#if 0 - defsubr (&Sx_rebind_key); - defsubr (&Sx_rebind_keys); - defsubr (&Sx_track_pointer); - defsubr (&Sx_grab_pointer); - defsubr (&Sx_ungrab_pointer); -#endif defsubr (&Sx_parse_geometry); defsubr (&Sx_create_frame); -#if 0 - defsubr (&Sx_horizontal_line); -#endif defsubr (&Sx_open_connection); defsubr (&Sx_close_connection); 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; @@ -10391,8 +11430,6 @@ Each element of the list is a symbol for a supported image type."); staticpro (&QCheuristic_mask); QCcolor_symbols = intern (":color-symbols"); staticpro (&QCcolor_symbols); - QCdata = intern (":data"); - staticpro (&QCdata); QCascent = intern (":ascent"); staticpro (&QCascent); QCmargin = intern (":margin"); @@ -10440,22 +11477,18 @@ Each element of the list is a symbol for a supported image type."); #endif defsubr (&Sclear_image_cache); + defsubr (&Simage_size); + defsubr (&Simage_mask_p); -#if GLYPH_DEBUG - defsubr (&Simagep); - defsubr (&Slookup_image); -#endif - - /* Busy-cursor. */ - defsubr (&Sx_show_busy_cursor); - defsubr (&Sx_hide_busy_cursor); - busy_count = 0; - inhibit_busy_cursor = 0; + 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);