2 * draw.c - draw functions
4 * Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <cairo-xlib.h>
33 #include "common/util.h"
36 draw_iso2utf8(char *iso
)
42 if(!(len
= a_strlen(iso
)))
45 if(!a_strcmp(nl_langinfo(CODESET
), "UTF-8"))
48 iso2utf8
= iconv_open("UTF-8", nl_langinfo(CODESET
));
49 if(iso2utf8
== (iconv_t
) -1)
52 warn("unable to convert text from %s to UTF-8, not available",
53 nl_langinfo(CODESET
));
55 perror("awesome: unable to convert text");
60 utf8len
= (3 * len
) / 2 + 1;
61 utf8
= utf8p
= p_new(char, utf8len
);
63 if(iconv(iso2utf8
, &iso
, &len
, &utf8
, &utf8len
) == (size_t) -1)
65 perror("awesome: text conversion failed");
70 if(iconv_close(iso2utf8
))
71 warn("error closing iconv");
76 /** Get a draw context
77 * \param phys_screen physical screen id
79 * \param height height
80 * \return draw context ref
83 draw_context_new(Display
*disp
, int phys_screen
, int width
, int height
, Drawable dw
)
85 DrawCtx
*d
= p_new(DrawCtx
, 1);
88 d
->phys_screen
= phys_screen
;
91 d
->depth
= DefaultDepth(disp
, phys_screen
);
92 d
->visual
= DefaultVisual(disp
, phys_screen
);
94 d
->surface
= cairo_xlib_surface_create(disp
, dw
, d
->visual
, width
, height
);
95 d
->cr
= cairo_create(d
->surface
);
101 draw_context_delete(DrawCtx
*ctx
)
103 cairo_surface_destroy(ctx
->surface
);
104 cairo_destroy(ctx
->cr
);
108 /** Draw text into a draw context
113 * \param align alignment
114 * \param padding padding to add before drawing the text
115 * \param font font to use
116 * \param text text to draw
117 * \param fg foreground color
118 * \param bg background color
121 draw_text(DrawCtx
*ctx
,
125 XftFont
*font
, char *text
,
126 XColor fg
, XColor bg
)
130 char *buf
= NULL
, *utf8
= NULL
;
131 cairo_font_face_t
*font_face
;
133 draw_rectangle(ctx
, area
, True
, bg
);
135 if(!(len
= olen
= a_strlen(text
)))
138 /* try to convert it to UTF-8 */
139 if((utf8
= draw_iso2utf8(text
)))
142 len
= olen
= a_strlen(buf
);
145 buf
= a_strdup(text
);
147 /* check that the text is not too long */
148 while(len
&& (nw
= (draw_textwidth(ctx
->display
, font
, buf
)) + padding
* 2) > area
.width
)
151 /* we can't blindly null the char, we need to check if it's not part of
152 * a multi byte char: if mbtowc return -1, we know that we must go back
153 * in the string to find the beginning of the multi byte char */
154 while(mbtowc(NULL
, buf
+ len
, a_strlen(buf
+ len
)) < 0)
159 return; /* too long */
170 font_face
= cairo_ft_font_face_create_for_pattern(font
->pattern
);
171 cairo_set_font_face(ctx
->cr
, font_face
);
172 cairo_set_font_size(ctx
->cr
, font
->height
);
173 cairo_set_source_rgb(ctx
->cr
, fg
.red
/ 65535.0, fg
.green
/ 65535.0, fg
.blue
/ 65535.0);
178 cairo_move_to(ctx
->cr
, area
.x
+ padding
, area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
181 cairo_move_to(ctx
->cr
, area
.x
+ (area
.width
- nw
) + padding
,
182 area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
185 cairo_move_to(ctx
->cr
, area
.x
+ ((area
.width
- nw
) / 2) + padding
,
186 area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
190 cairo_show_text(ctx
->cr
, buf
);
192 cairo_font_face_destroy(font_face
);
197 /** Setup color-source for cairo (gradient or mono)
198 * \param ctx Draw context
199 * \param x x-offset of widget
200 * \param y y-offset of widget
201 * \param width width in pixels
202 * \param color color to use from 0%
203 * \param pcolor_center color at 50% of width
204 * \param pcolor_end color at 100% of width
205 * \return pat pattern or NULL; needs to get cairo_pattern_destroy()'ed;
208 setup_cairo_color_source(DrawCtx
*ctx
, int x
, int y
, int width
,
209 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
211 cairo_pattern_t
*pat
;
213 if(pcolor_center
|| pcolor_end
) /* draw a gradient */
215 pat
= cairo_pattern_create_linear(x
, y
, x
+ width
, y
);
217 cairo_pattern_add_color_stop_rgb(pat
, 0, color
.red
/ 65535.0,
218 color
.green
/ 65535.0, color
.blue
/ 65535.0);
220 cairo_pattern_add_color_stop_rgb(pat
, 0.5, pcolor_center
->red
/ 65535.0,
221 pcolor_center
->green
/ 65535.0, pcolor_center
->blue
/ 65535.0);
223 cairo_pattern_add_color_stop_rgb(pat
, 1, pcolor_end
->red
/ 65535.0,
224 pcolor_end
->green
/ 65535.0, pcolor_end
->blue
/ 65535.0);
226 cairo_pattern_add_color_stop_rgb(pat
, 1, color
.red
/ 65535.0,
227 color
.green
/ 65535.0, color
.blue
/ 65535.0);
228 cairo_set_source(ctx
->cr
, pat
);
231 else /* no gradient */
233 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
239 * \param ctx Draw context
240 * \param geometry geometry
241 * \param filled filled rectangle?
242 * \param color color to use
245 draw_rectangle(DrawCtx
*ctx
, Area geometry
, Bool filled
, XColor color
)
247 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
248 cairo_set_line_width(ctx
->cr
, 1.0);
249 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
252 cairo_rectangle(ctx
->cr
, geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
257 cairo_rectangle(ctx
->cr
, geometry
.x
+ 1, geometry
.y
, geometry
.width
- 1, geometry
.height
- 1);
258 cairo_stroke(ctx
->cr
);
261 /** Draw rectangle with gradient colors
262 * \param ctx Draw context
263 * \param geometry geometry
264 * \param fullwidth width of full bar in pixels
265 * \param filled filled rectangle?
266 * \param color color to use from 0%
267 * \param pcolor_center color at 50%
268 * \param pcolor_end color at 100%
271 draw_rectangle_gradient(DrawCtx
*ctx
, Area geometry
, int fullwidth
, Bool filled
,
272 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
274 cairo_pattern_t
*pat
;
276 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
277 cairo_set_line_width(ctx
->cr
, 1.0);
279 pat
= setup_cairo_color_source(ctx
, geometry
.x
, geometry
.y
, fullwidth
,
280 color
, pcolor_center
, pcolor_end
);
284 cairo_rectangle(ctx
->cr
, geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
289 cairo_rectangle(ctx
->cr
, geometry
.x
+ 1, geometry
.y
, geometry
.width
- 1, geometry
.height
- 1);
290 cairo_stroke(ctx
->cr
);
294 cairo_pattern_destroy(pat
);
297 /** Setup some cairo-things for drawing a graph
298 * \param ctx Draw context
301 draw_graph_setup(DrawCtx
*ctx
)
303 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
304 cairo_set_line_width(ctx
->cr
, 1.0);
305 /* without it, it can draw over the path on sharp angles (...too long lines) */
306 cairo_set_line_join (ctx
->cr
, CAIRO_LINE_JOIN_ROUND
);
309 * \param ctx Draw context
310 * \param x x-offset of widget
311 * \param y y-offset of widget
312 * \param w width in pixels
313 * \param from array of starting-point offsets to draw a graph-lines
314 * \param to array of end-point offsets to draw a graph-lines
315 * \param cur_index current position in data-array (cycles around)
316 * \param color color to use from 0%
317 * \param pcolor_center color at 50%
318 * \param pcolor_end color at 100%
321 draw_graph(DrawCtx
*ctx
, int x
, int y
, int w
, int *from
, int *to
, int cur_index
,
322 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
325 cairo_pattern_t
*pat
;
326 pat
= setup_cairo_color_source(ctx
, x
, y
, w
, color
, pcolor_center
, pcolor_end
);
331 cairo_move_to(ctx
->cr
, x
, y
- from
[cur_index
]);
332 cairo_line_to(ctx
->cr
, x
, y
- to
[cur_index
]);
339 cairo_stroke(ctx
->cr
);
342 cairo_pattern_destroy(pat
);
344 /** Draw a line into a graph-widget
345 * \param ctx Draw context
346 * \param x x-offset of widget
347 * \param y y-offset of widget
348 * \param w width in pixels
349 * \param to array of offsets to draw the line through...
350 * \param cur_index current position in data-array (cycles around)
351 * \param color color to use from 0%
352 * \param pcolor_center color at 50%
353 * \param pcolor_end color at 100%
356 draw_graph_line(DrawCtx
*ctx
, int x
, int y
, int w
, int *to
, int cur_index
,
357 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
360 int flag
= 0; /* used to prevent drawing a line from 0 to 0 values */
361 cairo_pattern_t
*pat
;
363 pat
= setup_cairo_color_source(ctx
, x
, y
, w
, color
, pcolor_center
, pcolor_end
);
365 /* x-1 (on the border), paints *from* the last point (... not included itself) */
366 /* makes sense when you assume there is already some line drawn to it. */
367 cairo_move_to(ctx
->cr
, x
- 1, y
- to
[cur_index
]);
369 for (i
= 0; i
< w
; i
++)
371 if (to
[cur_index
] > 0)
373 cairo_line_to(ctx
->cr
, x
, y
- to
[cur_index
]);
378 if(flag
) /* only draw from values > 0 to 0-values */
380 cairo_line_to(ctx
->cr
, x
, y
);
384 cairo_move_to(ctx
->cr
, x
, y
);
387 if (--cur_index
< 0) /* cycles around the index */
391 cairo_stroke(ctx
->cr
);
394 cairo_pattern_destroy(pat
);
398 draw_circle(DrawCtx
*ctx
, int x
, int y
, int r
, Bool filled
, XColor color
)
400 cairo_set_line_width(ctx
->cr
, 1.0);
401 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
403 cairo_new_sub_path(ctx
->cr
); /* don't draw from the old reference point to.. */
407 cairo_arc (ctx
->cr
, x
+ r
, y
+ r
, r
, 0, 2 * M_PI
);
411 cairo_arc (ctx
->cr
, x
+ r
, y
+ r
, r
- 1, 0, 2 * M_PI
);
413 cairo_stroke(ctx
->cr
);
416 void draw_image_from_argb_data(DrawCtx
*ctx
, int x
, int y
, int w
, int h
,
417 int wanted_h
, unsigned char *data
)
421 cairo_surface_t
*source
;
423 source
= cairo_image_surface_create_for_data(data
, CAIRO_FORMAT_ARGB32
, w
, h
, 0);
424 cr
= cairo_create(ctx
->surface
);
425 if(wanted_h
> 0 && h
> 0)
427 ratio
= (double) wanted_h
/ (double) h
;
428 cairo_scale(cr
, ratio
, ratio
);
429 cairo_set_source_surface(cr
, source
, x
/ ratio
, y
/ ratio
);
432 cairo_set_source_surface(cr
, source
, x
, y
);
436 cairo_surface_destroy(source
);
440 draw_image(DrawCtx
*ctx
, int x
, int y
, int wanted_h
, const char *filename
)
444 cairo_surface_t
*source
;
446 cairo_status_t cairo_st
;
448 source
= cairo_image_surface_create_from_png(filename
);
449 if((cairo_st
= cairo_surface_status(source
)))
451 warn("failed to draw image %s: %s\n", filename
, cairo_status_to_string(cairo_st
));
454 cr
= cairo_create (ctx
->surface
);
455 if(wanted_h
> 0 && (h
= cairo_image_surface_get_height(source
)) > 0)
457 ratio
= (double) wanted_h
/ (double) h
;
458 cairo_scale(cr
, ratio
, ratio
);
459 cairo_set_source_surface(cr
, source
, x
/ ratio
, y
/ ratio
);
462 cairo_set_source_surface(cr
, source
, x
, y
);
466 cairo_surface_destroy(source
);
470 draw_get_image_size(const char *filename
)
472 Area size
= { -1, -1, -1, -1, NULL
};
473 cairo_surface_t
*surface
;
474 cairo_status_t cairo_st
;
476 surface
= cairo_image_surface_create_from_png(filename
);
477 if((cairo_st
= cairo_surface_status(surface
)))
478 warn("failed to get image size %s: %s\n", filename
, cairo_status_to_string(cairo_st
));
481 cairo_image_surface_get_width(surface
);
484 size
.width
= cairo_image_surface_get_width(surface
);
485 size
.height
= cairo_image_surface_get_height(surface
);
486 cairo_surface_destroy(surface
);
493 draw_rotate(DrawCtx
*ctx
, int screen
, double angle
, int tx
, int ty
)
495 cairo_surface_t
*surface
, *source
;
497 Drawable newdrawable
;
499 newdrawable
= XCreatePixmap(ctx
->display
,
500 RootWindow(ctx
->display
, screen
),
501 ctx
->height
, ctx
->width
,
503 surface
= cairo_xlib_surface_create(ctx
->display
, newdrawable
, ctx
->visual
, ctx
->height
, ctx
->width
);
504 source
= cairo_xlib_surface_create(ctx
->display
, ctx
->drawable
, ctx
->visual
, ctx
->width
, ctx
->height
);
505 cr
= cairo_create (surface
);
507 cairo_translate(cr
, tx
, ty
);
508 cairo_rotate(cr
, angle
);
510 cairo_set_source_surface(cr
, source
, 0.0, 0.0);
514 cairo_surface_destroy(source
);
515 cairo_surface_destroy(surface
);
521 draw_textwidth(Display
*disp
, XftFont
*font
, char *text
)
523 cairo_surface_t
*surface
;
525 cairo_font_face_t
*font_face
;
526 cairo_text_extents_t te
;
531 surface
= cairo_xlib_surface_create(disp
, DefaultScreen(disp
),
532 DefaultVisual(disp
, DefaultScreen(disp
)),
533 DisplayWidth(disp
, DefaultScreen(disp
)),
534 DisplayHeight(disp
, DefaultScreen(disp
)));
535 cr
= cairo_create(surface
);
536 font_face
= cairo_ft_font_face_create_for_pattern(font
->pattern
);
537 cairo_set_font_face(cr
, font_face
);
538 cairo_set_font_size(cr
, font
->height
);
539 cairo_text_extents(cr
, text
, &te
);
541 cairo_surface_destroy(surface
);
542 cairo_font_face_destroy(font_face
);
544 return MAX(te
.x_advance
, te
.width
);
548 draw_get_align(const char *align
)
550 if(!a_strncmp(align
, "left", 4))
552 else if(!a_strncmp(align
, "center", 6))
554 else if(!a_strncmp(align
, "right", 5))
560 /** Initialize an X color
561 * \param disp display ref
562 * \param screen Physical screen number
563 * \param colstr Color specification
564 * \return XColor struct
567 draw_color_new(Display
*disp
, int phys_screen
, const char *colstr
, XColor
*color
)
572 if(!(ret
= XAllocNamedColor(disp
,
573 DefaultColormap(disp
, phys_screen
),
577 warn("awesome: error, cannot allocate color '%s'\n", colstr
);
582 /** Remove a area from a list of them,
583 * spliting the space between several area that
585 * \param head list head
586 * \param elem area to remove
589 area_list_remove(Area
**head
, Area
*elem
)
591 Area
*r
, inter
, *extra
, *rnext
;
593 for(r
= *head
; r
; r
= rnext
)
596 if(area_intersect_area(*r
, *elem
))
598 /* remove it from the list */
599 area_list_detach(head
, r
);
601 inter
= area_get_intersect_area(*r
, *elem
);
603 if(AREA_LEFT(inter
) > AREA_LEFT(*r
))
605 extra
= p_new(Area
, 1);
608 extra
->width
= AREA_LEFT(inter
) - r
->x
;
609 extra
->height
= r
->height
;
610 area_list_append(head
, extra
);
613 if(AREA_TOP(inter
) > AREA_TOP(*r
))
615 extra
= p_new(Area
, 1);
618 extra
->width
= r
->width
;
619 extra
->height
= AREA_TOP(inter
) - r
->y
;
620 area_list_append(head
, extra
);
623 if(AREA_RIGHT(inter
) < AREA_RIGHT(*r
))
625 extra
= p_new(Area
, 1);
626 extra
->x
= AREA_RIGHT(inter
);
628 extra
->width
= AREA_RIGHT(*r
) - AREA_RIGHT(inter
);
629 extra
->height
= r
->height
;
630 area_list_append(head
, extra
);
633 if(AREA_BOTTOM(inter
) < AREA_BOTTOM(*r
))
635 extra
= p_new(Area
, 1);
637 extra
->y
= AREA_BOTTOM(inter
);
638 extra
->width
= r
->width
;
639 extra
->height
= AREA_BOTTOM(*r
) - AREA_BOTTOM(inter
);
640 area_list_append(head
, extra
);
643 /* delete the elem since we removed it from the list */
649 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80