Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / bsdinstaller-1.1.6 / docs / dfui / dfui.html
1 <html>
2 <head><title>DragonFly Abstract User Interface (DFUI)</title></head>
3 <body bgcolor="#ffffff">
4
5 <h1>DragonFly Abstract User Interface (DFUI)</h1>
6
7 <p><i>$Id: dfui.html,v 1.2 2004/07/17 01:50:34 cpressey Exp $</i></p>
8
9 <h2>Introduction</h2>
10
11 <p>This document describes a user interface (UI) abstraction which allows an
12 application to communicate with a user through any number of kinds
13 of concrete user interfaces, such as <code>curses</code>, GTK+, or a
14 web browser.</p>
15
16 <p>This abstraction should be suitable for presenting the DragonFly system
17 installer to the user; however it need not and should not be limited to only the
18 installer.  Many other tasks in DragonFly, such as system configuration and
19 package installation, could and for the sake of consistency should use the
20 same UI.</p>
21
22 <h2>Abstraction</h2>
23
24 <p>The DFUI abstraction isolates the semantics of user interation from
25 the concrete details of it, similar to a <i>Model View Controller</i>
26 abstraction.  Such a system is split into <i>frontend</i> and <i>backend</i>:
27
28 <ul>
29 <li>The backend contains the control logic and invokes the tool programs (such
30 as <code>fdisk</code>.)</li>
31 <li>The frontend constructs the concrete user interface (with widgets and such)
32 and presents it to the user.</li>
33 </ul></p>
34
35 <p>The backend tells the frontend what kind of conversation to hold with the user;
36 the frontend tells the backend the user's ultimate decisions.</p>
37
38 <p>The backend talks to the frontend in terms of abstract user interface elements.
39 It tells the frontend nothing about <em>how</em> the UI should
40 be presented, only the bare essentials of what it must convey.  Likewise, the frontend
41 talks to the backend in terms of data.  It keeps trivia about the user (like what language
42 they are using) to itself.  In this way, we maintain component isolation: the
43 backend is concerned only with the <i>semantics</i> of the user interaction, while
44 the frontend is concerned only with its <i>implementation</i>.</p>
45
46 <p>The frontend may be constructed in almost any manner.  It may be a
47 <code>curses</code> program, or it may be a CGI.  This presents a unique barrier, namely that the
48 frontend might be either a single, long-running process <em>or</em> a series of short-running
49 processes.  This essentially requires a concurrently design where the frontend and backend
50 exist in seperate processes that exchange messages using IPC.</p>
51
52 <p>The contrast between the <code>curses</code> scenario and the CGI scenario
53 is illustrated below.</p>
54
55 <p>
56 <table width="100%" cellpadding=4 border=1>
57 <tr><td align="left" valign="top">
58 <p><img src="session_curses.png"></p>
59 </td><td align="right" valign="top">
60 <p><img src="session_cgi.png"></p>
61 </td></tr>
62 </table>
63 </p>
64
65 <p>This abstraction gives us several benefits:
66
67 <ul>
68 <li>We're not tying the installer (and other tasks) to one specific user interface toolkit.
69 <li>We're not even tying the installer (and other tasks) to one particular <i>kind</i> of UI.
70 <li>The code for the installation process itself should be easier to manage
71 because it won't be clouded with UI details.
72 <li>The code for the UI itself should be easier to manage
73 because it won't be clouded with installer details.
74 <li>The frontend and backend don't have to be written in the same
75 programming language (with some caveats; see below.)
76 <li>We have the opportunity for the frontend and backend to work
77 asymchronously (with some caveats; see below.)
78 <li>We can use the same UI consistently for all tasks, rather than only in the installer.
79 </ul></p>
80
81 <h2>Data Types</h2>
82
83 <p>Each of the messages sent between the frontend and the backend consists of
84 one piece of data.  Before getting to the messages themselves, we'll examine the
85 data types in use.</p>
86
87 <h3>Id's</h3>
88
89 <p>Many of the other data types each have an <i>id</i>.</p>
90
91 <p>An id is a key which identifies the object to both the frontend and the
92 backend.  It should be unique across all similar objects in the same container.
93 (i.e. a form id should be unique across all forms in a session, a field id should
94 be unique across all fields in a form, etc.)</p>
95
96 <p>Id's are implemented as a short alphanumeric (plus underscores) strings
97 such as <code>disk_select_menu</code>.</p>
98
99 <h3>Info Blocks</h3>
100
101 <p>Many of the other data types each have an <i>info block</i>.</p>
102
103 <p>In contrast to the id, which the computer uses to tell objects apart, an
104 info block is for the human operator's benefit.  Each info block contains:
105
106 <ul>
107 <li>the <i>name</i> of the object, which should be a short and
108 descriptive title such as "Select Disk";</li>
109 <li>the <i>short description</i> of the object, which should be one or
110 two sentences describing the purpose of the object; and</li>
111 <li>the <i>long description</i> of the object, which is analogous to
112 a "help file" on the object which should describe its purpose and
113 behaviour in a usefully verbose way.  The long description component
114 of an info block need not contain the actual text of the long description;
115 it should instead contain a reference to it in the form of a filename or URL.</li>
116 </ul></p>
117
118 <h3>Forms</h3>
119
120 <p>The basic unit of interaction is a <i>form</i>.  While typically displayed as
121 a familiar electronic version of a paper fill-out form, it is defined abstractly enough
122 to not pose any obstacles should we need to deal with other media.  However,
123 in this document we will give typical examples using a familiar GUI interpretation.</p>
124
125 <p>Each form consists of:
126
127 <ul>
128 <li>a form id</li>
129 <li>an info block</li>
130 <li>a <i>multiplicity flag</i></li>
131 <li>zero or more <i>fields</i></li>
132 <li>one or more <i>actions</i></li>
133 <li>zero or more <i>properties</i></li>
134 <li>zero or more <i>datasets</i></li>
135 </ul></p>
136
137 <p>The multiplicity flag determines whether there is a single set of data displayed on the
138 form (multiplicity = false), or multiple sets of data (multiplicity = true).
139 The former resembles a 'dialog box' in a typical UI.
140 The latter resembles a spreadsheet or database entry form, with zero or more 'rows'
141 of a data, and facilities for the user to add, remove, etc rows.  Examples of these
142 two different layout styles are shown below (note of course that these are only
143 examples - the frontend may interpret them any reasonable way it likes.)</p>
144
145 <p>
146 <table width="100%" cellpadding=4 border=1>
147 <tr><td align="left" valign="top">
148 <p align="center">multiplicity = false</p>
149 <p><img src="form_single.png"></p>
150 </td><td align="right" valign="top">
151 <p align="center">multiplicity = true</p>
152 <p><img src="form_multiple.png"></p>
153 </td></tr>
154 </table>
155 </p>
156
157 <h3>Fields</h3>
158
159 <p>Each field describes a piece of data that the user can view and possibly manipulate.
160 In a typical GUI, fields are rendered as text boxes and similar widgets.
161 Each field has:
162
163 <ul>
164 <li>a field id</li>
165 <li>an info block</li>
166 <li>zero or more <i>options</i></li>
167 <li>zero or more <i>properties</i></li>
168 </ul></p>
169
170 <p>A form with zero fields has only actions, and might typically look like a menu
171 or informative-only dialog box.</p>
172
173 <h3>Actions</h3>
174
175 <p>Each action describes an action that can be taken.
176 In a typical GUI, actions are rendered as buttons that can be clicked.
177 Each action consists of:
178
179 <ul>
180 <li>an action id</li>
181 <li>an info block</li>
182 <li>zero or more <i>properties</i></li>
183 </ul></p>
184
185 <h3>Datasets</h3>
186
187 <p>Each dataset describes a set of data that is displayed or entered into a
188 form.  In a form with more than zero fields, a multiplicity = false form has
189 exactly one dataset, while a multiplicity = true form may have more than one.
190 A form with zero fields must have zero datasets.</p>
191
192 <p>Each dataset consists of a set of one celldata for each field of the form.</p>
193
194 <h3>Celldatas</h3>
195
196 <p>A celldata represents a value entered into a field on a form, either
197 as a default value by the backend, or by the user's choice.</p>
198
199 <p>Each celldata consists of:
200
201 <ul>
202 <li>the field id to which it applies</li>
203 <li>the value</li>
204 </ul></p>
205
206 <p>The data type of all celldata values, as far as DFUI is concerned, is text.
207 The backend may convert the text into a more semantically meaningful format,
208 such as floating-point values, internally.</p>
209
210 <h3>Options</h3>
211
212 <p>Each field has zero or more options.  These are supplied values that the
213 user may choose to place in the field.</p>
214
215 <p>The semantics of options in combination with the editable flag are summarized here:
216
217 <table border=1>
218 <tr><th>Editable flag</th><th>Number of Options</th><th>Semantics</th></tr>
219 <tr><td>true</td><td>zero</td><td>user may manually enter any value in field</td></tr>
220 <tr><td>true</td><td>one or more</td><td>user may manually enter any value in field, or may select any option to fill field with ("combo box")</td></tr>
221 <tr><td>false</td><td>zero</td><td>user may not enter data into field in any fashion</td></tr>
222 <tr><td>false</td><td>one or more</td><td>user may not manually enter values into field, but may select an option to fill the field with ("list box")</td></tr>
223 </table></p>
224
225 <h3>Responses</h3>
226
227 <p>Each response describes a user's decision.  Each response consists of:
228
229 <ul>
230 <li>a form id</li>
231 <li>an action id</li>
232 <li>zero or more datasets</li>
233 </ul></p>
234
235 <p>The form id indicates the form that this is a response to.</p>
236
237 <p>The action id indicates the action that the user executed which caused
238 this response.</p>
239
240 <p>The datasets contain the user's choices.  When the form is
241 multiplicity = false, there should be exactly one dataset; when the form
242 is multiplicity = true, there should be one dataset per "row" of data that the
243 user entered.  Either way, there should always be one celldata per field
244 of the form in each dataset.</p>
245
246 <h3>Progresses</h3>
247
248 <p>A progress represents some task which takes a relatively long time to
249 complete; in a typical GUI it would be implemented by a progress bar.
250 Each progress consists of:
251
252 <ul>
253 <li>an info block</li>
254 <li>an <i>amount</i></li>
255 </ul></p>
256
257 <p>The amount is an integer which ranges from 0 to 100, where 0 indicates
258 no progress has been made, and 100 indicates that the task is finished.</p>
259
260 <p>Progresses do not have responses, but they may be cancelled.</p>
261
262 <h3>Properties</h3>
263
264 <p>See <code><a href="property.html">dfui_property</a></code>.</p>
265
266 <h2>Messages</h2>
267
268 <p>The backend may send the following kinds of messages to the frontend:
269
270 <ul>
271 <li><code>present(<i>form</i>)</code></li>
272 <li><code>prog_begin(<i>progress</i>)</code></li>
273 <li><code>prog_update(<i>progress</i>)</code></li>
274 <li><code>prog_end()</code></li>
275 <li><code>stop()</code></li>
276 </ul>
277 </p>
278
279 <p>The frontend may send the following kinds of replies to the backend:
280
281 <ul>
282 <li><code>submit(<i>response</i>)</code></li>
283 <li><code>continue()</code></li>
284 <li><code>cancel()</code></li>
285 <li><code>abort()</code></li>
286 </ul>
287 </p>
288
289 <p>These may, in theory, be asynchronous: the backend may, for example, send several
290 <code>present</code> messages before receiving any <code>submit</code> replies,
291 so long as there is some way to identify which <code>submit</code>s are associated with
292 which <code>present</code>s (i.e. the form id).</p>
293
294 <p>However, in practice, while some UI's support concurrently displaying multiple
295 UI elements in a natural fashion (typified by "windows",) others do not.  So to begin,
296 we will limit ourselves by enforcing
297 a sychronous discipline: each message from the backend must be replied by a reply from
298 the frontend before the next message from the backend is sent.</p>
299
300 <h3>The <code>present</code> Message</h3>
301
302 <p>The <code>present</code> message carries a form as a payload.  Upon
303 receipt of this message, the frontend should display the form and let the user
304 manipulate it.</p>
305
306 <p>The frontend may then reply to a <code>present</code> message with:
307
308 <ul>
309 <li>a <code>submit</code> reply with the corresponding form id, which
310 indicates that the user chose an action from the form; or</li>
311 <li>an <code>abort</code> reply, which indicates something catastrophic happened
312 and that the backend should halt everything.</li>
313 </ul>
314 </p>
315
316 <h3>The <code>submit</code> Reply</h3>
317
318 <p>The <code>submit</code> reply carries a response as a payload.  It should
319 only occur after a <code>present</code> message.</p>
320
321 <h3>The <code>prog_begin</code> Message</h3>
322
323 <p>A <code>prog_begin</code> message carries a progress as a payload
324 and indicates that a progress has begun.</p>
325
326 <p>The frontend may reply to a <code>prog_begin</code> message with:
327
328 <ul>
329 <li>a <code>continue</code> reply, indicating all is well; or</li>
330 <li>an <code>abort</code> reply, indicating the backend should halt.</li>
331 </ul>
332 </p>
333
334 <h3>The <code>prog_update</code> Message</h3>
335
336 <p>A <code>prog_update</code> message carries a progress as a payload
337 and indicates that the state of progress has changed.</p>
338
339 <p>The frontend may reply to a <code>prog_update</code> message with:
340
341 <ul>
342 <li>a <code>continue</code> reply, indicating all is well;</li>
343 <li>a <code>cancel</code> reply, indicating that the user cancelled the
344 progress; or</li>
345 <li>an <code>abort</code> reply, indicating the backend should halt.</li>
346 </ul>
347 </p>
348
349 <h3>The <code>prog_end</code> Message</h3>
350
351 <p>A <code>prog_end</code> message indicates that a progress is finished.
352 It has no payload.</p>
353
354 <p>The frontend may reply to a <code>prog_end</code> message with:
355
356 <ul>
357 <li>a <code>continue</code> reply, indicating all is well; or</li>
358 <li>an <code>abort</code> reply, indicating the backend should halt.</li>
359 </ul>
360 </p>
361
362 <h3>The <code>stop</code> Message</h3>
363
364 <p>The <code>stop</code> message has no payload; it simply tells the frontend
365 that the backend has finished processing.  The frontend may exit, if it is a
366 long-running process, or it may simply display a 'finished' page, if it is a
367 short-running CGI process.</p>
368
369 <h2>Pseudocode</h2>
370
371 <table width=100% cellpadding=4 border=1>
372 <tr>
373 <td align="center">curses frontend</td>
374 <td align="center">CGI frontend</td>
375 </tr>
376 <tr>
377 <td><pre>
378     if backend is running
379         error "backend is already running"
380     start backend
381     while not done {
382         wait for a message from backend
383         if the message is 'stop'
384             done := TRUE
385         else if the message is 'present' {
386             display a form based on the message
387             let the user interact with the form
388             until the user selects an action
389             send 'submit' message to backend
390         }
391     }
392 </pre></td>
393 <td><pre>
394     if the request method was 'POST' {
395         create 'submit' message from POSTed data
396         send 'submit' message to backend
397     } else {
398         if backend is running
399             error "backend is already running"
400         start backend
401     }
402     wait for a message from backend
403     if the message is 'stop'
404         display 'done' page
405     else if the message is 'present'
406         display a form based on the message
407 </pre></td>
408 </tr></table>
409
410 <h2>Library</h2>
411
412 <p>Let us consider now a library, <code>libdfui</code>, which implements this abstraction:
413
414 <ul>
415 <li>A C program could use functions in this library directly.
416 <li>A program in another high-level language, such as Python,
417 could use the functions in this library with a C binding made
418 (with, say, SWIG) for this purpose.
419 <li>A shell script could use these functions via
420 utility programs which parse their arguments and call
421 these functions directly (or via a proxy daemon.)
422 </ul></p>
423
424 <h3>Backend</h3>
425
426 <p>From the backend's point of view, the useful functions in this library are
427 as follows.  Each function returns an error response (1 = success, 0 = failure.)</p>
428
429 <ul>
430 <li><code>dfui_be_start(struct dfui_connection *c)</code>
431 <p>Connect to the frontend and return a handle to it in <code>c</code>.  XXX this function
432 also needs to know what frontend to try to connect to and how somehow.</p>
433 <li><code>dfui_be_present(struct dfui_connection *c, struct dfui_form *f, struct dfui_response *r)</code>
434 <p>Ask the frontend on the other end of <code>c</code> to present the form <code>f</code>, wait
435 for a submit reply, and return the response in <code>r</code></p>
436 <li><code>dfui_be_prog_begin(struct dfui_connection *c, struct dfui_progress *p)</code>
437 <p>Ask the frontend on the other end of <code>c</code> to start showing the progress <code>p</code>.</p>
438 <li><code>dfui_be_prog_update(struct dfui_connection *c, struct dfui_progress *p)</code>
439 <p>Ask the frontend on the other end of <code>c</code> to update the progress <code>p</code>.</p>
440 <li><code>dfui_be_prog_end(struct dfui_connection *c)</code>
441 <p>Ask the frontend on the other end of <code>c</code> to stop showing any progress.</p>
442 <li><code>dfui_be_stop(struct dfui_connection *c)</code>
443 <p>Tell the frontend on the other end of <code>c</code> to stop, and close the connection.</p>
444 </ul>
445
446 <p>Notably, there are also functions for constructing <code>struct dfui_form</code>s and its
447 brethren, and for intrerpreting and freeing <code>struct dfui_response</code>s.</p>
448
449 <h3>Frontend</h3>
450
451 <p>From the frontend's point of view, the useful functions in this library are
452 as follows. Like the backend, these all return failure/success values.</p>
453
454 <ul>
455 <li><code>dfui_fe_start(struct dfui_connection *c)</code>
456 <p>Connect to the backend XXX somehow.</p>
457 <li><code>dfui_fe_receive(struct dfui_connection *c, char *msgtype, void **payload)</code>
458 <p>Wait for the backend to send a message, then return the message type in <code>*msgtype</code>
459 and the message itself in <code>*payload</code>.  It is the frontend's reponsibility to cast the
460 payload to the correct data type (based on <code>*msgtype</code>,) and to free it at some later
461 time if it is not NULL.</p>
462 <li><code>dfui_fe_submit(struct dfui_connection *c, struct dfui_response *r)</code>
463 <p>Send a submit reply to the backend.</p>
464 <li><code>dfui_fe_progress_continue(struct dfui_connection *c)</code>
465 <p>Send a continue reply to the backend.</p>
466 <li><code>dfui_fe_progress_cancel(struct dfui_connection *c)</code>
467 <p>Send a cancel reply to the backend.</p>
468 <li><code>dfui_fe_abort(struct dfui_connection *c)</code>
469 <p>Send an abort reply to the backend.</p>
470 <li><code>dfui_fe_stop(struct dfui_connection *c)</code>
471 <p>Close the connection.</p>
472 </ul>
473
474 <p>Notably, there are also functions for intrerpreting <code>struct dfui_form</code>s et al,
475 and for constructing and freeing <code>struct dfui_responses</code>s.</p>
476
477 <h2>Implementation</h2>
478
479 <p>The IPC used by libdfui, through which the frontend and backend exchange messages
480 and replies, is a wrapper around the CAPS mechanism.  CAPS is the lightweight messaging
481 abstraction Matt Dillon has introduced into DragonFly.</p>
482
483 <p>CAPS was chosen for two reasons: it fulfils DFUI's needs as an IPC mechanism, and DFUI
484 provides a good "real world" test for CAPS.</p>
485
486 <p>The implementation of libdfui in CAPS is fairly straightforward, with one significant twist.</p>
487
488 <p>Despite the backend sending 'requests' and the frontend sending 'replies', the backend is
489 a CAPS service, while the frontend is a CAPS client.  This is necessary as the frontend could
490 be a series of short-lived processes.  A CAPS service cannot receive a message in one
491 process and reply to it in another.</p>
492
493 <p>We implement this role reversal by having each message exchange, as described above,
494 be implemented by two full CAPS message-reply exchange cycles internally.</p>
495
496 <p>That is, for a backend to send a message to the frontend, the backend waits until it
497 receives a generic 'client ready' message from the frontend, then sends it's request as a
498 CAPS reply.  It then waits for another message from the frontend (the DFUI reply) and
499 replies to that with a generic 'service ready' message.</p>
500
501 </body>
502 </html>