Fensterln erleichtert

Verwaltet man in eigenen Programmen eines oder mehrere Fenster, so erhält man vom AES eine WMREDRAW-Mitteilung, sobald ein Bildschirmbereich neu gezeichnet werden muß. Bestandteil dieser Nachricht ist die Angabe der Koordinaten des betroffenen Bildschirmausschnitts.

Für jedes Fenster stellt das AES darüber hinaus eine sogenannte Rechteckliste zur Verfügung. Diese Liste enthält die Koordinaten der kleinstmöglichen Zahl an Rechtecken, die den auf dem Bildschirm sichtbaren Bereich des Fensters abdecken. Zum Neuzeichnen eines Fensters ist es erforderlich, mittels wind_get die Rechteckliste abzuarbeiten, und nur dann, wenn sich ein Rechteck mit dem neu zu zeichnenden Bildschirmbereich überschneidet, diesen Teil eines Fenster neu auf den Bildschirm zu bringen.

Manche C-Compiler für den Atari ST stellen zur Ermittlung der Schnittfläche zweier Rechtecke die Funktion rc_intersect zur Verfügung:

int rc_intersect(GRECT *p1, GRECT *p2);

Hiermit ist es problemlos möglich, die Ausmaße des Bildschirmbereiches zu ermitteln, die beim Neuzeichnen von Bedeutung sind.

Als ich kürzlich in TURBO C 2.0 (jetzt eigentlich Pure C) Redraw-Routinen für eine Fensterverwaltung programmieren wollte, mußte ich wieder einmal die Gültigkeit von Murphy’s Gesetz anerkennen, das besagt:

Die Libraries des eigenen C-Compilers enthalten grundsätzlich keine rc_intersect-Routine.

Die Extended Rectangle Library

Um hier Abhilfe zu schaffen, machte ich mich selber an die Implementierung einer solchen Routine. Bei dieser Gelegenheit bot es sich an, neben rc_intersect auch die restlichen Funktionen der Extended Rectangle Library des PC GEM/3 für TURBO C zugänglich zu machen, um eine komfortablere Verwaltung von Rechteckstrukturen zu ermöglichen. Darüber hinaus bietet auch die Extended Object Library des GEM/3 einige interessante Funktionen, auf deren Realisierung ich im nächsten Heft zu sprechen komme.

Bei den Extended Rectangle Funktionen handelt es sich im einzelnen um die folgenden Aufrufe:

int rc_equal(GRECT *prec1, GRECT *prec2); 

Diese Funktion vergleicht zwei Rechtecke. Stimmen beide überein, liefert rc_equal das Ergebnis TRUE, andernfalls FALSE.

void rc_copy(GRECT *psbox, GRECT *pdbox); 

rc_copy kopiert die Koordinaten einer Rechteckstrukur in eine andere Rechteckstruktur.

int rc_intersect(GRECT *p1, GRECT *p2); 

rc_intersect überprüft, ob sich zwei Rechtecke überlappen. Ist dies nicht der Fall, ist das Ergebnis FALSE. Bei Überlappung wird TRUE zurückgeliefert, und die Struktur p2 enthält nach dem Aufruf die Koordinaten des Schnittrechtecks.

int rc_inside(int x, int y, GRECT *pt); 

Mittels rc_inside kann ermittelt werden, ob der Punkt mit den Koordinaten x und y innerhalb des durch pt beschriebenen Rechtecks liegt. In diesem Fall ist das Funktionsergebnis TRUE;

void grect_to_array(GRECT *area, int *array);

grect_to_array rechnet die Angaben x,y,w,h der GRECT-Struktur (s.u.) in die Koordinaten der Eckpunkte eines Rechtecks x1,y1,x2,y2 um. (Diese Routine ist in den Unterlagen zum PC GEM/3 übrigens falsch beschrieben.)

Eingabeparameter sind für jede der aufgeführten Routinen unter anderem Strukturen des Typs GRECT, der in AES.H wie folgt definiert ist:

typedef struct{int g_x; int g_y;
               intg_w; intg_h;} 
               GRECT;

GRECT definiert also ein Recheck über die Angabe des linken oberen Eckpunktes sowie die Breite und Höhe.

