EWindow Advanced handle-based textwindowing.

EWindow is an implementation of a more advanced textwindowing unit from scratch. I started implementing it mainly because some textwindowing module/unit is a standard module in Modula2, and I wanted to port some more advanced units (pulldown-menus, helpsystem) to FPC, which are based on this kind of windowing unit.

The existing Textwindow-units in SWAG were too simple, and FreeVision is complicated and not ready yet.

News!



Additional remarks, bugs and principles

One of the most interesting standard units of the Modula2 RTL (also a Wirth language like Pascal) is module Window. It implements multithreaded textwindowing. So this module does NOT exist in the modula2 version of XTDLIB, it was written from scratch while peeking at the several Modula2 implementations.

This module emulates the Modula2 counterpart A BIT. The module is still under development, but the raw core stands, the rest is changing window colors, implementing shades etc, moving etc.

Opposed to the Modula2(TopSpeeds, not the ISO) version, this module is

Other comments:

Advantages compared to Modula2 version, and to an average equivalent from SWAG (the latter marked with PAS):



Project Status

General

The old Runtime-204 bug has been fixed, there are no bugs as far as I know. A very simple errorhandling system has been setup to detect corrupted windows and weird coordinates

The system isn't waterproof, and can be turned of with a conditional. If you however use good coordinates, everything works.

The current system is temporarily I think. If you have suggestions how to handle this kind of errors (create runtime error, do nothing on error(but try to fix data), maybe even signals), let me know.

FPC DOS/Go32v2

(no special Go32V2 bugs afaik). When you use crazy numbers of windows(>50,100), and open and close them without doing much (which delays), screen can flicker a bit. I tried to reduce this by buffering output on the stack, but that wasn't so successful. Anyway, the routine which caused the flicker doesn't reflect a real application anyway, it is more a benchmarking/testing routine.

Dos version opens and closes 500 windows in 5 seconds on a Cyrix P166+

FPC DOS/Go32v1

Status unknown. I use RTL procedures Go32.DosMemGet and -Put, but these do exist under Go32V1. Another incompability might be the assembler procedures (which set/get the cursorsize) for Dos in ELib. If you test under Go32V1, let me know.

Borland Pascal DOS

(no bugs, except that FPC-Crt standard issues highvideo, and Bp-Crt not, which results in blinking attributes.)

I don't test BP as much as the other OSes, so maybe the newest additions may cause some problems.

FPC Linux

Writing to bottom right character (Width,Height) scrolls the screen and messes up screen. Haven't found a working workaround yet.

Linux version seems fast enough now, but I'm running on a Cyrix P166+

The unit is more than usable under Linux, and even faster than make menuconfig (textwindowed kernel-configuration, see your kernel source). Make Menuconfig updates the screen to often. The Crt of the FPC/Linux is very good, and since Ewindow is based on it, it inherites those advantages.

I haven't tried running a program that uses EWindow via a telnet connection, since my second computer is offline.

The release 0.99.8 requires Crt.pp to be patched (See devel directory, only change: virtual screen moved from implementation to interface), newer snapshots (0.99.9 since mid-october) won't have this problem.

Other OS'es not implemented yet, I just wrote a letter to Dan for OS/2 help, and got some response. At least now I'm sure it's possible, and not extremely much work. The OS/2 port will depend on me be able to test under OS/2. IOW, do I get OS/2 installed? :-)



News!

I gave the idea of speeding up module EWindow a bit thought, looked at the TopSpeed RTL, and wondered how they solved it. The trick seems to be to build the windowlist (a linked list of the records with window-information, of type WinType) from top to bottom instead of bottom to top., but I can't figure out exactly how they do it (pretty complicated code, multithreading and writes to hidden windows)

The old approach when redrawing a screen is to

  1. Clear the screen, just in case the windows don't cover the entire screen
  2. Write the "lowest" window.
  3. Write the but lowest window
  4. etc etc until the top window
