Inhaltsverzeichnis

Die Tastatur

So. Nachdem wir uns nun ausführlich das Bitmap, angeschaut haben (und nun auch wissen wie man darauf einen Text ausgibt), wenden wir uns etwas ähnlich wichtigem zu.

Nämlich der Tastatur. Allegro unterstützt das Abfragen der Tastatur, und wie genau das funktioniert werden wir uns jetzt anschauen. Startet also wieder ein neues Projekt.

Die Tastatur initialisieren

Da wir Allegro initialisieren müssen, um Zugriff auf seine meisten Funktionen zu erhalten, sollte es nicht wundern, dass wir auch das Keyboard initialisieren müssen. Das ganze passiert mit der folgenden Zeile:

install_keyboard();

Wenn wir dieses Stückchen Code ausführen kann Allegro zwar auf die Tastatur zugreifen, blockiert sie dafür aber auch. D.h. keine andere Api kann gleichzeitig genutzt werden um auf die Tastatur zuzugreifen (innerhalb des Programms natürlich nur). Das alleine stellt übrigens noch nicht sicher, dass es geht, manchmal muss auch erst ein gfx mode gesetzt werden, bevor die Tastatur einsatzbereit ist.

Einfachste Tastaturzugriffe

So nachdem wir jetzt also Allegro auf die Tastatur angesetzt haben sollten wir vielleicht mal versuchen, die ein oder andere Eingabe anzunehmen. Wir fangen ganz einfach an:

#include <AllegroW/Allegro.hpp>
 
int main(){
    int k = 0;
 
    allg::initialisieren(1024, 768);
    install_keyboard();
 
    while(k != 'q'){
        if(keypressed()){
            k = readkey() & 0xFF;
            textprintf_ex(screen, font, 10, 10, makecol(255, 255, 255),
                    -1, "du hast %c gedrueckt", k );
            blit(screen, screen, 0, 0, 0, 10, SCREEN_W, SCREEN_H);
        }
    }
 
    return 0;
 
}
END_OF_MAIN()

So das ist jetzt ein ziemlich einfacher Code. Ich erkläre wieder die neuen Funktionen:

keypressed()

keypressed() sagt nur, ob eine Info im Keyboardbuffer wartet, also ob ein Knopf gedrückt wurde. Allerdings könnte man auch einmal einen drücken, und dann beliebig oft keypressed() aufrufen, und es würde immer wieder true zurückgeben, bis der Puffer leer ist, logisch oder?

readkey()

Gibt einen Integer zurück mit dem ASCII-Code des letzten Button im Keyboard-Buffer im niederwertigsten Byte und dem Scancode 1) des Buttons im höherwertigen Byte. Um also an den ASCII-Code zu gelangen müssen wir das letzte Byte herausfiltern, was wir mit der binären Addition von '0xff' erreichen.

Das Blit verschiebt den gesamten Bildschirm um 10 Pixel nach unten, es ist also eine scrolling Funktion.

Der Keyboard Handler

Was wir jetzt oben gesehen haben, ist ja ganz nett, aber es ist doch auch ein Bisschen unpraktisch, deshalb gibt es unter Allegro etwas, das nennt sich Keyboard-Handler, wie das funktioniert zeige ich euch jetzt, außerdem hat das Folgende Programm den Vorteil, dass es gleich auch mal dazu taugt, die Scancodes der einzelnen Buchstaben anzuzeigen. Ihr drückt einen Knopf und es wird der Scancode angezeigt (übrigens ein C-Prgramm).

#include <Allegro.h>
#include <stdio.h>
#include <stdlib.h>
 
int scode = 0;
 
void Handler(int scancode){
    if(scancode & 0x80){
        scode = scancode & 0x7f;
        blit(screen, screen, 0, 0, 0, 10, SCREEN_W, SCREEN_H);
         textprintf_ex(screen, font, 10, 10, 0xFFFFFF,
                    -1, "Taste: %s | ASCII-Wert: %i | Scancode: %i",
                     scancode_to_name(scode), scancode_to_ascii(scode), scode);
 
    }
 
}END_OF_FUNCTION(Handler)
 
 
int main()
{
    allegro_init();
    set_gfx_mode(GFX_AUTODETECT, 1024, 768, 0, 0);
    install_keyboard();
    LOCK_FUNCTION(Handler);
    keyboard_lowlevel_callback = Handler;
 
    while((readkey() & 0xFF) != 27) _sleep(50);
 
    return 0;
}END_OF_MAIN()

Was ist ein Handler

