summaryrefslogtreecommitdiff
path: root/aptitude-hackers-guide.txt
blob: 0ff925826c3d5a36427d2b940b6692d9645e3af0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
                       APTITUDE HACKERS GUIDE (for 0.2.0)

  This intends to be a high-level overview of the internals of Aptitude,
and useful as a jumping-off point for people looking to modify it.  It covers
the overall structure and some specific stuff, and was inspired by the
Freeciv Hackers Guide.  (I'm sure there are other documents like this out
there, but that's the one I've read)  Previously, this covered each and every
source file.  This is no longer the case; it doesn't `scale' ;-)  Instead, I
just present a broad overview of the various subsystems.

  Aptitude is written in C++, with extremely heavy use of some language
features: in particular, templating and inheritence.  The 0.2.x series also
is heavily based around the libsigc++ library and heavily uses callbacks and
signal/slot style programming.

  The source tree is divided into three parts: the "generic" routines: various
utility routines and routines for interfacing with the apt libraries;
the "vscreen" library, containing the UI widget library; and the toplevel
directory, which contains aptitude itself.

                                VSCREEN

  The vscreen library is the core of aptitude's user interface.  It is
a widget set based on libsigc++ using Curses.  It supports what is
needed in aptitude; some things (like a multiline text entry widget or
a dropdown menu) are not yet available because I haven't needed them
yet.  However, within the feature set that it has, it should be fully
functional.

  The "testvscreen" program tests the minimal functionality of most of
the widgets in the vscreen library.  It is also probably useful as a
programming example, as a simple program utilizing the library.

  vscreen/vscreen.h contains functions to manipulate the vscreen main
loop and other global state.  They include:
  vscreen_update() -- queues a request to repaint the screen.  (note
   that vscreen takes a brute-force approach to redrawing and counts on
   Curses to optimize things)
     Multiple requests to paint the screen will be merged into a
   single request.  Calling vscreen_update() is (should be) very
   efficient; don't be afraid to do it if you aren't sure whether it's
   necessary.
  vscreen_tryupdate() -- handles any pending vscreen_update() requests
  vscreen_mainloop() -- enters the main loop, in which the program
   waits for and handles events.
  vscreen_exitmain() -- exits the currently running main loop.  (note
   that main loops are allowed to nest)
  vscreen_poll()
  vscreen_addtimeout() -- installs a new timeout which will be called
   in no less than the given amount of time.  The timeout should be
   provided as a libsigc++-style slot.

  Other public routines exist as well; they should be described in vscreen.h.  


  There are several low-level pieces of glue that aptitude uses.
These are mostly inherited from previous versions of the program,
although modifications have been made:

  curses++.* contain a C++ class wrapper around Curses windows.
Although this was initially merely a bunch of syntactic sugar, there
are now very good reasons to use it: it turned out that the underlying
Curses windows have to be destroyed in a "children before parents"
fashion, or memory leaks occur.  Therefore, the wrapper class now
refcounts each WINDOW * that it allocates.  This is done (supposedly)
entirely transparently and with any luck you won't have to mess with
that.

  config/colors.* contian some routines to handle symbolic allocation
of colors.  There are a few things that need to be fixed here:
in particular, attributes like A_BOLD should be configured here as
well.  The interface is straightforward.

  config/keybindings.* contain routines to parse, store, compare, and
print keybindings.  Again, the interface is straightforward.

  vscreen/vscreen_widget.* contains the base class for all widgets in
the vscreen widget system.  Note that it is a *relatively* heavy
class; the expectation is that a fairly small (say, less than
100-1000) number will be allocated.  Allocating one widget for each
package in the package cache is a questionable idea.  (although who
knows, it might work)

  Widgets should generally not be explicitly deleted; instead, call
their "destroy" method.  This will place the widget onto a queue to be
deleted, generally by the main program loop.  This allows the widget
to clean itself up gracefully, and also makes things like destroying a
widget currently in use on the stack (relatively) safe.

  Widgets have quite a bit of state.  Each widget has an "owner",
which is the container into which the widget has been placed.  The
cwindow assigned to the widget will be contained within the cwindow
assigned to its owner; the location and size is determined by the
"geom" member variable.  It stores an X location, a Y location, a
width and a height, where X and Y are relative to the origin of the
owner.
  Widgets also can have a default background attribute; before
entering a custom paint routine, the background and current colors
will be set to this value, and the widget will be cleared (so it only
contains blanks of that color)

  The size_request() method specifies the desired minimum size of
the widget (see README.layout).  Override it to calculate the correct
value.
  The focus_me() method should return true if the widget should