Der Assembler-Quelltext RCLIB.S wurde für den MAS-Assembler von Borland geschrieben, sollte aber anderen Assemblern keine größeren Probleme bereiten. Die Parameterübergabe erfolgt in der für TURBO C spezifischen Art und Weise: Ganzzahlige Datentypen werden in den Datenregistern D0-D2 übergeben. Liegen mehr als 3 solcher Parameter vor, befinden sich die restlichen Werte auf dem Stack. Pointer werden in den Adreßregistern A0 und A1 übergeben. Hat ein Aufruf mehr als 2 Pointer aus Parametern, so werden die überzähligen Pointer auf dem Stack abgelegt.

Die Datei RCLIB.H enthält die Prototypen für die Funktionen der Extended Rectangle-Library.

Das Listing WINDOW.C liefert zwei Beispiele dafür, wie man die Extended Rectangle Library bei der Fensterverwaltung gewinnbringend einsetzen kann, rc_intersect sowie rc_grect_to_array vereinfachen das Neuzeichnen von Fensterausschnitten nach einer WM_REDRAW-Mitteilung.

rc_equal ist nützlich, wenn nach dem Anklicken des Vollfeldes festgestellt werden muß, ob ein Fenster bereits Maximalgröße besitzt.

Die Routinen in WINDOW.C sind übrigens problemlos auf das GEM/3 unter MS-DOS übertragbar. Überhaupt ist die Kompatibilität der GEM-Systeme auf IBM-kompatiblen PCs und dem Atari ST sehr hoch, insbesondere bei der Verwendung von TURBO C 2.0.


/*****************************************/
/* RCLIB.H Allgemeine RCLIB Definitionen */ 
/*****************************************/ 
/*                                       */
/*      Autor: Uwe Seimet                */
/*                                       */
/*****************************************/ 

#ifndef ___RCLIB___
#define ___RCLIB___

int rc_equal( GRECT *prec1, GRECT *prec2 ); 
void rc_copy( GRECT *psbox, GRECT *pdbox ); 
int rc_intersect ( GRECT *p1, GRECT *p2 ); 
int rc_inside( int x, int y, GRECT *pt ); 
void rc_grect_to_array( GRECT *area, int *array );

/* RCLIB S
   (c) 1991 MAXON Computer
*/

        globl rc_equal
        globl rc_copy
        globl rc_intersect
        globl rc_inside
        globl rc_grect_to_array

g_x=0; 
g_y=2; 
g_w=4; 
g_h=6;

xmin1 = -16 
ymin1 = -14 
xmin2 = -12 
ymin2 = -10 
xmax1 = -8 
ymax1 = -6 
xmax2 = -4 
ymax2 = -2

        text

rc_equal:
        move.l (a0)+,d0 
        cmp.l (a1)+,d0
        bne notequal ;x- und y-Koordinaten unterschiedlich 
        move.l (a0),d0 
        cmp.l (a1),d0
        bne notequal ;Breite und Höhe unterschiedlich 
        moveq #1,d0 ;Flag für Gleichheit
        rts
notequal:
        clr d0 ;Flag für Ungleichheit
        rts

rc_copy:
        move.l (a0)+,(a1)+ 
        move.l (a0),(a1) 
        rts

rc_intersect:
        link a6,#-16 
        move g_y(a0),d0 
        cmp g_y(a1),d0 
        bcc skip
        move.l g_x(a0),xmin1(a6)
        move g_x(a0),d0
        add g_w(a0),d0
        move d0,xmax1(a6)
        move g_y(a0),d0
        add g_h(a0),d0
        move d0, ymax1(a6)
        move.l g_x(a1),xmin2(a6)
        move g_x(a1),d0
        add g_w(a1),d0
        move d0,xmax2(a6)
        move g_y(a1),d0
        add g_h(a1),d0
        move d0,ymax2(a6)
        bra.skp
skip:
        move.l g_x(a0),xmin2(a6)
        move g_x(a0),d0
        add g_w(a0),d0
        move d0,xmax2(a6)
        move g_y(a0),d0
        add g_h(a0),d0
        move d0,ymax2(a6)
        move.l g_x(a1),xmin1(a6)
        move g_x(a1),d0
        add g_w(a1),d0
        move d0,xmax1(a6)
        move g_y(a1),d0
        add g_h(a1),d0
        move d0,ymax1(a6)
