2 <!DOCTYPE doc SYSTEM "/usr/local/xslt/doc.dtd">
8 <title>AXE Tutorial</title>
13 <heading>Introduction</heading>
15 The <strong>Andromeda X-Windows Encapsulation (AXE)</strong> is a C++ class
16 library which encapsulates the X Windows library (<emph>Xlib</emph>).
17 Classical programming with Xlib is rather cumbersome to say the least.
18 The Xlib calls are rather complicated and often require many parameters
19 that can more easily be stored in objects.
20 Also, the classical event loop present in most X applications is something
21 you don't want to reinvent every time.
22 That sort of thing is better left to a framework which provides you
23 with more easy way to deal with events from the X server.
27 The intention of <strong>AXE</strong> is to make programming for X a lot
29 It was first developed to teach programming in C++ to students and let
30 them do some graphics without being confronted by the X library.
31 Over the past years it has grown into a framwork that not only encapsulates
32 the resources in the X server, like windows, colors and graphic context,
33 but provides some mechanisms that handle low-level communication with
34 the X server, user interface classes, multithreading and utility classes.
35 Although functionality of <strong>AXE</strong> overlaps with some other
36 user interface libraries such as <emph>gtk+</emph> or <emph>Qt</emph>,
37 the first goal of <strong>AXE</strong> is not to be a user interface
39 Some of the design goals in <strong>AXE</strong> are not met yet, but
40 they include features such as dynamically loading of the user interface,
41 a strong set of graphical object functions, image processing, high-level
42 graphics objects and lots more.
46 To get you started with <strong>AXE</strong>, this tutorial takes you through
47 a sample application in which some the features of <strong>AXE</strong>
49 The sample application is a doodle program, where the user can freely
50 draw pictures with the mouse.
51 The pictures are stored as simple polylines with a few attributes.
52 Writing the doodle program will show you how to build an <strong>AXE</strong>
53 application and create the most basic functions for an application.
54 In this tutorial, I assume you have sucessfully downloaded and installed
61 <heading>A minimal AXE application</heading>
64 In this chapter we'll start writing the very minimum to get an AXE
66 All we do is make a top-level window, draw something in that window
67 and add a button to quit the application.
71 <heading>The start: class xapplication</heading>
74 Assuming you installed <strong>AXE</strong>, you can start creating the
76 The first thing you need in each and every <strong>AXE</strong> application
77 is to create an application class, derived from the class
78 <code>xapplication</code> and declare a static object of that class.
79 In the example below, our application class is called <code>doodle</code>
80 and the static object is <code>DoodleApp</code>:
84 #include <AXE/xappl.h>
86 class doodle: public xapplication
94 Note that you need to include the header file <code>AXE/xappl.h</code>
95 which contains the definition of the <code>xapplication</code> class.
96 Also note that, other than in classical C or C++ programs, you do not
97 have a <code>main()</code> function.
98 This is taken care of by the <strong>AXE</strong> library.
100 Next, compile and link the application:
104 g++ -o doodle1 doodle1.o -lAXE -L/usr/X11R6/lib -lX11 -lXpm
107 Start the doodle program and you'll see that nothing happes.
108 All this program does is connect to the X server and wait for
109 events that will never come.
110 To do someting visible, we have to create a window and map in on
112 The <code>main()</code> function in <strong>AXE</strong> calls virtual
113 functions in the static application object, which you can override
114 in your own application class to get some useful work done.
115 One of the first functions it calls after makeing the connection with
116 the X display is <code>SetupResources</code>.
117 We will now override this function and create a top-level window for
118 doodle (see <emph>demos/doodle1.cpp</emph>):
121 #include <AXE/xappl.h>
123 class doodle: public xapplication
125 managed_window *main_frame;
127 virtual void SetupResources(void);
132 void doodle::SetupResources()
134 main_frame = new managed_window("Doodling with AXE");
135 main_frame->Map();
139 We added two items to our <code>doodle</code> class:
141 <item>The <code>managed_window</code> pointer to our top-level window:
142 <code>main_frame</code>
144 <item>The overridden fuunction from the base class:
145 <code>SetupResources()</code>
149 In the <code>SetupResources()</code> function, we create the actual
150 window as a <code>managed_window</code> object.
151 A <code>managed_window</code> is a window like any other window, except
152 that is a direct child of the server's root window and as such is
153 managed by the window manager.
154 The window manager will put all kinds of decorations on our top-level
155 window like a title bar, a close button, a menu button, a border for
156 resizing and that sort of stuff.
157 Your mileage may vary, depending on the window manager you use.
158 We pass the title of our application to the constructor of the
159 <code>managed_window</code>, so we'll know what window we're looking at:
162 main_frame = new managed_window("Doodling with AXE");
165 The window is created but is not visible yet.
166 To show the window on the screen, we have to <strong>Map</strong> the
167 window with the member function <code>Map()</code>.
168 When you start the program, it will show the empty window with our
169 'Doodling with AXE' title in the titlebar:
172 <picture src='tutor2.1.png' eps='tutor2.1.eps'/>
175 You can do most of the usual things a window manager lets you do with
177 You can minimize and restore or resize and move the window, provided
178 your window manager implements those functions.
179 The one thing you can not do is close the window.
180 The only way to stop doodle1 is to press ctrl-C in the terminal you
182 This is because we do not handle the proper events to let the
183 window manager close our application.
184 As a matter of fact we do not handle any events at all, but we'll
185 get to that in the next section.
191 <heading>Drawing in the window</heading>
194 For drawing graphics in X Windows, we need two things.
195 First of all the window to draw in, of course. Second, we need a
196 <emph>Graphics Context</emph>.
197 A graphics context is a resource in X Windows that defines how graphical
198 primitives are drawn. It holds all the properties such as backround and
199 foreground color, line size and dashing, the font for text strings and
201 The window part of drawing is easy.
202 We just created a window for doodle and all graphical drawing functions
203 are simply member functions of the <code>window</code> class.
204 The graphics context is encapsulated with the <code>gc</code> class.
205 For our first example we will create a <code>gc</code> object with a foreground
207 This means that everything we draw in the window is black on a white background
208 (assuming the default background of the window is white).
209 To start drawing graphics, we add two lines to our <code>SetupResources()</code>
214 main_frame->DrawRectangle(graphic, 50, 70, 200, 100);
218 The parameters to <code>DrawRectangle()</code> are: the graphics context, the X and
219 Y coordinates of the top left corner and the size (width and height) of the rectangle.
220 So, this example draws a rectangle that has a top left corner 50 pixels to the right
221 of the left side of our window and 70 pixels down from the top side of our window.
222 The rectangle is 200 pixels wide and 100 pixels high.
226 Getting a picture in a window is not so hard in itself.
227 However, drawing once and immediately after creating the window is not enough
228 in any practical application.
229 Try some window operations on your fresh drawing after doodle puts the window
231 You can resize the window or move windows from other applications over the doodle
232 window and you'll see the rectangle disappear just as easily as you created it.
233 That is because the X server does not remember what you drew in the window.
234 At the moment you call the <code>DrawRectangle()</code> function, the X server
235 renders the pixels to make the area on the screen look like a rectangle and then
236 immediately forgets the request to draw the rectangle.
237 When the doodle window becomes visible again after having been obscured for a while
238 or when the contents of the window are supposed to change for any other reason,
239 all the X server can do is clear the area of the window.
240 It is left to the application to redraw the contents of the window.
244 Fortunately, the X server lets you know when somthing interesting happens to
245 your windows and when you are supposed to react.
246 It does this by sending <emph>events</emph> to the application that owns
247 the window where the event takes place.
248 The X server sends lots of events for all sorts of reasons.
249 Events are sent to your application for example when the user clicks a mouse button,
250 moves the mouse, hits a key on the keyboard or makes parts of your
252 For each type of event, there is a virtual function in the <code>window</code> class.
253 If you want to handle any of those events from the X server, you have to
254 override an event handling function in a class you derive from the <code>window</code>
256 In our doodle application, we will make a new class and a new window.
257 The new window is created as a child window of our top level window and we will
258 draw pictures only in this child window.
259 Generally, it is not a good idea to do all kinds of drawing in the top level
261 Any real application uses many windows for various kinds of user interaction
262 and the top level window is used only to organize all these sub windows and
263 interact with the window manager.
264 So, here we go with our own class derived from <code>window</code>, which we will
265 call a <code>doodle_view</code>:
269 class doodle_view : public window
273 doodle_view(window *parent) : window (*parent, 10, 30, 300, 220)
275 SelectInput(ExposureMask, 1);
276 Background(color("lightyellow"));
279 virtual int EV_Expose(XExposeEvent);
284 We define two member functions for our <code>doodle_view</code> class: The contructor
285 function <code>doodle_view()</code> and the overridden event handler for
286 <emph>Expose</emph> events, <code>EV_Expose()</code>.
287 In the constructor, we pass the parent window to the contructor of the base class,
288 <code>window</code> and add the default geometry of our subwindow.
289 The geometry for a subwindow is very similar to drawing a rectangle.
290 The four numbers are the X and Y coordinate of the top left corner, the width
291 and the height of the subwindow, relative to the parent window.
292 Inside the contructor, we tell <strong>AXE</strong> that we want to handle
293 <emph>Expose</emph> events by calling <code>SelectInput()</code>.
294 The first parameter of <code>SelectInput()</code> is a bitmask of the events
296 The binary values and their names for the mask are the same as the ones defined
298 The second parameter is a boolean value which is '1' if you want to turn selection
299 of the events on and '0' of you want these events to be turned off.
300 We also add a little color to the subwindow, so it stands out more clearly
301 in the parent window.
302 The parent window specifies in which window we want to put our subwindow.
303 In doodle, we create the subwindow as a child of the top level window we created earlier.
304 A pointer to the subwindow is created inside the <code>doodle class</code>:
308 class doodle: public xapplication
310 managed_window *main_frame;
311 doodle_view *draw_frame;
316 The actual creation of the subwindow is put in the <code>SetupResources()</code>
317 member funtion of the <code>doodle</code> class, right after the creation
318 of the top level window:
321 void doodle::SetupResources()
323 main_frame = new managed_window("Doodling with AXE");
324 main_frame->Map();
326 draw_frame = new doodle_view(main_frame);
327 draw_frame->Map();
331 Note that we have to <emph>Map</emph> the subwindow, just like the top level
332 window to make it visible.
336 Finally, we have to implement the event handler function of the <emph>Expose</emph>
337 event handler, <code>EV_Expose()</code>.
338 As you may have noticed, the <code>DrawRectangle()</code> is removed from the
339 <code>SetupResources()</code> function.
340 We now wait for an expose event from the X server to do the actual drawing.
341 Every time our window becomes 'exposed' the X server sends an <emph>Expose</emph>
342 event, to which we react by redrawing our rectangle in the window.
343 Here is the implementation of the <emph>Expose</emph> event handler:
347 int doodle_view::EV_Expose(XExposeEvent ev)
351 DrawRectangle(graphic, 50, 70, 200, 100);
357 Here you see the <code>gc</code> object and the <code>DrawRectangle()</code>
358 again, nearly the same as we previously had in the <code>SetupResources()</code>
359 function of the <code>doodle</code> class.
360 There is one difference: The pointer to the window (<code>main_frame-></code>
361 in our previous example) is removed from the function call.
362 This is because we are now inside a member funtion of the <code>doodle_view</code>
363 class and our window is passed implicitly as the <strong>this</strong> pointer
364 of the object itself.
368 The complete source code of the second doodle example is in the file
369 <strong>demos/doodle2.cpp</strong> of the <strong>AXE</strong> distribution.
370 If you compile and run doodle2, it should look like this:
373 <picture src='tutor2.2.png' eps='tutor2.2.eps'/>
376 You can now move the window all over the screen, resize it, move other windows
377 over the doodle window and the rectangle we draw in the little yellow window
384 <heading>Quitting the application</heading>
387 Up until now, we do not have a decent way to quit the doodle application.
388 You have to kill doodle by sending it a signal, like hitting ctrl-C on your
390 It's about time to remedy that situation.
391 There are many ways to close an application, like hitting a specific key
392 ('q' for quit might be a good one), clicking on the close button provided
393 by the window manager or providing a 'Quit' button or menu item in the
395 We will explore all of these possibilities in later chapters.
396 For now, to keep things simple, we will use the keyboard to quit doodle.
400 When the user hits a key while the focus is on the doodle window, the X server
401 sends a <emph>KeyPress</emph> event to our application.
402 This <emph>KeyPress</emph> event is dispatched by <strong>AXE</strong> to the
403 window on which the key was pressed, just as <emph>Expose</emph> events are
404 dispatched to the appropriate window.
405 In <strong>AXE</strong>, you can use any event to quit your application program.
406 All you need to do is return the value 0 (zero) from the event handler function.
407 You may have noticed in the previous section that the <code>EV_Expose()</code>
408 handler function returned the value 1 (one).
409 This return value tells the <strong>AXE</strong> library not the quit the program
410 after the event is handled.
411 If you return 0 (zero) from an event handler function, <strong>AXE</strong> will
412 destroy all your windows, close the display connection with the X server
413 and quit the application.
417 So, to quit doodle on a key stroke, we select <emph>KeyPress</emph> events
418 next to <emph>Expose</emph> events in our subwindow and override the
419 <code>EV_KeyPress()</code> event handler function.
420 To select <emph>KeyPress</emph> events, we add another <code>SelectInput()</code>
421 in our <code>doodle_view</code> contructor:
424 doodle_view(window *parent) : window (*parent, 10, 30, 300, 220)
426 SelectInput(ExposureMask, 1);
427 SelectInput(KeyPressMask, 1);
428 Background(color("lightyellow"));
432 Next, also in the <code>doodle_view</code> class declaration, we add the
433 declaration of the member function to handle the KeyPress events:
436 virtual int EV_KeyPress(XKeyEvent ev);
439 The implementation of the event handler is very trivial.
440 Just return the value 0 (zero) and doodle will close down when any key
441 is pressed in the light yellow window:
444 int doodle_view::EV_KeyPress(XKeyEvent ev)
450 Having any key at all, even the Ctrl or the CapsLock key, quit doodle is a bit
451 cruel, don;t you think.
452 So, let's restrain the quitting to just the lower case 'q' key.
453 When we get an event from the X server in one of our event handling functions,
454 all kinds of information about that event is passed in the <emph>ev</emph>
456 Until now, we silently ignored the <emph>ev</emph> parameter but is this
457 case we need it to find out which key was actually pressed.
458 There is no special provision in <strong>AXE</strong> to do this.
459 There are Xlib functions that do the job just fine.
460 The one Xlib function we use here is <code>XLookupKeysym()</code>, which extracts
461 information from the <code>XKeyEvent</code> structure and returns
462 a standardized code (a <emph>KeySym</emph>) of the key that was actually pressed.
463 For most 'ordinary' keys, the <emph>KeySym</emph> value is the same as the ASCII
465 We can use the 'q' key simply by checking the <emph>KeySym</emph> for the
469 int doodle_view::EV_KeyPress(XKeyEvent ev)
473 key = XLookupKeysym(&ev, ev.state & 1);
486 With this extra check, you can hit any key you want on doodle, but
487 only the 'q' key makes doodle leave the desktop.
488 You can find the complete source of this example in <strong>demos/doodle3.cpp</strong>.
498 tracking mouse events