2007.04_USB Device Explorer_[Programowanie].pdf

(1134 KB) Pobierz
439114674 UNPDF
dla programistów
USB Device Explorer
USB Device Explorer
Wszystkie informacje o koniguracji sprzętowej oraz programowej systemu Linux są zawarte w plikach
znajdujących się w katalogu /proc . Informacje zgromadzone w /proc najczęściej są zapisane w postaci
plików tekstowych, toteż możemy je odczytać choćby za pomocą polecenia cat z poziomu konsoli.
Jednak dziś, gdy mamy bardzo dobre graiczne środowiska pracy jak KDE, GNOME, XFCE, korzystanie
z konsoli – szczególnie przez początkującego – użytkownika nie jest na topie.
to dane tekstowe, nie jest trudne i warto
w ramach treningu pokusić się o opra-
cowanie całkiem użytecznego programu,
który będzie informował np. o urządzeniach USB aktual-
nie podłączonych do komputera. W tym artykule pokażę
jak można opracować tego typu program dla środowiska
GNOME. Urządzenia USB podłączone do komputera zgru-
pujemy i wyświetlimy w postaci drzewa za pomocą widgetu
GtkTreeView . Dodatkowo wyświetlimy też nieco technicz-
nych informacji, gdy użytkownik wskaże urządzenie USB.
sób nasz program nie będzie wymagał obecności dodatko-
wych plików.
Po utworzeniu interfejsu należy wykonać istotną czyn-
ność, a mianowicie odczyt informacji o urządzeniach USB.
Informacje te uzyskamy z pliku /proc/bus/usb/devices , ponad-
to w programie dodamy także możliwość wskazania innego
pliku z opisem urządzeń USB. Po odczytaniu informacji i za-
pisaniu ich do jednokierunkowej listy następną czynnością
będzie utworzenie drzewa struktury urządzeń USB. W ten
sposób użytkownik będzie mógł się zorientować, jakie por-
ty USB są zainstalowane w komputerze oraz do których por-
tów zostały podłączone nowe urządzenia.
Kolejne dwa zdarzenia są opcjonalne, jeśli użytkownik
wybierze inny plik do analizy, to naturalnie program do-
kona analizy wskazanego pliku i utworzy nowe drzewo
Plan programu
Łatwo wyróżnić najważniejsze czynności w programie,
który zamierzamy opracować. Rysunek 1 przedstawia
przepływ sterowania w naszej aplikacji. Pierwszą czyn-
nością jaką będzie realizował nasz program jest utworze-
nie interfejsu użytkownika. Odpowiednie okna dialogo-
we interfejsu użytkownika zostaną utworzone za pomocą
nieocenionego programu GLADE. Przy czym nie będzie-
my korzystać z pliku z opisem interfejsu, ale wygeneruje-
my kod źródłowy tworzący odpowiednie okna. W ten spo-
O autorze
Autor zajmuje się tworzeniem oprogramowania dla Li-
nuksa oraz WIN32. Zainteresowania: teoria języków prog-
ramowania oraz dobra literatura.
36
maj 2007
Marek Sawerwain
W ydobycie informacji, z racji tego, że są
439114674.040.png 439114674.041.png 439114674.042.png 439114674.043.png
 
