News   Magazin   Börse   Links   ArcArchie   Homepages
 Magazin  RISC OS & C-Programmierung 9: Symbole und Ereignisbehandlung Home 
Hardware   Software   Praxis   Sonstiges  
RISC OS & C-Programmierung 9: Symbole und Ereignisbehandlung  Alexander Ausserstorfer 7. 5. 2017

Wenn die Wimp Fenster (engl. windows) und Symbole (engl. icons) anlegt, wird immer mit Kopien von Datenblöcken gearbeitet. Das heißt, der Datenblock, mit dem wir ein Fenster und dessen Symbole beschreiben, wird bei der Erzeugung des Fensters von der Wimp kopiert. Den ursprünglichen Datenblock können wir dann löschen. Die Position des Datenblock, den die Wimp für sich selbst zur Beschreibung des Fensters anlegt, ist uns jedoch unbekannt. Die Zustände von Symbolen und damit deren Symbolzeichen (engl. icon flags) können aber durch den Anwender verändert werden. Außerdem möchten wir unter Umständen die Inhalte von Symbolen (Texte oder Sprites) nachträglich ändern. Deshalb ist es wichtig, dass wir bestimmte Informationen von Symbolen abrufen können.

Symbole auslesen

Zum Abrufen von Symbolen stellt uns die Wimp die Funktion

   extern void wimp_get_icon_state (wimp_icon_state *icon_state);
						

zur Verfügung. Mir ihr lassen sich die Zustände von Symbolen (gesetzte Symbolzeichen, engl. icon flags) sowie die Zeigerwerte (Adressen) von indirekt adressierten Symbolinhalten ermitteln. Damit können wir also nachschauen, was wo steht und wie gesetzt ist. Beim Aufruf der Funktion müssen wir einen Zeiger auf einen Datenblock von der Struktur wimp_icon_state mit übergeben. Denn die Wimp liefert uns die Zustände und Adressen des betreffenden Symbols wieder als Kopie zurück, welche dann in diesem Datenblock enthalten sind.

Bei wimp_icon_state handelt es sich um eine Struktur mit den Strukturkomponenten wimp_w w, wimp_i i und wimp_icon icon:

   typedef struct
   {
   wimp_w w;
   wimp_i i;
   wimp_icon icon;
   }
   wimp_icon_state;
						

Die Komponente wimp_w w muss beim Aufruf die Nummer des Fensters enthalten, in welchem das gefragte Symbol vorkommt. Die Nummer beziehungsweise wimp_w w ist vom Datentyp Integer. Es handelt sich also um eine Ganzzahl. Diese Nummer wird bei der Erzeugung des Fensters durch die Wimp vergeben und an uns zurückgegeben. Zum Beispiel geschieht dies in der Zeile

   block.open.w = wimp_create_window (window);
						

Die Nummer des Symbols (engl. icon number) wimp_i i wird bereits während der Erzeugung der Vorlage durch den Vorlageneditor (engl. template editor) vergeben. Man sieht sie bei der im Editor WinEd geöffneten Vorlage im Informationsfenster, wenn man mit dem Mauszeiger über die verschiedenen, bereits im Fenster platzierten Symbole fährt. Enthält der Programmcode selbst den Datenblock für das Symbol, so handelt es sich einfach um die vom Programm zugewiesene Nummer des Symbols, die dem i (für Index) in window->icons[i] entspricht.

In der Strukturkomponente wimp_icon icon muss beim Aufruf nichts weiter stehen. Sie entspricht der bereits in Kursteil sieben behandelten Struktur wimp_icon mit den Unterkomponenten os_box extent, wimp_icon_flags flags und wimp_icon_data data für Symbole. Nach dem Aufruf enthält icon aber sämtliche, aktuell gesetzten Werte wie Symbolzeichen, Symbolinhalte (direkt adressierte Texte und Sprites), Speicheradressen (Zeiger von indirekt adressiertem Sprite und / oder Texte) sowie Position und Lage des abgefragten Symbols auf dem Bildschirm. Es handelt sich dabei wohlgemerkt nur um eine Kopie der tatsächlichen Informationen. Das heißt ein Verändern der Werte und Inhalte in der Strukturkomponente icon von wimp_icon_state bewirkt hier an dieser Stelle nichts. Natürlich aber lassen sich mit Hilfe der entsprechenden Zeiger von indirekt adressierten Texten und Symbole dann diese Texte oder Symbole verändern.

Wollen wir zum Beispiel nachträglich den Text im Symbol mit der Nummer 0 ändern, so könnten wir dies folgendermaßen machen:

   /* Datenblock für Symbol (Auslesen, Zustand) */
      wimp_icon_state icon_state;
      icon_state.w = block.open.w;
      icon_state.i = 0;

   /* Zustand des Symbols[0] auslesen */
      wimp_get_icon_state(&icon_state);

   /* Text des Symbols ändern */
      strncpy(icon_state.icon.data.indirected_text.text,
              "Kernschleife\0",
              strlen(icon_state.icon.data.indirected_text.text)+1);
						

+1 ist notwendig, damit die binäre Null als Endmarke für den Text mitkopiert wird.

