Wimp (Nested Windows) Functional Specification
Distribution: COMPANY CONFIDENTIAL
Project: NC Model 1
Issue: 0.01
Author(s): Neil Raine
Date: 30-Jul-96
Last Issue:
Contents.
History
0.01 NRaine 30-Jul-96 Created.
Outstanding issues
The following features do not form part of the proposed enhancements
in this specification, but are worthy of later consideration:
- Recursively nested windows (ie. child windows to be allowed to have
children).
- Child window resizing/dragging ought to be restricted to the work area
extent of the parent window.
- Auto-scrolling options when dragging/resizing child windows within
a parent that can scroll
- Optimizations for child windows which have no work area (eg. scrollbar
controls)
- Wimp_OpenWindow to be able to dynamically alter a window's flags (eg.
add/remove scrollbars)
Overview
The Window Manager is to be extended to allow nested (or 'child' windows),
with the following behaviour:
- Wimp_OpenWindow is extended to allow the parent window and alignment
flags to be specified when opening a child window
- Wimp_GetWindowState is extended to allow a child window's parent and
alignment flags to be read
- Child windows are stacked within the parent window's work area, and
are redrawn clipped within it
- Child windows can be attached to the work area of the parent, or with
any of its edges, by specifying alignment flags in Wimp_OpenWindow
- Child windows can have any or all of the same tools as top-level windows,
and can be dragged, resized etc. as normal
- Wimp_OpenWindow is optimized to reduce flicker and unnecessary redraws
when moving a parent window and its children together
- A child window does not have to be owned by the same task as the parent
window, ie. windows can be opened and invalidated by tasks other than the
one which owns the window
- Closing a parent window closes the children as well, but retains the
stacking order of the children, so if you re-open the parent window, the
children are still open in their original positions (and Wimp_GetWindowState
still indicates that they're open even if the parent is closed).
- Deleting a parent window does not delete the children, but does close
them.
Technical background
Window stacks
The global window stack retains its original meaning, but now indicates
the open top-level windows,and not the child windows. Each window has a
list of child windows which are open within it, which works exactly as
the global list works for top-level windows.
Window coordinates
All window coordinates are screen-relative, including those of
child windows (ie. they're not relative to the work area of the parent).
There are two reasons for this:
- Code for redrawing a child window need not take the coordinates of
the parent into account
- Child windows can be attached in many different ways to their parents,
not just relative to the work area origin (see below)
Wimp_OpenWindow API extension
- R1 -> open block
- R2 = "TASK" => extended version of Wimp_OpenWindow:
- R3 = handle of parent window
- R4 = alignment flags:
- bit 0 clear => defer the open until Wimp_Poll, or Wimp_OpenWindow
with this bit set
- bits 16,17 => how left edge of child is linked to parent
- bits 18,19 => how bottom edge of child is linked to parent
- bits 20,21 => how right edge of child is linked to parent
- bits 22,23 => how top edge of child is linked to parent
- bits 24,25 => how x-scroll of child is linked to parent
- bits 26,27 => how y-scroll of child is linked to parent
- The flag bit pairs (high bit, low bit) have the following meanings:
- 00 => linked to work area (inc. scroll offset) of parent
- 01 => linked to left/bottom edge of parent work area
- 10 => linked to right/top edge of parent work area
- 11 => reserved
Bit 0 of the flags is normally clear, to indicate that the Window Manager
can defer the block-copying of the window until later. This allows it to
'collect up' a series of Wimp_OpenWindows of related windows, which is
important for the optimization of certain common cases (eg. moving a parent
and its children together, so their relative positions don't change).
The other alignment flags allow child windows to act as panes, which
can be stuck to any edge of the parent window, scrolling with it or not
as required, or to simply be stuck to the work area of the parent.
Note that when a window is opened using this version of the call, the
parent window handle and flags will be re-used if the old version of Wimp_OpenWindow
is used subsequently on the same window. Because an Open_Window_Request
never needs to alter the parent window or alignment flags, it actually
works out quite nicely that the extra parameters are passed in in registers,
rather than in the OpenWindow block, as you can carry on using the old
version of Wimp_OpenWindow to service Open_Window_Requests.
Wimp_GetWindowState API extension
Entry:
- R1 -> block to receive window state
- R2 = "TASK" => extended version of Wimp_GetWindowState:
Exit:
- [R1,#0..32] as for old version of Wimp_GetWindowState
- R3 = parent window handle (as R3 on entry to Wimp_OpenWindow)
- R4 = alignment flags (as R4 on entry to Wimp_OpenWindow)
Redrawing windows
The global invalid rectangle list is still used for redrawing, but the
algorithm for stepping through the windows is extended as follows:
- For all windows in the top-level stack (starting at the front)
- Set windowrects to the intersection of the invalid list with outer
portion of window (ie. including scroll bars etc.)
- If this list is non-null:
- For all child windows of this parent:
- Intersect outer box of child with work area of parent
- If the resulting box intersects any of the rectangles in the window
list:
- Set windowrects to the resulting list, and generate a redraw of the
child
- If no children intersect the list, generate a redraw of the parent
Wimp_RedrawWindow now does the following:
- If redrawhandle is set to this window, just perform a redraw exactly
as the old Wimp would have done
- We're assuming here that none of the rectangles overlap any child windows,
due to the redraw algorithm above
- Each rectangle is explicitly re-validated as it's redrawn by the client
- System borders are re-validated separately, by subtracting the work
area and then subtracting the result from the invalid list
- Otherwise:
- Call visibleouterportion to generate the list for the border regions,
redraw them and re-validate border area as normal
- Intersect with work area, and subtract all child window (outer) rectangles
- Proceed with work area redraw
Note that visibleouterportion does not subtract any children
of the window in question, but does have extra code to deal with
this window itself being a child window (this affects a direct call of
Wimp_RedrawWindow for a child window):
- Start with outer box of child
- Subtract outer boxes of any child windows in front of this one within
the parent's stack
- Clip to the parent's work area
- Subtract outer boxes of any top-level windows in front of the parent
Note also that invalidouterportion is only ever called for top-level
windows, and it can stay the same, while invalidinnerportion is never called
anyway.
Mouse clicks
This is pretty simple, but must be slightly extended to cope with child
windows:
- For all windows in the top-level stack (starting at the front):
- If the hit point is within the outer portion of the window:
- If the hit point is within the work area of the window:
- Recursively check the child windows for a hit
- If none of them contains it, generate a click in the work area of the
parent
- else work out which system border item has got the click, and process
it
Note that once the mouse handling is dealt with, the Wimp's handling
of the system areas (eg. scrollbars) should generate the correct results
for child windows, as long as the Wimp_OpenWindow code can deal with reopening
the child windows as required. Note that if you call the old version of
Wimp_OpenWindow, the child window's parent and alignment flags will remain
the same (which is what we want).
Opening windows
This is where it all gets a bit tricky!
Several things have to be dealt with at once:
- Windows changing position within a window stack (eg. moving to the
front or back of the stack)
- Windows changing their parent window (ie. moving between stacks)
- Windows moving, resizing and/or scrolling
- Child windows being automatically moved/resized/scrolled according
to their alignment flags
- Child windows being explicitly moved/resized/scrolled by the task which
deals with opening the parent
Note that the automatic reopening of child windows according to their
alignment flags takes place when the parent window is reopened, so if you
want to perform your own adjustment of child windows, you should open the
parent first (which will adjust the children as well), followed by reopening
the child windows as required. Note also that the restriction that only
the owner of a window can call OpenWindow on it has been removed, to allow
the owner of the parent window to directly reopen the child windows,even
if some of them are owned by another task. This applies to Wimp_ForceRedraw
as well.
The result of a Wimp_OpenWindow, besides altering the current stack
position and coordinates of a window, is to update the screen display by
performing a series of block-copy operations where possible, and adding
rectangles to the invalid list (to be redrawn later) where it wasn't possible
to copy the contents from the old visible area of the window.
For efficiency reasons, it is important to 'collect up' a series of
Wimp_OpenWindow calls so we can decide where the child windows are moving
in relation to the parent (and in relation to their siblings). There are
several reasons for this:
- To optimize the block-copying of a parent and its children in the case
where they are all moving as one unit
- To avoid 'shuffling' problems where a set of child windows moves as
a unit
[ The problem is that, unless you can arrange to deal with all the child
windows and the parent as one, you have to move one of the windows first,
which can obscure and then reveal a portion of one of the windows next
to it, which in turn results in a redraw that should not be necessary if
both windows end up moving by the same amount ].
On the other hand, the problem when you've got a whole series of windows
to move at the same time is that you'll end up with a set of rectangles
to copy by different x,y offsets. Ideally these must all be copied simultaneously,
so that you don't obscure portions of other source rectangles when copying
to a destination rectangle. Effectively we must build up a list of all
required block-copy rectangles, and copy them in an order which avoids
trashing other rectangles. The problem then is that, since the x,y offset
is not the same for all rectangles, it's possible to have a circular set
of rectangles for which there is no copy order that doesn't trash one or
other of the rectangles. In this case we must just copy the 'least damaging'
rectangle, and subtract the destination rectangle from the list of source
rectangles.
So, the fancy new Wimp_OpenWindow looks like this:
- Round coordinates to pixel boundaries, and restrict window coordinates
and scroll offsets to valid ranges
- Update 'current' window position (but leave a copy of the most-recently-displayed
position)
- Deal with stacking order changes - we need to get a visible area list
for each window prior to the stack order change
- For all children of this window, inspect alignment flags and call Wimp_OpenWindow
recursively (without flushing)
- If 'flushing' (on Wimp_Poll, or Wimp_OpenWindow with R4 bit 0 set):
- For all top-level windows:
- (Note that oldwindowrects is generated from the 'displayed' coordinates
of all windows, not the 'current' ones)
- (This is crucial, as it allows butted siblings to move simultaneously
without knobbling each other's visible areas)
- (NOTE: unfortunately we also require the old stacking order of the
windows, which could be a bit tricky!)
- If window is moving/scrolling/resizing:
- If window isn't being resized or scrolled:
- Include border regions in oldwindowrects
- else
- If border regions are to be moved or resized, invalidate them
- Include only the work area in oldwindowrects
- For all child windows of this parent:
- Recurse on the child, generating oldwindowrects from its work area
and borders
- Invalidate other child border areas if resizing or moving relative
to parent's work area
- If the child's work area rectangles are moving by a different x,y offset
from the parent's work area:
- Remove child rectangles from parent's oldwindowrects list
- Add child rectangles to the moving list, with the appropriate x,y offset
for the child
- Clip parent's oldwindowrects to the (new) visible portion of the parent
(with or without borders)
- (Note that the 'visible portion' may or may not include the children,
depending on whether the children are moving with the parent)
- Add remaining parent's oldwindowrects to the moving list, with the
appropriate x,y offset for the parent
- Perform a block-copy of the rectangle list (where each rectangle can
have a different x,y offset)
- Update all windows' most-recently-displayed coordinates to their current
positions
It would also be nice if the most-recently-displayed and current window
positions could be used to fix the problem of having two 'linked' top-level
windows moving together, so that they avoid temporarily overlapping each
other and generating an unnecessary redraw. This is in fact the same problem
as that of ensuring that child windows don't temporarily overlap their
siblings when being moved together.
Implementation plan
Due to the lack of adequate debugging and tracing facilities, it is
advisable to proceed with caution! This means that the best plan involves
a step-by-step approach which avoids 'breaking' the existing code as far
as possible.
So, the plan is:
- Get the test platform set up:
- Build the WindowManager from the existing sources, and get it working
on the RISC PC.
- Bracket all changes with the new ChildWindows option flag
- Write a test program that opens a window, and arrange for this to be
the only task which runs under the Wimp
- Minimal extension to Wimp_OpenWindow for child windows:
- Add data structures for child window stacks
- Extend Wimp_OpenWindow so it can open child windows (but ignore update
code for now)
- Implement Wimp_GetWindowState extensions (easy)
- Implement redraw code for child window stacks:
- Recursive method of redrawing, clipping child windows to the parent's
work area
- Extend test program so it opens a single child window
- Test with a single child window
- Extend test program so it opens another (overlapping) child window
- partly outside the parent window's work area
- Test redraw code for the new case
- Implement mouse click detection for child windows
- Recurse down child window stack to find the correct window
- Check that the Wimp's system border handling works (eg. for dragging
and resizing child windows)
- Implement full Wimp_OpenWindow functionality
- Defer Wimp_OpenWindow flushing until the next Wimp_Poll (or when explicitly
requested)
- Get Wimp_OpenWindow update code working (not necessarily optimized)
- Implement alignment flags for child windows
- Extend test program so it opens child windows with all combinations
of alignment flags
- Optimize Wimp_OpenWindow to avoid flicker and unnecessary redraws
- Test reopening at different stack depths
- Test reopening in a different stack altogether (swapping from child
window to top-level and vice-versa)
- Test child window which is owned by a different task from the parent
- Implement other window-redraw related stuff:
- Check that Wimp_UpdateWindow works on child windows, and within a parent
containing children
- Check that Wimp_ForceRedraw works in similar circumstances
- Check that icon redraw works in child windows and parents containing
children
- Check the above where the parent and/or child is partially off-screen
Acceptance test
To be determined.
Development test strategy
To be determined.
Product organisation
The WindowManager sources will form part of the NC model 1 ROM build.
Future enhancements
None planned at present.