Matt Tucker <tuck(a)whistlingfish.net> writes:
I would content that "It's always worked this way" and
"This is the
correct behavior" are two totally different statements. Why should
anyone have to muck around with a completely different facility for
only the _first_ frame? I would say this is a bug. Even if it's a
very complex bug that would be extremely difficult to resolve, that
doesn't change the fact that it's a bug.
I wrote/ported the code, and I agree that it's a bug. I'll explain
the background story first, and then give some ideas about the
possible remedies.
The background story:
XEmacs has specifiers, which are in spirit similar to how Custom
specifies faces. Ideally, a `defface' statement would be converted
into a fairly complex and equivalent specifier specification. At
first I tried to implement it that way, but that didn't work because
the specifiers missed some features.
This was some time ago, so I might not be remembering every detail.
But from what I do remember, the biggest problem was that it was
impossible to express the `background' matching requirement using a
specifier tag. `define-specifier-tag' didn't work because the custom
tags accept a DEVICE argument, whereas we needed a FRAME argument,
because you can have two frames with different backgrounds.
(In fact, background "lightness" should be defined on window level
with fallback to frame and device, but Custom insisted on dealing with
frames, so it was simpler to think in terms of frames.)
A possible course of action was to extend `define-specifier-tag' so
that you tell it that you wanted your tag frame-specific, not
device-specific. Something like
(define-specifier-tag 'background-lightness
(lambda (frame)
... blah blah ...)
:scope 'frame ; or even :scope 'window
)
But I never did that, for reasons I don't quite remember. It may have
had to do with the specifier cache getting in the way; the specifiers
were never meant to be that "dynamic". (For example, changing the
background of the `default' face should cause the background-lightness
thing to be recalculated.) But it may also have been the case that I
found the specifier code too complex at the time.
Then there were some weird issues with fonts. Font modifiers such as
`bold' and `italic' are even now almost useless, especially with Mule.
I'm pretty sure they wouldn't fit in the specifier model.
The current implementation:
So when I was unable to make it work the "right" way, I punted and
implemented things similar to how Per's original code did it: by
providing a function that runs whenever a frame is created, and that
inspects the frame's properties and modifies all the faces to match
custom's specifications *for that frame*. In case you're curious, the
function is `custom-initialize-frame', defined in `faces.el', and
called directly from Fmake_frame.
That doesn't work for the first frame for the obvious reason that it
is created before `.emacs' is run. I didn't have an obvious solution
to the problem, so I postponed it until later. Which is never a smart
thing to do, as proven by the fact that the problem is still there.
Possible solutions:
IMHO the simplest immediate solution would be to call the appropriate
Custom calls from `frame-notice-user-settings'. This might be
trickier than it initially seems because of all the hair that takes
place in that function, and because of how complex the face stuff is,
and how complex the Custom face stuff is, and the impedance mismatch
between XEmacs face concepts and Custom face concepts. To quote Ben's
comment in frame-notice-user-settings, "this [...] stuff is a load of
trash, and so is this function we're in." But all in all, it
shouldn't be too hard.
A much nicer solution would be for someone with the time and the
inclination to make defface translate into a proper specifier
specification. It would be an interesting project, and the reward of
deleting `custom-initialize-frame' from the face of the earth would
more than make up for all the hours of effort. :-)