Voraussetzung für diese Veränderung des Textes ist natürlich, dass der Text zuvor indirekt mit Hilfe des Zeigers .data.indirected_text.text adressiert wurde. Ein direkt adressierter Text kann nachträglich, das heißt nach der Erzeugung des Symbols, nicht mehr verändert werden. Wir können zwar einen direkt adressierten Text abfragen. In unserem Beispiel würde dieser dann an der Stelle icon_state.icon.data.text stehen. Wir können den Text an dieser Stelle freilich auch ändern. Nur bewirkt das bei unserem Programm nichts, weil es nicht die Stelle im Speicher ist, die von der Wimp für das Symbol verwendet wird. Denn bei icon_state.icon.data.text handelt es sich ja nur um eine Kopie des von der Wimp verwendeten Textes. Wir würden damit eine andere Stelle verändern als jene, die von der Wimp für den Text verwendet wird.

Wichtig an dieser Stelle zu verstehen ist, dass es sich auch bei den Zeigern nur um Kopien handelt. Folglich ändern wir nicht die Zeiger, welche mit Hilfe der Funktion wimp_create_window() erzeugt worden sind, sondern ermitteln mit Hilfe der Funktion wimp_get_icon_state() nur die Position des Textes im Speicher, also die Adressen, auf welche die Zeiger zeigen, und überschreiben dann mit Hilfe der Funktion strncpy() den dort stehenden Text. Ein Ändern der Adresse, auf die der Zeiger icon_state.icon.data.indirected_text.text zeigt, würde auch hier nichts, rein gar nichts bewirken, weil es sich auch hierbei nur um eine Kopie des originalen Zeigers handelt. Die Stelle, wo der von der Wimp verwaltete Zeiger steht, können wir nicht ermitteln. Wir können damit also nur die indirekt adressierten Inhalte überschreiben und verändern.

Beachtet werden muss, dass dabei keine falschen Inhalte überschrieben werden. Normalerweise steht die reservierte Länge eines indirekt adressierten Textes an der Stelle icon[i].data.indirected_text.size. Mehr Bytes dürfen wir nicht überschreiben. Wir haben damit jetzt zwar nachträglich zum Beispiel den Text geändert. Aber die Wimp weiß erst einmal nichts davon und aktualisiert deshalb auch nicht automatisch das Symbol. Das soll heißen, dass wir von der Änderung unter Umständen erst einmal gar nichts sehen werden. Wir müssen der Wimp diese Änderung ausdrücklich mitteilen, damit sie das Symbol auf den neuesten Stand bringt. Und das können wir mit der folgenden Funktion machen.

Symbole beschreiben

Nachträglich ändern lassen sich nur die Symbolzeichen (engl. icon flags) sowie die über Zeiger indirekt adressierten Inhalte Text und Sprite. Für letztere beide benötigt man die Adressen, auf welche die Zeiger zeigen, also deren Inhalte. Um die Inhalte von Zeigern und damit die indirekt adressierten Inhalte eines Symbols zu ermitteln, hat man die im vorangegangenen Abschnitt erwähnte Funktion wimp_get_icon_state() zu verwenden. Allerdings wird damit das betreffende Symbol noch nicht automatisch aktualisiert. Dazu und um die Symbolzeichen (engl. icon flags) selbst zu verändern, müssen wir die Funktion

   extern void wimp_set_icon_state (wimp_w w, wimp_i i,
                                    wimp_icon_flags eor_bits,
                                    wimp_icon_flags clear_bits);
						

verwenden.

Die beiden Komponenten beziehungsweise Parameter wimp_w w und wimp_i i wurden bereits im vorangegangenen Abschnitt Symbole auslesen näher erläutert. Das dort Dargestellte gilt natürlich auch hier. Mit den beiden Parametern wählt man also das Fenster sowie das darin enthaltene Symbol aus, deren Symbolzeichen man nachträglich ändern möchte.

Die beiden Parameter wimp_icon_flags eor_bits und wimp_icon_flags clear_bits bedürfen einer näheren Erläuterung. Grundsätzlich handelt es sich bei beiden Parametern um alle möglichen, wie bereits im Kursteil 7 erwähnten, Symbolzeichen (engl. icon flags) eines Symbols. Ein jeder Parameter repräsentiert damit in Wirklichkeit ein 32-Bit-Wort, dessen einzelne Bits, die in ihren Werten und in ihrer Bedeutung exakt den Symbolzeichen entsprechen, gesetzt oder gelöscht werden müssen. Wie diese einzelnen Bits des 32-Bit-Wortes manipuliert werden können, wurde bereits ausführlich im Kursteil sechs Fenster unter dem Abschnitt Einzelne Bits setzen erläutert.

Es gibt nun insgesamt vier Möglichkeiten, mit einem Symbolzeichen zu verfahren:

wimp_icon_flags eor_bits wimp_icon_flags clear_bits Bedeutung
0 0 Symbolzeichen bleibt unverändert
1 0 Symbolzeichen wird umgeschaltet
0 1 Symbolzeichen wird gelöscht
1 1 Symbolzeichen wird gesetzt

In Worten:

Soll ein Symbolzeichen von dieser Funktion unangerührt bleiben, muss es in beiden Parametern ungesetzt (= 0) sein. Das ist bereits der Fall, wenn wir nichts Näheres in den beiden Parametern angeben, das heißt die entsprechenden Symbolzeichen dort gar nicht erst auftauchen. Falls wir jedoch gar keine Symbolzeichen angeben möchten, muss 0 gesetzt werden (das heißt eor_bits = 0 und clear_bits = 0).

Soll das Symbolzeichen (= Bit) gesetzt, das heißt eins werden, muss es in beiden Parametern gesetzt sein.

Soll das Symbolzeichen umgeschaltet werden, muss das entsprechende Bit in wimp_icon_flags eor_bits gesetzt und das entsprechende Bit in wimp_icon_flags clear_bits gelöscht sein. Umschalten bedeutet, dass das Bit gesetzt wird, wenn es zuvor gelöscht oder nicht gesetzt oder null ist, und dass es gelöscht wird, falls es zuvor bereits schon gesetzt ist.

Das Symbolzeichen wird gelöscht, falls das entsprechende Bit in wimp_icon_flags eor_bits gelöscht und das entsprechende Bit in wimp_icon_flags clear_bits gesetzt ist.

Einige Beispiele verdeutlichen das Gesagte. Möchte man ein Symbol nachträglich ausgrauen, so hat man für beide Parameter

   wimp_ICON_SHADED
						

zu verwenden. Der vollständige Funktionsaufruf würde damit lauten:

   wimp_set_icon_state (w, i, wimp_ICON_SHADED, wimp_ICON_SHADED);
						

Möchte man das Symbol dagegen umdrehen beziehungsweise invertieren, das heißt eine eventuell vorhandene Schattierung aufheben oder eine nicht vorhandene Schattierung setzen, so hat man

   wimp_set_icon_state (w, i, wimp_ICON_SHADED, 0);
						

zu verwenden.

Möchte man mehrere Symbolzeichen eines Symbols gleichzeitig verändern, so hat man wie bereits ausführlich in Teil 6 Fenster unter dem Abschnitt Einzelne Bits setzen genau erläutert zu verfahren. Zur Erinnerung: Mehrere Bits können gleichzeitig mit einem bitweisen | (ODER) gesetzt werden. Möchte man also nachträglich ein Symbol in seiner Schattierung umdrehen und gleichzeitig den Rahmen setzen, wäre zu schreiben:

   wimp_set_icon_state (w, i, wimp_ICON_SHADED | wimp_ICON_BORDER,
                        wimp_ICON_BORDER);
						

Möchte man ein Symbol aktualisieren, weil dessen Inhalte wie Texte oder Sprites geändert worden sind, reicht es, einmal wimp_set_icon_state() ohne eine Veränderung der Symbolzeichen aufzurufen, also zum Beispiel wimp_set_icon_state (w, i, 0, 0). Das entsprechende Symbol wird dann neu gezeichnet.

Die Funktion wimp_poll()

Wimp Ablaufschema Beim Erarbeiten dieses Kapitels hat sich gezeigt, dass es unumgänglich ist, zu behandeln, wann Symbole aktualisiert und verändert werden dürfen.

Sehen wir uns also das Grundgerüst eines kooperativen Multitasking-Programmes, so wie RISC OS es erfordert, noch einmal genauer in der nebenstehenden Grafik an.

Die Programme verfügen stets über eine Hauptschleife. In dieser Hauptschleife gibt es immer eine Funktion namens wimp_poll(). Grundsätzlich hat die Funktion wimp_poll() für uns nur die Bedeutung einer Funktion, welche uns einen ganzen Datenblock mit Parametern beschrieben zurückliefert, der unser Programm betrifft und den wir daher auswerten müssen.

Mit dem Aufruf von wimp_poll() geben wir jedoch auch die Kontrolle über den gesamten Prozessor ans Betriebssystem ab. Wir wecken oder starten damit das Betriebssystem, das anschließend die Kontrolle an weitere, im Multitasking laufende Programme abgibt. Zu einem ganz bestimmten Zeitpunkt arbeitet immer nur ein Programm. Die Wimp führt eine Liste von gestarteten Programmen (Aufgaben genannt, zu englisch tasks), welche im Task Manager (zu deutsch in etwa Aufgabenverwalter) zu sehen ist. Beim kooperativen Multitasking sind alle Programme einschließlich der Wimp gleichberechtigt und starten sich gegenseitig. Zu einem ganz bestimmten Zeitpunkt läuft immer nur ein ganz bestimmtes Programm. Dieses Programm aber, das gerade läuft, bestimmt selbst, wann es die Kontrolle wieder an die Wimp zurückgibt. Dies bedeutet, wenn ein Programm den Rechner belegt, können die anderen Programme nicht arbeiten und damit auch nicht reagieren. Es folgt: Wenn das System sehr flüssig wirken soll, muss der Wechsel zwischen den einzelnen Programmen sehr schnell geschehen.

Wollen wir ein Symbol mit der Funktion wimp_set_icon_state() aktualisieren, so können wir dies natürlich unmittelbar direkt in der Hauptschleife machen. Allerdings bedeutet das auch, dass das Symbol immer dann aktualisiert wird, wenn unser Programm beim nächsten Durchlauf wieder von der Wimp angesprungen wird. Grundsätzlich kann das sehr oft sein. Außerdem kann in diesem Fall das Symbol zu flimmern anfangen und die Aufgabe den Prozessor ziemlich intensiv beschäftigen. Also keine so gute Idee.

