Programowanie C/C++ OpenKODE Janusz Ganczarski penKODE jest zbiorem funkcji (API) prze- znaczonych do budowania aplikacji mul- Kody błędów Otimedialnych i gier na urządzenia prze- Kody błędów oparto o błędy zdefiniowane w standardach nośne. Jednym z podstawowych założeń biblio- C/BSD/POSIX: KD_EACCES (brak dostępu), KD_EADDRINU- teki OpenKODE jest ułatwienie tworzenia aplika- SE (adres jest używany), KD_EADDRNOTAVAIL (adres niedo- cji na urządzeniach mobilnych i przenoszenia ich stępny), KD_EAFNOSUPPORT (niewspierana rodzina adresów), na różne platformy systemowe i sprzętowe. Doce- KD_EAGAIN (zasoby chwilowo niedostępne), KD_EALREADY lowo OpenKODE ma łączyć większość otwartych (trwa połączenie), KD_EBADF (nieprawidłowy deskryptor pli- standardów opracowanych przez Khronos Gro- ku), KD_EBUSY (zajęte urządzenie lub zasób), KD_ECONNRE- up OpenGL ES (grafika trójwymiarowa), Ope- FUSED (połączenie odrzucone), KD_ECONNRESET (połącze- nVG (dwuwymiarowa grafika wektorowa), Open- nie zrestartowane), KD_EDESTADDRREQ (wymagany adres MAX (multimedia) i OpenSL ES (dzwięk). Do two- docelowy), KD_EDOM (wartość argumentu funkcji poza dzie- rzenia kontekstów graficznych w systemach udo- dziną), KD_ERANGE (wartość argumentu funkcji poza zakre- sem), KD_EEXIST (plik istnieje), KD_EFBIG (zbyt duży rozmiar stępniających mechanizm okien OpenKODE wyko- pliku), KD_EHOSTUNREACH (węzeł niedostępny), KD_EINVAL rzystuje bibliotekę EGL. Budowę aplikacji mobilnej (błędny argument), KD_EIO (błąd wejścia-wyjścia), KD_EIL- przed i po zastosowaniu OpenKODE przedstawia- SEQ (niepoprawna sekwencja bajtów), KD_EISCONN (gniazdo ją Rysunki 1 i 2. jest już połączone), KD_EISDIR (katalog), KD_EMFILE (za du- Artykuł został oparty o pierwszą korektę tymcza- żo otwartych plików), KD_ENAMETOOLONG (zbyt długa nazwa sowej specyfikacji OpenKODE, która została opubli- pliku), KD_ENOENT (brak pliku lub katalogu), KD_ENOMEM (brak kowana 30.03.2007r. Specyfikacja ta obecnie łączy pamięci), KD_ENOSPC (brak wolnego miejsca na urządzeniu), jedynie biblioteki OpenGL ES i OpenVG. Opracowa- KD_ENOSYS (funkcja nie jest obsługiwana), KD_ENOTCONN nie finalnej wersji standardu planowane jest na trzeci (gniazdo nie jest połączone), KD_EOPNOTSUPP (operacja nie kwartał 2007 roku. jest obsługiwana), KD_EOVERFLOW (nadmiar), KD_EPERM (ope- racja nie jest możliwa), KD_EPIPE (gniazdo nie jest już połą- czone), KD_ETIMEDOUT (przekroczony czas połączenia). Podstawy API OpenKODE Do opisu funkcji bibliotecznych specyfikacja OpenKO- DE wykorzystuje język C. Znaczną część API stanowią odpowiedniki funkcji języka C (zarówno standardu C89 nej wartości w przypadku wystąpienia błędu (ty- jak i C99) oraz interfejsów programistycznych standar- powo jest to wartość 1 lub KD _ NULL). Ustawia- dów BSD i POSIX. Funkcje zapożyczone z powyższych ny jest także globalny wskaznik ostatniego błędu, standardów mają identyczne nazwy, poza charakte- który można odczytać przy pomocy funkcji kdGe- rystycznym dla całej biblioteki OpenKODE przedrost- tError (kod błędu nie jest modyfikowany po od- kiem kd. W głównej części artykułu skupimy się na naj- czycie) lub ustawić korzystając z funkcji kdSetEr- ważniejszych elementach API OpenKODE, natomiast ror. Opis kodów błędów przedstawiono w ramce. w ramkach czytelnik znajdzie krótkie opisy pozostałych Omawiana tymczasowa specyfikacja OpenKODE funkcji, ze szczególnym uwzględnieniem elementów od- nie przewiduje wsparcia dla aplikacji wielowątko- biegających od standardów C/BSD/POSIX. wych, dlatego obsługa błędów opiera się na glo- Całość interfejsu programistycznego biblioteki balnym wskazniku błędu. W przypadku wprowa- OpenKODE zawarto w pliku kd.h, który automatycznie dzenia w przyszłych wersjach OpenKODE wspar- włącza także plik egl.h z biblioteki EGL. Podstawowe cia dla wielowątkowości, wskazniki błędu będą od- stałe i typy danych przedstawiono w ramce. Definicje rębne dla każdego wątku. stałych i zmiennych zależnych od platformy systemo- wej umieszczono w odrębnym pliku kdplatform.h. Wersje i rozszerzenia Odczyt wybranych właściwości implementacji biblio- Obsługa błędów teki OpenKODE umożliwia funkcja: Obsługa błędów w bibliotece OpenKODE spro- wadza się do zwracania przez funkcje specjal- const KDchar *kdQueryAttribcv (KDint attribute) której parametr attribute może przyjąć jedną z Autor jest matematykiem i informatykiem dwóch wartości: KD _ ATTRIB _ VENDOR autor imple- Kontakt z autorem: JanuszG@enter.net.pl mentacji oraz KD _ ATTRIB _ VERSION wersja bibliote- Strona domowa: http://www.januszg.hg.pl ki. Wersję biblioteki opisuje ciąg znaków w następu- 24 www.sdjournal.org Software Developer s Journal 08/2007 OpenKODE stępnych rozszerzeń biblioteki. Ilość atrybutów (w tym wypad- ku rozszerzeń biblioteki) zwracana jest za pośrednictwem pa- Podstawowe stałe i typy danych rametru value. Biblioteka OpenKODE udostępnia szereg typów liczb całkowitych o W drugim etapie pobierane są kolejne wartości wybra- precyzji od 8 do 64 bitów: KDchar, KDint32, KDuint32, KDint64, KDu- nego atrybutu, do czego służy funkcja: const KDchar *kdQue- int64, KDint16, KDuint16, KDint8, KDuint8, KDint, KDuint oraz licz- ryIndexedAttribcv (KDint attribute, KDint index) Jak się Czy- by zmiennoprzecinkowe KDfloat32 (standard IEEE 754). Typy KDint i telnik domyśla, także tutaj parametr attribute może przy- KDuint zakładają co najmniej 32 bitową precyzję. Dostępny jest tak- jąć tylko jedną wartość KD _ ATTRIB _ EXTENSIONS określającą że typ logiczny KDboolean, który opiera się na KDint. Warto zwrócić pobieranie informacji o rozszerzeniach biblioteki. Numera- uwagę na brak liczb zmiennoprzecinkowych podwójnej precyzji, co cja poszczególnych wartości atrybutu, umieszczana w pa- autorzy specyfikacji tłumaczą ich niewielką przydatnością w grach. rametrze index, zaczyna się od zera. Używana w trakcie Ponadto zdefiniowanych jest kilka typów pochodnych po ty- pach podstawowych: KDuintptr (typ całkowity wielkości pozwa- testów biblioteka OpenKODE posiadała jedno rozszerze- lającej na zmieszczenie wskaznika), KDsize i KDssize (typy całko- nie KD _ KHR _ staticdata. Jego dostępność oznacza, że im- wite pozwalające na zmieszczenie rozmiaru największego obiektu plementacja wspiera zmienne nieautomatyczne, czyli np. w pamięci), KDsocklen (typ całkowity określający rozmiar struktu- zmienne statyczne i globalne. ry KDSockaddr), KDtime (typ KDint64 używany do określania liczby Brak dostępności takich zmiennych może np. wystąpić sekund), KDust (typ KDint64 używany do określania liczby nano- w przypadku implementacji OpenKODE w prostych syste- sekund), KDoff (typ KDint64 używany do określania wielkości pli- mach wbudowanych w pamięci ROM. W trakcie opracowy- ku lub pozycji w pliku) oraz KDmode (typ KDuint32 używany w polu wania są dwa następne rozszerzenia biblioteki OpenKO- st _ mode struktury KDStat). DE: KD _ KHR _ performer i KD _ KHR _ crypto. Pierwsze roz- Większość podstawowych stałych określa zakresy wartości szerzenie wprowadza mechanizmy przekazujące informa- typów podstawowych: KDINT _ MIN, KDINT _ MAX, KDUINT _ MAX, cje o sprzęcie (np. procesor, pamięć, grafika) oraz określa- KDINT32 _ MIN, KDINT32 _ MAX, KDUINT32 _ MAX, KDINT64 _ MIN, KDINT64 _ MAX oraz KDUINT64 _ MAX. Zdefiniowano także odpo- jące jego wydajność. wiedniki znanych stałych: KD _ TRUE, KD _ FALSE i KD _ NULL. Drugie z opracowywanych rozszerzeń zawiera szereg funkcji kryptograficznych. Obsługiwany jest algorytm AES (ang. Advanced Encryption Standard) z kluczami o długo- jącym formacie: numer wersji, kropka, numer podwersji, spa- ści 128, 192 i 256 bitów. cja, opcjonalne informacje o implementacji. Implementacja bi- Niektóre implementacje OpenKODE mogą wymagać po- blioteki używana w trakcie testów zwracała następujące ciągi bierania wskazników do funkcji dostępnych w rozszerze- znaków: Acrodea OpenKODE / Windows oraz 1.0 Provisio- niach (podobna sytuacja ma miejsce także w przypadku bi- nal build Feb 27 2007 . Odczyt dostępnych rozszerzeń biblio- blioteki EGL). Pobranie wskaznika do wybranej funkcji reali- teki OpenKODE, a w przyszłości także innych atrybutów im- zuje funkcja: plementacji, wymaga dwuetapowego działania. W pierwszym kroku należy ustalić ilość wartości dostępnych dla danego atry- void *kdGetProcAddress (const KDchar *name) butu, co wymaga wywołania funkcji: Wejście i wyjście programu KDint kdQueryAttribi (KDint attribute, KDint *value) Wejście programu realizowane jest przez funkcję: Obecnie jedyną możliwą wartością parametru attribute jest KDint kdMain (KDint argc, const KDchar **argv) KD _ ATTRIB _ NUM _ EXTENSIONS, który określa pobranie ilości do- Rysunek 1. Aplikacja mobilna bez OpenKODE (zródło Rysunek 2. Aplikacja mobilna z OpenKODE (zródło Khronos Khronos Group) Group) Software Developer s Journal 08/2007 www.sdjournal.org 25 Programowanie C/C++ której argumenty odpowiadają analogicznym argumentom funkcji main w języku C. Wyjście z aplikacji zapewnia funkcja: Funkcje narzędziowe Do grupy funkcji narzędziowych należą funkcje w większości znane void kdExit (KDint status) z języka C: kdAbs, kdStrtof, kdStrtol, kdStrtoul, kdLtostr (kon- wersja liczby całkowitej ze znakiem do ciągu znaków), kdUltostr będąca odpowiednikiem funkcji exit z języka C. (konwersja liczby całkowitej bez znaku do ciągu znaków), kdFtostr (konwersja liczby zmiennoprzecinkowej do ciągu znaków) oraz Obsługa zdarzeń kdCryptoRandom (generowanie ciągu liczb losowych). Zauważmy, Biblioteka OpenKODE korzysta z modelu zdarzeniowego, że biblioteka OpenKODE nie zawiera odpowiednika funkcji sprintf w którym aplikacja ma pojedynczy punkt wejścia zawierają- oraz snprintf. Funkcje konwertujące liczby na ciągi znaków udostęp- cy główną pętlę obsługi zdarzeń systemowych. Jednak moż- niają część ich możliwości. Maksymalną długość generowanych liwe jest także korzystanie z mechanizmu funkcji wywoływa- ciągów znaków, łącznie z końcowym znakiem NULL, określają stałe: nych zwrotnie, co ułatwia przenoszenie programów stosują- KD_LTOSTR_MAXLEN, KD_ULTOSTR_MAXLEN i KD_FTOSTR_MAXLEN. cych tę technikę. Przy przetwarzaniu zdarzeń wykorzystywana jest struk- tura KDEvent, której zawartość zależy od rodzaju zdarzenia. NameLookup namelookup, KDEventWindowFocus windowfocus, Struktura zawiera cztery pola: KDEventUser user). " timestamp czas wystąpienia zdarzenia (czas UST Trzy podstawowe zdarzenia systemowe (globalne) identyfiko- patrz ramka), wane są przez następujące stałe: KD _ EVENT _ QUIT zakończe- " type rodzaj zdarzenia identyfikowany jedną ze stałych z nie działania aplikacji, KD _ EVENT _ PAUSE zatrzymanie działa- grupy KD _ EVENT _ *, nia aplikacji, KD _ EVENT _ RESUME wznowienie działania aplika- " userptr wskaznik na dane przekazywane przy wywoła- cji. Do tej grupy należy także opisywane dalej zdarzenie wej- niu zdarzenia, ścia-wyjścia KD _ IOGROUP _ EVENT związane z zasilaniem urządze- " data unia KDEventData zawierająca jedną ze struktur ob- nia. Biblioteka OpenKODE definiuje także szereg zdarzeń lokal- sługujących konkretny rodzaj zdarzenia (KDEventInput in- nych związanych z obsługą procesów wejścia-wyjścia, gniazda- put, KDEventInputPointer inputpointer, KDEventInput- mi sieciowymi, oknami oraz licznikiem czasu. Zdarzenia te omó- Stick inputstick, KDEventSocketReadable socketreada- wimy nieco dalej, bądz są one przedstawione w ramkach. Obsłu- ble, KDEventSocketWritable socketwritable; KDEventSoc- ga zdarzeń przebiega w pętli, w której zasadniczym elementem ketError socketerror, KDEventSocketConnect socketcon- jest oczekiwanie na zajście zdarzenia. Realizuje to funkcja: nect, KDEventSocketIncoming socketincoming, KDEvent- const KDEvent *kdWaitEvent (KDust timeout) która zwraca wskaznik na strukturę KDEvent opisującą zdarze- nie. Parametr timeout określa maksymalny czas (w nanose- kundach), w jakim następuje oczekiwanie na zdarzenie. Poda- nie wartości -1 oznacza, że funkcja kdWaitEvent czeka tak dłu- go, aż w kolejce zdarzeń pojawi się zdarzenie. W przypadku wystąpienia błędu funkcja zwraca wartość KD _ NULL. Domyślna obsługa zdarzenia nieprzetwarzanego przez program sprowadza się do wywołania funkcji: void kdDefaultEvent (const KDEvent *event) Obsługa zdarzenia KD _ EVENT _ QUIT przez funkcję kdDefaultE- vent jest równoważna wywołaniu funkcji kdExit z parametrem 0. Zdarzenia globalne domyślnie nie generują żadnych da- nych w polu userptr struktury KDEvent (umieszczana jest war- tość KD _ NULL). Przekazywane za pośrednictwem tego pola dane można określić korzystając z funkcji: void kdSetEventUserptr (void *userptr) Tworzenie zdarzeń Program może umieszczać własne zdarzenia w kolejce zda- rzeń. Mogą to być zarówno zdarzenia systemowe, jak i zda- rzenia zdefiniowane przez użytkownika. Utworzenie zdarze- nia wymaga wywołania funkcji: Rysunek 3. Początkowe okno programu Tygrys KDEvent *kdCreateEvent (void) 26 www.sdjournal.org Software Developer s Journal 08/2007 OpenKODE Listing 1. Program Tygrys (fragmenty) [...] break; // program główny // obsługa wskaznika (np. myszki) KDint kdMain (KDint argc, const KDchar **argv) { case KD_EVENT_INPUT_POINTER: // utworzenie okna if (event -> data.inputpointer.select) { KDWindow *window = kdCreateWindow (KD_NULL,KD_NULL); // przypadek, gdy przycisk wskaznika // rozmiary okna // jest naciśnięty i jest w ruchu kdSetWindowSize (window,(KDint)tigerMaxX, ż if (pointer_select) { (KDint)tigerMaxY); // pobranie rozmiarów okna w pikselach // tytuł okna int width,height; kdSetWindowCaption (window,"Tygrys"); eglQuerySurface (display,window_ // wyświetlenie okna surface,EGL_WIDTH,&width); kdShowWindow (window, KD_WINDOWSTATUS_VISIBLE); eglQuerySurface (display,window_ // sprawdzenie dostępności opcjonalnych przycisków gry surface,EGL_HEIGHT,&height); KDint32 buffer; // przeliczenie wektora przesunięcia kdInputPolli (KD_IO_GAMEKEYS_AVAILABILITY,9,&buffer); translatex += event -> // włączenie obsługi przycisków gry (jeżeli przyciski data.inputpointer.x - pointer_x; // są dostępne) translatey += pointer_y - event -> if (buffer == 511) { data.inputpointer.y; kdInputEventEnable (KD_IOGROUP_GAMEKEYSNC,KD_TRUE); // zapamiętanie współrzędnych położenia } // wskaznika // włączenie obsługi przycisków gry pointer_x = event -> data.inputpointer.x; kdInputEventEnable (KD_IOGROUP_GAMEKEYSNC,KD_TRUE); pointer_y = event -> data.inputpointer.y; // włączenie obsługi wskaznika } else { kdInputEventEnable (KD_IOGROUP_POINTER,KD_TRUE); // przycisk wskaznika nie był wcześniej [...] // naciśnięty // struktura z opisem zdarzeń pointer_select = true; const KDEvent *event; pointer_x = event -> data.inputpointer.x; // dane do obsługi wskaznika pointer_y = event -> data.inputpointer.y; KDint32 pointer_x,pointer_y; } bool pointer_select = false; Display (display,window_surface,tiger); // główna pętla obsługi zdarzeń } else while ((event = kdWaitEvent (-1)) != KD_NULL) // przycisk wskaznika został zwolniony switch (event -> type) { pointer_select = false; // zakończenie działania aplikacji break; case KD_EVENT_QUIT: // obsługa przycisków case KD_EVENT_WINDOW_CLOSE: case KD_EVENT_INPUT: { // usunięcie danych o ścieżkach // sprawdzenie stanu przycisków gry PS_destruct (tiger); switch (event -> data.input.index) { // porządki w bibliotece EGL // skalowanie obrazu eglMakeCurrent (display,KD_NULL, case KD_IO_GAMEKEYSNC_A: scale = 1.0; KD_NULL,KD_NULL); break; eglDestroyContext (display,openvg_context); case KD_IO_GAMEKEYSNC_B: scale = 2.0; eglDestroySurface (display,window_surface); break; eglTerminate (display); case KD_IO_GAMEKEYSNC_C: scale = 3.0; eglReleaseThread (); break; // usunięcie okna case KD_IO_GAMEKEYSNC_D: scale = 4.0; kdDestroyWindow (window); break kdExit (0); } break; Display (display,window_surface,tiger); // zmiana rozmiaru okna } case KD_EVENT_WINDOW_RESIZE: // domyślne przetworzenie pozostałych zdarzeń Display (display,window_surface,tiger); default: break; kdDefaultEvent (event); // otrzymanie fokusa - odrysowanie okna break; case KD_EVENT_WINDOW_FOCUS: } if (event -> data.windowfocus.hasfocus) return 0; // wyjście Display (display,window_surface,tiger); } Software Developer s Journal 08/2007 www.sdjournal.org 27 Programowanie C/C++ Po określeniu odpowiednich pól w strukturze KDEvent opisu- jącej zdarzenie, umieszczenie zdarzenie w kolejce zdarzeń Funkcje obsługujące czas sprowadza się do wywołania funkcji: Biblioteka OpenKODE wykorzystuje wewnętrznie czas UST (ang. KDint kdPostEvent (KDEvent *event) unadjusted system time) zliczający ilość nanosekund, które upłynę- ły od północy 1 stycznia 1970 roku. Odczyt bieżącego czasu UST Usunięcie struktury KDEvent umożliwia funkcja: umożliwia funkcja kdGetTimeUST. Czas UST przechowywany jest void kdFreeEvent (KDEvent *event) w liczbach typu KDust (czyli KDint64). Dostępna jest również funk- cja kdTime, która pobiera uniwersalny czas koordynowany (UTC), Dla zdarzeń tworzonych przez użytkownika biblioteka Open- przechowywany w liczbach typu KDtime (także KDint64). Konwer- KODE rezerwuje identyfikatory o wartościach z przedziału od sję czasu UTC na czas uniwersalny GMT oraz czas lokalny reali- KD _ EVENT _ USER do KDINT32 _ MAX. Dane zdarzenia użytkowni- zują funkcje kdGmtime_r i kdLocaltime_r. Funkcje te korzystają ka przechowywane są w strukturze typu KDEventUser. Struktu- ze struktury KDtm, która zawiera pola: m_sec, tm_min, tm_hour, tm_ ra ta zawiera dwie unie value1 i value23, o wielkościach liczb mday, tm_mon, tm_year, tm_wday, tm_yday i tm_isdst, w pełni od- typu KDint64, których wewnętrzna budowa pozwala na różno- powiadające polom struktury tm z języka C. Wszystkie pola są typu rodne wykorzystanie. całkowitego KDint32. Ostatnią funkcją z opisywanej grupy jest kdUSTAtEpoch, która zwraca czas UST odpowiadający początkowi epoki (czyli północy Zdarzenia 1 stycznia 1970 roku) czasu UTC. Wynik (liczba typu KDust) mo- a funkcje wywoływane zwrotnie że mieć wartość ujemną. Funkcja kdUSTAtEpoch ułatwia konwer- Dołączenie lub odłączenie funkcji wywoływanej zwrotnie, ob- sję pomiędzy czasem UST i UTC. sługującej wybraną grupę zdarzeń, wymaga użycia funkcji: KDint kdInstallCallback (KDCallbackFunc *func, KDint eventtype, void *eventuserptr) W sytuacji, gdy funkcja zwrotna nie obsługuje części otrzy- mywanych zdarzeń, powinna zastosować domyślną obsługę Przypadki, gdy do obsługi zdarzenia lub grupy zdarzeń będzie zdarzenia wywołując kdDefaultEvent. W przeciwnym wypadku wywoływana funkcja zwrotna wskazana w parametrze func, informacja o danym zdarzeniu zostanie utracona i usunięta z określa kombinacja wartości parametrów eventtype i eventu- kolejki zdarzeń. serptr. Pierwszy z tych parametrów określa rodzaj zdarzenia, a Jeżeli jakieś zdarzenie lub grupa zdarzeń obsługiwana drugi wartość wskaznika na dane przekazywane przy wywoły- jest przez funkcje wywoływane zwrotnie, nie są one zgłasza- waniu zdarzeń (odpowiada to parametrowi eventuserptr przeka- ne przy wywołaniu kdWaitEvent. W przypadku, gdy wszystkie zywanego np. przy niektórych funkcjach). Podanie wartości 0 w zdarzenia obsługiwane są w ten sposób, główna pętla prze- miejscu parametru eventtype oznacza obsługę wszystkich zda- twarzania zdarzeń będzie wyglądała następująco: rzeń, natomiast podanie wartości KD _ NULL dla parametru eventu- serptr oznacza obsługę przez funkcję dowolnej wartości wskaz- KDEvent *event; nika na dane przekazywane przy wywoływaniu zdarzeń. Ten pro- while ((event = kdWaitEvent (-1)) != KD_NULL) sty mechanizm pozwala przykładowo na łatwe wyodrębnienie kdDefaultEvent (event); funkcji zwrotnych obsługujących różne okna aplikacji. Funkcja wy- woływana zwrotnie musi być zgodna z następującym typem: Usprawnienie obsługi zdarzeń przez funkcje wywoływane zwrotnie umożliwia funkcja: typedef void (KDCallbackFunc) (const KDEvent *event) KDint kdPumpEvents (void) która wykonuje takie przemieszczenie zdarzeń w kolejce (ang. event pump), aby w pierwszej kolejności obsługiwane były zda- rzenia odpowiadające zarejestrowanym funkcjom zwrotnym. Pozostałe zdarzenia umieszczane są w dalszej części kolejki. Zdarzenia wejścia-wyjścia Biblioteka OpenKODE zawiera obsługę szeregu lokalnych (działających na poziomie części API biblioteki) zdarzeń wej- ścia-wyjścia umożliwiających interakcję z użytkownikiem. Zdarzenia te są podzielone na grupy identyfikowane następu- jącymi stałymi: " KD _ IOGROUP _ GAMEKEYS przyciski obsługujące gry (z ob- sługą wielu przycisków jednocześnie), " KD _ IOGROUP _ GAMEKEYSNC przyciski obsługujące gry (bez obsługi wielu przycisków jednocześnie), " KD _ IOGROUP _ PHONEKEYPAD przyciski telefonu, Rysunek 4. Początkowe okno programu Sześcian " KD _ IOGROUP _ POINTER wskaznik (np. myszka), 28 www.sdjournal.org Software Developer s Journal 08/2007 OpenKODE Grupa ta jest identyfikowana stałą KD _ IOGROUP _ EVENT i nale- Funkcje narodowe i językowe żą do niej dwa zdarzenia wejściowe: KD _ IO _ EVENT _ USING _ BATTERY informacja o pracy na bateriach (dane binarne, war- Generalnie biblioteka OpenKODE nie zawiera wsparcia dla obsługi tość 1 urządzenie korzysta z baterii, wartość 0 urządzenie programów wielojęzycznych. Możliwe jest jednak odczytanie bieżą- korzysta z zasilania głównego) oraz KD _ IO _ EVENT _ LOW _ BAT- cego języka oraz kraju, a także strefy czasowej. Język (norma ISO TERY informacja o niskim poziomie naładowania baterii (dane 639-1) oraz kod kraju (norma ISO 3166-1 alpha 2), oddzielone zna- binarne, wartość 1 niski stan naładowania baterii, wartość 0 kiem podkreślenia, zwraca funkcja kdGetLocale. Na przykład dla baterie naładowane). Polski będzie to pl_PL . Odczyt bieżącej strefy czasowej, określo- Poza funkcjami z grup kdInputPoll dane związane ze zda- nej jako przesunięcie w stosunku do uniwersalnego czasu koordy- rzeniami wejściowymi można odczytywać korzystając z infor- nowanego (UTC), umożliwia funkcja kdGetTzOffset. macji umieszczanych w strukturze KDEvent. W tym celu wszyst- kie zdarzenia podzielone są na trzy rodzaje: KD _ EVENT _ IN- " KD _ IOGROUP _ JOGDIAL pokrętło jog dial, PUT _ POINTER zdarzenie związane ze wskaznikiem, KD _ " KD _ IOGROUP _ JOYSTICK dżojstik, EVENT _ INPUT _ STICK zdarzenie związane z dżojstikiem oraz " KD _ IOGROUP _ VIBRATE wibracja, KD _ EVENT _ INPUT pozostałe zdarzenia wejściowe. Z każ- " KD _ IOGROUP _ BACKLIGHT podświetlenie. dym rodzajem zdarzenia wejściowego związane są inne da- ne umieszczane w polu data (unii) struktury KDEvent. Są to od- Dla zdarzeń wejścia-wyjścia zależnych od implementacji specy- powiednio struktury: KDEventInputPointer inputpointer, KDE- fikacja OpenKODE rezerwuje indeksy z przedziału KD _ IO _ UN- ventInputStick inputstick oraz KDEventInput input. W każ- DEFINED KDINT32 _ MAX. Włączenie lub wyłączenie obsługi wy- dej z powyższych grup zdarzeń zawartość pola userptr struk- branej grupy zdarzeń wejściowych wymaga wywołania funkcji: tury KDEvent zależy od tego, czy jedno z okien aplikacji posiada fokus. Jeżeli tak, to przekazywana jest tam wartość parametru KDint kdInputEventEnable (KDint idx, KDint enable) eventuserptr funkcji tworzącej okno. Jeżeli żadne okno nie po- siada fokusa, przekazywana jest wartość KD _ NULL. Parametr idx określa rodzaj grupy zdarzeń i przyjmuje jedną z Struktura KDEventInput zawiera dwa pola. Pierwsze pole index wymienionych wcześniej wartości identyfikującą grupę, a para- (typ KDint32) określa rodzaj zdarzenia wejściowego. Drugie pole metr enable określa czy obsługa wskazanej grupy zdarzeń ma value zawiera unię przechowującą jedną z liczb typu: KDint32 (po- zostać włączona (KD _ TRUE) czy wyłączona (KD _ FALSE). Zdarze- le value.i), KDint64 (pole value.l) lub KDfloat32 (pole value.f), które nia wejścia-wyjścia mogą generować cztery rodzaje informacji: są używane w zależności od rodzaju danych zwracanych przez binarne (tylko wejście), KDint32 (wejście i wyjście), KDint64 (tyl- zdarzenie. Stałe opisujące zdarzenia wejściowe oraz zwracane ko wejście) oraz KDfloat32 (wejście i wyjście). Próbkowanie stanu przez te zdarzenia dane opisano w dalszej części tekstu. wybranych wejść z danej grupy zdarzeń wejścia-wyjścia umożli- Struktura KDEventInputPointer zawiera cztery liczby typu wiają funkcje z grupy kdInputPoll (odrębnie dla każdego rodza- KDint32. Pole index określa rodzaj zdarzenia wejściowego, po- ju danych wejściowych), pokazane są one na Listingu 2. Para- le select zawiera stan przycisku wskaznika (1 przycisk naci- metr startidx określa numer pierwszego odpytywanego zdarze- śnięty, 0 przycisk zwolniony), a dwa ostatnie pola x i y prze- nia, a parametr numidxs ilość kolejnych odpytywanych zdarzeń z chowują współrzędne położenia wskaznika. danej grupy zdarzeń. Nie ma możliwości jednorazowego odczy- Ostatnia opisywana struktura KDEventInputStick także za- tania danych zdarzeń pochodzących z różnych grup lub zdarzeń wiera cztery pola typu KDint32. Pole index zawiera numer (in- z jednej grypy, które zwracają różnego rodzaju informacje. Od- deks) dżojstika, który wygenerował zdarzenie wejściowe, a czytane dane umieszczane są w buforze wskazywanym w pa- trzy pozostałe pola x, y i z określają kąty obrotu drążka w kie- rametrze buffer. Na programie ciąży obowiązek zapewnienia ta- runku osi X, Y i Z (opcjonalnie). kiej ilości miejsca w buforze, aby pomieścić wszystkie pobierane dane. Analogiczne parametry posiadają funkcje z grupy kdOut- Grupy zdarzeń wejścia-wyjścia putSet, które odpowiadają za generowanie zdarzeń wyjściowych: Grupa zdarzeń KD _ IOGROUP _ GAMEKEYS związana jest z przyciskami obsługującymi gry, przy czym możliwa jest ob- KDint kdOutputSeti (KDint startidx, KDuint numidxs, ż sługa wielu jednocześnie naciśniętych przycisków. Biblio- const KDint32 *buffer) teka OpenKODE implementuje przyciski dostępne w stan- KDint kdOutputSetf (KDint startidx, KDuint numidxs, ż dardzie Java MIDP2, czyli w praktyce przyciski dostępne w const KDfloat32 *buffer) Listing 2. Funkcje z grupy kdInputPoll Zestawienie zdarzeń związanych z poszczególnymi grupami zdarzeń wejścia-wyjścia przedstawiono w dalszej części ar- KDint kdInputPollb (KDint startidx, KDuint numidxs, ż tykułu. Oczywiście od implementacji i specyfiki systemu oraz KDint32 *buffer) sprzętu zależy, które grupy zdarzeń wejścia-wyjścia są do- KDint kdInputPolli (KDint startidx, KDuint numidxs, ż stępne dla programu. Także w ramach niektórych grup zda- KDint32 *buffer) rzeń wejścia-wyjścia nie wszystkie zdarzenia muszą być ob- KDint kdInputPolll (KDint startidx, KDuint numidxs, ż sługiwane przez implementację. Sprawdzenie wszystkich tych KDint64 *buffer) zależności umożliwiają funkcji z grupy kdInputPoll. KDint kdInputPollf (KDint startidx, KDuint numidxs, ż Ponadto jest jeszcze grupa zdarzeń wejścia-wyjścia na- KDfloat32 *buffer) leżąca do zdarzeń występujących na poziomie całej aplikacji. Software Developer s Journal 08/2007 www.sdjournal.org 29 Programowanie C/C++ zane są dane binarne: 0 (przycisk zwolniony) i 1 (przy- Funkcje obsługi pamięci oraz pamięci cisk naciśnięty). Zapytanie o dostępność przycisków tele- fonu identyfikowane jest poprzez zdarzenie KD _ IO _ PHONE- lokalnej wątków (TLS) KEYPAD _ AVAILABILITY, a dane zwracane są w zmiennej typu Obsługę pamięci w OpenKODE zapewniają funkcje: kdMalloc, KDint32. Minimalna zwrócona wartość to 0xfff (dostępne kdFree i kdRealloc, które są odpowiednikami funkcji malloc, free przyciski podstawowe), wartość maksymalna 0x3fff (do- i realloc z języka C. Jedyna różnica polega na zgłaszaniu przez stępne wszystkie przyciski). kdMalloc i kdRealloc błędu KD_ENOMEM przy braku pamięci (funkcje Grupa zdarzeń związanych z wibracją, identyfikowana C nie modyfikowały wartości errno). stałą KD _ IOGROUP _ VIBRATE, zawiera zarówno zdarzenia wej- Wprawdzie obecna tymczasowa specyfikacja OpenKODE nie ściowe jak i wyjściowe. wspiera wielowątkowości, to jednak zdefiniowane zostały dwie Wyjście to głośność wibracji (stała KD _ IO _ VIBRATE _ funkcje obsługujące pamięć lokalną. Są to: kdGetTLS (pobranie VOLUME) i dostępna opcjonalnie jej częstotliwość (stała KD _ wskaznika do pamięci lokalnej) i kdSetTLS (zapis wskaznika do pamięci lokalnej). W przypadku wprowadzenia obsługi wielowąt- IO _ VIBRATE _ FREQUENCY). Obie wartości opisują dane ty- kowości, obie funkcje będą działały w pamięci lokalnej wątków. pu KDint32, przy czym głośność wyrażana jest liczbami z przedziału od 0 (cisza) do 1000 (maksymalna głośność), a częstotliwość określana jest w mHz (przykładowo 25.000 urządzeniach przenośnych. Przyciski można podzielić na oznacza 25Hz). Zdarzenia wejściowe są opcjonalne i zwra- dwie grupy: obowiązkowe (góra, lewo, prawo, dół, strzał) cają informacje o minimalnej (stała KD _ IO _ VIBRATE _ MIN- oraz opcjonalne (A, B, C i D). Odpowiadają im następujące FREQUENCY) i maksymalnej (stała KD _ IO _ VIBRATE _ MAXFRE- zdarzenia: KD _ IO _ GAMEKEYS _ UP, KD _ IO _ GAMEKEYS _ LEFT, QUENCY) dostępnej częstotliwości wibracji. Obie wartości KD _ IO _ GAMEKEYS _ RIGHT, KD _ IO _ GAMEKEYS _ DOWN, KD _ IO _ opisywane są liczbami typu KDint32 i wyrażane w mHz. GAMEKEYS _ FIRE, KD _ IO _ GAMEKEYS _ A, KD _ IO _ GAMEKEYS _ Zdarzenie związane z zapytaniem o dostępne zdarzenia z B, KD _ IO _ GAMEKEYS _ C i KD _ IO _ GAMEKEYS _ D. Z przyciska- grupy KD _ IOGROUP _ VIBRATE identyfikowane jest stałą KD _ mi związane są dane binarne: 0 (przycisk zwolniony) i 1 IO _ VIBRATE _ AVAILABILITY. Ponieważ opcjonalne zdarzenia (przycisk naciśnięty). Zapytanie o dostępność przycisków z tej grupy związane z częstotliwością wibracji muszą być obsługujących gry identyfikowane jest poprzez zdarze- dostępne wspólnie (nie ma możliwości, aby implementacja nie: KD _ IO _ GAMEKEYS _ AVAILABILITY, a dane zwracane są obsługiwała tylko jedno lub dwa), dane binarne zwracane w zmiennej typu KDint32. Minimalna zwrócona wartość to w wyniku zdarzenia KD _ IO _ VIBRATE _ AVAILABILITY, zawar- 31, co odpowiada dostępności podstawowych przycisków, te w liczbie typu KDint32, mogą przyjąć wyłącznie wartość maksymalna 511, gdy wszystkie przyciski są dostępne. 9 (brak obsługi częstotliwości wibracji) lub 31 (dostępność Analogiczną funkcję do grupy KD _ IOGROUP _ GAMEKEYS obsługi częstotliwości wibracji). spełnia następna grupa zdarzeń wejścia-wyjścia obsługu- Kolejną grupą zdarzeń wejścia-wyjścia są zdarzenia jąca przyciski gry, która identyfikowana jest stałą KD _ IO- związane ze wskaznikiem (stała KD _ IOGROUP _ POINTER). Za GROUP _ GAMEKEYSNC. Jedyna różnica w stosunku do pierwszej pośrednictwem zdarzeń z tej grupy mogą być obsługiwane grupy polega na braku obsługi wielu przycisków jednocze- różnego rodzaju urządzenia wskazujące, np. ekran dotyko- śnie. Zdarzenia związane z przyciskami, wśród których tak- wy, mysz albo gładzik (ang. trackpad). Warto zauważyć, że że występuje podział na obowiązkowe i opcjonalne, iden- rozważane jest utworzenie wydzielonej grupy zdarzeń prze- tyfikowane są stałymi: KD _ IO _ GAMEKEYSNC _ UP, KD _ IO _ GA- znaczonych wyłącznie do obsługi myszki. Z obsługą wskaz- MEKEYSNC _ LEFT, KD _ IO _ GAMEKEYSNC _ RIGHT, KD _ IO _ GAME- nika związane są trzy zdarzenia wejściowe identyfikowane KEYSNC _ DOWN, KD _ IO _ GAMEKEYSNC _ FIRE, KD _ IO _ GAMEKEY- stałymi: KD _ IO _ POINTER _ X, KD _ IO _ POINTER _ Y oraz KD _ SNC _ A, KD _ IO _ GAMEKEYSNC _ B, KD _ IO _ GAMEKEYSNC _ C i KD _ IO _ POINTER _ SELECT. Pierwsze dwa to współrzędne położe- IO _ GAMEKEYSNC _ D. Zapytanie o dostępność przycisków z tej nia wskaznika określane liczbami typu KDint32. Używane są grupy identyfikowane jest zdarzeniem KD _ IO _ GAMEKEYSNC _ AVAILABILITY. Zarówno zwracane dane związane z przyci- skami, jak i z zapytaniem o ich dostępność są takie same jak w przypadku poprzednio opisanej grupy zdarzeń KD _ IO- GROUP _ GAMEKEYS. Kolejną grupą zdarzeń wejścia-wyjścia są zdarze- nia związane z przyciskami telefonu identyfikowane stałą KD _ IOGROUP _ PHONEKEYPAD. Do przycisków telefonu zalicza się klawisze 0 9, *, # oraz dwa opcjonalne przyciski (le- wy i prawy) umieszczone pod wyświetlaczem. Wymienio- ne przyciski związane są ze zdarzeniami opisanymi sta- łymi: KD _ IO _ PHONEKEYPAD _ 0, KD _ IO _ PHONEKEYPAD _ 1, KD _ IO _ PHONEKEYPAD _ 2, KD _ IO _ PHONEKEYPAD _ 3, KD _ IO _ PHO- NEKEYPAD _ 4, KD _ IO _ PHONEKEYPAD _ 5, KD _ IO _ PHONEKEYPAD _ 6, KD _ IO _ PHONEKEYPAD _ 7, KD _ IO _ PHONEKEYPAD _ 8, KD _ IO _ PHONEKEYPAD _ 9, KD _ IO _ PHONEKEYPAD _ STAR, KD _ IO _ PHO- NEKEYPAD _ HASH, KD _ IO _ PHONEKEYPAD _ LEFTSOFT oraz KD _ IO _ PHONEKEYPAD _ RIGHTSOFT. Z przyciskami telefonu zwią- Rysunek 5. Efekt działania programu Quadric 30 www.sdjournal.org Software Developer s Journal 08/2007 OpenKODE współrzędne kartezjańskie, gdzie przestrzeń robocza okna odzwierciedla pierwszą ćwiartkę układu, a zakresy zwraca- Funkcje matematyczne nych współrzędnych zawierają się od zera do odpowiednio pomniejszonej o jeden wysokości lub szerokości okna mie- OpenKODE udostępnia typowe funkcje matematyczne, w zdecy- dowanej większości znane ze standardowych bibliotek języka C lub rzonego w pikselach. Trzecie zdarzenie KD _ IO _ POINTER _ SE- standardu POSIX: kdAcosf, kdAsinf, kdAtanf, kdAtan2f, kdCosf, LECT zwraca stan przycisku wskaznika w postaci danej binar- kdSinf, kdTanf, kdExpf, kdLogf, kdFabsf, kdPowf, kdSqrtf, kdCe- nej o dwóch możliwych wartościach: 1 (przycisk wskaznika ilf, kdFloorf, kdRoundf, kdInvsqrtf (odwrotność pierwiastka przyciśnięty) i 0 (przycisk wskaznika zwolniony). Zauważmy, kwadratowego) i kdFmodf. Wszystkie powyższe funkcje przyjmują i że OpenKODE udostępnia obsługę tylko jednego przycisku zwracają liczby typu KDfloat32. Ponadto API zawiera definicje wie- wskaznika. Nie ma także możliwości sprawdzenia dostęp- lu typowych stałych przydatnych w obliczeniach matematycznych: ności wskaznika. Następna grupa zdarzeń wejścia-wyjścia to KD_E_F, KD_PI_F, KD_PI_2_F, KD_2PI_F, KD_LOG2E_F, KD_LOG10E_ podświetlenie ekranu identyfikowane stałą KD _ IOGROUP _ BAC- F, KD_LN2_F, KD_LN10_F, KD_PI_4_F, KD_1_PI_F, KD_2_PI_F, KD_2_ KLIGHT. W tej grupie znajduje się jedno zdarzenie wyjściowe SQRTPI_F, KD_SQRT2_F, KD_SQRT1_2_F, KD_MAXFLOAT (największa (stała KD _ IO _ BACKLIGHT _ FORCE), które określa stopień pod- liczba zmiennoprzecinkowa), KD_INFINITY (nieskończoność ilo- raz 1.0F/0.0F), KD_NAN (symbol nieoznaczony iloraz 0.0F/0.0F), świetlenia ekranu za pomocą liczby typu KDint32. Wartość 0 KD_HUGE_VALF (nieskończoność iloraz 1.0F/0.0F), KD_DEG_TO_ oznacza domyślne podświetlenie (jest to wartość początko- RAD_F i KD_RAD_TO_DEG_F. wa), wartość niezerowa wymusza włączenie podświetlenia. Nie ma możliwości sprawdzenia dostępności podświetlenia. Obsługą pokrętła jog dial zajmują się zdarzenia z grupy KD _ IOGROUP _ JOGDIAL. Pokrętło opisane jest czterema przyciskami ciski, 1 kapturek oraz 1 kulkę. Ponadto zarezerwowane są kierunkowymi: góra, lewo (opcjonalny), prawo (opcjonalny), numery indeksów zdarzeń umożliwiające obsługę dodatko- dół oraz przyciskiem selekcji, które identyfikowane są po- wych 63 dżojstików, przy czym każdy może zawierać takie przez następujące zdarzenia wejściowe: KD _ IO _ JOGDIAL _ UP, same ilości elementów jak pierwszy dżojstik. Odczyt danych KD _ IO _ JOGDIAL _ LEFT, KD _ IO _ JOGDIAL _ RIGHT, KD _ IO _ JOG- dżojstika umożliwiają następujące zdarzenia wejściowe: DIAL _ DOWN i KD _ IO _ JOGDIAL _ SELECT. Wszystkie te zdarzenia KD _ IO _ JOYSTICK _ NUMSTICKS, KD _ IO _ JOYSTICK _ NUMBUTTONS, generują dane binarne, gdzie wartość 1 oznacza naciśnięcie KD _ IO _ JOYSTICK _ NUMHATS oraz KD _ IO _ JOYSTICK _ NUMBALLS, przycisku, a 0 zwolnienie przycisku. Zapytanie o dostępność które zwracają dane w liczbach typu KDint32. pokrętła jog dial identyfikowane jest poprzez zdarzenie: KD _ Kolejne zdarzenia wejściowe związane z obsługą po- IO _ JOGDIAL _ AVAILABILITY, gdzie dane zwracane są w zmien- jedynczego drążka w dżojstiku opisują następujące stałe: nej typu KDint32. Przy dostępności wszystkich przycisków KD _ IO _ JOYSTICK _ STICK _ NUMAXES ilość osi obrotu drążka zwracana jest wartość 31, w pozostałych przypadkach bę- (2 lub 3, liczba KDint32), KD _ IO _ JOYSTICK _ X, KD _ IO _ JOY- dzie to wartość 25. STICK _ Y, KD _ IO _ JOYSTICK _ Z kąty obrotu drążka w kie- Ostatnią grupą zdarzeń wejścia-wyjścia są zdarzenia runku osi X, Y i opcjonalnie Z (liczby KDint32, zakres war- związane z obsługą dżojstika (lub dżojstików) opisane stałą tości od -32768 do 32767), KD _ IO _ JOYSTICK _ HAT _ UP, KD _ KD _ IOGROUP _ JOYSTICK. W bibliotece OpenKODE dżojstik za- IO _ JOYSTICK _ HAT _ LEFT, KD _ IO _ JOYSTICK _ HAT _ RIGHT, KD _ wiera następujące elementy: jeden lub więcej drążków (ang. IO _ JOYSTICK _ HAT _ DOWN stan przycisków kapturka (dane sticks) z dwiema lub trzema osiami obrotu, jeden lub więcej binarne: wartość 1 przycisk naciśnięty, wartość 0 przycisk przycisków (ang. buttons), zero lub więcej kapturków (ang. zwolniony), KD _ IO _ JOYSTICK _ BALL _ X, KD _ IO _ JOYSTICK _ hats) oraz zero lub więcej kulek (ang. balls). OpenKODE BALL _ Y skumulowany obrót kulki w kierunku osi X i Y może obsłużyć dżojstik posiadający maksymalnie 16 drąż- (dane KDint32 z przedziału od KDINT32 _ MIN do KDINT32 _ ków, 512 przycisków, 16 kapturków oraz 16 kulek, przy zało- MAX) oraz KD _ IO _ JOYSTICK _ BUTTON stan pierwszego przy- żeniu, że pojedynczy drążek posiada maksymalnie 32 przy- cisku (dane binarne: wartość 1 przycisk naciśnięty, war- tość 0 przycisk zwolniony). Dane następnych przycisków identyfikowane są zdarzeniami o kolejnych numerach za- czynających się od stałej KD _ IO _ JOYSTICK _ BUTTON. Zdarzenia związane z kolejnymi drążkami pierwszego dżojstika identyfikowane są opisanymi wyżej stałymi powięk- szonymi o wartość KD _ IO _ JOYSTICK _ STRIDE (wartość 64). Na- tomiast zdarzenia związane z kolejnymi dżojstikami identyfi- kowane będą także powyższymi stałymi, ale powiększonymi o wartość 0x400. Gniazda sieciowe Model obsługi gniazd sieciowych biblioteka OpenKODE przejęła ze standardów BSD i POSIX, przy czym zmieniono składnię i nazwy części funkcji. Zasadnicza różnica polega na wykorzystaniu zdarzeń przy obsłudze gniazd, stąd część funkcji działa w trybie asynchronicznym (nie blokującym). Mniejsza jest także ilość obsługiwanych protokołów komu- Rysunek 6. Gra WakeBreaker nikacyjnych. Aktualnie OpenKODE wspiera wyłącznie ko- Software Developer s Journal 08/2007 www.sdjournal.org 31 Programowanie C/C++ munikację w dziedzinie Internetu protokoły TCP (transmi- sja połączeniowa) i UDP (transmisja bezpołączeniowa), oba Funkcje operujące na ciągach znaków działające na IPv4. Możliwe jednak, że w przyszłych wer- Większość funkcji OpenKODE operujących na ciągach znaków sjach biblioteka OpenKODE wzbogaci się o obsługę innych ma swoje odpowiedniki w bibliotekach standardowych języka C: protokołów komunikacyjnych. kdMemchr, kdMemcmp, kdMemcpy, kdMemmove, kdMemset, kdStrchr, Odpowiednikami funkcji obsługujących gniazda sieciowe kdStrcmp, kdStrlen, kdStrnlen (długość ciągu znaków z ograni- w standardach BSD/POSIX są następujące funkcje bibliote- czeniem maksymalnym), kdStrncat_s (łączenie ciągów znaków ki OpenKODE: kdNameLookup (gethostbyname), kdSocketCreate z ograniczeniem maksymalnej długości), kdStrncmp, kdStrcpy_s i (socket), kdSocketBind (bind), kdSocketGetName (getsockname), kdStrncpy_s (kopiowanie ciągów znaków z ograniczeniem maksy- kdSocketConnect (connect), kdSocketListen (listen), kdSocke- malnym). Funkcje z przyrostkiem _s są bezpieczniejszymi wersjami tAccept (accept), kdSocketSend (send), kdSocketSendTo (send- standardowych funkcji języka C. to), kdSocketRecv (recv) oraz kdSocketRecvFrom (recvfrom). W nawiasach podano nazwy funkcji BSD/POSIX. Bibliote- ka OpenKODE zawiera także odpowiedniki wybranych funk- SOCK _ TCP protokół TCP i KD _ SOCK _ UDP protokół UDP. cji narzędziowych BSD/POSIX przydatnych przy obsłudze Gniazdo w bibliotece OpenKODE identyfikowane jest po- gniazd sieciowych. Są to: kdHtonl (htonl), kdHtons (htons), przez strukturę KDSocket. Struktura ta nie jest jednak zgod- kdNtohl (ntohl), kdNtohs (ntohs), kdInetAton (inet _ aton) oraz na ze strukturą KDFile identyfikującą plik, stąd na gniazdach kdInetNtoa (inet _ ntoa). W nawiasach znajdują się oczy- nie są dopuszczalne typowe operacje plikowe. Z tego także wiście nazwy funkcji BSD/POSIX. Wspomniane wcześniej powodu OpenKODE zawiera odrębną funkcję kdSocketClose, rodzaje protokołów połączeniowych określają stałe: KD _ której zadaniem jest zamknięcie gniazda. Drugą niestandar- Listing 3. Program Sześcian (fragmenty) [...] kdSetWindowCaption (window,"Sześcian"); // obsługa przycisków gry // wyświetlenie okna void KeyHandler (const KDEvent *event) kdShowWindow (window,KD_WINDOWSTATUS_VISIBLE); { // włączenie obsługi przycisków gry // pobranie stanu przycisków kierunkowych kdInputEventEnable (KD_IOGROUP_GAMEKEYSNC,KD_TRUE); KDint32 keys; // dowiązanie funkcji zwrotnej obsługującej przyciski gry kdInputPollb (KD_IO_GAMEKEYS_UP,4,&keys); kdInstallCallback (KeyHandler,KD_EVENT_INPUT,KD_NULL); // KD_IO_GAMEKEYSNC_UP // włączenie obsługi wskaznika if (keys & 0x01) kdInputEventEnable (KD_IOGROUP_POINTER,KD_TRUE); rotatex -= 5.0; [...] // KD_IO_GAMEKEYSNC_LEFT // utworzenie licznika czasu sterującego wyświetleniem if (keys & 0x02) sceny 3D rotatey -= 5.0; KDTimer *timer = kdSetTimer (0,KD_TIMER_ONESHOT,KD_NULL); // KD_IO_GAMEKEYSNC_RIGHT // struktura z opisem zdarzeń if (keys & 0x04) const KDEvent *event; rotatey += 5.0; // dane do obsługi wskaznika // KD_IO_GAMEKEYSNC_DOWN KDint32 pointer_x,pointer_y; if (keys & 0x08) bool pointer_select = false; rotatex += 5.0; // główna pętla obsługi zdarzeń // przycisk strzału - wyjście z programu while ((event = kdWaitEvent (-1)) != KD_NULL) if (event -> data.input.index == KD_IO_GAMEKEYSNC_FIRE) switch (event -> type) { { // utworzenie i sygnalizacja zdarzenia [...] KDEvent *ev = kdCreateEvent (); // wyświetlenie zawartości sceny 3D ev -> type = KD_EVENT_QUIT; case KD_EVENT_TIMER: kdPostEvent (ev); kdCancelTimer (timer); } Reshape (display,window_surface); } Display (display,window_surface); // program główny timer = kdSetTimer (0,KD_TIMER_ONESHOT,KD_NULL); KDint kdMain (KDint argc, const KDchar **argv) break; { [...] // utworzenie okna } KDWindow *window = kdCreateWindow (KD_NULL,KD_NULL); // wyjście // rozmiary okna return 0; kdSetWindowSize (window,500,500); } // tytuł okna 32 www.sdjournal.org Software Developer s Journal 08/2007 OpenKODE " KD _ EVENT _ SOCKET _ CONNECT _ COMPLETE zdarzenie gene- rowane po ukończeniu połączenia gniazda (funkcje kdSoc- Funkcje licznika czasu ketConnect, kdSocketListen i kdSocketAccept), " KD _ EVENT _ NAME _ LOOKUP _ COMPLETE zakończenie pobie- OpenKODE obsługuje wiele liczników czasu, zarówno wywoływa- nych jednorazowo jak i periodycznie, z których każdy generuje zda- rania adresu węzła (funkcja kdNameLookup). rzenie po upływie zaprogramowanego czasu. Utworzenie licznika wymaga wywołania funkcji: Funkcje kdSocketCreate, kdSocketAccept, kdNameLookup, kdNa- meLookupCancel zawierają dodatkowy parametr eventuserptr, KDTimer *kdSetTimer (KDint64 interval, KDint periodic, void który jest przekazywany przy wystąpieniu zdarzenia związa- *eventuserptr) nego z gniazdem jako parametr userptr struktury KDEvent. Z Sposób działania licznika określa parametr periodic, który przyjmu- czterema pierwszymi zdarzeniami przekazywane są dane za- je jedną z trzech wartości: KD_TIMER_ONESHOT (licznik wywoływany warte w następujących strukturach: KDEventSocketReadable, jednokrotnie po upływie czasu nie krótszego niż określony w para- KDEventSocketWritable, KDEventSocketError i KDEventSocketIn- metrze interval), KD_TIMER_PERIODIC_AVERAGE (licznik wywoływa- coming. Struktury te zawierają jedynie pole socket zawierające ny periodycznie po upływie czasu zbliżonego do określonego w pa- wskaznik na strukturę KDSocket identyfikującą gniazdo, z któ- rametrze interval), KD_TIMER_PERIODIC_MINIMUM (licznik wywoły- rym związane jest dane zdarzenie. wany periodycznie po upływie czasu nie krótszego niż określony w Przy zdarzeniu KD _ EVENT _ SOCKET _ CONNECT _ COMPLETE da- parametrze interval). Czas reakcji licznika, zawarty w parametrze ne przekazywane są w strukturze KDEventSocketConnect, któ- interval, określany jest w nanosekundach, przy czym rzeczywista ra oprócz pola socket, zawiera także pole error typu KDint32. dokładność licznika zależy oczywiście od możliwości systemu ope- racyjnego. W ostatnim parametrze można umieścić wskaznik na da- Pole to może przyjąć wartość jednego z następujących ko- ne przekazywane wraz ze zdarzeniem licznika (pole userptr struk- dów błędów: KD _ EADDRINUSE, KD _ EAFNOSUPPORT, KD _ EALRE- tury KDEvent), które identyfikowane jest stałą KD_EVENT_TIMER. ADY, KD _ ECONNREFUSED, KD _ ECONNRESET, KD _ EHOSTUNREACH, KD _ Funkcja kdSetTimer zwraca wskaznik do struktury KDTimer EINVAL, KD_EIO, KD _ EISCONN lub KD _ ETIMEDOUT. W przypadku identyfikującej licznik. Struktura ta jest potrzebna do usunięcia braku błędu pole error przyjmuje wartość 0. Z ostatnim zda- licznika przy użyciu funkcji kdCancelTimer. rzeniem KD _ EVENT _ NAME _ LOOKUP _ COMPLETE związane są da- ne przekazywane w strukturze KDEventNameLookup. W przy- padku poprawnego zakończenia funkcji, adres IP żądanego dową funkcją jest kdNameLookupCancel, która przerywa po- węzła umieszczany jest w polu result będącym wskaznikiem branie adresu IPv4 rozpoczęte wywołaniem asynchronicz- do struktury KDSockaddr (lub KDSockaddr _ in), a pole error ty- nej funkcji kdNameLookup. pu KDint32 przyjmuje wartość 0. Wielkość zwróconej struk- Podstawową strukturą opisującą adres sieciowy gniaz- tury zawiera pole resultlen typu KDint32. Struktura KDEvent- da jest struktura KDSockaddr. Zawiera ona dwa pola: sa _ fa- NameLookup zawiera jeszcze pole more typu KDboolean, które mily rodzina protokołów (typ KDint16) oraz sa _ data wła- wskazuje, czy w kolejce zdarzeń dostępne jest kolejne zda- ściwy adres (typ KDuint8[14]). Ponieważ OpenKODE obsłu- rzenie zawierające następny pobrany adres IP. W przypadku guje wyłącznie protokoły internetowe, pole sa _ family może wystąpienia błędu, wspomniane pole error przyjmuje jedną z przyjąć jedynie wartość KD _ AF _ INET, która jest odpowied- wartości: KD _ HOST _ NOT _ FOUND nie odnaleziono węzła, KD _ nikiem stałej AF _ INET z BSD/POSIX. Z tą rodziną protoko- NO _ DATA nazwa węzła jest poprawna, ale nie posiada ad- łów związana jest struktura KDSockaddr _ in, która w prakty- resu, KD _ NO _ RECOVERY nienaprawialny błąd serwera nazw, ce poprzez rzutowanie zastępuje strukturę bazową KDSoc- KD _ TRY _ AGAIN tymczasowy błąd serwera nazw, możliwe kaddr. Struktura KDSockaddr _ in posiada trzy pola: sin _ fa- ponowne zapytanie. mily rodzina protokołów (odpowiednik funkcjonalny pola sa _ family struktury KDSockaddr, wartość KD _ AF _ INET, typ Obsługa okien KDint16), sin _ address adres IPv4 (typ KDuint32) oraz sin _ Jak już napisaliśmy na wstępie, biblioteka OpenKODE do two- port numer portu (typ KDuint16, sieciowa kolejność baj- rzenia kontekstów graficznych w systemach udostępniających tów). Dostępna jest także stała KD _ INADDR _ ANY, będąca od- mechanizm okien wykorzystuje bibliotekę EGL. Okna w biblio- powiednikiem stałej INADDR _ ANY standardów BSD/POSIX. Z tece OpenKODE identyfikowane są za pomocą uchwytów obsługą gniazd sieciowych w bibliotece OpenKODE związa- struktur KDWindow. ne są następujące zdarzenia: Utworzenie okna dla wybranej płaszczyzny wyświetlania, wygenerowanej przez bibliotekę EGL, sprowadza się do wy- " KD _ EVENT _ SOCKET _ READABLE sygnalizacja, że gniazdo wołania jednej z dwóch funkcji: jest w trybie do odczytu (funkcje kdSockedBind i kdSocke- tAccept), KDWindow *kdCreateFullScreenWindow (EGLDisplay ż " KD _ EVENT _ SOCKET _ WRITABLE sygnalizacja, że gniazdo jest display, const void *mode, void *eventuserptr) w trybie do zapisu (funkcje kdSockedCreate i kdSocketAccept), KDWindow *kdCreateWindow ż " KD _ EVENT _ SOCKET _ ERROR sygnalizacja wystąpienia błę- (EGLDisplay display, void *eventuserptr) du (funkcje kdSockedCreate, kdSocketAccept, kdSocketSend, kdSocketRecv, kdSocketSendTo i kdSocketSendTo), Różnica w działaniu powyższych funkcji sprowadza się " KD _ EVENT _ SOCKET _ INCOMING gniazdo wykryło połącze- oczywiście do rodzaju utworzonego okna. Pierwsza z funk- nie przychodzące lub wystąpił błąd (funkcje kdSocketLi- cji utworzy okno obejmujące cały ekran, które domyślnie sten i kdSocketAccept), jest widoczne. Druga funkcja tworzy okno początkowo nie- Software Developer s Journal 08/2007 www.sdjournal.org 33 Programowanie C/C++ widoczne i jest dostępna wyłącznie w systemach obsługują- Z obsługą okien związane są następujące zdarzenia: cych wiele okien. Parametr display zawiera wskaznik na płaszczyznę wy- " KD _ EVENT _ WINDOW _ CLOSE zamknięcie okna, świetlenia utworzoną przez bibliotekę EGL. Wskaznik na " KD _ EVENT _ WINDOW _ RESIZE zmiana rozmiaru okna, dane, zawarty w parametrze eventuserptr, przekazywany " KD _ EVENT _ WINDOW _ FOCUS uzyskanie, bądz utrata foku- jest przy obsłudze zdarzeń związanych z oknami jako war- sa. tość pola userptr struktury KDEvent. W przypadku podania wartości KD _ NULL, pole userptr otrzyma wartość wskazni- Przy wystąpieniu dwóch pierwszych zdarzeń nie są przekazy- ka na uchwyt okna, z którym związane jest zdarzenie. Pa- wane dodatkowe informacje, natomiast rametr mode funkcji kdCreateFullScreenWindow może przyjąć z ostatnim zdarzeniem związana jest struktura KDEven- jedynie wartość KD _ NULL. Usunięcie okna wymaga wywo- tWindowFocus, która zawiera jedno pole hasfocus (liczba typu łania funkcji: KDint). Wartość 0 oznacza, że okno utraciło fokus, natomiast uzyskanie fokusa sygnalizowane jest wartością 1. W każdym void kdDestroyWindow (KDWindow *window) z trzech powyższych zdarzeń wartość pola userptr struktury KDEvent zawiera dane przekazane w parametrze eventuserptr Przy usuwaniu okna zwalniane są także wszystkie przydzie- przy wywołaniu funkcji kdCreateWindow lub kdCreateFullScre- lone wraz z nim zasoby. Należy jednak pamiętać, aby zaso- enWindow lub wartość uchwytu okna, która jest przyjęta auto- by przydzielone przez bibliotekę EGL zwolnić przed usunię- matycznie przez bibliotekę. ciem okna. Początkowe rozmiary i położenie okna utworzonego przy Programy przykładowe użyciu funkcji kdCreateWindow są nieokreślone. Zmianę tych Programy przykładowe kompilowano w systemie operacyj- parametrów umożliwiają funkcje: nym Microsoft Windows XP SP2 przy użyciu kompilatora Mi- crosoft Visual C++ 2005 Express Edition z pakietem Micro- KDint kdSetWindowPosition (KDWindow *window, KDint x, KDint y) soft Platform SDK for Windows Server 2003 R2. Wykorzysta- KDint kdSetWindowSize (KDWindow *window, KDint width, ż no implementację OpenKODE autorstwa firmy Acrodea oraz KDint height) implementację bibliotek EGL, OpenGL ES i OpenVG z pakie- tu Rasteroid 3.1 firmy Hybrid Graphics. Użyta implementacja Zmianę statusu widzialności okna utworzonego przy użyciu biblioteki OpenKODE została skompilowana jako jednowątko- funkcji kdCreateWindow umożliwia funkcja: wa, stąd wymagana jest konsolidacja programu z jednowąt- kowymi bibliotekami RTL. Wymaga to modyfikacji standar- KDint kdShowWindow (KDWindow *window, KDint status) dowych ustawień w kompilatorze Visual C++ 2005 Express. Przykładowe programy konstruowano z zamiarem ilustra- której parametr status przyjmuje jedną z trzech wartości: KD _ WINDOWSTATUS _ HIDDEN okno niewidoczne, KD _ WINDOWSTATUS _ VISIBLE okno widoczne oraz KD _ WINDOWSTATUS _ MINIMIZED Funkcje obsługujące system plików okno zminimalizowane. Kolejne dwie funkcje umożliwiające Zdecydowaną większość funkcji obsługujących system plików bi- modyfikację stanu okna to: blioteka OpenKODE przejęła bezpośrednio z języka C i standar- du POSIX: kdFopen, kdFclose, kdFflush, kdFread, kdFwrite, KDint kdActivateWindow (KDWindow *window) kdGetc, kdPutc, kdFgets, kdFEOF, kdFerror, kdClearerr, kdFse- KDint kdSetWindowCaption (KDWindow ż ek, kdFtell, kdMkdir, kdRmdir, kdRename, kdRemove, kdTrunca- *window, const KDchar *caption) te, kdStat, kdFstat, kdOpenDir, kdReadDir, kdCloseDir, kdGet- Free (zwolnienie pamięci przydzielonej na nazwę pliku lub katalo- Pierwsza z nich zmienia status okna na aktywny (okno otrzy- gu), kdChdir oraz kdGetCwd. muje fokus). Od implementacji zależy zachowanie okna czy Funkcje obsługujące pliki korzystają ze struktury KDFile. Do- stępne są także odpowiedniki stałych: KD _ EOF, KD _ SEEK _ SET, jest ono w momencie wywołania tej funkcji ukryte, czy zminima- KD _ SEEK _ CUR i KD _ SEEK _ END. Dane o pliku lub katalogu (lub lizowane. Druga funkcja pozwala na zmianę tytułu okna na ciąg urządzeniu identyfikowanym w systemie jako plik), pobierane znaków umieszczony w parametrze caption (w formacie UTF- przez funkcje kdStat i kdFstat, zwracane są w strukturze KDStat. 8). Od implementacji zależy gdzie i w jaki sposób wyświetlany Struktura ta zawiera następujące pola: st _ size rozmiar pliku w jest tytuł okna, a także jaka jest jego maksymalna długość. bajtach, st _ mtime czas ostatniej modyfikacji oraz st _ mode Ostatnie funkcje operujące na oknach spełniają zadania informacja o pliku/katalogu oraz prawach dostępu. Odczyt ostat- pomocnicze. Pierwsza: niego pola odbywa się za pośrednictwem makr: KD _ ISREG czy jest plik, KD _ ISDIR czy jest katalog, KD _ READABLE czy są pra- void *kdGetWindowNativeType (KDWindow *window) wa odczytu pliku/katalogu oraz KD _ WRITABLE czy są prawa za- pisu pliku/katalogu. zwraca wskaznik do uchwytu okna specyficznego dla danego Funkcje obsługujące katalogi (kdOpenDir, kdReadDir i kdC- loseDir) wykorzystują strukturę KDDir. Przy odczycie następne- systemu. Uchwyt ten jest wymagany przez bibliotekę EGL przy go pliku w wybranym katalogu funkcja kdReadDir zwraca struktu- tworzeniu powierzchni funkcja eglCreateWindowSurface. Dru- rę KDDirent. Struktura ta zawiera jedno pole d _ name określające ga funkcja zwraca położenie lewego górnego narożnika okna: nazwę odczytanego pliku. KDint kdGetWindowPosition (KDWindow *window, KDint *x, KDint *y) 34 www.sdjournal.org Software Developer s Journal 08/2007 OpenKODE określany jest na podstawie wartości pola data.input.index struktury KDEvent. Wciśnięcie tego ostatniego przycisku po- Asercje i dziennik komunikatów woduje wygenerowanie zdarzenia KD _ EVENT _ QUIT i w konse- Wzorem języka C biblioteka OpenKODE zawiera makro kdAssert kwencji zakończenie działania programu. Obroty sześcianu obsługujące asercje. Programista może zmienić standardową ob- umożliwia zarówno myszka, jak i wspomniane przyciski kie- sługę asercji definiując funkcję: runkowe gry. Cykliczne rysowanie sceny 3D reguluje licznik czasowy uruchamiany każdorazowo po narysowaniu sceny. void kdHandleAssertion (const KDchar *condition, const KDchar Początkowy wygląd okna programu przedstawia Rysunek 4. *filename, KDint linenumber) Dodatkowo w ramach testów implementacji OpenKODE firmy której parametrami są kolejno: generowany komunikat, nazwa pli- Acrodea skompilowano dwa programy przykładowe pocho- ku oraz numer linii programu, w którym został spełniony warunek dzące z pakietu intent GamePlayer ADK firmy Tao Group. Pa- asercji. Zapis informacji do dziennika komunikatów (logu) realizu- kiet ten zawiera, oprócz bibliotek OpenGL ES i EGL, imple- je funkcja: mentację biblioteki OpenKODE. Pierwszym przetestowanym w ten sposób programem był Quadric, który wyświetla kulę void kdLogMessage (const KDchar *string) pokrytą teksturą (Rysunek 5.). Podczas kompilacji i w trak- Dezaktywacja obsługi asercji i dziennika komunikatów sprowadza cie działania programu nie stwierdzono żadnych problemów. się do zdefiniowania w programie makra KD_NDEBUG. Drugim programem była prosta gra WakeBreaker. W tym wy- padku kompilacja programu wymagała niewielkiej ingerencji w kod zródłowy, ale nie było to związane z biblioteką Open- cji wykorzystania biblioteki OpenKODE, stąd większość ele- KODE. Niestety wystąpiły problemy z prawidłową obsługą mentów dotyczących OpenGL ES i OpenVG pochodzi z pro- przycisków, co jest najprawdopodobniej związane z błędami gramów przykładowych dostępnych w wymienionych wyżej w implementacji biblioteki OpenKODE. Zrzut przykładowego pakietach oraz w referencyjnej implementacji biblioteki Ope- ekranu gry przedstawiamy na Rysunku 6. nVG autorstwa Khronos Group. Program przedstawiony na Listingu 1. rysuje przy uży- Podsumowanie ciu biblioteki OpenVG popularny rysunek głowy tygrysa. OpenKODE to najnowszy projekt Khronos Group, często okre- Poruszanie wyświetlanym obrazem umożliwia przycisk ślany jako odpowiednik pakietu DirectX na urządzenia przeno- wskaznika, czyli, w przypadku użytej implementacji, lewy śne. Jak wspomnieliśmy na wstępie, docelowo OpenKODE po- przycisk myszki. Dodatkowo przyciski gry: KD _ IO _ GAME- łączy pięć otwartych standardów opracowywanych przez Khro- KEYSNC _ A, KD _ IO _ GAMEKEYSNC _ B, KD _ IO _ GAMEKEYSNC _ C i nos Group: OpenGL ES, OpenVG, OpenMAX, OpenSL ES oraz KD _ IO _ GAMEKEYSNC _ D (w stosowanej implementacji są to EGL. Pytanie może budzić celowość obecności w standardzie przyciski 1, 2, 3 i 4) umożliwiają powiększenie obrazu od dwóch bibliotek graficznych: OpenGL ES i OpenVG. Jednak jednego do czterech razy. Ponieważ są to przyciski opcjo- projektowano je dla tak odmiennych zastosowań, że rozdziele- nalne, przed włączeniem obsługi związanej z nimi gru- nie grafiki trójwymiarowej od dwuwymiarowej na urządzeniach py zdarzeń wejścia-wyjścia, program, korzystając z funk- mobilnych wydaje się być w pełni uzasadnione. Wystarczy za- cji kdInputPolli, sprawdza ich dostępność. Warto zwrócić uważyć, że do implementacji na urządzeniu przenośnym prze- uwagę na to, że umożliwienie programowi przetwarzania glądarki plików Flash i SVG, czy też wyświetlenia mapy drogo- zdarzeń związanych ze wskaznikiem i przyciskami gry wy- wej w zupełności wystarczy biblioteka grafiki dwuwymiarowej. magało dwukrotnego wywołania funkcji kdInputEventEna- Najbardziej rozpowszechnionym konkurentem OpenKO- ble. Obsługa wszystkich zdarzeń zawarta jest w pojedyn- DE jest JavaME. Java jest stosunkowo łatwa w implementacji, czej pętli umieszczonej w ciele funkcji kdMain. Początkowe a jej wersję opracowaną na potrzeby urządzeń przenośnych okno programu przedstawia Rysunek 3. można w znacznym stopniu konfigurować. Jednak główną wa- Drugi przykładowy program (Listing 3.) korzystając z bi- dą rozwiązań opartych o języki wykorzystujące kod pośredni, blioteki OpenGL ES rysuje wielobarwny sześcian. W sto- jakim jest Java, jest ich mniejsza szybkość w stosunku do pro- sunku do pierwszego przykładu zmieniono obsługę przyci- gramów kompilowanych. sków gry tworząc wywoływaną zwrotnie funkcję KeyHandler. Także inne systemy operacyjne dostępne na urządzeniach Stan przycisków kierunkowych gry odczytywany jest za po- przenośnych (Symbian, Palm OS, Windows Mobile) oferują średnictwem pojedynczego wywołania funkcji kdInputPollb, a bezpośrednio lub pośrednio funkcjonalność biblioteki OpenKO- stan przycisku strzał (w stosowanej implementacji spacja) DE. Jednak z samej definicji są to rozwiązania zależne od sys- temu operacyjnego, co znacznie zmniejsza ich przenośność. Za docelowym wyborem OpenKODE przemawia otwartość W Sieci standardu i możliwość kompleksowej obsługi wszystkich ele- mentów urządzenia przenośnego. Nadzieję na rozpowszech- " http://www.khronos.org specyfikacje bibliotek OpenKODE, nienie OpenKODE (bo pewności oczywiście nie ma) daje du- OpenGL ES, OpenVG, EGL, OpenMAX i OpenSL ES, ża ilość firm wspierających nowy standard, w tym obecność " http://www.acrodea.co.jp/en/ implementacja biblioteki Open- takich gigantów jak Nokia, NVIDIA, Samsung, Sony czy Sym- KODE, " http://www.hybrid.fi pakiet Hybrid Rasteroid 3.1 z implemen- bian. OpenKODE pojawia się w odpowiednim momencie, gdy tacjami bibliotek OpenGL ES, OpenVG i EGL, rozwiązania mobilne oferują coraz większe możliwości, a ich " http://tao-group.com pakiet intent GamePlayer ADK. wykorzystanie zdaje się być prostsze, gdy mamy do czynienia z otwartymi i wieloplatformowymi standardami. n Software Developer s Journal 08/2007 www.sdjournal.org 35