awesome-client: we use bash extensions
[awesome.git] / widget.c
blobf8c00e3db1a0ea5d3c124a4f5a8d262e382a0dbf
1 /*
2 * widget.c - widget managing
4 * Copyright © 2007-2009 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 <math.h>
24 #include <xcb/xcb.h>
25 #include <xcb/xcb_atom.h>
27 #include "screen.h"
28 #include "mouse.h"
29 #include "widget.h"
30 #include "wibox.h"
31 #include "client.h"
32 #include "common/atoms.h"
33 #include "common/xutil.h"
35 #include "widgetgen.h"
37 DO_LUA_TOSTRING(widget_t, widget, "widget")
39 /** Collect a widget structure.
40 * \param L The Lua VM state.
41 * \return 0
43 static int
44 luaA_widget_gc(lua_State *L)
46 widget_t *widget = luaL_checkudata(L, 1, "widget");
47 if(widget->destructor)
48 widget->destructor(widget);
49 button_array_wipe(&widget->buttons);
50 luaL_unref(globalconf.L, LUA_REGISTRYINDEX, widget->mouse_enter);
51 luaL_unref(globalconf.L, LUA_REGISTRYINDEX, widget->mouse_leave);
52 return 0;
55 /** Delete a widget node structure.
56 * \param node The node to destroy.
58 void
59 widget_node_delete(widget_node_t *node)
61 widget_unref(globalconf.L, node->widget);
64 /** Get a widget node from a wibox by coords.
65 * \param Container position.
66 * \param widgets The widget list.
67 * \param width The container width.
68 * \param height The container height.
69 * \param x X coordinate of the widget.
70 * \param y Y coordinate of the widget.
71 * \return A widget.
73 widget_t *
74 widget_getbycoords(position_t position, widget_node_array_t *widgets,
75 int width, int height, int16_t *x, int16_t *y)
77 int tmp;
79 /* Need to transform coordinates like it was top/bottom */
80 switch(position)
82 case Right:
83 tmp = *y;
84 *y = width - *x;
85 *x = tmp;
86 break;
87 case Left:
88 tmp = *y;
89 *y = *x;
90 *x = height - tmp;
91 break;
92 default:
93 break;
95 foreach(w, *widgets)
96 if(w->widget->isvisible
97 && *x >= w->geometry.x && *x < w->geometry.x + w->geometry.width
98 && *y >= w->geometry.y && *y < w->geometry.y + w->geometry.height)
99 return w->widget;
101 return NULL;
104 /** Convert a Lua table to a list of widget nodet.
105 * \param L The Lua VM state.
106 * \param widgets The linked list of widget node.
108 void
109 luaA_table2widgets(lua_State *L, widget_node_array_t *widgets)
111 if(lua_istable(L, -1))
113 lua_pushnil(L);
114 while(luaA_next(L, -2))
115 luaA_table2widgets(L, widgets);
116 /* remove the table */
117 lua_pop(L, 1);
119 else
121 widget_t *widget = luaA_toudata(L, -1, "widget");
122 if(widget)
124 widget_node_t w;
125 p_clear(&w, 1);
126 w.widget = widget_ref(L);
127 widget_node_array_append(widgets, w);
129 else
130 lua_pop(L, 1); /* remove value */
134 /** Render a list of widgets.
135 * \param wibox The wibox.
136 * \todo Remove GC.
138 void
139 widget_render(wibox_t *wibox)
141 draw_context_t *ctx = &wibox->sw.ctx;
142 int left = 0, right = 0;
143 area_t rectangle = { 0, 0, 0, 0 };
144 color_t col;
146 rectangle.width = ctx->width;
147 rectangle.height = ctx->height;
149 if(ctx->bg.alpha != 0xffff)
151 int x = wibox->sw.geometry.x, y = wibox->sw.geometry.y;
152 xcb_get_property_reply_t *prop_r;
153 char *data;
154 xcb_pixmap_t rootpix;
155 xcb_get_property_cookie_t prop_c;
156 xcb_screen_t *s = xutil_screen_get(globalconf.connection, ctx->phys_screen);
157 prop_c = xcb_get_property_unchecked(globalconf.connection, false, s->root, _XROOTPMAP_ID,
158 PIXMAP, 0, 1);
159 if((prop_r = xcb_get_property_reply(globalconf.connection, prop_c, NULL)))
161 if(prop_r->value_len
162 && (data = xcb_get_property_value(prop_r))
163 && (rootpix = *(xcb_pixmap_t *) data))
164 switch(wibox->sw.orientation)
166 case North:
167 draw_rotate(ctx,
168 rootpix, ctx->pixmap,
169 s->width_in_pixels, s->height_in_pixels,
170 ctx->width, ctx->height,
171 M_PI_2,
172 y + ctx->width,
173 - x);
174 break;
175 case South:
176 draw_rotate(ctx,
177 rootpix, ctx->pixmap,
178 s->width_in_pixels, s->height_in_pixels,
179 ctx->width, ctx->height,
180 - M_PI_2,
181 - y,
182 x + ctx->height);
183 break;
184 case East:
185 xcb_copy_area(globalconf.connection, rootpix,
186 wibox->sw.pixmap, wibox->sw.gc,
187 x, y,
188 0, 0,
189 ctx->width, ctx->height);
190 break;
192 p_delete(&prop_r);
196 widget_node_array_t *widgets = &wibox->widgets;
198 /* compute geometry */
199 for(int i = 0; i < widgets->len; i++)
200 if(widgets->tab[i].widget->align == AlignLeft
201 && widgets->tab[i].widget->isvisible)
203 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
204 wibox->screen, ctx->height,
205 ctx->width - (left + right));
206 widgets->tab[i].geometry.x = left;
207 left += widgets->tab[i].geometry.width;
210 for(int i = widgets->len - 1; i >= 0; i--)
211 if(widgets->tab[i].widget->align == AlignRight && widgets->tab[i].widget->isvisible)
213 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
214 wibox->screen, ctx->height,
215 ctx->width - (left + right));
216 right += widgets->tab[i].geometry.width;
217 widgets->tab[i].geometry.x = ctx->width - right;
220 /* save left value */
221 int fake_left = left;
223 /* compute width of flex or fixed aligned widgets */
224 int flex = 0;
225 for(int i = 0; i < widgets->len; i++)
226 if(widgets->tab[i].widget->align & (AlignFlex | AlignFixed)
227 && widgets->tab[i].widget->isvisible)
229 if(widgets->tab[i].widget->align_supported & AlignFlex
230 && widgets->tab[i].widget->align == AlignFlex)
231 flex++;
232 else
233 fake_left += widgets->tab[i].widget->geometry(widgets->tab[i].widget,
234 wibox->screen, ctx->height,
235 ctx->width - (fake_left + right)).width;
238 /* now compute everybody together! */
239 int flex_rendered = 0;
240 for(int i = 0; i < widgets->len; i++)
241 if(widgets->tab[i].widget->align & (AlignFlex | AlignFixed)
242 && widgets->tab[i].widget->isvisible)
244 if(widgets->tab[i].widget->align_supported & AlignFlex
245 && widgets->tab[i].widget->align == AlignFlex)
247 int width = (ctx->width - (right + fake_left)) / flex;
248 /* give last pixels to last flex to be rendered */
249 if(flex_rendered == flex - 1)
250 width += (ctx->width - (right + fake_left)) % flex;
251 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
252 wibox->screen, ctx->height,
253 width);
254 flex_rendered++;
256 else
257 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
258 wibox->screen, ctx->height,
259 ctx->width - (left + right));
260 widgets->tab[i].geometry.x = left;
261 left += widgets->tab[i].geometry.width;
264 /* draw background image, only if the background color is not opaque */
265 if(wibox->bg_image && ctx->bg.alpha != 0xffff)
266 draw_image(ctx, 0, 0, 1.0, wibox->bg_image);
268 /* draw background color */
269 xcolor_to_color(&ctx->bg, &col);
270 draw_rectangle(ctx, rectangle, 1.0, true, &col);
272 /* draw everything! */
273 for(int i = 0; i < widgets->len; i++)
274 if(widgets->tab[i].widget->isvisible)
276 widgets->tab[i].geometry.y = 0;
277 widgets->tab[i].widget->draw(widgets->tab[i].widget,
278 ctx, widgets->tab[i].geometry, wibox);
281 switch(wibox->sw.orientation)
283 case South:
284 draw_rotate(ctx, ctx->pixmap, wibox->sw.pixmap,
285 ctx->width, ctx->height,
286 ctx->height, ctx->width,
287 M_PI_2, ctx->height, 0);
288 break;
289 case North:
290 draw_rotate(ctx, ctx->pixmap, wibox->sw.pixmap,
291 ctx->width, ctx->height,
292 ctx->height, ctx->width,
293 - M_PI_2, 0, ctx->width);
294 break;
295 case East:
296 break;
300 /** Invalidate widgets which should be refresh depending on their types.
301 * \param screen Virtual screen.
302 * \param type Widget type to invalidate.
304 void
305 widget_invalidate_bytype(screen_t *screen, widget_constructor_t *type)
307 foreach(wibox, screen->wiboxes)
308 foreach(wnode, (*wibox)->widgets)
309 if(wnode->widget->type == type)
311 (*wibox)->need_update = true;
312 break;
316 /** Set a wibox needs update because it has widget, or redraw a titlebar.
317 * \todo Probably needs more optimization.
318 * \param widget The widget to look for.
320 void
321 widget_invalidate_bywidget(widget_t *widget)
323 foreach(screen, globalconf.screens)
324 foreach(wibox, screen->wiboxes)
325 if(!(*wibox)->need_update)
326 foreach(wnode, (*wibox)->widgets)
327 if(wnode->widget == widget)
329 (*wibox)->need_update = true;
330 break;
333 foreach(_c, globalconf.clients)
335 client_t *c = *_c;
336 if(c->titlebar && !c->titlebar->need_update)
337 for(int j = 0; j < c->titlebar->widgets.len; j++)
338 if(c->titlebar->widgets.tab[j].widget == widget)
340 c->titlebar->need_update = true;
341 break;
346 /** Create a new widget.
347 * \param L The Lua VM state.
349 * \luastack
350 * \lparam A table with at least a type value. Optional attributes
351 * are: align.
352 * \lreturn A brand new widget.
354 static int
355 luaA_widget_new(lua_State *L)
357 const char *align, *type;
358 widget_t *w;
359 widget_constructor_t *wc;
360 size_t len;
362 luaA_checktable(L, 2);
364 type = luaA_getopt_lstring(L, 2, "type", NULL, &len);
366 if((wc = name_func_lookup(type, len, WidgetList)))
368 w = widget_new(L);
369 wc(w);
371 else
373 luaA_warn(L, "unkown widget type: %s", type);
374 return 0;
377 w->type = wc;
379 align = luaA_getopt_lstring(L, 2, "align", "left", &len);
380 w->align_supported |= AlignLeft | AlignRight | AlignFixed;
381 w->align = draw_align_fromstr(align, len);
383 /* Set visible by default. */
384 w->isvisible = true;
386 w->mouse_enter = w->mouse_leave = LUA_REFNIL;
388 return 1;
391 /** Get or set mouse buttons bindings to a widget.
392 * \param L The Lua VM state.
394 * \luastack
395 * \lvalue A widget.
396 * \lparam An array of mouse button bindings objects, or nothing.
397 * \return The array of mouse button bindings objects of this widget.
399 static int
400 luaA_widget_buttons(lua_State *L)
402 widget_t *widget = luaL_checkudata(L, 1, "widget");
403 button_array_t *buttons = &widget->buttons;
405 if(lua_gettop(L) == 2)
407 luaA_button_array_set(L, 2, buttons);
408 return 1;
411 return luaA_button_array_get(L, buttons);
414 /** Generic widget.
415 * \param L The Lua VM state.
416 * \return The number of elements pushed on stack.
417 * \luastack
418 * \lfield align The widget alignment.
419 * \lfield visible The widget visibility.
420 * \lfield mouse_enter A function to execute when the mouse enter the widget.
421 * \lfield mouse_leave A function to execute when the mouse leave the widget.
423 static int
424 luaA_widget_index(lua_State *L)
426 size_t len;
427 widget_t *widget = luaL_checkudata(L, 1, "widget");
428 const char *buf = luaL_checklstring(L, 2, &len);
429 awesome_token_t token;
431 if(luaA_usemetatable(L, 1, 2))
432 return 1;
434 switch((token = a_tokenize(buf, len)))
436 case A_TK_ALIGN:
437 lua_pushstring(L, draw_align_tostr(widget->align));
438 return 1;
439 case A_TK_VISIBLE:
440 lua_pushboolean(L, widget->isvisible);
441 return 1;
442 case A_TK_MOUSE_ENTER:
443 if(widget->mouse_enter != LUA_REFNIL)
444 lua_rawgeti(L, LUA_REGISTRYINDEX, widget->mouse_enter);
445 else
446 return 0;
447 return 1;
448 case A_TK_MOUSE_LEAVE:
449 if(widget->mouse_leave != LUA_REFNIL)
450 lua_rawgeti(L, LUA_REGISTRYINDEX, widget->mouse_leave);
451 else
452 return 0;
453 return 1;
454 default:
455 break;
458 return widget->index ? widget->index(L, token) : 0;
461 /** Generic widget newindex.
462 * \param L The Lua VM state.
463 * \return The number of elements pushed on stack.
465 static int
466 luaA_widget_newindex(lua_State *L)
468 size_t len;
469 widget_t *widget = luaL_checkudata(L, 1, "widget");
470 const char *buf = luaL_checklstring(L, 2, &len);
471 awesome_token_t token;
473 switch((token = a_tokenize(buf, len)))
475 case A_TK_ALIGN:
476 buf = luaL_checklstring(L, 3, &len);
477 widget->align = draw_align_fromstr(buf, len);
478 break;
479 case A_TK_VISIBLE:
480 widget->isvisible = luaA_checkboolean(L, 3);
481 break;
482 case A_TK_MOUSE_ENTER:
483 luaA_registerfct(L, 3, &widget->mouse_enter);
484 return 0;
485 case A_TK_MOUSE_LEAVE:
486 luaA_registerfct(L, 3, &widget->mouse_leave);
487 return 0;
488 default:
489 return widget->newindex ? widget->newindex(L, token) : 0;
492 widget_invalidate_bywidget(widget);
494 return 0;
497 const struct luaL_reg awesome_widget_methods[] =
499 { "__call", luaA_widget_new },
500 { NULL, NULL }
502 const struct luaL_reg awesome_widget_meta[] =
504 { "buttons", luaA_widget_buttons },
505 { "__index", luaA_widget_index },
506 { "__newindex", luaA_widget_newindex },
507 { "__gc", luaA_widget_gc },
508 { "__tostring", luaA_widget_tostring },
509 { NULL, NULL }
512 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80