Listing 9-1: Fenster Ein Symbol sollte nur dann aktualisiert werden, wenn sich dessen Inhalt verändert hat. Schließlich muss es ja nicht aktualisiert werden, wenn sich nichts geändert hat. Eine Änderung des Symbols kann dann notwendig werden, wenn ein Ereignis aufgetreten ist. Denn dann ist klar, dass sich etwas geändert hat. In diesem Fall kann dann zusammen mit der Ereignisbehandlung auch gleich das Symbol aktualisiert oder verändert werden.

Das Listing 9.1 zeigt ein solches Beispiel. In diesem Programm werden die aktuellen Fenster-Koordinaten in den enthaltenen Symbolen ausgegeben. Nun werden sich diese Werte aber nur dann ändern, wenn das Fenster bewegt oder verschoben wird. Dabei tritt das Ereignis wimp_OPEN_WINDOW_REQUEST auf. Deshalb werden in dem Listing nur dann die Symbole neu gezeichnet, wenn das Fenster verändert wird und damit das Ereignis wimp_OPEN_WINDOW_REQUEST auftritt. Das folgende Listing wie die folgenden Listings kann man herunterladen.

Listing 9-1 - zeigt die aktuellen Koordinaten des eigenen Fensters an

Mit Hilfe eines Programms wie TaskUsage kann man sich ansehen, wie viel Zeit ein bestimmtes Programm den Prozessor belegt. Der Wert sollte grundsätzlich so gering wie möglich sein.

Im Quellcode haben wir außerdem die Pollmaske entsprechend optimiert:

   wimp_poll_flags mask = wimp_MASK_NULL | wimp_QUEUE_REDRAW |
                          wimp_MASK_LEAVING | wimp_MASK_ENTERING |
                          wimp_QUEUE_MOUSE | wimp_QUEUE_KEY |
                          wimp_QUEUE_KEY | wimp_MASK_LOSE |
                          wimp_MASK_GAIN | wimp_MASK_POLLWORD |
                          wimp_MASK_ICON_LEAVING | wimp_MASK_ICON_ENTERING;
						

Die Wimp ignoriert damit unser Programm, wenn eines der in der Programmzeile aufgeführten Ereignisse auftritt.

Die vier Zeilen

   snprintf(window->icons[0].data.indirected_text.text, (size_t)255, "%d",
            block2.open.visible.x0);
   snprintf(window->icons[1].data.indirected_text.text, (size_t)255, "%d",
            block2.open.visible.y0);
   snprintf(window->icons[2].data.indirected_text.text, (size_t)255, "%d",
            block2.open.visible.x1);
   snprintf(window->icons[3].data.indirected_text.text, (size_t)255, "%d",
            block2.open.visible.y1);
						

gilt es noch kurz näher zu erläutern. Die aktuellen Fensterkoordinaten liegen in den Strukturkomponenten block2.open.visible.x0, block2.open.visible.y0, block2.open.visible.x1 und block2.open.visible.y1 im Datentyp Integer vor. Wir benötigen die enthaltenen Werte für die Zeichenausgabe in den Symblen aber anders kodiert - nämlich ASCII-kodiert als Text. Für die Umkodierung können wir die Funktion snprintf() verwenden. Sie arbeitet genauso wie die Funktion printf(), nur dass sie den Text nicht am Bildschirm ausgibt, sondern an die Adresse window->icons[i].data.indirected_text.text im Speicher ablegt.

Das nächste Listing zählt die Aufrufe unserer Aufgabe (Ansprünge) durch die Wimp innerhalb einer Sekunde und zeigt diesen Wert in einem Fenster an. So oft also kommt die Aufgabe innerhalb einer Sekunde unter allen anderen Aufgaben dran.

Listing 9-2: Pollspeed Auf verschieden schnellen Rechnern kann sich der Wert (zum Teil ganz erheblich) unterscheiden. Auf dem Raspberry Pi 2 liegt er zum Beispiel bei bis zu 30.000 Aufrufen pro Sekunde. Ein mit StrongARM betriebener Risc PC wird kaum über 7.000 Aufrufe pro Sekunde hinauskommen. Der Wert ist auch davon abhängig, was andere, gerade laufende Aufgaben zu erledigen haben und wie intensiv sie deshalb den Prozessor in Anspruch nehmen.

Listing 9-2 - zählt die Aufrufe pro Sekunde der Aufgabe durch die Wimp.

Im Listing Pollspeed verwenden wir die Funktion os_read_monotonic_time(), um den Verlauf einer Sekunde mitzuzählen. Diese Funktion liest den Zählerstand einer eingebauten Uhr aus. Genaueres folgt im nächsten Abschnitt. Wir nutzen die Funktion hier auch, um das Symbol nur innerhalb einer Sekunde zu aktualisieren. Denn ein Ereignis, das eine Änderung des Symbols zur Folge hätte und die Wimp kennt, tritt hier nicht auf.

Die Funktion wimp_poll_idle()