dla programistów
USB Device Explorer
z urządzeniami USB. Drugie zdarzenie to ak-
tualizacja danych, w ten sposób nowo przyłą-
czone urządzenie zostanie przez nasz program
samodzielnie dopisane do drzewa urządzeń.
Sposób realizacji takiej własności jest dość pro-
sty, należy w równych odstępach czasu (raz
na kilka sekund) odczytywać zawartość pliku
z opisem urządzeń USB.
Ostatnia czynność to zakończenie dzia-
łania naszego programu poprzez wybranie
opcji z menu albo przez okno menedżera za-
dań. Dla nas jest to dość ważna czynność, po-
nieważ program zbierając informacje o urzą-
dzeniach USB, tworzy w pamięci listę jedno-
kierunkową, toteż przydzieloną pamięć nale-
ży zwolnić.
Wstępne czynności związane
z interfejsem użytkownika
Interfejs naszego programu to dwa okna: ok-
no główne aplikacji (widget o nazwie Main-
Win ) oraz dodatkowe okno dialogowe (wid-
get USBPathWindow ), w którym dajemy moż-
liwość podania nowej ścieżki do pliku z opi-
sem urządzeń USB. Gdy w programie GLA-
DE wybierzemy przycisk Buduj (ang. Build ),
utworzony zostanie kompletny projekt łącznie
ze skryptem conigure . Niczego nie zmieniamy
w utworzonych plikach, gdyż będziemy ko-
rzystać z dwóch plików źródłowych: interface.c
oraz support.c (łącznie z plikami nagłówkowy-
mi). Pierwszy plik zawiera kod odpowiedzial-
ny za zbudowanie interfejsu, jednak nie wy-
konuje on wszystkich czynności, widget Gtk-
TreeView nie jest poprawnie skonigurowany,
ponieważ został tylko utworzony. Natomiast
drugi plik support.c zawiera kilka przydatnych
funkcji, a najważniejsza dla nas będzie funkcja
lookup_widget .
Nie korzystamy z pliku callbacks.c , w któ-
rym GLADE tworzy funkcje obsługujące zda-
rzenia, ponieważ utworzymy własne funkcje
w głównym pliku o nazwie maincode.c . Z tego
też powodu nie wykorzystujemy pliku main.c ,
gdzie GLADE umieszcza własną funkcję main
wraz z podstawowym kodem tworzącym ok-
no główne i uruchamiającym aplikację.
Pierwsze czynności jakie wykonujemy
w funkcji main są typowe dla programów pi-
sanych w GTK/GNOME z wykorzystaniem
kodu generowanego przez GLADE. Dokonu-
jemy inicjalizacji biblioteki GTK+, tworzymy
okno główne, a następnie podłączamy obsłu-
gę sygnału delete_event , który jest genero-
wany w momencie zamknięcia okna przez
menadżera zadań.
Rysunek 1. Przepływ sterowania w tworzonym programie
g_signal_connect (MainWin,
"delete_event", G_CALLBACK
(gtk_main_quit), NULL);
USBDevPathEDT =
lookup_widget
(USBPathWindow,
"USBDevPathEDT");
W podobny sposób postąpimy z drugim ok-
nem – utworzymy je za pomocą funkcji wy-
generowanej przez GLADE: create_USBPa-
thWindow , a następnie podłączymy obsłu-
gę sygnału delete_event . Nim przejdziemy
do następnych czynności, trzeba do zmien-
nej usb_dev_path wpisać domyślną ścieżkę
do pliku z opisem urządzeń USB, jak rów-
nież wpisać tę wartość do kontrolki USBDe-
vPathEDT , która znajduje się w oknie dialogo-
wym. Uzyskanie odniesienia do potrzebnego
widgetu realizujemy w następujący sposób:
Pierwszy argument to okno dialogowe zawie-
rające tę kontrolkę, natomiast w drugim argu-
mencie podajemy nazwę szukanej kontrolki.
Z tego powodu bardzo ważne jest, aby pod-
czas projektowania wszystkie kontrolki od-
powiednio nazwać.
Następna czynność to ustalenie ścieżki dla
pliku z opisem urządzeń USB. Wartość do-
myślną odnoszącą się do katalogu proc prze-
nosimy do odpowiednich zmiennych w nastę-
pujący sposób:
gtk_init(&argc, &argv);
MainWin = create_MainWin();
www.lpmagazine.org
37
439114674.001.png 439114674.002.png 439114674.003.png 439114674.004.png 439114674.005.png 439114674.006.png 439114674.007.png 439114674.008.png 439114674.009.png 439114674.010.png 439114674.011.png 439114674.012.png 439114674.013.png 439114674.014.png 439114674.015.png 439114674.016.png 439114674.017.png 439114674.018.png
 
