29bb731a6a659f6203631a099a8243a592b97024
[sxemacs] / src / ui / specifier.c
1 /* Specifier implementation
2    Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
3    Copyright (C) 1995, 1996 Ben Wing.
4    Copyright (C) 1995 Sun Microsystems, Inc.
5
6 This file is part of SXEmacs
7
8 SXEmacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 SXEmacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
20
21
22 /* Synched up with: Not in FSF. */
23
24 /* Design by Ben Wing;
25    Original version by Chuck Thompson;
26    rewritten by Ben Wing;
27    Magic specifiers by Kirill Katsnelson;
28 */
29
30 #include <config.h>
31 #include "lisp.h"
32
33 #include "buffer.h"
34 #include "device.h"
35 #include "frame.h"
36 #include "opaque.h"
37 #include "specifier.h"
38 #include "window.h"
39 #include "chartab.h"
40 #include "rangetab.h"
41
42 Lisp_Object Qspecifierp;
43 Lisp_Object Qremove_tag_set_prepend, Qremove_tag_set_append;
44 Lisp_Object Qremove_locale, Qremove_locale_type;
45
46 Lisp_Object Qconsole_type, Qdevice_class;
47
48 Lisp_Object Qspecifier_syntax_error;
49 Lisp_Object Qspecifier_argument_error;
50 Lisp_Object Qspecifier_change_error;
51
52 static Lisp_Object Vuser_defined_tags;
53
54 typedef struct specifier_type_entry specifier_type_entry;
55 struct specifier_type_entry {
56         Lisp_Object symbol;
57         struct specifier_methods *meths;
58 };
59
60 typedef struct {
61         Dynarr_declare(specifier_type_entry);
62 } specifier_type_entry_dynarr;
63
64 static specifier_type_entry_dynarr *the_specifier_type_entry_dynarr;
65
66 static const struct lrecord_description ste_description_1[] = {
67         {XD_LISP_OBJECT, offsetof(specifier_type_entry, symbol)},
68         {XD_STRUCT_PTR, offsetof(specifier_type_entry, meths), 1,
69          &specifier_methods_description},
70         {XD_END}
71 };
72
73 static const struct struct_description ste_description = {
74         sizeof(specifier_type_entry),
75         ste_description_1
76 };
77
78 static const struct lrecord_description sted_description_1[] = {
79         XD_DYNARR_DESC(specifier_type_entry_dynarr, &ste_description),
80         {XD_END}
81 };
82
83 static const struct struct_description sted_description = {
84         sizeof(specifier_type_entry_dynarr),
85         sted_description_1
86 };
87
88 static Lisp_Object Vspecifier_type_list;
89
90 static Lisp_Object Vcached_specifiers;
91 /* Do NOT mark through this, or specifiers will never be GC'd. */
92 static Lisp_Object Vall_specifiers;
93
94 static Lisp_Object Vunlock_ghost_specifiers;
95
96 /* #### The purpose of this is to check for inheritance loops
97    in specifiers that can inherit from other specifiers, but it's
98    not yet implemented.
99
100    #### Look into this for 19.14. */
101 /* static Lisp_Object_dynarr current_specifiers; */
102
103 static void recompute_cached_specifier_everywhere(Lisp_Object specifier);
104
105 EXFUN(Fspecifier_specs, 4);
106 EXFUN(Fremove_specifier, 4);
107 \f
108 /************************************************************************/
109 /*                       Specifier object methods                       */
110 /************************************************************************/
111
112 /* Remove dead objects from the specified assoc list. */
113
114 static Lisp_Object cleanup_assoc_list(Lisp_Object list)
115 {
116         Lisp_Object loop, prev, retval;
117
118         loop = retval = list;
119         prev = Qnil;
120
121         while (!NILP(loop)) {
122                 Lisp_Object entry = XCAR(loop);
123                 Lisp_Object key = XCAR(entry);
124
125                 /* remember, dead windows can become alive again. */
126                 if (!WINDOWP(key) && object_dead_p(key)) {
127                         if (NILP(prev)) {
128                                 /* Removing the head. */
129                                 retval = XCDR(retval);
130                         } else {
131                                 Fsetcdr(prev, XCDR(loop));
132                         }
133                 } else
134                         prev = loop;
135
136                 loop = XCDR(loop);
137         }
138
139         return retval;
140 }
141
142 /* Remove dead objects from the various lists so that they
143    don't keep getting marked as long as this specifier exists and
144    therefore wasting memory. */
145
146 void cleanup_specifiers(void)
147 {
148         Lisp_Object rest;
149
150         for (rest = Vall_specifiers;
151              !NILP(rest); rest = XSPECIFIER(rest)->next_specifier) {
152                 Lisp_Specifier *sp = XSPECIFIER(rest);
153                 /* This effectively changes the specifier specs.
154                    However, there's no need to call
155                    recompute_cached_specifier_everywhere() or the
156                    after-change methods because the only specs we
157                    are removing are for dead objects, and they can
158                    never have any effect on the specifier values:
159                    specifiers can only be instantiated over live
160                    objects, and you can't derive a dead object
161                    from a live one. */
162                 sp->device_specs = cleanup_assoc_list(sp->device_specs);
163                 sp->frame_specs = cleanup_assoc_list(sp->frame_specs);
164                 sp->buffer_specs = cleanup_assoc_list(sp->buffer_specs);
165                 /* windows are handled specially because dead windows
166                    can be resurrected */
167         }
168 }
169
170 void kill_specifier_buffer_locals(Lisp_Object buffer)
171 {
172         Lisp_Object rest;
173
174         for (rest = Vall_specifiers;
175              !NILP(rest); rest = XSPECIFIER(rest)->next_specifier) {
176                 Lisp_Specifier *sp = XSPECIFIER(rest);
177
178                 /* Make sure we're actually going to be changing something.
179                    Fremove_specifier() always calls
180                    recompute_cached_specifier_everywhere() (#### but should
181                    be smarter about this). */
182                 if (!NILP(assq_no_quit(buffer, sp->buffer_specs)))
183                         Fremove_specifier(rest, buffer, Qnil, Qnil);
184         }
185 }
186
187 static Lisp_Object mark_specifier(Lisp_Object obj)
188 {
189         Lisp_Specifier *specifier = XSPECIFIER(obj);
190
191         mark_object(specifier->global_specs);
192         mark_object(specifier->device_specs);
193         mark_object(specifier->frame_specs);
194         mark_object(specifier->window_specs);
195         mark_object(specifier->buffer_specs);
196         mark_object(specifier->magic_parent);
197         mark_object(specifier->fallback);
198         if (!GHOST_SPECIFIER_P(XSPECIFIER(obj)))
199                 MAYBE_SPECMETH(specifier, mark, (obj));
200         return Qnil;
201 }
202
203 /* The idea here is that the specifier specs point to locales
204    (windows, buffers, frames, and devices), and we want to make sure
205    that the specs disappear automatically when the associated locale
206    is no longer in use.  For all but windows, "no longer in use"
207    corresponds exactly to when the object is deleted (non-deleted
208    objects are always held permanently in special lists, and deleted
209    objects are never on these lists and never reusable).  To handle
210    this, we just have cleanup_specifiers() called periodically
211    (at the beginning of garbage collection); it removes all dead
212    objects.
213
214    For windows, however, it's trickier because dead objects can be
215    converted to live ones again if the dead object is in a window
216    configuration.  Therefore, for windows, "no longer in use"
217    corresponds to when the window object is garbage-collected.
218    We now use weak lists for this purpose.
219
220 */
221
222 void prune_specifiers(void)
223 {
224         Lisp_Object rest, prev = Qnil;
225
226         for (rest = Vall_specifiers;
227              !NILP(rest); rest = XSPECIFIER(rest)->next_specifier) {
228                 if (!marked_p(rest)) {
229                         Lisp_Specifier *sp = XSPECIFIER(rest);
230                         /* A bit of assertion that we're removing both parts of the
231                            magic one altogether */
232 #if !defined HAVE_BDWGC || !defined EF_USE_BDWGC
233 #  define MARKED_P(a) marked_p(a)
234 #else
235 #  define MARKED_P(a) 1
236 #endif
237                         assert(!MAGIC_SPECIFIER_P(sp)
238                                || ( BODILY_SPECIFIER_P(sp) && 
239                                     MARKED_P(sp->fallback) )
240                                || ( GHOST_SPECIFIER_P(sp) && 
241                                     MARKED_P(sp->magic_parent)));
242 #undef MARKED_P
243                         /* This specifier is garbage.  Remove it from the list. */
244                         if (NILP(prev))
245                                 Vall_specifiers = sp->next_specifier;
246                         else
247                                 XSPECIFIER(prev)->next_specifier =
248                                     sp->next_specifier;
249                 } else
250                         prev = rest;
251         }
252 }
253
254 static void
255 print_specifier(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
256 {
257         Lisp_Specifier *sp = XSPECIFIER(obj);
258         int count = specpdl_depth();
259         Lisp_Object the_specs;
260
261         if (print_readably)
262                 error("printing unreadable object #<%s-specifier 0x%x>",
263                       sp->methods->name, sp->header.uid);
264
265         write_fmt_string(printcharfun, "#<%s-specifier global=", sp->methods->name);
266         specbind(Qprint_string_length, make_int(100));
267         specbind(Qprint_length, make_int(5));
268         the_specs = Fspecifier_specs(obj, Qglobal, Qnil, Qnil);
269         if (NILP(the_specs))
270                 /* there are no global specs */
271                 write_c_string("<unspecified>", printcharfun);
272         else
273                 print_internal(the_specs, printcharfun, 1);
274         if (!NILP(sp->fallback)) {
275                 write_c_string(" fallback=", printcharfun);
276                 print_internal(sp->fallback, printcharfun, escapeflag);
277         }
278         unbind_to(count, Qnil);
279         write_fmt_str(printcharfun," 0x%x>", sp->header.uid);
280 }
281
282 static void finalize_specifier(void *header, int for_disksave)
283 {
284         Lisp_Specifier *sp = (Lisp_Specifier *) header;
285         /* don't be snafued by the disksave finalization. */
286         if (!for_disksave && !GHOST_SPECIFIER_P(sp) && sp->caching) {
287                 xfree(sp->caching);
288                 sp->caching = 0;
289         }
290 }
291
292 static int specifier_equal(Lisp_Object obj1, Lisp_Object obj2, int depth)
293 {
294         Lisp_Specifier *s1 = XSPECIFIER(obj1);
295         Lisp_Specifier *s2 = XSPECIFIER(obj2);
296         int retval;
297         Lisp_Object old_inhibit_quit = Vinhibit_quit;
298
299         /* This function can be called from within redisplay.
300            internal_equal can trigger a quit.  That leads to Bad Things. */
301         Vinhibit_quit = Qt;
302
303         depth++;
304         retval =
305             (s1->methods == s2->methods &&
306              internal_equal(s1->global_specs, s2->global_specs, depth) &&
307              internal_equal(s1->device_specs, s2->device_specs, depth) &&
308              internal_equal(s1->frame_specs, s2->frame_specs, depth) &&
309              internal_equal(s1->window_specs, s2->window_specs, depth) &&
310              internal_equal(s1->buffer_specs, s2->buffer_specs, depth) &&
311              internal_equal(s1->fallback, s2->fallback, depth));
312
313         if (retval && HAS_SPECMETH_P(s1, equal))
314                 retval = SPECMETH(s1, equal, (obj1, obj2, depth - 1));
315
316         Vinhibit_quit = old_inhibit_quit;
317         return retval;
318 }
319
320 static unsigned long specifier_hash(Lisp_Object obj, int depth)
321 {
322         Lisp_Specifier *s = XSPECIFIER(obj);
323
324         /* specifier hashing is a bit problematic because there are so
325            many places where data can be stored.  We pick what are perhaps
326            the most likely places where interesting stuff will be. */
327         return HASH5((HAS_SPECMETH_P(s, hash) ?
328                       SPECMETH(s, hash, (obj, depth)) : 0),
329                      (unsigned long)s->methods,
330                      internal_hash(s->global_specs, depth + 1),
331                      internal_hash(s->frame_specs, depth + 1),
332                      internal_hash(s->buffer_specs, depth + 1));
333 }
334
335 inline static size_t
336 aligned_sizeof_specifier(size_t specifier_type_specific_size)
337 {
338         return ALIGN_SIZE(offsetof(Lisp_Specifier, data)
339                           + specifier_type_specific_size, ALIGNOF(max_align_t));
340 }
341
342 static size_t sizeof_specifier(const void *header)
343 {
344         const Lisp_Specifier *p = (const Lisp_Specifier *)header;
345         return aligned_sizeof_specifier(GHOST_SPECIFIER_P(p)
346                                         ? 0 : p->methods->extra_data_size);
347 }
348
349 static const struct lrecord_description specifier_methods_description_1[] = {
350         {XD_LISP_OBJECT, offsetof(struct specifier_methods, predicate_symbol)},
351         {XD_END}
352 };
353
354 const struct struct_description specifier_methods_description = {
355         sizeof(struct specifier_methods),
356         specifier_methods_description_1
357 };
358
359 static const struct lrecord_description specifier_caching_description_1[] = {
360         {XD_END}
361 };
362
363 static const struct struct_description specifier_caching_description = {
364         sizeof(struct specifier_caching),
365         specifier_caching_description_1
366 };
367
368 static const struct lrecord_description specifier_description[] = {
369         {XD_STRUCT_PTR, offsetof(Lisp_Specifier, methods), 1,
370          &specifier_methods_description},
371         {XD_LO_LINK, offsetof(Lisp_Specifier, next_specifier)},
372         {XD_LISP_OBJECT, offsetof(Lisp_Specifier, global_specs)},
373         {XD_LISP_OBJECT, offsetof(Lisp_Specifier, device_specs)},
374         {XD_LISP_OBJECT, offsetof(Lisp_Specifier, frame_specs)},
375         {XD_LISP_OBJECT, offsetof(Lisp_Specifier, window_specs)},
376         {XD_LISP_OBJECT, offsetof(Lisp_Specifier, buffer_specs)},
377         {XD_STRUCT_PTR, offsetof(Lisp_Specifier, caching), 1,
378          &specifier_caching_description},
379         {XD_LISP_OBJECT, offsetof(Lisp_Specifier, magic_parent)},
380         {XD_LISP_OBJECT, offsetof(Lisp_Specifier, fallback)},
381         {XD_SPECIFIER_END}
382 };
383
384 const struct lrecord_description specifier_empty_extra_description[] = {
385         {XD_END}
386 };
387
388 DEFINE_LRECORD_SEQUENCE_IMPLEMENTATION("specifier", specifier,
389                                        mark_specifier, print_specifier,
390                                        finalize_specifier,
391                                        specifier_equal, specifier_hash,
392                                        specifier_description,
393                                        sizeof_specifier, Lisp_Specifier);
394 \f
395 /************************************************************************/
396 /*                       Creating specifiers                            */
397 /************************************************************************/
398
399 static struct specifier_methods *decode_specifier_type(Lisp_Object type,
400                                                        Error_behavior errb)
401 {
402         int i;
403
404         for (i = 0; i < Dynarr_length(the_specifier_type_entry_dynarr); i++) {
405                 if (EQ
406                     (type,
407                      Dynarr_at(the_specifier_type_entry_dynarr, i).symbol))
408                         return Dynarr_at(the_specifier_type_entry_dynarr,
409                                          i).meths;
410         }
411
412         maybe_signal_type_error(Qspecifier_argument_error,
413                                 "Invalid specifier type", type, Qspecifier,
414                                 errb);
415
416         return 0;
417 }
418
419 static int valid_specifier_type_p(Lisp_Object type)
420 {
421         return decode_specifier_type(type, ERROR_ME_NOT) != 0;
422 }
423
424 DEFUN("valid-specifier-type-p", Fvalid_specifier_type_p, 1, 1, 0,       /*
425 Given a SPECIFIER-TYPE, return non-nil if it is valid.
426 Valid types are 'generic, 'integer, 'boolean, 'color, 'font, 'image,
427 'face-boolean, and 'toolbar.
428 */
429       (specifier_type))
430 {
431         return valid_specifier_type_p(specifier_type) ? Qt : Qnil;
432 }
433
434 DEFUN("specifier-type-list", Fspecifier_type_list, 0, 0, 0,     /*
435 Return a list of valid specifier types.
436 */
437       ())
438 {
439         return Fcopy_sequence(Vspecifier_type_list);
440 }
441
442 void
443 add_entry_to_specifier_type_list(Lisp_Object symbol,
444                                  struct specifier_methods *meths)
445 {
446         struct specifier_type_entry entry;
447
448         entry.symbol = symbol;
449         entry.meths = meths;
450         Dynarr_add(the_specifier_type_entry_dynarr, entry);
451         Vspecifier_type_list = Fcons(symbol, Vspecifier_type_list);
452 }
453
454 static Lisp_Object
455 make_specifier_internal(struct specifier_methods *spec_meths,
456                         size_t data_size, int call_create_meth)
457 {
458         Lisp_Object specifier;
459         Lisp_Specifier *sp = (Lisp_Specifier *)
460             alloc_lcrecord(aligned_sizeof_specifier(data_size),
461                            &lrecord_specifier);
462
463         sp->methods = spec_meths;
464         sp->global_specs = Qnil;
465         sp->device_specs = Qnil;
466         sp->frame_specs = Qnil;
467         sp->window_specs = make_weak_list(WEAK_LIST_KEY_ASSOC);
468         sp->buffer_specs = Qnil;
469         sp->fallback = Qnil;
470         sp->magic_parent = Qnil;
471         sp->caching = 0;
472         sp->next_specifier = Vall_specifiers;
473
474         XSETSPECIFIER(specifier, sp);
475         Vall_specifiers = specifier;
476
477         if (call_create_meth) {
478                 struct gcpro gcpro1;
479                 GCPRO1(specifier);
480                 MAYBE_SPECMETH(XSPECIFIER(specifier), create, (specifier));
481                 UNGCPRO;
482         }
483         return specifier;
484 }
485
486 static Lisp_Object make_specifier(struct specifier_methods *meths)
487 {
488         return make_specifier_internal(meths, meths->extra_data_size, 1);
489 }
490
491 Lisp_Object make_magic_specifier(Lisp_Object type)
492 {
493         /* This function can GC */
494         struct specifier_methods *meths = decode_specifier_type(type, ERROR_ME);
495         Lisp_Object bodily, ghost;
496         struct gcpro gcpro1;
497
498         bodily = make_specifier(meths);
499         GCPRO1(bodily);
500         ghost = make_specifier_internal(meths, 0, 0);
501         UNGCPRO;
502
503         /* Connect guys together */
504         XSPECIFIER(bodily)->magic_parent = Qt;
505         XSPECIFIER(bodily)->fallback = ghost;
506         XSPECIFIER(ghost)->magic_parent = bodily;
507
508         return bodily;
509 }
510
511 DEFUN("make-specifier", Fmake_specifier, 1, 1, 0,       /*
512 Return a new specifier object of type TYPE.
513
514 A specifier is an object that can be used to keep track of a property
515 whose value can be per-buffer, per-window, per-frame, or per-device,
516 and can further be restricted to a particular console-type or
517 device-class.  Specifiers are used, for example, for the various
518 built-in properties of a face; this allows a face to have different
519 values in different frames, buffers, etc.
520
521 When speaking of the value of a specifier, it is important to
522 distinguish between the *setting* of a specifier, called an
523 \"instantiator\", and the *actual value*, called an \"instance\".  You
524 put various possible instantiators (i.e. settings) into a specifier
525 and associate them with particular locales (buffer, window, frame,
526 device, global), and then the instance (i.e. actual value) is
527 retrieved in a specific domain (window, frame, device) by looking
528 through the possible instantiators (i.e. settings).  This process is
529 called \"instantiation\".
530
531 To put settings into a specifier, use `set-specifier', or the
532 lower-level functions `add-spec-to-specifier' and
533 `add-spec-list-to-specifier'.  You can also temporarily bind a setting
534 to a specifier using `let-specifier'.  To retrieve settings, use
535 `specifier-specs', or its lower-level counterpart
536 `specifier-spec-list'.  To determine the actual value, use
537 `specifier-instance'.
538
539 For more information, see `set-specifier', `specifier-instance',
540 `specifier-specs', and `add-spec-to-specifier'; or, for a detailed
541 description of specifiers, including how exactly the instantiation
542 process works, see the chapter on specifiers in the SXEmacs Lisp
543 Reference Manual.
544
545 TYPE specifies the particular type of specifier, and should be one of
546 the symbols 'generic, 'integer, 'natnum, 'boolean, 'color, 'font,
547 'image, 'face-boolean, 'display-table, 'gutter, 'gutter-size,
548 'gutter-visible or 'toolbar.
549
550 For more information on particular types of specifiers, see the