Added a title page
[AXE.git] / doc / tutorial.xml
1 <?xml version="1.0"?>
2 <!DOCTYPE doc SYSTEM "/usr/local/xslt/doc.dtd">
3
4 <doc>
5
6 <!--
7       AXE - Andromeda X-Windows Encapsulation
8       Original author :  Arjen Baart - arjen@andromeda.nl
9       Version         : $Revision: 1.2 $
10
11       This document is prepared for XMLDoc. Transform to HTML,
12       LaTeX, Postscript or plain text with XMLDoc utilities and
13       XSLT sheets from http://www.andromeda.nl/projects/xmldoc/
14 -->
15
16 <book>
17
18 <titlepage>
19    <title>AXE Tutorial</title>
20    <author>Arjen Baart <code>&lt;arjen@andromeda.nl&gt;</code></author>
21    <date>July 24, 2002</date>
22    <docinfo>
23       <infoitem label="Version">0.2</infoitem>
24       <infoitem label="Organization">Andromeda Technology &amp; Automation</infoitem>
25    </docinfo>
26    <abstract>
27    </abstract>
28 </titlepage>
29
30 <toc/>
31
32 <chapter>
33 <heading>Introduction</heading>
34 <para>
35 The <strong>Andromeda X-Windows Encapsulation (AXE)</strong> is a C++ class
36 library which encapsulates the X Windows library (<emph>Xlib</emph>).
37 Classical programming with Xlib is rather cumbersome to say the least.
38 The Xlib calls are rather complicated and often require many parameters
39 that can more easily be stored in objects.
40 Also, the classical event loop present in most X applications is something
41 you don't want to reinvent every time.
42 That sort of thing is better left to a framework which provides you
43 with more easy way to deal with events from the X server.
44 </para>
45
46 <para>
47 The intention of <strong>AXE</strong> is to make programming for X a lot
48 easier.
49 It was first developed to teach programming in C++ to students and let
50 them do some graphics without being confronted by the X library.
51 Over the past years it has grown into a framwork that not only encapsulates
52 the resources in the X server, like windows, colors and graphic context,
53 but provides some mechanisms that handle low-level communication with
54 the X server, user interface classes, multithreading and utility classes.
55 Although functionality of <strong>AXE</strong> overlaps with some other
56 user interface libraries such as <emph>gtk+</emph> or <emph>Qt</emph>,
57 the first goal of <strong>AXE</strong> is not to be a user interface
58 library.
59 Some of the design goals in <strong>AXE</strong> are not met yet, but
60 they include features such as dynamically loading of the user interface,
61 a strong set of graphical object functions, image processing, high-level
62 graphics objects and lots more.
63 </para>
64
65 <para>
66 To get you started with <strong>AXE</strong>, this tutorial takes you through
67 a sample application in which some the features of <strong>AXE</strong>
68 are touched upon.
69 The sample application is a doodle program, where the user can freely
70 draw pictures with the mouse.
71 The pictures are stored as simple polylines with a few attributes.
72 Writing the doodle program will show you how to build an <strong>AXE</strong>
73 application and create the most basic functions for an application.
74 In this tutorial, I assume you have sucessfully downloaded and installed
75 <strong>AXE</strong>.
76 </para>
77
78 </chapter>
79
80 <chapter>
81 <heading>A minimal AXE application</heading>
82
83 <para>
84 In this chapter we'll start writing the very minimum to get an AXE
85 application going.
86 All we do is make a top-level window, draw something in that window
87 and add a button to quit the application.
88 </para>
89
90 <section>
91 <heading>The start: class xapplication</heading>
92
93 <para>
94 Assuming you installed <strong>AXE</strong>, you can start creating the
95 doodle program.
96 The first thing you need in each and every <strong>AXE</strong> application
97 is to create an application class, derived from the class
98 <code>xapplication</code> and declare a static object of that class.
99 In the example below, our application class is called <code>doodle</code>
100 and the static object is <code>DoodleApp</code>:
101
102 <verbatim>
103
104 #include &lt;AXE/xappl.h&gt;
105
106 class doodle: public xapplication
107 {
108 };
109
110 doodle DoodleApp;
111
112 </verbatim>
113
114 Note that you need to include the header file <code>AXE/xappl.h</code>
115 which contains the definition of the <code>xapplication</code> class.
116 Also note that, other than in classical C or C++ programs, you do not
117 have a <code>main()</code> function.
118 This is taken care of by the <strong>AXE</strong> library.
119
120 Next, compile and link the application:
121
122 <verbatim>
123 g++ -c doodle1.cpp
124 g++ -o doodle1 doodle1.o -lAXE -L/usr/X11R6/lib -lX11 -lXpm
125 </verbatim>
126
127 Start the doodle program and you'll see that nothing happes.
128 All this program does is connect to the X server and wait for
129 events that will never come.
130 To do someting visible, we have to create a window and map in on
131 the screen.
132 The <code>main()</code> function in <strong>AXE</strong> calls virtual
133 functions in the static application object, which you can override
134 in your own application class to get some useful work done.
135 One of the first functions it calls after makeing the connection with
136 the X display is <code>SetupResources</code>.
137 We will now override this function and create a top-level window for
138 doodle (see <emph>demos/doodle1.cpp</emph>):
139
140 <verbatim>
141 #include &lt;AXE/xappl.h&gt;
142
143 class doodle: public xapplication
144 {
145    managed_window *main_frame;
146
147    virtual void SetupResources(void);
148 };
149
150 doodle DoodleApp;
151
152 void doodle::SetupResources()
153 {
154    main_frame = new managed_window("Doodling with AXE");
155    main_frame-&gt;Map();
156 }
157 </verbatim>
158
159 We added two items to our <code>doodle</code> class:
160 <enumerate>
161 <item>The <code>managed_window</code> pointer to our top-level window:
162       <code>main_frame</code>
163 </item>
164 <item>The overridden fuunction from the base class:
165       <code>SetupResources()</code>
166 </item>
167 </enumerate>
168
169 In the <code>SetupResources()</code> function, we create the actual
170 window as a <code>managed_window</code> object.
171 A <code>managed_window</code> is a window like any other window, except
172 that is a direct child of the server's root window and as such is
173 managed by the window manager.
174 The window manager will put all kinds of decorations on our top-level
175 window like a title bar, a close button, a menu button, a border for
176 resizing and that sort of stuff.
177 Your mileage may vary, depending on the window manager you use.
178 We pass the title of our application to the constructor of the
179 <code>managed_window</code>, so we'll know what window we're looking at:
180
181 <verbatim>
182    main_frame = new managed_window("Doodling with AXE");
183 </verbatim>
184
185 The window is created but is not visible yet.
186 To show the window on the screen, we have to <strong>Map</strong> the
187 window with the member function <code>Map()</code>.
188 When you start the program, it will show the empty window with our
189 'Doodling with AXE' title in the titlebar:
190
191 <para>
192 <picture src='tutor2.1.png' eps='tutor2.1.eps'/>
193 </para>
194
195 You can do most of the usual things a window manager lets you do with
196 the doodle window.
197 You can minimize and restore or resize and move the window, provided
198 your window manager implements those functions.
199 The one thing you can not do is close the window.
200 The only way to stop doodle1 is to press ctrl-C in the terminal you
201 started the program.
202 This is because we do not handle the proper events to let the
203 window manager close our application.
204 As a matter of fact we do not handle any events at all, but we'll
205 get to that in the next section.
206 </para>
207
208 </section>
209
210 <section>
211 <heading>Drawing in the window</heading>
212
213 <para>
214 For drawing graphics in X Windows, we need two things.
215 First of all the window to draw in, of course. Second, we need a
216 <emph>Graphics Context</emph>.
217 A graphics context is a resource in X Windows that defines how graphical
218 primitives are drawn. It holds all the properties such as backround and
219 foreground color, line size and dashing, the font for text strings and
220 much more.
221 The window part of drawing is easy.
222 We just created a window for doodle and all graphical drawing functions
223 are simply member functions of the <code>window</code> class.
224 The graphics context is encapsulated with the <code>gc</code> class.
225 For our first example we will create a <code>gc</code> object with a foreground
226 color of black.
227 This means that everything we draw in the window is black on a white background
228 (assuming the default background of the window is white).
229 To start drawing graphics, we add two lines to our <code>SetupResources()</code>
230 member funtion:
231
232 <verbatim>
233    gc    graphic("black");
234    main_frame-&gt;DrawRectangle(graphic, 50, 70, 200, 100);
235
236 </verbatim>
237
238 The parameters to <code>DrawRectangle()</code> are: the graphics context, the X and
239 Y coordinates of the top left corner and the size (width and height) of the rectangle.
240 So, this example draws a rectangle that has a top left corner 50 pixels to the right
241 of the left side of our window and 70 pixels down from the top side of our window.
242 The rectangle is 200 pixels wide and 100 pixels high.
243 </para>
244
245 <para>
246 Getting a picture in a window is not so hard in itself.
247 However, drawing once and immediately after creating the window is not enough
248 in any practical application.
249 Try some window operations on your fresh drawing after doodle puts the window
250 on the screen.
251 You can resize the window or move windows from other applications over the doodle
252 window and you'll see the rectangle disappear just as easily as you created it.
253 That is because the X server does not remember what you drew in the window.
254 At the moment you call the <code>DrawRectangle()</code> function, the X server
255 renders the pixels to make the area on the screen look like a rectangle and then
256 immediately forgets the request to draw the rectangle.
257 When the doodle window becomes visible again after having been obscured for a while
258 or when the contents of the window are supposed to change for any other reason,
259 all the X server can do is clear the area of the window.
260 It is left to the application to redraw the contents of the window.
261 </para>
262
263 <para>
264 Fortunately, the X server lets you know when somthing interesting happens to
265 your windows and when you are supposed to react.
266 It does this by sending <emph>events</emph> to the application that owns
267 the window where the event takes place.
268 The X server sends lots of events for all sorts of reasons.
269 Events are sent to your application for example when the user clicks a mouse button,
270 moves the mouse, hits a key on the keyboard or makes parts of your
271 window visible.
272 For each type of event, there is a virtual function in the <code>window</code> class.
273 If you want to handle any of those events from the X server, you have to
274 override an event handling function in a class you derive from the <code>window</code>
275 base class.
276 In our doodle application, we will make a new class and a new window.
277 The new window is created as a child window of our top level window and we will
278 draw pictures only in this child window.
279 Generally, it is not a good idea to do all kinds of drawing in the top level
280 window.
281 Any real application uses many windows for various kinds of user interaction
282 and the top level window is used only to organize all these sub windows and
283 interact with the window manager.
284 So, here we go with our own class derived from <code>window</code>, which we will
285 call a <code>doodle_view</code>:
286
287 <verbatim>
288
289 class doodle_view : public window
290 {
291 public:
292
293    doodle_view(window *parent) : window (*parent, 10, 30, 300, 220)
294    {
295       SelectInput(ExposureMask, 1);
296       Background(color("lightyellow"));
297    }
298
299    virtual int EV_Expose(XExposeEvent);
300 };
301
302 </verbatim>
303
304 We define two member functions for our <code>doodle_view</code> class: The contructor
305 function <code>doodle_view()</code> and the overridden event handler for
306 <emph>Expose</emph> events, <code>EV_Expose()</code>.
307 In the constructor, we pass the parent window to the contructor of the base class,
308 <code>window</code> and add the default geometry of our subwindow.
309 The geometry for a subwindow is very similar to drawing a rectangle.
310 The four numbers are the X and Y coordinate of the top left corner, the width
311 and the height of the subwindow, relative to the parent window.
312 Inside the contructor, we tell <strong>AXE</strong> that we want to handle
313 <emph>Expose</emph> events by calling <code>SelectInput()</code>.
314 The first parameter of <code>SelectInput()</code> is a bitmask of the events
315 we want to select.
316 The binary values and their names for the mask are the same as the ones defined
317 for the X library.
318 The second parameter is a boolean value which is '1' if you want to turn selection
319 of the events on and '0' of you want these events to be turned off.
320 We also add a little color to the subwindow, so it stands out more clearly
321 in the parent window.
322 The parent window specifies in which window we want to put our subwindow.
323 In doodle, we create the subwindow as a child of the top level window we created earlier.
324 A pointer to the subwindow is created inside the <code>doodle class</code>:
325
326 <verbatim>
327
328 class doodle: public xapplication
329 {
330    managed_window *main_frame;
331    doodle_view    *draw_frame;
332
333
334 </verbatim>
335
336 The actual creation of the subwindow is put in the <code>SetupResources()</code>
337 member funtion of the <code>doodle</code> class, right after the creation
338 of the top level window:
339
340 <verbatim>
341 void doodle::SetupResources()
342 {
343    main_frame = new managed_window("Doodling with AXE");
344    main_frame-&gt;Map();
345
346    draw_frame = new doodle_view(main_frame);
347    draw_frame-&gt;Map();
348 }
349 </verbatim>
350
351 Note that we have to <emph>Map</emph> the subwindow, just like the top level
352 window to make it visible.
353 </para>
354
355 <para>
356 Finally, we have to implement the event handler function of the <emph>Expose</emph>
357 event handler, <code>EV_Expose()</code>.
358 As you may have noticed, the <code>DrawRectangle()</code> is removed from the
359 <code>SetupResources()</code> function.
360 We now wait for an expose event from the X server to do the actual drawing.
361 Every time our window becomes 'exposed' the X server sends an <emph>Expose</emph>
362 event, to which we react by redrawing our rectangle in the window.
363 Here is the implementation of the <emph>Expose</emph> event handler:
364
365 <verbatim>
366
367 int doodle_view::EV_Expose(XExposeEvent ev)
368 {
369    gc    graphic("black");
370
371    DrawRectangle(graphic, 50, 70, 200, 100);
372
373    return 1;
374 }
375 </verbatim>
376
377 Here you see the <code>gc</code> object and the <code>DrawRectangle()</code>
378 again, nearly the same as we previously had in the <code>SetupResources()</code>
379 function of the <code>doodle</code> class.
380 There is one difference: The pointer to the window (<code>main_frame-&gt;</code>
381 in our previous example) is removed from the function call.
382 This is because we are now inside a member funtion of the <code>doodle_view</code>
383 class and our window is passed implicitly as the <strong>this</strong> pointer
384 of the object itself.
385 </para>
386
387 <para>
388 The complete source code of the second doodle example is in the file
389 <strong>demos/doodle2.cpp</strong> of the <strong>AXE</strong> distribution.
390 If you compile and run doodle2, it should look like this:
391
392 <para>
393 <picture src='tutor2.2.png' eps='tutor2.2.eps'/>
394 </para>
395
396 You can now move the window all over the screen, resize it, move other windows
397 over the doodle window and the rectangle we draw in the little yellow window
398 should stay intact.
399 </para>
400
401 </section>
402
403 <section>
404 <heading>Quitting the application</heading>
405
406 <para>
407 Up until now, we do not have a decent way to quit the doodle application.
408 You have to kill doodle by sending it a signal, like hitting ctrl-C on your
409 keyboard.
410 It's about time to remedy that situation.
411 There are many ways to close an application, like hitting a specific key
412 ('q' for quit might be a good one), clicking on the close button provided
413 by the window manager or providing a 'Quit' button or menu item in the
414 application itself.
415 We will explore all of these possibilities in later chapters.
416 For now, to keep things simple, we will use the keyboard to quit doodle.
417 </para>
418
419 <para>
420 When the user hits a key while the focus is on the doodle window, the X server
421 sends a <emph>KeyPress</emph> event to our application.
422 This <emph>KeyPress</emph> event is dispatched by <strong>AXE</strong> to the
423 window on which the key was pressed, just as <emph>Expose</emph> events are
424 dispatched to the appropriate window.
425 In <strong>AXE</strong>, you can use any event to quit your application program.
426 All you need to do is return the value 0 (zero) from the event handler function.
427 You may have noticed in the previous section that the <code>EV_Expose()</code>
428 handler function returned the value 1 (one).
429 This return value tells the <strong>AXE</strong> library not the quit the program
430 after the event is handled.
431 If you return 0 (zero) from an event handler function, <strong>AXE</strong> will
432 destroy all your windows, close the display connection with the X server
433 and quit the application.
434 </para>
435
436 <para>
437 So, to quit doodle on a key stroke, we select <emph>KeyPress</emph> events
438 next to <emph>Expose</emph> events in our subwindow and override the
439 <code>EV_KeyPress()</code> event handler function.
440 To select <emph>KeyPress</emph> events, we add another <code>SelectInput()</code>
441 in our <code>doodle_view</code> contructor:
442
443 <verbatim>
444    doodle_view(window *parent) : window (*parent, 10, 30, 300, 220)
445    {
446       SelectInput(ExposureMask, 1);
447       SelectInput(KeyPressMask, 1);
448       Background(color("lightyellow"));
449    }
450 </verbatim>
451
452 Next, also in the <code>doodle_view</code> class declaration, we add the
453 declaration of the member function to handle the KeyPress events:
454
455 <verbatim>
456    virtual int EV_KeyPress(XKeyEvent ev);
457 </verbatim>
458
459 The implementation of the event handler is very trivial.
460 Just return the value 0 (zero) and doodle will close down when any key
461 is pressed in the light yellow window:
462
463 <verbatim>
464 int doodle_view::EV_KeyPress(XKeyEvent ev)
465 {
466    return 0;
467 }
468 </verbatim>
469
470 Having any key at all, even the Ctrl or the CapsLock key, quit doodle is a bit
471 cruel, don;t you think.
472 So, let's restrain the quitting to just the lower case 'q' key.
473 When we get an event from the X server in one of our event handling functions,
474 all kinds of information about that event is passed in the <emph>ev</emph>
475 parameter.
476 Until now, we silently ignored the <emph>ev</emph> parameter but is this
477 case we need it to find out which key was actually pressed.
478 There is no special provision in <strong>AXE</strong> to do this.
479 There are Xlib functions that do the job just fine.
480 The one Xlib function we use here is <code>XLookupKeysym()</code>, which extracts
481 information from the <code>XKeyEvent</code> structure and returns
482 a standardized code (a <emph>KeySym</emph>) of the key that was actually pressed.
483 For most 'ordinary' keys, the <emph>KeySym</emph> value is the same as the ASCII
484 value.
485 We can use the 'q' key simply by checking the <emph>KeySym</emph> for the
486 value 'q':
487
488 <verbatim>
489 int doodle_view::EV_KeyPress(XKeyEvent ev)
490 {
491    KeySym   key;
492
493    key = XLookupKeysym(&amp;ev, ev.state &amp; 1);
494
495    if (key == 'q')
496    {
497       return 0;
498    }
499    else
500    {
501       return 1;
502    }
503 }
504 </verbatim>
505
506 With this extra check, you can hit any key you want on doodle, but
507 only the 'q' key makes doodle leave the desktop.
508 You can find the complete source of this example in <strong>demos/doodle3.cpp</strong>.
509 </para>
510
511 </section>
512
513 </chapter>
514
515 <!--
516  
517   Drawing
518     tracking mouse events
519     storing the strokes.
520
521   Menus, file IO
522     the file selector.
523     about box
524
525    Window manager
526      changing the title
527      resizing
528      close button
529      icon
530 -->
531 </book>
532
533 </doc>
534
535