Mercurial > hg > xemacs-beta
diff src/faces.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/faces.c Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,2028 @@ +/* "Face" primitives + Copyright (C) 1994 Free Software Foundation, Inc. + Copyright (C) 1995 Board of Trustees, University of Illinois. + Copyright (C) 1995, 1996 Ben Wing. + Copyright (C) 1995 Sun Microsystems, Inc. + +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, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, 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 "console-x.h" +#include "objects-x.h" +#include "EmacsFrame.h" +#endif /* HAVE_X_WINDOWS */ + +#ifdef HAVE_NEXTSTEP +#include "console-ns.h" +#include "objects-ns.h" +#endif /* HAVE_NEXTSTEP */ + +/* Qfont, Qdoc_string, Qface defined in general.c */ +Lisp_Object Qfacep; +Lisp_Object Qforeground, Qbackground, Qdisplay_table; +/* Qhighlight, Qreverse defined in general.c */ +Lisp_Object Qbackground_pixmap, Qunderline, Qdim; +Lisp_Object Qblinking, Qstrikethru; + +Lisp_Object Qinit_face_from_resources; +Lisp_Object Qinit_frame_faces; +Lisp_Object Qinit_device_faces; +Lisp_Object Qinit_global_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; +Lisp_Object Vleft_margin_face, Vright_margin_face, Vtext_cursor_face; +Lisp_Object Vpointer_face; + +/* Qdefault, Qhighlight defined in general.c */ +Lisp_Object Qmodeline, Qleft_margin, Qright_margin, Qtext_cursor; + +/* 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; + +Lisp_Object Vbuilt_in_face_specifiers; + + +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 Lisp_Object face_getprop (Lisp_Object obj, Lisp_Object prop); +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_plist (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_plist, 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->strikethru)); + ((markobj) (face->highlight)); + ((markobj) (face->dim)); + ((markobj) (face->blinking)); + ((markobj) (face->reverse)); + + ((markobj) (face->charsets_warned_about)); + + return (face->plist); +} + +static void +print_face (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag) +{ + struct Lisp_Face *face = XFACE (obj); + + if (print_readably) + { + write_c_string ("#s(face name ", printcharfun); + print_internal (face->name, printcharfun, 1); + write_c_string (")", printcharfun); + } + else + { + 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->strikethru, f2->strikethru, 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, 0, 0, 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 Lisp_Object +face_getprop (Lisp_Object obj, Lisp_Object prop) +{ + struct Lisp_Face *f = XFACE (obj); + +#define FROB(propprop) \ +do { \ + if (EQ (prop, Q##propprop)) \ + { \ + return f->propprop; \ + } \ +} while (0) + + FROB (foreground); + FROB (background); + FROB (font); + FROB (display_table); + FROB (background_pixmap); + FROB (underline); + FROB (strikethru); + FROB (highlight); + FROB (dim); + FROB (blinking); + FROB (reverse); + FROB (doc_string); + +#undef FROB + + return external_plist_get (&f->plist, prop, 0, ERROR_ME); +} + +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 (strikethru); + FROB (highlight); + FROB (dim); + FROB (blinking); + FROB (reverse); + +#undef FROB + + if (EQ (prop, Qdoc_string)) + { + if (!NILP (value)) + CHECK_STRING (value); + f->doc_string = value; + return 1; + } + + external_plist_put (&f->plist, prop, value, 0, ERROR_ME); + 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 (strikethru); + FROB (highlight); + FROB (dim); + FROB (blinking); + FROB (reverse); + +#undef FROB + + if (EQ (prop, Qdoc_string)) + { + f->doc_string = Qnil; + return 1; + } + + return external_remprop (&f->plist, prop, 0, ERROR_ME); +} + +static Lisp_Object +face_plist (Lisp_Object obj) +{ + struct Lisp_Face *f = XFACE (obj); + Lisp_Object result = Qnil; + +#define FROB(propprop) \ +do { \ + /* backwards order; we reverse it below */ \ + result = Fcons (f->propprop, Fcons (Q##propprop, result)); \ +} while (0) + + FROB (foreground); + FROB (background); + FROB (font); + FROB (display_table); + FROB (background_pixmap); + FROB (underline); + FROB (strikethru); + FROB (highlight); + FROB (dim); + FROB (blinking); + FROB (reverse); + +#undef FROB + return nconc2 (Fnreverse (result), f->plist); +} + + +/************************************************************************/ +/* face read syntax */ +/************************************************************************/ + +static int +face_name_validate (Lisp_Object keyword, Lisp_Object value, + Error_behavior errb) +{ + if (ERRB_EQ (errb, ERROR_ME)) + { + CHECK_SYMBOL (value); + return 1; + } + + return SYMBOLP (value); +} + +static int +face_validate (Lisp_Object data, Error_behavior errb) +{ + int name_seen = 0; + Lisp_Object valw; + + data = Fcdr (data); /* skip over Qface */ + while (!NILP (data)) + { + Lisp_Object keyw = Fcar (data); + + data = Fcdr (data); + valw = Fcar (data); + data = Fcdr (data); + if (EQ (keyw, Qname)) + { + if (name_seen) + { + maybe_signal_simple_error ("Duplicate face name given", valw, + Qface, errb); + return 0; + } + name_seen = 1; + } + else + abort (); + } + + if (!name_seen) + { + maybe_error (Qface, errb, "No face name given"); + return 0; + } + + if (NILP (Ffind_face (valw))) + { + maybe_signal_simple_error ("No such face", valw, Qface, errb); + return 0; + } + + return 1; +} + +static Lisp_Object +face_instantiate (Lisp_Object data) +{ + return Fget_face (Fcar (Fcdr (data))); +} + + +/**************************************************************************** + * 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->strikethru = Qnil; + f->highlight = Qnil; + f->dim = Qnil; + f->blinking = Qnil; + f->reverse = Qnil; + f->plist = Qnil; + f->charsets_warned_about = 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 value = XCDR (XCAR (values)); + if (VECTORP (value) && XVECTOR (value)->size) + { + if (EQ (Ffind_face (vector_data (XVECTOR (value))[0]), inh_face)) + Fset_specifier_dirty_flag + (FACE_PROPERTY_SPECIFIER (inh_face, property)); + } + } + } + + 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, Qstrikethru) + || 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, Qstrikethru); + 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; +} + +Lisp_Object +face_property_matching_instance (Lisp_Object face, Lisp_Object property, + Lisp_Object charset, Lisp_Object domain, + Error_behavior errb, int no_fallback, + Lisp_Object depth) +{ + Lisp_Object retval = + specifier_instance_no_quit (Fget (face, property, Qnil), charset, + domain, errb, no_fallback, depth); + + if (UNBOUNDP (retval) && !no_fallback) + { + if (EQ (property, Qfont)) + { + if (NILP (memq_no_quit (charset, + XFACE (face)->charsets_warned_about))) + { + warn_when_safe (Qfont, Qwarning, + "Unable to instantiate font for face %s", + string_data (symbol_name + (XSYMBOL (XFACE (face)->name)))); + XFACE (face)->charsets_warned_about = + Fcons (charset, XFACE (face)->charsets_warned_about); + } + retval = Vthe_null_font_instance; + } + } + + return retval; +} + + +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. +If FACE-OR-NAME is a face object, it is simply returned. +Otherwise, FACE-OR-NAME should be a symbol. If there is no such face, +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); + + /* 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. +Same as `find-face' except an error is signalled if there is no such +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); +} + +DEFUN ("built-in-face-specifiers", Fbuilt_in_face_specifiers, + Sbuilt_in_face_specifiers, 0, 0, 0 /* +Return a list of all built-in face specifier properties. +Don't modify this list! +*/ ) + () +{ + return Vbuilt_in_face_specifiers; +} + +/* These values are retrieved so often that we make a special + function. +*/ + +void +default_face_font_info (Lisp_Object domain, int *ascent, int *descent, + int *height, int *width, int *proportional_p) +{ + Lisp_Object font_instance; + + if (noninteractive) + { + if (ascent) + *ascent = 1; + if (descent) + *descent = 0; + if (height) + *height = 1; + if (width) + *width = 1; + if (proportional_p) + *proportional_p = 0; + return; + } + + /* We use ASCII here. This is probably reasonable because the + people calling this function are using the resulting values to + come up with overall sizes for windows and frames. */ + if (WINDOWP (domain)) + { + struct face_cachel *cachel; + struct window *w = XWINDOW (domain); + + /* #### It's possible for this function to get called when the + face cachels have not been initialized. I don't know why. */ + if (!Dynarr_length (w->face_cachels)) + reset_face_cachels (w); + cachel = WINDOW_FACE_CACHEL (w, DEFAULT_INDEX); + font_instance = FACE_CACHEL_FONT (cachel, Vcharset_ascii); + } + else + { + font_instance = FACE_FONT (Vdefault_face, domain, Vcharset_ascii); + } + + if (height) + *height = XFONT_INSTANCE (font_instance)->height; + if (width) + *width = XFONT_INSTANCE (font_instance)->width; + if (ascent) + *ascent = XFONT_INSTANCE (font_instance)->ascent; + if (descent) + *descent = XFONT_INSTANCE (font_instance)->descent; + if (proportional_p) + *proportional_p = XFONT_INSTANCE (font_instance)->proportional_p; +} + +void +default_face_height_and_width (Lisp_Object domain, + int *height, int *width) +{ + default_face_font_info (domain, 0, 0, height, width, 0); +} + +DEFUN ("face-list", Fface_list, Sface_list, 0, 1, 0 /* +Return a list of the names of all defined faces. +If TEMPORARY is nil, only the permanent faces are included. +If it is t, only the temporary faces are included. If it is any +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 ("make-face", Fmake_face, Smake_face, 1, 3, 0 /* +Defines and returns a new FACE described by DOC-STRING. +You can modify the font, color, etc of a face with the set-face- functions. +If the face already exists, it is unmodified. +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); + if (!NILP (doc_string)) + CHECK_STRING (doc_string); + + 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->strikethru = Fmake_specifier (Qface_boolean); + set_face_boolean_attached_to (f->strikethru, face, Qstrikethru); + 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->strikethru, + Fget (Vdefault_face, Qstrikethru, 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); + + /* Note that it's OK if we dump faces. + When we start up again when we're not noninteractive, + `init-global-faces' is called and it resources all + existing faces. */ + if (initialized && !noninteractive) + { + 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 && !noninteractive) + { + 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 * + ****************************************************************************/ + +/* + +#### Here is a description of how the face cache elements ought +to be redone. It is *NOT* how they work currently: + +However, when I started to go about implementing this, I realized +that there are all sorts of subtle problems with cache coherency +that are coming up. As it turns out, these problems don't +manifest themselves now due to the brute-force "kill 'em all" +approach to cache invalidation when faces change; but if this +is ever made smarter, these problems are going to come up, and +some of them are very non-obvious. + +I'm thinking of redoing the cache code a bit to avoid these +coherency problems. The bulk of the problems will arise because +the current display structures have simple indices into the +face cache, but the cache can be changed at various times, +which could make the current display structures incorrect. +I guess the dirty and updated flags are an attempt to fix +this, but this approach doesn't really work. + +Here's an approach that should keep things clean and unconfused: + +1) Imagine a "virtual face cache" that can grow arbitrarily + big and for which the only thing allowed is to add new + elements. Existing elements cannot be removed or changed. + This way, any pointers in the existing redisplay structure + into the cache never get screwed up. (This is important + because even if a cache element is out of date, if there's + a pointer to it then its contents still accurately describe + the way the text currently looks on the screen.) +2) Each element in the virtual cache either describes exactly + one face, or describes the merger of a number of faces + by some process. In order to simplify things, for mergers + we do not record which faces or ordering was used, but + simply that this cache element is the result of merging. + Unlike the current implementation, it's important that a + single cache element not be used to both describe a + single face and describe a merger, even if all the property + values are the same. +3) Each cache element can be clean or dirty. "Dirty" means + that the face that the element points to has been changed; + this gets set at the time the face is changed. This + way, when looking up a value in the cache, you can determine + whether it's out of date or not. For merged faces it + does not matter -- we don't record the faces or priority + used to create the merger, so it's impossible to look up + one of these faces. We have to recompute it each time. + Luckily, this is fine -- doing the merge is much + less expensive than recomputing the properties of a + single face. +4) For each cache element, we keep a hash value. (In order + to hash the boolean properties, we convert each of them + into a different large prime number so that the hashing works + well.) This allows us, when comparing runes, to properly + determine whether the face for that rune has changed. + This will be especially important for TTY's, where there + aren't that many faces and minimizing redraw is very + important. +5) We can't actually keep an infinite cache, but that doesn't + really matter that much. The only elements we care about + are those that are used by either the current or desired + display structs. Therefore, we keep a per-window + redisplay iteration number, and mark each element with + that number as we use it. Just after outputting the + window and synching the redisplay structs, we go through + the cache and invalidate all elements that are not clean + elements referring to a particular face and that do not + have an iteration number equal to the current one. We + keep them in a chain, and use them to allocate new + elements when possible instead of increasing the Dynarr. + + */ + +/* mark for GC a dynarr of face cachels. */ + +void +mark_face_cachels (face_cachel_dynarr *elements, + void (*markobj) (Lisp_Object)) +{ + int elt; + + if (!elements) + return; + + for (elt = 0; elt < Dynarr_length (elements); elt++) + { + struct face_cachel *cachel = Dynarr_atp (elements, elt); + + { + int i; + + for (i = 0; i < NUM_LEADING_BYTES; i++) + if (!NILP (cachel->font[i]) && !UNBOUNDP (cachel->font[i])) + ((markobj) (cachel->font[i])); + } + ((markobj) (cachel->face)); + ((markobj) (cachel->foreground)); + ((markobj) (cachel->background)); + ((markobj) (cachel->display_table)); + ((markobj) (cachel->background_pixmap)); + } +} + +/* ensure that the given cachel contains an updated font value for + the given charset. Return the updated font value. */ + +Lisp_Object +ensure_face_cachel_contains_charset (struct face_cachel *cachel, + Lisp_Object domain, Lisp_Object charset) +{ + Lisp_Object new_val; + Lisp_Object face = cachel->face; + int bound = 1; + int offs = XCHARSET_LEADING_BYTE (charset) - MIN_LEADING_BYTE; + + if (!UNBOUNDP (cachel->font[offs]) + && cachel->font_updated[offs]) + return cachel->font[offs]; + + if (UNBOUNDP (face)) + { + /* a merged face. */ + int i; + struct window *w = XWINDOW (domain); + + new_val = Qunbound; + cachel->font_specified[offs] = 0; + for (i = 0; i < cachel->nfaces; i++) + { + struct face_cachel *oth; + + oth = Dynarr_atp (w->face_cachels, + FACE_CACHEL_FINDEX_UNSAFE (cachel, i)); + /* Tout le monde aime la recursion */ + ensure_face_cachel_contains_charset (oth, domain, charset); + + if (oth->font_specified[offs]) + { + new_val = oth->font[offs]; + cachel->font_specified[offs] = 1; + break; + } + } + + if (!cachel->font_specified[offs]) + /* need to do the default face. */ + { + struct face_cachel *oth = + Dynarr_atp (w->face_cachels, DEFAULT_INDEX); + ensure_face_cachel_contains_charset (oth, domain, charset); + + new_val = oth->font[offs]; + } + + if (!UNBOUNDP (cachel->font[offs]) && !EQ (cachel->font[offs], new_val)) + cachel->dirty = 1; + cachel->font_updated[offs] = 1; + cachel->font[offs] = new_val; + return new_val; + } + + new_val = face_property_matching_instance (face, Qfont, charset, domain, + /* #### look into ERROR_ME_NOT */ + ERROR_ME_NOT, 1, Qzero); + if (UNBOUNDP (new_val)) + { + bound = 0; + new_val = face_property_matching_instance (face, Qfont, + charset, domain, + /* #### look into + ERROR_ME_NOT */ + ERROR_ME_NOT, 0, Qzero); + } + if (!UNBOUNDP (cachel->font[offs]) && !EQ (new_val, cachel->font[offs])) + cachel->dirty = 1; + cachel->font_updated[offs] = 1; + cachel->font[offs] = new_val; + cachel->font_specified[offs] = (bound || EQ (face, Vdefault_face)); + return new_val; +} + +/* Ensure that the given cachel contains updated fonts for all + the charsets specified. */ + +void +ensure_face_cachel_complete (struct face_cachel *cachel, + Lisp_Object domain, unsigned char *charsets) +{ + int i; + + for (i = 0; i < NUM_LEADING_BYTES; i++) + if (charsets[i]) + { + Lisp_Object charset; + + charset = CHARSET_BY_LEADING_BYTE (i + MIN_LEADING_BYTE); + assert (CHARSETP (charset)); + ensure_face_cachel_contains_charset (cachel, domain, charset); + } +} + +void +face_cachel_charset_font_metric_info (struct face_cachel *cachel, + unsigned char *charsets, + struct font_metric_info *fm) +{ + int i; + + fm->width = 1; + fm->height = fm->ascent = 1; + fm->descent = 0; + fm->proportional_p = 0; + + for (i = 0; i < NUM_LEADING_BYTES; i++) + { + if (charsets[i]) + { + Lisp_Object charset; + Lisp_Object font_instance; + struct Lisp_Font_Instance *fi; + + charset = CHARSET_BY_LEADING_BYTE (i + MIN_LEADING_BYTE); + assert (CHARSETP (charset)); + font_instance = FACE_CACHEL_FONT (cachel, charset); + assert (FONT_INSTANCEP (font_instance)); + fi = XFONT_INSTANCE (font_instance); + fm->ascent = max ((int) fi->ascent, (int) fm->ascent); + fm->descent = max ((int) fi->descent, (int) fm->descent); + fm->height = fm->ascent + fm->descent; + if (fi->proportional_p) + fm->proportional_p = 1; + if (EQ (charset, Vcharset_ascii)) + fm->width = fi->width; + } + } +} + +/* Called when the updated flag has been cleared on a cachel. */ + +void +update_face_cachel_data (struct face_cachel *cachel, + Lisp_Object domain, + Lisp_Object face) +{ + if (XFACE (face)->dirty || UNBOUNDP (cachel->face)) + { + int default_face = EQ (face, Vdefault_face); + 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, domain, 1, Qzero); \ + int bound = 1; \ + if (UNBOUNDP (new_val)) \ + { \ + bound = 0; \ + new_val = FACE_PROPERTY_INSTANCE (face, Q##field, domain, 0, Qzero); \ + } \ + 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 (display_table); + FROB (background_pixmap); +#undef FROB + + ensure_face_cachel_contains_charset (cachel, domain, Vcharset_ascii); + +#define FROB(field) \ + do { \ + Lisp_Object new_val = \ + FACE_PROPERTY_INSTANCE (face, Q##field, domain, 1, Qzero); \ + int bound = 1; \ + int new_val_int; \ + if (UNBOUNDP (new_val)) \ + { \ + bound = 0; \ + new_val = FACE_PROPERTY_INSTANCE (face, Q##field, domain, 0, Qzero); \ + } \ + 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 (strikethru); + FROB (highlight); + FROB (dim); + FROB (reverse); + FROB (blinking); +#undef FROB + } + + cachel->updated = 1; +} + +/* Merge the cachel identified by FINDEX in window W into the given + cachel. */ + +static void +merge_face_cachel_data (struct window *w, face_index findex, + struct face_cachel *cachel) +{ +#define FINDEX_FIELD(field) \ + Dynarr_atp (w->face_cachels, 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 (display_table); + FROB (background_pixmap); + FROB (underline); + FROB (strikethru); + FROB (highlight); + FROB (dim); + FROB (reverse); + FROB (blinking); + /* And do ASCII, of course. */ + { + int offs = LEADING_BYTE_ASCII - MIN_LEADING_BYTE; + + if (!cachel->font_specified[offs] && FINDEX_FIELD (font_specified[offs])) + { + cachel->font[offs] = FINDEX_FIELD (font[offs]); + cachel->font_specified[offs] = 1; + cachel->dirty = 1; + } + } + +#undef FROB +#undef FINDEX_FIELD + + cachel->updated = 1; +} + +/* Initialize a cachel. */ + +void +reset_face_cachel (struct face_cachel *cachel) +{ + memset (cachel, 0, sizeof (struct face_cachel)); + cachel->face = Qunbound; + cachel->nfaces = 0; + cachel->merged_faces = 0; + cachel->foreground = Qunbound; + cachel->background = Qunbound; + { + int i; + + for (i = 0; i < NUM_LEADING_BYTES; i++) + cachel->font[i] = Qunbound; + } + cachel->display_table = Qunbound; + cachel->background_pixmap = Qunbound; +} + +/* Add a cachel for the given face to the given window's cache. */ + +static void +add_face_cachel (struct window *w, Lisp_Object face) +{ + struct face_cachel new_cachel; + Lisp_Object window; + + reset_face_cachel (&new_cachel); + XSETWINDOW (window, w); + update_face_cachel_data (&new_cachel, window, face); + Dynarr_add (w->face_cachels, new_cachel); +} + +/* Retrieve the index to a cachel for window W that corresponds to + the specified face. If necessary, add a new element to the + cache. */ + +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_cachels); elt++) + { + struct face_cachel *cachel = WINDOW_FACE_CACHEL (w, elt); + + if (EQ (cachel->face, face)) + { + Lisp_Object window = Qnil; + XSETWINDOW (window, w); + if (!cachel->updated) + update_face_cachel_data (cachel, window, face); + return elt; + } + } + + /* If we didn't find the face, add it and then return its index. */ + add_face_cachel (w, face); + return elt; +} + +void +reset_face_cachels (struct window *w) +{ + /* #### Not initialized in batch mode for the stream device. */ + if (w->face_cachels) + { + int i; + + for (i = 0; i < Dynarr_length (w->face_cachels); i++) + { + struct face_cachel *cachel = Dynarr_atp (w->face_cachels, i); + if (cachel->merged_faces) + Dynarr_free (cachel->merged_faces); + } + Dynarr_reset (w->face_cachels); + 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_cachels_as_clean (struct window *w) +{ + int elt; + + for (elt = 0; elt < Dynarr_length (w->face_cachels); elt++) + Dynarr_atp (w->face_cachels, elt)->dirty = 0; +} + +void +mark_face_cachels_as_not_updated (struct window *w) +{ + int elt; + + for (elt = 0; elt < Dynarr_length (w->face_cachels); elt++) + { + struct face_cachel *cachel = Dynarr_atp (w->face_cachels, elt); + int i; + + cachel->updated = 0; + for (i = 0; i < NUM_LEADING_BYTES; i++) + cachel->font_updated[i] = 0; + } +} + +#ifdef MEMORY_USAGE_STATS + +int +compute_face_cachel_usage (face_cachel_dynarr *face_cachels, + struct overhead_stats *ovstats) +{ + int total = 0; + + if (face_cachels) + { + int i; + + total += Dynarr_memory_usage (face_cachels, ovstats); + for (i = 0; i < Dynarr_length (face_cachels); i++) + { + int_dynarr *merged = Dynarr_at (face_cachels, i).merged_faces; + if (merged) + total += Dynarr_memory_usage (merged, ovstats); + } + } + + return total; +} + +#endif /* MEMORY_USAGE_STATS */ + + +/***************************************************************************** + * merged face functions * + *****************************************************************************/ + +/* Compare two merged face cachels to determine whether we have to add + a new entry to the face cache. + + Note that we do not compare the attributes, but just the faces the + cachels are based on. If they are the same, then the cachels certainly + ought to have the same attributes, except in the case where fonts + for different charsets have been determined in the two -- and in that + case this difference is fine. */ + +static int +compare_merged_face_cachels (struct face_cachel *cachel1, + struct face_cachel *cachel2) +{ + int i; + + if (!EQ (cachel1->face, cachel2->face) + || cachel1->nfaces != cachel2->nfaces) + return 0; + + for (i = 0; i < cachel1->nfaces; i++) + if (FACE_CACHEL_FINDEX_UNSAFE (cachel1, i) + != FACE_CACHEL_FINDEX_UNSAFE (cachel2, i)) + return 0; + + return 1; +} + +/* Retrieve the index to a cachel for window W that corresponds to + the specified cachel. If necessary, add a new element to the + cache. This is similar to get_builtin_face_cache_index() but + is intended for merged cachels rather than for cachels representing + just a face. + + Note that a merged cachel for just one face is not the same as + the simple cachel for that face, because it is also merged with + the default face. */ + +static face_index +get_merged_face_cache_index (struct window *w, + struct face_cachel *merged_cachel) +{ + int elt; + int cache_size = Dynarr_length (w->face_cachels); + + for (elt = 0; elt < cache_size; elt++) + { + struct face_cachel *cachel = + Dynarr_atp (w->face_cachels, elt); + + if (compare_merged_face_cachels (cachel, merged_cachel)) + return elt; + } + + /* We didn't find it so add this instance to the cache. */ + merged_cachel->updated = 1; + merged_cachel->dirty = 1; + Dynarr_add (w->face_cachels, *merged_cachel); + return cache_size; +} + +face_index +get_extent_fragment_face_cache_index (struct window *w, + struct extent_fragment *ef) +{ + struct face_cachel cachel; + int len = Dynarr_length (ef->extents); + face_index findex; + Lisp_Object window = Qnil; + XSETWINDOW (window, w); + + /* Optimize the default case. */ + if (len == 0) + return DEFAULT_INDEX; + else + { + int i; + + /* Merge the faces of the extents together in order. */ + + reset_face_cachel (&cachel); + + for (i = len - 1; i >= 0; i--) + { + EXTENT current = Dynarr_at (ef->extents, i); + int has_findex = 0; + Lisp_Object face = extent_face (current); + + if (FACEP (face)) + { + findex = get_builtin_face_cache_index (w, face); + has_findex = 1; + merge_face_cachel_data (w, findex, &cachel); + } + /* remember, we're called from within redisplay + so we can't error. */ + else while (CONSP (face)) + { + Lisp_Object one_face = XCAR (face); + if (FACEP (one_face)) + { + findex = get_builtin_face_cache_index (w, one_face); + merge_face_cachel_data (w, findex, &cachel); + + /* code duplication here but there's no clean + way to avoid it. */ + if (cachel.nfaces >= NUM_STATIC_CACHEL_FACES) + { + if (!cachel.merged_faces) + cachel.merged_faces = Dynarr_new (int); + Dynarr_add (cachel.merged_faces, findex); + } + else + cachel.merged_faces_static[cachel.nfaces] = findex; + cachel.nfaces++; + } + face = XCDR (face); + } + + if (has_findex) + { + if (cachel.nfaces >= NUM_STATIC_CACHEL_FACES) + { + if (!cachel.merged_faces) + cachel.merged_faces = Dynarr_new (int); + Dynarr_add (cachel.merged_faces, findex); + } + else + cachel.merged_faces_static[cachel.nfaces] = findex; + cachel.nfaces++; + } + } + + /* Now finally merge in the default face. */ + findex = get_builtin_face_cache_index (w, Vdefault_face); + merge_face_cachel_data (w, findex, &cachel); + + return get_merged_face_cache_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_X_P (frm)) + { + Arg av[10]; + int ac = 0; + + if (EQ (name, Qforeground)) + { + Lisp_Object color = FACE_FOREGROUND (Vdefault_face, frame); + XColor fgc; + + if (!EQ (color, Vthe_null_color_instance)) + { + 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; + + if (!EQ (color, Vthe_null_color_instance)) + { + 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, Vcharset_ascii); + + if (!EQ (font, Vthe_null_font_instance)) + XtSetArg (av[ac], XtNfont, + (void *) FONT_INSTANCE_X_FONT (XFONT_INSTANCE (font))); + ac++; + } + else + abort (); + + XtSetValues (FRAME_X_TEXT_WIDGET (frm), av, ac); + +#ifdef HAVE_TOOLBARS + /* 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)); +#endif + + /* 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_int (frm->width), + make_int (frm->height), + Qnil); + } + } +#endif /* HAVE_X_WINDOWS */ + +#ifdef HAVE_NEXTSTEP + if (FRAME_NS_P (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 frmcons; + + DEVICE_FRAME_LOOP (frmcons, XDEVICE (locale)) + update_EmacsFrame (XCAR (frmcons), name); + } + else if (EQ (locale, Qglobal) || EQ (locale, Qfallback)) + { + Lisp_Object frmcons, devcons, concons; + + FRAME_LOOP_NO_BREAK (frmcons, devcons, concons) + update_EmacsFrame (XCAR (frmcons), 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 devcons, concons; + + DEVICE_LOOP_NO_BREAK (devcons, concons) + MARK_DEVICE_FRAMES_FACES_CHANGED (XDEVICE (XCAR (devcons))); + } + + 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, +or makes an already-existing face be exactly like another. LOCALE, +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); + + /* 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->strikethru, fnew->strikethru, 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 +syms_of_faces (void) +{ + /* Qdefault defined in general.c */ + defsymbol (&Qmodeline, "modeline"); + defsymbol (&Qleft_margin, "left-margin"); + defsymbol (&Qright_margin, "right-margin"); + defsymbol (&Qtext_cursor, "text-cursor"); + + defsubr (&Sfacep); + defsubr (&Sfind_face); + defsubr (&Sget_face); + defsubr (&Sface_name); + defsubr (&Sbuilt_in_face_specifiers); + defsubr (&Sface_list); + defsubr (&Smake_face); + defsubr (&Scopy_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"); + defsymbol (&Qstrikethru, "strikethru"); + /* 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"); +} + +void +structure_type_create_faces (void) +{ + struct structure_type *st; + + st = define_structure_type (Qface, face_validate, face_instantiate); + + define_structure_type_keyword (st, Qname, face_name_validate); +} + +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 (&Vleft_margin_face); + Vleft_margin_face = Qnil; + staticpro (&Vright_margin_face); + Vright_margin_face = Qnil; + staticpro (&Vtext_cursor_face); + Vtext_cursor_face = Qnil; + staticpro (&Vpointer_face); + Vpointer_face = Qnil; + + { + Lisp_Object syms[20]; + int n = 0; + + syms[n++] = Qforeground; + syms[n++] = Qbackground; + syms[n++] = Qfont; + syms[n++] = Qdisplay_table; + syms[n++] = Qbackground_pixmap; + syms[n++] = Qunderline; + syms[n++] = Qstrikethru; + syms[n++] = Qhighlight; + syms[n++] = Qdim; + syms[n++] = Qblinking; + syms[n++] = Qreverse; + + Vbuilt_in_face_specifiers = pure_list (n, syms); + staticpro (&Vbuilt_in_face_specifiers); + } +} + +void +complex_vars_of_faces (void) +{ + Vpermanent_faces_cache = make_lisp_hashtable (10, HASHTABLE_NONWEAK, + HASHTABLE_EQ); + Vtemporary_faces_cache = make_lisp_hashtable (0, HASHTABLE_WEAK, + HASHTABLE_EQ); + + /* 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); + + /* Provide some last-resort fallbacks to avoid utter fuckage if + someone provides invalid values for the global specifications. */ + + { + Lisp_Object fg_inst_list = Qnil, bg_inst_list = Qnil; + +#ifdef HAVE_X_WINDOWS + fg_inst_list = Fcons (Fcons (list1 (Qx), build_string ("black")), + fg_inst_list); + bg_inst_list = Fcons (Fcons (list1 (Qx), build_string ("white")), + bg_inst_list); +#endif +#ifdef HAVE_TTY + fg_inst_list = Fcons (Fcons (list1 (Qtty), Fvector (0, 0)), + fg_inst_list); + bg_inst_list = Fcons (Fcons (list1 (Qtty), Fvector (0, 0)), + bg_inst_list); +#endif + set_specifier_fallback (Fget (Vdefault_face, Qforeground, Qnil), + fg_inst_list); + set_specifier_fallback (Fget (Vdefault_face, Qbackground, Qnil), + bg_inst_list); + } + + /* #### We may want to have different fallback values if NeXTstep + support is compiled in. */ + { + Lisp_Object inst_list = Qnil; +#ifdef HAVE_X_WINDOWS + CONST char *fonts[30]; + int n = 0; + + /* 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); +#endif +#ifdef HAVE_TTY + inst_list = Fcons (Fcons (list1 (Qtty), build_string ("normal")), + inst_list); +#endif + set_specifier_fallback (Fget (Vdefault_face, Qfont, Qnil), inst_list); + } + + set_specifier_fallback (Fget (Vdefault_face, Qunderline, Qnil), + list1 (Fcons (Qnil, Qnil))); + set_specifier_fallback (Fget (Vdefault_face, Qstrikethru, 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))); + + /* Now create the other faces that redisplay needs to refer to + directly. We could create them in Lisp but it's simpler this + way since we need to get them anyway. */ + Vmodeline_face = Fmake_face (Qmodeline, build_string ("modeline face"), + Qnil); + Vleft_margin_face = Fmake_face (Qleft_margin, + build_string ("left margin face"), + Qnil); + Vright_margin_face = Fmake_face (Qright_margin, + build_string ("right margin face"), + Qnil); + Vtext_cursor_face = Fmake_face (Qtext_cursor, + build_string ("face for text cursor"), + Qnil); + Vpointer_face = + Fmake_face (Qpointer, + build_string + ("face for foreground/background colors of mouse pointer"), + Qnil); +}