receive keystrokes, and false otherwise.  Widgets which have multiple
children, for instance, may use this to determine where to forward
keystroke events.

  To draw a widget in a non-standard way, override its paint()
method.

  To alter the behavior of a widget on keystrokes, there are two
options.  You can override handle_char, or you can connect a "key
signal" to the widget.
  Overriding handle_char is pretty simple: just check whether the key
is a key that you want to handle (see the way the stock widgets do it
if you aren't sure).  If your widget handled the keystroke, return
true, otherwise return false.  Be sure to call the superclass's
handle_char() method if your class does not need the keystroke (for
one thing, key signals may not work if you don't!)
  Key signals are signals that are triggered when a keyboard
event is sent.  They may be activated before or after the widget has
finished processing keystrokes, and work pretty much like normal
signals.  The intent behind key signals is to cut down on unneccessary
subclassing by allowing the user of the widget to slightly modify its
behavior.  Key signals are also, of course, more dynamic than
inheritance (ie, you can add and delete them on the fly)  In general,
you should override dispatch_char() when creating a new widgettype,
and use key signals if they are fine by themselves.  See the usage of
these two in the source for more guidance, and use your judgement.
  The connect_key, connect_key_post, disconnect_key, and
disconnect_key_post routines manage key signals.

focussed() and unfocussed() are actually signals, but they should be
  called when the widget gains or loses focus.  (for instance, some
  widgets might use this to determine when to show or hide a "cursor")

  The following signals are emitted by widgets:
shown(), hidden() -- emitted when the widget is shown or hidden (it is
  already visible or invisible at that point)

destroyed() -- emitted when the widget is destroyed (it no longer has
  a valid owner, but some signals may be connected)

do_layout() -- sent when a widget's layout has been altered.  It is
  not entirely clear to me why this is not a virtual method.
  Presumably it made sense at the time, but it doesn't now.

  The following stock vscreen widgets are available:
vs_bin -- abstract class for a container holding a single widget

vs_button -- a standard pushbutton

vs_center -- a widget that centers its contents within itself; should
            be replaced by a generic 'layout' widget

vs_container -- abstract class for a widget that holds other widgets

vs_editline -- a line-editing widget

vs_file_pager -- displays the contents of a file.  Subclass of vs_pager.

vs_frame -- a widget that draws a frame around its contents

vs_label -- a widget that displays static (possibly multi-line) text

vs_menubar -- a widget to generate a menubar

vs_menu -- a single menu.  Currently very reliant on the menubar, and
          not very graceful about small terminals.

vs_minibuf_win -- a holdover from the old vscreen, which can display
                   a message in a "header" line, various widgets in
                   the "status" line, and a "main" widget in the
                   middle.

                   DEPRECATED.

vs_pager -- displays long amounts of text with scrolling.

vs_passthrough -- an abstract container with a notion of "the
                 currently focusssed child".  Keyboard events are
                 passed through to this child, as are some other
                 things.  For instance, vs_table is one of these.

vs_radiogroup -- controls a set of togglebuttons so that only a single
                one can be selected at once.

vs_scrollbar -- a vertical or horizontal scrollbar.

vs_stacked -- displays several stacked (possibly overlapping)
             widgets.

vs_statuschoice -- displays a single-line question; eg: "Continue? [yn]".
                  Useful in status lines.

vs_table -- arranges several widgets in a "table".  Widgets can be
           added and removed, and the table will dynamically update.
           The algorithm could probably use some work; see
           README.layout for a description of it.  (it was basically
           lifted from GTK)

vs_togglebutton -- a button that can either be on or off.  (eg, a
                  checkbutton)

  In addition, some common interface dialogs are available in vs_util.h:
popup messages, yes/no choices, string inputs, and a popup file pager.

  Generic utility code and useful APT routines are stored in generic/
Important files in here include:

 -> undo.* contains a generic undo system.

 -> Getting the package changelogs from cgi.debian.org is done in
  pkg_changelog.*  -- a pretty simple extension of the Acquire system.
  Right now this is totally broken because the changelogs on debian.org
  have been broken by package pools.

 -> aptcache.* contains replacements for the APT pkgCacheFile and pkgDepCache
classes.  The reason for this is that I wanted/needed to add extra persistent
information for each class, and this seemed like the logical way to do it.

  Previous versions of this file suggested that the aptitudeDepCache
would eventually be used to handle persistent selection of a
particular version.  With pins available, I think that that is no
longer necessary (and in fact not especially desirable)

    This file also handles the generation of undo information about
apt actions.

 -> matchers.* defines the generalized package matching system.  This is
currently used to sort for a particular package and to limit the display.  It
works by compiling a pattern (using an incredibly hairy and somewhat quirky
parser which should probably be sanitized a little) into an internal tree of
operators which can then be (relatively) efficiently applied to packages.

 -> config_signal.* wraps the apt configuration class.  It allows
slots to be connected to 'signals' which are generated when a
configuration option changes.  Quite useful.  (IMO :) )

  aptitude-specific code is stored in the top of the source tree (src/)

  Important classes include:

 -> apt_trees are a parent for all the various trees which display information
about packages.  The following behaviors are handled here: handling undo
information when the screen is quit, preventing undos from touching actions
performed before the screen was entered, displaying APT errors in the status
line, performing install runs, updating the package lists, and restoring the
current display state after an install run or a package-list update occurs.

 -> The most important interface element is the pkg_tree class.  This
class displays a hierarchy of all the packages (sort of, see below)
and allows you to mark them for installation or removal. pkg_trees
work by creating hierarchies of pkg_subtrees and pkg_items, but are
very flexible in how they create this hierarchy.  In fact, actually
creating the hierarchy is the job of a 'grouping policy' -- a class
that knows how to sort out packages into several groups and add them
to a tree.  Grouping policies can be nested for greater
configurability, as diagrammed (poorly :) ) below.  (this shows the
flow of data through the system from the package cache to the final
tree structure)

