adds fg_half as an option to the progressbar
[awesome.git] / common / draw.c
blobcf91fad1552dcec2e1593e1cfc4ada6e2880b16f
1 /*
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.
22 #include <cairo.h>
23 #include <cairo-ft.h>
24 #include <cairo-xlib.h>
26 #include <langinfo.h>
27 #include <iconv.h>
28 #include <errno.h>
30 #include <math.h>
32 #include "draw.h"
33 #include "common/util.h"
35 static char *
36 draw_iso2utf8(char *iso)
38 iconv_t iso2utf8;
39 size_t len, utf8len;
40 char *utf8;
42 if(!(len = a_strlen(iso)))
43 return NULL;
45 if(!a_strcmp(nl_langinfo(CODESET), "UTF-8"))
46 return iso;
48 iso2utf8 = iconv_open("UTF-8", nl_langinfo(CODESET));
49 if(iso2utf8 == (iconv_t) -1)
51 if(errno == EINVAL)
52 warn("unable to convert text from %s to UTF-8, not available",
53 nl_langinfo(CODESET));
54 else
55 perror("awesome: unable to convert text");
57 return NULL;
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");
66 return NULL;
69 if(iconv_close(iso2utf8))
70 warn("error closing iconv");
72 return utf8;
75 /** Get a draw context
76 * \param phys_screen physical screen id
77 * \param width width
78 * \param height height
79 * \return draw context ref
81 DrawCtx *
82 draw_context_new(Display *disp, int phys_screen, int width, int height, Drawable dw)
84 DrawCtx *d = p_new(DrawCtx, 1);
86 d->display = disp;
87 d->phys_screen = phys_screen;
88 d->width = width;
89 d->height = height;
90 d->depth = DefaultDepth(disp, phys_screen);
91 d->visual = DefaultVisual(disp, phys_screen);
92 d->drawable = dw;
93 d->surface = cairo_xlib_surface_create(disp, dw, d->visual, width, height);
94 d->cr = cairo_create(d->surface);
96 return d;
99 void
100 draw_context_delete(DrawCtx *ctx)
102 cairo_surface_destroy(ctx->surface);
103 cairo_destroy(ctx->cr);
104 p_delete(&ctx);
107 /** Draw text into a draw context
108 * \param x x coord
109 * \param y y coord
110 * \param w width
111 * \param h height
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
119 void
120 draw_text(DrawCtx *ctx,
121 Area area,
122 Alignment align,
123 int padding,
124 XftFont *font, char *text,
125 XColor fg, XColor bg)
127 int nw = 0;
128 static char buf[256];
129 size_t len, olen;
130 cairo_font_face_t *font_face;
132 draw_rectangle(ctx, area, True, bg);
134 olen = len = a_strlen(text);
136 if(!len)
137 return;
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);
150 buf[len] = 0;
151 while(len && (nw = (draw_textwidth(ctx->display, font, buf)) + padding * 2) > area.width)
152 buf[--len] = 0;
153 if(nw > area.width)
154 return; /* too long */
155 if(len < olen)
157 if(len > 1)
158 buf[len - 1] = '.';
159 if(len > 2)
160 buf[len - 2] = '.';
161 if(len > 3)
162 buf[len - 3] = '.';
165 switch(align)
167 case AlignLeft:
168 cairo_move_to(ctx->cr, area.x + padding, area.y + font->ascent + (ctx->height - font->height) / 2);
169 break;
170 case AlignRight:
171 cairo_move_to(ctx->cr, area.x + (area.width - nw) + padding,
172 area.y + font->ascent + (ctx->height - font->height) / 2);
173 break;
174 default:
175 cairo_move_to(ctx->cr, area.x + ((area.width - nw) / 2) + padding,
176 area.y + font->ascent + (ctx->height - font->height) / 2);
177 break;
180 cairo_show_text(ctx->cr, buf);
182 cairo_font_face_destroy(font_face);
185 /** Draw rectangle
186 * \param ctx Draw context
187 * \param geometry geometry
188 * \param filled filled rectangle?
189 * \param color color to use
191 void
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);
197 if(filled)
199 cairo_rectangle(ctx->cr, geometry.x, geometry.y, geometry.width, geometry.height);
200 cairo_fill(ctx->cr);
202 else
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%
218 void
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);
230 if(color_half)
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);
233 if(color_full)
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);
236 else
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);
242 if(filled)
244 cairo_rectangle(ctx->cr, geometry.x, geometry.y, geometry.width, geometry.height);
245 cairo_fill(ctx->cr);
247 else
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 */
257 void
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). */
269 void
270 draw_graph(DrawCtx *ctx, int x, int y, int w, int *from, int *to, int cur_index, XColor color)
272 int i;
274 cairo_set_source_rgb(ctx->cr, color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0);
276 i = -1;
277 while(++i < w)
279 cairo_move_to(ctx->cr, x, y - from[cur_index]);
280 cairo_line_to(ctx->cr, x, y - to[cur_index]);
281 x++;
283 if (--cur_index < 0)
284 cur_index = w - 1;
287 cairo_stroke(ctx->cr);
289 void
290 draw_graph_line(DrawCtx *ctx, int x, int y, int w, int *to, int cur_index, XColor color)
292 int i;
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]);
305 flag = 1;
307 else
309 if(flag) /* only draw from values > 0 to 0-values */
311 cairo_line_to(ctx->cr, x, y);
312 flag = 0;
314 else
315 cairo_move_to(ctx->cr, x, y);
318 if (--cur_index < 0) /* cycles around the index */
319 cur_index = w - 1;
320 x++;
322 cairo_stroke(ctx->cr);
325 void
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.. */
333 if(filled)
335 cairo_arc (ctx->cr, x + r, y + r, r, 0, 2 * M_PI);
336 cairo_fill(ctx->cr);
338 else
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)
347 double ratio;
348 cairo_t *cr;
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);
359 else
360 cairo_set_source_surface(cr, source, x, y);
362 cairo_paint(cr);
364 cairo_surface_destroy(source);
367 void
368 draw_image(DrawCtx *ctx, int x, int y, int wanted_h, const char *filename)
370 double ratio;
371 int h;
372 cairo_surface_t *source;
373 cairo_t *cr;
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));
380 return;
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);
389 else
390 cairo_set_source_surface(cr, source, x, y);
391 cairo_paint(cr);
393 cairo_destroy(cr);
394 cairo_surface_destroy(source);
397 Area
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));
407 else
409 cairo_image_surface_get_width(surface);
410 size.x = 0;
411 size.y = 0;
412 size.width = cairo_image_surface_get_width(surface);
413 size.height = cairo_image_surface_get_height(surface);
414 cairo_surface_destroy(surface);
417 return size;
420 Drawable
421 draw_rotate(DrawCtx *ctx, int screen, double angle, int tx, int ty)
423 cairo_surface_t *surface, *source;
424 cairo_t *cr;
425 Drawable newdrawable;
427 newdrawable = XCreatePixmap(ctx->display,
428 RootWindow(ctx->display, screen),
429 ctx->height, ctx->width,
430 ctx->depth);
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);
439 cairo_paint(cr);
441 cairo_destroy(cr);
442 cairo_surface_destroy(source);
443 cairo_surface_destroy(surface);
445 return newdrawable;
448 unsigned short
449 draw_textwidth(Display *disp, XftFont *font, char *text)
451 cairo_surface_t *surface;
452 cairo_t *cr;
453 cairo_font_face_t *font_face;
454 cairo_text_extents_t te;
456 if (!a_strlen(text))
457 return 0;
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);
468 cairo_destroy(cr);
469 cairo_surface_destroy(surface);
470 cairo_font_face_destroy(font_face);
472 return MAX(te.x_advance, te.width);
475 Alignment
476 draw_get_align(const char *align)
478 if(!a_strncmp(align, "center", 6))
479 return AlignCenter;
480 else if(!a_strncmp(align, "right", 5))
481 return AlignRight;
483 return AlignLeft;
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
492 XColor
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),
499 colstr,
500 &screenColor,
501 &exactColor))
502 eprint("awesome: error, cannot allocate color '%s'\n", colstr);
504 return screenColor;
507 /** Remove a area from a list of them,
508 * spliting the space between several area that
509 * can overlaps
510 * \param head list head
511 * \param elem area to remove
513 void
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);
531 extra->x = r->x;
532 extra->y = r->y;
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);
541 extra->x = r->x;
542 extra->y = r->y;
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);
552 extra->y = r->y;
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);
561 extra->x = r->x;
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