skp:    move ymin2(a6),d0
        cmp ymax1(a6),d0 
        bcc ret
        move xmin1(a6),d0 
        cmp xmax2(a6),d0 
        bcc ret
        move xmin2(a6),d0 
        cmp xmax1(a6),d0 
        bcc ret
        move xmin1(a6),d0 
        move xmin2(a6),d1 
        cmp d1,d0 
        bcc.b (*)+4 
        move d1,d0 
        move d0,g_x(a1) 
        move ymin1(a6),d0 
        move ymin2(a6),d1 
        cmp d1,d0 
        bcc.b (*)+4 
        move d1,d0  
        move d0,. g_y(a1) 
        move xmax1(a6),d0 
        move xmax2(a6),d1 
        cmp d1,d0 
        bcs.b (*)+4 
        move d1,d0 
        move d0,g_w(a1) 
        move ymax1(a6),d0 
        move ymax2(a6),d1 
        cmp d1,d0 
        bcs.b (*)+4 
        move d1,d0 
        move d0,g_h(a1) 
        move g_x(a1),d0 
        sub d0,g_w(a1) 
        move g_y(a1),d0 
        sub d0,g_h(a1)
        moveq #1,d0 ;Flag für Überlappung
        unlk a6 
        rts
ret:    clr d0 ;Flag für keine Überlappung
        unlk a6 
        rts

rc_inside:
        move g_x(a0),d2
        cmp d2,d0 
        bcs outside 
        add g_w(a0),d2 
        cmp d2,d0 
        bcc outside 
        move g_y(a0),d2 
        cmp d2,d1 
        bcs outside 
        add g_h(a0),d2 
        cmp d2,d1 
        bcc outside
        moveq #1,d0 ;TRUE, falls innerhalb
        rts
outside: clr d0 ;FALSE, falls außerhalb
        rts

rc_grect_to_array:
        move.l g_x(a0),(a1)+ 
        movem (a0)+,d0-d1 
        add (a0)+,d0 
        subq #1,d0 
        add (a0),d1 
        subq #1,d1 
        movem d0-d1,(a1) 
        rts
/* Beispielroutine zum Window-Redraw unter Verwendung von       */
/* rc_intersect und rc_grect_to_array                           */
/* Eingabeparameter:                                            */
/* handle: Window-Handle des zu zeichnenden Fensters            */
/* rec:    Pointer auf neu zu zeichnenden Bildschirmausschnitt  */

void wm_redraw(handle,rec) 
int handle;
GRECT *rec;
{
    GRECT box;
    int pxyarray[4];

    wind_get (handle, WF_FIRSTXYWH, &box.g_x, &box.g_y, &box.g_w, &box.g_h); 
    while (box.g_w||box.g_h)
    {
        if (rc_intersect(rec,&box))
            /* Überlappung ermitteln */
        {
            rc_grect_to_array(&box,pxyarray);
            /* ergibt Clipping-Koordinaten */ 
            vs_clip(g_handle,1,pxyarray); 
            draw(); /* Routine zum Neuzeichnen aufrufen */
        }
        wind_get(handle,WF_NEXTXYWH,&box.g_x, &box.g_y,&box.g_w,&box.g_h);
    }
}

/* Reaktion auf Größenfeld, Demonstration für rc_equal */

void wm_fulled(handle) 
int handle;
{
    GRECT prev; /* vorherige Fenstergröße */ 
    GRECT curr; /* aktuelle Fenstergröße  */ 
    GRECT full; /* maximale Fenstergröße  */

    wind_get(handle,WF_CURRXYWH,&curr.g_x,&curr.g_y,&curr.g_w,&curr.g_h); 
    wind_get(handle,WF_PREVXYWH,&prev.g_x,&prev.g_y,&prev.g_w,&prev.g_h); 
    wind_get(handle,WF_FULLXYWH,&full.g_x,&full.g_y,&full.g_w,&full.g_h); 
    if (rc_equal(&curr,&full)) /* ist Fenster bereits bildschirmgroß? */ 
        wind_set(handle,WF_CURRXYWH,prev.g_x,prev.g_y,prev.g_w,prev.g_h);
    else
        wind_set(handle,WF_CURRXYWH,full.g_x,full.g_y,full.g_w,full.g_h);
}

Uwe Seimet
Aus: ST-Computer 11 / 1991, Seite 85

Links

Copyright-Bestimmungen: siehe Über diese Seite