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"
35 /** Convert text from any charset to UTF-8 using iconv
36 * \param iso the ISO string to convert
37 * \return NULL if error, otherwise pointer to the new converted string
40 draw_iso2utf8(char *iso
)
46 if(!(len
= a_strlen(iso
)))
49 if(!a_strcmp(nl_langinfo(CODESET
), "UTF-8"))
52 iso2utf8
= iconv_open("UTF-8", nl_langinfo(CODESET
));
53 if(iso2utf8
== (iconv_t
) -1)
56 warn("unable to convert text from %s to UTF-8, not available",
57 nl_langinfo(CODESET
));
59 perror("awesome: unable to convert text");
64 utf8len
= (3 * len
) / 2 + 1;
65 utf8
= utf8p
= p_new(char, utf8len
);
67 if(iconv(iso2utf8
, &iso
, &len
, &utf8
, &utf8len
) == (size_t) -1)
69 perror("awesome: text conversion failed");
74 if(iconv_close(iso2utf8
))
75 warn("error closing iconv");
80 /** Get a draw context
81 * \param disp Display ref
82 * \param phys_screen physical screen id
84 * \param height height
85 * \param dw Drawable object to store in DrawCtx
86 * \return draw context ref
89 draw_context_new(Display
*disp
, int phys_screen
, int width
, int height
, Drawable dw
)
91 DrawCtx
*d
= p_new(DrawCtx
, 1);
94 d
->phys_screen
= phys_screen
;
97 d
->depth
= DefaultDepth(disp
, phys_screen
);
98 d
->visual
= DefaultVisual(disp
, phys_screen
);
100 d
->surface
= cairo_xlib_surface_create(disp
, dw
, d
->visual
, width
, height
);
101 d
->cr
= cairo_create(d
->surface
);
106 /** Delete a draw context
107 * \param ctx DrawCtx to delete
110 draw_context_delete(DrawCtx
*ctx
)
112 cairo_surface_destroy(ctx
->surface
);
113 cairo_destroy(ctx
->cr
);
117 /** Draw text into a draw context
118 * \param ctx DrawCtx to draw to
119 * \param area area to draw to
120 * \param align alignment
121 * \param padding padding to add before drawing the text
122 * \param font font to use
123 * \param text text to draw
124 * \param fg foreground color
125 * \param bg background color
128 draw_text(DrawCtx
*ctx
,
132 XftFont
*font
, char *text
,
133 XColor fg
, XColor bg
)
137 char *buf
= NULL
, *utf8
= NULL
;
138 cairo_font_face_t
*font_face
;
140 draw_rectangle(ctx
, area
, True
, bg
);
142 if(!(len
= olen
= a_strlen(text
)))
145 /* try to convert it to UTF-8 */
146 if((utf8
= draw_iso2utf8(text
)))
149 len
= olen
= a_strlen(buf
);
152 buf
= a_strdup(text
);
154 /* check that the text is not too long */
155 while(len
&& (nw
= (draw_textwidth(ctx
->display
, font
, buf
)) + padding
* 2) > area
.width
)
158 /* we can't blindly null the char, we need to check if it's not part of
159 * a multi byte char: if mbtowc return -1, we know that we must go back
160 * in the string to find the beginning of the multi byte char */
161 while(mbtowc(NULL
, buf
+ len
, a_strlen(buf
+ len
)) < 0)
166 return; /* too long */
177 font_face
= cairo_ft_font_face_create_for_pattern(font
->pattern
);
178 cairo_set_font_face(ctx
->cr
, font_face
);
179 cairo_set_font_size(ctx
->cr
, font
->height
);
180 cairo_set_source_rgb(ctx
->cr
, fg
.red
/ 65535.0, fg
.green
/ 65535.0, fg
.blue
/ 65535.0);
185 cairo_move_to(ctx
->cr
, area
.x
+ padding
, area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
188 cairo_move_to(ctx
->cr
, area
.x
+ (area
.width
- nw
) + padding
,
189 area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
192 cairo_move_to(ctx
->cr
, area
.x
+ ((area
.width
- nw
) / 2) + padding
,
193 area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
197 cairo_show_text(ctx
->cr
, buf
);
199 cairo_font_face_destroy(font_face
);
204 /** Setup color-source for cairo (gradient or mono)
205 * \param ctx Draw context
206 * \param x x-offset of widget
207 * \param y y-offset of widget
208 * \param width width in pixels
209 * \param color color to use from 0%
210 * \param pcolor_center color at 50% of width
211 * \param pcolor_end color at 100% of width
212 * \return pat pattern or NULL; needs to get cairo_pattern_destroy()'ed;
214 static cairo_pattern_t
*
215 draw_setup_cairo_color_source(DrawCtx
*ctx
, int x
, int y
, int width
,
216 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
218 cairo_pattern_t
*pat
= NULL
;
220 if(pcolor_center
|| pcolor_end
) /* draw a gradient */
222 pat
= cairo_pattern_create_linear(x
, y
, x
+ width
, y
);
224 cairo_pattern_add_color_stop_rgb(pat
, 0, color
.red
/ 65535.0,
225 color
.green
/ 65535.0, color
.blue
/ 65535.0);
227 cairo_pattern_add_color_stop_rgb(pat
, 0.5, pcolor_center
->red
/ 65535.0,
228 pcolor_center
->green
/ 65535.0, pcolor_center
->blue
/ 65535.0);
230 cairo_pattern_add_color_stop_rgb(pat
, 1, pcolor_end
->red
/ 65535.0,
231 pcolor_end
->green
/ 65535.0, pcolor_end
->blue
/ 65535.0);
233 cairo_pattern_add_color_stop_rgb(pat
, 1, color
.red
/ 65535.0,
234 color
.green
/ 65535.0, color
.blue
/ 65535.0);
235 cairo_set_source(ctx
->cr
, pat
);
238 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
244 * \param ctx Draw context
245 * \param geometry geometry
246 * \param filled fill rectangle?
247 * \param color color to use
250 draw_rectangle(DrawCtx
*ctx
, Area geometry
, Bool filled
, XColor color
)
252 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
253 cairo_set_line_width(ctx
->cr
, 1.0);
254 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
257 cairo_rectangle(ctx
->cr
, geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
262 cairo_rectangle(ctx
->cr
, geometry
.x
+ 1, geometry
.y
, geometry
.width
- 1, geometry
.height
- 1);
263 cairo_stroke(ctx
->cr
);
266 /** Draw rectangle with gradient colors
267 * \param ctx Draw context
268 * \param geometry geometry
269 * \param fullwidth width of full bar in pixels
270 * \param filled filled rectangle?
271 * \param color color to use from 0%
272 * \param pcolor_center color at 50%
273 * \param pcolor_end color at 100%
276 draw_rectangle_gradient(DrawCtx
*ctx
, Area geometry
, int fullwidth
, Bool filled
,
277 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
279 cairo_pattern_t
*pat
;
281 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
282 cairo_set_line_width(ctx
->cr
, 1.0);
284 pat
= draw_setup_cairo_color_source(ctx
, geometry
.x
, geometry
.y
, fullwidth
,
285 color
, pcolor_center
, pcolor_end
);
289 cairo_rectangle(ctx
->cr
, geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
294 cairo_rectangle(ctx
->cr
, geometry
.x
+ 1, geometry
.y
, geometry
.width
- 1, geometry
.height
- 1);
295 cairo_stroke(ctx
->cr
);
299 cairo_pattern_destroy(pat
);
302 /** Setup some cairo-things for drawing a graph
303 * \param ctx Draw context
306 draw_graph_setup(DrawCtx
*ctx
)
308 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
309 cairo_set_line_width(ctx
->cr
, 1.0);
310 /* without it, it can draw over the path on sharp angles (...too long lines) */
311 cairo_set_line_join (ctx
->cr
, CAIRO_LINE_JOIN_ROUND
);
315 * \param ctx Draw context
316 * \param x x-offset of widget
317 * \param y y-offset of widget
318 * \param w width in pixels
319 * \param from array of starting-point offsets to draw a graph-lines
320 * \param to array of end-point offsets to draw a graph-lines
321 * \param cur_index current position in data-array (cycles around)
322 * \param color color to use from 0%
323 * \param pcolor_center color at 50%
324 * \param pcolor_end color at 100%
327 draw_graph(DrawCtx
*ctx
, int x
, int y
, int w
, int *from
, int *to
, int cur_index
,
328 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
331 cairo_pattern_t
*pat
;
332 pat
= draw_setup_cairo_color_source(ctx
, x
, y
, w
, color
, pcolor_center
, pcolor_end
);
337 cairo_move_to(ctx
->cr
, x
, y
- from
[cur_index
]);
338 cairo_line_to(ctx
->cr
, x
, y
- to
[cur_index
]);
345 cairo_stroke(ctx
->cr
);
348 cairo_pattern_destroy(pat
);
351 /** Draw a line into a graph-widget
352 * \param ctx Draw context
353 * \param x x-offset of widget
354 * \param y y-offset of widget
355 * \param w width in pixels
356 * \param to array of offsets to draw the line through...
357 * \param cur_index current position in data-array (cycles around)
358 * \param color color to use from 0%
359 * \param pcolor_center color at 50%
360 * \param pcolor_end color at 100%
363 draw_graph_line(DrawCtx
*ctx
, int x
, int y
, int w
, int *to
, int cur_index
,
364 XColor color
, XColor
*pcolor_center
, XColor
*pcolor_end
)
367 int flag
= 0; /* used to prevent drawing a line from 0 to 0 values */
368 cairo_pattern_t
*pat
;
370 pat
= draw_setup_cairo_color_source(ctx
, x
, y
, w
, color
, pcolor_center
, pcolor_end
);
372 /* x-1 (on the border), paints *from* the last point (... not included itself) */
373 /* makes sense when you assume there is already some line drawn to it. */
374 cairo_move_to(ctx
->cr
, x
- 1, y
- to
[cur_index
]);
376 for (i
= 0; i
< w
; i
++)
378 if (to
[cur_index
] > 0)
380 cairo_line_to(ctx
->cr
, x
, y
- to
[cur_index
]);
385 if(flag
) /* only draw from values > 0 to 0-values */
387 cairo_line_to(ctx
->cr
, x
, y
);
391 cairo_move_to(ctx
->cr
, x
, y
);
394 if (--cur_index
< 0) /* cycles around the index */
398 cairo_stroke(ctx
->cr
);
401 cairo_pattern_destroy(pat
);
405 * \param ctx Draw context to draw to
406 * \param x x coordinate
407 * \param y y coordinate
408 * \param r size of the circle
409 * \param filled fill circle?
410 * \param color color to use
413 draw_circle(DrawCtx
*ctx
, int x
, int y
, int r
, Bool filled
, XColor color
)
415 cairo_set_line_width(ctx
->cr
, 1.0);
416 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
418 cairo_new_sub_path(ctx
->cr
); /* don't draw from the old reference point to.. */
422 cairo_arc (ctx
->cr
, x
+ r
, y
+ r
, r
, 0, 2 * M_PI
);
426 cairo_arc (ctx
->cr
, x
+ r
, y
+ r
, r
- 1, 0, 2 * M_PI
);
428 cairo_stroke(ctx
->cr
);
431 /** Draw an image from ARGB data to a draw context.
432 * Data should be stored as an array of alpha, red, blue, green for each pixel
433 * and the array size should be w * h elements long.
434 * \param ctx Draw context to draw to
435 * \param x x coordinate
436 * \param y y coordinate
439 * \param wanted_h wanted height: if > 0, image will be resized
440 * \param data the image pixels array
442 void draw_image_from_argb_data(DrawCtx
*ctx
, int x
, int y
, int w
, int h
,
443 int wanted_h
, unsigned char *data
)
447 cairo_surface_t
*source
;
449 source
= cairo_image_surface_create_for_data(data
, CAIRO_FORMAT_ARGB32
, w
, h
, 0);
450 cr
= cairo_create(ctx
->surface
);
451 if(wanted_h
> 0 && h
> 0)
453 ratio
= (double) wanted_h
/ (double) h
;
454 cairo_scale(cr
, ratio
, ratio
);
455 cairo_set_source_surface(cr
, source
, x
/ ratio
, y
/ ratio
);
458 cairo_set_source_surface(cr
, source
, x
, y
);
463 cairo_surface_destroy(source
);
466 /** Draw an image (PNG format only) from a file to a draw context
467 * \param ctx Draw context to draw to
468 * \param x x coordinate
469 * \param y y coordinate
470 * \param wanted_h wanted height: if > 0, image will be resized
471 * \param filename file name to draw
474 draw_image(DrawCtx
*ctx
, int x
, int y
, int wanted_h
, const char *filename
)
478 cairo_surface_t
*source
;
480 cairo_status_t cairo_st
;
482 source
= cairo_image_surface_create_from_png(filename
);
483 if((cairo_st
= cairo_surface_status(source
)))
485 warn("failed to draw image %s: %s\n", filename
, cairo_status_to_string(cairo_st
));
486 cairo_surface_destroy(source
);
489 cr
= cairo_create (ctx
->surface
);
490 if(wanted_h
> 0 && (h
= cairo_image_surface_get_height(source
)) > 0)
492 ratio
= (double) wanted_h
/ (double) h
;
493 cairo_scale(cr
, ratio
, ratio
);
494 cairo_set_source_surface(cr
, source
, x
/ ratio
, y
/ ratio
);
497 cairo_set_source_surface(cr
, source
, x
, y
);
501 cairo_surface_destroy(source
);
504 /** Get an imag size (PNG format only)
505 * \param filename file name
506 * \return Area structure with width and height set to image size
509 draw_get_image_size(const char *filename
)
511 Area size
= { -1, -1, -1, -1, NULL
};
512 cairo_surface_t
*surface
;
513 cairo_status_t cairo_st
;
515 surface
= cairo_image_surface_create_from_png(filename
);
516 if((cairo_st
= cairo_surface_status(surface
)))
517 warn("failed to get image size %s: %s\n", filename
, cairo_status_to_string(cairo_st
));
520 cairo_image_surface_get_width(surface
);
523 size
.width
= cairo_image_surface_get_width(surface
);
524 size
.height
= cairo_image_surface_get_height(surface
);
527 cairo_surface_destroy(surface
);
532 /** Rotate a drawable
533 * \param ctx Draw context to draw to
534 * \param phys_screen physical screen id
535 * \param angle angle to rotate
536 * \param tx translate to this x coordinate
537 * \param ty translate to this y coordinate
538 * \return new rotated drawable
541 draw_rotate(DrawCtx
*ctx
, int phys_screen
, double angle
, int tx
, int ty
)
543 cairo_surface_t
*surface
, *source
;
545 Drawable newdrawable
;
547 newdrawable
= XCreatePixmap(ctx
->display
,
548 RootWindow(ctx
->display
, phys_screen
),
549 ctx
->height
, ctx
->width
,
551 surface
= cairo_xlib_surface_create(ctx
->display
, newdrawable
, ctx
->visual
, ctx
->height
, ctx
->width
);
552 source
= cairo_xlib_surface_create(ctx
->display
, ctx
->drawable
, ctx
->visual
, ctx
->width
, ctx
->height
);
553 cr
= cairo_create (surface
);
555 cairo_translate(cr
, tx
, ty
);
556 cairo_rotate(cr
, angle
);
558 cairo_set_source_surface(cr
, source
, 0.0, 0.0);
562 cairo_surface_destroy(source
);
563 cairo_surface_destroy(surface
);
568 /** Return the width of a text in pixel
569 * \param disp Display ref
570 * \param font font to use
571 * \param text the text
575 draw_textwidth(Display
*disp
, XftFont
*font
, char *text
)
577 cairo_surface_t
*surface
;
579 cairo_font_face_t
*font_face
;
580 cairo_text_extents_t te
;
585 surface
= cairo_xlib_surface_create(disp
, DefaultScreen(disp
),
586 DefaultVisual(disp
, DefaultScreen(disp
)),
587 DisplayWidth(disp
, DefaultScreen(disp
)),
588 DisplayHeight(disp
, DefaultScreen(disp
)));
589 cr
= cairo_create(surface
);
590 font_face
= cairo_ft_font_face_create_for_pattern(font
->pattern
);
591 cairo_set_font_face(cr
, font_face
);
592 cairo_set_font_size(cr
, font
->height
);
593 cairo_text_extents(cr
, text
, &te
);
595 cairo_surface_destroy(surface
);
596 cairo_font_face_destroy(font_face
);
598 return MAX(te
.x_advance
, te
.width
);
601 /** Transform a string to a Alignment type.
602 * Recognized string are left, center or right. Everything else will be
603 * recognized as AlignAuto.
604 * \param align string with align text
605 * \return Alignment type
608 draw_get_align(const char *align
)
610 if(!a_strncmp(align
, "left", 4))
612 else if(!a_strncmp(align
, "center", 6))
614 else if(!a_strncmp(align
, "right", 5))
616 else if(!a_strncmp(align
, "flex", 4))
622 /** Initialize an X color
623 * \param disp display ref
624 * \param phys_screen Physical screen number
625 * \param colstr Color specification
626 * \param color XColor struct to store color to
627 * \return true if color allocation was successfull
630 draw_color_new(Display
*disp
, int phys_screen
, const char *colstr
, XColor
*color
)
635 if(!(ret
= XAllocNamedColor(disp
,
636 DefaultColormap(disp
, phys_screen
),
640 warn("awesome: error, cannot allocate color '%s'\n", colstr
);
645 /** Remove a area from a list of them,
646 * spliting the space between several area that can overlaps
647 * \param head list head
648 * \param elem area to remove
651 area_list_remove(Area
**head
, Area
*elem
)
653 Area
*r
, inter
, *extra
, *rnext
;
655 for(r
= *head
; r
; r
= rnext
)
658 if(area_intersect_area(*r
, *elem
))
660 /* remove it from the list */
661 area_list_detach(head
, r
);
663 inter
= area_get_intersect_area(*r
, *elem
);
665 if(AREA_LEFT(inter
) > AREA_LEFT(*r
))
667 extra
= p_new(Area
, 1);
670 extra
->width
= AREA_LEFT(inter
) - r
->x
;
671 extra
->height
= r
->height
;
672 area_list_append(head
, extra
);
675 if(AREA_TOP(inter
) > AREA_TOP(*r
))
677 extra
= p_new(Area
, 1);
680 extra
->width
= r
->width
;
681 extra
->height
= AREA_TOP(inter
) - r
->y
;
682 area_list_append(head
, extra
);
685 if(AREA_RIGHT(inter
) < AREA_RIGHT(*r
))
687 extra
= p_new(Area
, 1);
688 extra
->x
= AREA_RIGHT(inter
);
690 extra
->width
= AREA_RIGHT(*r
) - AREA_RIGHT(inter
);
691 extra
->height
= r
->height
;
692 area_list_append(head
, extra
);
695 if(AREA_BOTTOM(inter
) < AREA_BOTTOM(*r
))
697 extra
= p_new(Area
, 1);
699 extra
->y
= AREA_BOTTOM(inter
);
700 extra
->width
= r
->width
;
701 extra
->height
= AREA_BOTTOM(*r
) - AREA_BOTTOM(inter
);
702 area_list_append(head
, extra
);
705 /* delete the elem since we removed it from the list */
711 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80