Matchbox Internals Documentation - v0.1 - mallum@o-hand.com =========================================================== Introduction ==== This document describes and discusses the internal source structure of the Matchbox Window Manager, a unique X11 window manager with a restrictive PDA style of window management. Matchbox aims to be small, fast, light and flexible with both compile and run time features so its UI tailored to fit different embedded type platforms. Matchbox has been developed over the past 3 years and much has been learnt from its development. Here I present a fairly comprehensive overiew of the source codes inner working in the hope of guideing any potential contributers. Coding style ==== Matchbox is written in C with the source approximating to GNU style coding standards. All functions prefixed with the source file name they live in, for example wm_new() lives in wm.c. Each .c file has an associated header file listing any shared functionality. 'Global' structures, enums and defines are in structs.h. Overview ==== The window manager starting point is in main.c . Initially, a new Wm 'object', used throughout to store various environment and global state data, is created via wm_new(). This wm_new() function initially loads any user configuration data ( via wm_load_config() ) from command line parameters and/or XResources. Various Wm X11 environment parameters are then initialized ( such as display size, window manager event setup ) before initializing the various 'subsystems' - EWMH, key shortcuts, various compile time features etc. Finally a new Wm object is returned which is then used extensably throughout the programs life cycle. Any pre-existing windows are then processed ( wm_init_existing() ) and the program then enters its main loop ( wm_event_loop() ). Like all window managers, Matchbox is event based. Events come mainly in the form of redirected ones from clients attempting actions that are to be managed by the window manager. For example an application attempting to resize will cause a 'configure request' event to get sent to the window manager, which can then decide how this is honored or handled. When a new client window appears, Matchbox receives a map notify event with the new window ID. Assuming this ID is unknown to matchbox, a new 'Client' object will be created by wm_make_new_client(). This function will examine various window properties and based on these decide the 'type' of Client object to be initialized. Types include Application, Dialog and panel windows, with each needing to be managed slightly differently. To achieve this a psudeo object orientated C style is used where each client type has its own class which inherits from a base client class. Pointers to functions within the Client struct help provide this functionality. All created client objects are stored in a circular doublely linked list, there are various pointers in the top level Wm object that reference clients in this list. Once a client is created various core methods are called in sequence to initialize and finally realize the then docorated framed client; - Properties relevant to the window manager are communicated ( set ) on the client. See ewmh_set_allowed_actions() . - The clients configure() method is called to calculate suitable client geometry. - The client window is then re-parented. It becomes a child window of a matchbox frame window. It is the frame window that decorations are painted on to. - The client window and frame are resized and positioned to fit the matchbox management style ( from the previously created geometry ). - The client show() method is called which will ultimately realize the client. Its very likely an initial call of this method with render the frame decorations. Rendering will call into theme engine code. - Finally global root window properties are updated. During the lifetime of a client it will generate other events ( such as resizing ) which will ultimately get mapped into the client objects methods and managed. The other source of events is user interaction. This could be from a key shortcut press or window decoration button press. Key presses are handled by wm_handle_keypress(), which looks to see if an action has been defined for the received key press ( defined in keys.c ). If this is true the action is carried out by calling relevant 'global' functions in wm.c and client_common.c . Button presses are more complicated, wm_handle_button_event() will detect where the press happened and if required call a resulting client objects button_press() method. client_common.c contains code to deal with button management. Window decoration buttons are simple objects created by client_button_new(). Each client object has a list referencing its button objects. client_button_do_ops() helps a client manage it buttons and provide visual states. A final source of events is X messages, for example a request to show the desktop from an external application will send a X message to the root window. These are handled by wm_handle_client_message() which handles matchbox specific messages before calling ewmh_handle_root_message() which handles standard EWMH ones. Wm.c also has various functions for 'global' operations on clients such as resizing/repositioning all clients. Numerous points throughout the codebase will use this. Note a diagram reprenting visually what has been discussed in this overview is available via; http://matchbox.handhelds.org/documentation/developers/internals/internals.png File Structure ==== structs.h All global structures ( aka objects ), defines, enums are defined here. wm.c The window manager 'core'. Contains the event loop and event handlers and various functions for dealing with global state, such as re-stacking and resizing all clients. base_client.c base client 'class', all other clients source files ( or types ) are derived from this optionally overriding various methods. dockbar_client.c Derived 'class' for handling panel window types. main_client.c Derived 'class' for handling application windows select_client.c Handles the application drop down selection menu toolbar_client.c Derived 'class' for handling toolbar windows ( eg xkbd, input windows ). desktop_client.c Derived 'class' for handling desktop windows. dialog_client.c Derived 'class' for handling dialog windows client_common.c Various client utility and misc funcs common to all client types. This includes various basic window communication handling, decoration caching and button management. ewmh.c Provides functions for freedesktop.org extended window manager hint protocols. composite-engine.c composite engine , can be compiled out. See below for a further explanation of its workings. keys.c Provides key shortcut handling ( can be compiled out ) xml.c Simple xml parser used by mbtheme.c ( can be compiled out for stand-alone or expat used instead ) list.c Simple list api, used by mbtheme for storing and referencing the various theme resources. misc.c misc stuff - include strsep implementation for building on systems that lack it ( eg solaris ), error trappping / reporting funcs, signal handling. mbtheme.c Provides the complex libmatchbox pixbuf based theme engine. See below for a further explanation. mbtheme-standalone.c Provides a alternative smaller and simpler X primitives based theme engine. This is used by stand-alone builds. main.c Program main entry, init and startup. 'Objects' ==== structs.h Client Object methods Client* XX_client_new (Wm *w, Window win); Creates a client object. Will set up structure function pointers. void XX_client_configure (Client *c); Calculates client size and position variables. void XX_client_reparent (Client *c); Is responsable for re-parenting the client window to the decoration frame window. void XX_client_redraw (Client *c, Bool use_cache); Renders the window decorations void XX_client_button_press (Client *c, XButtonEvent *e); Handles any reported button events. void XX_client_move_resize (Client *c); Safely moves and resizes a client ( to positions set by client_configure ) or other. void XX_client_get_coverage (Client *c, int *x, int *y, int *w, int *h); returns the display area covered by the client and its decoration frame. void XX_client_hide (Client *c); Hides ( unmaps ) a client. void XX_client_show (Client *c); Shows ( maps ) a client. void XX_client_destroy (Client *c); Removes a client from the circular list cleanly, freeing up any resources. Themeing ==== 'Themeing' is the name used to describe the painting of window frame decorations. Matchbox provides two themeing implementations or 'engines'; a basic small implementation which uses X drawing primitives and no configuration files ( mbtheme-standalone.c ) and an advanced implementation which uses libMatchbox pixbufs for painting and advanced xml based configuration files. Both engines export a very similar api which can be used by the core of the window manager to render defined window decorations. I shall explain the operation of the advanced engine, but the actual structure also applys to the basic ( standalone ) implementation. The theme engine is initially created with mbtheme_init(), this will create and initialize an engine specific theme structure. A suitable theme.xml file will then be located and parsed. The parsing ( see parse_*_tag ) populates more of the mbtheme structure with lists of theme resources ( such as fonts, or loaded image data ) and layout data for each window type decoration. A window that needs its decorations painting will call theme_frame_paint(), specifying the frame type to be painted and its dimensions relative to the decoration window frame. The frame type is basically the window type extended with an actual edge ( eg FRAME_MAIN_NORTH for the title area, FRAME_MAIN_EAST for the right edge ). The function the builds up the required decoration from the lists of layout and resource data before rendering to the clients backing pixmap. The function will also create any masks needed for window shaping. Separate function is provided for the painting of buttons ( theme_frame_button_paint() ) and the window select menu ( theme_frame_menu_paint() ). Both these and theme_frame_paint() share various utility calls for painting ( _theme_paint_core(), _theme_paint_gradient() etc ) The theme engine also provides various calls for client to code to get specified decoration dimensions ( theme_frame_menu_get_dimentions(), theme_frame_defined_width_get(), theme_frame_defined_height_get() ) When the theme is changed, mbtheme_switch() will be called. This function will free all current defined theme resources and in effect re theme_init() itself with the new theme before repainting window decorations. Compositing ==== Matchbox window manager optionally provides compile time support for the new X Composite extension ( with associated Damage and Fixes extensions ). With this active matchbox manages not just window positions etc but also how the window contents are rendered to the display - this allows for effects like shadowing and translucency. The compositing api is at the time of writing considered quite experimental and therefor unstable. Because of this the composite code is kept quite un-intrusively self contained in composite-engine.c. The composite functions to a degree map onto client object methods. The composite engine initiates itself via comp_engine_init(). This function checks for available extensions, sets up the server for composite style rendering ( via XCompositeRedirectSubwindows() ) and initializes composite related Wm resources. Back buffers of all window type contents ( this includes override redirect windows, of which matchbox now has to track, see wm_handle_map_notify() ) are now stored in client structures via 32bit render pictures for visible clients ( see comp_engine_client_show(), comp_engine_client_hide(), comp_engine_client_init() ). Accumulated Damage events provide information on what areas of the display needs to be updated. This information is used with the client back buffers and Xfixes regions to efficiently update the display ( see comp_engine_render() ). It also adds effects such as shadowing and basic translucency. Optimizations ==== Some optimizations matchbox uses; - The theme engine caches a 'base' matchbox pixbuf representation of main application window type declarations. This means a new main window needs just text painting rather than its entire decoration layers being built up. This cache will be cleared if the application window size changes or the theme changes. The cache is also used to enable button window pixbuf compositing. - Decoration painting is done via XSetWindowBackground with a theme engine created pixmap, that is immediately free after the paint. Painting to the window background lets the server handle exposes and results in simpler smaller code with a 'smoother' appearance. - Compositing only back buffers visible windows - ie the top of the stack to save memory. Pictures not visible are freed. Composite Gaussian shadows are pregenerated and cut into tiles on start up. The shadows are then built up from the tiles - Matchbox's small binary size comes from careful coding and the fact that due to its style of restrictive window management it avoids much of the code needed by a desktop window manager. Further reading ==== Matchbox WM source http://cvs.handhelds.org/cgi-bin/viewcvs.cgi/apps/matchbox/matchbox-window-manager/src/ Matchbox Developer documentation. http://matchbox.handhelds.org/developer.shtml Matchbox X Communication specifics ( ie freedesktop.org standards support ) http://matchbox.handhelds.org/documentation/developers/matchbox-key.txt libMatchbox API documentation. http://matchbox.handhelds.org/documentation/developers/api/