Last updated at 4:57 pm UTC on 8 June 2016
Areithfa Ffenestri (Welsh for platform windows) is an architecture for supporting multiple host platform windows within Squeak.
As of this date (Oct 6 2004) there is an architecture description and reference alpha-quality implementations for RISC OS and Mac OS/X. This architecture is for the lowest levels of the system, the virtual machine and dungeon levels of the image. NO support yet exists for Morphic since there is a vast amount of code and class cleanup needed. Tweak has been our sole higher level target thus far since it is somewhat less broken, not to mention the target of our main project remit.
Ffenestri can open, update, move and close platform windows as well as set various attributes such as title string, close buttons and so on. The main interface to the OS is via a fairly small plugin which make minimal assumptions about the capabilities of the OS. The low-level image code uses a bridge pattern to support platform portability as cleanly as possible, including re-establishing host windows that were open during an image save. We add some input events to report window activity such as size changes and paint events, which requires some small core vm changes. The Smalltalk interface to all this is via the Canvas>forceToScreen: idiom and a small api on HostWindowProxy (sub)instances.
Low level multiple windows architecture v5
This is an introduction and discussion of the low level changes, the mechanism implementation, to the Squeak VM needed to support multiple host windows. Policy changes in the image will be addressed elsewhere. v5 reflects the separation of the platform specific host window proxy class from the portable client that most programmers will work with.
It is likely there will be a need for some structure within the VM to store window related data that is not held by the OS window structure. We also need some reasonably simple way to refer to the host window attached to a particular Squeak window in a portable manner.
The created window structure (hereinafter referred to as a windowDescriptorBlock) can be managed by platform code using a linked list (Areithfa Ffenestri is an example list implementation as used in Mac & RISC OS) or similar construct and the only identifier available to the image will be a SmallInteger key returned by the window creation primitive. Note that this is the only restriction in the design of the VM window related code - if you can really manage with only the OS window handle and it can be converted losslessly to a SmallInteger and back then feel free to use it thus.
Do consider safety at this level - if someone sets the windowHandle ivar of a HostWindowProxy object to some random number will your system blow up? - if you allocate window index values sequentially what will happen when that value wraps? Also consider limits - do you want to limit the number windows able to be open at any time?
The DisplayScreen instance currently used as the sole window pixmap is expected to be installed as host window index #1 very early in the VM startup and various special tests will be needed while it continues to be used. Eventually we expect that this special case can be removed.
So far in experimental implementations we have found a need to be able to map from
so consider how to make these efficient. Mapping the window index to an OS window handle is likely to be important if your OS needs to be informed of display updates. The inverse will almost certainly be need to provide the window index for input events.
- window index to OS window handle
- OS window handle to window index
- window descriptor block from index
- window descriptor block from OS window handle
The interface to the primitives is contained within the HostWindowPproxy tree; this allows easy use of platform specific subclasses that can translate from a portable request api to the platform's needs. Initially the only area needing this is the converting of window attributes into a platform convenient format - see AcornWindowProxy>handleAttributes: for an example. it may prove useful to convert other parameters later.
Another benefit of using a proxy in this manner is that extended behaviour can be provided, not normally available to a platform. As an example, it may not be possible to add a titlebar to an already open window - using the proxy we can detect the problem and completely replace the previous window without the client having to do anything.
This usage of a proxy is commonly referred to as a Bridge Pattern, for those of you interested in the gang of four pattern terminology. Clients (such as DisplayHostWindow in our experimental system) need only keep a proxy instance and feed it portable api messages.
To create a host window we use HostWindowPlugin > primitiveCreateHostWindowWidth:height:originX:y:attributes: which makes no expectation on the receiver. The prim parameters for height, width and origin are expected to be taken from the Form used by the client instance (See DisplayHostWindow for example). The last parameter is a list of attributes for things like titlebar, floating, resizable etc. but is this is intended to be platform defined - platform subclasses of HostWindowProxy will assemble suitable values and the platform plugin code will decode them. Some example implementations are
The primitive code calls the platform specific function createWindowWidthheightoriginXyattrlength(int w, int h, int x, int y, int
- assemble a 64bit flag word to match the Mac OS window flag
- use the bytes as numeric tags on the assumption that there are unlikely to be more than 256 options
- encode strings.
attributeList, int listLength) which takes the same parameters as the prim but decoded to ints, the first indexable field address (char ) of the list nad the byte length of the list.
Enough of a windowDescriptorBlock should be built to allow the window to be displayed on without crashing. On RISC OS the ioShowDisplayOn() code will complete the creation and opening of the actual window in a lazy manner. Other platforms may fully realize the window as part of the creation primitive or follow the lazy-initialization path. The obvious difference is that lazy-initialization avoids having a blank window open and (presumably) getting OS events.
Window attributes, decorations
This revision adds a mechanism for handling window decorations (titlebar icons such as close buttons, size toggling etc) and attributes (modality, size restrictions). The client of a proxy can ask for a type of window which is converted by the proxy to specific decorations etc. See the protocol 'window decoratons' in the HostWindowProxy classes. The plugin has been updated to support this use of the attribute list (which was unused before) and test added to check function. Initially support is provided for closeButton, iconiseButton and sizeToggleButton plus title strings with no support as yet for modality settings. The sole current type of window provided is #defaultWindowType which is expected to be a normal fully decorated window.
Since this version of the code does not hook into BitBlt we have a primitive very similar to primitiveShowDisplayRect() to force the bitmap to display. To provide extra flexibility and remove dependency upon the object format of the HostWindowProxy the prim takes parameters for the windowHandle, the bitmap, the bitmap width, height and depth as well as the affected rectangle. This prim initially is expected to be used when a Canvas is created with a DisplayHostWindow as its target form. Other structures need only ensure that a suitable bitmap and related metrics can be provided. See HostWindowProxy > forceToScreen: and HostWindowPlugin > primitiveShowHostWindow:bits:width:height:depth:left:right:top:bottom: which uses the platform specific function ioShowDisplayOnWindow().
Window size initial setting is currently specified in the create primitive, as is the initial on-screen position. The size is expected to be provided in pixels and the origin is provided in pixels with the origin zero-point at the topleft of the screen.
The HostWindowProxy > primitiveWindowSize: will return the current size of the window in pixels, using HostWindowPlugin > primitiveHostWindowSize: via platform code 'ioSizeOfWindow()' which has the single window index parameter and returns the size in in the same x<<16||y format used by ioScreenSize().
Resizing can be done two ways - by using the Host UI tools or programmatically. HostWindowProxy > primitiveWindowSize:x:y: will change the size of the specified window using HostWindowPlugin > primitiveHostWindowSizeSet:x:y: via platform code 'ioSizeOfWindowSetxy()'. Platform code will need to cope with oversize requests and potential ludicrous undersize requests. It must also return (in the same x<<16||y format used by ioScreenSize()) the actual value resulting from the change so that image code can cope with any restrictions or limits. A failure condition (float values, OS disallowed size etc) are indicated with a -1 return value which, since it corresponds to a size of 64k@64k is safely out of reach of physical displays for several years.
Interactive resizing is currently done as a side effect of the Morphic loop constantly asking what the screen (ie window ) size is and altering the Display extent when needed. We have added a mechanism to provide input events (see later) to notify of a window size change so that things can be a little cleaner.
Window iconising, hiding
Some OSs allow windows to be iconised - closed down to icon sized - without being closed. Some need no action from the aplication, some do. Other OSs provide a similar functionality by hiding windows. There is currently no programmatic support for this.
Window title changing
HostWindowProxy > primitiveWindowTitle:string: uses HostWindowPlugin > primitiveHostWindowTitle:string: and sets the titlebar label string of the specified window via ioSetTitleOfWindow(id, titleString, titleLength). An inappropriate windowIndex or improper string (bad characters, too long, etc) results in a -1 return value and prim failure.
As with window resizing there are two ways to move a window. Some OSs deprecate programmatic window moving and primitives for these should probably simply fail. HostWindowProxy > primitiveWindowPosition: will return the window origin (that is to say the topleft corner of the Squeak accessible area not counting any borders or title/menu bars) via HostWindowPlugin > primitiveHostWindowPosition: and the 'ioPositionOfWindow()' code, which returns the value in the usual x<<16||y form. Currently the value -1 indicates failure which as above corresponds to an implausible position.
Likewise HostWindowProxy > primitiveWindowPosition:x:y: uses HostWindowPlugin > primitiveHostWindowPosition:x:y: via 'ioPositionOfWindowSetxy()' to change the window origin if allowed by the OS. As with iosSizeOfWindowSetxy() it must return the actual resultant value or -1 for failure.
HostWindowPlugin > primitiveCloseHostWindow is used by HostWindowProxy > primitiveWindowClose:, taking a single argument of the window index previously provided by the creation prim. Since it is just about conceivable that the window might be closed before all display updates on it are completed, consider making some safeguards in the relevant display code. The primitive will fail if the platform code 'closeWindow()' returns 0.
Window change events
As mentioned above, we provide some input events to notify the image of changes to open windows. We define several subtypes of the window event
These events are passed into the image in the normal manner via ioGetNextEvent(). Note that this implies some linkage betweeen the plugin and the core vm that is not particularly desirable in the long term. We anticipate a move to having all of the window display and event input code moved to a unified plugin and out of the core vm insofar as this is possible.
- MetricChange; size or position of window changed and included in the event are left/top/right/bottom values
- Close; window close icon pressed
- Iconise; window iconised or hidden etc
- Activated; window made active - some platforms only - do not rely upon this
- Paint; window area (values in rest of event) needs updating. Some platforms do not need to send this so do not rely on it in image. Handle by passing area on to #forceToDisplay etc
- Stinks; just to see if you are actually paying attention.
Window SUnit test
An SUnit test is included that
- creates a window with a close box
- checks the window size
- dispalys a section of Display on it
- updates the entire DisplayHostWindow
- displays on it again
- updates a partial area
- checks the window origin position
- changes the origin position and checks it
- changes the size and checks it
- changes the title to a reasonable string
- attempts to change the title to a foolishly long string (which should fail)
HostWindows for Pharo.
I've begun trying to pull the pieces of this together and bring it into Pharo. Missing is the HostWindowProxy for Unix although there is code for a plugin in the VM source tree. I'm working on a Mac. There has been a lot of bit rot as there have been many enhancements to morphic in Pharo and event handling needs to be extended to support the host window events. I'm still struggling with this. A screen shot follows of a morph embedded into a host native window. Mouse events seem OK, keyboard events remain problematic. I seem to have exposed some unfortunate assumptions about mouse location coordinates that will need sorting out. -tb