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
= p_new(char, utf8len
);
63 if(iconv(iso2utf8
, &iso
, &len
, &utf8
, &utf8len
) == (size_t) -1)
65 perror("awesome: text conversion failed");
69 if(iconv_close(iso2utf8
))
70 warn("error closing iconv");
75 /** Get a draw context
76 * \param phys_screen physical screen id
78 * \param height height
79 * \return draw context ref
82 draw_context_new(Display
*disp
, int phys_screen
, int width
, int height
, Drawable dw
)
84 DrawCtx
*d
= p_new(DrawCtx
, 1);
87 d
->phys_screen
= phys_screen
;
90 d
->depth
= DefaultDepth(disp
, phys_screen
);
91 d
->visual
= DefaultVisual(disp
, phys_screen
);
93 d
->surface
= cairo_xlib_surface_create(disp
, dw
, d
->visual
, width
, height
);
94 d
->cr
= cairo_create(d
->surface
);
100 draw_context_delete(DrawCtx
*ctx
)
102 cairo_surface_destroy(ctx
->surface
);
103 cairo_destroy(ctx
->cr
);
107 /** Draw text into a draw context
112 * \param align alignment
113 * \param padding padding to add before drawing the text
114 * \param font font to use
115 * \param text text to draw
116 * \param fg foreground color
117 * \param bg background color
120 draw_text(DrawCtx
*ctx
,
124 XftFont
*font
, char *text
,
125 XColor fg
, XColor bg
)
130 cairo_font_face_t
*font_face
;
132 draw_rectangle(ctx
, area
, True
, bg
);
134 if(!(len
= olen
= a_strlen(text
)))
137 /* copy text to buffer */
138 buf
= a_strdup(text
);
139 /* try to convert it to UTF-8 */
140 if(!(buf
= draw_iso2utf8(buf
)))
143 /* check that the text is not too long */
144 while(len
&& (nw
= (draw_textwidth(ctx
->display
, font
, buf
)) + padding
* 2) > area
.width
)
147 /* we can't blindly null the char, we need to check if it's not part of
148 * a multi byte char: if mbtowc return -1, we know that we must go back
149 * in the string to find the beginning of the multi byte char */
150 while(mbtowc(NULL
, buf
+ len
, a_strlen(buf
+ len
)) < 0)
155 return; /* too long */
166 font_face
= cairo_ft_font_face_create_for_pattern(font
->pattern
);
167 cairo_set_font_face(ctx
->cr
, font_face
);
168 cairo_set_font_size(ctx
->cr
, font
->height
);
169 cairo_set_source_rgb(ctx
->cr
, fg
.red
/ 65535.0, fg
.green
/ 65535.0, fg
.blue
/ 65535.0);
174 cairo_move_to(ctx
->cr
, area
.x
+ padding
, area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
177 cairo_move_to(ctx
->cr
, area
.x
+ (area
.width
- nw
) + padding
,
178 area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
181 cairo_move_to(ctx
->cr
, area
.x
+ ((area
.width
- nw
) / 2) + padding
,
182 area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
186 cairo_show_text(ctx
->cr
, buf
);
188 cairo_font_face_destroy(font_face
);
192 /** Setup color-source for cairo (gradient or mono)
193 * \param ctx Draw context
194 * \param x x-offset of widget
195 * \param y y-offset of widget
196 * \param width width in pixels
197 * \param color color to use from 0%
198 * \param pcolor_center color at 50% of width
199 * \param pcolor_end color at 100% of width
200 * \return pat pattern or NULL; needs to get cairo_pattern_destroy()'ed;
203 setup_cairo_color_source(DrawCtx
*ctx
, int x
, int y
, int width
,
204 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
206 cairo_pattern_t
*pat
;
208 if(pcolor_center
|| pcolor_end
) /* draw a gradient */
210 pat
= cairo_pattern_create_linear(x
, y
, x
+ width
, y
);
212 cairo_pattern_add_color_stop_rgb(pat
, 0, color
.red
/ 65535.0,
213 color
.green
/ 65535.0, color
.blue
/ 65535.0);
215 cairo_pattern_add_color_stop_rgb(pat
, 0.5, pcolor_center
->red
/ 65535.0,
216 pcolor_center
->green
/ 65535.0, pcolor_center
->blue
/ 65535.0);
218 cairo_pattern_add_color_stop_rgb(pat
, 1, pcolor_end
->red
/ 65535.0,
219 pcolor_end
->green
/ 65535.0, pcolor_end
->blue
/ 65535.0);
221 cairo_pattern_add_color_stop_rgb(pat
, 1, color
.red
/ 65535.0,
222 color
.green
/ 65535.0, color
.blue
/ 65535.0);
223 cairo_set_source(ctx
->cr
, pat
);
226 else /* no gradient */
228 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
234 * \param ctx Draw context
235 * \param geometry geometry
236 * \param filled filled rectangle?
237 * \param color color to use
240 draw_rectangle(DrawCtx
*ctx
, Area geometry
, Bool filled
, XColor color
)
242 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
243 cairo_set_line_width(ctx
->cr
, 1.0);
244 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
247 cairo_rectangle(ctx
->cr
, geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
252 cairo_rectangle(ctx
->cr
, geometry
.x
+ 1, geometry
.y
, geometry
.width
- 1, geometry
.height
- 1);
253 cairo_stroke(ctx
->cr
);
256 /** Draw rectangle with gradient colors
257 * \param ctx Draw context
258 * \param geometry geometry
259 * \param fullwidth width of full bar in pixels
260 * \param filled filled rectangle?
261 * \param color color to use from 0%
262 * \param pcolor_center color at 50%
263 * \param pcolor_end color at 100%
266 draw_rectangle_gradient(DrawCtx
*ctx
, Area geometry
, int fullwidth
, Bool filled
,
267 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
269 cairo_pattern_t
*pat
;
271 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
272 cairo_set_line_width(ctx
->cr
, 1.0);
274 pat
= setup_cairo_color_source(ctx
, geometry
.x
, geometry
.y
, fullwidth
,
275 color
, pcolor_center
, pcolor_end
);
279 cairo_rectangle(ctx
->cr
, geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
284 cairo_rectangle(ctx
->cr
, geometry
.x
+ 1, geometry
.y
, geometry
.width
- 1, geometry
.height
- 1);
285 cairo_stroke(ctx
->cr
);
289 cairo_pattern_destroy(pat
);
292 /** Setup some cairo-things for drawing a graph
293 * \param ctx Draw context
296 draw_graph_setup(DrawCtx
*ctx
)
298 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
299 cairo_set_line_width(ctx
->cr
, 1.0);
300 /* without it, it can draw over the path on sharp angles (...too long lines) */
301 cairo_set_line_join (ctx
->cr
, CAIRO_LINE_JOIN_ROUND
);
304 * \param ctx Draw context
305 * \param x x-offset of widget
306 * \param y y-offset of widget
307 * \param w width in pixels
308 * \param from array of starting-point offsets to draw a graph-lines
309 * \param to array of end-point offsets to draw a graph-lines
310 * \param cur_index current position in data-array (cycles around)
311 * \param color color to use from 0%
312 * \param pcolor_center color at 50%
313 * \param pcolor_end color at 100%
316 draw_graph(DrawCtx
*ctx
, int x
, int y
, int w
, int *from
, int *to
, int cur_index
,
317 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
320 cairo_pattern_t
*pat
;
321 pat
= setup_cairo_color_source(ctx
, x
, y
, w
, color
, pcolor_center
, pcolor_end
);
326 cairo_move_to(ctx
->cr
, x
, y
- from
[cur_index
]);
327 cairo_line_to(ctx
->cr
, x
, y
- to
[cur_index
]);
334 cairo_stroke(ctx
->cr
);
337 cairo_pattern_destroy(pat
);
339 /** Draw a line into a graph-widget
340 * \param ctx Draw context
341 * \param x x-offset of widget
342 * \param y y-offset of widget
343 * \param w width in pixels
344 * \param to array of offsets to draw the line through...
345 * \param cur_index current position in data-array (cycles around)
346 * \param color color to use from 0%
347 * \param pcolor_center color at 50%
348 * \param pcolor_end color at 100%
351 draw_graph_line(DrawCtx
*ctx
, int x
, int y
, int w
, int *to
, int cur_index
,
352 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
355 int flag
= 0; /* used to prevent drawing a line from 0 to 0 values */
356 cairo_pattern_t
*pat
;
358 pat
= setup_cairo_color_source(ctx
, x
, y
, w
, color
, pcolor_center
, pcolor_end
);
360 /* x-1 (on the border), paints *from* the last point (... not included itself) */
361 /* makes sense when you assume there is already some line drawn to it. */
362 cairo_move_to(ctx
->cr
, x
- 1, y
- to
[cur_index
]);
364 for (i
= 0; i
< w
; i
++)
366 if (to
[cur_index
] > 0)
368 cairo_line_to(ctx
->cr
, x
, y
- to
[cur_index
]);
373 if(flag
) /* only draw from values > 0 to 0-values */
375 cairo_line_to(ctx
->cr
, x
, y
);
379 cairo_move_to(ctx
->cr
, x
, y
);
382 if (--cur_index
< 0) /* cycles around the index */
386 cairo_stroke(ctx
->cr
);
389 cairo_pattern_destroy(pat
);
393 draw_circle(DrawCtx
*ctx
, int x
, int y
, int r
, Bool filled
, XColor color
)
395 cairo_set_line_width(ctx
->cr
, 1.0);
396 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
398 cairo_new_sub_path(ctx
->cr
); /* don't draw from the old reference point to.. */
402 cairo_arc (ctx
->cr
, x
+ r
, y
+ r
, r
, 0, 2 * M_PI
);
406 cairo_arc (ctx
->cr
, x
+ r
, y
+ r
, r
- 1, 0, 2 * M_PI
);
408 cairo_stroke(ctx
->cr
);
411 void draw_image_from_argb_data(DrawCtx
*ctx
, int x
, int y
, int w
, int h
,
412 int wanted_h
, unsigned char *data
)
416 cairo_surface_t
*source
;
418 source
= cairo_image_surface_create_for_data(data
, CAIRO_FORMAT_ARGB32
, w
, h
, 0);
419 cr
= cairo_create(ctx
->surface
);
420 if(wanted_h
> 0 && h
> 0)
422 ratio
= (double) wanted_h
/ (double) h
;
423 cairo_scale(cr
, ratio
, ratio
);
424 cairo_set_source_surface(cr
, source
, x
/ ratio
, y
/ ratio
);
427 cairo_set_source_surface(cr
, source
, x
, y
);
431 cairo_surface_destroy(source
);
435 draw_image(DrawCtx
*ctx
, int x
, int y
, int wanted_h
, const char *filename
)
439 cairo_surface_t
*source
;
441 cairo_status_t cairo_st
;
443 source
= cairo_image_surface_create_from_png(filename
);
444 if((cairo_st
= cairo_surface_status(source
)))
446 warn("failed to draw image %s: %s\n", filename
, cairo_status_to_string(cairo_st
));
449 cr
= cairo_create (ctx
->surface
);
450 if(wanted_h
> 0 && (h
= cairo_image_surface_get_height(source
)) > 0)
452 ratio
= (double) wanted_h
/ (double) h
;
453 cairo_scale(cr
, ratio
, ratio
);
454 cairo_set_source_surface(cr
, source
, x
/ ratio
, y
/ ratio
);
457 cairo_set_source_surface(cr
, source
, x
, y
);
461 cairo_surface_destroy(source
);
465 draw_get_image_size(const char *filename
)
467 Area size
= { -1, -1, -1, -1, NULL
};
468 cairo_surface_t
*surface
;
469 cairo_status_t cairo_st
;
471 surface
= cairo_image_surface_create_from_png(filename
);
472 if((cairo_st
= cairo_surface_status(surface
)))
473 warn("failed to get image size %s: %s\n", filename
, cairo_status_to_string(cairo_st
));
476 cairo_image_surface_get_width(surface
);
479 size
.width
= cairo_image_surface_get_width(surface
);
480 size
.height
= cairo_image_surface_get_height(surface
);
481 cairo_surface_destroy(surface
);
488 draw_rotate(DrawCtx
*ctx
, int screen
, double angle
, int tx
, int ty
)
490 cairo_surface_t
*surface
, *source
;
492 Drawable newdrawable
;
494 newdrawable
= XCreatePixmap(ctx
->display
,
495 RootWindow(ctx
->display
, screen
),
496 ctx
->height
, ctx
->width
,
498 surface
= cairo_xlib_surface_create(ctx
->display
, newdrawable
, ctx
->visual
, ctx
->height
, ctx
->width
);
499 source
= cairo_xlib_surface_create(ctx
->display
, ctx
->drawable
, ctx
->visual
, ctx
->width
, ctx
->height
);
500 cr
= cairo_create (surface
);
502 cairo_translate(cr
, tx
, ty
);
503 cairo_rotate(cr
, angle
);
505 cairo_set_source_surface(cr
, source
, 0.0, 0.0);
509 cairo_surface_destroy(source
);
510 cairo_surface_destroy(surface
);
516 draw_textwidth(Display
*disp
, XftFont
*font
, char *text
)
518 cairo_surface_t
*surface
;
520 cairo_font_face_t
*font_face
;
521 cairo_text_extents_t te
;
526 surface
= cairo_xlib_surface_create(disp
, DefaultScreen(disp
),
527 DefaultVisual(disp
, DefaultScreen(disp
)),
528 DisplayWidth(disp
, DefaultScreen(disp
)),
529 DisplayHeight(disp
, DefaultScreen(disp
)));
530 cr
= cairo_create(surface
);
531 font_face
= cairo_ft_font_face_create_for_pattern(font
->pattern
);
532 cairo_set_font_face(cr
, font_face
);
533 cairo_set_font_size(cr
, font
->height
);
534 cairo_text_extents(cr
, text
, &te
);
536 cairo_surface_destroy(surface
);
537 cairo_font_face_destroy(font_face
);
539 return MAX(te
.x_advance
, te
.width
);
543 draw_get_align(const char *align
)
545 if(!a_strncmp(align
, "left", 4))
547 else if(!a_strncmp(align
, "center", 6))
549 else if(!a_strncmp(align
, "right", 5))
555 /** Initialize an X color
556 * \param disp display ref
557 * \param screen Physical screen number
558 * \param colstr Color specification
559 * \return XColor struct
562 draw_color_new(Display
*disp
, int phys_screen
, const char *colstr
)
564 XColor screenColor
, exactColor
;
566 if(!XAllocNamedColor(disp
,
567 DefaultColormap(disp
, phys_screen
),
571 eprint("awesome: error, cannot allocate color '%s'\n", colstr
);
576 /** Remove a area from a list of them,
577 * spliting the space between several area that
579 * \param head list head
580 * \param elem area to remove
583 area_list_remove(Area
**head
, Area
*elem
)
585 Area
*r
, inter
, *extra
;
587 area_list_detach(head
, elem
);
589 for(r
= *head
; r
; r
= r
->next
)
590 if(area_intersect_area(*r
, *elem
))
592 /* remove it from the list */
593 area_list_detach(head
, r
);
595 inter
= area_get_intersect_area(*r
, *elem
);
597 if(AREA_LEFT(inter
) > AREA_LEFT(*r
))
599 extra
= p_new(Area
, 1);
602 extra
->width
= AREA_LEFT(inter
) - r
->x
;
603 extra
->height
= r
->height
;
604 area_list_push(head
, extra
);
607 if(AREA_TOP(inter
) > AREA_TOP(*r
))
609 extra
= p_new(Area
, 1);
612 extra
->width
= r
->width
;
613 extra
->height
= AREA_TOP(inter
) - r
->y
;
614 area_list_push(head
, extra
);
617 if(AREA_RIGHT(inter
) < AREA_RIGHT(*r
))
619 extra
= p_new(Area
, 1);
620 extra
->x
= AREA_RIGHT(inter
);
622 extra
->width
= AREA_RIGHT(*r
) - AREA_RIGHT(inter
);
623 extra
->height
= r
->height
;
624 area_list_push(head
, extra
);
627 if(AREA_BOTTOM(inter
) < AREA_BOTTOM(*r
))
629 extra
= p_new(Area
, 1);
631 extra
->y
= AREA_BOTTOM(inter
);
632 extra
->width
= r
->width
;
633 extra
->height
= AREA_BOTTOM(*r
) - AREA_BOTTOM(inter
);
634 area_list_push(head
, extra
);
639 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80