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