Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

[OpenGL] Obsługa klawiatury

Dzisiaj chciałbym poruszyć temat możliwych do zarejestrowania funkcji. Dodatkowo zacznę implementować obsługę klawiatury aby można było w łatwy sposób wykryć wciśnięcie kilku klawiszy na raz. Wpis jest pisany w pewnym stopniu bez planu (oczywiście poruszane zagadnienia już w pewien sposób zostały przeze mnie przerobione). Jeżeli więc wpis nie "rozrośnie się" zbytnio to przejdę do tematu obsługi kamery oraz myszki. Zaczynajmy więc!

Słowem wstępu

Po pomyślnej konfiguracji projektu czas przejść do pisania kodu. Jak już wcześniej zostało powiedziane, wszystkie zmiany są zapisywane i umieszczane w repozytorium na github.com. Będę dodawał nowe commity w miarę dodawania nowych funkcji, zmiennych i modyfikacji. Czyli ogólnie pojętej funkcjonalności. W tym oraz w każdym kolejnym wpisie będę podawał linki do poszczególnych commitów abyś mógł w łatwy sposób śledzić postępy.

Funkcje klawiatury

Biblioteka GLUT udostępnia dwie funkcje do naciśnięć klawiszy na klawiaturze glutKeyboardFunc oraz glutSpecialFunc. Natomiast FreeGLUT dodaje kolejne dwie glutKeyboardUpFunc oraz glutSpecialUpFunc do zdarzeń zwolnienia klawisza.

Na samym początku trzeba oczywiście zdefiniować odpowiednie funkcje, oczywiście zgodne z dokumentacją. Niech ich standardową funkcjonalnością będzie wyświetlanie komunikatu o znaku klawisza, którego stan został zmieniony. Jeżeli będą to klawisze specjalne, wyświetlany będzie kod klawisza. Komunikaty będą wyświetlane na standardowym wyjściu log'u clog. Dlatego wymagane jest też dołączenie odpowiedniego nagłówka pozwalającego na to.

Warto już tutaj wspomnieć o tym, że klawisze podstawowe posiadają kod typu unsigned char. Oznacza to, że kody są z zakresu [0;255] i mogą być wyświetlane jako znak ASCII. Natomiast funkcja obsługująca klawisze specjalne rozróżnia je typem wyliczeniowym, który przekazywany jest jako int, zatem wyświetlanie ich jest kłopotliwe z uwagi na zakres typu. Dlatego będziemy wyświetlać tylko ich kod.

Implementacje

Konstrukcja funkcji obsługujących zwykłe klawisze jest prosta. Składa się ze switcha, który w prosty sposób będzie realizował zadanie.

void keyFunc(unsigned char key, int x, int y){ switch(key) { default: std::clog << "Nacisnieo klawisz " << (char)key <<" kod "<< (int)key << "\n"; break; } }

void keyUpFunc(unsigned char key, int x, int y){ switch(key) { default: std::clog << "Zwoniono klawisz " << (char)key <<" kod "<< (int)key << "\n"; break; } }

Aby uzyskać podobną złożoność funkcji dla klawiszy specjalnych zdefiniujemy funkcję pomocniczą przyjmującą w parametrze kod klawisza, a zwracającą ciąg znaków. Pojawia się jednak problem, ponieważ w implementacji FreeGLUT nie doszukałem się zdefiniowania stałej przeznaczonej do używania w funkcji klawiszy specjalnych dla klawiszy CRTL, ALT, i SHIFT zarówno tych z lewej jak i prawej strony klawiatury. Natomiast funkcja normalnych klawiszy obsługuje je lecz nie rozróżnia strony lewej od prawej. Pozostaje więc odpowiednio zmodyfikować poprzednie funkcje aby klawisze były obsługiwane jak najdokładniej lecz nie podwójnie.

std::string getSpecialKeyString(int key){ switch(key){ case GLUT_KEY_F1: return "F1"; case GLUT_KEY_F2: return "F2"; case GLUT_KEY_F3: return "F3"; case GLUT_KEY_F4: return "F4"; case GLUT_KEY_F5: return "F5"; case GLUT_KEY_F6: return "F6"; case GLUT_KEY_F7: return "F7"; case GLUT_KEY_F8: return "F8"; case GLUT_KEY_F9: return "F9"; case GLUT_KEY_F10: return "F10"; case GLUT_KEY_F11: return "F11"; case GLUT_KEY_F12: return "F12"; case GLUT_KEY_PAGE_UP: return "PAGE UP"; case GLUT_KEY_PAGE_DOWN: return "PAGE DOWN"; case GLUT_KEY_HOME: return "HOME"; case GLUT_KEY_END: return "END"; case GLUT_KEY_LEFT: return "LEFT"; case GLUT_KEY_RIGHT: return "RIGHT"; case GLUT_KEY_UP: return "UP"; case GLUT_KEY_DOWN: return "DOWN"; case GLUT_KEY_INSERT: return "INSERT"; case 0x70: return "LEFT SHIFT"; case 0x71: return "RIGHT SHIFT"; case 0x72: return "LEFT CTRL"; case 0x73: return "RIGHT CTRL"; case 0x74: return "LEFT ALT"; case 0x75: return "RIGHT ALT"; default: return "UNKNOWN"; } }