Kommen wir zuerst zur Handler Funktion. Diese Funktion wird jedes mal als Interrupt aufgerufen, wenn eine Taste gedrückt oder Losgelassen wird. Sie ist der Handler, sie handlet die Tastendrücke. Das sie als Interrupt aufgerufen wird bedeutet, dass der Rest des Codes angehalten wird um diese Funktion aufzurufen, so im Gegensatz zu parallel laufenden Funktionen. Zuerst wird geprüft, ob die Taste gedrückt oder losgelassen wurde, und dann wird, falls sie losgelassen wurde, angezeigt, welche gedrückt wurde. Weil der Handler ein Interrupt ist, muss auch ein END_OF_FUNCTION, sowie in der main ein LOCK_FUNCTION aufgerufen werden, das hat was damit zu tun, dass der Code irgendwie im Speicher fest gemacht werden muss, oder so ähnlich… :?: Also ich weiß es nicht, aber Fakt ist, dass man es braucht ;-) FIXME

keyboard_lowlevel_callback

Damit Allegro weiß, welche Funktion es aufrufen soll, wenn eine Taste gedrückt wird, müssen wir es ihm mitteilen, und das geht mit keyboard_lowlevel_callback. keyboard_lowlevel_callback ist ein Funktionszeiger, der auf eine beliebige Funktion zeigen kann, die void als return type hat, und ein int als Parameter nimmt, das int ist dann die Info über die Taste. Es gibt auch noch ein keyboard_callback, wenn man diesen Zeiger nutzt, dann wird direkt der ScanCode übergeben, ohne die Info ob die Taste gedrückt, oder losgelassen wurde, deshalb werden wir immer die lowlevel-Version benutzen.

Was es sonst noch zu sagen gibt

Die Endbedingung für die while-Schleife ist übrigens auf deutsch: „Wenn es ESC gedrückt wird ;-)“. Prinzipiell gibt es noch eine Menge anderer Funktionen die einem Infos anzeigen, oder über die man die LEDs steuern kann, oder, oder, oder… Nachlesen könnt ihr sie … Ihr wisst schon wo :-)

Keyboard Wrapper

Eigentlich kann hier nicht wirklich von einem Wrapper gesprochen werden, da wir dieses mal keine Klasse schreiben, sondern nur ein Set aus zusammenhängenden Funktionen. Das ganze sieht so aus, dass wir 2 Verschiedene Handler-Funktionen schreiben werden, eine die es Registriert, wenn die Taste gedrückt wird, und registriert, wenn sie losgelassen wird, diesen Handler braucht man dann, um während eines Spiels das Bewegen der Figuren oder so etwas zu machen. Und der 2te Handler reagiert nur, wenn die Taste gedrückt wird, und eignet sich somit für ein Menü, oder ähnliche Sachen.

Hier zuerst der Komplette Quellcode, und danach die Erklärungen, das ist für euch bequemer zum Kopieren ;-)

AllegroW\header\AllegKeyboard.h

//AllegKey
 
#ifndef ALLEGKEY
    #define ALLEGKEY
 
namespace kbd{
    bool Taste_gedrueckt_char(char *Taste);
    bool Taste_gedrueckt_char_menue(char *Taste);
    bool Taste_gedrueckt(int Code);
    bool Taste_gedrueckt_menue(int Code);
    void keypress_handler(int scancode);
    void keypress_handler_no_key_up(int scancode);
    void tasten_leeren();
}
 
 
 
#endif

AllegroW/SRC/AllegKeyboard.cpp

//AllegKeyboard.cpp
#include "../header/AllegKeyboard.h"
#include <allegro.h>
#include <string.h>
 
 
int Tasten[KEY_MAX];
 
 
 
 
void kbd::keypress_handler(int scancode)
{
    int i, losgelassen ;
 
    i = scancode & 0x7f;
    losgelassen = scancode & 0x80;
 
    if ( losgelassen ){
        Tasten[i] = 0;
    }
    else{
        Tasten[i] = 1;
    }
 
 
}
END_OF_FUNCTION(keypress_handler)
 
void kbd::keypress_handler_no_key_up(int scancode)
{
    int i, losgelassen ;
 
    i = scancode & 0x7f;
    losgelassen = scancode & 0x80;
 
    if ( !losgelassen ){
        Tasten[i] = 1;
    }
 
 
}
END_OF_FUNCTION(keypress_handler_no_key_up)
 
 
bool kbd::Taste_gedrueckt_char(char *Taste){
    int i = 0;
 
    char ausgabe[1000];
 
 
    for(  i=0;i<KEY_MAX;i++ ){
          if( Tasten[i] ){
              if( !strcmp( scancode_to_name(i), Taste ) ){
                    return true;
              }
          }
    }
    return false;
}
 
