gui tut1


GUI Tutorial v1.0
Autor: Jarek Pelczar
E-mail: jpelczar@interia.pl
Licencja: Common Creative

Ten tutorial można traktować jako dodatek do kursu OS.
Najpierw trzeba napisać sterownik do karty graficznej, musi udostępniać funkcje rysujące, czyli putpixel, linia, prostokąt,
wypełniony prostokąt, kopiowanie bitmap na ekran, i inne.
Na tej podstawie można zrobić proste GUI. Jak mają być okna to musi być jakaś struktura opisująca
je np.
typedef struct {
int id;
int x,y;
int width,height;
char * title;
} window_t;

do menedżera okien trzeba też dodać tzw. stos okien, czyli np. okno aktywne jest na pozycji 0
a okna, które są zakryte na dalszych pozycjach, np.
#define MAX_WINDOWS 8
window_t * window_stack[MAX_WINDOWS];
Na ostatniej pozycji można dać okno pulpitu.
Np. jak zaznaczasz jakieś okno, to trzeba je dać na pozycję 0, ale wcześniej wszystkie okna o numerze
w stosie okien niższym przesunąć o 1 w prawo.

Inną sprawą jest wyznaczane regionów okna podczas rysowania, czyli tych części, które są widoczne.
Najprościej jest użyć mapy pikseli, czyli każdemu pikselowi na ekranie odpowiada ID okna.
Na przykład:
unsigned char pixel_map[200][320]; /* dla 320x200 */

i podczas rysowania pixela można sprawdzać czy pixel_map[y][x] jest równe ID okna programu,
jak nie to nie rysuje tam pixela.
Przy przesuwaniu oknien (czyli zmiana rozmiaru, pozycji czy zmiana położenia okna w stosie okien)
trzeba tę mapę zaktualizować.
Można iść od końca stosu i wypełniać obszary mapy np.
int i;
for(i=MAX_WINDOWS-1;i>=0;i--)
{
if(window_stack[i])
{
wypelnij_obszar(window_stack[i]->x,window_stack[i]->y,
window_stack[i]->width,window_stack[i]->height,
window_stack[i]->id);
}
}

a funkcja wypelnij_obszar wypełnia mapę pixeli numerami ID okna.

Inna metoda, to lista prostokątów widocznych. Jest bardziej skomplikowana
w obsłudze, ale bardziej wydajna podczas rysowania i umożliwia stosowanie
akceleracji sprzętowej do rysowania grafiki. Dla każdego okna można trzymać
listę obszarów widocznych. Gdy okno zostanie przesunięte, to trzeba tę listę aktualizować
i jest to dość czasochłonne. Rysowanie można zoptymalizować wydzielając 3 rodzaje
widoczności okna:
- widoczny całkowicie (nie trzeba sprawdzać listy obszarów widocznych)
- widoczny częściowo (trzeba sprawdzać listę obszarów widocznych)
- niewidoczny (funkcja nie robi nic, tylko wraca od razu)

Oczywiście funkcje dla poprzedniej metody można zoptymalizować w asemblerze,
że to będzie chodzić dość szybko. Samego putpixel nie ma sensu optymalizować,
ale np. rysowanie linii, wypełnianie obszarów i wyświetlanie bitmap tak.
Poprzednia metoda ma także tę zaletę, że można stosować okna o dowolnych
kształtach. Tylko trzeba trochę przerobić te funkcje do aktualizacji mapy pixeli.

W menedżerze oknien można dodać także okna podrzędne, takie, które nie wychodzą
poza obszar okna nadrzędnego. Ale na początek wystarczy prosty menedżer okien.

Dodawanie widgetów, czyli elementów okna jest identyczne w przypadku obu metod.
Na początek w zupełności wystarczą widgety prostokątne, bez bajerów.
Widget może opisywać struktura
typedef struct __widget_t widget_t;
struct __widget_t {
int x,y;
int width,height;
int selected:1;
widget_t * next;
widget_t * prev;
window_t * window;
void (* mouse_click)(widget_t *,int x,int y,int button);
void (* mouse_move)(widget_t *,int x,int y);
void (* key_pressed)(widget_t *,int key);
void (* draw)(widget_t *);
};
To jest tylko prosty przykład. x,y - pozycja w oknie, widgth,height - szerokość i wysokość
selected - oznacza czy widget został zaznaczony
next,prev - lista widgetów w oknie
mouse_click - funkcja wywoływana jak klikniemy myszą na widgecie
mouse_move - funkcja wywoływana jak przesuniemy myszą nad widgetem
key_pressed - wywołana jak naciśniemy klawisz i widget jest zaznaczony
draw - rysowanie widgeta
window - okno, w którym jest widget
do struktury okna trzeba dodać listę widgetów.