Es gibt Fälle, in denen von der Wimp kein Ereignis festgestellt wird, in der Zwischenzeit aber trotzdem eine Änderung stattgefunden hat oder haben kann und deshalb eine Aktualisierung des Symbols vorgenommen werden muss. Ein solcher Fall ist zum Beispiel die Systemuhr, die wir in der Kommandozeile mit *time abrufen können:

   *time
   Tue,03 Jan 2017.18:52:04
   *
						

Wie wir schon erahnen können, ändert sich der Wert jede Sekunde. Folglich müssen wir das Symbol einmal pro Sekunde aktualisieren, wollen wir fortlaufend die Systemuhr mit allen Sekunden ausgeben. Nun kennt die Wimp für eine Änderung der Systemuhr aber kein Ereignis und kann dementsprechend natürlich auch kein solches den Aufgaben melden. Wir müssen daher in solchen Fällen bei der Poll-Maske unbedingt das Bit wimp_MASK_NULL ungesetzt lassen! Denn wenn wir mit mask = wimp_MASK_NULL; dieses Bit bei der Pollmaske setzen, wird die Aufgabe nicht mehr angesprungen, falls kein Ereignis für sie vorliegt. Das ist in dem vorliegenden Fall aber nicht in unserem Sinne.

Wird wimp_MASK_NULL bei der Poll-Maske nicht gesetzt, wird die Aufgabe auch ohne Ereignis von der Wimp angesprungen - und damit können wir das Symbol mit dem aktuellen Inhalt neuzeichnen. Der Haken: Der Aufruf unserer Aufgabe kann je nach Schnelligkeit des verwendeten ARM-Prozessors innerhalb der Zeitspanne von einer Sekunde ziemlich oft und damit unnötig oft erfolgen. Eine sichtbare Änderung der Systemuhr erfolgt aber nur in Zeitabschnitten (Intervallen) von jeweils einer Sekunde.

Um die Aufrufe unserer Aufgabe einzuschränken beziehungsweise zu bremsen, existiert die Funktion wimp_poll_idle(), welche die Funktion wimp_poll() ersetzt und gleichzeitig erweitert. Unser Programm kann damit indirekt eine Zeitspanne vorgeben, die vergehen soll, bis es das nächste Mal wieder von der Wimp aufgerufen wird.

wimp_poll_idle() verlangt einen zusätzlichen Parameter namens os_t t.

   extern wimp_event_no wimp_poll_idle (wimp_poll_flags mask,
                                        wimp_block *block,
                                        os_t t, int *pollword);
						

Wir können hier mit os_t t einen nahzu beliebigen Zeitpunkt angeben, zu dem die Aufgabe das nächste mal wieder von der Wimp angesprungen werden soll. Das kann in dem genannten Beispiel mit der Systemuhr zum Beispiel nach dem Verstreichen von einer Sekunde der Fall sein. Wohlgemerkt müssen wir hier einen Zeitpunkt angeben! Es wäre völlig falsch, hier zum Beispiel eine Sekunde angeben zu wollen.

Ein auf einem ARM-Prozessor basierendes System hat in der Regel gleich mehrere verschiedene Uhren eingebaut. Da ist einmal die Systemuhr, welche die eigentliche Uhrzeit und das Datum enthält. Diese Systemuhr können wir unter RISC OS mit dem Befehl *time abfragen. Zum anderen sind da aber noch einige weitere Uhren oder Zeitmesser eingebaut. Eine davon nennt sich monotonic timer. Diese Uhr startet beim Einschalten des Rechners und kann nicht vom Anwender verändert werden. Sie startet bei Null und erhöht sich innerhalb von 10 Millisekunden, also 1/100 einer Sekunde um genau einen Wert (0, 1, 2, 3, 4, 5 usw.). Es handelt sich also um eine Laufzeituhr. Genau auf diese Uhr greift wimp_poll_idle() mit os_t t zurück.

Wir müssen hier den Wert angeben, welche der monotonic timer haben wird, wenn die Wimp unser Programm wieder anspringen soll. In der Regel geht man aber von einem Zeitabstand aus (zum Beispiel alle x Sekunden). Um jetzt einen genauen Zeitabschnitt, also eine Dauer zwischen zwei Ansprüngen ermitteln zu können, müssen wir dazu erst einmal den aktuellen Wert des monotonic timers kennen. Wir können diesen auslesen:

   #include "oslib/os.h"

   os_t time = 0;
   time = os_read_monotonic_time();
						

Die Funktion os_read_monotonic_time() verlangt beim Aufruf keinen Parameter und gibt den aktuellen Wert der Laufzeituhr im Format os_t zum Zeitpunkt der Abfrage zurück. Wir müssen noch beachten, dass wir bei der Verwendung dieser Funktion die Datei oslib/os.h mit einbinden müssen.

Soll das Programm jetzt erst wieder in einer Sekunde von der Wimp angesprungen werden, so müssen zu time 100 Millisekunden dazugezählt werden (da die Uhr alle 10 Millisekunden beziehungsweise jede Hundertstel- oder Zentisekunde weiterzählt). Ein Beispiel für den Aufruf von wimp_poll_idle() wäre:

   event = wimp_poll_idle(mask, &block2, time+100, NULL);
						