Funkcje przygotowane do zarejestrowania będą miały następujące postaci

void specialKey(int key, int x, int y){ std::string _key = getSpecialKeyString(key); std::clog << "Nacisnieo klawisz " << _key.c_str() <<" kod "<< key << "\n"; }

void specialKeyUp(int key, int x, int y){ std::string _key = getSpecialKeyString(key); std::clog << "Zwolniono klawisz " << _key.c_str() <<" kod "<< key << "\n"; }

Teraz pozostaje podpiąć je do odpowiednich zdarzeń GLUT w funkcji main bądź init.

glutKeyboardFunc(keyFunc); glutKeyboardUpFunc(keyUpFunc); glutSpecialFunc(specialKey); glutSpecialUpFunc(specialKeyUp);

Wciśnij więcej niż 2

Pozostała jedna rzecz. Mianowicie detekcja wciśnięcia kilku klawiszy jednocześnie. W tym celu posłużymy się dwoma buforami. Będzie w nich przechowywany stan poszczególnych klawiszy oraz kombinacji klawiszy. Będzie odbywało się to poprzez zmianę flagi (wartości bool) w odpowiedniej tablicy, oczywiście utworzonej przez nas.

bool* keys = new bool[255](); bool* specialKeys = new bool[128]();

Pierwsza tablica odpowiada normalnym klawiszom alfanumerycznym, druga natomiast klawiszom specjalnym. Rozmiar pierwszej tablicy nie powinien być zagadką. Zgodnie z tym co wyświetla się w oknie konsoli programu mamy nie więcej niż 128 klawiszy specjalnych, więc ta liczba wydaje się odpowiednią. Warto dodać, że tablice są od razu wypełniane wartością false. Zostaje teraz dodanie kilku linijek kody w celu zapalania (wartość true) i gaszenia (wartość false) wybranych flag w tablicach oraz oczywiście sprawdzenie, czy to rozwiązanie działa. Zalecam wybranie dowolnej kombinacji trzech klawiszy i wyświetlenie trójkąta tylko wtedy gdy są one jednocześnie wciśnięte, ja wybrałem klawisze LCTRL + LALT + LSHIFT. Po wykryciu wciśnięcia klawisza dobrze jest zarządać odświerzenia okna, można to uzyskać za pomoca funkcji glutPostRedisplay(). Dla ułatwienia rozróżniania tych klawiszy dodałem dodatkowe definicje dla compilatora.

#define GLUT_KEY_LSHIFT 0x0070 #define GLUT_KEY_RSHIFT 0x0071 #define GLUT_KEY_LCTRL 0x0072 #define GLUT_KEY_RCTRL 0x0073 #define GLUT_KEY_LALT 0x0074 #define GLUT_KEY_RALT 0x0075

Na tym zakończę implementowanie obsługi klawiatury. Na koniec dodam, że możliwe jest zdefiniowane kilku takich funkcji o odpowiednio podpinanie ich do GLUT'a w czasie wykonywania programu. Zademonstruję to w którymś z najbliższych wpisów. Jak zwykle zachęcam do zostawiania komentarzy. Finalny kod z dzisiejszego wpisu znajdziecie w tym commicie.

Ciao :) 

porady programowanie

Komentarze

0 nowych
  #1 09.07.2012 20:28

Używanie gołych wskaźników w C++ w taki sposób powinno być karalne. Jeśli przy drugiej alokacji wyskoczy wyjątek, to pamięć zaalokowana pod pierwszy wycieknie. Pisanie kodu odpornego na wyjątki to podstawa w C++, w którym odśmieciarki nie ma. Użyj zautomatyzowanej tablicy w rodzaju std::vector, albo boost:array.

vircung   5 #2 17.07.2012 22:58

Dzięki za dobrą sugestię :) Wszyscy cały czas się uczymy!

nintyfan   10 #3 12.08.2012 15:27

Pomińmy to, że OpenGL nie ma obsługi klawiatury.

  #4 12.01.2013 11:44

I to zadziała?? FreeGLUT nie obsługuje wciśnięcia klawiszy shift, alt i ctrl. Co prawda od pewnego czasu we FreeGLUT-cie została wprowadzona obsługa shift alt i ctrl, ale wykrywane jest tylko ich puszczenie, klawisz shift ma tam kod 0x0010 czyli 16. Czyli lipa ogólnie :/

vircung   5 #5 17.02.2013 13:58

Powiem Ci że to działa. Może nie do końca tak jak powinno ale działa :) Prawdziwym rozwiązaniem jest zastosowanie biblioteki, która obsługuje urządzenia wejścia w sposób poprawny, np. SDL.