The new approach will be : (On a per line basis, and only the part that has to be redrawn, redrawscreen(1,1,Width,Depth) rewrites the entire screen. This is the general idea, some details (like which tests should be greater AND equal instead of only greater) aren't described here.
  1. Temporary values, TX1:=X1; TX2:=X2; where X1-X2 is the range of the current (partial) line we are rewriting
  2. Get a (first : top) window. 5 Cases :
    1. window.X2 <TX1. Do nothing, that window not in current range
    2. window.X1 >TX2. Do nothing, that window not in current range
    3. Window.X1 <TX1 TX1<Window.X2<TX2 Rewrite TX1..Window.X2 from buffer then Tx1:=Window.X2 (since original Tx1 ..Window.X2 is now in it's final state)
    4. Window.X2 >TX2 TX1>Window.X1<TX2 Rewrite Window.X1..TX2 from buffer then Tx2:=Window.X1
    5. Both Window.X1 and Window.X2 lie in the TX1 and TX2 interval. Don't know how to solve this yet. Redraw Window.X1..Window.X2 and then recursively redraw X1..Window.X1 and Window.X2..X2? I can't figure out how TopSpeed solves it, but I don't see recursion.
  3. IF TX1<>TX2 and Window.Next<>NIL take nextwindow and repeat step 2
(Added later) I see know how the TopSpeed guys do it. It's not recursive, but not really with less overhead. They kind of use a bi-section system. But I'm not sure what TopSpeeds system will do in this case:

|      yyyyyyyy         xxxxxxxxx   zzzzzzzzz   |
This is a rare case, and the TopSpeed programmers only redraw parts of windows at once, and check for a lot of operations when redrawing (they don't redraw the screen using the same procedure). Like Hide. Hide uses a specialised procedure which draws anything below the window to be hidden.

On the other hand, the TopSpeed RTL is to good to allow such bugs. I used this module for years, and never saw one flaw. What am I missing! At least I see why such complicated textwindowing units aren't included in SWAG :-)

I still will try to implement above algoritm, since it's the best, but for inbetween I implemented another system, which is much faster than the old one, but a bit slower than the new algoritm, but simpler, mainly because it doesn't require the windowlist (and with that a lot procedures more) to be changed.

The current implementation also only redraw the parts of the screen that have changed, but writes bottom up, which is slower because all windows in the range which is rewritten, are redrawn, instead of only the top ones. Under Dos this is no problem. Under Linux, with it's terminal driven screen it is, so I changed the unit to do the building up of the screen in (fast) memory, not directly on the screen, which is a lot faster.

Maybe it's slow on 386/33's etc, but above 486 DX2/66 speed won't be a problem.



Error handling

Right now, the error handling of EWindow is rather simple, just to avoid GPF's after real bad bugs in the mainunit. The errorhandling, though usable from outside the unit, is mainly meant for internal use, since it might change entirely. (using signals etc)

The bottomline is:



Types and constants

Styles

(Style definitions are not shown because it contains a lot of high ascii, and will get mangled)

Styles are small arrays with all characters needed to make frames in textwindows. The exact definition may change, so please use one of the predefined styles if you can.

Coordinates

AbsCoord= INTEGER;
RelCoord= INTEGER;

As you can see the above definitions aren't very complex, their main purpose is to let clearly see in the definition what are coordinates for the entire screen (e.g. window-positions), and which ones are positions IN windows. (e.g. cursorposition).

Right now all positions are AbsCoords, even the one marked with RelCoord. The positions typed with RelCoord are scheduled to become real relative coordinates somewhere in the future.

WinType

THE main type of this unit. A WinType is a type which describes a certain window. You should never directly manipulate a WinType parameter, or the record it points to. Use the EWindow procedures to achieve what you want.

WinDef

A WinDef describes all parameters needed to open a new window:

 WinDef    = RECORD
              X1,Y1,
              X2,Y2        : AbsCoord;   Coordinates of Window to create
              Foreground   : BYTE;       Initial colors of window
              Background   : BYTE;
              FrameOn      : BOOLEAN;    Should a frame be created for the window?
              FrameDef     : PChar;      Bordertype, POINTER TO Style
              FrameFore    : BYTE;       Colors of the frame
              FrameBack    : BYTE;
              END;
WinErrorType (Only exists if compiled with WindowCheck defined).

WinErrorType is an ordinal type which is passed to the errorhandler, and contains the type of error found. WinErrorType is defined as:

TYPE
     WinErrorType=(WinOk,XTooBig,YTooBig,NilWindow,BadIdent,NilBuffer);

The values have the following meaning:

valueplace where error was found meaning
WinOk(No error)No error, not used, I wanted to reserve ordinal value=0)
XTooBigWinDef The X2 coordinate is smaller than X1.
YTooBigWinDef The Y2 coordinate is smaller than Y1.
NilWindowWinType A procedure received a WinType parameter which was NIL, which isn't allowed for that procedure.
BadIdentWinType A corrupt WinType record was detected
NilBufferWinType An unallocated (NIL) Window buffer was detected(which will trash next rewrite).
ScreenTooWideWhen updating screen The screen you tried to update is too wide (probably >132 characters). Increase the InternalBufferSize constant in procedure UpdateScreen to avoid this. I put 132 there to keep memoryusage down.



Variables

VAR Height
    Width : INTEGER;
    WinError : WinErrorProc      Procedure variable, the errorhandler.
Height and Width are the dimensions of the text-mode screen

VAR  FullScreen: WinType;
"Background window", filled with screen as it was when the unit initialised. (Under dos, empty under Linux, I don't know how to get the startup screen under Linux). Can be used as a normal window.

VAR    WinError : WinErrorProc
Procedure variable which points to the default errorhandler. Follow above link (WinErrorProc) to find-out more about errorhandling.



Procedures



WinOpen

Declaration

FUNCTION WinOpen(WD : WinDef):WinType;

Description

Opens a window described by a WinDef, and puts it on top.

The returnvalue is used by the unit for further operations on that window. You'll have to save it.

Notes:

See also WinClose

Example:

CONST WD:WinDef=(X1:10;Y1:13;X2:45;Y2:25;Foreground:White;Background:Black;
               Frameon:TRUE; FrameDef:Style1;FrameFore:Yellow;FrameBack:Blue);


VAR Win : WinType;

BEGIN
 Win:=EWindow.WinOpen(WD);     {Open the window}
 SetTitle(Win,'Window 1');      {Set the title on this framewindow (FRAMEON=TRUE)}
 Readln;
 EWindow.WinClose(Win);         {Close the @$$%@ Window}
END.



WinClose

Declaration

PROCEDURE WinClose(VAR W:WinType);

Description

Closes the Window associated with the WinType. NIL is assigned to the WinType parameter

Notes:

See also WinOpen

Example:
See WinOpen



SetTitle

Declaration

PROCEDURE SetTitle(W:WinType;Title:PChar);

Description

Sets the title of the window, only use this on Frame-windows. The title is showed in the upper frame-part (centered)

Example:
See WinOpen



Use

Declaration

PROCEDURE Use(VAR W:WinType); Description

Puts the window on top, so that it can be written. Window must be WinOpen first.

Example:


BEGIN
 Win:=EWindow.WinOpen(WD);      {Open the window}
 Win2:=Ewindow.WinOpen(Win2);   {opens a second window, which is on top}
 Use(Win);                      {Puts window 1 back on top, so it can be written}
 EWindow.WinClose(Win2);        {You don't have to use a window to close it}
 EWindow.WinClose(Win);         {Close the other Window}
END.


Hide

Declaration

PROCEDURE Hide(W:WinType);

Description

Hides the window (makes it invisible) Makes the next window in line on top, but that is not 100% defined. You should always issue a Use command after a Hide command.

Notes

Example:
BEGIN
 Win:=EWindow.WinOpen(WD);     {Open the window}
 Hide(Win);                     {makes Window invisible}
 ReadLn;
 UnHide(W);                     {Makes Window visible again}
 Readln;
 EWindow.WinClose(Win);         {Close the @$$%@ Window}
END.


UnHide

Declaration

PROCEDURE UnHide(W:WinType);

Description

Unhides the window (makes it visible again). Doesn't put it on top, you should always issue a Use command after an UnHide command.

Notes See Hide

Example: See Hide



Change

Declaration

PROCEDURE Change(W: WinType; OX1,OY1,OX2,OY2: AbsCoord);

Description

Change changes the dimensions and/or position of window W to top left corner (OX1,OY1) and bottom right corner (OX2,OY2).

Only contents which fit in new window are copied, if the new window is bigger, the rest is filled with spaces with the current attributes for that window. If you resize to a smaller dimension, the surplus is lost

See Also WinMove
Example:

BEGIN
 Win:=EWindow.WinOpen(WD);     {Open the window}
 ReadLn;
 Change(Win,1,10,20,20);         {Change dimensions to (1,10) (20,20)}
 ReadLn;
 EWindow.WinClose(Win);        {Close the @$$%@ Window}
END.


WinMove

Declaration

PROCEDURE WinMove(W: WinType; OX1,OY1: AbsCoord);

Description

WinMove move a window so that top left of the window is on position (OX1,OY1)

WinMove is faster than Change, cause it doesn't resizes.

See Also Change
Example:

BEGIN
 Win:=EWindow.WinOpen(WD);     {Open the window}
 ReadLn;
 WinMove(Win,1,10);         {WinMove dimensions to (1,10)}
 ReadLn;
 EWindow.WinClose(Win);        {Close the @$$%@ Window}
END.


Clear

Declaration

PROCEDURE Clear(W:WinType);

Description

Clears a window with the proper attributes, and performs a GotoXY(1,1);
Crt.ClrScr may seem work right in Dosmode, but maybe it won't on other platforms. Clear is also faster, and allows you to clear a window which is not on top.

Example:

BEGIN
 Win:=EWindow.WinOpen(WD);     {Open the window}
 Writeln('text');
 ReadLn;
 Clear(Win);                   {Clear the window}
 ReadLn;
 EWindow.WinClose(Win);        {Close the @$$%@ Window}
END.


WinClone

Declaration

FUNCTION WinClone(W:WinType):WinType;

Description

Clone window W, hide de clone and return the cloned window.

Example:


VAR Win1,Win2 :WinType;

BEGIN
 Win:=EWindow.WinOpen(WD);     {Open the window}
 ReadLn;
 Win2:=WinClone(Win);          {Clear the window}
 Writeln('text');              {Write text to the original window}
 WinMove(Win2,10,10);          {Move Win2 to another position}
 Unhide(Win2);                 {Unhide it}
 Use(Win2);                    {Put on top and redirect output to it}
 writeln('Not the same text');
 ReadLn;
 EWindow.WinClose(Win);        {Close the @$$%@ Window}
 EWindow.WinClose(Win2);       {Close the @$$%@ Window}
 
END.


WrapWrite(Ln)

Declaration

PROCEDURE WrapWrite(CONST Towrite:String);

PROCEDURE WrapWriteLn(CONST Towrite:String);

Description

Writes string Towrite to the current Window, and tries to wrap the text in it. WrapWriteLn adds a linefeed.

Example:


VAR Win1,Win2 :WinType;

BEGIN
 Win:=EWindow.WinOpen(WD);     {Open the window}
 WrapWriteln('A longer text than which will fit into this window on one line I hope');
 ReadLn;
 EWindow.WinClose(Win);        {Close the @$$%@ Window}
END.