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