dla programistów
USB Device Explorer
Listing 1 . Główna funkcja dokonująca analizy danych o urządzeniach USB
ków, więc wystarczy sam standardowy obiekt
rysujący zawartość danej komórki w tabeli:
void usb_devices_parser ( gchar * usb_dev_path ) {
GIOChannel * usb_dev = NULL ;
GError * error = NULL ;
GString * line_buffer ;
usb_dev = g_io_channel_new_ile (& usb_dev_path [ 0 ] , "r" , & error );
if ( error ) {
g_printf ( "error in open %s \n " , usb_dev_path );
g_error_free ( error );
return ;
}
line_buffer = g_string_sized_new ( 1024 );
while ( g_io_channel_read_line_string ( usb_dev , line_buffer ,
NULL , & error ) != G_IO_STATUS_EOF ) {
g_string_set_size ( line_buffer , line_buffer -> len - 1 );
parse_line ( line_buffer -> str );
}
g_string_free ( line_buffer , TRUE );
g_io_channel_close ( usb_dev );
}
renderer = gtk_cell_
renderer_text_new ();
Dodanie nowej kolumny np.: kolumny iden-
tyikatora „Id” przedstawia się następująco:
col_offset = gtk_tree_view_
insert_column_with_attributes
(GTK_TREE_VIEW (USBDevTree),
-1, "Id",
renderer, "text",
0,
NULL);
Ważnym elementem jest zero w przedostat-
nim argumencie, które oznacza numer pola
w modelu, które ma być wyświetlane z de-
iniowanej kolumnie. Dlatego w drugiej ko-
lumnie mamy w tym miejscu jedynkę, gdyż
odwołujemy się do drugiego pola:
g_strcpy(&usb_dev_path[0],
"/proc/bus/usb/devices");
gtk_entry_set_text(GTK_ENTRY(
USBDevPathEDT ), &usb_dev_path[0]);
dzeń USB. Linia kodu realizująca to zadanie
jest następująca:
refresh_id = g_timeout_add(5*1000,
refresh_timeout, NULL);
Kompilacja programu
Kolejne czynności to m. in. uzyskanie odnie-
sienia do drzewa, gdzie umieścimy spis urzą-
dzeń USB:
Do kompilacji aplikacji przedstawianej w ar-
tykule nie potrzebujemy dodatkowych biblio-
tek poza samym środowiskiem GNOME.
Kompilacja naszego programu jest łatwa do
przeprowadzenia dzięki zastosowaniu na-
rzędzia pkg-conig . Zastosowanie tego pro-
gramu zwalnia nas od obowiązku podawa-
nia poszczególnych katalogów dla plików
nagłówkowych oraz bibliotek. Podczas kom-
pilacji (co potwierdzają poniższe przykłady)
należy też podać ścieżki do plików wygene-
rowanych przez program GLADE:
Koniguracja widgetu
GtkTreeView
Tworzenie modelu oraz koniguracja kontro-
lki odbywają się za pośrednictwem dwóch
funkcji:
USBDevTree = lookup_widget(MainWin,
"USBDevTree");
Do tej kontrolki podłączamy także obsługę sy-
gnału cursor_changed generowanego w mo-
mencie wyboru któregoś z wierszy kontrolki.
Uzyskujemy także widget, w którym znajdą
się dodatkowe informacje o wybranym urzą-
dzeniu USB:
model_setup_usb_dev_tree();
column_setup_usb_dev_tree();
Ponieważ w drzewie znajdować się będą tyl-
ko trzy kolumny, z czego jedynie dwie pierw-
sze będą widoczne, to nasz model będzie się
składał tylko z trzech typów danych – dwóch
ciągów znaków oraz ze wskaźnika:
USBDevInfo = lookup_widget(MainWin,
"USBDevInfo");
gcc -c usbexplorer/src/interface.c
-Iusbexplorer/src `pkg-conig
--clags gtk+-2.0`
gcc -c usbexplorer/src/support.c
-Iusbexplorer/src `pkg-conig
--clags gtk+-2.0`
gcc -c usbexp.c -Iusbexplorer/src
`pkg-conig --clags gtk+-2.0`
Dokładnie w tym momencie po wywołaniu
funkcji build_usb_dev_list następuje ana-
liza pliku, gdzie opisane są aktualnie dołą-
czone urządzenia USB, ale tym problemem
zajmiemy się za chwilę. Bowiem po analizie
tworzymy tzw. model danych dla widgetu
GtkTreeView oraz wykonujemy konigurację
kontrolki USBDevTree . Dopiero po tych czyn-
nościach możemy już dodać aktualnie do-
stępne urządzenia USB funkcją add_usb_dev
do kontrolki GtkTreeView .
Po tych czynnościach pozostaje już tyl-
ko wywołać funkcję gtk_main , ale zanim to
zrobimy, dodajemy jeszcze funkcję, która co
pięć sekund będzie aktualizować spis urzą-
USBDevTree_model=gtk_tree_store_new
(3, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_POINTER);
Utworzony model podłączamy do kontrolki:
Po kompilacji otrzymane pliki obiektowe mo-
żemy ostatecznie połączyć w plik binarny
i przedstawia się to w następujący sposób:
gtk_tree_view_set_model
(GTK_TREE_VIEW (USBDevTree),
GTK_TREE_MODEL(USBDevTree_model));
gcc -o usbexp usbexp.o interface.o
support.o `pkg-conig --libs
glib-2.0 gtk+-2.0`
Model posiada trzy elementy lecz w kontrolce
będziemy wyświetlać tylko dwie pierwsze ko-
lumny. Będzie to identyikator oraz nazwa
urządzenia. Obydwie kolumny to ciągi zna-
Po wykonaniu tych poleceń dysponujemy
już gotowym plikiem binarnym.
38
maj 2007
439114674.019.png 439114674.020.png 439114674.021.png 439114674.022.png
 
