diff options
author | jcid <devnull@localhost> | 2007-10-07 00:36:34 +0200 |
---|---|---|
committer | jcid <devnull@localhost> | 2007-10-07 00:36:34 +0200 |
commit | 93715c46a99c96d6c866968312691ec9ab0f6a03 (patch) | |
tree | 573f19ec6aa740844f53a7c0eb7114f04096bf64 /doc |
Initial revision
Diffstat (limited to 'doc')
-rw-r--r-- | doc/Cache.txt | 185 | ||||
-rw-r--r-- | doc/Cookies.txt | 85 | ||||
-rw-r--r-- | doc/Dillo.txt | 103 | ||||
-rw-r--r-- | doc/Dpid.txt | 454 | ||||
-rw-r--r-- | doc/Dw.txt | 383 | ||||
-rw-r--r-- | doc/DwImage.txt | 201 | ||||
-rw-r--r-- | doc/DwPage.txt | 152 | ||||
-rw-r--r-- | doc/DwRender.txt | 620 | ||||
-rw-r--r-- | doc/DwStyle.txt | 310 | ||||
-rw-r--r-- | doc/DwTable.txt | 205 | ||||
-rw-r--r-- | doc/DwWidget.txt | 339 | ||||
-rw-r--r-- | doc/HtmlParser.txt | 116 | ||||
-rw-r--r-- | doc/IO.txt | 468 | ||||
-rw-r--r-- | doc/Images.txt | 114 | ||||
-rw-r--r-- | doc/Imgbuf.txt | 177 | ||||
-rw-r--r-- | doc/Makefile.am | 19 | ||||
-rw-r--r-- | doc/NC_design.txt | 80 | ||||
-rw-r--r-- | doc/README | 53 | ||||
-rw-r--r-- | doc/Selection.txt | 149 |
19 files changed, 4213 insertions, 0 deletions
diff --git a/doc/Cache.txt b/doc/Cache.txt new file mode 100644 index 00000000..ac1ecf87 --- /dev/null +++ b/doc/Cache.txt @@ -0,0 +1,185 @@ + June 2000, --Jcid + Last update: Oct 2004 + + ------- + CACHE + ------- + + The cache module is the main abstraction layer between +rendering and networking. + + The capi module acts as a discriminating wrapper which either +calls the cache or the dpi routines depending on the type of +request. + + Every URL must be requested using a_Capi_open_url, no matter +if it is a http, file, dpi or whatever type of request. The capi +asks the dpi module for dpi URLs and the Cache for everything +else. + + Here we'll document non dpi requests. + + The cache, at its turn, sends the requested-data from memory +(if cached), or opens a new network connection (if not cached). + + This means that no mattering whether the answer comes from +memory or the net, the client requests it through the capi +wrapper, in a single uniform way. + + + ---------------- + CACHE PHILOSOPHY + ---------------- + + Dillo's cache is very simple, every single resource that's +retrieved (URL) is kept in memory. NOTHING is saved. This is +mainly for three reasons: + + - Dillo encourages personal privacy and it assures there'll be +no recorded tracks of the sites you visited. + + - The Network is full of intermediate transparent proxys that +serve as caches. + + - If you still want to have cached stuff, you can install an +external cache server (as WWWOFFLE), and benefit from it. + + + --------------- + CACHE STRUCTURE + --------------- + + Currently, dillo's cache code is spread in different sources: +mainly in cache.[ch], dicache.[ch] and it uses some other +functions from mime.c, Url.c and web.c. + + Cache.c is the principal source, and it also is the main +responsible for processing cache-clients (held in a queue). +Dicache.c is the "decompressed image cache" and it holds the +original data and its corresponding decompressed RGB +representation (more on this subject in Images.txt). + + Url.c, mime.c and web.c are used for secondary tasks; as +assigning the right "viewer" or "decoder" for a given URL. + + +---------------- +A bit of history +---------------- + + Some time ago, the cache functions, URL retrieving and +external protocols were a whole mess of mixed code, and it was +getting REALLY hard to fix, improve or extend the functionality. +The main idea of this "layering" is to make code-portions as +independent as possible so they can be understood, fixed, +improved or replaced without affecting the rest of the browser. + + An interesting part of the process is that, as resources are +retrieved, the client (dillo in this case) doesn't know the +Content-Type of the resource at request-time. It only gets known +when the resource header is retrieved (think of http), and it +happens when the cache has the control so, the cache sets the +proper viewer for it! (unless the Callback function is specified +with the URL request). + + You'll find a good example in http.c. + + Note: Files don't have a header, but the file handler inside +dillo tries to determine the Content-Type and sends it back in +HTTP form! + + +------------- +Cache clients +------------- + + Cache clients MUST use a_Cache_open_url to request an URL. The +client structure and the callback-function prototype are defined, +in cache.h, as follows: + +struct _CacheClient { + gint Key; /* Primary Key for this client */ + const char *Url; /* Pointer to a cache entry Url */ + guchar *Buf; /* Pointer to cache-data */ + guint BufSize; /* Valid size of cache-data */ + CA_Callback_t Callback; /* Client function */ + void *CbData; /* Client function data */ + void *Web; /* Pointer to the Web structure of our client */ +}; + +typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client); + + + Notes: + + * Op is the operation that the callback is asked to perform + by the cache. { CA_Send | CA_Close | CA_Abort }. + + * Client: The Client structure that originated the request. + + + +-------------------------- +Key-functions descriptions +-------------------------- + +································································ +int a_Cache_open_url(const char *Url, CA_Callback_t Call, void *CbData) + + if Url is not cached + Create a cache-entry for that URL + Send client to cache queue + Initiate a new connection + else + Feed our client with cached data + +································································ +ChainFunction_t a_Url_get_ccc_funct(const char *Url) + + Scan the Url handlers for a handler that matches + If found + Return the CCC function for it + else + Return NULL + + * Ex: If Url is an http request, a_Http_ccc is the matching +handler. + +································································ + +---------------------- +Redirections mechanism + (HTTP 30x answers) +---------------------- + + This is by no means complete. It's a work in progress. + + Whenever an URL is served under an HTTP 30x header, its cache +entry is flagged with 'CA_Redirect'. If it's a 301 answer, the +additional 'CA_ForceRedirect' flag is also set, if it's a 302 +answer, 'CA_TempRedirect' is also set (this happens inside the +Cache_parse_header() function). + + Later on, in Cache_process_queue(), when the entry is flagged +with 'CA_Redirect' Cache_redirect() is called. + + + + + + + +----------- +Notes +----------- + + The whole process is asynchronous and very complex. I'll try +to document it in more detail later (source is commented). + Currently I have a drawing to understand it; hope the ASCII +translation serves the same as the original. + If you're planning to understand the cache process troughly, +write me a note, just to assign a higher priority on further +improving of this doc. + Hope this helps! + + diff --git a/doc/Cookies.txt b/doc/Cookies.txt new file mode 100644 index 00000000..8b5111b8 --- /dev/null +++ b/doc/Cookies.txt @@ -0,0 +1,85 @@ +Jan 2002, Jörgen Viksell - jorgen.viksell@telia.com, + Jorge Arellano Cid -- +Last update: Dec 2004 + + +================== + Cookies in Dillo +================== + + The cookie support in Dillo aims to support cookies of the old +original Netscape style, as well as the kind specified in RFC 2109. + Between sessions, the cookies get saved to ~/.dillo/cookies. +At the moment the only enforcements on the amount of cookies to +save to disk is max 20 per domain. + There's also a file for controlling cookies: ~/.dillo/cookiesrc. Dillo +initially sets it to ignore (reject) all cookies, so if you want to use +cookies, change it to meet your needs. + + If you don't want cookies at all, you have two options: + +1.- Delete ~/.dillo/cookiesrc (or leave it just as dillo creates it). +2. Configure Dillo with ./configure --disable-cookies. Then all the + cookie stuff will be skipped at compilation. + + +===================== + Controlling cookies +===================== + + There is a small and simple way to restrict urls from setting cookies +in Dillo. In the file ~/.dillo/cookiesrc You may specify rules +for different domains. The syntax looks something like this: + +DEFAULT DENY +slashdot.org ACCEPT +.host.com ACCEPT_SESSION + + The first line says that we should deny all cookies from all domains +by default. + The second one tells Dillo to save all cookies from slashdot.org +across sessions, until it expires. + And finally, the third says that all subdomains of host.com should be +allowed to set cookies. But these cookies will only be saved in +memory until you exit. + + +=================== + Cookies & Privacy +=================== + + Cookies can be a severe threat to personal privacy. The pages you +visit can be tracked, logged, and associated to a peronal data-record, +allowing the possibility of building a detailed profile of your +browsing habits. + + This data is sold to companies that profit from direct use of such +information (SPAM, Spying, etc). + + If this data is cross-referenced with other databases, they can end up +with more information than you have about yourself. + + Some people may tell you this is "paranoid". But please, take my words +as those of someone that has written a web browser, a cookies implementation, +and that has deep understanding of HTTP (RFC-2068) and cookies (RFC-2965). + + Non technical persons may like to read: + http://www.junkbusters.com/cookies.html + http://www.newsfactor.com/perl/story/16455.html (about user-spying) + + The dillo project is especially concerned about privacy and security +issues. Our advice is to avoid cookies whenever possible and at most set +ACCEPT_SESSION to specific, trusted sites. -- You have been warned. + + +============== + Restrictions +============== + + If you use a single dillo with multiple windows, then there's no +problem, but if you launch different dillos the latter ones will +have cookies disabled. + + + +Thats all folks! diff --git a/doc/Dillo.txt b/doc/Dillo.txt new file mode 100644 index 00000000..47f89780 --- /dev/null +++ b/doc/Dillo.txt @@ -0,0 +1,103 @@ +"Eliminate the guesswork and quality goes up." + + + ------- + DILLO + ------- + + These notes are written with a view to make it less hard, not +easier yet ;), to get into Dillo development. + When I first got into it, I was totally unaware of the browser +internals. Now that I've made my way deep into the core of it, +(we rewrote it 90% and modified the rest), is time to write some +documentation, just to make a less steep learning curve for new +developers. + + --Jcid + + + + -------- + OVERVIEW + -------- + + Dillo can be viewed as the sum of five main parts: + + 1.- Dillo Widget: A custom widget, FLTK2 based, that holds the +neccesary data structures and mechanisms for graphical rendering. +(Described in Dw*.txt, dw*.c files among the sources.) + + 2.- Dillo Cache: Integrated with a signal driven Input/Output +engine that handles file descriptor activity, the cache acts as +the main abstraction layer between rendering and networking. + Every URL, whether cached or not, must be retrieved using +a_Cache_open_url (Described briefly in Cache.txt, source +contained in cache.c). + IO is described in IO.txt (recommended), source in IO/. + + 3.- The HTML parser: A streamed parser that joins the Dillo +Widget and the Cache functionality to make browsing possible +(Described in HtmlParser.txt, source mainly inside html.c). + + 4.- Image processing code: The part that handles image +retrieving, decoding, caching and displaying. (Described in +Images.txt. Sources: image.c, dw_image.c, dicache.c, gif.c, +jpeg.c and png.c) + + 5.- The dpi framework: a gateway to interface the browser with +external programs (Example: the bookmarks server plugin). +Dpi spec: http://www.dillo.org/dpi1.html + + + ------------------------- + HOW IS THE PAGE RENDERED? + ------------------------- + +(A short description of the internal function calling process) + + When the user requests a new URL, a_Interface_entry_open_url +is queried to do the job; it calls a_Nav_push (The highest level +URL dispatcher); a_Nav_push updates current browsing history and +calls Nav_open_url. Nav_open_url closes all open connections by +calling a_Interface_stop and a_Interface_stop, and then calls +a_Capi_open_url wich calls a_Cache_open_url (or the dpi module if +this gateway is used). + + If Cache_search hits (due to a cached url :), the client is +fed with cached data, but if the URL isn't cached yet, a new CCC +(Concomitant Control Chain) is created and commited to fetch the +URL. Note that a_Cache_open_url will return the requested URL, +whether cached or not. + + The next CCC link is dynamically assigned by examining the +URL's protocol. It can be: + + a_Http_ccc + a_File_ccc + a_About_ccc + a_Plugin_ccc (not implemented yet) + + + If we have a HTTP URL, a_Http_ccc will succeed, and the http +module will be linked; it will create the proper HTTP query and +link the IO module to submit and deliver the answer. + + Note that as the Content-type of the URL is not always known +in advance, the answering branch decides where to dispatch it to +upon HTTP header arrival. + + + What happens then? + + Well, the html parser gets fed, and proper functions are +called for each tag (to parse and call the appropriate methods) +and the whole page is contructed in a streamed way. + Somewhere in the middle of it, resize and repaint functions +are activated and idle functions draw to screen what has been +processed. + + (The process for images is described in Images.txt) + + + + diff --git a/doc/Dpid.txt b/doc/Dpid.txt new file mode 100644 index 00000000..8f69843e --- /dev/null +++ b/doc/Dpid.txt @@ -0,0 +1,454 @@ +Aug 2003, Jorge Arellano Cid, + Ferdi Franceschini -- +Last update: Dec 2004 + + + ------ + dpid + ------ + +------------- +Nomenclature: +------------- + + dpi: + generic term referring to dillo's plugin system (version1). + + dpi1: + specific term for dillo's plugin spec version 1. + at: http://www.dillo.org/dpi1.html + + dpi program: + any plugin program itself. + + dpi framework: + the code base inside and outside dillo that makes dpi1 + working possible (it doesn't include dpi programs). + + dpip: + dillo plugin protocol. The HTML/XML like set of command tags + and information that goes inside the communication sockets. + Note: not yet fully defined, but functional. + Note2: it was designed to be extensible. + + dpid: + dillo plugin daemon. + + server plugin: + A plugin that is capable of accepting connections on a socket. Dpid will + never run more than one instance of a server plugin at a time. + + filter plugin: + Any program/script that can read or write to stdio. If you can write a + shell script you can write one of these (see examples at the end). + Warning, dpid will run multiple instances of filter plugins if requested. + This is safe if the plugin only writes to stdout which is what the filter + type dpis do at the moment. + +----------- +About dpid: +----------- + + * dpid is a program which manages dpi connections. + * dpid is a daemon that serves dillo using unix domain + sockets (UDS). + * dpid launches dpi programs and arranges socket communication + between the dpi program and dillo. + + The concept and motivation is similar to that of inetd. The +plugin manager (dpid) listens for a service request on a Unix +domain socket and returns the socket name of a plugin that +handles the service. It also watches sockets of inactive plugins +and starts them when a connection is requested. + + +----------------------------------------------------------- +What's the problem with managing dpi programs inside dillo? +----------------------------------------------------------- + + That's the other way to handle it, but it started to show some +problems (briefly outlined here): + + * When having two or more running instances of Dillo, one + should prevail, and take control of dpi managing (but all + dillos carry the managing code). + * If the managing dillo exits, it must pass control to another + instance, or leave it void if there's no other dillo running! + * The need to synchronise all the running instances of + dillo arises. + * If the controlling instance finishes and quits, all the + dpi-program PIDs are lost. + * Terminating hanged dpis is hard if it's not done with signals + (PIDs) + * Forks can be expensive (Dillo had to fork its dpis). + * When a managing dillo exits, the new one is no longer the + parent of the forked dpis. + * If the Unix domain sockets for the dpis were to be named + randomly, it gets very hard to recover their names if the + controlling instance of dillo exits and another must "take + over" the managing. + * It increments dillo's core size. + * ... + + That's why the managing daemon scheme was chosen. + + +---------------------- +What does dpid handle? +---------------------- + + It solves all the above mentioned shortcomings and also can do: + + * Multiple dillos: + dpid can communicate and serve more than one instance + of dillo. + + * Multiple dillo windows: + two or more windows of the same dillo instance accessing dpis + at the same time. + + * Different implementations of the same service + dpi programs ("dpis") are just an implementation of a + service. There's no problem in having more than one for the + same service. + + * Upgrading a service: + to a new version or implementation without requiring + bringing down the dpid or patching dillo's core. + + + And finally, being aware that this design can support the +following functions is very helpful: + + SCHEME Example + ------------------------------------------------------------ + * "one demand/one response" man, preferences, ... + * "resident while working" downloads, mp3, ... + * "resident until TERM signal" bookmarks, ... + + * "one client only" cd burner, ... + * "one client per instance" man, ... + * "multiple clients/one instance" downloads, cookies ... + + +-------- +Features +-------- + * Dpi programs go in: "EPREFIX/dillo/dpi" or "~/.dillo/dpi". The binaries + are named <name>.dpi as "bookmarks.dpi" and <name>.filter.dpi as in + "hello.filter.dpi". The ".filter" plugins simply read and write to stdio + and can be implemented with a shell script easily. + * Register/update/remove dpis from list of available dpis when a + <dpi cmd='register_all'> is received. + * dpid terminates when it receives a <dpi cmd='DpiBye'> command. + * dpis can be terminated with a <dpi cmd='DpiBye'> command. + * dpidc control program for dpid, currently allows register and stop. + + +----- +todo: +----- + + These features are already designed, waiting for implementation: + + * How to register/update/remove/ individual dpis? + * How to kill dpis? (signals) + + How: + + A useful and flexible way is to have a "control program" for +dpid (it avoids having to find its PID among others). + + Let's say: + + dpidc [register | upgrade | stop | ...] + + It can talk to a dpid UDS that serves for that (the same that +dillo would use). That way we may also have a dpidc dpi! :-) + + Seriously, what I like from this approach is that it is very +flexible and can be implemented incrementally ("dpidc register" +is enough to start). + + It also avoids the burden of having to periodically check the +dpis directory structure for changes). + + It also lets shell scripts an easy way to do the "dirty" work +of installing dpis; as is required with distros' package +systems. + +<note> + How do we tell a crashed dpi? That's the question. + We're thinking about using the "lease" concept (as in JINI). +</note> + + +----------------- +How does it work? +----------------- + +o on startup dpid reads dpidrc for the path to the dpi directory + (usually EPREFIX/lib/dillo/dpi). ~/.dillo/dpi is scanned first. + +o both directories are scanned for the list of available plugins. + ~/.dillo/dpi overrides system-wide dpis. + +o ~/.dillo/dpi_socket_dir is then checked for the name of the dpi socket + directory, if dpi_socket_dir does not exist it will be created. + +o next it creates Unix domain sockets for the available plugins and + then listens for service requests on its own socket (dpid.srs) + and for connections to the sockets of inactive plugins. + +o dpid returns the name of a plugin's socket when a client (dillo) + requests a service. + +o if the requested plugin is a 'server' then + 1) dpid stops watching the socket for activity + 2) forks and starts the plugin + 3) resumes watching the socket when the plugin exits + +o if the requested plugin is a 'filter' then + 1) dpid accepts the connection + 2) duplicates the connection on stdio + 3) forks and starts the plugin + 4) continues to watch the socket for new connections + + + + +--------------------------- +dpi service process diagram +--------------------------- + + These drawings should be worth a thousand words! :) + + +(I) + .--- s1 s2 s3 ... sn + | + [dpid] [dillo] + | + '--- srs + + The dpid is running listening on several sockets. + + +(II) + .--- s1 s2 s3 ... sn + | + [dpid] [dillo] + | | + '--- srs ------------------' + + dillo needs a service so it connects to the service request + socket of the dpid (srs) and asks for the socket name of the + required plugin (using dpip). + + +(III) + .--- s1 s2 s3 ... sn + | | + [dpid] | [dillo] + | | | + '--- srs '---------------' + + then it connects to that socket (s3, still serviced by dpid!) + + +(IV) + .--- s1 s2 s3 ... sn + | | + .[dpid] | [dillo] + . | | | + . '--- srs '---------------' + . + .............[dpi program] + + when s3 has activity (incoming data), dpid forks the dpi + program for it... + + +(V) + .--- s1 s2 (s3) ... sn + | + [dpid] [dillo] + | | + '--- srs .---------------' + | + [dpi program] + + ... and lets it "to take over" the socket. + + Once there's a socket channel for dpi and dillo, the whole +communication process takes place until the task is done. When +the dpi program exits, dpid resumes listening on the socket (s3). + + +----------------------------------------------- +How are the unix-domain-sockets for dpis named? +----------------------------------------------- + + Let's say we have two users, "fred" and "joe". + + When Fred's dillo starts its dpid, the dpid creates the +following directory (rwx------): + + /tmp/fred-XXXXXX + + using mkdtemp(). + + and saves that filename within "~/.dillo/dpi_socket_dir". + + That way, another dillo instance of user Fred can easily find +the running dpid's service request socket at: + + /tmp/fred-XXXXXX/dpid.srs + + (because it is saved in "~/.dillo/dpi_socket_dir"). + + Now, we have a dpi directory per user, and its permissions are +locked so only the user has access, thus the following directory +tree structure should pose no problems: + + /tmp/fred-XXXXXX/bookmarks + /downloads + /cookies + /ftp + ... + dpid.srs + + If user Joe starts his dillo, the same happens for him: + + /tmp/joe-XXXXXX/bookmarks + /downloads + /cookies + /ftp + ... + dpid.srs + + + What should dpid do at start time: + + Check if both, ~/.dillo/dpi_socket_dir and its directory, exist + (it can also check the ownership and permissions). + + If (both exist) + use them! + else + delete ~/.dillo/dpi_socket_dir + create another /tmp/<user>-XXXXXX directory + save the new directory name into ~/.dillo/dpi_socket_dir + (we could also add some paranoid level checks) + + To some degree, this scheme solves the tmpnam issue, different +users of dillo at the same time, multiple dillo instances, +polluting /tmp (cosmetic), and reasonably accounts for an +eventual dillo or dpid crash. + + It has worked very well so far! + + +-------------------------------- +So, how do I make my own plugin? +-------------------------------- + + First, at least, read the "Developing a dillo plugin" section +of dpi1 spec! :-) + + Note that the dpi1 spec may not be absolutely accurate, but the +main ideas remain. + + Once you've got the concepts, contrast them with the drawings +in this document. Once it all makes sense, start playing with +hello.dpi, you can run it by starting dillo with + dillo dpi:/hello/ +or entering + dpi:/hello/ +as the url. Then try to understand how it works (use the drawings) +and finally look at its code. + + Really, the order is not that important, what really matters is +to do it all. + + Start modifying hello.dpi, and then some more. When you feel +like trying new things, review the code of the other plugins for +ideas. + + The hardest part is to try to modify the dpi framework code +inside dillo; you have been warned! It already supports a lot of +functionality, but if you need to do some very custom stuff, try +extending the "chat" command. + + +--------------------------------- +Examples: Simple 'filter' plugins +--------------------------------- + + For a quick and dirty introduction to dpis try the following shell scripts. + + #!/bin/sh + + read -d'>' dpip_tag # Read dillo's request + + # Don't forget the empty line after the Content-type + cat <<EOF + <dpi cmd='start_send_page' url='dpi:/hi/hi.filter.dpi'> + Content-type: text + + EOF + + echo Hi + + Of course you should use html in a real application (perl makes this easy). + + A more useful example uses the "si" system info viewer: + + #!/bin/sh + # si - System Information Viewer + + read -d'>' dpip_tag + + # We don't need to send the Content-type because "si --html" does this + # for us. + cat <<EOF + <dpi cmd='start_send_page' url='dpi:/si/si.dpi.filter'> + EOF + + si --html + + just make sure that you have si installed or you wont get far. + +To try out the examples create two directories for the scripts under your home directory as follows: + mkdir -p ~/.dillo/dpi/hi + mkdir -p ~/.dillo/dpi/si + +then create the scripts and put them in the dpi service directories so that you end up with + ~/.dillo/dpi/hi/hi.filter.dpi + ~/.dillo/dpi/si/si.filter.dpi + +Don't forget to make them executable. + +If dpid is already running register the new plugins with + dpidc register + +You can now test them by entering + dpi:/hi/ +or + dpi:/si/ +as the url. Or simply passing the url to dillo on startup + + dillo dpi:/si/ + + + You can edit the files in place while dpid is running and reload them in +dillo to see the result, however if you change the file name or add a new +script you must run 'dpidc register'. + +WARNING +Multiple instances of a filter plugin may be run concurrently, this could be a +problem if your plugin records data in a file, however it is safe if you simply +write to stdout. Alternatively you could write a 'server' plugin instead as +they are guaranteed not to run concurrently. + >>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<< + diff --git a/doc/Dw.txt b/doc/Dw.txt new file mode 100644 index 00000000..6ffd55d0 --- /dev/null +++ b/doc/Dw.txt @@ -0,0 +1,383 @@ +Jan 2001, S.Geerken@ping.de +Last update: Dec 2004 + +================ +Dw: Dillo Widget +================ + +NOTE (Aug 2004): The rendering has changed quite much, and many other +documents are out of date, in ways described in DwRendering.txt. They +will be updated some day. + +Dw is mainly the module for rendering HTML. It provides a framework +for widgets, based on the Gtk+ object framework, and is very similar +to Gtk+, so that experiences in using and extending Gtk+ help very +much in understanding Dw. There is some documentation at www.gtk.org +(and probably on your local harddisk, somewhere in /usr/doc/*gtk*), +you should especially have read the chapter "Writing Your Own +Widgets" in the tutorial. + + +Why Not Gtk+? +============= + +There are two reasons for designing a new model instead of simply +using Gtk+ objects: + + 1. Most important, Gtk+ widgets are limited in size, because X + windows are so. + 2. There are a few extensions which are due to the different needs + for HTML rendering compared to GUI's. (Of course, this could + have been solved by defining a new object derived from + GtkWidget.) + + +Notes On Naming +=============== + +According to the naming standards, functions beginning with "a_Dw_" +may be used outside of Dw, while, as an extention, functions used +within Dw (e.g. p_Dw_widget_queue_resize) are prefixed with "p_Dw_". +[todo: This could be included in NC_design.txt.] + +Non-static functions beginning with "Dw_" are only used between +GtkDwViewport and DwWidget (e.g. Dw_gtk_viewport_remove_dw), they +belong to the core of Dw. And, of course, functions only used within a +sub-module (e.g. a specific widget) start with "Dw_" and are static +(e.g. Dw_page_find_line_index). + +Dw widgets and some other structures have the prefix "Dw", while Gtk+ +widgets in Dw have the prefix "GtkDw", but functions of them begin +with "Dw_gtk_" or "a_Dw_gtk", respectively. + + +Basic Overview +============== + +Dw widgets are objects derived from DwWidget, which itself derives +from GtkObject. DwWidget is quite similar to GtkWidget, the main +difference is that Dw widgets are always windowless and that they are +presented in a viewport, so there is no need to limit the size. Much +of the functionality normally provided by the X server is simulated +by Dw. + +The interface between Gtk+ and Dw is the Gtk+ widget GtkDwViewport, +which contains (at most) one top-level Dw widget. + +A Few Definitions: + + - world coordinates: coordinates relative to the upper left corner + of the whole scrolled area ("the world") + - viewport coordinates: coordinates relative to the upper left + corner of the visible area + - widget coordinates: coordinates relative to the upper left corner + of the widget + +Dw widgets draw into the viewport window, and must adhere to +*viewport coordinates*: the "world" is only an abstract term, there +is no window for it. When GtkDwViewport processes expose events, they +are automatically delivered to the Dw widgets. Redrawing requests due +to scrolling of the viewport is done by the base object GtkLayout, +you will not find any code for this in Dw. + +Mouse events also contain viewport coordinates. Dw will try to find +the right Dw widget to deliver the event to. + +Resizing the GtkDwViewport will not resize the top-level Dw widget, +but the latter will get some hints, so that, e.g., the page widget +rewraps the lines at the appropriate width. + +See DwWidget.txt for more details. + + +Embedding Gtk+ Widgets In Dw +---------------------------- +Dw Widgets may embed Gtk+ widgets, this is done by the Dw widget +DwEmbedGtk. For Gtk+, these embedded Gtk+ widgets are themselves +children of the GtkDwViewport, since Gtk+ does not care about Dw. + +Of course, embedded Gtk+ widgets are again limited in size, but but +in position: GtkDwViewport is derived from GtkLayout which is exactly +designed for positioning widgets in an infinite scrolled area. + + +How To Get The Top-Level Dw Widget From A BrowserWindow +------------------------------------------------------- +The member "docwin" of BrowserWindow points on a GtkDwScrolledWindow, +which contains a GtkDwScrolledFrame, which contains a GtkDwViewport. +The member "child" of the latter points on the top-level Dw widget, +or may be NULL. The top-level Dw is (currently) a DwPage (HTML and +plain text documents) or a DwImage (images). + +There is a function a_Dw_gtk_scrolled_window_get_dw for this. + + +Sizes +----- +A feature adapted from the old Dw are baselines. As well DwAllocation +as DwRequisition do not have a height member, but instead ascent and +descent, both positive or zero. (Originally I removed this, but there +will be a few widgets in future depending on this, e.g., math +formulas.) + +Unlike in Gtk, sizes of zero are allowed. The upper limit for the +size of a widget is 2^31 (this will be enough to show the contents of +a small library in a web page). + + +Resizing +======== + +From outside: When writing a new widget, you should implement the +signal "size_request". When the widget changes its size, it should +call p_Dw_widget_queue_resize, as in a_Dw_image_size. See "Incremental +Resizing" below for a way to increase the speed. + +Even if the implementation of "size_request" gets quite expensive, +you do not have to check whether the size has changed, this is done +by a_Dw_widget_size_request. + +Inside: q_Dw_widget_queue_resize will set the DW_NEEDS_RESIZE flag, a +further call of a_Dw_widget_size_request will only then emit the +"size_request" signal. Furthermore, mark_size_change and +mark_extremes_change are called (see below). After that, the resizing +is done in an idle loop, this prevents too many size requests. The +algorithm is quite simple: any widget with a child which needs +resizing, needs resizing, thus all parents up to top-level widget are +marked. + +Incremental Resizing +--------------------- +A widget may calculate its size based on size calculations already +done before. In this case, a widget must exactly know the reasons, why +a call of size_request is necessary. To make use of this, a widget +must implement the following: + + 1. There is a member in DwWidget, called parent_ref, which is + totally under control of the parent widget (and so sometimes not + used at all). It is necessary to define how parent_ref is used + by a specific parent widget, and it has to be set to the correct + value whenever necessary. + + 2. The widget must implement mark_size_change and + mark_extremes_change, these methods are called in two cases: + + a) directly after q_Dw_widget_queue_resize, with the argument + ref was passed to q_Dw_widget_queue_resize, and + b) if a child widget has called q_Dw_widget_queue_resize, + with the value of the parent_ref member of this child. + +This way, a widget can exactly keep track on size changes, and so +implement resizing in a faster way. A good example on how to use this +is the DwPage widget, see DwPage.txt for details. + + +Anchors and Scrolling +===================== + +Anchors +------- +todo: This section is out of sync with the actual code. + +To set the anchor a page is viewed at, you can use one of the +following functions: + + - void a_Dw_gtk_viewport_set_anchor (GtkDwViewport *viewport, + gchar *anchor) + + Scroll directly to an anchor. The anchor does not need to exist + already, see below. + + - void a_Dw_gtk_viewport_queue_anchor (GtkDwViewport *viewport, + gchar *anchor) + + Set the anchor for the next top-level DwWidget (the next call + of a_Dw_gtk_viewport_add_dw). + +There are wrappers, a_Dw_gtk_scrolled_window_queue_anchor and +a_Dw_gtk_scrolled_window_set_anchor. + +After a_Dw_gtk_viewport_set_anchor has been called (indirectly by +Nav_open_url, or by a_Dw_gtk_viewport_add_dw), changes of anchor +positions (e.g., if widgets change there size or the anchor was not +known before) will correct the viewport adjustment (in function +p_Dw_gtk_viewport_update_anchor), but only as long as the user did not +change it directly. Look at Dw_gtk_scrolled_window_init for details +about the latter. + +Use p_Dw_widget_set_anchor to add anchors to a widget, see +DwWidget.txt. + +Scrolling +--------- +Here is an overview on more functions for scrolling: + + - To scroll to a given position, there are two possibilities: + a_Dw_gtk_viewport_set_scrolling_position simply scrolls to this + position, while Dw_gtk_viewport_scroll_to has more facilities: + you specify a rectangle you want to see, and the way how it is + seen (at the border, centered, or just scroll as much as + necessary, that it is seen). If you have a widget, you can also + use Dw_widget_scroll_to. There is also a wrapper for + GtkDwScrolledWindow, + a_Dw_gtk_scrolled_window_set_scrolling_position, and two + functions for getting the position, + a_Dw_gtk_scrolled_window_get_scrolling_position_x, and + a_Dw_gtk_scrolled_window_get_scrolling_position_y. + + - If you have a region, and want to display it, use + a_Dw_iterator_scroll_to. For example, the findtext module makes + use of it. There are equivalents for DwExtIterator and + DwWordIterator. See comments on and in the function for more + informations. + + - If you just want to determine where some content is allocated, + represented by an iterator, you can use + a_Dw_iterator_get_allocation. There are equivalents for + DwExtIterator and DwWordIterator. + + +The Objects +=========== + +This is the hierarchy of all objects of Dw: + + (GtkObject) + +-DwWidget + | +----DwBullet + | +----DwContainer + | | `----DwPage + | +----DwEmbedGtk + | +----DwHruler + | `----DwImage + `----(GtkWidget) + `----(GtkContainer) + +----(GtkBin) + | +----(GtkScrolledWindow) + | | `----GtkDwScrolledWindow + | `----GtkDwScrolledFrame + `----(GtkLayout) + `----GtkDwViewport + +Objects in parentheses are part of Gtk+, not of Dw. + + +DwBullet +-------- +Simple widget used for unnumbered list (<ul>). + + +DwContainer +----------- +The base object for Dw widgets which contain other Dw widgets. As in +Gtk+, containers are responsible for storing the children, there is +no common data structure. There are a few signals: + + - void add (DwContainer *container, + DwWidget *child); + + Currently not used, but may be in future. + + - void remove (DwContainer *container, + DwWidget *widget); + + *Recognize* that a widget is destroyed, i.e., an implementation + should remove *the pointer* from the list or so, but not + destroy the child widget. It is called by Dw_widget_shutdown. + + - void forall (DwContainer *container, + DwCallback callback, + gpointer callback_data); + + Process callback for all children, in the form + (*callback)(child, callback_data). + + The include_internals of the Gtk+ equivalent was not adapted, + since it is used for more sophisticated purposes not needed in + Dw. + + +DwEmbedGtk +---------- +This Dw widget is used to embed Gtk+ widgets into Dw container +widgets. The Gtk+ widget is set by a_Dw_embed_gtk_add_gtk, and can +simply be removed by destroying it. + +If the DwEmbedGtk contains no Gtk+ widget, it always returns 0x0x0 as +size, so, for speed reasons, first add the Gtk+ widget into the +DwEmbedGtk, and then the DwEmbedGtk into the other Dw widget, as at +the end of Html_tag_open_input. + + +DwHruler +-------- +Simple widget used for the <hr> tag. + + +DwImage +------- +Widget for displaying image. See DwImage.txt for details. + + +DwPage +------ +A widget for displaying texts. See DwPage.txt for details. + + +DwTable +------- +A container widget for rendering tables. See DwTable.txt for details. + + +DwWidget +-------- +The base object for all Dw widgets. See DwWidget.txt for details. + + +GtkDwScrolledWindow +------------------- +Adds a few functionalities to GtkScrolledWindow: it creates the +GtkDwScrolledFrame and the GtkDwViewport, connects some signals, and +provides some wrappers for using the GtkDwViewport. + + +GtkDwScrolledFrame +------------------ +General purpose scrolled widget containing another scrolled widget, +adding a border and a focus frame. Furthermore, it processes key +presses and mouse drags (button 2, as in Gimp) to move the viewport. + +There are two signals (except "set_scroll_adjustments"), +"user_hchanged" and "user_vchanged", which are emitted when the user +changed the viewport adjustments horizontally/vertically by using the +keys or button 2 dragging. + + +GtkDwViewport +------------- +The interface between Gtk+ and Dw. It is responsible for displaying +Dw Widgets and processing their events. It is derived from GtkLayout, +to make embedding Gtk+ widgets into Dw widgets simpler, see the +documentation of GtkLayout in the Gtk+ tutorial for details. + +GtkDwViewport contains at most one top-level Dw Widget, if it exists. +The Gtk+ methods of GtkDwViewport are more or less mapped on the +methods of the DwWidget. In detail: + + - Dw_gtk_viewport_size_allocate will call a_Dw_widget_set_width, + a_Dw_widget_set_ascent (with allocation->height) and + a_Dw_widget_set_descent (with zero as argument), and then allocate + the Dw widget at the size returned by a_Dw_widget_size_request. + + - Dw_gtk_viewport_draw and Dw_gtk_viewport_expose will call + a_Dw_widget_draw, which will emit the "draw" signal. + + - Handling of mouse events is mostly done in Dw_widget_mouse_event, + see DwWidget.txt for details. Note that the functions return + FALSE, if the event was not processed, so that they are delivered + to the parent widget(s) of the GtkDwViewport, this scheme e.g. + prevents dragging of the viewport (done by GtkScrolledFrame) when + pressing mouse button 2 on a link. + +You may call gtk_container_set_border_width for a border around the +scrolled area. diff --git a/doc/DwImage.txt b/doc/DwImage.txt new file mode 100644 index 00000000..d79f50bf --- /dev/null +++ b/doc/DwImage.txt @@ -0,0 +1,201 @@ +Jan 2001, S.Geerken@ping.de +Last update: Dec 2004 + +======= +DwImage +======= + +A widget for displaying images and handling image maps. + + +Image Maps +========== + +Client Side Image Maps +---------------------- +You must first create a list of image maps: Allocate a DwImageMapList, +and initialize it by calling a_Dw_image_map_list_init. Adding a map is +done by a_Dw_image_map_list_add_map. a_Dw_image_map_list_add_shape +adds a shape to the last map. For the meaning of the link argument, +see Section "Signals". + +Image maps are referred by a URL (not only by a name). But currently, +the image map list is stored in DilloHtmlLB and there is no +possibility to parse documents without rendering, so images can only +use maps defined in the same document. + +To use a map in an image, call a_Dw_image_set_usemap with the image, +the map list, and the URL of the map. Passing the whole list makes it +possible to use maps parsed after the image is created. + + +Server Side Image Maps +---------------------- +To use images for server side image maps, you must call +a_Dw_image_set_ismap and the style must contain a valid link +element. See section "Signals" for more details. + + +Signals +======= + +There are five signals, which can be connected to process actions with +links. All have at least three arguments: + + - link is the link element of the DwStyle (server side image maps) + or DwImageMapShape (client side image maps). The value is an + integer, which is currently only used for hypertext links. But + generally, it depends on the signal callback how this value is + used. + + - x and y are, when server side image maps are used, the relative + coordinates of the mouse pointer, otherwise always -1. + +Note that, unlike by DwPage before, no cursors are set. Instead, the +signal callback function has to do this. + +The signals: + + - void link_entered (DwImage *image, + gint link, gint x, gint y) + + Emitted when the link the mouse pointer is over has + changed. "Changed" includes link, x and y, so this signal is also + emitted each time the pointer is moved within a server side image + map. If the pointer is outside of a link, all arguments have the + value -1. + + + - void link_pressed (DwImage *image, + gint link, gint x, gint y, + GdkEventButton *event) + + Emitted when the user presses a mouse button _inside_ a link, + this signal is never emitted with link = -1. You can use the + event to get information about the button, shift keys, etc. + + + - void link_released (DwImage *image, + gint link, gint x, gint y, + GdkEventButton *event) + + - void link_clicked (DwImage *image, + gint link, gint x, gint y, + GdkEventButton *event) + + Analogue to link_pressed. + + - void void (*image_pressed) (DwImage *page, + GdkEventButton *event) + + Emitted when the user presses the mouse button on an image which + has no related map. In some cases, it may be neccessary to + suppress event processing by a_Dw_widget_set_button_sensitive(). + + +Future Extentions +================= + +(See also Imgbuf.txt, what is named DilloImageBuffer here, has been +implemented under the name Imgbuf.) + +These are some ideas for a different design, which will solve several +problems (image transparency, memory usage when implementing a global +size factor): + +1. Instead of a guchar array, a new data type, DilloImageBuffer, + should be used (DICacheEntry::ImageBuffer and DwImage::buffer). Any + access is done by function calls. Copying the lines (in + a_Image_write) is done by a_Image_buffer_copy_line, etc. The call to + Image_line becomes obsolete, since DilloImageBuffer will deal with + different types: indexed, RGB, gray, RGBA, gray-alpha(?). This may + be useful for a more efficient implementation of DilloImageBuffer. + +2. The modules Png, Jpeg, Gif, Image and DICache deal with the + original image size (read by the decoders), while DwImage gets the + "viewed" size (original size, multiplied by the global image + scaling factor) from DilloImageBuffer. + +3. For DwImage, there are further methods which replace the current + direct access. Note to worth: + + - Scaled buffers are shared, reference counters are used. Copying + rows will automatically also affect the related scaled buffers. + + - There are two methods for drawing, one called after expose events + (Dw_image_draw) and another called by a_Dw_image_draw_row. The + exact rules, how the buffer is scaled (this can, e.g., differ by a + pixel or so), is hidden by DilloImageBuffer. + + - As noted above, all DwImage code is based on the viewed size. + +4. The global image scaling factor is used in two places. The HTML + parser must apply it on the WIDTH and HEIGHT attributes of the + <IMG> tag and DilloImageBuffer must use it to scale the inherent + size of an image. There are two modes, which the user can switch + by a dillorc option: + + (i) The original image data is preserved, and an additional scaled + buffer is created: + + +-------+ +------------------+ additional + | Image | -- copy --> | DilloImageBuffer | --> scaled + +-------+ rows | (original size) | buffers + +------------------+ + | ^ + | | + scale requests + each row for scaled + | buffers + | | + v | + +------------------+ + | DilloImageBuffer | + | (viewed size) | + +------------------+ + ^ + | + +---------+ + | DwImage | + +---------+ + + (ii) The original data gets lost just after scaling: + + +-------+ +------------------+ + | Image | -- copy --> | DilloImageBuffer | --> scaled + +-------+ rows | (viewed size) | buffers + +------------------+ + ^ + | + +---------+ + | DwImage | + +---------+ + + (ii) uses generally less memory, while in some cases leads to a + lower rendering quality, as in this example: + + "foo.png" has a size of 100x100 pixels, the image scaling factor is + 50%, and the following HTML sniplet is rendered: + + <img src="foo.png"> + <img src="foo.png" width=200 height=200> + + The first image is displayed at a size of 50x50, the second at + 100x100. (i) will, for the second, use the original buffer, but + (ii) will enlarge the buffer, which was scaled down before, so + resulting in a "pixelized" image. + +5. Any implementation of DilloImageBuffer will handle transparency + independent of the background, i.e., the background colour will not + be used anymore. The first implementation may be based on GdkRGB + and (when needed) dithered clipping bitmaps. Better implementations + may then be developed in the future. + +6. Background images: The modules Image and DICache do no longer + access the DwImage directly, all calls are replaced by an + Interface, which is then implemented by DwImage and an appropriate + structure for background images (part of DwStyle). The interface is + in C realized by a struct of function pointers, and a generic + pointer on DwImage, etc. + +7. If someone really needs it, animated GIF's may be considered. diff --git a/doc/DwPage.txt b/doc/DwPage.txt new file mode 100644 index 00000000..71e6af88 --- /dev/null +++ b/doc/DwPage.txt @@ -0,0 +1,152 @@ +Nov 2001, S.Geerken@ping.de +Last update: Dec 2004 + +====== +DwPage +====== + +A widget for displaying texts. It is (currently) the main widget for +rendering HTML documents. + + +Signals +======= + +DwPage defines the same signals as DwImage, except "image_pressed", +with the exception that the coordinates are always -1. See +DwImage.txt for more details. + + +Collapsing Spaces +================= + +The idea behind this is that every text box has a specific vertical +space around and that they are combined to one space, according to +rules stated below. A rule is either a paragraph within a DwPage +widget, or a DwPage within a DwPage widget, in a single line; the +latter is used for indented boxes and list items. + +The rules: + + 1. If a box is following another, the space between them is the + maximum of both box spaces: + + +---------+ + |/////////| + |/////////| +---------+ + +---------+ |/////////| + | A | |/////////| + +---------+ +---------+ + |/////////| | A | + |/////////| +---------+ + +---------+ are combined like this: |/////////| + |XXXXXXXXX| + +---------+ +---------+ + |\\\\\\\\\| | B | + +---------+ +---------+ + | B | |\\\\\\\\\| + +---------+ +---------+ + |\\\\\\\\\| + +---------+ + + 2. a) If one box is the first box within another, the upper space + of these boxes collapse. b) The analogue is the case for the + last box: + + +---------+ If B and C are put into A, + |/////////| the result is: + |/////////| + +---------+ +---------+ +---------+ + | A | <--+-- |\\\\\\\\\| |/////////| + +---------+ ¦ +---------+ |XXXXXXXXX| + |/////////| | | B | +---------+ + |/////////| | +---------+ | B | + +---------+ | |\\\\\\\\\| +---------+ + | +---------+ |\\\\\\\\\| + | |\\\\\\\\\| + | +---------+ |\\\\\\\\\| + `-- |\\\\\\\\\| +---------+ + |\\\\\\\\\| | C | + |\\\\\\\\\| +---------+ + +---------+ |\\\\\\\\\| + | C | |XXXXXXXXX| + +---------+ |XXXXXXXXX| + |\\\\\\\\\| +---------+ + |\\\\\\\\\| + |\\\\\\\\\| + +---------+ + +For achieving this, there are some features of DwPage: + + - Consequent breaks are automatically combined, according to + rule 1. See the code of a_Dw_page_add_break for details. + + - If a break is added as the first word of the DwPage within + another DwPage, collapsing according to rule 2a is done + automatically. See the code of a_Dw_page_add_break. + + - To collapse spaces according to rule 2b, + a_Dw_page_hand_over_break must be called for the *inner* + widget. The HTML parser does this in Html_eventually_pop_dw. + + +Collapsing Margins +================== + +Collapsing margins, as defined in the CSS2 specification, are, +supported in addition to collapsing spaces. Also, spaces and margins +collapse themselves. I.e., the space between two paragraphs is the +maximum of the space calculated as described in "Collapsing Spaces" +and the space calculated according to the rules for collapsing margins. + +(This is an intermediate hybrid state, collapsing spaces are used in +the current version of dillo, while I implemented collapsing margins +for CSS and integrated it already into the main trunk. For a pure +CSS-based dillo, collapsing spaces will not be needed anymore, and may +be removed for simplicity.) + + +Some Internals +============== + +There are two lists, words and lines. The word list is quite static; +only new words may be added. A word is either text, a widget, a break +or an anchor. Anchors are stored in the text, because it may be +necessary to correct the scroller positions at rewrapping. + +Lines refer to the word list (first and last), they are completely +redundant, i.e., they can be rebuilt from the words. Lines can be +rewrapped either completely or partially (see "Incremental Resizing" +below). For the latter purpose, several values are accumulated in the +lines. See the file "dw_page.h" for details. + + +Incremental Resizing +-------------------- +DwPage makes use of incremental resizing as described in Dw.txt, +section "Resizing". The parent_ref is, for children of a DwPage, +simply the number of the line. + +Generally, there are three cases which may change the size of the +widget: + + 1. The available size of the widget has changed, e.g., because the + user has changed the size of the browser window. In this case, + it is necessary to rewrap all the lines. + + 2. A child widget has changed its size. In this case, only a rewrap + down from the line where this widget is located is necessary. + + (This case is very important for tables. Tables are quite at the + bottom, so that a partial rewrap is relevant. Otherwise, tables + change their size quite often, so that this is necessary for a + fast, non-blocking rendering) + + 3. A word (or widget, break etc.) is added to the page. This makes + it possible to reuse the old size by simply adjusting the + current width and height, so no rewrapping is necessary. + +The state of the size calculation is stored in wrap_ref within DwPage, +which has the value -1 if no rewrapping of lines necessary, or +otherwise the line from which a rewrap is necessary. + diff --git a/doc/DwRender.txt b/doc/DwRender.txt new file mode 100644 index 00000000..73fd0ded --- /dev/null +++ b/doc/DwRender.txt @@ -0,0 +1,620 @@ +Aug 2004, S.Geerken@ping.de + +=========================== + Dw Render Abstraction +=========================== + +This document describes the rendering abstraction used in Dw. At the +time this document was written, some other documents about Dw were out +of date, and have to be rewritten. + +Sometimes, you will find remarks about the old design (with one +concrete Gtk+ widget instead of the layout/multiple rendering views); +those are useful for those, who are already familiar with the old +design. Furthermore, it makes the explanation often simpler, because +the new design is a bit more complex. + +Naming +------ +As stated in "Dw.txt", the prefix "p_Dw_" is used for functions used +within the whole Dw module, but not outside. Typically, these are +protected functions, which are called by inherited classes. E.g., a +specific widget will have to call p_Dw_widget_queue_resize(), but this +function should not called outside from Dw. + +The "Dw_" prefix is not only used for functions, which use is +restricted to a sub-module, but also for functions used only within +the core of Dw. The distinction between both can simply made, whether +the function is declared as static or not. The core of Dw consists of +the modules Dw_widget and Dw_render, anything else is inherited from +these two modules (widgets, platforms, views). As an example, +Dw_gtk_viewport_queue_resize() is only called in the Dw_widget module, +it should not be called by a widget implementation. + + +Overview +======== + +Rendering of Dw is done in a way resembling the model-view pattern, at +least formally. Actually, the counterpart of the model, the layout +(DwRenderLayout), does a bit more than a typical model, namely +calculating the layout, and the views do a bit less than a typical +view, i.e. only the actual drawing. + +Additionally, there is a structure representing common properties of +the platform, views generally work only together with one specific +platform. A platform is typically related to the underlying UI +toolkit, but other uses may be thought of. + +The general structure looks like this: + + 1 +------------------+ + .---->| DwRenderPlatform | + +----------------+ 1 / +------------------+ + | DwRenderLayout |--< ^ 1 + +----------------+ \ | * + 1 ^ | 1 \ +--------------+ + | | `---->| DwRenderView | + | | * +--------------+ + | | + layout | | toplevel_dw + | | + * | V 0..1 + +----------+ 0..1 + | DwWidget |--. parent + +----------+ | + * | | + `-------' + (children) + +(This is a bit simplified, actually, only DwContainer has child +widgets, and the relation is only defined in an abstract way). + +DwRenderLayout and DwWidget (including sub classes) are platform and +view independant, i.e., it should be possible to adopt them into +another platform, after suitable implementations of DwRenderPlatform +and DwRenderView have been developed. + +(The only exception is DwEmbedGtk, a widget which, in the old design, +embeds Gtk+ widgets into the DwWidget tree. This will still remain a +platform specific widget, see notes in the section "UI Widgets". + +Furthermore, this design does not yet cover DwStyle, which is still +bound to the Gtk+ platform. It may be simply replaced for other +platforms, but this will lose the possibility to have multiple +implementations of DwRenderPlatform running within the same program. +The same aplies to DwTooltip and Imgbuf.) + +This new design helps to archieve two important goals: + + 1. Abstraction of the actual rendering. Currently, we only have the + normal viewport, but a list of planned implementations can be + found below in this text. + + 2. It makes different views of the same document simple, e.g. the + normal viewport and the preview window. + + 3. It makes portability simpler. + +Vieports are handles by this design, but only in a rather abstract +way, and may be removed completely, i.e., they will be only part of +specific view implementations. This also means, that the distinction +between world coordinates and viewport coordinates vanishes from most +parts of Dw. + + +World +===== + +The world is the whole area, in which content is rendered. For a view +without a viewport, this is the whole itself. A view with a viewport +provides a way, so that the user may see some part of the world within +a (in most cases) smaller window, the viewport. + +The world may have a base line, so that the worls size is described by +three numbers, width, ascent and descent. + +Any view must implement the method set_world_size(), which is called, +whenever the world size changes. For viewports, this will change +scroll bars etc., for views without viewport, this will normally +change the size of the view itself. (Although on this level, "view +size" is not defined. This should not be confused with the viewport +size!) + + +Drawing +======= + +A view must implement several drawing methods, which work on the whole +world. If it is neccesary to convert them (e.g. in DwGtkViewport), +this is done in a way fully transparent to DwWidget and +DwRenderingLayout, instead, this is done by the view implementation. + +There exist following situations: + + - A view gets an expose event: It will delegate this to the + rendering layout, which will then pass it to the widgets, with + the view as a parameter. Eventually, the widgets will call + drawing methods of the view. + + - A widget requests a redraw: In this case, the widget will + delegate this to the layout. The drawing requests are queued, and + compressed. in an this idle function, DwWidget::draw is called + for the toplevel widget, for each view. + + (This is still something to consider, the queueing may be moved + again into the view implementations.) + +If the draw method of a widget is implemented in a way that it may +draw outside of the widget's allocation, it should draw into a +clipping view. A clipping view is a rendering view related to the +actual rendering view, which guarantees that the parts drawn outside +are discarded. At the end, the clipping view is merged into the actual +view. Sample code for widget DwFoo: + + void Dw_foo_draw (DwWidget *widget, + DwRenderView *view, + DwRectangle *area) + { + DwRenderView *clip_view; + + /* 1. Create a clipping view. */ + clip_view = + p_Dw_render_view_get_clipping_view (view, + widget->allocation.x, + widget->allocation.y, + widget->allocation.width + DW_WIDGET_HEIGHT (widget)); + + /* 2. Draw into clip_view. */ + Dw_render_view_do_some_drawing (clip_view, ...); + + /* 3. Draw the children, they receive the clipping view as argument. */ + for (<all relevant children>) { + if (p_Dw_widget_intersect (button->child, area, &child_area)) + p_Dw_widget_draw (child, clip_view, child_area); + } + + p_Dw_render_view_merge_clipping_view (view, clip_view); + } + +A drawing process is always embedded into calls of +DwRenderView::start_drawing() and DwRenderView::finish_drawing(). An +implementation of this are backing pixmaps, to prevent flickering. + + +Viewports and Scrolling Positions +================================= + +Although the design implies that the usage of viewports should be +fully transparent to the Dw_render module, this cannot be fully +archived, for the following reasons: + + 1. Some features, which are used on the level of DwWidget, + e.g. anchors, refer to scrolling positions. + + 2. Some code in DwWidget is currently tightly bound to viewports. + This may be change, by delegating some of this functionality to + viewports, and defining scrolling positions in a more abstract + way. + +Therefor, DwRenderLayout keeps track of the viewport size, the +viewport position, and even the thickness of the scrollbars (or, +generally, "viewport marker"), they are relevant, see below. These +sizes are always equal in all views. However, a given view may not use +viewports at all, and there may be the case, that no view related to a +layout uses viewports, in this case, the viewport size is not defined +at all. + +Unlike world sized, viewports are not considered to have a base line, +i.e., they are described by only two numbers, width and height. + +Viewport Size +------------- +All viewport sizes and positions are the same in all views, which uses +viewports. There are two cases, in which the viewport size changes: + + 1. As an reaction on a user event, e.g. when the user changes the + window size. In this case, the affected view delegates this + change to the layout, by calling + p_Dw_render_layout_vieport_size_changed(). All other views are + told about this, by calling DwRenderView::set_viewport_size(). + + 2. The viewport size may also depend on the visibility of UI + widgets, which depend on the world size, e.g scrollbars, + generally called "viewport markers". This is described in an own + section. + +After the creation of the layout, the viewport size is undefined. When +a view is attached to a layout, and this view is already to be able to +define its viewport size, it may already call +p_Dw_render_layout_vieport_size_changed() within the implementation of +set_layout. If not, it may do this, as soon as the viewport size gets +known. + +Each call of p_Dw_render_layout_vieport_size_changed() will change the +viewport size of all other views, which use viewports, so this +function has to be called with care. + +If there is no view attached, which used viewports, the viewport size +remains undefined. + +Viewport Markers +---------------- +Viewport markers are UI widgets, which display to the user the +relation of the world size and the widget size. Most commonly, +scrollbars will be used. + +When they are not needed, they are hidden, as in the following figure: + + + +--------------------+ + | This is only a | + | very short text. | + | | + | | + | | + +--------------------+ + +but shown, as soon as the world size exceeds the viewport size: + + +------------------+-+ + | In this example, |^| + | there is some |#| + | more text, so |#| + | that the | | + | vertical |v| + +------------------+-+ + +A view using viewports must provide, how large the differences +are. Generally, there are four cases, from the combinations of whether +the world width is smaller (-) or greater (+) than the viewport width, +and whether the world height is smaller (-) or greater (+) than the +viewport height. So there are eight numbers, the horizontal difference +dh, and the vertical difference dv, for the cases --, -+, +-, and ++. + +For scrollbars, the values are rather simple: + + - dh is 0 for the cases -- and -+, and the thickness of the + vertical scrollbar in the cases +- and ++. + + - dv is 0 for the cases -- and +-, and the thickness of the + horizontal scrollbar in the cases -+ and ++. + +For any view implementation, the following rules must be fullfeeded +(dx means either dh or dv): + + - dx(-+) >= d(--) + - dx(++) >= d(+-) + - dx(+-) >= d(--) + - dx(++) >= d(-+) + +In short, when smaller world dimensions (-) grow (switch to +), the +differences may not become less. + +The sizes of viewport markers are part of the viewport size, the +method DwRenderView::set_viewport_size() has four parameters: + + - two for the size of the viewport, *including* the markers + (i.e. the markers never change the size), and + + - two, which denote the differences between the above and the + actual viewport size, caused by the markers. If a value of these + is 0, the respective marker is hidden, if it is greater than 0, + it is shown. In the latter case, the maximun is calculated, and + passed to all views. + +(Actually, the decision, whether the markers are visible or not, is a +bit more complicated, since the vieport size also defines the size +hints for the topmost widget, which may affect the widget size, and so +the world size. Handling this problem is done within DwRenderLayout, +look at the comments there.) + +Scrolling Positions +------------------- +The scrolling position is the world position at the upper left corner +of the viewport. Views using viewports must + + 1. change this value on request, and + 2. tell other changes to the layout, e.g. caused by user events. + +Applications of scrolling positions (anchors, test search etc.) are +handled by the layout, in a way fully transparent to the views. + + +An Example with Nested Views +============================ +The following example refers to graphical plugins, which are not yet +realized (I have some working proof-of-concept code), but which will +most likely follow the design, which is here shortly described, as +needed to understand the example (since there is no specification of +graphical plugins yet). I included this, to demonstrate, how nested +layouts can be used. + +Graphical plugins will communicate with dillo via two protocols, dpi1 +(for anything not directly related to rendering), and a new protocol, +which is an extension of the XEmbed protocol (see somewhere at +http://freedesktop.org, this is the protocol, which GtkPlug and +GtkSocket use). Within dillo, a socket window will be created, which +window id will be (via dpi1?) passed to the plugin. The plugin will +then create a plugin window, in which the contents of a web recource +is shown, which dillo cannot show natively. + +XEmbed will be extended, so that the plugins may make use of the +extensions, which the Dw size model adds to the XEmbed size +model. Such a plugin behaves (on an abstract level) like a DwWidget, +so following extensions are necessary: + + - a plugin may not simply have a size, but instead distinguish + between ascent and descent, + - a plugin may return width extremes, and + - it is possible to send size hints to the plugin. + +Within Dw, the socket will be realized by a special plugin, +DwSocketGtk (or a respective widget for other platforms), which is a +sub class of DwEmbedGtk, and embeds another UI widget, which will +provide the socket window (for Gtk+, either GtkSocket, or a sub class +of this). + +The plugins may be implemented independently of Dw, they either do not +support the extensions to XEmbed (then, the respective features behave +in a default way), or they may contain there own implementation. +However, Dw may be extracted from dillo, to become an own library. A +plugin using this library may then use a special type of view, +GtkDwFlatView (for plugins based on Gtk+, actually, the UI toolkit for +dillo and the plugin must not be the same, since the protocol will be +UI toolkit independant.) + +This will lead to a structure like this: + + top_layout:DwRenderLayout ----- top_page:DwPage + / \ | + :GtkDwPlatform top_view:GtkDwView `- table:DwTable + | + `- cell:DwTableCell + | + `- socket:DwSocketGtk + | + DILLO gtk_socket:GtkSocket + . +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - . (extension of + . XEmbed) + PLUGIN + plugin_layout:DwRenderLayout ----- dw_flat_view:GtkDwFlatView + \ + `------- foo:DwFoo + +GtkDwFlatView is both an extension of GtkSocket, as well as an +implementation of DwRenderView. + +This case must be equivalent to the case, that the widget "foo" is a +direct child of the widget "cell", since all objects between them only +delegate all functionality. Size hints must so sent as in the +following scenario: + + 1. The view "top_view" receieves an event, which will change the + viewport size, and delegates this to the layout. + + 2. The layout will make size hints of this viewport size, i.e., it + calls the set_* methods of the widget "top_page". + + 3. DwPage::set_* will queue a resize request, which is delegated + to the layout. + + 4. Handling the resize request is done within a idle loop. Here, + size_request is called for the widget "top_page", to determine + the preferred size. + + 5. DwPage::size_request will depend on the size hints, especially, + it will use the hinted size to determine the word wrap width. + Within DwPage::size_request, size hints will be sent to the + child widgets. The size hints sent to the children are those + sent to the page, minus the respective border widths (margins, + borders, paddings). (How the ascent and descent hints should + generally be delegated to the children, is still a matter of + consideration, and may change.) + + 6. DwTable::size_request, which is called within this context, has + a different way to delegate the size hints to the children: + ascent and descent are handled the same way, but the widths are + calculated from the column widths, which are calculated within + DwTable::size_request. + + 7. Via the widget "cell", finally the widget "socket" receives + appropriate size hints. These must be (equally) delegated to + the widget "foo", this is done in a way described in the + following steps. + + 8. The size hints are transmitted via the protocol extending + XEmbed. The Gtk+ widget "dw_flat_view" receives them (via the + widget "gtk_socket"), and delegates them to the respective + layout "plugin_layout". (TODO: How is this done? What has to be + done is to send the size hints to the toplevel widget, but a + view has no access to it.) + + 9. The layout "plugin_layout" will then delegate this (what ever + "this" means), as size hints, to the widget "foo". + + 10. The widget "foo" will queue a size_request, which is delegated + to the layout. In the idle function handling this request, the + recalculation of the widget size will change the world size, + which is delegated to the view "dw_flat_view". + + 11. For GtkDwFlatView, the world size is the view size. For this + reason, it changes its size, to fit the world size. This size + is send to the DwSocket, which will then request a size + request. + + +UI Widgets +========== +A note before: This chapter refers often to Gtk+ as base UI toolkit, +just to make naming simpler, For other UI toolkits, the same, mostly +only with other names, applies. + +In some cases, it is necessary to embed widgets from the UI toolkit +into the view, e.g. to realize HTML forms in dillo. + +The Dw_render module provides no interface at all, instead, this is +completely left to the platform implementations. Below, two design +approaches are discussed, which have following in common: + + 1. There is a special Dw widget, which embeds something related to + the UI widgets (below called DwEmbedGtk). + + 2. This Dw widget, the platform, and the views are tightly bound, + beyond the methods, which the respective interfaces provide. The + Dw widget can simply refer to the platform by + (DwWidget::render_layout->platform, and the platform may refer + to the views, when DwPlatform::attach_view and + DwPlatform::detach_view are implemented. + +General Problems +---------------- +For most UI toolkits, there have to be multiple instances of widgets +in different views. Furthermore, there may be, on the same platform, +different classes for the same widgets. Consider this simple example: + + :DwRenderLayout ------------------- :DwPage + / \ | + ,----' `----. dw:DwEmbedGtk + / \ + view1:GtkDwViewport view2:GtkDwViewport + | | + gtk1:GtkButton gtk2:GtkButton + +This structure represents a page with one button in it, which is, in +the DwWidget tree, represented by the DwEmbedGtk "dw", and, in the +views "view1" and "view2", by the Gtk+ widgets "gtk1" and "gtk2", +respectively. The old approach, where the UI widget (in this case +GtkButton) is not directly applicable, since there must be multiple +instances of this UI widget. + +Here is a more complex example: + + :DwRenderLayout ------------------- :DwPage + / \ | + ,----' `----. dw:DwEmbedGtk + / \ + view1:GtkDwFooView view2:GtkDwBarView + | | + gtk1:GtkFooButton gtk2:GtkBarButton + +In this case, the different views GtkDwFooView and GtkDwBarView deal +with different classes for buttons, GtkFooButton and GtkBarButton. + +Simple Approach +--------------- +Within dillo, the following limitations are reasonable: + + 1. There is only one view instance, which actually needs UI widgets + (GtkDwViewport for Gtk+). There may be multiple views, but these + additional views do not need instances of widgets, e.g. in the + preview, a UI widget is simply shown as a grey box. + + 2. There is only one type of UI widget, i.e. normal Gtk+ widgets. + +Because of these limitations, the implementation of UI widgets is +currently quite simple in dillo. As before, the widget DwEmbedGtk +refers to an instance of a concrete Gtk+ widget. This Gtk+ widget is +told to the platform, of which DwEmbedGtk knows, that it is an +instance of GtkDwPlatform. GtkDwPlatform will add this Gtk+ widget to +the single view, which needs it, and DwEmbedGtk will be responsible of +allocating etc. of the Gtk+ widget. + +Advanced Approach +----------------- +This is a short overview of an approach, which will overcome the +limitations of the simple approach, but with the costs of a greater +complexity. It will be detailed, in the case, that it is implemented. + + +------------+ factory +---------------------+ + | DwEmbedGtk | ---------> | DwGtkWidgetFactory | + +------------+ 1 1 +---------------------+ + | create_foo_widget() | + | create_bar_widget() | + +---------------------+ + . . . + /_\ /_\ /_\ + | | | + - - - - - - - - - - - - - - - - - . + | | + +---------------------+ +---------------------+ | + | DwGtkButtonFactory | | DwGtkListFactoty | (other ...) + +---------------------+ +---------------------+ + | create_foo_widget() | | create_foo_widget() | + | create_bar_widget() | | create_bar_widget() | + +---------------------+ | add_list_item(...) | + +---------------------+ + +DwEmbedGtk refers to a factory, which creates the UI widgets for each +view. For each general widget type (e.g. button, list, ...), there is +an implementation of this interface. + +This interface DwGtkWidgetFactory contains different method for +different views. E.g., when a button is shown, and so DwEmbedGtk +refers to a DwGtkButtonFactoty, GtkDwFooView may need a GtkFooButton, +which is created by create_foo_widget(), while GtkDwBarView may call +create_bar_widget(), to get a GtkBarButton. (This design still makes +it hard to add new views, which then need, say, GtkBazButtons.) + +The concrete factories may contain additional methods, in the diagram +above, DwGtkListFactory must provide a function to add items to the +lists. This method will add a item to all list widget, which were +created by this factory. + + +More Ideas for View Implementations +=================================== + +Preview Window +-------------- +The status bar gets a new button, on which the user may click to get a +small image of the small page, in which he can select the viewport +position by dragging a rubber-band rectangle. The actual scaling +factor will probably depend on the aspect ratio of the page. + +(This has already been implemented for Gtk+, as GtkDwPreview and +GtkDwPreviewButton.) + +Graphical Plugin +---------------- +See section "An Example with Nested Views" for explanations. + +====================================================================== + +Alternative design for handling UI widgets: + +- There is a platform *independant* widget, the platform dependencies + are within the factories. + + DwEmbedUI::draw + -> DwRenderView::draw_ui_widget(..., factory) + +Implementation of "draw_ui_widget" in a preview view: + -> draw some gray boxes etc. + +Implementation of "draw_ui_widget" in a normal "bar" view (for platform "foo"): + -> determine that is is a factory for a "baz" widget? + -> (BarFactory*)factory->get_foo_bar_widget(this) + +This method either returns an already created widget, or creates one +with the current state of the factory. "This" is taken as a key. + +---- + + | general | widget specific +----------------------+------------------+------------------------ + platform independant | nothing? | e.g. adding list items +----------------------+------------------+------------------------ + platform dependant | e.g. creating a | ??? + | widget, or | + | informations | + | about drawing in | + | a preview | + +====================================================================== + +Abstraction of DwStyle +---------------------- +structures needed: diff --git a/doc/DwStyle.txt b/doc/DwStyle.txt new file mode 100644 index 00000000..9a1ec4e5 --- /dev/null +++ b/doc/DwStyle.txt @@ -0,0 +1,310 @@ +Apr 2001, S.Geerken@ping.de +Last update: Dec 2004 + +======= +DwStyle +======= + +Styles of Dillo Widgets + + +Note +==== + +DwStyle has derived from DwPageAttr, and its current structure is very +similar to it. In the future, there will be some changes and extensions. +Namely: + + - image maps will be handled differently (done), + - margins, borders, paddings (done), + - background colors/images, and + - cursors and tooltips will perhaps move into DwStyle. + +Furthermore, widgets will probably refer to different styles for +different states. + + +Overview +======== + +DwStyle provides some resources and attributes for drawing widgets, as +well as for parts of a widget (e.g., DwPage uses DwStyle's for its +words). Creating a style is done by filling a DwStyle with the +attributes (except the ref_count), and calling Dw_style_new: + + DwStyle style_attrs, *style; + + style_attrs.foo = bar; + // etc. + style = a_Dw_style_new (&style_attrs, random_window); + // do something with style + +After this, the attributes of style should not be changed anymore, +since styles are often shared between different widgets etc. (see +below). Most times, you simply copy the attributes of another style +and modify them: + + style_attrs = *another_style; + style_attrs.foo = bar; + style = a_Dw_style_new (&style_attrs, random_window); + +The font structure can be created by Dw_style_font_new, in a similar +way (the GdkFont in font_attrs will be ignored), and colors by +Dw_style_color_new, passing 0xrrggbb as an argument. Note that fonts +and colors are only intended to be used in conjunction with DwStyle. + + +Lengths and Percentages +======================= + +DwStyleLength is a simple data type for lengths and percentages: + + - A length refers to an absolute measurement. It is used to + represent the HTML type %Pixels; and the CSS type <length>. + + For CSS lenghts, there are two units: (i) pixels and absolute + units, which have to be converted to pixels (a pixel is, unlike + in the CSS specification, treated as absolute unit), and (ii) the + relative units "em" and "ex" (see below). + + - A percentage refers to a value relative to another value. It is + used for the HTML type %Length; (except %Pixels;), and the CSS + type <percentage>. + + - A relative length can be used in lists of HTML MultiLengths. + +Since many values in CSS may be either lengths or percentages, a +single type is very useful. + +Useful macros and functions +--------------------------- +Macros for creating lengths: + + DW_STYLE_CREATE_LENGTH (n) Returns a length of n pixels. + + DW_STYLE_CREATE_EX_LENGTH (n) Returns a length of n times the + 'x-height' + + DW_STYLE_CREATE_EM_LENGTH (n) Returns a length of n times the + 'font-size' + + DW_STYLE_CREATE_PERCENTAGE (n) Returns a percentage, n is relative + to 1, not to 100. + + DW_STYLE_CREATE_RELATIVE (n) Returns a relative length. + + DW_STYLE_UNDEF_LENGTH Used to indicate unspecified sizes, + errors, and the end of a list of + lengths. + +Furthermore, there are some functions in html.c: + + DwStyleLength Html_parse_length (gchar *attr); + + Returns a length or a percentage, or DW_STYLE_UNDEF_LENGTH in + case of an error. + + DwStyleLength* Html_parse_multi_length (gchar *attr); + + Returns a vector of lengths/percentages. The caller has to free + the result when it is not longer used. + +Macros for examining lengths: + + DW_STYLE_IS_LENGTH (l) Returns TRUE if l is a length. + + DW_STYLE_IS_PERCENTAGE (l) Returns TRUE if l is a percentage. + + DW_STYLE_IS_RELATIVE (l) Returns TRUE if l is a relative + length. + + DW_STYLE_GET_LENGTH (l, f) Returns the value of a length in + pixels, as an integer. f is the + font, this is used if l is based on + font sizes. + + DW_STYLE_GET_PERCENTAGE (l) Returns the value of a percentage, + relative to 1, as a float. + + DW_STYLE_GET_RELATIVE (l) Returns the value of a relative + length, as a float. + + +Representation +-------------- +Notes: + + 1. This is not part of the interface and may change! Use the + macros described above. + 2. Negative numbers may not work yet. + +DwStyleLength is represented by an integer (n is the number of bits of +an integer): + + - Undefined lengths are represented by 0. + + - Lenghts in pixel: + + +---+ - - - +---+---+---+---+ + | int value | 0 | 1 | + +---+ - - - +---+---+---+---+ + n-1 3 2 1 0 + + - Lengths in in x-height: + + +---+ - - - +---+---+---+---+ + | real value | 0 | 1 | 1 | + +---+ - - - +---+---+---+---+ + n-1 3 2 1 0 + + - Lengths in in font-size: + + +---+ - - - +---+---+---+---+ + | real value | 1 | 1 | 1 | + +---+ - - - +---+---+---+---+ + n-1 3 2 1 0 + + - Percentages: + + +---+ - - - +---+---+---+---+ + | real value | 0 | 1 | 0 | + +---+ - - - +---+---+---+---+ + n-1 3 2 1 0 + + - Relative lengths: + + +---+ - - - +---+---+---+---+ + | real value | 1 | 1 | 0 | + +---+ - - - +---+---+---+---+ + n-1 3 2 1 0 + +A "real value" is a fixed point number consisting of (m is the number +of bits of the value, not the whole integer): + + +---+ - - - +---+---+ - - - +---+ + | integer part | rest | + +---+ - - - +---+---+ - - - +---+ + m 16 15 0 + +For *internal* use, there are two converting macros, +DW_STYLE_REAL_TO_FLOAT and DW_STYLE_FLOAT_TO_REAL. + + +DwStyle Boxes +============= + +The CSS Box Model +----------------- +For borders, margins etc., DwStyle uses the box model defined by +CSS2. DwStyle contains some members defining these attributes. A +widget must use these values for any calculation of sizes. There are +some helper functions (see dw_style.h). A DwStyle box looks quite +similar to a CSS box: + + + ,-- margin.left + | ,-- border.left + | | ,-- padding.left + |---+---+---| + +---------------------------------------+ --- + | | | margin.top + | +-------------------------------+ | -+- + | | Border | | | border.top + | | +-----------------------+ | | -+- + | | | Padding | | | | padding.top + new widget | | | +---------------+ | | | --- + allocation -->| | | | | | | | + | | | | Content | | | | + former widget ------------>| | | | | + allocation | | | +---------------+ | | | --- + | | | | | | | margin.bottom + | | +-----------------------+ | | -+- + | | | | | border.bottom + | +-------------------------------+ | -+- + | | | padding.bottom + +---------------------------------------+ --- + |---+---+---| + padding.right --' | | + border.right --' | + margin.right --' + +Background colors +----------------- +The background color is stored in style->background_color, which be +NULL (the background color of the parent widget is shining through). + +For toplevel widgets, this color is set as the background color of the +viewport, for other widgets, a filled rectangle is drawn, covering the +content and padding. (This is compliant with CSS2, the background +color of the toplevel element covers the whole canvas.) + +Drawing +------- +There is a new function Dw_widget_draw_widget_box, which should be +called at the beginning of Dw_foo_draw. For parts referring to styles +(e.g., words in a page), Dw_widget_draw_box should be used. + + +Notes on Memory Management +========================== + +Memory management is done by reference counting, a_Dw_style_new +returns a pointer to DwStyle with an increased reference counter, so +you should care about calling Dw_style_unref if it is not used +anymore. You do *not* need to care about the reference counters of +fonts and styles. + +In detail: + + - a_Dw_style_ref is called in + + * a_Dw_widget_set_style, to assign a style to a widget, + + * a_Dw_page_add_text, a_Dw_page_add_widget, + a_Dw_page_add_anchor, to assign a style to a word, + + * and Html_push_tag (often the reference counter is again + decreased shortly after this). + + - a_Dw_unref_style is called in: + + * Dw_page_destroy, Dw_widget_destroy, Html_cleanup_tag, + Html_pop_tag, Html_close, + + * a_Dw_widget_set_style, Html_set_top_font (and several + Html_tag_open_... functions), these functions overwrite an + existing style. + + +HTML Stack +========== + +(This is not DwStyle specific, but may be useful if you are working on +the HTML parser.) + +The topmost element of the HTML stack contains a (reference to a) +style which will be used to add the text to the current page. If you +use a style only for a short while (see Html_tag_open_frame for an +example), you may use it this way: + + style_attrs = *html->stack[html->stack_top].style; + style_attrs.foo = bar; + style = a_Dw_style_new (&style_attrs, random_window); + +Do not forget to unref it afterwards. A good choice for random_window +is html->bw->main_window->window. + +In many cases, you want to set the style for the content of an element +(e.g., <A>). Then you must store it in the stack: + + DwStyle style_attrs, *old_style; + + old_style = html->stack[html->stack_top].style; + style_attrs = *old_style; + style_attrs.foo = bar; + html->stack[html->stack_top].style = + a_Dw_style_new (&style_attrs, random_window); + a_Dw_style_unref (old_style); + +The macro HTML_SET_TOP_ATTR can be used for single attributes, for +changing more attributes, this code should be copied for efficiency. diff --git a/doc/DwTable.txt b/doc/DwTable.txt new file mode 100644 index 00000000..07c06180 --- /dev/null +++ b/doc/DwTable.txt @@ -0,0 +1,205 @@ +May 2001, S.Geerken@ping.de +Last update: Dec 2004 + +======= +DwTable +======= + +A container widget for rendering tables. + + +The DwTable Widget +================== + +DwTable is a container widget for rendering tables. It aligns other +DwWidgets (normally DwPage), according to the following rules: + + 1. All columns have have the same width W, except: + + - W is less than the minimal column width, or + - W is greater than the maximal column width. + + Furthermore, W is + + - less than all minimal widths of columns not having W as + width, and + - greater than all maximal widths of columns not having W as + width. + + 2. The table tries to use exactly the whole available width, except + if it is not possible, because the it is less/greater than the + minimal/maximal table width. + +This is simple to implement for columns with COLSPAN == 1, using +a_Dw_get_extremes for getting the minimal and maximal widths. For +arbitrary COLSPAN values, an approach described in "Subtables" is +used to get optimal results (as described above) in most cases, while +the rendering remains fast. + + +Subtables +========= + +A table is divided into subtables, which do not (in most cases) share +spanning cells, until single columns are left. Cells spanning the +whole width are removed before dividing further. Example: + + +---+-------+---+ + | A | B | C | + +---+-------+---+ + | D | E | + +---+-------+---+ + | F | G | H | + +---+-------+---+ + ' ' ` ` + ' ' ` ` + +---+-------+ +---+ + | A | B | | C | + +---+-------+ +---+ + removed --> | D | | E | + +---+-------+ +---+ + | F | G | | H | + +---+-------+ +---+ + ' ' ` ` final + ' ' ` ` + +---+ +-------+ + | A | | B | <-. + +---+ +-------+ >- removed + | F | | G | <-' + +---+ +-------+ + final ' ' ` ` + ' ' ` ` + [empty] [empty] + final final + +There is a structure, DwTableSub, for holding all the information. It +is rebuilt when new cells are added. Do not confuse this with nested +tables, these are represented by the Dw widget hierarchy. + +If table cells overlap horizontally, they are (virtually) divided. The +minimal and maximal widths are apportioned to the other columns +(resulting in a non optimal layout): + + +-------+---+---+ + | A | B | C | + +---+---+---+---+ + | D | E | + +---+-----------+ + ' ' ` ` + ' ' ` ` + +-------+ +---+---+ + | A | | B | C | + +---+---+ +---+---+ + | D |1/3| | 2/3 E | + | | E | | | + +---+---+ +-------+ + +Example for a non-optimal case +------------------------------ +The HTML document fragment + + <table> + <tr> + <td colspan="2">Text + <td>LongText + <tr> + <td>Text + <td colspan="2">LongText + </table> + +will result in: + + | 0 | 1 | 2 | + + +------------+----------+ + | Text | LongText | + +------+-----+----------+ + | Text | LongText | + +------+----------------+ + +The width of column 1 is determined by the half of the minimal width +of the LongText. An optimal rendering would be something like: + + ,- 1 + | 0 || 2 | + + +-------+----------+ + | Text | LongText | + +------++----------+ + | Text | LongText | + +------+-----------+ + + +Algorithms +========== + +Calculating extremes +-------------------- +The extremes of all subtables are calculated by +Dw_table_sub_get_extremes and stored in DwTableSub: + + minimal/maximal width (sub table) = + - for single column: maximum of all minimal/maximal widths + - otherwise: maximum of + 1. all minimal/maximal widths of cells spanning + the whole width, and + 2. the sum of the minimal/maximal widths of the + sub-subtables + + In step 1, the width argument is used to adjust the maximum + and minimum width of the whole subtable and mark it as fixed. + +todo: describe percentages. + +Calculating column widths +------------------------- +The calculation is based on a fixed width, which is, at the top, the +width set by a_Dw_widget_set_width. This is corrected by the minimal and +maximal width of the whole table, and, if given, the width attribute +of the table. At each level, the available width is always between the +minimal and the maximal width of the subtable. + +For single columns, the width is the passed fixed width. Otherwise: + + 1. Calculate relative widths, they effect the minimal and maximal + widths. (Temporally, not permanently!) + + 2. The sum of these corrected minima may be greater as the fixed + width of the subtable. In this case, decrease them again to + match exactly the fixed width, then use them as sub-subtable + fixed widths and finish. Otherwise, continue: + + 3. If the extremes of the spanning widths of the subtable are + greater than the sum of sub-subtables extremes, adjust the + extremes of sub-subtables which are not fixed, i.e., where no + width argument (either percentage or fixed) freezes the width. + + 4. Use an iteration on the subtables, to determine the column + widths, see Dw_table_sub_calc_col_widths for details. + + 5. After this, apply this recursively on all subtables and pass the + subtable width as fixed width. + + +Borders, Paddings, Spacing +========================== + +Currently, DwTable supports only the separated borders model (see CSS +specification). Borders, paddings, spacing is done by creating DwStyle +structures with values equivalent to following CSS: + + TABLE { + border: outset <table-border>; + border-collapse: separate; + border-spacing: <table-cellspacing> + background-color: <table-bgcolor> + } + + TD TH { + border: inset <table-border>; + padding: <table-cellspacing> + background-color: <td/th-bgcolor> + } + +Here, <foo-bar> refers to the attribute bar of the tag foo. See +Html_open_table and Html_open_table_cell for more details. diff --git a/doc/DwWidget.txt b/doc/DwWidget.txt new file mode 100644 index 00000000..fed0a3be --- /dev/null +++ b/doc/DwWidget.txt @@ -0,0 +1,339 @@ +Jan 2001, S.Geerken@ping.de +Last update: Dec 2004 + +======== +DwWidget +======== + +The base object for all Dw widgets. + + +Structures +========== + +DwRectangle +----------- +A replacement for GdkRectangle, the only difference is the use of 32 +instead of 16 bit integers. + + +DwAllocation, DwRequisition +--------------------------- +Similar to GtkAllocation and GtkRequisition. Instead of a height, you +have two members, ascent and descent. + + +DwExtremes +---------- +A structure containing the minimal and maximal width of a widget. See +get_extremes below for details. + + +DwWidget +-------- +Some members you may use: + + parent The parent widget. NULL for toplevel widgets. + + flags See below. + + style The style attached to the widget, this must + always be defined, but is not created + automatically. Instead, this has to be done + immediately after creating the widget + (e.g., in a_Web_dispatch_by_type). This style + contains attributes and resources for general + drawing. See DwStyle.txt for details. + +These members should only be used within the "core" (GtkDw*, DwWidget +and DwEmbedGtk): + + viewport The viewport containing the widget. Defined + for all widgets. + + allocation, + requisition, + extremes, + user_requisition These are used to optimize several wrappers, + see below. + + anchors_table See notes on achors. + +Flags +----- +Flags can be set and unset with DW_WIDGET_SET_FLAGS and +DW_WIDGET_UNSET_FLAGS. For reading flags use the macros DW_WIDGET_... + + DW_NEEDS_RESIZE + DW_EXTREMES_CHANGED Denotes that the widget must be resized. Used + only internally. See Dw.txt and the source + for more details. + + DW_NEEDS_ALLOCATE Set to overide the optimation in + a_Dw_widget_size_allocate. Used only + internally. + + DW_REALIZED Set when the widget is realized. Should be + used to prevent crashes in certain + situations. + +Following flags describe characteristics of widgets and are typically +set in the init function: + + DW_USES_HINTS A widget with this flag set has a complex way + to deal with sizes, and should + + - implement the functions set_width, + set_ascent, set_descent, and + get_extremes, and + - deal completely with width and height + in widget->style. + + Examples are DwPage and DwTable. Other + widgets, like DwImage and DwHRuler, are much + simpler and don't have to set this flag. For + these widgets, much of the size calculation + is done by the parent widget. + + This flag is unset by default. + + DW_HAS_CONTENT If this flag is set, more space is reserved + for the widget in some circumstances. E.g., + if an image has a width of 100%, it makes + sense to use more space within a table cell, + as compared to a horizontal ruler, which does + not "have a content". + + This flag is set by default. + + +Signal Prototypes +================= + + - void size_request (DwWidget *widget, + DwRequisition *requisition); + + Similar to Gtk. + + void get_extremes (DwWidget *widget, + DwExtremes *extremes); + + Return the maximal and minimal width of the widget, equivalent + to the requisition width after calling set_width with zero and + infinitive, respectively. This is important for fast table + rendering. Simple widgets do not have to implement this; the + default is the requisition width for both values. + + - void size_allocate (DwWidget *widget, + DwAllocation *allocation); + + Similar in Gtk. Note: allocation has world coordinates. + + - void set_width (DwWidget *widget, + guint32 width); + + - void set_height (DwWidget *widget, + guint32 height); + + These are hints by the caller, which *may* influence the size + returned by size_request. The implementation should call + Dw_widget_queue_resize if necessary. In most cases, these + signals do not have to be implemented. Currently, only the + DwPage widget uses this to determine the width for rewrapping + text (note that the resulting width returned by + Dw_page_size_request may be _bigger_) and relative sizes of the + children. + + - void draw (DwWidget *widget, + DwRectangle *area, + GdkEventExpose *event); + + Draw the widget's content in the specified area. It may either + be caused by an expose event, or by an internal drawing request + (e.g., followed by resizing of widgets). In the first case, you + get the *original* expose event as third argument. In the + latter, event is NULL. The area argument has widget + coordinates. A DwContainer is responsible for drawing its + children. + + (Since DwWidget's are always windowless, there was no need for + two signals, "draw" and "expose_event".) + + - void realize (DwWidget *widget); + + Create all necessary X resources. Called when either the + viewport (top-level widgets) or, respectively, the parent Dw + widget is realized, or an widget is added to an already + realized Dw widget/viewport. + + - void unrealize (DwWidget *widget); + + Remove created X resources. + + - gint button_press_event (DwWidget *widget, + guint32 x, + guint32 y, + GdkEventButton *event); + + This signal is emitted when the user presses a mouse button in + a DwWidget. x and y are the coordinates relative to the origin + of the widget, event is the *original* event, which may, e.g., + be used to determine the number of the pressed button, the state + of the shift keys, etc. The implementation of this signal + should return TRUE, if the event has been processed, otherwise + FALSE. + + A DwContainer is *not* responsible for delivering button press + events to its children. Instead, Dw first emits the + button_press_event signal for the most inner widgets and + continues this for the parents, until TRUE is returned. + + - gint button_release_event (DwWidget *widget, + guint32 x, + guint32 y, + GdkEventButton *event); + + Compare button_press_event. + + - gint motion_notify_event (DwWidget *widget, + guint32 x, + guint32 y, + GdkEventMotion *event); + + Compare button_press_event. event may be NULL when the call was + caused by something different than a "real" motion notify event. + E.g., either when widgets are moved (not yet implemented), or the + viewport. + + - gint enter_notify_event (DwWidget *widget, + DwWidget *last_widget, + GdkEventMotion *event); + + These "events" are simulated based on motion nofify events. + event is the *original* event (may also be NULL in some cases). + last_widget is the widget in which the pointer was in before. + + - gint leave_notify_event (DwWidget *widget, + DwWidget *next_widget, + GdkEventMotion *event); + + Compare enter_notify_event. next_widget is the widget the + pointer is now in. + + +Useful Internal Functions and Macros +==================================== + + - gint Dw_widget_intersect (DwWidget *widget, + DwRectangle *area, + DwRectangle *intersection); + + Calculates the intersection of widget->allocation and area, + returned in intersection (in widget coordinates!). Typically + used by containers when drawing their children. Returns whether + intersection is not empty. + + - gint32 Dw_widget_x_viewport_to_world (DwWidget *widget, + gint16 viewport_x); + + - gint32 Dw_widget_y_viewport_to_world (DwWidget *widget, + gint16 viewport_y); + + - gint16 Dw_widget_x_world_to_viewport (DwWidget *widget, + gint32 world_x); + + - gint16 Dw_widget_y_world_to_viewport (DwWidget *widget, + gint32 world_y); + + These functions convert between world and viewport coordinates. + + - void Dw_widget_queue_draw (DwWidget *widget); + + - void Dw_widget_queue_draw_area (DwWidget *widget, + gint32 x, + gint32 y, + guint32 width, + guint32 height); + + - void Dw_widget_queue_clear (DwWidget *widget); + + - void Dw_widget_queue_clear_area (DwWidget *widget, + gint32 x, + gint32 y, + guint32 width, + guint32 height); + + Equivalents to the Gtk+ functions. They (currently) result in a + call of gtk_widget_xxx_area with the viewport as first + argument. x and y are widget coordinates. + + - void Dw_widget_queue_resize (DwWidget *widget, + gint ref, + gboolean extremes_changed); + + Similar to gtk_widget_queue_resize. Call this function when the + widget has changed its size. The next call to + Dw_xxx_size_request should then return the new size. + + See Dw.txt for explanation on how to use the ref argument, + extremes_changed specifies whether the extremes have changed + (the latter is often not the case for an implementations of + set_{width|ascent|descent}). + + - void Dw_widget_set_anchor (DwWidget *widget, + gchar *name, + int pos); + + Add an anchor to a widget. The name will not be copied, it has + to be stored elsewhere (DwPage e.g. stores it in the DwPageWord + structure). + + - void a_Dw_widget_set_cursor (DwWidget *widget, + GdkCursor *cursor) + + Set the cursor for a DwWidget. cursor has to be stored + elsewhere, it is not copied (and not destroyed). If cursor is + NULL, the cursor of the parent widget is used. + + (This will probably be changed in the future and replaced by a + common style mechanism.) + + - DW_WIDGET_WINDOW (widget) + + Returns the window a widget should draw into. + + +External Functions +================== + + - void a_Dw_widget_set_usize (DwWidget *widget, + guint32 width, + guint32 ascent, + guint32 descent); + + Override the "desired" size of a widget. Further calls of + a_Dw_widget_request_size will return these values, except those + specified as -1. A possible use shows Html_add_widget in + html.c. + + (This will probably be removed. Instead DwStyle should be used.) + + + - void a_Dw_widget_set_bg_color (DwWidget *widget, + gint32 color); + + Set the background color of a widget. This works currently only + for the top-level widget. In this case, the background color of + the GtkDwViewport is changed. In future, background colors for + all widgets will be needed, e.g., for table cells (will be + DwPage's), this will (probably) be based on filled rectangles. + + - void a_Dw_widget_scroll_to (DwWidget *widget, + int pos) + + Scroll viewport to pos (vertical widget coordinate). + +There are furthermore wrappers for the signals, in some cases they +are optimized and/or provide further functionality. In (almost) no +case should you emit the signals directly. See dw_widget.c for more +details. diff --git a/doc/HtmlParser.txt b/doc/HtmlParser.txt new file mode 100644 index 00000000..ec64164d --- /dev/null +++ b/doc/HtmlParser.txt @@ -0,0 +1,116 @@ + October 2001, --Jcid + Last update: Dec 2004 + + --------------- + THE HTML PARSER + --------------- + + + Dillo's parser is more than just a HTML parser, it does XHTML +and plain text also. It has parsing 'modes' that define its +behaviour while working: + + typedef enum { + DILLO_HTML_PARSE_MODE_INIT, + DILLO_HTML_PARSE_MODE_STASH, + DILLO_HTML_PARSE_MODE_STASH_AND_BODY, + DILLO_HTML_PARSE_MODE_BODY, + DILLO_HTML_PARSE_MODE_VERBATIM, + DILLO_HTML_PARSE_MODE_PRE + } DilloHtmlParseMode; + + + The parser works upon a token-grained basis, i.e., the data +stream is parsed into tokens and the parser is fed with them. The +process is simple: whenever the cache has new data, it gets +passed to Html_write, which groups data into tokens and calls the +appropriate functions for the token type (TAG, SPACE or WORD). + + Note: when in DILLO_HTML_PARSE_MODE_VERBATIM, the parser +doesn't try to split the data stream into tokens anymore, it +simply collects until the closing tag. + +------ +TOKENS +------ + + * A chunk of WHITE SPACE --> Html_process_space + + + * TAG --> Html_process_tag + + The tag-start is defined by two adjacent characters: + + first : '<' + second: ALPHA | '/' | '!' | '?' + + Note: comments are discarded ( <!-- ... --> ) + + + The tag's end is not as easy to find, nor to deal with!: + + 1) The HTML 4.01 sec. 3.2.2 states that "Attribute/value + pairs appear before the final '>' of an element's start tag", + but it doesn't define how to discriminate the "final" '>'. + + 2) '<' and '>' should be escaped as '<' and '>' inside + attribute values. + + 3) The XML SPEC for XHTML states: + AttrValue ::== '"' ([^<&"] | Reference)* '"' | + "'" ([^<&'] | Reference)* "'" + + Current parser honors the XML SPEC. + + As it's a common mistake for human authors to mistype or + forget one of the quote marks of an attribute value; the + parser solves the problem with a look-ahead technique + (otherwise the parser could skip significative amounts of + well written HTML). + + + + * WORD --> Html_process_word + + A word is anything that doesn't start with SPACE, and that's + outside of a tag, up to the first SPACE or tag start. + + SPACE = ' ' | \n | \r | \t | \f | \v + + +----------------- +THE PARSING STACK +----------------- + + The parsing state of the document is kept in a stack: + + struct _DilloHtml { + [...] + DilloHtmlState *stack; + gint stack_top; /* Index to the top of the stack [0 based] */ + gint stack_max; + [...] + }; + + struct _DilloHtmlState { + char *tag; + DwStyle *style, *table_cell_style; + DilloHtmlParseMode parse_mode; + DilloHtmlTableMode table_mode; + gint list_level; + gint list_number; + DwWidget *page, *table; + gint32 current_bg_color; + }; + + + Basically, when a TAG is processed, a new state is pushed into +the 'stack' and its 'style' is set to reflect the desired +appearance (details in DwStyle.txt). + + That way, when a word is processed later (added to the Dw), all +the information is within the top state. + + Closing TAGs just pop the stack. + + diff --git a/doc/IO.txt b/doc/IO.txt new file mode 100644 index 00000000..fb691223 --- /dev/null +++ b/doc/IO.txt @@ -0,0 +1,468 @@ + +This is the updated base of a paper I wrote with Horst. +It provides a good introduction to Dillo's internals. +(Highly recommended if you plan to patch or develop in Dillo) + +It may not be exactly accurate (it's quite old), but it explains +the theory behind in some detail, so it's more than recommended +reading material. +--Jcid + + +----------------------------------------------------- +Parallel network programming of the Dillo web browser +----------------------------------------------------- + + Jorge Arellano-Cid <jcid@dillo.org> + Horst H. von Brand <vonbrand@inf.utfsm.cl> + + +-------- +Abstract +-------- + + Network programs face several delay sources when sending or +retrieving data. This is particularly problematic in programs +which interact directly with the user, most notably web browsers. +We present a hybrid approach using threads communicated through +pipes and signal driven I/O, which allows a non-blocking main +thread and overlapping waiting times. + + +------------ +Introduction +------------ + + The Dillo project didn't start from scratch but mainly working +on the code base of gzilla (a light web browser written by Raph +Levien). As the project went by, the code of the whole source was +standardized, and the networking engine was replaced with a new, +faster design. Later, the parser was changed, the streamed +handling of data and its error control was put under the control +of the CCC (Concomitant Control Chain), and the old widget system +was replaced with a new one (Dw). The source code is currently +regarded as "very stable beta", and is available at +<http://www.dillo.org>. Dillo is a project licensed under the GNU +General Public License. + + This paper covers basic design aspects of the hybrid approach +that the Dillo web browser uses to solve several latency +problems. After introducing the main delay-sources, the main +points of the hybrid design will be addressed. + + +------------- +Delay sources +------------- + + Network programs face several delay-sources while sending or +retrieving data. In the particular case of a web browser, they +are found in: + + DNS querying: + The time required to solve a name. + + Initiating the TCP connection: + The three way handshake of the TCP protocol. + + Sending the query: + The time spent uploading queries to the remote server. + + Retrieving data: + The time spent expecting and receiving the query answer. + + Closing the TCP connection: + The four packet-sending closing sequence of the TCP protocol. + + In a WAN context, every single item of this list has an +associated delay that is non deterministic and often measured in +seconds. If we add several connections per browsed page (each one +requiring at least the 4 last steps), the total latency can be +considerable. + + +----------------------------------- +The traditional (blocking) approach +----------------------------------- + + The main problems with the blocking approach are: + + When issuing an operation that can't be completed + immediately, the process is put to sleep waiting for + completion, and the program doesn't do any other + processing in the meantime. + + When waiting for a specific socket operation to complete, + packets that belong to other connections may be arriving, + and have to wait for service. + + Web browsers handle many small transactions, + if waiting times are not overlapped + the latency perceived by the user can be very annoying. + + If the user interface is just put to sleep during network + operations, the program becomes unresponsive, confusing + and perhaps alarming the user. + + Not overlapping waiting times and processing makes + graphical rendering (which is arguably the central function + of a browser) unnecessarily slow. + + +--------------------- +Dillo's hybrid design +--------------------- + + Dillo uses threads and signal driven I/O extensively to +overlap waiting times and computation. Handling the user +interface in a thread that never blocks gives a good interactive +``feel.'' The use of GTK+, a sophisticated widget framework for +graphical user interfaces, helped very much to accomplish this +goal. All the interface, rendering and I/O engine was built upon +its facilities. + + The design is said to be ``hybrid'' because it uses threads +for DNS querying and reading local files, and signal driven I/O +for TCP connections. The threaded DNS scheme is potentially +concurrent (this depends on underlying hardware), while the I/O +handling (both local files and remote connections) is +definitively parallel. + + To simplify the structure of the browser, local files are +encapsulated into HTTP streams and presented to the rest of the +browser as such, in exactly the same way a remote connection is +handled. To create this illusion, a thread is launched. This +thread opens a pipe to the browser, it then synthesizes an +appropriate HTTP header, sends it together with the file to the +browser proper. In this way, all the browser sees is a handle, +the data on it can come from a remote connection or from a local +file. + + To handle a remote connection is more complex. In this case, +the browser asks the cache manager for the URL. The name in the +URL has to be resolved through the DNS engine, a socket TCP +connection must be established, the HTTP request has to be sent, +and finally the result retrieved. Each of the steps mentioned +could give rise to errors, which have to be handled and somehow +communicated to the rest of the program. For performance reasons, +it is critical that responses are cached locally, so the remote +connection doesn't directly hand over the data to the browser; +the response is passed to the cache manager which then relays it +to the rest of the browser. The DNS engine caches DNS responses, +and either answers them from the cache or by querying the DNS. +Querying is done in a separate thread, so that the rest of the +browser isn't blocked by long waits here. + + The activities mentioned do not happen strictly in the order +stated above. It is even possible that several URLs are being +handled at the same time, in order to overlap waiting and +downloading. The functions called directly from the user +interface have to return quickly to maintain interactive +response. Sometimes they return connection handlers that haven't +been completely set up yet. As stated, I/O is signal-driven, when +one of the descriptors is ready for data transfer (reading or +writing), it wakes up the I/O engine. + + Data transfer between threads inside the browser is handled by +pipes, shared memory is little used. This almost obviates the +need for explicit synchronization, which is one of the main areas +of complexity and bugs in concurrent programs. Dillo handles its +threads in a way that its developers can think of it as running +on a single thread of control. This is accomplished by making the +DNS engine call-backs happen within the main thread, and by +isolating file loading with pipes. + + Using threads in this way has three big advantages: + + The browser doesn't block when one of its child threads + blocks. In particular, the user interface is responsive + even while resolving a name or downloading a file. + + Developers don't need to deal with complex concurrent + concerns. Concurrency is hard to handle, and few developers + are adept at this. This gives access a much larger pool of + potential developers, something which can be critical + in an open-source development project. + + By making the code mostly sequential, debugging the code + with traditional tools like gdb is possible. Debugging + parallel programs is very hard, and appropriate tools are + hard to come by. + + Because of simplicity and portability concerns, DNS querying +is done in a separate thread. The standard C library doesn't +provide a function for making DNS queries that don't block. The +alternative is to implement a new, custom DNS querying function +that doesn't block. This is certainly a complex task, integrating +this mechanism into the thread structure of the program is much +simpler. + + Using a thread and a pipe to read a local file adds a +buffering step to the process (and a certain latency), but it has +a couple of significative advantages: + + By handling local files in the same way as remote + connections, a significant amount of code is reused. + + A preprocessing step of the file data can be added easily, + if needed. In fact, the file is encapsulated into an HTTP + data stream. + + +----------- +DNS queries +----------- + + Dillo handles DNS queries with threads, letting a child thread +wait until the DNS server answers the request. When the answer +arrives, a call-back function is called, and the program resumes +what it was doing at DNS-request time. The interesting thing is +that the call-back happens in the main thread, while the child +thread simply exits when done. This is implemented through a +server-channel design. + + +The server channel +------------------ + + There is one thread for each channel, and each channel can +have multiple clients. When the program requests an IP address, +the server first looks for a cached match; if it hits, the client +call-back is invoked immediately, but if not, the client is put +into a queue, a thread is spawned to query the DNS, and a GTK+ +idle client is set to poll the channel 5~times per second for +completion, and when it finally succeeds, every client of that +channel is serviced. + + This scheme allows all the further processing to continue on +the same thread it began: the main thread. + + +------------------------ +Handling TCP connections +------------------------ + + Establishing a TCP connection requires the well known +three-way handshake packet-sending sequence. Depending on network +traffic and several other issues, significant delay can occur at +this phase. + + Dillo handles the connection by a non blocking socket scheme. +Basically, a socket file descriptor of AF_INET type is requested +and set to non-blocking I/O. When the DNS server has resolved the +name, the socket connection process begins by calling connect(2); + {We use the Unix convention of identifying the manual section + where the concept is described, in this case + section 2 (system calls).} +which returns immediately with an EINPROGRESS error. + + After the connection reaches the EINPROGRESS ``state,'' the +socket waits in background until connection succeeds (or fails), +when that happens, a callback function is awaked to perform the +following steps: set the I/O engine to send the query and expect +its answer (both in background). + + The advantage of this scheme is that every required step is +quickly done without blocking the browser. Finally, the socket +will generate a signal whenever I/O is possible. + + +---------------- +Handling queries +---------------- + + In the case of a HTTP URL, queries typically translate into a +short transmission (the HTTP query) and a lengthy retrieval +process. Queries are not always short though, specially when +requesting forms (all the form data is attached within the +query), and also when requesting CGI programs. + + Regardless of query length, query sending is handled in +background. The thread that was initiated at TCP connecting time +has all the transmission framework already set up; at this point, +packet sending is just a matter of waiting for the +write signal (G_IO_OUT) to come and then sending the data. When +the socket gets ready for transmission, the data is sent using +IO_write. + + +-------------- +Receiving data +-------------- + + Although conceptually similar to sending queries, retrieving +data is very different as the data received can easily exceed the +size of the query by many orders of magnitude (for example when +downloading images or files). This is one of the main sources of +latency, the retrieval can take several seconds or even minutes +when downloading large files. + + The data retrieving process for a single file, that began by +setting up the expecting framework at TCP connecting time, simply +waits for the read signal (G_IO_IN). When it happens, the +low-level I/O engine gets called, the data is read into +pre-allocated buffers and the appropriate call-backs are +performed. Technically, whenever a G_IO_IN event is generated, +data is received from the socket file descriptor, by using the +IO_read function. This iterative process finishes upon EOF (or on +an error condition). + + +---------------------- +Closing the connection +---------------------- + + Closing a TCP connection requires four data segments, not an +impressive amount but twice the round trip time, which can be +substantial. When data retrieval finishes, socket closing is +triggered. There's nothing but a IO_close_fd call on the socket's +file descriptor. This process was originally designed to split +the four segment close into two partial closes, one when query +sending is done and the other when all data is in. This scheme is +not currently used because the write close also stops the reading +part. + + +The low-level I/O engine +------------------------ + + Dillo I/O is carried out in the background. This is achieved +by using low level file descriptors and signals. Anytime a file +descriptor shows activity, a signal is raised and the signal +handler takes care of the I/O. + + The low-level I/O engine ("I/O engine" from here on) was +designed as an internal abstraction layer for background file +descriptor activity. It is intended to be used by the cache +module only; higher level routines should ask the cache for its +URLs. Every operation that is meant to be carried out in +background should be handled by the I/O engine. In the case of +TCP sockets, they are created and submitted to the I/O engine for +any further processing. + + The submitting process (client) must fill a request structure +and let the I/O engine handle the file descriptor activity, until +it receives a call-back for finally processing the data. This is +better understood by examining the request structure: + + typedef struct { + gint Key; /* Primary Key (for klist) */ + gint Op; /* IORead | IOWrite | IOWrites */ + gint FD; /* Current File Descriptor */ + gint Flags; /* Flag array */ + glong Status; /* Number of bytes read, or -errno code */ + + void *Buf; /* Buffer place */ + size_t BufSize; /* Buffer length */ + void *BufStart; /* PRIVATE: only used inside IO.c! */ + + void *ExtData; /* External data reference (not used by IO.c) */ + void *Info; /* CCC Info structure for this IO */ + GIOChannel *GioCh; /* IO channel */ + guint watch_id; /* glib's event source id */ + } IOData_t; + + To request an I/O operation, this structure must be filled and +passed to the I/O engine. + + 'Op' and 'Buf' and 'BufSize' MUST be provided. + + 'ExtData' MAY be provided. + + 'Status', 'FD' and 'GioCh' are set by I/O engine internal +routines. + + When there is new data in the file descriptor, 'IO_callback' +gets called (by glib). Only after the I/O engine finishes +processing the data are the upper layers notified. + + +The I/O engine transfer buffer +------------------------------ + + The 'Buf' and 'BufSize' fields of the request structure +provide the transfer buffer for each operation. This buffer must +be set by the client (to increase performance by avoiding copying +data). + + On reads, the client specifies the amount and where to place +the retrieved data; on writes, it specifies the amount and source +of the data segment that is to be sent. Although this scheme +increases complexity, it has proven very fast and powerful. For +instance, when the size of a document is known in advance, a +buffer for all the data can be allocated at once, eliminating the +need for multiple memory reallocations. Even more, if the size is +known and the data transfer is taking the form of multiple small +chunks of data, the client only needs to update 'Buf' and +BufSize' to point to the next byte in its large preallocated +reception buffer (by adding the chunk size to 'Buf'). On the +other hand, if the size of the transfer isn't known in advance, +the reception buffer can remain untouched until the connection +closes, but the client must then accomplish the usual buffer +copying and reallocation. + + The I/O engine also lets the client specify a full length +transfer buffer when sending data. It doesn't matter (from the +client's point of view) if the data fits in a single packet or +not, it's the I/O engine's job to divide it into smaller chunks +if needed and to perform the operation accordingly. + + +------------------------------------------ +Handling multiple simultaneous connections +------------------------------------------ + + The previous sections describe the internal work for a single +connection, the I/O engine handles several of them in parallel. +This is the normal downloading behavior of a web page. Normally, +after retrieving the main document (HTML code), several +references to other files (typically images) and sometimes even +to other sites (mostly advertising today) are found inside the +page. In order to parse and complete the page rendering, those +other documents must be fetched and displayed, so it is not +uncommon to have multiple downloading connections (every one +requiring the whole fetching process) happening at the same time. + + Even though socket activity can reach a hectic pace, the +browser never blocks. Note also that the I/O engine is the one +that directs the execution flow of the program by triggering a +call-back chain whenever a file descriptor operation succeeds or +fails. + + A key point for this multiple call-back chained I/O engine is +that every single function in the chain must be guaranteed to +return quickly. Otherwise, the whole system blocks until it +returns. + + +----------- +Conclusions +----------- + + Dillo is currently in very stable beta state. It already shows +impressive performance, and its interactive ``feel'' is much +better than that of other web browsers. + + The modular structure of Dillo, and its reliance on GTK1 allow +it to be very small. Not every feature of HTML-4.01 has been +implemented yet, but no significant problems are foreseen in +doing this. + + The fact that Dillo's central I/O engine is written using +advanced features of POSIX and TCP/IP networking makes its +performance possible, but on the other hand this also means that +only a fraction of the interested hackers are able to work on it. + + A simple code base is critical when trying to attract hackers +to work on a project like this one. Using the GTK+ framework +helped both in creating the graphical user interface and in +handling the concurrency inside the browser. By having threads +communicate through pipes the need for explicit synchronization +is almost completely eliminated, and with it most of the +complexity of concurrent programming disappears. + + A clean, strictly applied layering approach based on clear +abstractions is vital in each programming project. A good, +supportive framework is of much help here. + + diff --git a/doc/Images.txt b/doc/Images.txt new file mode 100644 index 00000000..1f70b0ca --- /dev/null +++ b/doc/Images.txt @@ -0,0 +1,114 @@ + February 2001, --Jcid + + ------ + IMAGES + ------ + +* When a image tag is found within a HTML page, Html_tag_open_img +handles it by: + + - Parsing & getting attribute values. + - Creating a new image structure (DilloImage) and its + associated widget (DwImage). + i.e. If 'Image' is the var for the structure, then + 'Image->dw' is the widget. + - Requesting the image to be feeded by the cache. + - Sending some info to the browser interface. + +* The cache can either request the image data from the net, or +feed it directly from the dicache (decompressed image cache). + +* Both processes are somewhat different because the first one +requires to decode the image data into RGB format, and the second +one has the whole data already decoded. + +* Regardless of the RGB-data feeding method, the decoded data is +passed to the widget (DwImage) and drawn by GdkRGB in a streamed +way. + Note that INDEXED images are also decoded into RGB format. + + +--------------------- +Fetching from the net +--------------------- + +* a_Cache_open_url initiates the resource request, and when +finally the answer arrives, the HTTP header is examined for MIME +type and either the GIF or PNG or JPEG decoder is set to handle +the incoming data stream. + Decoding functions: a_Gif_image, a_Jpeg_image and a_Png_image. + +* The decoding function calls the following dicache methods as +the data is processed (listed in order): + + a_Dicache_set_parms + a_Dicache_set_cmap (only for indexed-GIF images) + a_Dicache_write + a_Dicache_close + +* The dicache methods call the necessary functions to connect +with the widget code. This is done by calling image.c functions: + + a_Image_set_parms + a_Image_set_cmap + a_Image_write + a_Image_close + +* The functions in image.c make the required a_Dw_image_... +calls. + + +------------------------- +Fetching from the dicache +------------------------- + +* a_Cache_open_url tests the cache for the image, and directly +enqueues a cache client for it, without asking the network for +the data. When the client queue is processed (a bit later), the +decoder is selected based on the cache entry Type. + +* When the decoder is called, it tests the dicache for the image; +if the image is found, then it sets a_Dicache_callback as the +handling function and gets out of the way (no image decoding is +needed). + +* Later on, the DwImage buffer is set to reference the +dicache-entry's buffer and the rest of the functions calls is +driven by a_Dicache_callback. + + +----------- +Misc. notes +----------- + +* Repeated images generate new cache clients, but only one of +them (the first) is handled with a_Dicache_* functions, the rest +is done with a_Dicache_callback.. + +* The cache-client callback is set when the Content-type of the +image is got. It can be: a_Png_image, a_Gif_image or a_Jpeg_image +Those are called 'decoders' because their main function is to +translate the original data into RGB format. + +* Later on, the decoder can substitute itself, if it finds the +image has been already decoded, with a_Dicache_callback function. +This avoids decoding it twice. + +* The dicache-entry and the Image structure hold bit arrays that +represent which rows had been decoded. + +* The image processing can be found in the following sources: + + - image.[ch] + - dicache.[ch] + - gif.[ch], png.[ch], jpeg.[ch] + - dw_image.[ch] + +* Bear in mind that there are three data structures for image +code: + + - DilloImage (image.h) + - DwImage (dw_image.h) + - DICacheEntry (dicache.h) + + diff --git a/doc/Imgbuf.txt b/doc/Imgbuf.txt new file mode 100644 index 00000000..1039ff9e --- /dev/null +++ b/doc/Imgbuf.txt @@ -0,0 +1,177 @@ +Aug 2004, S.Geerken@ping.de + +============= +Image Buffers +============= + +General +======= + +Image buffers depend on the platform (see DwRender.txt), but have a +general, platform independant interface, which is described in this +section. The next section describes the Gdk version of Imgbuf. + +The structure ImgBuf will become part of the image processing, between +image data decoding and the widget DwImage. Its purposes are + + 1. storing the image data, + 2. handling scaled versions of this buffer, and + 3. drawing. + +The latter must be done independently from the window. + +Storing Image Data +------------------ +Imgbuf supports five image types, which are listed in the table +below. The representation defines, how the colors are stored within +the data, which is passed to a_Imgbuf_copy_row(). + + | bytes per | + type | pixel | representation + ---------------+-----------+------------------------- + RGB | 3 | red, green, blue + RGBA | 4 | red, green, blue, alpha + gray | 1 | gray value + indexed | 1 | index to colormap + indexed alpha | 1 | index to colormap + +The last two types need a colormap, which is set by +a_Imgbuf_set_cmap(), which must be called before +a_Imgbuf_copy_row(). This function expects the colors as 32 bit +unsigned integers, which have the format 0xrrbbgg (for indexed +images), or 0xaarrggbb (for indexed alpha), respectively. + +Scaling +------- +The buffer with the original size, which was created by +a_Imgbuf_new(), is called root buffer. Imgbuf provides the ability to +scale buffers. Generally, both root buffers, as well as scaled +buffers, may be shared, memory management is done by reference +counters. + +Via a_Imgbuf_get_scaled_buf(), you can retrieve a scaled buffer. The +way, how this function works in detail, is described in the code, but +generally, something like this works always, in an efficient way: + + old_buf = cur_buf; + cur_buf = a_Imgbuf_get_scaled_buf(old_buf, with, height); + a_Imgbuf_unref (old_buf); + +Old_buf may both be a root buffer, or a scaled buffer. + +(As an exception, there should always be a reference on the root +buffer, since scaled buffers cannot exist without the root buffer, but +on the other side, do not hold references on it. So, if in the example +above, old_buf would be a root buffer, and there would, at the +beginning, only be one reference on it, new_buf would also be +destroyed, along with old_buf. Therefore, an external reference must +be added to the root buffer, which is in dillo done within the dicache +module.) + +The root buffer keeps a list of all children, and all operations +operating on the image data (a_Imgbuf_copy_row() and +a_Imgbuf_set_cmap()) are delegated to the scaled buffers, when +processed, and inherited, when a new scaled buffer is created. This +means, that they must only be performed for the root buffer. + +Drawing +------- +There are two situations, when drawing is necessary: + + 1. To react on expose events, the function a_Imgbuf_draw() can be + used. Notice that the exact signature of this function is + platform dependant. + + 2. When a row has been copied, it has to be drawn. To determine the + area, which has to be drawn, the function + a_Imgbuf_get_row_area() should be used. In dillo, the dicache + module will first call a_Img_copy_row(), and then call + a_Dw_image_draw_row() for the images connected to this image + buffer. a_Dw_image_draw_row() will then call + p_Dw_widget_queue_draw(), with an area determined by + a_Imgbuf_get_row_area(). + + +The Gdk Implementation +====================== + +The Gdk implementation is used by the Gtk+ platform. [... todo] + + +Global Scalers +============== + +In some cases, there is a context, where images have to be scaled +often, by a relatively constant factor. For example, the preview +window (GtkDwPreview) draws images via the Imgbuf draw functions, but +uses scaled buffers. Scaling such a buffer each time it is needed, +causes huge performance losses. On the other hand, if the preview +window would keep these scaled buffers (e.g. by lazy mapping of the +original buffer to the scaled buffer), the scaled buffers get never +freed, since the view is not told about, when the original buffer is +not needed anymore. (n.b., that currently, the scaled buffers are +destroyed, when the original buffer is destroyed, but this may change, +and even this would leave "zombies" in this mapping structure, where +the values refer to dead pointers). + +It is sufficient, that references on the scaled buffers are referred +somehow, so that they do not get destroyed between different +usages. The caller (in this case the preview) simply requests a scaled +buffer, but the Imgbuf returns this from the list of already scaled +buffers. + +These references are hold by special structures, which are called +"scalers". There are two types of scalers, local scalers, which are +bound to image buffers, and global scalers, which refer to multiple +scalers. + +What happens in different situations: + + - The caller (e.g. the preview) requests a scaled buffer. For this, + it uses a special method, which also passes the global image + scaler, which was created before (for the preview, there is a 1-1 + association). The Imgbuf uses this global image scaler, to + identify the caller, and keeps a list of them. If this global + scaler is not yet in the list, it is added, and a local scaler is + created. + + + + - + +There are three images in the page, i1a, i1b, and i2. I1a and i1b +refer to the same image recource, represented by the root image buffer +iba, which original size is 200 x 200. I1a is displayed in original +size, while i1b is displayed at 100 x 100. I2 refers to an other +recource, ibb, which has the size 300 x 300. I2 is shown in original +size. + + + :DwRenderLayout ------------------- :DwPage ----------. + / \ | + ,----' `----. ,------ i1a:DwImage --+ + / \ | | + view1:GtkDwViewport view2:GtkDwPreview | ,---- i1b:DwImage --| + | | | | + ,------------------------------' | | ,-- i2: DwImage --' + | | | | + | ,-------------------------------------' | | + | | ,--------------------------------' | + | | | ,----' + | | | | + | V | V + | iba:Imgbuf | ibb:Imgbuf -- 30x30 + | | | V | ^ + | | +- 100x100 ,- 20x20 ,- 10x10 | | + | | | | ^ | ^ | | + | | `----------+----|---' | `--. ,--' + | | ,--------------' | | | + | | | ,------------------' | | + | | | | | | + | lca:ImgbufLSc lcb:ImgbufLSc + | (factor 1/10) (factor 1/10) + | \ / + | `-----------. ,-------------------' + | \ / + `------------------> scl:ImgbufGSc + (factor 1/10) diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..9142fa3a --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,19 @@ +EXTRA_DIST = \ + Cache.txt \ + Cookies.txt \ + Dillo.txt \ + Dw.txt \ + DwImage.txt \ + DwPage.txt \ + DwRender.txt \ + DwStyle.txt \ + DwTable.txt \ + DwWidget.txt \ + HtmlParser.txt \ + IO.txt \ + Images.txt \ + Imgbuf.txt \ + NC_design.txt \ + Selection.txt \ + Dpid.txt \ + README diff --git a/doc/NC_design.txt b/doc/NC_design.txt new file mode 100644 index 00000000..6932b9cf --- /dev/null +++ b/doc/NC_design.txt @@ -0,0 +1,80 @@ + + _________________________________________________________________ + + Naming&Coding design + _________________________________________________________________ + + Dillo's code is divided into modules. For instance: bookmark, cache, + dicache, gif. + + Lets think of a module named "menu", then: + * Every internal routine of the module, should start with "Menu_" + prefix. + * "Menu_" prefixed functions are not meant to be called from outside + the module. + * If the function is to be exported to other modules (i.e. it will + be called from the outside), it should be wrapped with an "a_" + prefix. + + For instance: if the function name is "Menu_create", then it's an + internal function, but if we need to call it from the outside, then it + should be renamed to "a_Menu_create". + + Why the "a_" prefix? + Because of historical reasons. + And it reads better "a_Menu_create" than "d_Menu_create" cause the + first one suggests "a Menu create" function! + + Another way of understanding this is thinking of "a_" prefixed + functions as Dillo's internal library, and the rest ("Menu_" prefixed + in our example) as a private module-library. + + Indentation: Source code must be indented with 3 blank spaces, no Tabs. + Why? + Because different editors expand or treat tabs in several ways; 8 + spaces being the most common, but that makes code really wide and + we'll try to keep it within the 80 columns bounds (printer friendly). + + Function commenting: + + Every single function of the module should start with a short comment + that explains it's purpose; three lines must be enough, but if you + think it requires more, enlarge it. + +/* + * Try finding the url in the cache. If it hits, send the contents + * to the caller. If it misses, set up a new connection. + */ +int a_Cache_open_url(const char *url, void *Data) +{ + ... + ... + ... +} + + We also have the BUG: and todo: tags. + Use them within source code comments to spot hidden issues. For + instance: + +/* BUG: this counter is not accurate */ +++i; + +/* todo: get color from the right place */ +a = color; + _________________________________________________________________ + + What do we get with this? + + * A clear module API for Dillo; every function prefixed "a_" is to + be used outside the module. + * A way for identifying where the function came from (the + capitalized word is the module name). + * An inner ADT (Abstract data type) for the module. That can be + isolated, tested and replaced independently. + * A two stage instance for bug-fixing. You can change the exported + function algorithms while someone else fixes the internal + module-ADT! + * A coding standard ;) + _________________________________________________________________ + + Naming&Coding design by Jorge Arellano Cid diff --git a/doc/README b/doc/README new file mode 100644 index 00000000..3530292d --- /dev/null +++ b/doc/README @@ -0,0 +1,53 @@ +README: Last update Oct 2007 --jcid + + This documents need a review. They were current with dillo1 and +now, with dillo2, most of them are obsolete, specially Dw*txt, but +Dw2 is fully documented in html using doxygen. + + The other documents will be reviewed when I have some time. They +will give you an overview of what's going on but take them with a +pinch of salt. + + Of course I'd like to have these as doxygen files too! +If somebody wants to make this convertion, please let me know +to assign higher priority to updating these docs. + +-- +Jorge.- + + -------------------------------------------------------------------------- + FILE DESCRIPTION STATE + -------------------------------------------------------------------------- + NC_design.txt Naming&Coding design (Be sure to Current + read it before any other doc) + Dillo.txt General overview of the program Current + IO.txt Extensive introduction Current + Cache.txt Informative description Current + Images.txt Image handling and processing Current + HtmlParser.txt A versatile parser Current + Dw.txt The New Dillo Widget (Overview) Current + DwRendering.txt Dw Rendering Abstraction Current + DwWidget.txt The base object of Dw Current + DwImage.txt Dillo Widget image handling Incomplete + DwPage.txt Dillo Widget page (shortly) Incomplete + DwStyle.txt Styles of Dillo Widgets Pending + DwTable.txt Tables in dillo Current + Imgbuf.txt Image buffers Pending + Selection.txt Selections, and link activation Current (?) + Cookies.txt Explains how to enable cookies Current + Dpid.txt Dillo plugin daemon Current + -------------------------------------------------------------------------- + [This documents cover dillo's internal working. They're NOT a user manual] + -------------------------------------------------------------------------- + + + * Ah!, there's a small program (srch) within the src/ dir. It searches + tokens within the whole code (*.[ch]). It has proven very useful. + Ex: ./srch a_Image_write + ./srch todo: + + * Please submit your patches with 'diff -pru'. + + + Happy coding! + --Jcid diff --git a/doc/Selection.txt b/doc/Selection.txt new file mode 100644 index 00000000..08fe0abc --- /dev/null +++ b/doc/Selection.txt @@ -0,0 +1,149 @@ +Apr 2003, S.Geerken@ping.de +Last update: Dec 2004 + +========= +Selection +========= + +The selection module (selection.[ch]) handles selections, as well as +activation of links, which is closely related. + + +General Overview +================ + +The selection module defines a structure "Selection", which is +associated to GtkDwViewport, and so to a widget tree. The selection +state is controlled by "abstract events", which are sent by single +widgets by calling one of the following functions: + + a_Selection_button_press for button press events, + a_Selection_button_release for button release events, and + a_Selection_button_motion for motion events (with pressed mouse + button). + +The widget must construct simple iterators (DwIterator), which will be +transferred to extended iterators (DwExtIterator), see below for more +details. All event handling functions have the same signature, the +arguments in detail are: + + - DwIterator *it the iterator pointing on the item under + the mouse pointer, + - gint char_pos the exact (character) position within + the iterator, + - gint link if this item is associated with a link, + its number (see DwImage, section + "signals" for the meaning), otherwise + -1, + - GdkEventButton *event the event itself; only the button is + used, + - gboolean within_content TRUE, if there is some selectable + content unter the mouse cursor; if set + to FALSE, the "full screen" feature is + used on double click. + +In some cases, char_pos would be difficult to determine. E.g., when +the DwPage widget decides that the user is pointing on a position +_at_the_end_ of an image (DwImage), it constructs a simple iterator +pointing on this image widget. In a simple iterator, that fact that +the pointer is at the end, would be represented by char_pos == 1. But +when transferring this simple iterator into an extended iterator, this +simple iterator is discarded and instead the stack has an iterator +pointing to text at the top. As a result, only the first letter of the +ALT text would be copied. + +To avoid this problem, widgets should in this case pass SELECTION_EOW +(end of word) as char_pos, which is then automatically reduced to the +actual length of the extended(!) iterator. + +The return value is the same as in DwWidget event handling methods. +I.e., in most cases, they should simply return it. The events +"link_pressed", "link_released" and "link_clicked" (but not +"link_entered") are emitted by these functions, so that widgets which +let the selection module handle links, should only emit "link_entered" +for themselves. (See DwImage.txt for a description of this.) + + +Selection State +=============== + +Selection interferes with handling the activation of links, so the +latter is also handled by the selection module. Details are based on +following guidelines: + + 1. It should be simple to select links and to start selection in + links. The rule to distinguish between link activation and + selection is that the selection starts as soon as the user leaves + the link. (This is, IMO, a useful feature. Even after drag and + drop has been implemented in dillo, this should be somehow + preserved.) + + 2. The selection should stay as long as possible, i.e., the old + selection is only cleared when a new selection is started. + +The latter leads to a model with two states: the selection state and +the link handling state. + +The general selection works, for events not pointing on links, like +this (numbers in parantheses after the event denote the button, "n" +means arbitrary button): + + motion(1) + ,-----. + | | + press(1) on non-link V | + NONE -----------------------> SELECTING <----------------. + ^ | | + | | release(1) | + | | | press(1) + | no V yes | + `----------------------- Anything selected? --------> SELECTED + +The selected region is represented by two DwExtIterators. + +Links are handled by a different state machine: + + ,-----------------------------. + | | + | Switch to selection + | (SELECTING) for n == 1. + | ^ + | | no + | | yes + | Still the same link? --. + | ^ | + | | | + | | motion(n) | + V press(n) on links | | + NONE ---------------------> PRESSED(n) <-----' + ^ | + | | release(n) + | | + | V yes + | Still the same link? -----------------. + | | | + | | no V + | V Send "clicked" signal. + | Switch to selection | + | (SELECTED) for n == 1. | + | | | + |`----------------------------' | + | | + `----------------------------------------------------------' + +Switching to selection simply means that the selection state will +eventually be SELECTED/SELECTING, with the original and the actual +position making up the selection region. This happens for button 1, +events with buttons other than 1 do not affect selection at all. + + +TODO +==== + +* a_Selection_button_motion currently always assumes that button 1 has + been pressed (since otherwise it would not do anything). This should + be made a bit cleaner. + +* The selection should be cleared, when the user selects something + somewhere else (perhaps switched into "non-active" mode, as some + Gtk+ widgets do). |