Es sei an dieser Stelle aber schon einmal vorausgeschickt, dass sich die Wimp nicht daran halten wird, wenn ein Ereignis auftritt, welche unsere Aufgabe verlangt. Dies kann zum Beispiel der Fall sein, wenn das Fenster verschoben wird. Dann wird die Aufgabe auch in einer kürzeren Zeit wieder angesprungen.

Die Aufgabe wird immer zum nächst möglichen Zeitpunkt angesprungen. Wenn der monotonic timer gerade bei 1.000 steht und wir diesmal bei wimp_poll_idle() für time den Wert 900 angeben, wird die Aufgabe einfach zum nächst möglichen Zeitpunkt wieder angesprungen, wie wenn die Funktion wimp_poll() statt wimp_poll_idle() verwendet worden wäre. Die Aufgabe kann auch zu einem späteren Zeitpunkt als angegeben angesprungen, nämlich dann wenn die Wimp oder ein anderes Programm gerade mit etwas beschäftigt sind und somit keine Übergabe an unsere Aufgabe erfolgen kann.

Noch ein Wort zum Programmer's Reference Manual. Auf Seite 181 von Teil 3 (PRM3-181) steht unter dem Eintrag Wimp_PollIdle:

   SYS "OS_ReadMonotonicTime" TO newtime
   WHILE (newtime - oldtime) > 0
       oldtime = oldtime +100
   ENDWHILE
   REM Then pass oldtime to Wimp_PollIdle
						

Die Formel (newtime - oldtime) > 0 müsste richtigerweise lauten:

   (newtime - oldtime) >= 0
						

denn sonst würde in dem Fall, dass newtime = oldtime = 0 ist, das Programm so oft unmittelbar, also quasi sofort, wieder angesprungen werden, bis sich die Laufzeituhr um 1 erhöht hat beziehungsweise 10 Millisekunden vergangen sind und damit die Gleichung (newtime - oldtime) > 0 erfüllt ist.

Dieser Fall mit newtime = oldtime = 0 wird auch oft vorkommen. Denn wenn zum Beispiel der Wert von oldtime beim Verlassen der Aufgabe durch wimp_poll_idle() 400 lautet, wird die Aufgabe bei diesem Zählerstand der Laufzeituhr wieder angesprungen werden. Nun wird die Laufzeituhr wieder ausgelesen. Bei heutigen Prozessoren geschehen diese Vorgänge aber inzwischen so schnell, dass sich der monotonic timerin der Zwischenzeit nicht geändert hat. newtime und oldtime sind damit aber gleich. Damit tritt der besagte Fall nach jeder Erhöhung der Laufzeituhr ein, wenn die falsche Formel verwendet wird.

Im folgenden wird mit Listing 9-3 der komplette C-Quellcode für die Ausgabe der Systemuhr in einem Fenster vorgestellt.

Listing 9.3 - zeigt das Datum und die Uhrzeit in einem Fenster an

