Tato dokumentace obsahuje navrhy autoru, kterak implementovat (byly implementovany) jednotlive casti linksu. Dokumentace neni uplna, najdou se zde pouze nektere casti. Podle teto dokumentace se pak jednotlive casti implementovaly/budou implementovat. V dokumentaci je kladen duraz zejmena na rozhrani, kvuli komunikaci s ostatnimi castmi linksu.
Nejprve je potreba precist dokumentaci o select smycce.
+--------------- Bookmarks --------------------------------------------------+ | | | Bookmark 1 | | [-] Adresar bookmarku | | Bookmark v adresari | | [+] Sbaleny dalsi podadresar | | ... atd | | | | [ Pridej ] [ Edituj ] [ Adresar ] [ Presun ] a dalsi tlacitka ... | | | +----------------------------------------------------------------------------+
Vytvori se handle_event
, kde se budou zachytavat kliky mysi do
prislusneho seznamu seznamu bookmarku, stisky klaves nahoru, dolu, + a -.
Ostatni eventy se nechaji propadnout do standartniho handleru dialogoveho okna;
kod na tlacitka je jiz hotovy.
Prekreslovani bookmarku se umisti do funkce na vykreslovani okna. Nesmi se
zapomenout zavolat exclude_rect_from_set
pro obdelnik, ktery byl
prekreslen, aby nebyl prekreslen znovu bilou.
Princip funkce struct rect_set
:
struct rect_set
je mnozina obdelniku.
Pouzivaji se tyto funkce:
init_rect_set
– vyrobi novou mnozinu;
add_to_rect_set
– prida obdelnik do mnoziny;
exclude_rect_from_set
– vyjme obdelnik z mnoziny a rozstepi
vsechny obdelniky, ktere ho prekryvaji.
exclude_from_set
– totez jako predchozi, ale ma 4 parametry
jako souradnice misto jednoho rect.
Pokud se napriklad zavola add_to_rect_set
s obdelnikem (0,0 -
10,10) a nasledovne exclude_rect_from_set
s obdelnikem (2,2 - 5,5), pak
v mnozine vzniknou ctyri obdelniky (0,0 - 10,2); (0,2 - 2,5); (5,2 - 10,5); (0,5
- 10,10)
Dialog se kresli tak, ze se do struct rect_set *dlg->s
ulozi
cela procha, kdyz se pak nakresli nejaka polozka, tak se na ni udela
exclude_rect_from_set
a na konci se prekresli vsechno, co zbylo v
mnozine, bile. Tim se zajisti, ze dialogove okno nebude blikat pri
prekreslovani.
dlg->s
je vyrobena ve funkci draw_dlg
, takze
pote, co bude zavolana v layoutove funkci, budou nakresleny bookmarky a
nakreslene misto vyjmuto z dlg->s
. V textovem modu se tyto
triky nedelaji (a rect_set
tam vubec neexistuje), protoze tam
blikani nehrozi.
D_BOX
zasadne nepouzivat. Veskery kod, ktery
se na nej odkazuje bude smazan. D_BOX
je historicky prezitek a
strasna prasarna.
struct list_description { void *new_item(); void edit_item(struct terminal *, void *); void delete_item(struct terminal *, void *); void print_item(struct terminal *, void *, int x, int y, int width); };
Kdyz si kazdy pak da ruzne funkce, a tak bude moci editovat v podstate
libovolne polozky. new_item
vyrobi prazdnou polozku,
edit_item
zobrazi dialogove okno na editaci polozky,
delete_item
zobrazi dialogove okno na smazani polozky,
print_item
vypise polozku na danou pozici. Mozna budou potreba
dalsi funkce, toto je jen navrh, ne dogaticky pevne rozhrani.
Taky se musi davat pozor na memory leaky – zejmena ty vznikle zmacknutim Ctrl-C v nejakem okne. Pri ladeni je nutne zmacknout Ctrl-C v kazdem stavu, v jakem se editor muze nachazet (nejlepe pri tom jeste pouzit electric fence).
void init_dip();
void shutdown_dip();
struct style *g_get_style(int fg, int bg, int size, unsigned char *font,
int fflags);
fg
je barva popredi ve tvaru (red << 16) +
(green << 8) + blue. bg
je podobne barva pozadi. bg ==
-1
znamena, ze se pod text bude podkladat obrazek text se nebude
tisknou pomoci standartni g_print_text
, ale pomoci jine funkce,
ktera bude delat podkladani. size
je
vyska v pixelech. font
je retezec popisujici font. Je potreba
napsat nejake rozumne mapovani retezcu na fonty. Pokud je !strcasecmp(font,
"fixed")
, pak by se mel pouzit neproporcionalni font. fflags
je bitovy OR nasledujicich hodnot: FF_BOLD
, FF_ITALIC
,
FF_UNDERLINE
.
struct style
je soukromy typ pro dip modul, neni specifikovano, co
ma obsahovat. (asi by bylo dobre tu definici presunout do dip.c a do links.h dat
pouze struct style;
)
void g_free_style(struct style *styl)
struct style *g_clone_style(struct style *styl)
void g_print_text(struct graphics_driver *gd, struct graphics_device *dev,
int x, int y, struct style *style, unsigned char *text, int *width)
width != NULL
, tak tam ulozi velikost textu na
tu pozici.
Ohledne sirek pisem: pokud styl nema FF_ITALIC
, pak musi
platit, ze sirka spojeni dvou retezcu je soucet jejich sirek. Kod v bfu.c to
predpoklada. U kurzivy to pochopitelne neplati, kurziva se musi tisknout tak,
aby se horni konec jednoho pismene prekryval s dolnim koncem jineho.
Bude jedna globalni hash tabulka stylu. Kazdy styl ma pocitadlo pouziti. Pri
g_get_style
se prohleda tabulka a pokud se ten samy styl nalezne,
tak se mu zvysi pocitadlo. Pokud se nenalezne, tak bude vytvoren, zarazen do
hashe a nastavi se mu pocitadlo pouziti na 1. U hashovaci funkce je nutno se
vyvarovat nasobeni, deleni nebo modulo, ponevadz tyto operace trvaji dost
dlouho. Zasadne pouzivat operace jako napriklad +, -, ^, &, |, (<<,
>> jsou uz pomalejsi, ale pouzit se taky daji). Pri zavolani
g_free_style
stylu se snizi pocitadlo pouziti a odstrani se z
hashe, pokud uz v nem nejsou zadna nacachovana pismenka (viz nize.) Pri
g_clone_style
bude vracen ten samy styl a zvysi se mu pocitadlo
pouziti.
Kazdy styl ma hashovou tabulku pismen. Pristup k ni musi byt rychly,
nejlepsi by asi bylo pouze 'znak & (velikost_hashe - 1)
'. Kdyz
bude potreba tisknout pismeno, ktere neni v hashi, tak se do hashe prida.
Pismena (a dalsi objekty – jako napr. obrazky) tvori LRU seznam. Dobre by bylo udelat neco jako:
struct lru_object { struct lru_object *next; struct lru_object *prev; void (*release)(struct lru_object *); };a pak:
struct letter { struct letter *next; struct letter *prev; void (*release)(struct letter *); struct bitmap *bmp; a dalsi polozky ... };a
struct image { struct image *next; struct image *prev; void (*release)(struct image *); a dalsi polozky... };
Do LRU cache by se mely dat davat ruzne objekty, kazdy bude mit funkci
release
, ktera bude zavolana na posledni objekt v seznamu.
Pri tisku pismena by se pismeno melo dat na zacatek seznamu – to bude masirovat pamet (jedno presunuti objektu = 6 zapisu). Pokud by se to melo omezit, mohou se pouzit active/inactive listy, jako ma FreeBSD v memory managementu :-) Ale to asi neni potreba, protoze zapisovani do videopameti je daleko pomalejsi...
Pri mazani pismena z cache je potreba smazat i styl, ve kterem pismeno je, pokud styl nemaji uz zadna jina pismena.
Mazani objektu z cache by se nemelo provadet po jednom, ale tak, jak to dela Linux nebo BSD – maze se spousta naraz, a pak dlouho nic. Je to lepsi, nez kdyz se cache spravuje systemem "jednu polozku pridam/jednu odeberu".
Nesmi se tez opomenout, ze cilem spravy cache neni pevne dodrzet kvotu, ale
poskytnout dobry vykon. Kdyz bude na obrazovce spousta velkych pismenek nebo
obrazku, tak by byl nesmysl pri kazdem prekresleni obrazovky generovat vsechna
pismena jen proto, ze jejich velikost je nad kvotou.
Mozne reseni: 2 kvoty (tvrda a mekka) a timeout. Z cache se bude
mazat pokud velikost_cache >= tvrda_kvota || velikost_cache >=
mekka_kvota && cas_posledniho_objektu >= timeout
.
Bylo by dobre spravu cache obrazku narazit na memory.c :
register_cache_upcall
, aby Links mohl behat i na systemech bez
virtualni pameti (MS-DOS). Funguje to tak, ze se zavola void
register_cache_upcall(int (*upcall)(int), unsigned char *name)
.
upcall
je funkce, ktera se zavola pokud dochazi pamet,
name
je jmeno cachoveho subsystemu (dip), pouziva se pro chybove
hlasky. Links obcas zavola upcall
s parametrem:
SH_CHECK_QUOTA
— pouze se ma zkontrolovat, zda nebyla
prekrocena kvota.
SH_FREE_SOMETHING
— ma se neco uvolnit bez ohledu na to,
zda byla, nebo nebyla prekrocena kvota.
SH_FREE_ALL
— ma se uvolnit vsechno, co je mozno uvolnit.
(ale ne napr. styly, u kterych je nenulove pocitadlo pouziti).
Prislusna funkce upcall vrati hodnotu, ktera je OR nasledujicich maker:
ST_SOMETHING_FREED
— neco se podarilo uvolnit.
ST_CACHE_EMPTY
— cache je prazdna.
Dale je potreba pocitat s tim, ze upcall muze byt zavolan kdykoli, kdyz je
zavolano mem_alloc
nebo mem_realloc
. Taky se v
upcallu neda alokovat zadna pamet,
protoze upcall se vola pri nedostatku pameti (pak by to tuhlo jak memory
management v tucnackovi :-) ).
struct decoded_image
.
Struktura je soukroma pro modul; modul si do ni muze dat, co chce.
Napise se funkcevoid g_print_text_on_image(struct graphics_driver *gd, struct
graphics_device *dev, int x, int y, struct style *style, unsigned char *text,
int *width, struct decoded_image *i, int xoff, int yoff);
xoff
a
yoff
jsou offsety posunuti obrazku (v pixelu na obrazovce o
souradnicich [x, y] bude pixel obrazku o souradnicich [xoff, yoff]).
xoff
a yoff
muzou byt vetsi, nez je obrazek, v takovem
pripade se berou modulo velikost obrazku. Obrazek se pod textem periodicky
opakuje.
Jak natahovat obrazky bude popsano casem...
Obrazky obcas bude potreba stahovat. K tomu slouzi object requester. Je treba implementovat nasledujici funkce:
struct decoded_image *img_get_decoded_image(struct object_request
*rq)
struct decoded_image
je struktura, ktera
bude popisovat dekodovany obrazek (vhodny napriklad k podkladani textu apod.).
struktury se budou ukladat do obrazkove cache indexovane pomoci URL a delky
souboru (aby se vyresilo prubezne stahovani obrazku) a pouzije se refcount
— t.j. kdyz nekdo bude chcit tentyz obrazek znovu dekodovat, tak se
vrati pouze pointer z cache a zvysi se mu refcount. Stare neuplne obrazky je
treba z cache prednostne mazat, ale pouze pokud maji nulovy refcount.
Tato funkce nemusi resit animaci obrazku — bude proste treba vracet prvni ramec.
void img_release_decoded_image(struct decoded_image *d)
img_get_decoded_image
, tak je treba
zajistit, aby nebyla struktura zmenena, dokud se nezavola
img_release_decoded_image
.
void img_draw_decoded_image(struct graphics_device *dev, struct decoded_image
*d, int x, int y, int xw, int yw, int xo, int yo)
int img_get_image_size(struct object_request *rq, int *x, int *y)
struct img_active *img_draw_active_image(unsigned char *url, struct window
*w, int x, int y, int xw, int yw, struct rect *clip, long color_under, struct decoded_image
*image_under, int xo, int yo)
void img_release_active_image(struct img_active *img)
U poslednich dvou funkci je treba resit takove situace, jako napriklad kdyz se dvakrat na tentyz obrazek zavola img_draw_active_image, aby obrazek byl dekodovan pouze jednou apod. Take kdyz se zavola img_draw_active_image, pak hned img_release_active_image a pak zas img_draw_active_image, tak by se cely obrazek nemel znovu dekodovat. Doporucuji udelat dalsi zvenku neviditelnou strukturu, na kterou bude img_active ukazovat. V teto strukture se budou ukladat vsechna data ohledne nahravani a dekodovani, struktura bude take obsahovat seznam img_active na ni ukazujicich, a kdyz prijdou ze site nejaka data, tak je vsechna prekresli.
Nejdriv je potreba precist si o select smycce v select.html.
struct javascript_context
bude zakladni struktura obsahujici
kontext javascriptu. Tato struktura je soukroma pro modul js.c, proto si
modul do ni muze davat, co chce. Jlikoz muze soucasne bezet vice javascriptu
(predpokladam jeden na kazdy ram), je treba veskery stav javascript engine
dat do teto struktury. Nemelo by tam byt prilis mnoho globalnich promennych. Je
treba si uvedomit, ze javascript pracuje s kodem stahnutym ze site a
proto musi byt extremne bezpecny. Nedelat zadna pole staticke velikosti –
vzdy si pamet pro vsemozne retezce alokovat dynamicky (viz. info o retezcich). A uz vubec nedelat pole na
zasobniku!
struct javascript_context *js_create_context(void *p)
void js_destroy_context(struct javascript_context *context)
void js_execute_code(struct javascript_context *context, unsigned char
*code, int len)
Proto se musi zaregistrovat signal handler s casem 0 nebo bottom half, ktery teprve bude moci zavolat js_upcall_open_url. Protoze ho zavola rovnou z select smycky, nestane se, ze by se pak provedl navrat do funkce, pricemz struktura, se kterou pracovala neexistuje.
Domluvime se na upcallech a pak se zacnou implementovat.
Kde se bude kod vykonavat, je velku jedno. Asi nejlepsi by bylo primo z js_execute_code nevykonavat zadny kod (protoze se z neho nesmi delat upcally), ale vyrobit vyznamovy strom a (pokud neni prave vykonavan zadny jiny kod v danem kontextu) nastavit timer s hodnotou 0 nebo bottom half, ze ktereho se teprve bude kod vykonavat.
Budeme mit strukturu struct js_document_description
. Ta bude
popisovat, jake objekty se v dokumentu nalezaji, jaka maji jmena, pripadne
jejich tagy a podobne. U kazdeho objektu bude taky ulozeno ID (ale neni to
nutne; muze se napriklad vytvorit pole nejakych objektu a rici, ze ID je index
v tom poli). Struktura nebude popisovat aktualni stavy objektu – bude v
ni napriklad receno, ze nekde je nejaka polozka formulare, ktera se nejak
jmenuje, ale nebude tam uz jeji obsah. Struktura se bude menit pouze pri
pusteni html engine.
V pripade, ze dokument obsahuje ramy, bude struktura obsahovat ID objektu
ramu, pres ktera se budes moci pres urcity upcall zeptat na ID dokumentu ramu.
Bude mozno volat tyto funkce:
struct js_document_description *js_upcall_get_document_description(void
*p, long doc_id)
.p
je pointer predany pri js_create_context.id
je ID dokumentu.
void js_execute_code(struct javascript_context *context, unsigned char
*code, int len, void (*callback)(void *p))
callback
s parametrem
p
, ktery byl predan pri vytvareni contextu. Funkci
callback
je potreba zavolat z nejnizsi urovne – t.j. ne
primo z funkce js_execute_code
. Pokud je context zrusen pomoci
js_destroy_context
, tak callback
uz zavolano nebude. callback
, tak js_execute_code
nebude zavolana znovu.
Kdyby se to stalo, tak je nutne nahlasit INTERNAL ERROR. Frontu pozadavku na
spusteni kodu budu delat u sebe.
callback
uz neni mozno volat zadne funkce pro
modifikaci dokumentu.
struct javascript_context *js_create_context(void *p, long id)
Jiz je napsano prvni implementaci rozhrani na javascript. Jediny upcall, ktery tam zatim je, je
void js_upcall_document_write(void *p, unsigned char *str, int len)
Implementace je zatim primitivni – cte obsahy tagu <SCRIPT>, pocka 1 sekundu, prekonvertuje na velka pismena a predava je, jako by na byla zavolana document.write. Rozhrani mezi linksem a javascriptem je v jsint.c (tam budou dopisovany upcally). Vlastni interpret je v js.c. Zatim se neumi provadet javascript pri eventech (i kdyz uz je to trochu pripraveno).
struct session
.
Kazdy ram je popsan strukturou struct f_data_c
.
Ram popisujici cele okno v session je session->screen
.
Ram ma podramy f_data_c->subframes
a pointer na rodice
f_data_c->parent
. Pokud je to nejvyssi ram, je parrent ==
NULL
. Ram muze (ale nemusi - pozor na NULL) mit pointer na
view_state
a na f_data
. view_state
popisuje vsechny promenlive veci (prave aktivni link, stavy formularu).
f_data
popisuje zformatovany dokument. view_state
se
ukladaji do historie, f_data
se ukladaji do cache zformatovanych
dokumentu.
struct js_document_description
*js_upcall_get_document_description(void *p, long doc_id)
, ktera ma
vratit popis dokumentu. V popisu dokumentu zatim nic neni. Dalsi potrebne
polozky (napr. seznam polozek formulare atd.) se navrhnou a pridaji.
Analogicky k tomu funkce void jsint_destroy_document_description(struct
f_data *f)
tuto strukturu zrusi.
Upcally budou vypadat, jako napriklad
unsigned char *js_upcall_get_form_entry_value(void *p, long doc_id, long
entry_id)
. Upcall by mel pomoci jsint_find_document
nalezt
prislusny ram, pak pomoci jsint_can_access
zjistit, zda ma pravo
pristupovat (musi se delat — jinak je to security BUG!). Az se obdrzi
f_data_c ciloveho dokumentu, tak se v jeho view_state ta polozka muze najit.
Je potreba davat pozor na veskera preteceni. Nikdy se nesmi spolehat, ze se JS
nic nezmenilo. Muze se treba vyskytnout view_state
, ve kterem
zadne polozky formulare nejsou, a kod pri tom nesmi spadnout.
Upcall na document.write tam uz je hotovy. Pokud bude potreba zasahovat do
zformatovaneho dokumentu (treba menit linky apod.), muze se zasahovat rovnou do
f_data
. Ale pozor: pote, co se zmeni neco v f_data
musis se nastavit f_data->uncacheable
na 1, aby se ten dokument
se zmenami neulozil do cache. Taky by se pak mela zavolat draw_fd
,
aby se vsechny zmeny nakreslily.