# ereignissteuerung Specification ## Purpose Die Ereignissteuerung bestimmt, woher Ereignisse kommen, wann ein laufendes Programm sie zu sehen bekommt und wie es darauf reagiert: Ereignisquellen (Tastatur, Maus, Zeit, benutzerdefiniert) und ihre Warteschlange, die abschließende Liste der Zustellpunkte, die Maskierung je Quelle und global, sowie die Zustellung als `GOSUB` in das laufende Programm. Sie ist die gemeinsame Grundlage der klassischen Ereignis-Traps und späterer Forms-Ereignisse. ## Requirements ### Requirement: Ereignisquellen mit Zeit vom Host Die Laufzeit SHALL Ereignisse aus vier Quellen führen: Tastatur, Maus, Zeit und benutzerdefinierte Ereignisse. Die Zeit SHALL vom Host bezogen werden; die Laufzeit MUST NOT für die Ereigniszustellung selbst auf die Systemuhr zugreifen. Ein Host ohne Terminal MUST die Zeit frei stellen können, sodass zeitgesteuerte Programme ohne Wartezeit und mit reproduzierbarem Ergebnis prüfbar sind. Auch Wartefristen der VM SHALL ausschließlich von der Hostzeit abhängen. Ein Host mit stehender virtueller Zeit MUST NOT durch reale verstrichene Zeit eine Frist scheinbar erreichen. #### Scenario: Zeitgesteuerter Trap ohne echte Wartezeit - **WHEN** ein Testhost die Zeit um 5 Sekunden vorstellt und ein Programm `ON TIMER(5) GOSUB Marke` mit `TIMER ON` aktiv hat - **THEN** wird der Trap zugestellt, ohne dass der Test tatsächlich wartet #### Scenario: Wiederholter Lauf liefert dasselbe Ergebnis - **WHEN** dasselbe zeitgesteuerte Korpusprogramm zweimal mit demselben Zeitverlauf ausgeführt wird - **THEN** ist die Ausgabe beide Male identisch #### Scenario: Stehende virtuelle Zeit - **WHEN** ein Host während SLEEP seine Zeit unverändert hält - **THEN** endet SLEEP nicht wegen realer Zeit; eine vom Host gemeldete Frist oder ein zugestelltes Ereignis beendet das Warten ### Requirement: Mausereignisse in Zellenkoordinaten Die Darstellungsschicht SHALL Mausereignisse der Ausführungsumgebung an die Laufzeit weitergeben: Drücken, Loslassen und Bewegung, jeweils mit gedrückter Taste, Umschaltzustand und Position. Die Position SHALL in Zellen des Textbildschirms angegeben werden, gezählt wie bei `LOCATE`, also 1-basiert. Ereignisse außerhalb der aktuellen Bildschirmfläche MUST verworfen werden. Die Reihenfolge zwischen Maus- und Tastenereignissen SHALL der Reihenfolge ihres Eintreffens entsprechen. Solange kein Verbraucher ein Mausereignis annimmt, MUST es am Zustellpunkt verworfen werden, damit die Warteschlange nicht unbegrenzt wächst. Die Reihenfolge SHALL bis zur beobachtbaren Ausführung der zugehörigen Handler erhalten bleiben; getrennte Quellarten MUST NOT nachträglich nach ihrer Art umgeordnet werden. #### Scenario: Position als Zelle - **WHEN** in der linken oberen Ecke der Darstellungsfläche die linke Maustaste gedrückt wird - **THEN** trägt das Ereignis Zeile 1 und Spalte 1 #### Scenario: Reihenfolge bleibt erhalten - **WHEN** eine Taste gedrückt und danach die Maus geklickt wird - **THEN** liefert die Warteschlange erst das Tasten-, dann das Mausereignis #### Scenario: Ereignis außerhalb der Fläche - **WHEN** ein Mausereignis eine Position außerhalb der aktuellen Bildschirmgröße nennt - **THEN** wird es verworfen und erreicht die Warteschlange nicht #### Scenario: Kein Verbraucher, keine Anhäufung - **WHEN** ein Programm ohne Verbraucher für Mausereignisse läuft und die Maus dauerhaft bewegt wird - **THEN** wächst die Warteschlange nicht über ihre Grenze und das Programm läuft unverändert weiter #### Scenario: Taste vor Maus bis zum Handler - **WHEN** eine Taste vor einem Mausklick eintrifft und beide Handler eine Kennung anhängen - **THEN** steht die Tastenkennung vor der Mauskennung #### Scenario: Maus vor Taste bis zum Handler - **WHEN** ein Mausklick vor einer Taste eintrifft - **THEN** wird zuerst der Maushandler wirksam ### Requirement: Abschließende Liste der Zustellpunkte Ereignisse SHALL ausschließlich an folgenden Punkten zugestellt werden — das Vorbild prüft die Ereignismarke „before executing the next statement": an einer Anweisungsgrenze, bei `DOEVENTS`, während `SLEEP` und vor einer blockierenden Eingabe. Zwischen zwei Zustellpunkten MUST NOT ein Trap-Handler beginnen; insbesondere MUST NOT eine Anweisung in ihrer Mitte unterbrochen werden. Ein zugestelltes Ereignis SHALL ein laufendes `SLEEP` beenden. Forms-Ereignisse SHALL auch während SLEEP an dessen bestehenden Zustellpunkten verarbeitet werden. Eine für das Menü geltende Zustellsperre SHALL an jedem Zustellpunkt gleichermaßen gelten. #### Scenario: Keine Zustellung innerhalb einer Anweisung - **WHEN** ein Ereignis fällig wird, während eine mehrteilige Anweisung ausgewertet wird - **THEN** läuft die Anweisung zu Ende und der Handler beginnt erst an der folgenden Anweisungsgrenze #### Scenario: SLEEP endet durch Ereignis - **WHEN** ein Programm `SLEEP 60` ausführt und nach 2 Sekunden ein aktiver Trap fällig wird - **THEN** wird der Handler ausgeführt und `SLEEP` kehrt danach zurück, ohne die vollen 60 Sekunden abzuwarten #### Scenario: DOEVENTS stellt zu - **WHEN** ein anstehendes Ereignis vorliegt und `DOEVENTS` ausgewertet wird - **THEN** läuft der zugehörige Handler, bevor `DOEVENTS` einen Wert liefert #### Scenario: Forms-Timer beendet SLEEP - **WHEN** während SLEEP ein aktiver Forms-Timer fällig wird - **THEN** läuft dessen Handler und SLEEP kehrt danach zurück ### Requirement: Maskierung je Quelle mit drei Zuständen Jede Ereignisquelle SHALL drei Zustände kennen. `ON` stellt Ereignisse zu. `OFF` verwirft sie; ein während `OFF` eingetretenes Ereignis MUST NOT nachträglich zugestellt werden, und ein zuvor unter `STOP` gemerktes Ereignis MUST von `OFF` verworfen werden. `STOP` merkt höchstens ein anstehendes Ereignis und stellt es beim nächsten `ON` zu. Ohne vorangehende `ON …`-Trap-Definition MUST eine Steueranweisung wirkungslos bleiben, aber keinen Fehler auslösen. #### Scenario: OFF verwirft - **WHEN** `TIMER OFF` gilt, die Frist verstreicht und danach `TIMER ON` ausgeführt wird - **THEN** wird kein Handler ausgeführt #### Scenario: STOP merkt genau eines - **WHEN** `KEY(1) STOP` gilt, F1 dreimal gedrückt wird und danach `KEY(1) ON` ausgeführt wird - **THEN** läuft der Handler genau einmal #### Scenario: OFF verwirft das unter STOP Gemerkte - **WHEN** `TIMER STOP` gilt, die Frist verstreicht, danach `TIMER OFF` und dann `TIMER ON` ausgeführt werden - **THEN** läuft kein Handler ### Requirement: EVENT klammert Abschnitte ohne Ereignisprüfung `EVENT OFF` SHALL die Ereignisprüfung für den folgenden Abschnitt abschalten, `EVENT ON` sie wieder einschalten; die beiden klammern einen Abschnitt, in dem keine Ereignisse erkannt werden müssen. Ein dritter Zustand MUST NOT bestehen: `EVENT STOP` ist im Vorbild nicht dokumentiert und MUST namentlich abgewiesen werden. Der Schalter SHALL die Zustände der einzelnen Quellen überlagern, ohne sie zu verändern: nach `EVENT ON` gilt für jede Quelle wieder ihr eigener Zustand. Ereignisse, die während `EVENT OFF` eintreten, MUST NOT nachträglich zugestellt werden. #### Scenario: Klammer überlagert Einzelzustand - **WHEN** `TIMER ON` gilt und danach `EVENT OFF` ausgeführt wird - **THEN** wird kein Zeit-Trap zugestellt, und nach `EVENT ON` wird wieder zugestellt, ohne dass `TIMER ON` erneut nötig ist #### Scenario: EVENT STOP gibt es nicht - **WHEN** ein Modul `EVENT STOP` enthält - **THEN** wird es namentlich abgewiesen ### Requirement: Zustellung als GOSUB ohne Wiedereintritt Ein zugestelltes Ereignis SHALL das `GOSUB`-Ziel seines Traps ausführen und danach an die unterbrochene Stelle zurückkehren; der unterbrochene Zustand (aufrufende Prozedur, Schleifen, Variablen) MUST erhalten bleiben. Beim Eintritt in den Handler SHALL für seine eigene Quelle selbsttätig `STOP` wirken, sodass keine rekursiven Traps entstehen; das `RETURN` SHALL für sie selbsttätig `ON` ausführen — es sei denn, im Handler wurde ausdrücklich `OFF` für diese Quelle ausgeführt, dann bleibt sie aus. Ein `RETURN label` aus einem Handler MUST erlaubt sein. #### Scenario: Rückkehr in die unterbrochene Schleife - **WHEN** ein Trap während einer `FOR`-Schleife zugestellt wird - **THEN** läuft die Schleife nach `RETURN` mit unverändertem Zählerstand weiter #### Scenario: Kein Wiedereintritt - **WHEN** ein Zeit-Trap läuft und die nächste Frist während des Handlers verstreicht - **THEN** wird der Handler nicht erneut betreten, sondern das Ereignis nach `RETURN` einmal zugestellt #### Scenario: OFF im Handler überlebt das RETURN - **WHEN** ein Handler `TIMER OFF` ausführt und danach `RETURN` erreicht - **THEN** bleibt die Quelle aus und kein weiterer Zeit-Trap wird zugestellt ### Requirement: Getrappte Tasten verlassen den Eingabestrom Eine Taste, für die ein aktiver `ON KEY`-Trap besteht, SHALL dem Trap zugestellt werden und MUST NOT zusätzlich über `INKEY$`, `INPUT`, `LINE INPUT` oder `INPUT$` sichtbar werden. Bei `OFF` oder `STOP` der Quelle SHALL die Taste dem normalen Eingabestrom erhalten bleiben. Unter STOP SHALL die Taste normal lesbar bleiben und zugleich höchstens ein Ereignis für das spätere ON gemerkt werden. #### Scenario: Getrappte Taste erscheint nicht bei INKEY$ - **WHEN** `ON KEY(1) GOSUB Marke` mit `KEY(1) ON` gilt und F1 gedrückt wird - **THEN** läuft der Handler und ein anschließendes `INKEY$` liefert den leeren String #### Scenario: Nicht getrappte Taste bleibt im Strom - **WHEN** derselbe Trap mit `KEY(1) OFF` gilt und F1 gedrückt wird - **THEN** liefert `INKEY$` die Sondertastenfolge für F1 #### Scenario: STOP merkt und erhält Eingabe - **WHEN** unter KEY(1) STOP F1 gedrückt, per INKEY$ gelesen und danach KEY(1) ON ausgeführt wird - **THEN** liefert INKEY$ die Sondertastenfolge und der Handler läuft nach ON genau einmal ### Requirement: Benutzerdefiniertes Ereignis `SetUEvent` SHALL ein benutzerdefiniertes Ereignis auslösen, das über `ON UEVENT GOSUB` und die Steueranweisung `UEVENT` denselben Regeln für Maskierung und Zustellung unterliegt wie die übrigen Quellen. #### Scenario: SetUEvent löst den Handler aus - **WHEN** `ON UEVENT GOSUB Marke` mit `UEVENT ON` gilt und `SetUEvent` aufgerufen wird - **THEN** läuft der Handler am nächsten Zustellpunkt genau einmal ### Requirement: Reihenfolge bei mehreren fälligen Ereignissen Sind an einem Zustellpunkt mehrere Ereignisse fällig, SHALL genau eines zugestellt werden; die übrigen bleiben anstehend und werden an folgenden Zustellpunkten zugestellt. Die Reihenfolge SHALL festgelegt und dokumentiert sein, sodass sie über Läufe hinweg gleich bleibt. #### Scenario: Zwei gleichzeitig fällige Traps - **WHEN** an einem Zustellpunkt ein Zeit- und ein Tasten-Trap zugleich fällig sind - **THEN** läuft zuerst genau ein Handler und der andere am nächsten Zustellpunkt ### Requirement: Fehler in einem Handler Ein Laufzeitfehler in einem Trap-Handler SHALL der geltenden Fehlerbehandlung unterliegen. `RESUME` und `RESUME NEXT` SHALL sich auf die Anweisung im Handler beziehen, nicht auf die unterbrochene Anweisung. #### Scenario: Fehler im Handler erreicht den Handler des Moduls - **WHEN** ein Trap-Handler eine Anweisung mit Laufzeitfehler ausführt und ein modulweiter `ON ERROR GOTO`-Handler gesetzt ist - **THEN** wird dieser Handler betreten und `ERL` nennt die Zeile im Trap-Handler ### Requirement: Signal-Trap auf Betriebssystemsignalen `ON SIGNAL(n%) GOSUB` und die Steueranweisung `SIGNAL(n%)` SHALL denselben Regeln für Maskierung, Zustellung und Wiedereintritt unterliegen wie die übrigen Quellen. `n%` SHALL auf Betriebssystemsignale abgebildet werden, und zwar ausschließlich auf die Menge, die auf allen Zielplattformen besteht: 1 auf den Unterbrechungswunsch (`SIGINT`), 2 auf den Beendigungswunsch (`SIGTERM`). Ein anderer Wert MUST namentlich abgewiesen werden — zur Übersetzungszeit, wenn er konstant ist, sonst mit Laufzeitfehler 5. Ein Signal MUST NOT im Signalkontext verarbeitet werden; es SHALL über den Host als Ereignis in die Warteschlange gelangen. Die Abweichung zur OS/2-gebundenen Quelle des Vorbilds SHALL in der Sprachreferenz ausgewiesen sein. #### Scenario: Signal-Trap folgt der Maskierung - **WHEN** `ON SIGNAL(1) GOSUB Marke` mit `SIGNAL(1) STOP` gilt und ein `SIGINT` eintrifft - **THEN** läuft der Handler erst nach `SIGNAL(1) ON`, und zwar genau einmal #### Scenario: Unzulässige Signalnummer - **WHEN** ein Modul `ON SIGNAL(7) GOSUB Marke` enthält - **THEN** wird es namentlich abgewiesen und die Meldung nennt den zulässigen Bereich #### Scenario: Signal ohne Trap bleibt Abbruch - **WHEN** ein `SIGINT` eintrifft, ohne dass ein `SIGNAL(1)`-Trap aktiv ist - **THEN** bleibt es beim bisherigen Abbruchverhalten #### Scenario: SIGTERM wird zugestellt - **WHEN** SIGNAL(2) ON gilt und ein SIGTERM über den Host eintrifft - **THEN** wird das Ziel von ON SIGNAL(2) GOSUB am nächsten erlaubten Zustellpunkt ausgeführt