bool kbd::Taste_gedrueckt_char_menue(char *Taste){
    int i = 0;
 
    char ausgabe[1000];
 
 
    for(  i=0;i<KEY_MAX;i++ ){
          if( Tasten[i] ){
              if( !strcmp( scancode_to_name(i), Taste ) ){
                    Tasten[i] = 0;
                    return true;
              }
          }
    }
    return false;
}
 
bool kbd::Taste_gedrueckt(int Code){
    return (bool)Tasten[Code];
}
 
bool kbd::Taste_gedrueckt_menue(int Code){
    bool tmp = (bool)Tasten[Code];
    Tasten[Code] = 0;
    return tmp;
}
 
void kbd::tasten_leeren(){
    memset(Tasten, 0, sizeof(Tasten));
}

So jetzt zur Erklärung. Es gibt zu jedem Handler 2 Funktionen, die eine gibt die Info aus, ob die Taste gedrückt wurde anhand des Namen, und die Andere nimmt den Scancode, denkt dran, die Infos welche Codes und Namen zu welcher Taste gehören gibt es aus dem Programm weiter oben. Der Grund, warum wir unterschiedliche Abfragen für die Handler brauchen ist, dass bei einem Handler nicht selber aus dem Array gelöscht wird, sondern erst, wenn die Taste abgefragt wurde. Das tasten_leeren() ist dazu da, um die Sachen zu resetten, das braucht man auch ab und zu, z.B. sollte man tunlichst den Keyboradbuffer leeren, bevor man etwas anzeigt wie „drücken sie eine beliebige Taste“.

Wir können jetzt also sobald wir unseren Handler installiert haben per kbd::taste_gedruekt() oder eine der Anderen Funktionen überprüfen, was aktuell runtergedrückt ist, oder was gedrückt wurde, je nach Situation.

Anpassen der initialisieren Funktion

Nun wollen wir das ganze gleich noch in unsere Init-Funktion reinpacken, dann können wir das in Zukunft nutzen, ohne dabei Stress zu haben. Verändert die Funktion initialisieren in der Functions.cpp, sodass sie so aussieht:

#include "../Allegro.hpp"
 
#include <sstream>
#include <ctype.h>
 
using namespace allg;
 
const char* UppWord(const char* Word);
 
//------------------------------------------------------------------------------
void allg::initialisieren (int W, int H){
    //Allegro einschalten
    if (allegro_init() != 0){
        exit(1);
    }
 
    //Grafik initialisieren
    set_color_depth(32);
    if (set_gfx_mode(GFX_AUTODETECT, W, H, 0, 0) != 0) {
        set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
        allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
        _sleep(5000);
   }
 
 
 
 
     /* install a digital sound driver */
    if (install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL) != 0) {
        set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
        allegro_message("Error initialising sound driver\n%s\n",
              allegro_error);
        exit(1);
    }
 
    //install Keyboard
    install_keyboard();
    LOCK_FUNCTION(kbd::keypress_handler_no_key_up);
    keyboard_lowlevel_callback = kbd::keypress_handler_no_key_up;
 
}
//--------------------------------------------------------------------
const char* UppWord(const char* Word){
 
    std::string Temp;
    Temp.clear();
 
    for(int i = 0;i<strlen(Word);i++){
        if(!isupper(Temp[i]) && Temp[i] != 32){
            Temp.push_back(toupper(Word[i]));
        }else{
            Temp.push_back(Word[i]);
        }
    }
    return Temp.c_str();
}
//------------------------------------------------------------------------------
BITMAP *allg::createCircle(int Durchmesser, int ColorF, int ColorB){
    BITMAP *retB = create_bitmap(Durchmesser, Durchmesser);
    clear_to_color(retB, ColorB);
    circlefill(retB, Durchmesser/2, Durchmesser/2, Durchmesser/2, ColorF);
    return retB;
}
//------------------------------------------------------------------------------
BITMAP *allg::createRectangle(int w, int h, int Color){
    BITMAP *retB = create_bitmap(w, h);
    clear_to_color(retB, makecol(255,0,255));
    rectfill(retB, 0, 0, w, h, Color);
    return retB;
}

Ich weiß, dass die jetzt sehr stark angewachsen ist, aber ich habe keine Lust, auf meine Wrapper zu verzichten, weil dieser Teil noch nicht angepasst ist, deshalb übernehmt es einfach so, und ich erkläre es bei der Gelegenheit, wo wir es dann brauchen werden. Der Relevante Teil der jetzt dazu gekommen ist, ist folgender:

    //install Keyboard
    install_keyboard();
    LOCK_FUNCTION(kbd::keypress_handler_no_key_up);
    keyboard_lowlevel_callback = kbd::keypress_handler_no_key_up;

Aber der sollte keiner Erklärung mehr bedürfen.

Und weiter gehts

1)
Interner Code einer Taste, der auf verschiedener Hardware auch unterschiedlich sein kann.