Because the drawing engine API will most likely be the most used C-side
API of Ion, I would like some comments before implementing it. I've tried
to reach a balance between a) making the interface rather high-level yet
b) not requiring the drawing engine to specifically support every module
that wants to decorate its windows or making configuration of the drawing
engine difficult and the API bloated.
Please let me know if something is missing, if you think if the API is too
low or high level to  be usable and what changes you think would be needed
in order to implement some particular drawing engine you have in mind 
(e.g. a kludge that would use some existing theming engine) would be as
easily as possible without making things too complicated or moving too
much stuff into the drawing engine.

The general architechture is the following:
 * When an X window that we want to draw on is created, the drawing
   engine(s) are asked for a brush (its a bad name, but I couldn't
   think if anyther better concise) for the "style" of the window 
   with gr_get_brush. The actual drawing actions on that window are
   performed with this brush. In case of the default drawing module,
   all windows with the same style share the same brush, but e.g. an
   Xft module (yes, I will be removing Xft support from the standard
   drawing engine module and have no intentions of writing an Xft 
   module myself) could create a new brush for each window because it
   needs to store extra information.
   
 * Brushes may support substyle to change the appereance of things
   they draw depending on whether the object being draw on on is active, 
   tab is selected, and so on. Border sizes and fonts, however, are fixed
   for a  brush to keep calculations (outside the drawing engine) simple. 
   
 * Because of the fixed border sizes, "slave brushes" are needed to draw
   e.g. the tab bars. Again, in case of the standard module there is
   nothing special about these, but a more complicated module that needs
   to store per-window extra data might need to do some more.

 * What can be done with the brushes are:
    * Draw borders and get border sizes for placement calculations.
    * Draw strings and request simple text/font properties.
    * Draw single and multiple textboxes. Some modules might draw
      multiple textboxes/the tab-bar as a continuous area instead
      of multiple distinct boxes.
    * Set clipping rectangles for other operations. Queries need this.
    * Set window shapes. Floatframes need this for the tab-bar. There
      is a 'rough' parameter to the function which, if set, allows the
      module to apply extra decoration to the shape, e.g. superfluous
      corner smoothing.
    * Toggle background transparency. (I might still change this.)

   These functions should cover what other modules need to control of
   their appereance. The rest is up to the drawing engine and configuration
   files, but if you think something is missing, or if something is too
   low-level, please comment.

Ion's other modules, when they would something (substyle) or look for a
style/brush, would pass the drawing engine or brush a string of the form 
attr1-attr2-attr3-etc. The drawing engine should look for a style (or 
substyle) that has the longest initial match. E.g. the ionws module
would ask brush for the style 'frame-ionframe', but the drawing engine
could return brush for the style 'frame', because no more specific style has
been defined in the configuration file. (When looking for a style, if the
engine can't find any partially matching style, it should return brush for
some default style although modules must be able to handle the case that
there is no brush.)

To further clarify this, here's a draft of the new configuration file
format for the standard module. 

define_style("frame", {
    border_style = "inlaid",
    spacing = 1,
    highlight = 1,
    shadow = 1,
    padding = 2,
    padding_colour = "#123456", 
    highlight_colour = "#123456",
    shadow_colour = "#123456",
    background_colour = "#000000",
    
    substyle("active", {
        background_colour = "#123456",
        highlight_colour = "#123456",
        shadow_colour = "#123456",
    }),
})

define_style("frame-floatframe", {
    border_style = "double",
    ...
})

define_style("frame-tabbar", {
    border_style = "elevated",
    highlight = 1,
    shadow = 1,
    padding = 2,
    font="fixed",
    highlight_colour = "#123456",
    shadow_colour = "#123456",
    foreground_colour = "#000000",
    -- padding_colour assumed equal to background_colour
    background_colour = "#123456",

    substyle("active-selected", {
        background_colour = "#123456",
        highlight_colour = "#123456",
        shadow_colour = "#123456",
    }),

    substyle("*-unselected-urgent", {
        background_colour = "red",
        highlight_colour = "#123456",
        shadow_colour = "#123456",
    }),
    ...
})


Here's a draft of the actual C API:
    
/* Initilisation/deinitialisation */

extern WHooklist gr_get_brush_alt;

extern GrBrush *gr_get_brush(Window win, const WRectangle *geom,
                             const char *style);


extern GrBrush *grbrush_get_slave(GrBrush *draw, Window win, 
                                  const char *style);

extern void grbrush_release(GrBrush *brush, Window win);

/* Stylespecs are of the from attr1-attr2-etc. We require that each attr in
 * 'spec' matches the one at same index in 'attrib' when '*' matches anything.
 * The score increment for exact match is 2 and 1 for '*' match. If all
 * elements of 'spec' match those of 'attrib' exactly, the accumulated score 
 * is returned. Otherwise the matching fails and zero is returned. 
 * For example:
 *  
 *  spec        attrib              score
 *     foo-*-baz     foo-bar-baz        5
 *  foo-bar          foo-bar-baz        4
 *  foo-baz          foo-bar-baz        0
 */
extern uint gr_stylespec_score(const char *spec, const char *attrib);

/* Borders */

extern void grbrush_draw_border(GrBrush *brush, Window win, 
                                const WRectangle *geom,
                                const char *attrib);

extern void grbrush_border_widths(GrBrush *brush, 
                                  uint *top, uint *bottom,
                                  uint *left, uint *right);
extern uint grbrush_spacing(GrBrush *brush);

/* Strings */

extern void grbrush_draw_string(GrBrush *brush, Window win, int x, int y,
                                const char *str, int len, bool needfill,
                                const char *attrib);

extern bool grbrush_get_font_params(GrBrush *brush, 
                                    uint *height_ret,
                                    uint *max_width_ret,
                                    uint *baseline_ret);
extern uint grbrush_get_text_width(GrBrush *brush, const char *text, uint len);

/* Textboxes */

extern void grbrush_draw_textbox(GrBrush *brush, Window win, 
                                 const WRectangle *geom,
                                 const char *text, 
                                 const char *attr,
                                 bool needfill);

typedef struct{
    const char *text;
    int w; /* Should sum to geom->w */
    const char *attr;
} GrTextElem;

extern void grbrush_draw_textboxes(GrBrush *brush, Window win, 
                                   const WRectangle *geom,
                                   int n, const GrTextElem *elem, 
                                   bool needfill, const char *common_attrib);

/* Misc */

extern void grbrush_set_clipping_rectangle(GrBrush *brush, Window win,
                                           WRectangle geom);
extern void grbrush_clear_clipping_rectangle(GrBrush *brush, Window win);

/* Behaviour of the following two functions for "slave brushes" is undefined. 
 * If the parameter rough to grbrush_set_window_shape is set, the actual 
 * shape may be changed for corner smoothing and other superfluous effects.
 * (This feature is only used by floatframes.)
 */
extern void grbrush_set_window_shape(GrBrush *brush, bool rough,
                                     int n, const WRectangle *rects);
extern void grbrush_enable_transparency(GrBrush *brush, Window win, bool tr);

-- 
Tuomo

Reply via email to