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
)
128 static char buf
[256];
130 cairo_font_face_t
*font_face
;
132 draw_rectangle(ctx
, area
, True
, bg
);
134 olen
= len
= a_strlen(text
);
139 /* try to convert text to UTF-8 */
140 text
= draw_iso2utf8(text
);
142 font_face
= cairo_ft_font_face_create_for_pattern(font
->pattern
);
143 cairo_set_font_face(ctx
->cr
, font_face
);
144 cairo_set_font_size(ctx
->cr
, font
->height
);
145 cairo_set_source_rgb(ctx
->cr
, fg
.red
/ 65535.0, fg
.green
/ 65535.0, fg
.blue
/ 65535.0);
147 if(len
>= sizeof(buf
))
148 len
= sizeof(buf
) - 1;
149 memcpy(buf
, text
, len
);
151 while(len
&& (nw
= (draw_textwidth(ctx
->display
, font
, buf
)) + padding
* 2) > area
.width
)
154 return; /* too long */
168 cairo_move_to(ctx
->cr
, area
.x
+ padding
, area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
171 cairo_move_to(ctx
->cr
, area
.x
+ (area
.width
- nw
) + padding
,
172 area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
175 cairo_move_to(ctx
->cr
, area
.x
+ ((area
.width
- nw
) / 2) + padding
,
176 area
.y
+ font
->ascent
+ (ctx
->height
- font
->height
) / 2);
180 cairo_show_text(ctx
->cr
, buf
);
182 cairo_font_face_destroy(font_face
);
186 * \param ctx Draw context
187 * \param geometry geometry
188 * \param filled filled rectangle?
189 * \param color color to use
192 draw_rectangle(DrawCtx
*ctx
, Area geometry
, Bool filled
, XColor color
)
194 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
195 cairo_set_line_width(ctx
->cr
, 1.0);
196 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
199 cairo_rectangle(ctx
->cr
, geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
204 cairo_rectangle(ctx
->cr
, geometry
.x
+ 1, geometry
.y
, geometry
.width
- 1, geometry
.height
- 1);
205 cairo_stroke(ctx
->cr
);
209 /** Draw rectangle with gradient colors
210 * \param ctx Draw context
211 * \param geometry geometry
212 * \param fullwidth width of full bar in pixels
213 * \param filled filled rectangle?
214 * \param color color to use from 0%
215 * \param color_half color at 50%
216 * \param color_full color at 100%
219 draw_rectangle_gradient(DrawCtx
*ctx
, Area geometry
, int fullwidth
, Bool filled
,
220 XColor color
, XColor
* color_half
, XColor
* color_full
)
222 cairo_pattern_t
*pat
;
224 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
225 cairo_set_line_width(ctx
->cr
, 1.0);
227 pat
= cairo_pattern_create_linear(geometry
.x
, geometry
.y
, geometry
.x
+ fullwidth
, geometry
.y
);
228 cairo_pattern_add_color_stop_rgb(pat
, 0, color
.red
/ 65535.0,
229 color
.green
/ 65535.0, color
.blue
/ 65535.0);
231 cairo_pattern_add_color_stop_rgb(pat
, 0.5, color_half
->red
/ 65535.0,
232 color_half
->green
/ 65535.0, color_half
->blue
/ 65535.0);
234 cairo_pattern_add_color_stop_rgb(pat
, 1, color_full
->red
/ 65535.0,
235 color_full
->green
/ 65535.0, color_full
->blue
/ 65535.0);
237 cairo_pattern_add_color_stop_rgb(pat
, 1, color
.red
/ 65535.0,
238 color
.green
/ 65535.0, color
.blue
/ 65535.0);
240 cairo_set_source(ctx
->cr
, pat
);
244 cairo_rectangle(ctx
->cr
, geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
);
249 cairo_rectangle(ctx
->cr
, geometry
.x
+ 1, geometry
.y
, geometry
.width
- 1, geometry
.height
- 1);
250 cairo_stroke(ctx
->cr
);
253 cairo_pattern_destroy(pat
);
256 /* draw_graph functions */
258 draw_graph_setup(DrawCtx
*ctx
)
260 cairo_set_antialias(ctx
->cr
, CAIRO_ANTIALIAS_NONE
);
261 cairo_set_line_width(ctx
->cr
, 1.0);
262 /* without it, it can draw over the path on sharp angles (...too long lines) */
263 cairo_set_line_join (ctx
->cr
, CAIRO_LINE_JOIN_ROUND
);
266 /* draws a graph; it takes the line-lengths from *from and *to. It cycles
267 * backwards through those arrays (what have the length of w), beginning at
268 * position cur_index, until cur_index is reached again (wrapped around). */
270 draw_graph(DrawCtx
*ctx
, int x
, int y
, int w
, int *from
, int *to
, int cur_index
, XColor color
)
274 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
279 cairo_move_to(ctx
->cr
, x
, y
- from
[cur_index
]);
280 cairo_line_to(ctx
->cr
, x
, y
- to
[cur_index
]);
287 cairo_stroke(ctx
->cr
);
290 draw_graph_line(DrawCtx
*ctx
, int x
, int y
, int w
, int *to
, int cur_index
, XColor color
)
293 int flag
= 0; /* used to prevent drawing a line from 0 to 0 values */
294 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
296 /* x-1 (on the border), paints *from* the last point (... not included itself) */
297 /* makes sense when you assume there is already some line drawn to it. */
298 cairo_move_to(ctx
->cr
, x
- 1, y
- to
[cur_index
]);
300 for (i
= 0; i
< w
; i
++)
302 if (to
[cur_index
] > 0)
304 cairo_line_to(ctx
->cr
, x
, y
- to
[cur_index
]);
309 if(flag
) /* only draw from values > 0 to 0-values */
311 cairo_line_to(ctx
->cr
, x
, y
);
315 cairo_move_to(ctx
->cr
, x
, y
);
318 if (--cur_index
< 0) /* cycles around the index */
322 cairo_stroke(ctx
->cr
);
326 draw_circle(DrawCtx
*ctx
, int x
, int y
, int r
, Bool filled
, XColor color
)
328 cairo_set_line_width(ctx
->cr
, 1.0);
329 cairo_set_source_rgb(ctx
->cr
, color
.red
/ 65535.0, color
.green
/ 65535.0, color
.blue
/ 65535.0);
331 cairo_new_sub_path(ctx
->cr
); /* don't draw from the old reference point to.. */
335 cairo_arc (ctx
->cr
, x
+ r
, y
+ r
, r
, 0, 2 * M_PI
);
339 cairo_arc (ctx
->cr
, x
+ r
, y
+ r
, r
- 1, 0, 2 * M_PI
);
341 cairo_stroke(ctx
->cr
);
344 void draw_image_from_argb_data(DrawCtx
*ctx
, int x
, int y
, int w
, int h
,
345 int wanted_h
, unsigned char *data
)
349 cairo_surface_t
*source
;
351 source
= cairo_image_surface_create_for_data(data
, CAIRO_FORMAT_ARGB32
, w
, h
, 0);
352 cr
= cairo_create(ctx
->surface
);
353 if(wanted_h
> 0 && h
> 0)
355 ratio
= (double) wanted_h
/ (double) h
;
356 cairo_scale(cr
, ratio
, ratio
);
357 cairo_set_source_surface(cr
, source
, x
/ ratio
, y
/ ratio
);
360 cairo_set_source_surface(cr
, source
, x
, y
);
364 cairo_surface_destroy(source
);
368 draw_image(DrawCtx
*ctx
, int x
, int y
, int wanted_h
, const char *filename
)
372 cairo_surface_t
*source
;
374 cairo_status_t cairo_st
;
376 source
= cairo_image_surface_create_from_png(filename
);
377 if((cairo_st
= cairo_surface_status(source
)))
379 warn("failed to draw image %s: %s\n", filename
, cairo_status_to_string(cairo_st
));
382 cr
= cairo_create (ctx
->surface
);
383 if(wanted_h
> 0 && (h
= cairo_image_surface_get_height(source
)) > 0)
385 ratio
= (double) wanted_h
/ (double) h
;
386 cairo_scale(cr
, ratio
, ratio
);
387 cairo_set_source_surface(cr
, source
, x
/ ratio
, y
/ ratio
);
390 cairo_set_source_surface(cr
, source
, x
, y
);
394 cairo_surface_destroy(source
);
398 draw_get_image_size(const char *filename
)
400 Area size
= { -1, -1, -1, -1, NULL
};
401 cairo_surface_t
*surface
;
402 cairo_status_t cairo_st
;
404 surface
= cairo_image_surface_create_from_png(filename
);
405 if((cairo_st
= cairo_surface_status(surface
)))
406 warn("failed to get image size %s: %s\n", filename
, cairo_status_to_string(cairo_st
));
409 cairo_image_surface_get_width(surface
);
412 size
.width
= cairo_image_surface_get_width(surface
);
413 size
.height
= cairo_image_surface_get_height(surface
);
414 cairo_surface_destroy(surface
);
421 draw_rotate(DrawCtx
*ctx
, int screen
, double angle
, int tx
, int ty
)
423 cairo_surface_t
*surface
, *source
;
425 Drawable newdrawable
;
427 newdrawable
= XCreatePixmap(ctx
->display
,
428 RootWindow(ctx
->display
, screen
),
429 ctx
->height
, ctx
->width
,
431 surface
= cairo_xlib_surface_create(ctx
->display
, newdrawable
, ctx
->visual
, ctx
->height
, ctx
->width
);
432 source
= cairo_xlib_surface_create(ctx
->display
, ctx
->drawable
, ctx
->visual
, ctx
->width
, ctx
->height
);
433 cr
= cairo_create (surface
);
435 cairo_translate(cr
, tx
, ty
);
436 cairo_rotate(cr
, angle
);
438 cairo_set_source_surface(cr
, source
, 0.0, 0.0);
442 cairo_surface_destroy(source
);
443 cairo_surface_destroy(surface
);
449 draw_textwidth(Display
*disp
, XftFont
*font
, char *text
)
451 cairo_surface_t
*surface
;
453 cairo_font_face_t
*font_face
;
454 cairo_text_extents_t te
;
459 surface
= cairo_xlib_surface_create(disp
, DefaultScreen(disp
),
460 DefaultVisual(disp
, DefaultScreen(disp
)),
461 DisplayWidth(disp
, DefaultScreen(disp
)),
462 DisplayHeight(disp
, DefaultScreen(disp
)));
463 cr
= cairo_create(surface
);
464 font_face
= cairo_ft_font_face_create_for_pattern(font
->pattern
);
465 cairo_set_font_face(cr
, font_face
);
466 cairo_set_font_size(cr
, font
->height
);
467 cairo_text_extents(cr
, text
, &te
);
469 cairo_surface_destroy(surface
);
470 cairo_font_face_destroy(font_face
);
472 return MAX(te
.x_advance
, te
.width
);
476 draw_get_align(const char *align
)
478 if(!a_strncmp(align
, "center", 6))
480 else if(!a_strncmp(align
, "right", 5))
486 /** Initialize an X color
487 * \param disp display ref
488 * \param screen Physical screen number
489 * \param colstr Color specification
490 * \return XColor struct
493 draw_color_new(Display
*disp
, int phys_screen
, const char *colstr
)
495 XColor screenColor
, exactColor
;
497 if(!XAllocNamedColor(disp
,
498 DefaultColormap(disp
, phys_screen
),
502 eprint("awesome: error, cannot allocate color '%s'\n", colstr
);
507 /** Remove a area from a list of them,
508 * spliting the space between several area that
510 * \param head list head
511 * \param elem area to remove
514 area_list_remove(Area
**head
, Area
*elem
)
516 Area
*r
, inter
, *extra
;
518 area_list_detach(head
, elem
);
520 for(r
= *head
; r
; r
= r
->next
)
521 if(area_intersect_area(*r
, *elem
))
523 /* remove it from the list */
524 area_list_detach(head
, r
);
526 inter
= area_get_intersect_area(*r
, *elem
);
528 if(AREA_LEFT(inter
) > AREA_LEFT(*r
))
530 extra
= p_new(Area
, 1);
533 extra
->width
= AREA_LEFT(inter
) - r
->x
;
534 extra
->height
= r
->height
;
535 area_list_push(head
, extra
);
538 if(AREA_TOP(inter
) > AREA_TOP(*r
))
540 extra
= p_new(Area
, 1);
543 extra
->width
= r
->width
;
544 extra
->height
= AREA_TOP(inter
) - r
->y
;
545 area_list_push(head
, extra
);
548 if(AREA_RIGHT(inter
) < AREA_RIGHT(*r
))
550 extra
= p_new(Area
, 1);
551 extra
->x
= AREA_RIGHT(inter
);
553 extra
->width
= AREA_RIGHT(*r
) - AREA_RIGHT(inter
);
554 extra
->height
= r
->height
;
555 area_list_push(head
, extra
);
558 if(AREA_BOTTOM(inter
) < AREA_BOTTOM(*r
))
560 extra
= p_new(Area
, 1);
562 extra
->y
= AREA_BOTTOM(inter
);
563 extra
->width
= r
->width
;
564 extra
->height
= AREA_BOTTOM(*r
) - AREA_BOTTOM(inter
);
565 area_list_push(head
, extra
);
570 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80