This is faces.c in view mode; [Download] [Up]
/* "Face" primitives Copyright (C) 1994 Free Software Foundation, Inc. Copyright (C) 1995 Board of Trustees, University of Illinois Copyright (C) 1995 Ben Wing This file is part of XEmacs. XEmacs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. XEmacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with XEmacs; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Synched up with: Not in FSF. */ /* Written by Chuck Thompson and Ben Wing, based loosely on old face code by Jamie Zawinski. */ #include <config.h> #include "lisp.h" #include "buffer.h" #include "device.h" #include "elhash.h" #include "extents.h" #include "faces.h" #include "frame.h" #include "glyphs.h" #include "hash.h" #include "objects.h" #include "specifier.h" #include "window.h" #ifdef HAVE_X_WINDOWS #include "device-x.h" #include "frame-x.h" #include "objects-x.h" #include "EmacsFrame.h" #endif /* HAVE_X_WINDOWS */ #ifdef HAVE_NEXTSTEP #include "device-ns.h" #include "frame-ns.h" #include "objects-ns.h" #endif /* HAVE_NEXTSTEP */ /* Qfont, Qdoc_string defined in general.c */ Lisp_Object Qface, Qfacep; Lisp_Object Qforeground, Qbackground, Qdisplay_table; /* Qhighlight, Qreverse defined in general.c */ Lisp_Object Qbackground_pixmap, Qunderline, Qdim; Lisp_Object Qblinking; Lisp_Object Qinit_face_from_resources; Lisp_Object Qinit_frame_faces; Lisp_Object Qinit_device_faces; Lisp_Object Qinit_global_faces; Lisp_Object Qinit_faces; /* These faces are used directly internally. We use these variables to be able to reference them directly and save the overhead of calling Ffind_face. */ Lisp_Object Vdefault_face, Vmodeline_face, Vhighlight_face; Lisp_Object Vleft_margin_face, Vright_margin_face; /* Qdefault, Qhighlight defined in general.c */ Lisp_Object Qmodeline, Qleft_margin, Qright_margin; /* In the old implementation Vface_list was a list of the face names, not the faces themselves. We now distinguish between permanent and temporary faces. Permanent faces are kept in a regular hash table, temporary faces in a weak hash table. */ Lisp_Object Vpermanent_faces_cache; Lisp_Object Vtemporary_faces_cache; static Lisp_Object mark_face (Lisp_Object, void (*) (Lisp_Object)); static void print_face (Lisp_Object, Lisp_Object, int); static int face_equal (Lisp_Object, Lisp_Object, int depth); static unsigned long face_hash (Lisp_Object obj, int depth); static int face_getprop (Lisp_Object obj, Lisp_Object prop, Lisp_Object *value_out); static int face_putprop (Lisp_Object obj, Lisp_Object prop, Lisp_Object value); static int face_remprop (Lisp_Object obj, Lisp_Object prop); static Lisp_Object face_props (Lisp_Object obj); DEFINE_LRECORD_IMPLEMENTATION_WITH_PROPS ("face", face, mark_face, print_face, 0, face_equal, face_hash, face_getprop, face_putprop, face_remprop, face_props, struct Lisp_Face); static Lisp_Object mark_face (Lisp_Object obj, void (*markobj) (Lisp_Object)) { struct Lisp_Face *face = XFACE (obj); ((markobj) (face->name)); ((markobj) (face->doc_string)); ((markobj) (face->foreground)); ((markobj) (face->background)); ((markobj) (face->font)); ((markobj) (face->display_table)); ((markobj) (face->background_pixmap)); ((markobj) (face->underline)); ((markobj) (face->highlight)); ((markobj) (face->dim)); ((markobj) (face->blinking)); ((markobj) (face->reverse)); return (face->plist); } static void print_face (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag) { struct Lisp_Face *face = XFACE (obj); if (print_readably) error ("printing unreadable object #<face %s>", string_data (XSYMBOL (face->name)->name)); write_c_string ("#<face ", printcharfun); print_internal (face->name, printcharfun, 1); if (!NILP (face->doc_string)) { write_c_string (" ", printcharfun); print_internal (face->doc_string, printcharfun, 1); } write_c_string (">", printcharfun); } /* Faces are equal if all of their display attributes are equal. We don't compare names or doc-strings, because that would make equal be eq. This isn't concerned with "unspecified" attributes, that's what #'face-differs-from-default-p is for. */ static int face_equal (Lisp_Object o1, Lisp_Object o2, int depth) { struct Lisp_Face *f1 = XFACE (o1); struct Lisp_Face *f2 = XFACE (o2); depth++; if (!internal_equal (f1->foreground, f2->foreground, depth) || !internal_equal (f1->background, f2->background, depth) || !internal_equal (f1->font, f2->font, depth) || !internal_equal (f1->display_table, f2->display_table, depth) || !internal_equal (f1->background_pixmap, f2->background_pixmap, depth) || !internal_equal (f1->underline, f2->underline, depth) || !internal_equal (f1->highlight, f2->highlight, depth) || !internal_equal (f1->dim, f2->dim, depth) || !internal_equal (f1->blinking, f2->blinking, depth) || !internal_equal (f1->reverse, f2->reverse, depth) || plists_differ (f1->plist, f2->plist, depth + 1)) return 0; return 1; } static unsigned long face_hash (Lisp_Object obj, int depth) { struct Lisp_Face *f = XFACE (obj); depth++; /* No need to hash all of the elements; that would take too long. Just hash the most common ones. */ return HASH3 (internal_hash (f->foreground, depth), internal_hash (f->background, depth), internal_hash (f->font, depth)); } static int face_getprop (Lisp_Object obj, Lisp_Object prop, Lisp_Object *value_out) { struct Lisp_Face *f = XFACE (obj); #define FROB(propprop) \ do { \ if (EQ (prop, Q##propprop)) \ { \ *value_out = f->propprop; \ return 1; \ } \ } while (0) FROB (foreground); FROB (background); FROB (font); FROB (display_table); FROB (background_pixmap); FROB (underline); FROB (highlight); FROB (dim); FROB (blinking); FROB (reverse); #undef FROB return internal_getf (f->plist, prop, value_out); } static int face_putprop (Lisp_Object obj, Lisp_Object prop, Lisp_Object value) { struct Lisp_Face *f = XFACE (obj); #define FROB(propprop) \ do { \ if (EQ (prop, Q##propprop)) \ return 0; \ } while (0) FROB (foreground); FROB (background); FROB (font); FROB (display_table); FROB (background_pixmap); FROB (underline); FROB (highlight); FROB (dim); FROB (blinking); FROB (reverse); #undef FROB if (EQ (prop, Qdoc_string)) { if (!NILP (value)) CHECK_STRING (value, 0); f->doc_string = value; return 1; } internal_putf (&f->plist, prop, value); return 1; } static int face_remprop (Lisp_Object obj, Lisp_Object prop) { struct Lisp_Face *f = XFACE (obj); #define FROB(propprop) \ do { \ if (EQ (prop, Q##propprop)) \ return -1; \ } while (0) FROB (foreground); FROB (background); FROB (font); FROB (display_table); FROB (background_pixmap); FROB (underline); FROB (highlight); FROB (dim); FROB (blinking); FROB (reverse); #undef FROB if (EQ (prop, Qdoc_string)) { f->doc_string = Qnil; return 1; } return internal_remprop (&f->plist, prop); } static Lisp_Object face_props (Lisp_Object obj) { struct Lisp_Face *f = XFACE (obj); Lisp_Object result = Qnil; #define FROB(propprop) \ do { \ result = Fcons (f->propprop, Fcons (Q##propprop, result)); \ } while (0) /* backwards order; we reverse it below */ FROB (reverse); FROB (blinking); FROB (dim); FROB (highlight); FROB (underline); FROB (background_pixmap); FROB (display_table); FROB (font); FROB (background); FROB (foreground); #undef FROB return nconc2 (Fnreverse (result), Fcopy_sequence (f->plist)); } /***************************************************************************** utility functions ****************************************************************************/ static void reset_face (struct Lisp_Face *f) { f->name = Qnil; f->doc_string = Qnil; f->dirty = 0; f->foreground = Qnil; f->background = Qnil; f->font = Qnil; f->display_table = Qnil; f->background_pixmap = Qnil; f->underline = Qnil; f->highlight = Qnil; f->dim = Qnil; f->blinking = Qnil; f->reverse = Qnil; f->plist = Qnil; } static struct Lisp_Face * allocate_face (void) { struct Lisp_Face *result = alloc_lcrecord (sizeof (struct Lisp_Face), lrecord_face); reset_face (result); return result; } /* We store the faces in hash tables with the names as the key and the actual face object as the value. Occasionally we need to use them in a list format. These routines provide us with that. */ struct face_list_closure { Lisp_Object *face_list; }; static void add_face_to_list_mapper (CONST void *hash_key, void *hash_contents, void *face_list_closure) { /* This function can GC */ Lisp_Object key, contents; Lisp_Object *face_list; struct face_list_closure *fcl = face_list_closure; CVOID_TO_LISP (key, hash_key); VOID_TO_LISP (contents, hash_contents); face_list = fcl->face_list; *face_list = Fcons (XFACE (contents)->name, *face_list); } static Lisp_Object faces_list_internal (Lisp_Object list) { Lisp_Object face_list = Qnil; struct gcpro gcpro1; struct face_list_closure face_list_closure; GCPRO1 (face_list); face_list_closure.face_list = &face_list; elisp_maphash (add_face_to_list_mapper, list, &face_list_closure); UNGCPRO; return face_list; } static Lisp_Object permanent_faces_list (void) { return faces_list_internal (Vpermanent_faces_cache); } static Lisp_Object temporary_faces_list (void) { return faces_list_internal (Vtemporary_faces_cache); } static void mark_face_as_clean_mapper (CONST void *hash_key, void *hash_contents, void *flag_closure) { /* This function can GC */ Lisp_Object key, contents; int *flag = flag_closure; CVOID_TO_LISP (key, hash_key); VOID_TO_LISP (contents, hash_contents); XFACE (contents)->dirty = *flag; } static void mark_all_faces_internal (int flag) { elisp_maphash (mark_face_as_clean_mapper, Vpermanent_faces_cache, &flag); elisp_maphash (mark_face_as_clean_mapper, Vtemporary_faces_cache, &flag); } void mark_all_faces_as_clean (void) { mark_all_faces_internal (0); } /* #### OBSOLETE ME, PLEASE. Maybe. Maybe this is just as good as any other solution. */ struct face_inheritance_closure { Lisp_Object face; Lisp_Object property; }; static void update_inheritance_mapper_internal (Lisp_Object cur_face, Lisp_Object inh_face, Lisp_Object property) { /* #### fix this function */ Lisp_Object elt = Qnil; struct gcpro gcpro1; GCPRO1 (elt); for (elt = FACE_PROPERTY_SPEC_LIST (cur_face, property, Qall); !NILP (elt); elt = XCDR (elt)) { Lisp_Object locale, values; locale = XCAR (XCAR (elt)); values = XCDR (XCAR (elt)); for (; !NILP (values); values = XCDR (values)) { Lisp_Object tag = XCAR (XCAR (values)); Lisp_Object value = XCDR (XCAR (values)); if (VECTORP (value) && XVECTOR (value)->size) { if (EQ (Ffind_face (vector_data (XVECTOR (value))[0]), inh_face)) { /* #### This should probably be changed. We just want to mark the face as dirty and reset its specifier instance list. */ SET_FACE_PROPERTY (cur_face, property, value, locale, tag, Qnil); } } } } UNGCPRO; } static void update_face_inheritance_mapper (CONST void *hash_key, void *hash_contents, void *face_inheritance_closure) { Lisp_Object key, contents; struct face_inheritance_closure *fcl = face_inheritance_closure; CVOID_TO_LISP (key, hash_key); VOID_TO_LISP (contents, hash_contents); if (EQ (fcl->property, Qfont)) { update_inheritance_mapper_internal (contents, fcl->face, Qfont); } else if (EQ (fcl->property, Qforeground) || EQ (fcl->property, Qbackground)) { update_inheritance_mapper_internal (contents, fcl->face, Qforeground); update_inheritance_mapper_internal (contents, fcl->face, Qbackground); } else if (EQ (fcl->property, Qunderline) || EQ (fcl->property, Qhighlight) || EQ (fcl->property, Qdim) || EQ (fcl->property, Qblinking) || EQ (fcl->property, Qreverse)) { update_inheritance_mapper_internal (contents, fcl->face, Qunderline); update_inheritance_mapper_internal (contents, fcl->face, Qhighlight); update_inheritance_mapper_internal (contents, fcl->face, Qdim); update_inheritance_mapper_internal (contents, fcl->face, Qblinking); update_inheritance_mapper_internal (contents, fcl->face, Qreverse); } } static void update_faces_inheritance (Lisp_Object face, Lisp_Object property) { struct face_inheritance_closure face_inheritance_closure; struct gcpro gcpro1, gcpro2; GCPRO2 (face, property); face_inheritance_closure.face = face; face_inheritance_closure.property = property; elisp_maphash (update_face_inheritance_mapper, Vpermanent_faces_cache, &face_inheritance_closure); elisp_maphash (update_face_inheritance_mapper, Vtemporary_faces_cache, &face_inheritance_closure); UNGCPRO; } DEFUN ("facep", Ffacep, Sfacep, 1, 1, 0, "Return non-nil if OBJECT is a face.") (object) Lisp_Object object; { return (FACEP (object) ? Qt : Qnil); } DEFUN ("find-face", Ffind_face, Sfind_face, 1, 1, 0, "Retrieve the face of the given name.\n\ If FACE-OR-NAME is a face object, it is simply returned.\n\ Otherwise, FACE-OR-NAME should be a symbol. If there is no such face,\n\ nil is returned. Otherwise the associated face object is returned.") (face_or_name) Lisp_Object face_or_name; { Lisp_Object retval; if (FACEP (face_or_name)) return face_or_name; CHECK_SYMBOL (face_or_name, 0); /* Check if the name represents a permanent face. */ retval = Fgethash (face_or_name, Vpermanent_faces_cache, Qnil); if (!NILP (retval)) return retval; /* Check if the name represents a temporary face. */ return Fgethash (face_or_name, Vtemporary_faces_cache, Qnil); } DEFUN ("get-face", Fget_face, Sget_face, 1, 1, 0, "Retrieve the face of the given name.\n\ Same as `find-face' except an error is signalled if there is no such\n\ face instead of returning nil.") (name) Lisp_Object name; { Lisp_Object face = Ffind_face (name); if (NILP (face)) signal_simple_error ("No such face", name); return face; } DEFUN ("face-name", Fface_name, Sface_name, 1, 1, 0, "Return the name of the given face.") (face) Lisp_Object face; { return (XFACE (Fget_face (face))->name); } void face_font_metric_info (Lisp_Object face, Lisp_Object domain, struct font_metric_info *fm) { struct device *d = XDEVICE (DFW_DEVICE (domain)); DEVMETH (d, font_metric_info, (d, FACE_FONT (face, domain), fm)); } /* These values are retrieved so often that we make a special function. #### Should this perhaps look in the face-cache-element cache for the domain, esp. if it is a window (as is usually the case)? */ void default_face_height_and_width (Lisp_Object domain, int *height, int *width) { struct device *d = XDEVICE (DFW_DEVICE (domain)); struct font_metric_info fm; DEVMETH (d, font_metric_info, (d, FACE_FONT (Vdefault_face, domain), &fm)); *height = fm.height; *width = fm.width; } DEFUN ("face-ascent", Fface_ascent, Sface_ascent, 1, 2, 0, "Return the ascent portion of the FACE's height in the given DOMAIN.\n\ If DOMAIN is nil, it will be determined from the selected window.") (face, domain) Lisp_Object face, domain; { struct font_metric_info fm; face = Fget_face (face); domain = decode_domain (domain); face_font_metric_info (face, domain, &fm); return (make_number (fm.ascent)); } DEFUN ("face-descent", Fface_descent, Sface_descent, 1, 2, 0, "Return the descent portion of the FACE's height in the given DOMAIN.\n\ If DOMAIN is nil, it will be determined from the selected window.") (face, domain) Lisp_Object face, domain; { struct font_metric_info fm; face = Fget_face (face); domain = decode_domain (domain); face_font_metric_info (face, domain, &fm); return (make_number (fm.descent)); } DEFUN ("face-width", Fface_width, Sface_width, 1, 2, 0, "Return the width of FACE in the given DOMAIN.\n\ If DOMAIN is nil, it will be determined from the selected window.") (face, domain) Lisp_Object face, domain; { struct font_metric_info fm; face = Fget_face (face); domain = decode_domain (domain); face_font_metric_info (face, domain, &fm); return (make_number (fm.width)); } DEFUN ("face-list", Fface_list, Sface_list, 0, 1, 0, "Return a list of the names of all defined faces.\n\ If TEMPORARY is nil, only the permanent faces are included.\n\ If it is t, only the temporary faces are included. If it is any\n\ other non-nil value both permanent and temporary are included.") (temporary) Lisp_Object temporary; { Lisp_Object face_list = Qnil; /* Added the permanent faces, if requested. */ if (NILP (temporary) || !EQ (Qt, temporary)) face_list = permanent_faces_list (); if (!NILP (temporary)) { struct gcpro gcpro1; GCPRO1 (face_list); face_list = nconc2 (face_list, temporary_faces_list ()); UNGCPRO; } return face_list; } DEFUN ("extent-face", Fextent_face, Sextent_face, 1, 1, 0, "Return the name of the face in which EXTENT is displayed, or nil\n\ if the extent's face is unspecified.") (extent) Lisp_Object extent; { CHECK_EXTENT (extent, 0); if (NILP (extent_face (XEXTENT (extent)))) return Qnil; else return (XFACE (extent_face (XEXTENT (extent)))->name); } DEFUN ("set-extent-face", Fset_extent_face, Sset_extent_face, 2, 2, 0, "Make the given EXTENT have the graphic attributes specified by FACE.") (extent, face) Lisp_Object extent, face; { EXTENT e; CHECK_EXTENT (extent, 0); e = XEXTENT (extent); if (!NILP (face)) face = Fget_face (face); set_extent_face (e, face); return face; } DEFUN ("make-face", Fmake_face, Smake_face, 1, 3, 0, "Defines and returns a new FACE described by DOC-STRING.\n\ You can modify the font, color, etc of a face with the set-face- functions.\n\ If the face already exists, it is unmodified.\n\ If TEMPORARY is non-nil, this face will cease to exist if not in use.") (name, doc_string, temporary) Lisp_Object name, doc_string, temporary; { /* This function can GC if initialized is non-zero */ struct Lisp_Face *f; Lisp_Object face; CHECK_SYMBOL (name, 0); if (!NILP (doc_string)) CHECK_STRING (doc_string, 0); face = Ffind_face (name); if (!NILP (face)) return face; f = allocate_face (); XSETFACE (face, f); f->name = name; f->doc_string = doc_string; f->foreground = Fmake_specifier (Qcolor); set_color_attached_to (f->foreground, face, Qforeground); f->background = Fmake_specifier (Qcolor); set_color_attached_to (f->background, face, Qbackground); f->font = Fmake_specifier (Qfont); set_font_attached_to (f->font, face, Qfont); f->background_pixmap = Fmake_specifier (Qimage); set_image_attached_to (f->background_pixmap, face, Qbackground_pixmap); /* #### need a special display-table specifier */ f->display_table = Fmake_specifier (Qgeneric); f->underline = Fmake_specifier (Qface_boolean); set_face_boolean_attached_to (f->underline, face, Qunderline); f->highlight = Fmake_specifier (Qface_boolean); set_face_boolean_attached_to (f->highlight, face, Qhighlight); f->dim = Fmake_specifier (Qface_boolean); set_face_boolean_attached_to (f->dim, face, Qdim); f->blinking = Fmake_specifier (Qface_boolean); set_face_boolean_attached_to (f->blinking, face, Qblinking); f->reverse = Fmake_specifier (Qface_boolean); set_face_boolean_attached_to (f->reverse, face, Qreverse); if (!NILP (Vdefault_face)) { /* If the default face has already been created, set it as the default fallback specifier for all the specifiers we just created. This implements the standard "all faces inherit from default" behavior. */ set_specifier_fallback (f->foreground, Fget (Vdefault_face, Qforeground, Qunbound)); set_specifier_fallback (f->background, Fget (Vdefault_face, Qbackground, Qunbound)); set_specifier_fallback (f->font, Fget (Vdefault_face, Qfont, Qunbound)); set_specifier_fallback (f->background_pixmap, Fget (Vdefault_face, Qbackground_pixmap, Qunbound)); set_specifier_fallback (f->display_table, Fget (Vdefault_face, Qdisplay_table, Qunbound)); set_specifier_fallback (f->underline, Fget (Vdefault_face, Qunderline, Qunbound)); set_specifier_fallback (f->highlight, Fget (Vdefault_face, Qhighlight, Qunbound)); set_specifier_fallback (f->dim, Fget (Vdefault_face, Qdim, Qunbound)); set_specifier_fallback (f->blinking, Fget (Vdefault_face, Qblinking, Qunbound)); set_specifier_fallback (f->reverse, Fget (Vdefault_face, Qreverse, Qunbound)); } /* Add the face to the appropriate list. */ if (NILP (temporary)) Fputhash (name, face, Vpermanent_faces_cache); else Fputhash (name, face, Vtemporary_faces_cache); if (initialized) { struct gcpro gcpro1, gcpro2; GCPRO2 (name, face); call1 (Qinit_face_from_resources, name); UNGCPRO; } return face; } /***************************************************************************** initialization code ****************************************************************************/ void init_global_faces (struct device *d) { /* When making the initial terminal device, there is no Lisp code loaded, so we can't do this. */ if (initialized) { call_critical_lisp_code (d, Qinit_global_faces, Qnil); } } void init_device_faces (struct device *d) { /* When making the initial terminal device, there is no Lisp code loaded, so we can't do this. */ if (initialized) { Lisp_Object tdevice; XSETDEVICE (tdevice, d); call_critical_lisp_code (d, Qinit_device_faces, tdevice); } } void init_frame_faces (struct frame *frm) { /* When making the initial terminal device, there is no Lisp code loaded, so we can't do this. */ if (initialized) { Lisp_Object tframe; XSETFRAME (tframe, frm); /* DO NOT change the selected frame here. If the debugger goes off it will try and display on the frame being created, but it is not ready for that yet and a horrible death will occur. Any random code depending on the selected-frame as an implicit arg should be tracked down and shot. For the benefit of the one known, xpm-color-symbols, make-frame sets the variable Vframe_being_created to the frame it is making and sets it to nil when done. Internal functions that this could trigger which are currently depending on selected-frame should use this instead. It is not currently visible at the lisp level. */ call_critical_lisp_code (XDEVICE (FRAME_DEVICE (frm)), Qinit_frame_faces, tframe); } } /***************************************************************************** face cache element functions ****************************************************************************/ void mark_face_cache_elements (face_cache_element_dynarr *elements, void (*markobj) (Lisp_Object)) { int elt; if (!elements) return; for (elt = 0; elt < Dynarr_length (elements); elt++) { struct face_cache_element *inst = Dynarr_atp (elements, elt); ((markobj) (inst->face)); ((markobj) (inst->foreground)); ((markobj) (inst->background)); ((markobj) (inst->font)); ((markobj) (inst->display_table)); ((markobj) (inst->background_pixmap)); } } static void update_face_cache_element_data (struct window *w, Lisp_Object face, struct face_cache_element *cachel) { if (XFACE (face)->dirty || UNBOUNDP (cachel->face)) { int default_face = EQ (face, Vdefault_face); Lisp_Object window = Qnil; XSETWINDOW (window, w); cachel->face = face; /* We normally only set the _specified flags if the value was actually bound. The exception is for the default face where we always set it since it is the ultimate fallback. */ #define FROB(field) \ do { \ Lisp_Object new_val = FACE_PROPERTY_INSTANCE (face, Q##field, window, 1); \ int bound = 1; \ if (UNBOUNDP (new_val)) \ { \ bound = 0; \ new_val = FACE_PROPERTY_INSTANCE (face, Q##field, window, 0); \ } \ if (!EQ (new_val, cachel->field)) \ { \ cachel->field = new_val; \ cachel->dirty = 1; \ } \ cachel->field##_specified = (bound || default_face); \ } while (0) FROB (foreground); FROB (background); FROB (font); FROB (display_table); FROB (background_pixmap); #undef FROB #define FROB(field) \ do { \ Lisp_Object new_val = FACE_PROPERTY_INSTANCE (face, Q##field, window, 1); \ int bound = 1; \ int new_val_int; \ if (UNBOUNDP (new_val)) \ { \ bound = 0; \ new_val = FACE_PROPERTY_INSTANCE (face, Q##field, window, 0); \ } \ new_val_int = EQ (new_val, Qt); \ if (cachel->field != new_val_int) \ { \ cachel->field = new_val_int; \ cachel->dirty = 1; \ } \ cachel->field##_specified = bound; \ } while (0) FROB (underline); FROB (highlight); FROB (dim); FROB (reverse); FROB (blinking); #undef FROB } cachel->updated = 1; } static void merge_face_cache_element_data (struct window *w, face_index findex, struct face_cache_element *cachel) { #define FINDEX_FIELD(field) \ Dynarr_atp (w->face_cache_elements, findex)->field #define FROB(field) \ do { \ if (!cachel->field##_specified && FINDEX_FIELD (field##_specified)) \ { \ cachel->field = FINDEX_FIELD (field); \ cachel->field##_specified = 1; \ cachel->dirty = 1; \ } \ } while (0) FROB (foreground); FROB (background); FROB (font); FROB (display_table); FROB (background_pixmap); FROB (underline); FROB (highlight); FROB (dim); FROB (reverse); FROB (blinking); #undef FROB #undef FINDEX_FIELD cachel->updated = 1; } static void reset_face_cache_element (struct face_cache_element *inst) { memset (inst, 0, sizeof (struct face_cache_element)); inst->face = Qunbound; inst->foreground = Qunbound; inst->background = Qunbound; inst->font = Qunbound; inst->display_table = Qunbound; inst->background_pixmap = Qunbound; } static void add_face_cache_element (struct window *w, Lisp_Object face) { struct face_cache_element new_cachel; reset_face_cache_element (&new_cachel); update_face_cache_element_data (w, face, &new_cachel); Dynarr_add (w->face_cache_elements, new_cachel); } face_index get_builtin_face_cache_index (struct window *w, Lisp_Object face) { int elt; if (noninteractive) return 0; for (elt = 0; elt < Dynarr_length (w->face_cache_elements); elt++) { struct face_cache_element *cachel = Dynarr_atp (w->face_cache_elements, elt); if (EQ (cachel->face, face)) { if (!cachel->updated) update_face_cache_element_data (w, face, cachel); return elt; } } /* If we didn't find the face, add it and then return its index. */ add_face_cache_element (w, face); return elt; } void reset_face_cache_elements (struct window *w) { /* #### Not initialized in batch mode for the stream device. */ if (w->face_cache_elements) { Dynarr_reset (w->face_cache_elements); get_builtin_face_cache_index (w, Vdefault_face); get_builtin_face_cache_index (w, Vmodeline_face); XFRAME (w->frame)->window_face_cache_reset = 1; } } void mark_face_cache_elements_as_clean (struct window *w) { int elt; for (elt = 0; elt < Dynarr_length (w->face_cache_elements); elt++) Dynarr_atp (w->face_cache_elements, elt)->dirty = 0; } void mark_face_cache_elements_as_not_updated (struct window *w) { int elt; for (elt = 0; elt < Dynarr_length (w->face_cache_elements); elt++) Dynarr_atp (w->face_cache_elements, elt)->updated = 0; } static int compare_face_cache_elements (struct face_cache_element *cachel1, struct face_cache_element *cachel2) { if (EQ (cachel1->foreground, cachel2->foreground) && EQ (cachel1->background, cachel2->background) && EQ (cachel1->font, cachel2->font) && EQ (cachel1->display_table, cachel2->display_table) && EQ (cachel1->background_pixmap, cachel2->background_pixmap) && cachel1->underline == cachel2->underline && cachel1->highlight == cachel2->highlight && cachel1->dim == cachel2->dim && cachel1->blinking == cachel2->blinking && cachel1->reverse == cachel2->reverse) return 1; else return 0; } /***************************************************************************** merged face functions ****************************************************************************/ static face_index get_merged_face_element_index (struct window *w, struct face_cache_element *merged_inst) { int elt; int cache_size = Dynarr_length (w->face_cache_elements); for (elt = 0; elt < cache_size; elt++) { struct face_cache_element *inst = Dynarr_atp (w->face_cache_elements, elt); /* Do not compare the face names. Merged elements will have it as nil and this would cause false positives. Also, by doing it this way if a merged instance happens to match a builtin face instance, we'll still return it. Why add two identical instances? */ if (compare_face_cache_elements (inst, merged_inst)) return elt; } /* We didn't find it so add this instance to the cache. */ merged_inst->updated = 1; merged_inst->dirty = 1; Dynarr_add (w->face_cache_elements, *merged_inst); return cache_size; } face_index get_extent_fragment_face_cache_index (struct window *w, struct extent_fragment *ef, EXTENT dummy_lhe_extent) { struct face_cache_element cachel; int len = Dynarr_length (ef->extents); face_index findex; /* Optimize the default case. */ if (len == 0) return DEFAULT_INDEX; else { int i; Lisp_Object window = Qnil; XSETWINDOW (window, w); /* Merge the faces of the extents together in order. Remember that one of the extents in the list might be our dummy extent representing the highlighting that is attached to some other extent that is currently mouse-highlighted. When an extent is mouse-highlighted, it is as if there are two extents there, of potentially different priorities: the extent being highlighted, with whatever face and priority it has; and an ephemeral extent in the `highlight' face with `mouse-highlight-priority'. */ reset_face_cache_element (&cachel); for (i = len - 1; i >= 0; i--) { EXTENT current = Dynarr_at (ef->extents, i); if (current == dummy_lhe_extent) { /* this isn't a real extent; use the highlight face. */ findex = get_builtin_face_cache_index (w, Vhighlight_face); merge_face_cache_element_data (w, findex, &cachel); } else if (!NILP (extent_face (current))) { findex = get_builtin_face_cache_index (w, extent_face (current)); merge_face_cache_element_data (w, findex, &cachel); } } /* Now finally merge in the default face. */ findex = get_builtin_face_cache_index (w, Vdefault_face); merge_face_cache_element_data (w, findex, &cachel); return get_merged_face_element_index (w, &cachel); } } /***************************************************************************** interface functions ****************************************************************************/ /* #### This function should be converted into appropriate device methods. */ static void update_EmacsFrame (Lisp_Object frame, Lisp_Object name) { struct frame *frm = XFRAME (frame); #ifdef HAVE_X_WINDOWS if (FRAME_IS_X (frm)) { Arg av[10]; int ac = 0; if (EQ (name, Qforeground)) { Lisp_Object color = FACE_FOREGROUND (Vdefault_face, frame); XColor fgc; fgc = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (color)); XtSetArg (av[ac], XtNforeground, (void *) fgc.pixel); ac++; } else if (EQ (name, Qbackground)) { Lisp_Object color = FACE_BACKGROUND (Vdefault_face, frame); XColor bgc; bgc = COLOR_INSTANCE_X_COLOR (XCOLOR_INSTANCE (color)); XtSetArg (av[ac], XtNbackground, (void *) bgc.pixel); ac++; /* Really crappy way to force the modeline shadows to be redrawn. But effective. */ MARK_FRAME_WINDOWS_STRUCTURE_CHANGED (frm); MARK_FRAME_CHANGED (frm); } else if (EQ (name, Qfont)) { Lisp_Object font = FACE_FONT (Vdefault_face, frame); XtSetArg (av[ac], XtNfont, (void *) FONT_INSTANCE_X_FONT (XFONT_INSTANCE (font))); ac++; } else abort (); XtSetValues (FRAME_X_TEXT_WIDGET (frm), av, ac); /* Setting the background clears the entire frame area including the toolbar so we force an immediate redraw of it. */ if (EQ (name, Qbackground)) MAYBE_DEVMETH (XDEVICE (frm->device), redraw_frame_toolbars, (frm)); /* The intent of this code is to cause the frame size in characters to remain the same when the font changes, at the expense of changing the frame size in pixels. It's not totally clear that this is the right thing to do, but it's not clearly wrong either. */ if (EQ (name, Qfont)) { EmacsFrameRecomputeCellSize (FRAME_X_TEXT_WIDGET (frm)); Fset_frame_size (frame, make_number (frm->width), make_number (frm->height), Qnil); } } #endif /* HAVE_X_WINDOWS */ #ifdef HAVE_NEXTSTEP if (FRAME_IS_NS (frm)) { /* This code still needs to be written */ } #endif /* HAVE_NEXSTEP */ } static void update_EmacsFrames (Lisp_Object locale, Lisp_Object name) { if (FRAMEP (locale)) { update_EmacsFrame (locale, name); } else if (DEVICEP (locale)) { Lisp_Object frm; FRAME_LOOP (frm, XDEVICE (locale)) update_EmacsFrame (XCAR (frm), name); } else if (EQ (locale, Qglobal) || EQ (locale, Qfallback)) { Lisp_Object dev, frm; DEVICE_AND_FRAME_LOOP (dev, frm) update_EmacsFrame (XCAR (frm), name); } } void update_frame_face_values (struct frame *f) { Lisp_Object frm = Qnil; XSETFRAME (frm, f); update_EmacsFrame (frm, Qforeground); update_EmacsFrame (frm, Qbackground); update_EmacsFrame (frm, Qfont); } void face_property_was_changed (Lisp_Object face, Lisp_Object property, Lisp_Object locale) { int default_face = EQ (face, Vdefault_face); /* If the locale could affect the frame value, then call update_EmacsFrames just in case. */ if (default_face && (EQ (property, Qforeground) || EQ (property, Qbackground) || EQ (property, Qfont))) update_EmacsFrames (locale, property); if (WINDOWP (locale)) { MARK_FRAME_FACES_CHANGED (XFRAME (XWINDOW (locale)->frame)); } else if (FRAMEP (locale)) { MARK_FRAME_FACES_CHANGED (XFRAME (locale)); } else if (DEVICEP (locale)) { MARK_DEVICE_FRAMES_FACES_CHANGED (XDEVICE (locale)); } else { Lisp_Object dev; DEVICE_LOOP (dev) MARK_DEVICE_FRAMES_FACES_CHANGED (XDEVICE (XCAR (dev))); } update_faces_inheritance (face, property); XFACE (face)->dirty = 1; } DEFUN ("copy-face", Fcopy_face, Scopy_face, 2, 6, 0, "Defines and returns a new face which is a copy of an existing one,\n\ or makes an already-existing face be exactly like another. LOCALE,\n\ TAG-SET, EXACT-P, and HOW-TO-ADD are as in `copy-specifier'.") (old_face, new_name, locale, tag_set, exact_p, how_to_add) Lisp_Object old_face, new_name, locale, tag_set, exact_p, how_to_add; { struct Lisp_Face *fold, *fnew; Lisp_Object new_face = Qnil; struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; old_face = Fget_face (old_face); /* We GCPRO old_face because it might be temporary, and GCing could occur in various places below. */ GCPRO4 (tag_set, locale, old_face, new_face); /* check validity of how_to_add now. */ (void) decode_how_to_add_specification (how_to_add); /* and of tag_set. */ tag_set = decode_specifier_tag_set (tag_set); /* and of locale. */ locale = decode_locale_list (locale); new_face = Ffind_face (new_name); if (NILP (new_face)) { Lisp_Object temp; CHECK_SYMBOL (new_name, 0); /* Create the new face with the same status as the old face. */ temp = (NILP (Fgethash (old_face, Vtemporary_faces_cache, Qnil)) ? Qnil : Qt); new_face = Fmake_face (new_name, Qnil, temp); } fold = XFACE (old_face); fnew = XFACE (new_face); Fcopy_specifier (fold->foreground, fnew->foreground, locale, tag_set, exact_p, how_to_add); Fcopy_specifier (fold->background, fnew->background, locale, tag_set, exact_p, how_to_add); Fcopy_specifier (fold->font, fnew->font, locale, tag_set, exact_p, how_to_add); Fcopy_specifier (fold->display_table, fnew->display_table, locale, tag_set, exact_p, how_to_add); Fcopy_specifier (fold->background_pixmap, fnew->background_pixmap, locale, tag_set, exact_p, how_to_add); Fcopy_specifier (fold->underline, fnew->underline, locale, tag_set, exact_p, how_to_add); Fcopy_specifier (fold->highlight, fnew->highlight, locale, tag_set, exact_p, how_to_add); Fcopy_specifier (fold->dim, fnew->dim, locale, tag_set, exact_p, how_to_add); Fcopy_specifier (fold->blinking, fnew->blinking, locale, tag_set, exact_p, how_to_add); Fcopy_specifier (fold->reverse, fnew->reverse, locale, tag_set, exact_p, how_to_add); /* #### should it copy the individual specifiers, if they exist? */ fnew->plist = Fcopy_sequence (fold->plist); UNGCPRO; return new_name; } void init_faces (void) { if (!initialized) return; Vpermanent_faces_cache = make_lisp_hashtable (10, 0, 0, HASHTABLE_NONWEAK); Vtemporary_faces_cache = make_lisp_hashtable (0, 0, 0, HASHTABLE_WEAK); /* Create the default face now so we know what it is immediately. */ Vdefault_face = Qnil; /* so that Fmake_face() doesn't set up a bogus default value */ Vdefault_face = Fmake_face (Qdefault, build_string ("default face"), Qnil); /* Now create and initialize the rest of the built-in faces. */ call0_with_handler (Qreally_early_error_handler, Qinit_faces); /* If Fget_face signals an error bad, bad things are going to happen. That should only be possible if someone decided to stop creating these faces in init-faces. Bad, bad someone. */ Vmodeline_face = Fget_face (Qmodeline); Vhighlight_face = Fget_face (Qhighlight); Vleft_margin_face = Fget_face (Qleft_margin); Vright_margin_face = Fget_face (Qright_margin); /* Provide some last-resort fallbacks to avoid utter fuckage if someone provides invalid values for the global specifications. */ set_specifier_fallback (Fget (Vdefault_face, Qforeground, Qnil), list1 (Fcons (Qnil, build_string ("black")))); set_specifier_fallback (Fget (Vdefault_face, Qbackground, Qnil), list1 (Fcons (Qnil, build_string ("white")))); /* #### We may want to have different fallback values if NeXTstep support is compiled in. */ #ifdef HAVE_X_WINDOWS { char *fonts[30]; int n = 0; Lisp_Object inst_list = Qnil; /* The same gory list from x-faces.el. (#### Perhaps we should remove the stuff from x-faces.el and only depend on this stuff here? That should work.) */ fonts[n++] = "-*-courier-medium-r-*-*-*-120-*-*-*-*-iso8859-*"; fonts[n++] = "-*-courier-medium-r-*-*-*-120-*-*-*-*-iso8859-*"; fonts[n++] = "-*-courier-*-r-*-*-*-120-*-*-*-*-iso8859-*"; fonts[n++] = "-*-*-medium-r-*-*-*-120-*-*-m-*-iso8859-*"; fonts[n++] = "-*-*-medium-r-*-*-*-120-*-*-c-*-iso8859-*"; fonts[n++] = "-*-*-*-r-*-*-*-120-*-*-m-*-iso8859-*"; fonts[n++] = "-*-*-*-r-*-*-*-120-*-*-c-*-iso8859-*"; fonts[n++] = "-*-*-*-r-*-*-*-120-*-*-*-*-iso8859-*"; fonts[n++] = "-*-*-medium-r-*-*-*-120-*-*-m-*-*-*"; fonts[n++] = "-*-*-medium-r-*-*-*-120-*-*-c-*-*-*"; fonts[n++] = "-*-*-*-r-*-*-*-120-*-*-m-*-*-*"; fonts[n++] = "-*-*-*-r-*-*-*-120-*-*-c-*-*-*"; fonts[n++] = "-*-*-*-r-*-*-*-120-*-*-*-*-*-*"; fonts[n++] = "-*-*-*-*-*-*-*-120-*-*-*-*-*-*"; fonts[n++] = "*"; for (--n; n >= 0; --n) inst_list = Fcons (Fcons (list1 (Qx), build_string (fonts[n])), inst_list); set_specifier_fallback (Fget (Vdefault_face, Qfont, Qnil), inst_list); } #else /* #### This assumes that a font of nil is an OK instantiator, which may not be the case. */ set_specifier_fallback (Fget (Vdefault_face, Qfont, Qnil), Qnil); #endif set_specifier_fallback (Fget (Vdefault_face, Qunderline, Qnil), list1 (Fcons (Qnil, Qnil))); set_specifier_fallback (Fget (Vdefault_face, Qhighlight, Qnil), list1 (Fcons (Qnil, Qnil))); set_specifier_fallback (Fget (Vdefault_face, Qdim, Qnil), list1 (Fcons (Qnil, Qnil))); set_specifier_fallback (Fget (Vdefault_face, Qblinking, Qnil), list1 (Fcons (Qnil, Qnil))); set_specifier_fallback (Fget (Vdefault_face, Qreverse, Qnil), list1 (Fcons (Qnil, Qnil))); } void syms_of_faces (void) { /* Qdefault defined in general.c */ defsymbol (&Qmodeline, "modeline"); defsymbol (&Qleft_margin, "left-margin"); defsymbol (&Qright_margin, "right-margin"); defsubr (&Sfacep); defsubr (&Sfind_face); defsubr (&Sget_face); defsubr (&Sface_name); defsubr (&Sface_ascent); defsubr (&Sface_descent); defsubr (&Sface_width); defsubr (&Sface_list); defsubr (&Sextent_face); defsubr (&Sset_extent_face); defsubr (&Smake_face); defsubr (&Scopy_face); defsymbol (&Qface, "face"); defsymbol (&Qfacep, "facep"); defsymbol (&Qforeground, "foreground"); defsymbol (&Qbackground, "background"); /* Qfont defined in general.c */ defsymbol (&Qdisplay_table, "display-table"); defsymbol (&Qbackground_pixmap, "background-pixmap"); defsymbol (&Qunderline, "underline"); /* Qhighlight, Qreverse defined in general.c */ defsymbol (&Qdim, "dim"); defsymbol (&Qblinking, "blinking"); defsymbol (&Qinit_face_from_resources, "init-face-from-resources"); defsymbol (&Qinit_global_faces, "init-global-faces"); defsymbol (&Qinit_device_faces, "init-device-faces"); defsymbol (&Qinit_frame_faces, "init-frame-faces"); defsymbol (&Qinit_faces, "init-faces"); } void vars_of_faces (void) { staticpro (&Vpermanent_faces_cache); Vpermanent_faces_cache = Qnil; staticpro (&Vtemporary_faces_cache); Vtemporary_faces_cache = Qnil; staticpro (&Vdefault_face); Vdefault_face = Qnil; staticpro (&Vmodeline_face); Vmodeline_face = Qnil; staticpro (&Vhighlight_face); Vhighlight_face = Qnil; staticpro (&Vleft_margin_face); Vleft_margin_face = Qnil; staticpro (&Vright_margin_face); Vright_margin_face = Qnil; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.