dla programistów
USB Device Explorer
col_offset = gtk_tree_view_
insert_column_with_attributes
(GTK_TREE_VIEW (USBDevTree),
-1, "Name",
renderer, "text",
1,
NULL);
T: Bus=03 Lev=00 Prnt=00 Port=00
Cnt=00 Dev#= 1 Spd=480 MxCh= 8
if( *(line) == 'S' ) {
s_parse( line + 4 );
}
Jedna z linii opisująca urządzenie przedstawia
się tak:
Jeśli warunek będzie prawdziwy, zostanie
wywołana odpowiednia funkcja, która zaj-
muje się szczegółową analizą tej linii. Do-
datkowo przesuwamy wskaźnik, aby pomi-
nąć początkowe cztery znaki, co ułatwia dal-
szą analizę i odczytywanie potrzebnych in-
formacji.
D: Ver= 2.00 Cls=00(>ifc ) Sub=00
Prot=00 MxPS=64 #Cfgs= 1
Analiza pliku z opisem
urządzeń USB
W kodzie funkcji main wyciągnięcie informa-
cji o urządzeniach USB jest inicjalizowane
przez funkcję build_usb_dev_list . Jedy-
na istotna dla nas czynność, która jest tam
realizowana to uruchomienie procesu ana-
lizy:
Natomiast linie dostarczające nazwę urządze-
nia i numer serii są następujące:
S: Manufacturer=Linux 2.6.18.4-kateos
ohci_hcd
S: Product=OHCI Host Controller
S: SerialNumber=0000:00:02.1
Analiza szczegółowa
Po wykryciu z jaką linijką tekstu mamy do czy-
nienia, wywołujemy odpowiednią funkcję,
która zajmie się odczytaniem konkretnych
danych. Gdy analizujemy linijki typu „S”,
musimy dodatkowo wykryć nazwę pola – ła-
two to zrobić, ponieważ nazwa każdego pola
zaczyna się od innej litery.
Odczytanie nazwy producenta (w przy-
padku pozostałych dwóch pól postępujemy
w sposób bardzo podobny) realizuje nastę-
pująca instrukcja warunkowa:
usb_devices_parser
( &usb_dev_path[0] );
Jak widać, pewną cechą charakterystycz-
ną jest fakt, iż każda z tych linii rozpoczy-
na się od innej litery i zawiera różne infor-
macje. Choć nie dotyczy to linii typu „S”,
bowiem mogą one zawierać trzy różne po-
la: Manufacturer , Product oraz Serial-
Number . Jeśli będziemy odczytywać pierw-
szy znak linii tekstu, łatwo wykryjemy,
z jaką linią mamy do czynienia. W ten spo-
sób działa funkcja parse_line . Wykrycie
linii typu „S” to jedna instrukcja warun-
kowa:
Listing 1 przedstawia pełny kod źródłowy
tej funkcji. Ponieważ jądro Linuksa generuje
opis urządzenia w postaci linii tekstu, a każ-
de nowe urządzenia rozpoczyna się od linii,
którą tu będę nazywał linią „T”, to cały pro-
blem analizy można sprowadzić do czytania
pliku linia po linii oraz analizowania każdej
linii oddzielnie.
Proces odczytu będziemy realizować za
pomocą funkcji biblioteki GLIB. Biblioteka ta
oferuje cały zestaw funkcji do obsługi kana-
łów danych, kanałem danych może też być
plik. W pierwszej części funkcji usb_devi-
ces_parser uzyskujemy dostęp do pliku za
pomocą funkcji g_io_channel_new_ile . Na-
stępnie sprawdzamy, czy zmienna error nie
zawiera informacji o błędzie, jeśli błąd nie
wystąpił, jesteśmy gotowi do odczytywania
zawartości pliku.
Dzięki pętli while zawartość pliku od-
czytujemy linia po linii, korzystając z funkcji
o jednoznacznej, choć nieco długiej nazwie:
g_io_channel_read_line_string . Poszcze-
gólne linijki odczytujemy tak długo, aż nie na-
stąpi koniec pliku. W treści pętli while usta-
lamy poprawnie długość odczytanego cią-
gu znaków, obcinając znak przejścia do no-
wej linii, a następnie odczytany ciąg znaków
przekazujemy do dalszej analizy w funkcji
parse_line .
if ( *line == 'P' ) {
str_tab=g_strsplit(line ,"=\0", 2);
g_strcpy( &product[0], str_tab[1]);
g_strfreev(str_tab);
Listing 2 . Dodanie urządzeń USB do kontrolki GtkTreeView
void add_usb_dev ( _usb_dev_desc * tmp ) {
static GtkTreeIter iter ;
static GtkTreeIter child_iter ;
char str_tmp [ 127 ];
if ( tmp != NULL ) {
add_usb_dev ( tmp -> next );
if ( tmp -> level == 0 ) {
g_sprintf (& str_tmp [ 0 ] , "%d/%d" , tmp -> bus , tmp -> level );
gtk_tree_store_append ( USBDevTree_model , & iter , NULL );
gtk_tree_store_set ( USBDevTree_model , & iter ,
0 , & str_tmp [ 0 ] ,
1 , & tmp -> product [ 0 ] ,
2 , tmp ,
- 1 );
}
else {
g_sprintf (& str_tmp [ 0 ] , "%d/%d" , tmp -> bus , tmp -> level );
gtk_tree_store_append ( USBDevTree_model , & child_iter , & iter );
gtk_tree_store_set ( USBDevTree_model , & child_iter ,
0 , & str_tmp [ 0 ] ,
1 , & tmp -> product [ 0 ] ,
2 , tmp ,
- 1 );
}
}
}
g_string_set_size(line_buffer,
line_buffer->len - 1);
parse_line(line_buffer->str);
Zadaniem funkcji parse_line jest rozpozna-
nie typu wczytanej linii. Linia rozpoczynają-
ca opis każdego urządzenia USB posiada na-
stępującą postać:
40
maj 2007
439114674.023.png 439114674.024.png 439114674.025.png 439114674.026.png
 
dla programistów
USB Device Explorer
g_strcpy(&head->product[0],
&product[0]);
}
ników str_tab . Dysponując tą pomocni-
czą tablicą, w łatwy sposób możemy od-
czytać potrzebne dane, a nazwę producen-
ta przenosimy do pola product zmiennej
head .
A czym jest zmienna head ? Stanowi ona
ostatni dodany elementem do listy urządzeń
USB, co oznacza, iż musimy w jakiś sposób
utworzyć nowy element listy, gdzie zebrali-
śmy spis urządzeń USB.
Tworzenie nowego elementu następuje
w funkcji o nazwie t_parse . Zgodnie z na-
zwą ta funkcja zajmuje się analizą linijek ty-
pu „T”. Wykorzystujemy fakt, iż opis każde-
go nowego urządzenia rozpoczyna się wła-
śnie od linijki typu „T”. W tej linii występu-
je wiele pól, ale łatwo je odczytać za pomocą
funkcji sscanf :
Główne zadanie jest wykonywane przez funk-
cję g_strsplit , która ciąg znaków dzieli na
dwa podciągi rozdzielone znakiem równo-
ści. W ten sposób otrzymamy ciąg znaków
w postaci:
sscanf(line, "Bus=%d Lev=%d Prnt=%d
Port=%d Cnt=%d Dev#=%d Spd=%d
MxCh=%d",
&bus, &level, &prnt, &port, &cnt,
&dev, &spd, &mxch);
Product=EHCI Host Controller
Dodanie nowego urządzenia USB do listy
wykonujemy na dwa sposoby. Jeśli zmien-
na head jest równa NULL , oznacza to, że lista
urządzeń jest pusta, czyli musimy utworzyć
zmienną oraz wpisać do niej nowo odczytane
wartości przy pomocy funkcji sscanf . Natu-
ralnie do pola next wpisujemy NULL , bowiem
jest to pierwszy element naszej listy.
To funkcja g_strsplit podzieli ten ciąg
na dwa podciągi: Product oraz EHCI Host
Controller , a wskaźniki do tych ciągów
znaków zostaną zapisane w tablicy wskaź-
Listing 3 . Odczyt informacji o urządzeniu USB i ich wizualizacja
void on_cursor_changed_usb_dev_tree_treeview ( GtkWidget * menuitem ,
gpointer data ) {
GtkTextBuffer * buffer = NULL ;
GtkTextIter iter_begin , iter_end ;
_usb_dev_desc * tmp ;
gchar txt_buf [ 2048 ];
GValue gv_data ;
GtkTreePath * tpath ;
GtkTreeIter iter ;
memset (& gv_data , 0 , sizeof ( gv_data ));
gtk_tree_view_get_cursor ( GTK_TREE_VIEW ( USBDevTree ) , & tpath , NULL );
gtk_tree_model_get_iter ( GTK_TREE_MODEL ( USBDevTree_model ) ,
& iter , tpath );
gtk_tree_model_get_value ( GTK_TREE_MODEL ( USBDevTree_model ) ,
& iter , 2 , & gv_data );
tmp =( _usb_dev_desc *) g_value_get_pointer ( & gv_data );
buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( USBDevInfo ) );
gtk_text_buffer_get_iter_at_offset ( buffer , & iter_begin , 0 );
gtk_text_buffer_get_iter_at_offset ( buffer , & iter_end , - 1 );
gtk_text_buffer_delete ( buffer , & iter_begin , & iter_end );
memset (& txt_buf , 0 , 2048 );
if ( tmp -> level == 0 )
{
g_sprintf (& txt_buf [ 0 ] , "%s \n\t Manufacturer: %s \n\t Serial Number: %s \n "
" \t BUS: %d Level: %d PRNT: %d PORT : %d CNT: %d DEV:
%d Speed: %d Channels: %d \n "
" \t USB Version: %2.2f Device Class: (%d)%s \n "
" \t Device Subclass: %d Device Protocol: %d \n "
" \t Maximum Default Endpoint: %d Number of Conigurations: %d \n "
" \t Max Power: %dmA \n\t Bandwidth allocated: %s Usage: %d%% \n " ,
& tmp -> product [ 0 ] , & tmp -> manufacturer [ 0 ] , & tmp -> serialnumber [ 0 ] ,
tmp -> bus , tmp -> level , tmp -> prnt , tmp -> port , tmp -> cnt ,
tmp -> dev , tmp -> spd , tmp -> mxch ,
tmp -> ver , tmp -> int_cls , & tmp -> cls [ 0 ] , tmp -> sub , tmp -> prot ,
tmp -> maxps , tmp -> hash_cfgs ,
tmp -> mxpwr , & tmp -> alloc [ 0 ] , tmp -> us );
}
else { // usuni ę ty fragment }
gtk_text_buffer_set_text ( GTK_TEXT_BUFFER ( buffer ) , & txt_buf [ 0 ] , - 1 );
}
head = (_usb_dev_desc*)g_
malloc(sizeof(_usb_dev_desc));
head->next = NULL;
head->bus = bus;
... // pozostałe przypisania
Jeśli jednak wartość zmiennej head jest różna
od NULL , to istnieją już urządzenia i trzeba do-
dać do istniejącej listy nowy element, dlate-
go tworzymy dodatkową zmienną tmp , a na-
stępnie postępujemy jak przy każdej liście
jednokierunkowej:
tmp = ( _usb_dev_desc* )g_
malloc(sizeof(_usb_dev_desc));
tmp->next = head;
tmp->bus = bus;
... // pozstałe przypisania
head = tmp;
Tworzenie drzewa urządzeń
Listing 2 zawiera pełen kod źródłowy funk-
cji, która tworzy drzewo urządzeń w kontro-
lce GtkTreeView . Funkcja add_usb_dev jest
funkcją rekurencyjną, wbrew pozorom zasto-
sowanie rekurencji upraszcza nam w sposób
znaczący tworzenie drzewa. Zastosowanie
rekurencji rozwiązuje jeden problem, a jest
nim fakt, iż urządzenia na liście są zapisane
w odwrotnej kolejności. Stosując w pierwszej
kolejności wywołanie rekurencyjne, a dopie-
ro w drugim kroku dodając nowy element do
drzewa, urządzenia zostaną zapisane w po-
prawnej kolejności.
W funkcji add_usb_dev z Listingu 2 sto-
sujemy też dwie zmienne statyczne iter oraz
child_iter . Pomimo rekurencji wartości
tych zmiennych są przekazywane pomiędzy
poszczególnymi wywołaniami funkcji gtk_
tree_store_append . W ten sposób będziemy
poprawnie umieszczać elementy listy w drze-
www.lpmagazine.org
41
439114674.027.png 439114674.028.png 439114674.029.png 439114674.030.png 439114674.031.png 439114674.032.png 439114674.033.png 439114674.034.png 439114674.035.png 439114674.036.png 439114674.037.png 439114674.038.png 439114674.039.png
 
Zgłoś jeśli naruszono regulamin