Merge remote-tracking branch 'origin/master' into for-steve
[sxemacs] / src / objects.c
1 /* Generic Objects and Functions.
2    Copyright (C) 1995 Free Software Foundation, Inc.
3    Copyright (C) 1995 Board of Trustees, University of Illinois.
4    Copyright (C) 1995, 1996 Ben Wing.
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 #include <config.h>
25 #include "lisp.h"
26
27 #include "ui/device.h"
28 #include "elhash.h"
29 #include "ui/faces.h"
30 #include "ui/frame.h"
31 #include "ui/objects.h"
32 #include "specifier.h"
33 #include "ui/window.h"
34
35 /* Objects that are substituted when an instantiation fails.
36    If we leave in the Qunbound value, we will probably get crashes. */
37 Lisp_Object Vthe_null_color_instance, Vthe_null_font_instance;
38
39 /* Authors: Ben Wing, Chuck Thompson */
40
41 void finalose(void *ptr)
42 {
43         Lisp_Object obj;
44         XSETOBJ(obj, ptr);
45
46         signal_simple_error
47             ("Can't dump an emacs containing window system objects", obj);
48 }
49 \f
50 /****************************************************************************
51  *                       Color-Instance Object                              *
52  ****************************************************************************/
53
54 Lisp_Object Qcolor_instancep;
55
56 static Lisp_Object mark_color_instance(Lisp_Object obj)
57 {
58         Lisp_Color_Instance *c = XCOLOR_INSTANCE(obj);
59         mark_object(c->name);
60         if (!NILP(c->device))   /* Vthe_null_color_instance */
61                 MAYBE_DEVMETH(XDEVICE(c->device), mark_color_instance, (c));
62
63         return c->device;
64 }
65
66 static void
67 print_color_instance(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
68 {
69         Lisp_Color_Instance *c = XCOLOR_INSTANCE(obj);
70         if (print_readably)
71                 error("printing unreadable object #<color-instance 0x%x>",
72                       c->header.uid);
73         write_c_string("#<color-instance ", printcharfun);
74         print_internal(c->name, printcharfun, 0);
75         write_c_string(" on ", printcharfun);
76         print_internal(c->device, printcharfun, 0);
77         if (!NILP(c->device))   /* Vthe_null_color_instance */
78                 MAYBE_DEVMETH(XDEVICE(c->device), print_color_instance,
79                               (c, printcharfun, escapeflag));
80         write_fmt_str(printcharfun, " 0x%x>", c->header.uid);
81 }
82
83 static void finalize_color_instance(void *header, int for_disksave)
84 {
85         Lisp_Color_Instance *c = (Lisp_Color_Instance *) header;
86
87         if (!NILP(c->device)) {
88                 if (for_disksave)
89                         finalose(c);
90                 MAYBE_DEVMETH(XDEVICE(c->device), finalize_color_instance, (c));
91         }
92 }
93
94 static int color_instance_equal(Lisp_Object obj1, Lisp_Object obj2, int depth)
95 {
96         Lisp_Color_Instance *c1 = XCOLOR_INSTANCE(obj1);
97         Lisp_Color_Instance *c2 = XCOLOR_INSTANCE(obj2);
98
99         return (c1 == c2) ||
100             (EQ(c1->device, c2->device) &&
101              DEVICEP(c1->device) &&
102              HAS_DEVMETH_P(XDEVICE(c1->device), color_instance_equal) &&
103              DEVMETH(XDEVICE(c1->device), color_instance_equal,
104                      (c1, c2, depth)));
105 }
106
107 static unsigned long color_instance_hash(Lisp_Object obj, int depth)
108 {
109         Lisp_Color_Instance *c = XCOLOR_INSTANCE(obj);
110         struct device *d = DEVICEP(c->device) ? XDEVICE(c->device) : 0;
111
112         return HASH2((unsigned long)d, !d ? LISP_HASH(obj)
113                      : DEVMETH_OR_GIVEN(d, color_instance_hash, (c, depth),
114                                         LISP_HASH(obj)));
115 }
116
117 DEFINE_LRECORD_IMPLEMENTATION("color-instance", color_instance,
118                               mark_color_instance, print_color_instance,
119                               finalize_color_instance, color_instance_equal,
120                               color_instance_hash, 0, Lisp_Color_Instance);
121 \f
122 DEFUN("make-color-instance", Fmake_color_instance, 1, 3, 0,     /*
123 Return a new `color-instance' object named NAME (a string).
124
125 Optional argument DEVICE specifies the device this object applies to
126 and defaults to the selected device.
127
128 An error is signaled if the color is unknown or cannot be allocated;
129 however, if optional argument NOERROR is non-nil, nil is simply
130 returned in this case. (And if NOERROR is other than t, a warning may
131 be issued.)
132
133 The returned object is a normal, first-class lisp object.  The way you
134 `deallocate' the color is the way you deallocate any other lisp object:
135 you drop all pointers to it and allow it to be garbage collected.  When
136 these objects are GCed, the underlying window-system data (e.g. X object)
137 is deallocated as well.
138 */
139       (name, device, noerror))
140 {
141         Lisp_Color_Instance *c;
142         Lisp_Object val = Qnil;
143         Lisp_Object dev = Qnil;
144         int retval = 0;
145         int count = specpdl_depth();
146         struct gcpro gcpro1, gcpro2;
147
148         CHECK_STRING(name);
149         XSETDEVICE(dev, decode_device(device));
150
151         c = alloc_lcrecord_type(Lisp_Color_Instance, &lrecord_color_instance);
152         c->name = name;
153         c->device = dev;
154         c->data = 0;
155
156         XSETCOLOR_INSTANCE(val, c);
157         GCPRO2(val,dev);
158         retval = MAYBE_INT_DEVMETH(XDEVICE(dev), initialize_color_instance,
159                                    (c, name, dev,
160                                     decode_error_behavior_flag(noerror)));
161         if (!retval) {
162           unbind_to(count, Qnil);
163           UNGCPRO;
164           return Qnil;
165         }
166
167         XSETCOLOR_INSTANCE(val, c);
168         unbind_to(count, Qnil);
169         UNGCPRO;
170         return val;
171 }
172
173 DEFUN("color-instance-p", Fcolor_instance_p, 1, 1, 0,   /*
174 Return non-nil if OBJECT is a color instance.
175 */
176       (object))
177 {
178         return COLOR_INSTANCEP(object) ? Qt : Qnil;
179 }
180
181 DEFUN("color-instance-name", Fcolor_instance_name, 1, 1, 0,     /*
182 Return the name used to allocate COLOR-INSTANCE.
183 */
184       (color_instance))
185 {
186         CHECK_COLOR_INSTANCE(color_instance);
187         return XCOLOR_INSTANCE(color_instance)->name;
188 }
189
190 DEFUN("color-instance-rgb-components", Fcolor_instance_rgb_components, 1, 1, 0, /*
191 Return a three element list containing the red, green, and blue
192 color components of COLOR-INSTANCE, or nil if unknown.
193 Component values range from 0 to 65535.
194 */
195       (color_instance))
196 {
197         Lisp_Color_Instance *c;
198
199         CHECK_COLOR_INSTANCE(color_instance);
200         c = XCOLOR_INSTANCE(color_instance);
201
202         if (NILP(c->device))
203                 return Qnil;
204
205         return MAYBE_LISP_DEVMETH(XDEVICE(c->device),
206                                   color_instance_rgb_components, (c));
207 }
208
209 DEFUN("valid-color-name-p", Fvalid_color_name_p, 1, 2, 0,       /*
210 Return true if COLOR names a valid color for the current device.
211
212 Valid color names for X are listed in the file /usr/lib/X11/rgb.txt, or
213 whatever the equivalent is on your system.
214
215 Valid color names for TTY are those which have an ISO 6429 (ANSI) sequence.
216 In addition to being a color this may be one of a number of attributes
217 such as `blink'.
218 */
219       (color, device))
220 {
221         struct device *d = decode_device(device);
222
223         CHECK_STRING(color);
224         return MAYBE_INT_DEVMETH(d, valid_color_name_p, (d, color)) ? Qt : Qnil;
225 }
226 \f
227 /***************************************************************************
228  *                       Font-Instance Object                              *
229  ***************************************************************************/
230
231 Lisp_Object Qfont_instancep;
232
233 static Lisp_Object font_instance_truename_internal(Lisp_Object xfont,
234                                                    Error_behavior errb);
235
236 static Lisp_Object mark_font_instance(Lisp_Object obj)
237 {
238         Lisp_Font_Instance *f = XFONT_INSTANCE(obj);
239
240         mark_object(f->name);
241         if (!NILP(f->device))   /* Vthe_null_font_instance */
242                 MAYBE_DEVMETH(XDEVICE(f->device), mark_font_instance, (f));
243
244         return f->device;
245 }
246
247 static void
248 print_font_instance(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
249 {
250         Lisp_Font_Instance *f = XFONT_INSTANCE(obj);
251         if (print_readably)
252                 error("printing unreadable object #<font-instance 0x%x>",
253                       f->header.uid);
254         write_c_string("#<font-instance ", printcharfun);
255         print_internal(f->name, printcharfun, 1);
256         write_c_string(" on ", printcharfun);
257         print_internal(f->device, printcharfun, 0);
258         if (!NILP(f->device))
259                 MAYBE_DEVMETH(XDEVICE(f->device), print_font_instance,
260                               (f, printcharfun, escapeflag));
261         write_fmt_str(printcharfun, " 0x%x>", f->header.uid);
262 }
263
264 static void finalize_font_instance(void *header, int for_disksave)
265 {
266         Lisp_Font_Instance *f = (Lisp_Font_Instance *) header;
267
268         if (!NILP(f->device)) {
269                 if (for_disksave)
270                         finalose(f);
271                 MAYBE_DEVMETH(XDEVICE(f->device), finalize_font_instance, (f));
272         }
273 }
274
275 /* Fonts are equal if they resolve to the same name.
276    Since we call `font-truename' to do this, and since font-truename is lazy,
277    this means the `equal' could cause XListFonts to be run the first time.
278  */
279 static int font_instance_equal(Lisp_Object obj1, Lisp_Object obj2, int depth)
280 {
281         /* #### should this be moved into a device method? */
282         return
283             internal_equal(font_instance_truename_internal(obj1, ERROR_ME_NOT),
284                            font_instance_truename_internal(obj2, ERROR_ME_NOT),
285                            depth + 1);
286 }
287
288 static unsigned long font_instance_hash(Lisp_Object obj, int depth)
289 {
290         return internal_hash(font_instance_truename_internal(obj, ERROR_ME_NOT),
291                              depth + 1);
292 }
293
294 DEFINE_LRECORD_IMPLEMENTATION("font-instance", font_instance,
295                               mark_font_instance, print_font_instance,
296                               finalize_font_instance, font_instance_equal,
297                               font_instance_hash, 0, Lisp_Font_Instance);
298 \f
299 DEFUN("make-font-instance", Fmake_font_instance, 1, 3, 0,       /*
300 Return a new `font-instance' object named NAME.
301 DEVICE specifies the device this object applies to and defaults to the
302 selected device.  An error is signalled if the font is unknown or cannot
303 be allocated; however, if NOERROR is non-nil, nil is simply returned in
304 this case.
305
306 The returned object is a normal, first-class lisp object.  The way you
307 `deallocate' the font is the way you deallocate any other lisp object:
308 you drop all pointers to it and allow it to be garbage collected.  When
309 these objects are GCed, the underlying X data is deallocated as well.
310 */
311       (name, device, noerror))
312 {
313         Lisp_Font_Instance *f;
314         Lisp_Object val;
315         int retval = 0;
316         Error_behavior errb = decode_error_behavior_flag(noerror);
317
318         if (ERRB_EQ(errb, ERROR_ME))
319                 CHECK_STRING(name);
320         else if (!STRINGP(name))
321                 return Qnil;
322
323         XSETDEVICE(device, decode_device(device));
324
325         f = alloc_lcrecord_type(Lisp_Font_Instance, &lrecord_font_instance);
326         f->name = name;
327         f->device = device;
328
329         f->data = 0;
330
331         /* Stick some default values here ... */
332         f->ascent = f->height = 1;
333         f->descent = 0;
334         f->width = 1;
335         f->proportional_p = 0;
336
337         retval = MAYBE_INT_DEVMETH(XDEVICE(device), initialize_font_instance,
338                                    (f, name, device, errb));
339
340         if (!retval)
341                 return Qnil;
342
343         XSETFONT_INSTANCE(val, f);
344         return val;
345 }
346
347 DEFUN("font-instance-p", Ffont_instance_p, 1, 1, 0,     /*
348 Return non-nil if OBJECT is a font instance.
349 */
350       (object))
351 {
352         return FONT_INSTANCEP(object) ? Qt : Qnil;
353 }
354
355 DEFUN("font-instance-name", Ffont_instance_name, 1, 1, 0,       /*
356 Return the name used to allocate FONT-INSTANCE.
357 */
358       (font_instance))
359 {
360         CHECK_FONT_INSTANCE(font_instance);
361         return XFONT_INSTANCE(font_instance)->name;
362 }
363
364 DEFUN("font-instance-ascent", Ffont_instance_ascent, 1, 1, 0,   /*
365 Return the ascent in pixels of FONT-INSTANCE.
366 The returned value is the maximum ascent for all characters in the font,
367 where a character's ascent is the number of pixels above (and including)
368 the baseline.
369 */
370       (font_instance))
371 {
372         CHECK_FONT_INSTANCE(font_instance);
373         return make_int(XFONT_INSTANCE(font_instance)->ascent);
374 }
375
376 DEFUN("font-instance-descent", Ffont_instance_descent, 1, 1, 0, /*
377 Return the descent in pixels of FONT-INSTANCE.
378 The returned value is the maximum descent for all characters in the font,
379 where a character's descent is the number of pixels below the baseline.
380 \(Many characters to do not have any descent.  Typical characters with a
381 descent are lowercase p and lowercase g.)
382 */
383       (font_instance))
384 {
385         CHECK_FONT_INSTANCE(font_instance);
386         return make_int(XFONT_INSTANCE(font_instance)->descent);
387 }
388
389 DEFUN("font-instance-width", Ffont_instance_width, 1, 1, 0,     /*
390 Return the width in pixels of FONT-INSTANCE.
391 The returned value is the average width for all characters in the font.
392 */
393       (font_instance))
394 {
395         CHECK_FONT_INSTANCE(font_instance);
396         return make_int(XFONT_INSTANCE(font_instance)->width);
397 }
398
399 DEFUN("font-instance-proportional-p", Ffont_instance_proportional_p, 1, 1, 0,   /*
400 Return whether FONT-INSTANCE is proportional.
401 This means that different characters in the font have different widths.
402 */
403       (font_instance))
404 {
405         CHECK_FONT_INSTANCE(font_instance);
406         return XFONT_INSTANCE(font_instance)->proportional_p ? Qt : Qnil;
407 }
408
409 static Lisp_Object
410 font_instance_truename_internal(Lisp_Object font_instance, Error_behavior errb)
411 {
412         Lisp_Font_Instance *f = XFONT_INSTANCE(font_instance);
413
414         if (NILP(f->device)) {
415                 maybe_signal_simple_error("Couldn't determine font truename",
416                                           font_instance, Qfont, errb);
417                 return Qnil;
418         }
419
420         return DEVMETH_OR_GIVEN(XDEVICE(f->device),
421                                 font_instance_truename, (f, errb), f->name);
422 }
423
424 DEFUN("font-instance-truename", Ffont_instance_truename, 1, 1, 0,       /*
425 Return the canonical name of FONT-INSTANCE.
426 Font names are patterns which may match any number of fonts, of which
427 the first found is used.  This returns an unambiguous name for that font
428 \(but not necessarily its only unambiguous name).
429 */
430       (font_instance))
431 {
432         CHECK_FONT_INSTANCE(font_instance);
433         return font_instance_truename_internal(font_instance, ERROR_ME);
434 }
435
436 DEFUN("font-instance-properties", Ffont_instance_properties, 1, 1, 0,   /*
437 Return the properties (an alist or nil) of FONT-INSTANCE.
438 */
439       (font_instance))
440 {
441         Lisp_Font_Instance *f;
442
443         CHECK_FONT_INSTANCE(font_instance);
444         f = XFONT_INSTANCE(font_instance);
445
446         if (NILP(f->device))
447                 return Qnil;
448
449         return MAYBE_LISP_DEVMETH(XDEVICE(f->device),
450                                   font_instance_properties, (f));
451 }
452
453 DEFUN("list-fonts", Flist_fonts, 1, 2, 0,       /*
454 Return a list of font names matching the given pattern.
455 DEVICE specifies which device to search for names, and defaults to the
456 currently selected device.
457 */
458       (pattern, device))
459 {
460         CHECK_STRING(pattern);
461         XSETDEVICE(device, decode_device(device));
462
463         return MAYBE_LISP_DEVMETH(XDEVICE(device), list_fonts,
464                                   (pattern, device));
465 }
466 \f
467 /****************************************************************************
468  Color Object
469  ***************************************************************************/
470 DEFINE_SPECIFIER_TYPE(color);
471 /* Qcolor defined in general.c */
472
473 static void color_create(Lisp_Object obj)
474 {
475         Lisp_Specifier *color = XCOLOR_SPECIFIER(obj);
476
477         COLOR_SPECIFIER_FACE(color) = Qnil;
478         COLOR_SPECIFIER_FACE_PROPERTY(color) = Qnil;
479 }
480
481 static void color_mark(Lisp_Object obj)
482 {
483         Lisp_Specifier *color = XCOLOR_SPECIFIER(obj);
484
485         mark_object(COLOR_SPECIFIER_FACE(color));
486         mark_object(COLOR_SPECIFIER_FACE_PROPERTY(color));
487 }
488
489 /* No equal or hash methods; ignore the face the color is based off
490    of for `equal' */
491
492 static Lisp_Object
493 color_instantiate(Lisp_Object specifier, Lisp_Object matchspec,
494                   Lisp_Object domain, Lisp_Object instantiator,
495                   Lisp_Object depth)
496 {
497         /* When called, we're inside of call_with_suspended_errors(),
498            so we can freely error. */
499         Lisp_Object device = DOMAIN_DEVICE(domain);
500         struct device *d = XDEVICE(device);
501
502         if (COLOR_INSTANCEP(instantiator)) {
503                 /* If we are on the same device then we're done.  Otherwise change
504                    the instantiator to the name used to generate the pixel and let the
505                    STRINGP case deal with it. */
506                 if (NILP(device)        /* Vthe_null_color_instance */
507                     ||EQ(device, XCOLOR_INSTANCE(instantiator)->device))
508                         return instantiator;
509                 else
510                         instantiator = Fcolor_instance_name(instantiator);
511         }
512
513         if (STRINGP(instantiator)) {
514                 /* First, look to see if we can retrieve a cached value. */
515                 Lisp_Object instance =
516                     Fgethash(instantiator, d->color_instance_cache, Qunbound);
517                 /* Otherwise, make a new one. */
518                 if (UNBOUNDP(instance)) {
519                         /* make sure we cache the failures, too. */
520                         instance =
521                             Fmake_color_instance(instantiator, device, Qt);
522                         Fputhash(instantiator, instance,
523                                  d->color_instance_cache);
524                 }
525
526                 return NILP(instance) ? Qunbound : instance;
527         } else if (VECTORP(instantiator)) {
528                 switch (XVECTOR_LENGTH(instantiator)) {
529                 case 0:
530                         if (DEVICE_TTY_P(d))
531                                 return Vthe_null_color_instance;
532                         else
533                                 signal_simple_error
534                                     ("Color instantiator [] only valid on TTY's",
535                                      device);
536
537                 case 1:
538                         if (NILP
539                             (COLOR_SPECIFIER_FACE(XCOLOR_SPECIFIER(specifier))))
540                                 signal_simple_error
541                                     ("Color specifier not attached to a face",
542                                      instantiator);
543                         return (FACE_PROPERTY_INSTANCE_1
544                                 (Fget_face(XVECTOR_DATA(instantiator)[0]),
545                                  COLOR_SPECIFIER_FACE_PROPERTY(XCOLOR_SPECIFIER
546                                                                (specifier)),
547                                  domain, ERROR_ME, 0, depth));
548
549                 case 2:
550                         return (FACE_PROPERTY_INSTANCE_1
551                                 (Fget_face(XVECTOR_DATA(instantiator)[0]),
552                                  XVECTOR_DATA(instantiator)[1], domain,
553                                  ERROR_ME, 0, depth));
554
555                 default:
556                         abort();
557                 }
558         } else if (NILP(instantiator)) {
559                 if (DEVICE_TTY_P(d))
560                         return Vthe_null_color_instance;
561                 else
562                         signal_simple_error
563                             ("Color instantiator [] only valid on TTY's",
564                              device);
565         } else
566                 abort();        /* The spec validation routines are screwed up. */
567
568         return Qunbound;
569 }
570
571 static void color_validate(Lisp_Object instantiator)
572 {
573         if (COLOR_INSTANCEP(instantiator) || STRINGP(instantiator))
574                 return;
575         if (VECTORP(instantiator)) {
576                 if (XVECTOR_LENGTH(instantiator) > 2)
577                         signal_simple_error
578                             ("Inheritance vector must be of size 0 - 2",
579                              instantiator);
580                 else if (XVECTOR_LENGTH(instantiator) > 0) {
581                         Lisp_Object face = XVECTOR_DATA(instantiator)[0];
582
583                         Fget_face(face);
584                         if (XVECTOR_LENGTH(instantiator) == 2) {
585                                 Lisp_Object field =
586                                     XVECTOR_DATA(instantiator)[1];
587                                 if (!EQ(field, Qforeground)
588                                     && !EQ(field, Qbackground))
589                                         signal_simple_error
590                                             ("Inheritance field must be `foreground' or `background'",
591                                              field);
592                         }
593                 }
594         } else
595                 signal_simple_error("Invalid color instantiator", instantiator);
596 }
597
598 static void color_after_change(Lisp_Object specifier, Lisp_Object locale)
599 {
600         Lisp_Object face = COLOR_SPECIFIER_FACE(XCOLOR_SPECIFIER(specifier));
601         Lisp_Object property =
602             COLOR_SPECIFIER_FACE_PROPERTY(XCOLOR_SPECIFIER(specifier));
603         if (!NILP(face)) {
604                 face_property_was_changed(face, property, locale);
605                 if (BUFFERP(locale))
606                         XBUFFER(locale)->buffer_local_face_property = 1;
607         }
608 }
609
610 void
611 set_color_attached_to(Lisp_Object obj, Lisp_Object face, Lisp_Object property)
612 {
613         Lisp_Specifier *color = XCOLOR_SPECIFIER(obj);
614
615         COLOR_SPECIFIER_FACE(color) = face;
616         COLOR_SPECIFIER_FACE_PROPERTY(color) = property;
617 }
618
619 DEFUN("color-specifier-p", Fcolor_specifier_p, 1, 1, 0, /*
620 Return t if OBJECT is a color specifier.
621
622 See `make-color-specifier' for a description of possible color instantiators.
623 */
624       (object))
625 {
626         return COLOR_SPECIFIERP(object) ? Qt : Qnil;
627 }
628 \f
629 /****************************************************************************
630  Font Object
631  ***************************************************************************/
632 DEFINE_SPECIFIER_TYPE(font);
633 /* Qfont defined in general.c */
634
635 static void font_create(Lisp_Object obj)
636 {
637         Lisp_Specifier *font = XFONT_SPECIFIER(obj);
638
639         FONT_SPECIFIER_FACE(font) = Qnil;
640         FONT_SPECIFIER_FACE_PROPERTY(font) = Qnil;
641 }
642
643 static void font_mark(Lisp_Object obj)
644 {
645         Lisp_Specifier *font = XFONT_SPECIFIER(obj);
646
647         mark_object(FONT_SPECIFIER_FACE(font));
648         mark_object(FONT_SPECIFIER_FACE_PROPERTY(font));
649 }
650
651 /* No equal or hash methods; ignore the face the font is based off
652    of for `equal' */
653
654 #ifdef MULE
655
656 int
657 font_spec_matches_charset(struct device *d, Lisp_Object charset,
658                           const Bufbyte * nonreloc, Lisp_Object reloc,
659                           Bytecount offset, Bytecount length)
660 {
661         return DEVMETH_OR_GIVEN(d, font_spec_matches_charset,
662                                 (d, charset, nonreloc, reloc, offset, length),
663                                 1);
664 }
665
666 static void font_validate_matchspec(Lisp_Object matchspec)
667 {
668         Fget_charset(matchspec);
669 }
670
671 #endif                          /* MULE */
672
673 static Lisp_Object
674 font_instantiate(Lisp_Object specifier, Lisp_Object matchspec,
675                  Lisp_Object domain, Lisp_Object instantiator,
676                  Lisp_Object depth)
677 {
678         /* When called, we're inside of call_with_suspended_errors(),
679            so we can freely error. */
680         Lisp_Object device = DOMAIN_DEVICE(domain);
681         struct device *d = XDEVICE(device);
682         Lisp_Object instance;
683
684 #ifdef MULE
685         if (!UNBOUNDP(matchspec))
686                 matchspec = Fget_charset(matchspec);
687 #endif
688
689         if (FONT_INSTANCEP(instantiator)) {
690                 if (NILP(device)
691                     || EQ(device, XFONT_INSTANCE(instantiator)->device)) {
692 #ifdef MULE
693                         if (font_spec_matches_charset(d, matchspec, 0,
694                                                       Ffont_instance_truename
695                                                       (instantiator), 0, -1))
696                                 return instantiator;
697 #else
698                         return instantiator;
699 #endif
700                 }
701                 instantiator = Ffont_instance_name(instantiator);
702         }
703
704         if (STRINGP(instantiator)) {
705 #ifdef MULE
706                 if (!UNBOUNDP(matchspec)) {
707                         /* The instantiator is a font spec that could match many
708                            different fonts.  We need to find one of those fonts
709                            whose registry matches the registry of the charset in
710                            MATCHSPEC.  This is potentially a very slow operation,
711                            as it involves doing an XListFonts() or equivalent to
712                            iterate over all possible fonts, and a regexp match
713                            on each one.  So we cache the results. */
714                         Lisp_Object matching_font = Qunbound;
715                         Lisp_Object hash_table =
716                             Fgethash(matchspec, d->charset_font_cache,
717                                      Qunbound);
718                         if (UNBOUNDP(hash_table)) {
719                                 /* need to make a sub hash table. */
720                                 hash_table =
721                                     make_lisp_hash_table(20,
722                                                          HASH_TABLE_KEY_WEAK,
723                                                          HASH_TABLE_EQUAL);
724                                 Fputhash(matchspec, hash_table,
725                                          d->charset_font_cache);
726                         } else
727                                 matching_font =
728                                     Fgethash(instantiator, hash_table,
729                                              Qunbound);
730
731                         if (UNBOUNDP(matching_font)) {
732                                 /* make sure we cache the failures, too. */
733                                 matching_font =
734                                     DEVMETH_OR_GIVEN(d, find_charset_font,
735                                                      (device, instantiator,
736                                                       matchspec), instantiator);
737                                 Fputhash(instantiator, matching_font,
738                                          hash_table);
739                         }
740                         if (NILP(matching_font))
741                                 return Qunbound;
742                         instantiator = matching_font;
743                 }
744 #endif                          /* MULE */
745
746                 /* First, look to see if we can retrieve a cached value. */
747                 instance =
748                     Fgethash(instantiator, d->font_instance_cache, Qunbound);
749                 /* Otherwise, make a new one. */
750                 if (UNBOUNDP(instance)) {
751                         /* make sure we cache the failures, too. */
752                         instance =
753                             Fmake_font_instance(instantiator, device, Qt);
754                         Fputhash(instantiator, instance,
755                                  d->font_instance_cache);
756                 }
757
758                 return NILP(instance) ? Qunbound : instance;
759         } else if (VECTORP(instantiator)) {
760                 assert(XVECTOR_LENGTH(instantiator) == 1);
761                 return (face_property_matching_instance
762                         (Fget_face(XVECTOR_DATA(instantiator)[0]), Qfont,
763                          matchspec, domain, ERROR_ME, 0, depth));
764         } else if (NILP(instantiator))
765                 return Qunbound;
766         else
767                 abort();        /* Eh? */
768
769         return Qunbound;
770 }
771
772 static void font_validate(Lisp_Object instantiator)
773 {
774         if (FONT_INSTANCEP(instantiator) || STRINGP(instantiator))
775                 return;
776         if (VECTORP(instantiator)) {
777                 if (XVECTOR_LENGTH(instantiator) != 1) {
778                         signal_simple_error
779                             ("Vector length must be one for font inheritance",
780                              instantiator);
781                 }
782                 Fget_face(XVECTOR_DATA(instantiator)[0]);
783         } else
784                 signal_simple_error("Must be string, vector, or font-instance",
785                                     instantiator);
786 }
787
788 static void font_after_change(Lisp_Object specifier, Lisp_Object locale)
789 {
790         Lisp_Object face = FONT_SPECIFIER_FACE(XFONT_SPECIFIER(specifier));
791         Lisp_Object property =
792             FONT_SPECIFIER_FACE_PROPERTY(XFONT_SPECIFIER(specifier));
793         if (!NILP(face)) {
794                 face_property_was_changed(face, property, locale);
795                 if (BUFFERP(locale))
796                         XBUFFER(locale)->buffer_local_face_property = 1;
797         }
798 }
799
800 void
801 set_font_attached_to(Lisp_Object obj, Lisp_Object face, Lisp_Object property)
802 {
803         Lisp_Specifier *font = XFONT_SPECIFIER(obj);
804
805         FONT_SPECIFIER_FACE(font) = face;
806         FONT_SPECIFIER_FACE_PROPERTY(font) = property;
807 }
808
809 DEFUN("font-specifier-p", Ffont_specifier_p, 1, 1, 0,   /*
810 Return non-nil if OBJECT is a font specifier.
811
812 See `make-font-specifier' for a description of possible font instantiators.
813 */
814       (object))
815 {
816         return FONT_SPECIFIERP(object) ? Qt : Qnil;
817 }
818 \f
819 /*****************************************************************************
820  Face Boolean Object
821  ****************************************************************************/
822 DEFINE_SPECIFIER_TYPE(face_boolean);
823 Lisp_Object Qface_boolean;
824
825 static void face_boolean_create(Lisp_Object obj)
826 {
827         Lisp_Specifier *face_boolean = XFACE_BOOLEAN_SPECIFIER(obj);
828
829         FACE_BOOLEAN_SPECIFIER_FACE(face_boolean) = Qnil;
830         FACE_BOOLEAN_SPECIFIER_FACE_PROPERTY(face_boolean) = Qnil;
831 }
832
833 static void face_boolean_mark(Lisp_Object obj)
834 {
835         Lisp_Specifier *face_boolean = XFACE_BOOLEAN_SPECIFIER(obj);
836
837         mark_object(FACE_BOOLEAN_SPECIFIER_FACE(face_boolean));
838         mark_object(FACE_BOOLEAN_SPECIFIER_FACE_PROPERTY(face_boolean));
839 }
840
841 /* No equal or hash methods; ignore the face the face-boolean is based off
842    of for `equal' */
843
844 static Lisp_Object
845 face_boolean_instantiate(Lisp_Object specifier, Lisp_Object matchspec,
846                          Lisp_Object domain, Lisp_Object instantiator,
847                          Lisp_Object depth)
848 {
849         /* When called, we're inside of call_with_suspended_errors(),
850            so we can freely error. */
851         if (NILP(instantiator) || EQ(instantiator, Qt))
852                 return instantiator;
853         else if (VECTORP(instantiator)) {
854                 Lisp_Object retval;
855                 Lisp_Object prop;
856                 int instantiator_len = XVECTOR_LENGTH(instantiator);
857
858                 assert(instantiator_len >= 1 && instantiator_len <= 3);
859                 if (instantiator_len > 1)
860                         prop = XVECTOR_DATA(instantiator)[1];
861                 else {
862                         if (NILP(FACE_BOOLEAN_SPECIFIER_FACE
863                                  (XFACE_BOOLEAN_SPECIFIER(specifier))))
864                                 signal_simple_error
865                                     ("Face-boolean specifier not attached to a face",
866                                      instantiator);
867                         prop =
868                             FACE_BOOLEAN_SPECIFIER_FACE_PROPERTY
869                             (XFACE_BOOLEAN_SPECIFIER(specifier));
870                 }
871
872                 retval = (FACE_PROPERTY_INSTANCE_1
873                           (Fget_face(XVECTOR_DATA(instantiator)[0]),
874                            prop, domain, ERROR_ME, 0, depth));
875
876                 if (instantiator_len == 3
877                     && !NILP(XVECTOR_DATA(instantiator)[2]))
878                         retval = NILP(retval) ? Qt : Qnil;
879
880                 return retval;
881         } else
882                 abort();        /* Eh? */
883
884         return Qunbound;
885 }
886
887 static void face_boolean_validate(Lisp_Object instantiator)
888 {
889         if (NILP(instantiator) || EQ(instantiator, Qt))
890                 return;
891         else if (VECTORP(instantiator) &&
892                  (XVECTOR_LENGTH(instantiator) >= 1 &&
893                   XVECTOR_LENGTH(instantiator) <= 3)) {
894                 Lisp_Object face = XVECTOR_DATA(instantiator)[0];
895
896                 Fget_face(face);
897
898                 if (XVECTOR_LENGTH(instantiator) > 1) {
899                         Lisp_Object field = XVECTOR_DATA(instantiator)[1];
900                         if (!EQ(field, Qunderline)
901                             && !EQ(field, Qstrikethru)
902                             && !EQ(field, Qhighlight)
903                             && !EQ(field, Qdim)
904                             && !EQ(field, Qblinking)
905                             && !EQ(field, Qreverse))
906                                 signal_simple_error
907                                     ("Invalid face-boolean inheritance field",
908                                      field);
909                 }
910         } else if (VECTORP(instantiator))
911                 signal_simple_error
912                     ("Wrong length for face-boolean inheritance spec",
913                      instantiator);
914         else
915                 signal_simple_error
916                     ("Face-boolean instantiator must be nil, t, or vector",
917                      instantiator);
918 }
919
920 static void face_boolean_after_change(Lisp_Object specifier, Lisp_Object locale)
921 {
922         Lisp_Object face =
923             FACE_BOOLEAN_SPECIFIER_FACE(XFACE_BOOLEAN_SPECIFIER(specifier));
924         Lisp_Object property =
925             FACE_BOOLEAN_SPECIFIER_FACE_PROPERTY(XFACE_BOOLEAN_SPECIFIER
926                                                  (specifier));
927         if (!NILP(face)) {
928                 face_property_was_changed(face, property, locale);
929                 if (BUFFERP(locale))
930                         XBUFFER(locale)->buffer_local_face_property = 1;
931         }
932 }
933
934 void
935 set_face_boolean_attached_to(Lisp_Object obj, Lisp_Object face,
936                              Lisp_Object property)
937 {
938         Lisp_Specifier *face_boolean = XFACE_BOOLEAN_SPECIFIER(obj);
939
940         FACE_BOOLEAN_SPECIFIER_FACE(face_boolean) = face;
941         FACE_BOOLEAN_SPECIFIER_FACE_PROPERTY(face_boolean) = property;
942 }
943
944 DEFUN("face-boolean-specifier-p", Fface_boolean_specifier_p, 1, 1, 0,   /*
945 Return non-nil if OBJECT is a face-boolean specifier.
946
947 See `make-face-boolean-specifier' for a description of possible
948 face-boolean instantiators.
949 */
950       (object))
951 {
952         return FACE_BOOLEAN_SPECIFIERP(object) ? Qt : Qnil;
953 }
954 \f
955 /************************************************************************/
956 /*                            initialization                            */
957 /************************************************************************/
958
959 void syms_of_objects(void)
960 {
961         INIT_LRECORD_IMPLEMENTATION(color_instance);
962         INIT_LRECORD_IMPLEMENTATION(font_instance);
963
964         DEFSUBR(Fcolor_specifier_p);
965         DEFSUBR(Ffont_specifier_p);
966         DEFSUBR(Fface_boolean_specifier_p);
967
968         defsymbol(&Qcolor_instancep, "color-instance-p");
969         DEFSUBR(Fmake_color_instance);
970         DEFSUBR(Fcolor_instance_p);
971         DEFSUBR(Fcolor_instance_name);
972         DEFSUBR(Fcolor_instance_rgb_components);
973         DEFSUBR(Fvalid_color_name_p);
974
975         defsymbol(&Qfont_instancep, "font-instance-p");
976         DEFSUBR(Fmake_font_instance);
977         DEFSUBR(Ffont_instance_p);
978         DEFSUBR(Ffont_instance_name);
979         DEFSUBR(Ffont_instance_ascent);
980         DEFSUBR(Ffont_instance_descent);
981         DEFSUBR(Ffont_instance_width);
982         DEFSUBR(Ffont_instance_proportional_p);
983         DEFSUBR(Ffont_instance_truename);
984         DEFSUBR(Ffont_instance_properties);
985         DEFSUBR(Flist_fonts);
986
987         /* Qcolor, Qfont defined in general.c */
988         defsymbol(&Qface_boolean, "face-boolean");
989 }
990
991 static const struct lrecord_description color_specifier_description[] = {
992         {XD_LISP_OBJECT,
993          specifier_data_offset + offsetof(struct color_specifier, face)},
994         {XD_LISP_OBJECT,
995          specifier_data_offset + offsetof(struct color_specifier,
996                                           face_property)},
997         {XD_END}
998 };
999
1000 static const struct lrecord_description font_specifier_description[] = {
1001         {XD_LISP_OBJECT,
1002          specifier_data_offset + offsetof(struct font_specifier, face)},
1003         {XD_LISP_OBJECT,
1004          specifier_data_offset + offsetof(struct font_specifier,
1005                                           face_property)},
1006         {XD_END}
1007 };
1008
1009 static const struct lrecord_description face_boolean_specifier_description[] = {
1010         {XD_LISP_OBJECT,
1011          specifier_data_offset + offsetof(struct face_boolean_specifier, face)},
1012         {XD_LISP_OBJECT,
1013          specifier_data_offset + offsetof(struct face_boolean_specifier,
1014                                           face_property)},
1015         {XD_END}
1016 };
1017
1018 void specifier_type_create_objects(void)
1019 {
1020         INITIALIZE_SPECIFIER_TYPE_WITH_DATA(color, "color",
1021                                             "color-specifier-p");
1022         INITIALIZE_SPECIFIER_TYPE_WITH_DATA(font, "font", "font-specifier-p");
1023         INITIALIZE_SPECIFIER_TYPE_WITH_DATA(face_boolean, "face-boolean",
1024                                             "face-boolean-specifier-p");
1025
1026         SPECIFIER_HAS_METHOD(color, instantiate);
1027         SPECIFIER_HAS_METHOD(font, instantiate);
1028         SPECIFIER_HAS_METHOD(face_boolean, instantiate);
1029
1030         SPECIFIER_HAS_METHOD(color, validate);
1031         SPECIFIER_HAS_METHOD(font, validate);
1032         SPECIFIER_HAS_METHOD(face_boolean, validate);
1033
1034         SPECIFIER_HAS_METHOD(color, create);
1035         SPECIFIER_HAS_METHOD(font, create);
1036         SPECIFIER_HAS_METHOD(face_boolean, create);
1037
1038         SPECIFIER_HAS_METHOD(color, mark);
1039         SPECIFIER_HAS_METHOD(font, mark);
1040         SPECIFIER_HAS_METHOD(face_boolean, mark);
1041
1042         SPECIFIER_HAS_METHOD(color, after_change);
1043         SPECIFIER_HAS_METHOD(font, after_change);
1044         SPECIFIER_HAS_METHOD(face_boolean, after_change);
1045
1046 #ifdef MULE
1047         SPECIFIER_HAS_METHOD(font, validate_matchspec);
1048 #endif
1049 }
1050
1051 void reinit_specifier_type_create_objects(void)
1052 {
1053         REINITIALIZE_SPECIFIER_TYPE(color);
1054         REINITIALIZE_SPECIFIER_TYPE(font);
1055         REINITIALIZE_SPECIFIER_TYPE(face_boolean);
1056 }
1057
1058 void reinit_vars_of_objects(void)
1059 {
1060         staticpro_nodump(&Vthe_null_color_instance);
1061         {
1062                 Lisp_Color_Instance *c =
1063                     alloc_lcrecord_type(Lisp_Color_Instance,
1064                                         &lrecord_color_instance);
1065                 c->name = Qnil;
1066                 c->device = Qnil;
1067                 c->data = 0;
1068
1069                 XSETCOLOR_INSTANCE(Vthe_null_color_instance, c);
1070         }
1071
1072         staticpro_nodump(&Vthe_null_font_instance);
1073         {
1074                 Lisp_Font_Instance *f =
1075                     alloc_lcrecord_type(Lisp_Font_Instance,
1076                                         &lrecord_font_instance);
1077                 f->name = Qnil;
1078                 f->device = Qnil;
1079                 f->data = 0;
1080
1081                 f->ascent = f->height = 0;
1082                 f->descent = 0;
1083                 f->width = 0;
1084                 f->proportional_p = 0;
1085
1086                 XSETFONT_INSTANCE(Vthe_null_font_instance, f);
1087         }
1088 }
1089
1090 void vars_of_objects(void)
1091 {
1092         reinit_vars_of_objects();
1093 }