PACKAGES ===> LIMIT ===> GROUP POLICY 1 ===> (GROUP POLICIES 2..N) ==> TREE

  Packages come from the package cache (on the left) and are filtered through
the tree's display limit, followed by any number of grouping policies.  Each
grouping policy will take action on a package based on the rules for that
policy: it may discard the package altogether, it may insert it into a tree, or
it may pass it onto the next grouping policy.  These policies are produced by
"grouping policy factories". Grouping policy factories are classes which, as
the name suggests, have a method which produces a grouping policy when called.
They are essentially closures hacked into C++.
  Grouping policies should never be created directly.  The reason for this is
that a grouping policy may create multiple subtrees, and each subtree will
require a separate grouping policy (because each policy can potentially track
state about stuff inserted into it, we can't just use a single policy for
each link in the chain)  Policy factories are used as the exclusive interface
for allocating policy objects; in fact, the policy classes themselves are
generally not even included in the .h files.
  Policies are named as pkg_grouppolicy_foo with corresponding factory
pkg_grouppolicy_foo_factory.

  One policy is particularly special: the end policy.  This terminates a policy
chain; instead of passing its arguments on to another link, it inserts the
item into the tree.  In the diagram above, GROUP POLICY N would be an end
policy.
  There are several other policies which act like the end policy, in that
they terminate a policy chain: in particular, the dep policy and the
ver policy.  These cause packages added to them to be directly inserted to
the tree, but with subtrees tacked on (see pkg_item_with_subtree.h).  In
particular, the dep policy adds a package's dependencies as subitems of the
package itself, and the ver policy adds its available versions as subitems.
There is no description policy (which would add the description as a subtree).
It is left as an exercise to the reader. (hint. ;-) )

  The bulk of the grouping policy stuff is in pkg_grouppolicy.*

  The work of parsing group-policies is done by load_grouppolicy.

 -> Information about the packages is displayed by several specialized screens:
pkg_ver_screen, pkg_dep_screen, pkg_description_screen, and pkg_info_screen.

 -> Downloading is done by download_list and download_bar.
download_bar was written for changelogs, but can be used for other
things.  Its major correctable deficiency is that it does not allow a
download to be cancelled.

  The code to pull all this together and actually download/install packages
is in download.* .

 -> Columnification.  This is split into two parts:
  * Parsing, storing, and using of column configuration files.  This is mainly
   in vscreen/config/column_definition.*, and used in pkg_columnizer.* .  Think
   of the column_generator class and its descendents as closures and it will
   (hopefully) be clearer.  If not, just think of it as a fancy function :)
   Basically, column_parser_func tells what integer ID a particular column name
   maps to, while column_generator maps from an ID (and some other information,
   stored in an instance of the class) to a string value.  The ID is used to
   avoid parsing too often.
  * Generation of columns.  See vscreen/columnify.* .  The columnify function
   returns a formatted string.

  -> Sorting is handled by 'sort policies'.  These are similar to
group policies, but in order to reduce typing they are generated by a
macro function that adds the redundant cruft (the sort policies
themselves are only created by factory routines, which would add some
unnecessary typing for each class)

     Note: there are way too many dynamic_casts and virtual methods in
the sort system.  Bonus cookie points to anyone who manages to
streamline it without making it any nastier than it is.

 -- Daniel Burrows <Daniel_Burrows@brown.edu>