czyli
typedef struct {
....
...,
char * title
widget_t * widget_list;
widget_t * selected_widget;
} window_t;

np. dla przycisku wystarczą funkcje draw i mouse_click.
No ale zdarzenia na widgetach trzeba przecież sygnalizować.
Można do tego użyć komunikacji między procesami.
Najprostszą formą IPC sa tzw. pulsy czyli wiadomości
o stałej długości. Dla GUI powinne w zupełności wystarczyć
tablica 10 intów.
np.
typedef struct __pulse_t {
int sender; /* PID procesu wysyłającego wiadomość */
int params[10];
struct __pulse_t * next; /* wskaźnik na następny puls */
} pulse_t;
i w strukturze opisującej zadanie można dodać taką listę
pulsów.
No i system może udostępniać funkcje do odbierania i wysyłania pulsów.
Oczywiście po stronie użytkownika nie trzeba pola next w strukturze pulsu.
Te funkcje w polach widgetu można zostawić, bo przecież serwer GUI też
może mieć wbudowane przyciski, bitmapy, pola textowe, żeby było szybciej.
I mogą być też widgety zewnętrzne, czyli te funkcje tylko wysyłają komunikaty
do procesu.

Np. może być takie coś w programie użytkownika

/* utworzenie okna */
int wnd=gui_create_window(10,10,320,200,"Okno");
/* utworzenie przycisku */
int przycisk=gui_create_button(30,30,80,30,"Przycisk");
/* dodanie przycisku do okna */
gui_window_add_widget(wnd,przycisk);

for(;;)
{
pulse_t puls;
pulse_receive(&puls);
switch(puls.params[0]) /* kod komunikatu */
{
case WM_QUIT: /* zamknięcie okna */
gui_close_window(wnd);
return;
case WM_BUTTON: /* naciśnięty przycisk */
if(puls.params[1]==wnd &&
puls.params[2]==przycisk)
{
gui_maximize_window(wnd); /* maksymalizacja okna */
}
break;
}
}

No i program główny, który pisze użytkownik może wyglądać tak.
Ja użyłem tutaj wymyślonych funkcji, ale są one w miarę proste
do realizacji.

Serwer GUI po prostu wysyła i odbiera komunikaty. Jest go prościej
napisać, bo działa tylko w jednym wątku i nie trzeba stosować
blokowania struktur.

Sterowniki do grafiki można dać serwerze GUI, tylko musi mieć on
umożliwiony bezpośredni dostęp do sprzętu.
Może lepiej zrobić serwer na kilku wątkach. Np. jeden odpowiada za
urządzenia wejścia (mysz, klawiatura) a inny za komunikaty.
Albo w ogóle lepiej zrobić osobne serwery dla myszy, klawiatury i
innych rzeczy. Czyli keyboard_server, mouse_server, gui_server.
keyboard_server i mouse_server wysyłają komunikaty do gui_server.
gui_server jako jedyny powinien mieć dostęp do hardware'u.

Inną sprawą jest rysowanie tekstu. Można zastosować czcionki bitmapowe,
bo są najprostsze w użyciu. Ładniej wyglądają czcionki TrueType, ale trzeba
użyć biblioteki freetype najlepiej w wersji 2. Jest ona trochę trudna w obsłudze,
ale czcionki z właczoną opcją smooth wyglądają bardzo ładnie.
Jeśli budujemy server GUI, to sprawa jest prosta. Wystarczy, że freetype załaduje sobie
czcionkę do RAMu. Tylko, że to wymaga w miarę dobrej biblioteki C, ale można prostą zrobić
samemu.


Wyszukiwarka

Podobne podstrony:
tut1 4
tut1 1
BeSweet GUI v0 7b4
gui
tut1 1
tut1 4
tut1 2
Focus S Series Hand Held and GUI Instrukcja Obsługi
tut1 4
tut1
Scripting GUI TclTk
block gui
KasperskyUpdater GUI change log rus
tut1 3
spanish pronunspelling gue gui?ited
TUT1
tut1 1

więcej podobnych podstron