Notes on the "silica" windowing layer of CLIM II On a CLX port, a basic application can be created by constructing a sheet hierarchy in disabled state, grafting it (causing at least the top-level sheet to have a mirror created for it), setting suitable window manager properties on the mirror, and then enabling the toplevel sheet (corresponding to a MapWindow request). The implication here is that we can begin with the silica layer, which is a portability framework of sorts and then extend it piecewise towards full CLIM by adding streams, then presentations, commands, and so on. ** Implications of event queues The CLIM II specification gives very few details about the nature of event queues, other than that they must be represented by first-class objects of some sort. These event queues must also be represented in a port-indepdent manner, as while they are defined in terms of "client" objects, sheets are clients instead of mirrors. There is no mechanism defined to create event queues, control the association between event queues and event clients, define a new kind of event client, or do anything with an event queue other than pass it to OPEN-WINDOW-STREAM. *** Why sheets within an application frame must share a common event queue My original argument for this hinged around the interepretation of the word "standard" in STANDARD-SHEET-INPUT-MIXIN, and the infeasability of having a single thread monitor multiple queues simultaneously. This was a weak argument, especially considering how wait-functions operate, but sufficed for an initial investigation. The new argument hinges around the behavior of QUEUE-REPAINT, and the infeasability of locating all of the queues that must be monitored if each PANE in an application frame has a separate event queue. Essentially, QUEUE-REPAINT is valid on any SHEET. This function adds a repaint request to the event queue associated with the SHEET. If each SHEET has its own event queue then calling STREAM-WAIT-INPUT on any given EXTENDED-INPUT-STREAM which is a STREAM-PANE must also check for repaint requests in the event queues associated with every SHEET associated with the application frame. While this can be done (call PANE-FRAME to obtain the frame, FRAME-TOP-LEVEL-SHEET to find the common ancestor of all SHEETs associated with the frame, then recursively walk the descendents of the top-level SHEET to find all of the PANEs associated with the frame), doing so for every call to STREAM-WAIT-INPUT is prohibitively expensive. Therefore, all of the SHEETs associated with an application frame must share the same event queue. *** Associating an event queue with an event client None of the SHEET or PANE creation interfaces described include a mechanism for specifying an associated event queue, save for OPEN-WINDOW-STREAM. Defining an interface for creating event queues is trivial. Defining an EVENT-CLIENT-MIXIN is likewise trivial. The question then becomes how to control the association between an event client and its queue. This brings to mind the following questions: 1. Which classes of object are defined to be event clients? 2. Under what circumstances are these objects created? With these questions determined, we can then attempt to devise some mechanism whereby "the right thing" will happen at least most of the time. The only objects which are /defined/ to be event clients are SHEETs. Neither application frames nor frame managers are defined to be event clients. So, under what circumstances are SHEETs (or PANEs, a subclass) created? 1. SHEET classes may be created by MAKE-INSTANCE, although there are no standard implementations of the SHEET protocol. 2. PANE classes may be created by MAKE-PANE, which ends up calling MAKE-PANE-1, which presumably calls MAKE-INSTANCE. 3. PANE classes may also be created by MAKE-INSTANCE directly. 4. STREAM-PANE classes may also be created by MAKE-CLIM-STREAM-PANE. 5. An unspecified STREAM-PANE class may be created by OPEN-WINDOW-STREAM. This function explicitly supports sharing an existing event queue. We have already established that all of the PANEs associated with an application frame must share the same event queue. Our primary concern must be the normal cases of creating the PANEs for a frame and WITH-OUPUT-AS-GADGET. The various ways to create new PANEs without explicitly passing an initarg for an event queue demands the use of an initform to provide a suitable default. Because this default depends on the frame for which the PANE is to be created, rather than a global or thread-affine value, the obvious thing to do is to use a special variable either as the initform or as part of the initform. The initial set of PANEs for a frame is built by GENERATE-PANES and LAYOUT-FRAME, as called from the context of ADOPT-FRAME, in turn called from (SETF FRAME-MANAGER), MAKE-APPLICATION-FRAME, or simply from a user program. In the latter case, one must presume that ADOPT-FRAME calls (SETF FRAME-MANAGER) itself. [ FIXME: Clearly, the actual protocol here still needs to be worked out. ] LAYOUT-FRAME may also be called from (SETF FRAME-CURRENT-LAYOUT). Perhaps the simplest method would be to have a slot on the application frame that holds the shared event queue for the frame, and arrange for some special variable (possibly even *APPLICATION-FRAME*) to be bound to the frame in ADOPT-FRAME. The initform for the event queue slot on a PANE could then retrieve the event queue from the frame. Having an initarg on the slot as well as an initform would allow OPEN-WINDOW-STREAM to create a new event queue or reuse an existing event queue as required. None of this machinery is suitable for associating an event queue with a SHEET in general, only with a PANE. The simplest solution here is to declare an initarg for providing an event queue to a SHEET, and then to add (or override) the initform for the event queue slot at the PANE level. As a matter of flexibility, it may make sense to define event client functions in terms of the event queue itself, and have the event clients forward to their queue. Alternately, it may make sense to define a single event-client mixin and allow opaque queue objects of explicitly unspecified representation to be passed around where necessary. ** Event queues and WinAPI WinAPI defines its own event queues to be per-thread data structures and windows are created to be thread-affine with respect to message (event) handling. There are a few scenarios here. - Single-threaded CLIM, single-threaded Lisp: Listening for events on a client also runs the main event loop (PROCESS-NEXT-EVENT). Events are placed into per-client queues as appropriate. - Single-threaded CLIM, multi-threaded Lisp: Proceeds as for single-threaded Lisp case, but any thread synchronization internal to CLIM may not occur, leading to potential badness. - Multi-threaded CLIM, event-handling loop thread: With only the event-handling loop listening for host OS events, all CLIM mirrors grafted to the display (so, not pixmap mirrors) must have their underlying windows created by the event-handling loop thread in order for their events to be delivered correctly. - Multi-threaded CLIM, no event-handling loop thread: Listening for events on a client blocks indefinitely, as the there is no main event loop. There are two ways forward. First, the single-threaded Lisp case already works. Second, the Multi-threaded CLIM, no event-handling loop thread case can be made to work with a winapi-specific hack in the client listener to call PROCESS-NEXT-EVENT just as would happen in the non-threaded case (this corresponds to the single-threaded CLIM, multi-threaded Lisp case, but with all of the thread synchronization in place). In any case, careful thought should be given to thread affinity throughout the specification for their effects on a WinAPI backend. [ NOTE: MAKE-APPLICATION-FRAME may well turn out to cause the panes of frame to be created (as this is where an application frame is/can be bound to a port and frame-manager). ] ** Sheet relationships The functions here are largely reasonable, but the mixin classes are slightly silly. - SHEET-PARENT-MIXIN is basically required for all sheets that are not grafts, and possibly also for some grafts. - SHEET-LEAF-MIXIN, SHEET-SINGLE-CHILD-MIXIN, and SHEET-MULTIPLE-CHILD-MIXIN correspond to the $0$, $1$, and $n$ cases for maximum number of child sheets. These classes are specified as "This class is mixed into sheet classes that...", but do not specify the responsible agency. The usual case for a host windowing system is that windows may have any number of children. The one exception that comes to mind is classic MacOS, where the window hierarchy is entirely flat: windows do not nest. All this said, there is evidence within the CLIM specification that SHEET-LEAF-MIXIN and SHEET-MULTIPLE-CHILD-MIXIN are used in some "internal" PANE classes, specifically LEAF-PANE and LAYOUT-PANE (possibly COMPOSITE-PANE). ** Sheet transformations and regions [ Call this section "sheet geometry"? ] [ FIXME: Discuss the geometry mixins, mention the question of grafts. ] [ FIXME: Much of this section is junk. ] Sheet transformations, when limited to rectilinear transformations, are mostly fine. When dealing with non-integer sizes / positions resulting from a scaling transformation, following the rendering conventions for pixel placement to set the actual bounding box is fine. Rectilinear rotations and inversions are iffier, particularly if they are supposed to affect things like font rendering (consider the issues involved if a ninety-degree rotation transformation caused text to be rendered in rotated fashion). General transforms, while possibly very cool, are out: This is a UI toolkit, not OpenGL. Sheet regions, that's another matter... mostly. Rule one: Negative coordinates makes it much harder to deal with a host window system, so don't do it. Rule two: Most host window systems have an upper limit on the size of a window, so there must be an effective upper limit on the size of a sheet region. Rule three: All sheet regions will end up being approximated by a set of bounding-boxes. Regions, in retrospect, provided that they are composed of AREAs rather than points, arc segments, line segments, or unbounded regions are fairly reasonable for use as window regions. Regions with areas of negative coordinates could be supported by biasing the actual sheet transformation as it appears to the host windowing environment. All this said, I am still concerned about the actual mechanics of CLIM regions. Lines and arcs are regions, the intersection of a line and an arc can generate up to two points. The only construct defined to handle a combination of disjoint regions is a region-set, which is defined in terms of rectangular strips. What happens to the two points? You have elliptical regions, and can apply an arbitrary transformation to the ellipse. If it has been converted to rectangular strips, what happens if you scale it up? If you have a rectangular region to start with, what happens if you apply a rotational transformation? *** Coordinate transforms in WinAPI and X WinAPI has a "mapping mode" for its drawing functions, which appears to operate solely in terms of position within a window (presumably not affecting things like font size). It is constrained to be a non-rotational, rectilinear transformation (thus, composed of scaling and translation transformations only). The mapping mode transformation affects drawing only: events and window placement are done in terms of "device units", which are pixels. Additionally, one can expect that anything other than the MM_TEXT mode, which is an identity or translation transformation only, is used infrequently at best. X is even more straightforward: All drawing operations are performed in terms of pixels; there are no coordinate transforms. *** Regions in WinAPI and X WinAPI supports an explicit REGION construct, comprised of rectangles, axis-aligned ellipses, and polygons, optionally combined pairwise using the intersection (AND), union (OR), relative complement (DIFF), and the relative complement of the intersection in the union (XOR) of two regions. X provides effectively the same REGION construct, only without support for ellipses. X regions are explicitly stated to be stored internally as collections of axis-aligned rectangular strips. X regions are entirely a client-side construct. Note that these REGION constructs differ from CLIM regions in that CLIM includes unbounded regions (nowhere and everywhere), paths, and points. Most of these objects make no sense in terms of specifying the area of a window or part of a window. *** Non-rectangular windows in WinAPI and X WinAPI has a function, SetWindowRgn, to set the "window region" of a window. This roughly corresponds to (SETF SHEET-REGION) in CLIM. X provides the "SHAPE" extension to set the "bounding region" and "clip region" of a window. These roughly correspond to (SETF SHEET-REGION) in CLIM. The "SHAPE" extension protocol provides what appears to be a subset of the REGION operations, but also provides the ability to specify a region as a set of axis-aligned rectangles (corresponding to the client-side X region construct). A proper investigation of the behavior of both WinAPI and X shaped windows and their interaction with the more rectilinear mechanisms for specifying a window region should be undertaken at some point. ** Instantiable sheet classes The CLIM II specification does not include any instantiable SHEET classes that are not also PANEs. It also does not explicitly enumerate the requirements of an instantiable SHEET class. We can, however, infer many of the requirements from the specification. The first requirement is that a sheet inherit from the SHEET or BASIC-SHEET classes. The second requirement is relationships to other sheets. Most sheets will require one of SHEET-LEAF-MIXIN, SHEET-SINGLE-CHILD-MIXIN or SHEET-MULTIPLE-CHILD-MIXIN. Most sheets will also require SHEET-PARENT-MIXIN, the most notable exception being grafts. The third requierment is geometry. Most sheets will want to include SHEET-TRANSLATION-MIXIN. In rare cases one of the other geometry mixins may be selected (why?). [ FIXME: Do grafts require a geometry mixin? The mixins are defined relative to the parent sheet and grafts are "root" sheets with no parent sheet. ] The fourth requirement is the relationship with the host windowing system, or mirroring. The MIRRORED-SHEET-MIXIN means that a sheet may have a "direct" mirror, or associated host window. All sheets without MIRRORED-SHEET-MIXIN are constructs solely of the application and not the host window system. At least the top-level sheets of an application must have MIRRORED-SHEET-MIXIN. This is also important for the "adaptive toolkit" gadgets of the application layer. The fifth requirement is event handling. While there are some mixins for this, they are not required so long as the protocols are adhered to. Note that two of these mixins implement a SHEET-EVENT-QUEUE method, implying that they interact with the hypothesized EVENT-CLIENT-MIXIN (see [[*Associating%20an%20event%20queue%20with%20an%20event%20client][Associating an event queue with an event client]] for details). The sixth requirement is repaint handling. This should typically be handled by including STANDARD-REPAINTING-MIXIN. [ FIXME: When is it appropriate to use one of the other two repainting mixins? ] The seventh requirement is output protocol support. [ FIXME: Five mixin classes, in two groups, of which one of each is necessary, and one mixin in one of the groups is an abstract mixin. ]