Im Listing Systemuhr wird der Laufzeitzähler mit der Bedingung case wimp_NULL_REASON_CODE: nur dann ausgelesen, wenn kein Ereignis aufgetreten ist. Würde man newtime = os_read_monotonic_time(); mit

   /* Hauptschleife
      main loop */
    while (!quit_pending)
       {

                newtime = os_read_monotonic_time();
						

Listing 9-3: Systemuhr direkt in die Hauptschleife integrieren, würde sich der in der Zeile event = wimp_poll_idle(mask, &block2, newtime+100, NULL); mit newtime + 100 gegebene neue Ansprungzeitpunkt auch dann ein jedes Mal ändern, wenn zum Beispiel das Fenster der Aufgabe verschoben wird. Dann würde man während der Verschiebung aber keine Änderung der Zeitanzeige mehr sehen.

Noch eine Ergänzung zum Auslesen der Systemuhr, da dies nicht ganz so einfach ist. Die Systemuhr wird hier mit Hilfe der Funktion extern void oswordreadclock_local_string (oswordreadclock_local_string_block *string); ausgelesen. Nun handelt es sich laut dem StrongHelp-Manual von OSLib bei dem Datentyp oswordreadclock_local_string_block *string aber um einen Zeiger auf einen Verbund:

   typedef union
   {
       oswordreadclock_op op;
       struct
       {
         char c[...];
       }
       string;
       }
   oswordreadclock_local_string_block;
						

Beim Aufruf der Funktion muss man

   systemclock->op = oswordreadclock_OP_LOCAL_STRING;
						

gesetzt haben, wenn man die Systemuhr als Zeichenkette zurückgeliefert bekommen haben möchte.

Nun ist es jedoch leider so, dass laut OSLib-Handbuch char c[...] dieses Feld undefiniert ist. Wer in der Datei osword (Pfad: !OSLib.elf-unixlib.oslib.h.osword) von OSLib nachschaut, findet jedoch

   union oswordreadclock_local_string_block
      {  oswordreadclock_op op;
         struct
         {  char c [UNKNOWN];
         }
         string;
      };
						

Bei UNKNOWN handelt es sich um eine Variable, die ebenfalls irgendwo festgelegt worden sein muss. In osword ist jedoch keine entsprechende Festlegung zu finden. Wir können aber im Kopf von osword die Dateien sehen, welche mit eingebunden sind:

   #ifndef types_H
   #include "oslib/types.h"
   #endif

   #ifndef os_H
   #include "oslib/os.h"
   #endif
						

Die eigentliche Festlegung von UNKNOWN finden wir jetzt in der Datei types (Pfad: !OSLib.elf-unixlib.oslib.h.types).

   #define UNKNOWN     1
						

Wenn wir jetzt exakt diesen Verbund mit

   oswordreadclock_local_string_block systemclock;
   ...
   oswordreadclock_local_string (&systemclock);
						

verwenden würden, so hätten wir ein Problem (& liefert die Adresse von systemclock zurück). Die direkte Angabe oswordreadclock_local_string_block systemclock; reserviert nämlich auch gleich den Speicher, die in OSLib unter dieser Funktion so angegeben worden ist. Das heißt für den Verbund würde hier genau ein Byte reserviert (größte Komponente des Verbundes). Nun soll bei uns jedoch systemclock.c die Zeichenkette aufnehmen, welche uns die Funktion oswordreadclock_local_string () unter Angabe von oswordreadclock_OP_LOCAL_STRING zurückliefert:

   Sat,11 Feb 2017.14:29:45
						

Das sind definitiv mehr als nur ein Zeichen und damit auch mehr als nur ein Byte. Da der ASCII-Code verwendet wird gilt hier: 1 Zeichen = 1 Byte. Deshalb gäbe es unter der Verwendung vorgenannter Konstrukte spätestens nach dem Programmstart einen Fehler, da irgendetwas anderes im Speicher überschrieben wird. Es ist ja nicht genug Platz zur Aufnahme der von der Funktion oswordreadclock_local_string () zurückgelieferten Zeichenkette vorhanden.

In OSLib wird [UNKNOWN] verwendet, weil das Ergebnis der Funktion auf vier verschiedene Arten zurückgeliefert werden kann, die sich in der Länge unterscheiden können:

   oswordreadclock_OP_LOCAL_STRING
   oswordreadclock_OP_LOCAL_BCD
   oswordreadclock_OP_CONVERT_BCD_TO_STRING
   oswordreadclock_OP_UTC
						

Bei den anderen drei Ergebnissen handelt es sich jedoch um keine ASCII-Zeichen, also um keine Datentypen vom Format char, weshalb diese dann auch nicht unmittelbar im Text-Symbol darstellbar sind.

Deshalb muss man sich hier um die Speicherreservierung selbst kümmern beziehungsweise tricksen.

Wir könnten richtigerweise den für den verwendeten Zeiger oswordreadclock_local_string_block *systemclock; benötigten Speicher mit Hilfe der Funktion void malloc (size_t size) reservieren.

   oswordreadclock_local_string_block *systemclock;
   systemclock = (oswordreadclock_local_string_block) malloc (<size>);
						

und anschließend noch initialisieren:

   memset (systemclock, 0, <size>);
						

Aber in unserem Falle verwenden wir einen anderen Verbund, in welchem die Zeichenkette char reserved [64] festgelegt ist:

   union osword_block
      {  osword_timer_block timer;
         osword_char_definition_block char_definition;
         osword_palette_block palette;
         osword_cursor_position_block cursor_position;
         oswordreadclock_local_string_block string;
         oswordreadclock_local_bcd_block bcd;
         oswordreadclock_convert_bcd_to_string_block convert_bcd_to_string;
         oswordreadclock_utc_block utc;
         oswordwriteclock_block write_clock;
         oswordpointer_define_block pointer_define;
         oswordpointer_bbox_block pointer_bbox;
         oswordpointer_step_block pointer_step;
         oswordpointer_position_block pointer_position;
         osword_screen_base_block screen_base;
         char reserved [64];
      };
						

osword_block ist groß genug für die Aufnahme der Systemuhr als Zeichenkette. Genauer genommen: eigentlich zu groß. Wir setzen nun systemclock = &oswblk.string;, das heißt systemclock zeigt jetzt auf diesen Verbund. Das ist jedoch ein wenig schräg, weil die Komponente .op in osword_block osblk; normalerweise nicht enthalten ist. Aber mit der Angabe systemclock = &oswblk.string wird die Komponente .op dort ebenfalls hineingeschrieben. Damit hat man dann eigentlich einen Verbund in einer Verbundkomponente erzeugt, die 64 Bytes groß ist. Das ist für unseren Fall mehr als ausreichend.

Listing 9-4: Mauszeiger Listing Mauszeiger verwendet ebenfalls die Funktion wimp_poll_idle(). Es zeigt die aktuellen Koordinaten des Mauszeigers in einem Fenster an. Der Wert für newtime ist hier rein experimentiell dem Befinden nach zu bestimmen. Er kann auch größer oder kleiner gewählt werden und sich dementsprechend die Neuzeichnung des Symbols verlangsamen (kleiner) oder beschleunigen (größer).

Listing 9-4 - zeigt aktuellen Koordinaten des Mauszeigers an

Flackern von Symbolen

Wer aufmerksam beobachtet, der wird hier und da ein Flackern bei der Neuzeichnung von Textsymbolen beobachten können. Leider scheint es so zu sein, dass sich ein Flackern bei der Neuzeichnung von Textsymbolen generell nicht verhindern lässt. Es gibt jedoch andere Möglichkeiten und Wege, etwas flackerfrei auf den Bildschirm zu schreiben. Aber diese sollen auf Grund des Umfanges hier an dieser Stelle nicht behandelt werden.

Auswerten von Mausklicks

Werden Symbole mit der Maus angeklickt, so liefern uns die Funktionen wimp_poll() oder wimp_poll_idle() zuerst einmal das Ereignis wimp_MOUSE_CLICK zurück. Gleichzeitig aber beschreiben besagte Funktionen den Datenblock block, dessen Speicheradresse wir beim Aufruf der Funktionen mit übergeben haben, mit weiteren Informationen über das aufgetretene Ereignis:

    event = wimp_poll(maske, &block, NULL);
         switch (event)
   {
   ...
          case wimp_MOUSE_CLICK:
						

Wir finden nähere Angaben dazu im StrongHelp-Handbuch von OSLib unter unter dem Eintrag wimp -> Types -> wimp_block -> pointer oder natürlich auch in den PRMs.

   typedef struct
      { os_coord pos;
        wimp_mouse_state buttons;
        wimp_w w;
        wimp_i i;
      }
   wimp_pointer;
						

Die Funktion liefert uns also im Datenblock block die genauen Koordinaten des Mauszeigers (os_coord pos) zurück, den Zustand der Maustasten (wimp_mouse_state buttons), ob gedrückt oder nicht gedrückt, und mit wimp_w w die Fenster- und mit wimp_i i die Symbolnummer des angeklickten Symbols. Wollen wir also wissen, welches Symbol angeklickt worden ist, müssen wir nur block.pointer.i beziehungsweise bei mehreren Fenstern auch noch block.pointer.w abfragen.

Listing 9-5: KlickeMich Die Information, ob ein Symbol ausgewählt ist oder nicht, findet sich mit dem Zeichen (Bit) wimp_ICON_SELECTED in den Symbolzeichen wieder. Manchmal gibt es mehrere verschiedene Wege, die gleiche Wirkung zu erzielen. Grundsätzlich sollte man bei der Lösung seiner Aufgabe darauf achten, dass der Prozessor so wenig wie möglich machen muss.

Im folgenden wird das Listing KlickeMich vorgestellt, welches beim Klicken auf eine Schaltfläche beendet wird.

Listing 9-5 - Aufgabe wird beim Klicken auf eine Schaltfläche beendet

Für die Schaltfläche verwenden wir hier ein indirektes Textsymbol mit Umrahmung. Für die Art der Schaltfläche geben wir in den Symbolzeichen wimp_BUTTON_RELEASE an, damit die Aufgabe erst beim Loslassen der Maustaste über dem Symbol reagiert. Für den validation string oder die erlaubte Zeichenkette geben wir den Befehl R6,3 an. Die Beschreibung dazu finden wir im PRM 3, auf Seite 102 (PRM3-102) unter dem Abschnitt Wimp_CreateIcon.

Listing 9-6: Pollmaske Der R-Befehl betrifft immer den Rahmen (englisch boRder) des Symbols. 6 bedeutet, dass die Schaltfläche markiert (in den Farben umgedreht) wird, wenn das Symbol angewählt wird. Mit der 3 erhalten wir die 3D-Darstellung (Rille) des Rahmens.

Listing Pollmaske schließlich reagiert auf das Setzen von Häkchen. Es berechnet damit den Hexdezimalwert, der sich ergibt, wenn die entsprechenden Pollzeichen der Pollmaske gesetzt werden.

Listing 9-6 - berechnet den Wert der Pollmaske bei entsprechend gesetzten Pollzeichen

Die meiste Arbeit besteht hier sicherlich in dem Anlegen der vielen notwendigen Symbole. Dabei haben wir hier noch gar nicht sämtliche existierende Pollzeichen für die Pollmaske berücksichtigt. Die Darstellung von Symbolen und darunter auch die in diesem Listing verwendeten Auswahlschaltern wurden bereits in Kapitel 7 - Symbole behandelt.

Als vorletztes folgt noch Listing Webradio. Dies stellt eine Erweiterung des Listings 7.10 dar. Es prüft, welches Symbol gerade gesetzt ist und passt den Text des Radiosenders dementsprechend an.

Listing 9-7: Webradio Listing 9.7: prüft den gerade gewählten Radioschalter und passt dementsprechend die Textausgabe an

Listing 9.8 bringt eine Laufschrift in einem Fenster. Die Programmierung ist mit Sicherheit ziemlich primitiv und kann noch erheblich verbessert werden. Aber das überlasse ich dem geneigten Leser ;-)

Listing 9-8: Scrolltext

Alle Listings kann man hier herunterladen.

Kommentar zum Artikel, Fehler gefunden, etwas unklar, Ergänzung oder ein Tipp? Einfach eine Nachricht schicken!

Name: *

EMail:

Text: *
Hardware   Software   Praxis   Sonstiges  
ArcSite   News   Magazin   Börse   Links   ArcArchie   Homepages