Serwis Edukacyjny Nauczycieli w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2024 mgr Jerzy Wałaszek
|
SPIS TREŚCI |
Podrozdziały |
Współczesne systemy operacyjne są wielowątkowe i wykonują różne działania w tle. SDL w tle aplikacji użytkownika zbiera informacje o zaistniałych zdarzeniach i umieszcza je w tzw. kolejce zdarzeń (ang. event queue), z której aplikacja może je sobie pobierać w dowolnej chwili. Zdarzenia są tworzone przez różne urządzenia, z którymi współpracuje aplikacja (klawiatura, myszka, joystick, sterownik gier, itp.), przez system operacyjny (zdarzenia związane z obsługą okien) lub przez samą aplikację (tzw. zdarzenia użytkownika).
Zbieranie zdarzeń i umieszczanie ich w kolejce zdarzeń jest w SDL niezależne od platformy (odbywa się poza aplikacją użytkownika) i jest wykonywane przez podsystem obsługi zdarzeń (ang. event subsystem), który jest inicjalizowany przy inicjalizacji podsystemu wideo przez wywołanie funkcji:
SDL_Init(SDL_INIT_VIDEO);
O podsystemach SDL i ich inicjalizacji pomówimy w osobnym rozdziale.
Dużo zdarzeń jest wspólnych dla różnych platform i są one w SDL obsługiwane w bardzo podobny sposób. Jednak niektóre zdarzenia są specyficzne dla systemu operacyjnego, w którym pracuje aplikacja, np. zdarzenia związane z Windows.
Obsługa zdarzeń pozwala twojej aplikacji na współpracę z użytkownikiem. W dotychczasowych programach korzystaliśmy z tej funkcji w ograniczonym zakresie przy oczekiwaniu na zamknięcie okienka aplikacji. Oczywiście możliwości są dużo większe, co zaraz zobaczysz.
Wszystkie struktury zdarzeń posiadają jedno pole wspólne, które określa rodzaj struktury i jednocześnie rodzaj opisanego przez daną strukturę zdarzenia. Pole ma nazwę type. W zależności od zawartości tego pola unia SDL_Event zawiera odpowiednią strukturę. Poniżej w tabelce zebraliśmy wszystkie struktury zdarzeń, które mogą pojawić się w unii SDL_Event:
Typ struktury | Nazwa pola | Co zawiera |
SDL_CommonEvent | common | dane wspólne. |
SDL_WindowEvent | window | dane zdarzenia okna. |
SDL_KeyboardEvent | key | dane zdarzenia klawiatury. |
SDL_TextEditingEvent | edit | dane zdarzenia edycji. |
SDL_TextInputEvent | text | dane zdarzenia wprowadzania tekstu. |
SDL_MouseMotionEvent | motion | dane zdarzenia ruchu myszki. |
SDL_MouseButtonEvent | button | dane zdarzenia przycisku myszki. |
SDL_MouseWheelEvent | wheel | dane zdarzenia kółka myszki. |
SDL_JoyAxisEvent | jaxis | dane zdarzenia ruchu joysticka. |
SDL_JoyBallEvent | jball | dane zdarzenia ruchu kulką joysticka. |
SDL_JoyHatEvent | jhat | dane zdarzenia ruchu kursorem joysticka. |
SDL_JoyButtonEvent | jbutton | dane zdarzenia przycisku joysticka. |
SDL_JoyDeviceEvent | jdevice | dane zdarzenia urządzenia joysticka. |
SDL_ControllerAxisEvent | caxis | dane zdarzenia ruchu manetką kontrolera gier. |
SDL_ControllerButtonEvent | cbutton | dane zdarzenia przycisku kontrolera gier. |
SDL_ControllerDeviceEvent | cdevice | dane zdarzenia urządzenia kontrolera gier. |
SDL_AudioDeviceEvent | adevice | dane zdarzenia urządzenia audio (>= SDL 2.0.4). |
SDL_QuitEvent | quit | dane zdarzenia zakończenia aplikacji. |
SDL_UserEvent | user | dane zdarzenia zdefiniowanego przez użytkownika. |
SDL_SysWMEvent | syswm | dane zdarzenia systemowego okna. |
SDL_TouchFingerEvent | tfinger | dane zdarzenia dotknięcia palcem. |
SDL_MultiGestureEvent | mgesture | dane gestu wielopalcowego. |
SDL_DollarGestureEvent | dgesture | dane gestu wielopalcowego. |
SDL_DropEvent | drop | dane zdarzenia przeciągania i upuszczania. |
Struktury te zajmują w unii SDL_Event tę samą przestrzeń, jednakże w danej chwili unia przechowuje tylko jedną z nich. Dostęp do pól struktur masz poprzez nazwę pola unii, które zawiera daną strukturę. Zobaczmy jak to działa na prostym przykładzie.
Załóżmy, że zdefiniowałeś sobie unię SDL_Event i nadałeś jej nazwę e:
SDL_Event e;
Następnie SDL wypełniło tę unię zdarzeniem ruchu myszki, czyli unia e przechowuje teraz strukturę typu SDL_MouseMotionEvent, która posiada następujące pola danych:
Uint32 | type | rodzaj zdarzenia; SDL_MOUSEMOTION. |
Uint32 | timestamp | czas zdarzenia. |
Uint32 | windowID | okno ze skupieniem myszki, jeśli istnieje. |
Uint32 | which | identyfikator myszki lub SDL_TOUCH_MOUSEID |
Uint32 | state | stan przycisku |
Sint32 | x | współrzędna X względem okna. |
Sint32 | y | współrzędna Y względem okna. |
Sint32 | xrel | względny ruch w osi X. |
Sint32 | yrel | względny ruch w osi Y. |
Pole type jest wspólne dla wszystkich struktur i zawiera stałą określającą rodzaj zdarzenia. Dostęp do tego pola masz bezpośrednio poprzez unię: e.type. Załóżmy, że chcemy odczytać pozycję kursora myszki, którą SDL umieszcza w polach x i y tej struktury. Do danych tych musisz odwołać się kolejno poprzez nazwę unii (e), pole unii zawierające strukturę typu SDL_MouseMotionEvent (motion) i pola danych struktury zdarzenia (x i y):
e.motion.x; // dostęp do pozycji x e.motion.y; // dostęp do pozycji y
Zasada ta obowiązuje dla wszystkich pól struktur zdarzeń w SDL. Jedynym wyjątkiem jest pole type, które również jest samodzielnym polem unii SDL_Event, zatem można się do niego odwoływać jako e.type, ale możesz również odwołać się jako e.motion.type, ponieważ pole to znajduje się w tym samym miejscu we wszystkich strukturach zdarzeń. Musisz to dobrze zrozumieć.
Gdy poprosisz SDL o pobranie zdarzenia z kolejki, przekazujesz unię SDL_Event. W odpowiedzi SDL wypełnia tę unię odpowiednią strukturą zdarzenia, która jest pobierana z wewnętrznej kolejki zdarzeń. W polu type SDL umieszcza stałą identyfikującą pobrane zdarzenie oraz określa rodzaj struktury zdarzenia umieszczonej w unii SDL_Event. Stałe zdarzeń są następujące:
Rodzaj zdarzenia | Struktura zdarzenia | Pole unii SDL_Event |
SDL_AUDIODEVICEADDED SDL_AUDIODEVICEREMOVED |
SDL_AudioDeviceEvent | adevice |
SDL_CONTROLLERAXISMOTION | SDL_ControllerAxisEvent | caxis |
SDL_CONTROLLERBUTTONDOWN SDL_CONTROLLERBUTTONUP |
SDL_ControllerButtonEvent | cbutton |
SDL_CONTROLLERDEVICEADDED SDL_CONTROLLERDEVICEREMOVED SDL_CONTROLLERDEVICEREMAPPED |
SDL_ControllerDeviceEvent | cdevice |
SDL_DOLLARGESTURE SDL_DOLLARRECORD |
SDL_DollarGestureEvent | dgesture |
SDL_DROPFILE SDL_DROPTEXT SDL_DROPBEGIN SDL_DROPCOMPLETE |
SDL_DropEvent | drop |
SDL_FINGERMOTION SDL_FINGERDOWN SDL_FINGERUP |
SDL_TouchFingerEvent | tfinger |
SDL_KEYDOWN SDL_KEYUP |
SDL_KeyboardEvent | key |
SDL_JOYAXISMOTION | SDL_JoyAxisEvent | jaxis |
SDL_JOYBALLMOTION | SDL_JoyBallEvent | jball |
SDL_JOYHATMOTION | SDL_JoyHatEvent | jhat |
SDL_JOYBUTTONDOWN SDL_JOYBUTTONUP |
SDL_JoyButtonEvent | jbutton |
SDL_JOYDEVICEADDED SDL_JOYDEVICEREMOVED |
SDL_JoyDeviceEvent | jdevice |
SDL_MOUSEMOTION | SDL_MouseMotionEvent | motion |
SDL_MOUSEBUTTONDOWN SDL_MOUSEBUTTONUP |
SDL_MouseButtonEvent | button |
SDL_MOUSEWHEEL | SDL_MouseWheelEvent | wheel |
SDL_MULTIGESTURE | SDL_MultiGestureEvent | mgesture |
SDL_QUIT | SDL_QuitEvent | quit |
SDL_SYSWMEVENT | SDL_SysWMEvent | syswm |
SDL_TEXTEDITING | SDL_TextEditingEvent | edit |
SDL_TEXTINPUT | SDL_TextInputEvent | text |
SDL_USEREVENT | SDL_UserEvent | user |
SDL_WINDOWEVENT | SDL_WindowEvent | window |
Niektóre struktury są wykorzystywane przez kilka podobnych zdarzeń. Na przykład zdarzenia SDL_MOUSEBUTTONDOWN i SDL_MOUSEBUTTONUP. Pierwsze powstaje, gdy użytkownik nacisnął jeden z przycisków myszki (przycisk w dół, ang. down). Drugie powstaje, gdy użytkownik zwolnił naciśnięty przycisk myszki (przycisk w górę, ang. up). W obu przypadkach wykorzystywana jest ta sama struktura typu SDL_MouseButtonEvent, a dostęp do jej pól w unii SDL_Event następuje poprzez pole button, w którym ta struktura zostaje umieszczona.
Wynika z tego następujący sposób obsługi zdarzeń:
// Definiujemy unię SDL_Event SDL_Event e; ... // Każemy SDL pobrać zdarzenie z kolejki do unii e ... // Przetwarzamy pobrane zdarzenie switch(e.type) { ZDARZENIE1: ...; // obsługujemy zdarzenia break; ZDARZENIE2: ...; break ... }
Wszystko stanie się jasne dalej w przykładach konkretnych programów
SDL_WaitEvent(e) – czeka na
zdarzenie i pobiera je do unii e.
e – wskaźnik unii
SDL_Event, w której będzie umieszczone pobrane zdarzenie
W trakcie czekania aplikacja użytkownika zostaje wstrzymana.
SDL_WaitEventTimeout(e,t) – czeka zadany czas na dostępne
zdarzenie, po czym pobiera je do unii e.
e – wskaźnik unii SDL_Event, w
której będzie umieszczone pobrane zdarzenie
t – czas oczekiwania w
milisekundach
Funkcja zwraca 1, jeśli w czasie t pojawiło się zdarzenie w kolejce. W tym przypadku zdarzenie zostaje pobrane z kolejki i umieszczone w unii e. Jeśli czas oczekiwania upłynął bez zdarzenia, funkcja zwraca 0, a w unii e nic nie jest umieszczane. W trakcie czekania aplikacja użytkownika zostaje wstrzymana.
SDL_PollEvent(e) – Jeśli w kolejce jest zdarzenie, to je pobiera
do unii e, jeśli nie ma, to natychmiast wraca.
e – wskaźnik unii
SDL_Event, w której będzie umieszczone pobrane zdarzenie
Funkcja zwraca 1, jeśli pobrała zdarzenie z kolejki, lub 0, jeśli kolejka zdarzeń była pusta – w takim przypadku unia e nie jest zmieniana.
Funkcji takich jest więcej w SDL, lecz na razie wystarczą nam te trzy najważniejsze. Dwie pierwsze funkcje czekają aż w kolejce pojawi się jakieś zdarzenie, po czym pobierają je i umieszczają w unii e odpowiednią strukturę zdarzenia wraz z informacjami dodatkowymi o zdarzeniu (np. kod wciśniętego klawisza, współrzędne myszki, wartość wychylenia drążka joysticka, itp.). Pierwsza funkcja czeka nieokreślony czas, a druga czeka przez zadaną liczbę milisekund.
Trzecia funkcja nie czeka na zdarzenie. Jeśli jest w kolejce, to go pobiera i umieszcza w unii e, zwracając wynik 1. Jeśli kolejka jest pusta, to zwraca wynik 0 bez zmiany zawartości unii e. Pozwala to aplikacji użytkownika wykonywać różne działania w trakcie oczekiwania na zdarzenie. Przykładów można tutaj podać mnóstwo: samochód jedzie bez zmiany kierunku aż użytkownik wychyli drążek joysticka, a wtedy aplikacja zmienia odpowiednio kierunek jazdy.
Zdarzenia są pobierane i obsługiwane w pętli, którą nazywamy pętlą zdarzeń (ang. event loop). Na początku pętli wywołujemy funkcję pobierającą zdarzenie, następnie zdarzenie przetwarzamy w poleceniu switch (obsługujemy tylko te zdarzenia, które są nam potrzebne, inne ignorujemy), po czym pętla wykonuje kolejny obieg.
Poniższy program pokazuje sposób reagowania na kliknięcie dowolnym przyciskiem myszki. Przeanalizuj go dokładnie.
C++// Zdarzenia 1 //------------ #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: KLIKNIJ W OKNO PRZYCISKIEM MYSZKI", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Kasujemy treść okna i ustawiamy kolor czerwony SDL_SetRenderDrawColor(r,255,0,0,255); SDL_RenderClear(r); // Uaktualniamy okno SDL_RenderPresent(r); SDL_Event e; bool running = true; while(running) { SDL_WaitEvent(&e); // Czekamy na zdarzenie, pobieramy je do unii e switch(e.type) // Sprawdzamy rodzaj odebranego zdarzenia { case SDL_QUIT:; // Zamknięcie okna? case SDL_MOUSEBUTTONUP: // Zwolnienie przycisku myszki? running = false; // To przerwie pętlę break; } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Kolejny program demonstruje użycie funkcji SDL_WaitEventTimeout(). Aplikacja czeka przez 2 sekundy na kliknięcie myszką w okno. Jeśli takie zdarzenie się pojawi, okienko pozostanie aktywne. Jeśli w ciągu 2 sekund nie będzie kliknięcia, okno zostanie zamknięte.
C++// Zdarzenia 2 //------------ #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: KLIKNIJ W OKNO PRZYCISKIEM MYSZKI", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Kasujemy treść okna i ustawiamy kolor zielony SDL_SetRenderDrawColor(r,0,255,0,255); SDL_RenderClear(r); // Uaktualniamy okno SDL_RenderPresent(r); SDL_Event e; int counter = 20; while(counter) // Dopóki licznik > 0 { if(counter == 10) // Gdy czas się kończy, okno na czerwono { SDL_SetRenderDrawColor(r,255,0,0,255); SDL_RenderClear(r); SDL_RenderPresent(r); } if(SDL_WaitEventTimeout(&e,100)) // Czekamy na zdarzenie max 0,1 sekundy, pobieramy je do unii e switch(e.type) // Badamy rodzaj zdarzenia { case SDL_QUIT: counter = 0; // To przerwie pętlę i zakończy aplikację break; case SDL_MOUSEBUTTONDOWN: // Zdarzenia przycisków myszki case SDL_MOUSEBUTTONUP: SDL_SetRenderDrawColor(r,255,255,0,255); // Okno na żółto SDL_RenderClear(r); SDL_RenderPresent(r); counter = 20; // Odtwarzamy licznik break; } else counter--; // Upłynął czas, zmniejszamy licznik } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Następny przykład wykorzystuje funkcję SDL_PollEvent() do realizacji prostej gry w odbijanie piłeczki.
C++// Zdarzenia 3 //------------ #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: KLIKNIJ W OKNO PRZYCISKIEM MYSZKI", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjalizujemy generator pseudolosowy srand(time(NULL)); // Prostokąty "piłki" i pola SDL_Rect ball = {0,0,5,5}; SDL_Rect field = {W_W/2-W_W/4,W_H/2-W_H/4,W_W/2,W_H/2}; SDL_Rect dummy; int dx,dy; // kierunki ruchu piłki int fr,fg,fb; // kolor pola fr = 0; fg = 255; fb = 0; dx = 1 + rand() % 3; do dy = 1 + rand() % 3; while(dy == dx); SDL_Event e; bool running = true; int ddx,ddy; while(running) { // Rysujemy pole gry i piłkę SDL_SetRenderDrawColor(r,0,0,0,255); SDL_RenderClear(r); SDL_SetRenderDrawColor(r,fr,fg,fb,255); SDL_RenderFillRect(r,&field); SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderFillRect(r,&ball); // Modyfikujemy położenie piłki if((ball.x+dx < 0) || (ball.x+ball.w+dx >= W_W)) dx = -dx; if((ball.y+dy < 0) || (ball.y+ball.h+dy >= W_H)) dy = -dy; ball.x += dx; ball.y += dy; // Obsługujemy zdarzenia if(SDL_PollEvent(&e)) // Jeśli jest zdarzenie w kolejce, pobieramy go switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_MOUSEBUTTONDOWN: // Naciśnięty przycisk myszki // Zapamiętujemy kierunki ruchu piłki, zmieniając je na przeciwne ddx = 1; if(dx > 0) ddx = -1; ddy = 1; if(dy > 0) ddy = -1; // Wyliczamy nowe przyrosty ruchu piłki w obu osiach dx = 1 + rand() % 3; do dy = 1 + rand() % 3; while(dy == dx); dx *= ddx; // Ustawiamy odpowiednie kierunki ruchu dy *= ddy; field.x--; // Zwiększamy pole field.y--; field.w += 2; field.h += 2; } // Sprawdzamy, czy piłka dotknęła pola if(SDL_IntersectRect(&ball, &field, &dummy)) { // Zerujemy pozycję piłki ball.x = ball.y = 0; dx = 1 + rand() % 3; do dy = 1 + rand() % 3; while(dy == dx); // Zwiększamy kolor pola, który również służy nam jako licznik trafień fr += 64; fb += 64; // Sprawdzamy, czy koniec? if(fr > 255) { // Efekt końcowy - migotanie pola for(int i = 0; i < 16; i++) { SDL_SetRenderDrawColor(r,255,0,0,255); SDL_RenderFillRect(r,&field); SDL_RenderPresent(r); SDL_Delay(50); SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderFillRect(r,&field); SDL_RenderPresent(r); SDL_Delay(50); } running = false; // To zakończy program }; } // Odświeżamy okno SDL_RenderPresent(r); } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Sama gra jest nieco głupia, ale celem programu jest obsługa zdarzeń. Zasady gry są następujące:
W oknie rysowany jest zielony prostokąt oraz piłeczka. Twoim zadaniem jest odbijanie piłeczki (dowolnym przyciskiem myszki), tak aby nie uderzyła w pole. Jeśli uderzy 4 razy, gra się kończy. Po każdym odbiciu pole rośnie, również zmienia się kierunek ruchu piłki. Przeanalizuj program, nie powinieneś mieć z nim problemów.
W programie użyta została nowa funkcja:
SDL_IntersectRect(r1,r2,r3) – wylicza część wspólną prostokątów
r1 i r2 umieszczając ją w r3.
r1,r2, r3 – wskaźniki struktur
SDL_Rect
Funkcja zwraca wartość logiczną true, jeśli prostokąty r1 i r2 przecinają się. W przeciwnym razie zwraca false.
W naszej "grze" funkcję tę wykorzystujemy do sprawdzenia, czy piłka weszła na pole.
Zdarzenie SDL_MOUSEMOTION pojawia się, gdy użytkownik poruszy myszką. W takim przypadku unia SDL_Event zostaje wypełniona strukturą SDL_MouseMotionEvent:
Uint32 | type | rodzaj zdarzenia; SDL_MOUSEMOTION. |
Uint32 | timestamp | czas zdarzenia. |
Uint32 | windowID | okno ze skupieniem myszki, jeśli istnieje. |
Uint32 | which | identyfikator myszki. |
Uint32 | state | stan przycisków myszki. |
Sint32 | x | współrzędna X względem okna. |
Sint32 | y | współrzędna Y względem okna. |
Sint32 | xrel | względny ruch w osi X. |
Sint32 | yrel | względny ruch w osi Y. |
Dostęp do pól tej struktury następuje poprzez pole motion.
Poniższy program reaguje na ruch myszki rysując punkty na ekranie w miejscu otrzymania zdarzenia SDL_MOUSEMOTION. Wykorzystuje pola x i y, które zawierają współrzędne kursora myszki w momencie zdarzenia.
C++// Zdarzenia 4 // Ruch myszki //------------ #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: PRZESUWAJ KURSOR MYSZKI", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } bool running = true; // Tło okna białe SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); // Kolor rysowania czarny SDL_SetRenderDrawColor(r,0,0,0,255); // Uaktualniamy treść okna SDL_RenderPresent(r); SDL_Event e; while(running) { // Obsługujemy zdarzenia SDL_WaitEvent(&e); // Czekamy na zdarzenie switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_MOUSEMOTION: // Ruch myszki // Na współrzędnych kursora myszki rysujemy punkt SDL_RenderDrawPoint(r,e.motion.x,e.motion.y); // Uaktualniamy treść okna SDL_RenderPresent(r); } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Zwróć uwagę, iż szybki ruch myszką tworzy w okienku osobne punkty. Poniższy program eliminuje tę niedogodność przez rysowanie odcinków od poprzednich współrzędnych do aktualnych.
C++// Zdarzenia 5 // Ruch myszki //------------ #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: PRZESUWAJ KURSOR MYSZKI", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } bool running = true; Sint32 old_x = 0, old_y = 0; // poprzednie współrzędne kursora myszki // Tło okna białe SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); // Kolor rysowania czarny SDL_SetRenderDrawColor(r,0,0,0,255); // Uaktualniamy treść okna SDL_RenderPresent(r); SDL_Event e; while(running) { // Obsługujemy zdarzenia SDL_WaitEvent(&e); // Czekamy na zdarzenie switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_MOUSEMOTION: // Ruch myszki // Rysujemy linię od poprzednich współrzędnych do obecnych SDL_RenderDrawLine(r,old_x,old_y,e.motion.x,e.motion.y); // Obecne współrzędne zapamiętujemy old_x = e.motion.x; old_y = e.motion.y; // Uaktualniamy treść okna SDL_RenderPresent(r); } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Zdarzenie ruchu myszki udostępnia również informację o stanie przycisków myszki. Informacja ta znajduje się w polu state struktury SDL_MouseMotionEvent. Przyciski badamy w tym polu za pomocą masek:
SDL_BUTTON_LMASK – lewy przycisk myszki
SDL_BUTTON_RMASK – prawy przycisk myszki
Poniższy program sprawdza stan przycisków myszki. Jeśli jest wciśnięty przycisk prawy, to treść okna zostaje wymazana. Jeśli jest wciśnięty przycisk lewy, to zostaje wybrany losowy kolor rysowania.
C++// Zdarzenia 6 // Ruch myszki //------------ #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: PRZESUWAJ KURSOR MYSZKI", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } bool running = true; Sint32 old_x = 0, old_y = 0; // poprzednie współrzędne kursora myszki // Składowe koloru rysowania int rc = 0, gc = 0, bc = 0; // Inicjujemy generator pseudolosowy srand(time(NULL)); // Tło okna białe SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); // Uaktualniamy treść okna SDL_RenderPresent(r); SDL_Event e; while(running) { // Obsługujemy zdarzenia SDL_WaitEvent(&e); // Czekamy na zdarzenie switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_MOUSEMOTION: // Ruch myszki // Rysujemy linię od poprzednich współrzędnych do obecnych SDL_SetRenderDrawColor(r,rc,gc,bc,255); SDL_RenderDrawLine(r,old_x,old_y,e.motion.x,e.motion.y); // Obecne współrzędne zapamietujemy old_x = e.motion.x; old_y = e.motion.y; // Sprawdzamy stan przycisków myszki if(e.motion.state & SDL_BUTTON_LMASK) // Lewy przycisk? { rc = rand() %256; gc = rand() %256; bc = rand() %256; } if(e.motion.state & SDL_BUTTON_RMASK) // Prawy przycisk? { SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); } // Uaktualniamy treść okna SDL_RenderPresent(r); } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Zwróć uwagę, iż przyciski myszki są wykrywane tylko wraz z jej ruchem. Jeśli zatrzymasz kursor myszki w jednym miejscu okna, to naciskanie przycisków nic nie daje, ponieważ nie występuje zdarzenie ruchu myszki. Dlatego mamy osobne zdarzania związane ze zmianą stanu przycisków myszki:
To zdarzenie pojawia się, gdy użytkownik naciśnie przycisk myszki. Zawsze występuje jeden raz po naciśnięciu przycisku. Poprzednie zdarzenie ruchu myszki zgłaszało stan przycisków, jednakże w prosty sposób nie informowało nas o samym momencie naciśnięcia przycisku.
To zdarzenie pojawia się, gdy użytkownik zwolni naciśnięty poprzednio przycisk myszki.
W obu powyższych zdarzeniach unia SDL_Event zostaje wypełniona strukturą SDL_MouseButtonEvent. Dostęp do pól tej struktury masz poprzez pole button:
Uint32 | type | rodzaj zdarzenia; SDL_MOUSEBUTTONDOWN lub SDL_MOUSEBUTTONUP. |
Uint32 | timestamp | czas zdarzenia. |
Uint32 | windowID | okno ze skupieniem myszki, jeśli takie jest. |
Uint32 | which | identyfikator myszki. |
Uint8 | button | przycisk, którego stan się zmienił. |
Uint8 | state | stan tego przycisku; SDL_PRESSED lub SDL_RELEASED. |
Uint8 | clicks | 1 dla pojedynczego kliknięcia, 2 dla podwójnego kliknięcia, itd. (>= SDL 2.0.2). |
Sint32 | x | współrzędna X względem okna. |
Sint32 | y | współrzędna Y względem okna. |
Kliknięcie przycisku myszki udostępnia informację o położeniu kursora myszki w momencie kliknięcia: pola x i y.
W polu button znajduje się informacja, który z przycisków myszki zmienił swój stan:
SDL_BUTTON_LEFT – lewy przycisk
SDL_BUTTON_RIGHT – prawy przycisk
W polu state masz informację o stanie przycisku, którego stan uległ zmianie:
SDL_PRESSED – przycisk jest wciśnięty
SDL_RELEASED – przycisk jest zwolniony
Nie musisz z tego pola korzystać, ponieważ rodzaj zdarzenia informuje nas o stanie przycisku (SDL_MOUSEBUTTONDOWN – przycisk wciśnięty, SDL_MOUSEBUTTONUP – przycisk zwolniony).
Pole clicks może być przydatne, jeśli chcesz, aby twój program inaczej reagował na pojedyncze kliknięcie, a inaczej na podwójne. Opcja ta działa tylko z SDL2 o wersji co najmniej 2.0.2.
Poprzedni program zmodyfikujemy, tak aby teraz wykorzystywał zdarzenie kliknięcia przyciskiem myszki:
C++// Zdarzenia 7 // Przyciski myszki //----------------- #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: PRZESUWAJ KURSOR MYSZKI", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } bool running = true; Sint32 old_x = 0, old_y = 0; // poprzednie współrzędne kursora myszki // Składowe koloru rysowania int rc = 0, gc = 0, bc = 0; // Inicjujemy generator pseudolosowy srand(time(NULL)); // Tło okna białe SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); // Uaktualniamy treść okna SDL_RenderPresent(r); SDL_Event e; while(running) { // Obsługujemy zdarzenia SDL_WaitEvent(&e); // Czekamy na zdarzenie switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_MOUSEMOTION: // Ruch myszki // Rysujemy linię od poprzednich współrzędnych do obecnych SDL_SetRenderDrawColor(r,rc,gc,bc,255); SDL_RenderDrawLine(r,old_x,old_y,e.motion.x,e.motion.y); // Obecne współrzędne zapamiętujemy old_x = e.motion.x; old_y = e.motion.y; // Uaktualniamy treść okna SDL_RenderPresent(r); break; case SDL_MOUSEBUTTONDOWN: // Przycisk myszki if(e.button.button == SDL_BUTTON_LEFT) // Lewy przycisk? { rc = rand() %256; gc = rand() %256; bc = rand() %256; } else if(e.button.button == SDL_BUTTON_RIGHT) // Prawy przycisk? { SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); // Uaktualniamy treść okna SDL_RenderPresent(r); } break; } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Sprawdź, co się dzieje, gdy wychodzisz kursorem myszki poza obszar okna, a później wracasz nim do okienka w innym miejscu. Czy potrafisz wytłumaczyć to zachowanie programu?
Kolejny program jest prostym edytorem graficznym, który umożliwia rysowanie myszką po powierzchni okna. Na spodzie okna jest paleta dostępnych kolorów. Kolor wybierasz z palety przez wskazanie go myszką i kliknięcie lewym przyciskiem. Rysujesz naciskając lewy przycisk na powierzchni okna poza paletą. Prawy przycisk myszki czyści zawartość okna.
C++// Zdarzenia 8 // Przyciski myszki //----------------- #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Element palety kolorów struct PElement { SDL_Rect r; int cr,cg,cb; }; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: EDYTOR GRAFICZNY", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Zmienne bool running = true; // Definiujemy paletę kolorów PElement P[16]; const int PEH = W_H / 20; // Wysokość palety const int PEW = W_W / 16; // Szerokość elementu palety // Definiujemy kolejne elementy palety for(int i = 0; i < 16; i++) { // Najpierw pozycja zależna od numeru P[i].r.x = PEW * i; P[i].r.y = W_H - PEH; // Teraz wymiary P[i].r.w = PEW; P[i].r.h = PEH; // Teraz kolor zależny od numeru, z którego bierzemy kolejne bity // Trzy najmłodsze bity numeru to kolejno kolory b,g,r // Czwarty bit określa jasność kolorów, 127 - ciemny, 255 - jasny. P[i].cr = (i & 0x01); P[i].cg = (i & 0x02) >> 1; P[i].cb = (i & 0x04) >> 2; if(i & 0x08) { P[i].cr *= 255; P[i].cg *= 255; P[i].cb *= 255; } else { P[i].cr *= 127; P[i].cg *= 127; P[i].cb *= 127; } } // Kolor rysowania, początkowo czarny int dcr,dcg,dcb; dcr = dcg = dcb = 0; // Tryb rysowania lub nierysowania bool drawing = false; // Pozycja początku linii int xp,yp; // Tło okna białe SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); // Rysujemy paletę for(int i = 0; i < 16; i++) { // Ustawiamy kolor rysowania na kolor elementu palety SDL_SetRenderDrawColor(r,P[i].cr,P[i].cg,P[i].cb,255); // Rysujemy prostokąt SDL_RenderFillRect(r,&P[i].r); } // Uaktualniamy treść okna SDL_RenderPresent(r); SDL_Event e; while(running) { // Obsługujemy zdarzenia SDL_WaitEvent(&e); // Czekamy na zdarzenie switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_MOUSEBUTTONDOWN: if(e.button.button == SDL_BUTTON_RIGHT) // Czyścimy okienko { SDL_Rect rr; SDL_SetRenderDrawColor(r,255,255,255,255); rr.x = 0; rr.y = 0; rr.w = W_W; rr.h = W_H - PEH; SDL_RenderFillRect(r,&rr); drawing = false; SDL_RenderPresent(r); } else if(e.button.button == SDL_BUTTON_LEFT) { // Jeśli kursor myszki jest nad obszarem rysunku, to włączamy rysowanie // i stawiamy w tym miejscu punkt if(e.button.y < W_H - PEH) { drawing = true; SDL_SetRenderDrawColor(r,dcr,dcg,dcb,255); xp = e.button.x; yp = e.button.y; SDL_RenderDrawPoint(r,xp,yp); SDL_RenderPresent(r); } // W przeciwnym razie odczytujemy kolor kliknietego elementu palety else { int i = e.button.x / PEW; dcr = P[i].cr; dcg = P[i].cg; dcb = P[i].cb; } } break; case SDL_MOUSEBUTTONUP: drawing = false; break; case SDL_MOUSEMOTION: // Jeśli kursor jest w obszarze rysunkowym i lewy przycisk // myszki jest wciąż wciśniety, rysujemy linię od ostatniego // punktu if(!drawing) break; if(e.motion.state & SDL_BUTTON_LMASK) { if(e.motion.y < W_H - PEH) { SDL_SetRenderDrawColor(r,dcr,dcg,dcb,255); int xk = e.motion.x; int yk = e.motion.y; SDL_RenderDrawLine(r,xp,yp,xk,yk); // Uaktualniamy początek linii xp = xk; yp = yk; SDL_RenderPresent(r); } } else drawing = false; break; } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Uint32 | type | rodzaj zdarzenia; SDL_KEYDOWN lub SDL_KEYUP. |
Uint32 | timestamp | czas zdarzenia. |
Uint32 | windowID | okno ze skupieniem klawiatury, jeśli takie jest. |
Uint8 | state | stan klawisza; SDL_PRESSED lub SDL_RELEASED. |
Uint8 | repeat | wartość różna od 0, jeśli jest to powtórzenie klawisza. |
SDL_Keysym | keysym | struktura SDL_Keysym reprezentująca naciśnięty lub zwolniony klawisz. |
Dostęp do pól tej struktury uzyskujesz poprzez pole unii o nazwie key.
Aby oswoić się z obsługą klawiatury w SDL2, napiszemy prosty program, który mruga okienkiem na czerwono, gdy klawisz jest naciskany, a na zielono, gdy jest zwalniany.
C++// Zdarzenia 9 // Naciskanie i zwalnianie klawiszy na klawiaturze //------------------------------------------------ #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: KLAWIATURA", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Tło okna białe SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); // Uaktualniamy treść okna SDL_RenderPresent(r); SDL_Event e; bool running = true; bool key = false; while(running) { // Obsługujemy zdarzenia SDL_WaitEvent(&e); // Czekamy na zdarzenie switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_KEYDOWN: if(!e.key.repeat) // Jeśli to powtórka, nie mrugamy { SDL_SetRenderDrawColor(r,255,0,0,255); key = true; } break; case SDL_KEYUP: SDL_SetRenderDrawColor(r,0,255,0,255); key = true; break; } if(key) { key = false; SDL_RenderClear(r); SDL_RenderPresent(r); SDL_Delay(50); SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); SDL_RenderPresent(r); } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Zwróć uwagę, że zdarzenia SDL_KEYDOWN i SDL_KEYUP dotyczą każdego klawisza klawiatury, również klawiszy funkcyjnych i SHIFT/CTRL/ALT.
W kolejnym kroku nauczymy się odczytywać kody naciśniętych klawiszy. W tym celu struktura SDL_KeyboardEvent zawiera pole o nazwie keysym, które samo jest strukturą typu SDL_Keysym o następujących polach:
SDL_Scancode | scancode | fizyczny kod klawisza w SDL; zobacz na SDL_Scancode. |
SDL_Keycode | sym | wirtualny kod klawisza w SDL2; zobacz na SDL_Keycode. |
Uint16 | mod | bieżące modyfikatory klawisza; zobacz na SDL_Keymod. |
Uint32 | unused | nieużywane. |
Pierwsze pole o nazwie scancode zawiera tzw. kod matrycowy klawisza, który określa jego położenie na klawiaturze. Ponieważ klawiatury mogą być definiowane w różny sposób w systemie operacyjnym, kod matrycowy nie zawsze odzwierciedla znaczenie naciśniętego klawisza w obowiązującym układzie klawiatury. Kody matrycowe masz zdefiniowane w podanym linku.
Drugie pole o nazwie sym zawiera tzw. kod wirtualny klawisza, który powstaje po przetworzeniu kody matrycowego w obowiązującym układzie klawiatury. Używanie kodów wirtualnych gwarantuje automatyczne dostosowanie się programu do ustawień systemowych dotyczących klawiatury. Kody wirtualne masz dostępne w podanym linku.
Teraz trochę praktyki. Napiszemy prosty program, który rysuje w oknie mały kwadrat i reaguje na klawisze kursora do przesuwania tego kwadratu oraz na klawisz ESC, który kończy działanie programu.
C++// Zdarzenia 10 // Klawisze kursora i klawisz ESC //------------------------------- #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: KLAWISZE KURSORA", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Prostokąt wyświetlany w oknie SDL_Rect rr; rr.w = rr.h = W_H / 8; rr.x = W_W / 2 - rr.w / 2; // Początkowe współrzędne prostokąta; rr.y = W_H / 2 - rr.h / 2; SDL_Event e; bool running = true; bool key = true; while(running) { // Obsługujemy zdarzenia if(SDL_PollEvent(&e)) // Jeśli jest zdarzenie w kolejce, pobieramy go { switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_KEYDOWN: switch(e.key.keysym.sym) { case SDLK_ESCAPE: running = false; break; case SDLK_LEFT: if(rr.x > 0) { key = true; rr.x --; } break; case SDLK_RIGHT: if(rr.x + rr.w < W_W) { key = true; rr.x ++; } break; case SDLK_UP: if(rr.y > 0) { key = true; rr.y --; } break; case SDLK_DOWN: if(rr.y + rr.h < W_H) { key = true; rr.y ++; } break; default: break; } break; } if(key) { key = false; // Tło okna niebieskie SDL_SetRenderDrawColor(r,0,0,255,255); SDL_RenderClear(r); // Żółty prostokąt SDL_SetRenderDrawColor(r,255,255,0,255); SDL_RenderFillRect(r,&rr); // Uaktualniamy treść okna SDL_RenderPresent(r); } } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
W podobny sposób piszesz aplikacje reagujące na inne klawisze.
Zdarzenia okna powstają, gdy z oknem użytkownika coś się dzieje. W takim przypadku unia SDL_Event zostaje wypełniona strukturą SDL_WindowEvent. Pola tej struktury są następujące:
Uint32 | type | rodzaj zdarzenia; SDL_WINDOWEVENT. |
Uint32 | timestamp | czas zdarzenia. |
Uint32 | windowID | okno, którego dotyczy dane zdarzenie. |
Uint8 | event | rodzaj zdarzenia dla okna: SDL_WindowEventID. |
Sint32 | data1 | dane zależne od rodzaju zdarzenia w polu event. |
Sint32 | data2 | dane zależne od rodzaju zdarzenia w polu event. |
Zasada obsługi tych zdarzeń jest prosta. Po wykryciu w polu type zdarzenia okna SDL_WINDOWEVENT sprawdzamy zawartość pola event (wcześniej możemy sprawdzić pole windowID, jeśli aplikacja ma kilka okien), w którym znajdziemy numer określający, jakie zdarzenie okna wystąpiło. Numery zdarzeń okna są enumeracją SDL_WindowEventID, które przyjmuje następujące wartości:
SDL_WINDOWEVENT_NONE | (nigdy nieużywane). |
SDL_WINDOWEVENT_SHOWN | okno zostało wyświetlone. |
SDL_WINDOWEVENT_HIDDEN | okno zostało ukryte. |
SDL_WINDOWEVENT_EXPOSED | okno zmieniło treść i powinno zostać przerysowane. |
SDL_WINDOWEVENT_MOVED | okno zostało przesunięte. |
SDL_WINDOWEVENT_RESIZED | okno zmieniło rozmiar; to zdarzenie zawsze jest poprzedzone przez SDL_WINDOWEVENT_SIZE_CHANGED |
SDL_WINDOWEVENT_SIZE_CHANGED | okno zmieniło się w wyniku wywołania funkcji API, poprzez system lub w wyniku działań użytkownika; po tym zdarzeniu następuje SDL_WINDOWEVENT_RESIZED, jeśli zmianie uległ rozmiar w wyniku zdarzenia zewnętrznego, np. działanie użytkownika lub menadżera okien. |
SDL_WINDOWEVENT_MINIMIZED | okno zostało zminimalizowane. |
SDL_WINDOWEVENT_MAXIMIZED | okno zostało zmaksymalizowane. |
SDL_WINDOWEVENT_RESTORED | okno zostało przywrócone do standardowego rozmiaru i położenia. |
SDL_WINDOWEVENT_ENTER | okno otrzymało skupienie myszki. |
SDL_WINDOWEVENT_LEAVE | okno utraciło skupienie myszki. |
SDL_WINDOWEVENT_FOCUS_GAINED | okno otrzymało skupienie klawiatury. |
SDL_WINDOWEVENT_FOCUS_LOST | okno utraciło skupienie klawiatury. |
SDL_WINDOWEVENT_CLOSE | menadżer okien żąda jego zamknięcia. |
SDL_WINDOWEVENT_TAKE_FOCUS | okno otrzymuje propozycję przejęcia skupienia (powinno wywołać SDL_SetWindowInputFocus() na sobie, na podoknie lub zignorować zdarzenie) (>= SDL 2.0.5) |
SDL_WINDOWEVENT_HIT_TEST | okno miało hittest, który nie był SDL_HITTEST_NORMAL (>= SDL 2.0.5) |
W zależności od rodzaju zdarzenia okna pola data1 i data2 zawierają różne dane, związane z tym zdarzeniem.
Poniższy program demonstruje obsługę zdarzeń SDL_WINDOWEVENT_ENTER i SDL_WINDOWEVENT_LEAVE. Pierwsze zdarzenie powstaje, gdy kursor myszki wchodzi w obszar okna. Drugie zdarzenie powstaje, gdy kursor myszki opuszcza obszar okna. W programie będzie rysowany czerwony prostokąt. Gdy myszka znajdzie się nad obszarem okna, kolor prostokąta zmieni się na zielony. Gdy myszka opuści obszar okna, kolor prostokąta zmieni się na niebieski.
C++// Zdarzenia 11 // Zdarzenia okna //------------------------------- #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: OKNO", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Prostokąt wyświetlany w oknie SDL_Rect rr; rr.w = W_W / 2; rr.h = W_H / 2; rr.x = W_W / 4; rr.y = W_H / 4; SDL_Event e; bool running = true; bool draw = true; SDL_SetRenderDrawColor(r,255,0,0,0); // wstępny kolor prostokąta while(running) { // Obsługujemy zdarzenia if(SDL_PollEvent(&e)) // Jeśli jest zdarzenie w kolejce, pobieramy go { switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_WINDOWEVENT: if(e.window.event == SDL_WINDOWEVENT_ENTER) SDL_SetRenderDrawColor(r,0,255,0,255); if(e.window.event == SDL_WINDOWEVENT_LEAVE) SDL_SetRenderDrawColor(r,0,0,255,255); draw = true; break; default: break; } if(draw) { draw = false; SDL_RenderFillRect(r,&rr); // Uaktualniamy treść okna SDL_RenderPresent(r); } } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Poniższy program reaguje na zmianę rozmiaru okna i rysuje prostokąt proporcjonalny do nowych rozmiarów. Nowy rozmiar okna pobieramy z pól data1 (szerokość) i data2 (wysokość).
C++// Zdarzenia 12 // Zdarzenia okna //------------------------------- #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka int W_W = 640; int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Zdarzenia: OKNO - ROZMIAR", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, SDL_WINDOW_RESIZABLE); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Prostokąt wyświetlany w oknie SDL_Rect rr; SDL_Event e; bool running = true; bool draw = true; while(running) { // Obsługujemy zdarzenia if(SDL_PollEvent(&e)) // Jeśli jest zdarzenie w kolejce, pobieramy go { switch(e.type) // Sprawdzamy rodzaj zdarzenia { case SDL_QUIT: running = false; // To przerwie pętlę break; case SDL_WINDOWEVENT: if(e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { draw = true; W_W = e.window.data1; // Pobieramy nowy rozmiar okna W_H = e.window.data2; } break; default: break; } if(draw) { draw = false; rr.w = W_W / 2; rr.h = W_H / 2; rr.x = W_W / 4; rr.y = W_H / 4; // Tło okna białe SDL_SetRenderDrawColor(r,255,255,255,255); SDL_RenderClear(r); // Prostokąt czerwony; SDL_SetRenderDrawColor(r,255,0,0,255); SDL_RenderFillRect(r,&rr); // Uaktualniamy treść okna SDL_RenderPresent(r); } } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2024 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.