change codename
[awesome.git] / widget.c
bloba476713506642cd4b80b10cc7549485b21b1d596
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 luaA_ref_array_wipe(&widget->refs);
48 if(widget->destructor)
49 widget->destructor(widget);
50 button_array_wipe(&widget->buttons);
51 luaL_unref(globalconf.L, LUA_REGISTRYINDEX, widget->mouse_enter);
52 luaL_unref(globalconf.L, LUA_REGISTRYINDEX, widget->mouse_leave);
53 return 0;
56 /** Delete a widget node structure.
57 * \param node The node to destroy.
59 void
60 widget_node_delete(widget_node_t *node)
62 widget_unref(globalconf.L, node->widget);
65 /** Get a widget node from a wibox by coords.
66 * \param orientation Wibox orientation.
67 * \param widgets The widget list.
68 * \param width The container width.
69 * \param height The container height.
70 * \param x X coordinate of the widget.
71 * \param y Y coordinate of the widget.
72 * \return A widget.
74 widget_t *
75 widget_getbycoords(orientation_t orientation, widget_node_array_t *widgets,
76 int width, int height, int16_t *x, int16_t *y)
78 int tmp;
80 /* Need to transform coordinates like it was top/bottom */
81 switch(orientation)
83 case South:
84 tmp = *y;
85 *y = width - *x;
86 *x = tmp;
87 break;
88 case North:
89 tmp = *y;
90 *y = *x;
91 *x = height - tmp;
92 break;
93 default:
94 break;
96 foreach(w, *widgets)
97 if(w->widget->isvisible
98 && *x >= w->geometry.x && *x < w->geometry.x + w->geometry.width
99 && *y >= w->geometry.y && *y < w->geometry.y + w->geometry.height)
100 return w->widget;
102 return NULL;
105 /** Convert a Lua table to a list of widget nodet.
106 * \param L The Lua VM state.
107 * \param widgets The linked list of widget node.
109 void
110 luaA_table2widgets(lua_State *L, widget_node_array_t *widgets)
112 if(lua_istable(L, -1))
114 lua_pushnil(L);
115 while(luaA_next(L, -2))
116 luaA_table2widgets(L, widgets);
117 /* remove the table */
118 lua_pop(L, 1);
120 else
122 widget_t *widget = luaA_toudata(L, -1, "widget");
123 if(widget)
125 widget_node_t w;
126 p_clear(&w, 1);
127 w.widget = widget_ref(L);
128 widget_node_array_append(widgets, w);
130 else
131 lua_pop(L, 1); /* remove value */
135 /** Render a list of widgets.
136 * \param wibox The wibox.
137 * \todo Remove GC.
139 void
140 widget_render(wibox_t *wibox)
142 draw_context_t *ctx = &wibox->sw.ctx;
143 int left = 0, right = 0;
144 area_t rectangle = { 0, 0, 0, 0 };
145 color_t col;
147 rectangle.width = ctx->width;
148 rectangle.height = ctx->height;
150 if(ctx->bg.alpha != 0xffff)
152 int x = wibox->sw.geometry.x, y = wibox->sw.geometry.y;
153 xcb_get_property_reply_t *prop_r;
154 char *data;
155 xcb_pixmap_t rootpix;
156 xcb_get_property_cookie_t prop_c;
157 xcb_screen_t *s = xutil_screen_get(globalconf.connection, ctx->phys_screen);
158 prop_c = xcb_get_property_unchecked(globalconf.connection, false, s->root, _XROOTPMAP_ID,
159 PIXMAP, 0, 1);
160 if((prop_r = xcb_get_property_reply(globalconf.connection, prop_c, NULL)))
162 if(prop_r->value_len
163 && (data = xcb_get_property_value(prop_r))
164 && (rootpix = *(xcb_pixmap_t *) data))
165 switch(wibox->sw.orientation)
167 case North:
168 draw_rotate(ctx,
169 rootpix, ctx->pixmap,
170 s->width_in_pixels, s->height_in_pixels,
171 ctx->width, ctx->height,
172 M_PI_2,
173 y + ctx->width,
174 - x);
175 break;
176 case South:
177 draw_rotate(ctx,
178 rootpix, ctx->pixmap,
179 s->width_in_pixels, s->height_in_pixels,
180 ctx->width, ctx->height,
181 - M_PI_2,
182 - y,
183 x + ctx->height);
184 break;
185 case East:
186 xcb_copy_area(globalconf.connection, rootpix,
187 wibox->sw.pixmap, wibox->sw.gc,
188 x, y,
189 0, 0,
190 ctx->width, ctx->height);
191 break;
193 p_delete(&prop_r);
197 widget_node_array_t *widgets = &wibox->widgets;
199 /* compute geometry */
200 for(int i = 0; i < widgets->len; i++)
201 if(widgets->tab[i].widget->align == AlignLeft
202 && widgets->tab[i].widget->isvisible)
204 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
205 wibox->screen, ctx->height,
206 ctx->width - (left + right));
207 widgets->tab[i].geometry.x = left;
208 left += widgets->tab[i].geometry.width;
211 for(int i = widgets->len - 1; i >= 0; i--)
212 if(widgets->tab[i].widget->align == AlignRight && widgets->tab[i].widget->isvisible)
214 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
215 wibox->screen, ctx->height,
216 ctx->width - (left + right));
217 right += widgets->tab[i].geometry.width;
218 widgets->tab[i].geometry.x = ctx->width - right;
221 /* save left value */
222 int fake_left = left;
224 /* compute width of flex or fixed aligned widgets */
225 int flex = 0;
226 for(int i = 0; i < widgets->len; i++)
227 if(widgets->tab[i].widget->align & (AlignFlex | AlignFixed)
228 && widgets->tab[i].widget->isvisible)
230 if(widgets->tab[i].widget->align_supported & AlignFlex
231 && widgets->tab[i].widget->align == AlignFlex)
232 flex++;
233 else
234 fake_left += widgets->tab[i].widget->geometry(widgets->tab[i].widget,
235 wibox->screen, ctx->height,
236 ctx->width - (fake_left + right)).width;
239 /* now compute everybody together! */
240 int flex_rendered = 0;
241 for(int i = 0; i < widgets->len; i++)
242 if(widgets->tab[i].widget->align & (AlignFlex | AlignFixed)
243 && widgets->tab[i].widget->isvisible)
245 if(widgets->tab[i].widget->align_supported & AlignFlex
246 && widgets->tab[i].widget->align == AlignFlex)
248 int width = (ctx->width - (right + fake_left)) / flex;
249 /* give last pixels to last flex to be rendered */
250 if(flex_rendered == flex - 1)
251 width += (ctx->width - (right + fake_left)) % flex;
252 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
253 wibox->screen, ctx->height,
254 width);
255 flex_rendered++;
257 else
258 widgets->tab[i].geometry = widgets->tab[i].widget->geometry(widgets->tab[i].widget,
259 wibox->screen, ctx->height,
260 ctx->width - (left + right));
261 widgets->tab[i].geometry.x = left;
262 left += widgets->tab[i].geometry.width;
265 /* draw background image, only if the background color is not opaque */
266 if(wibox->bg_image && ctx->bg.alpha != 0xffff)
267 draw_image(ctx, 0, 0, 1.0, wibox->bg_image);
269 /* draw background color */
270 xcolor_to_color(&ctx->bg, &col);
271 draw_rectangle(ctx, rectangle, 1.0, true, &col);
273 /* draw everything! */
274 for(int i = 0; i < widgets->len; i++)
275 if(widgets->tab[i].widget->isvisible)
277 widgets->tab[i].geometry.y = 0;
278 widgets->tab[i].widget->draw(widgets->tab[i].widget,
279 ctx, widgets->tab[i].geometry, wibox);
282 switch(wibox->sw.orientation)
284 case South:
285 draw_rotate(ctx, ctx->pixmap, wibox->sw.pixmap,
286 ctx->width, ctx->height,
287 ctx->height, ctx->width,
288 M_PI_2, ctx->height, 0);
289 break;
290 case North:
291 draw_rotate(ctx, ctx->pixmap, wibox->sw.pixmap,
292 ctx->width, ctx->height,
293 ctx->height, ctx->width,
294 - M_PI_2, 0, ctx->width);
295 break;
296 case East:
297 break;
301 /** Invalidate widgets which should be refresh depending on their types.
302 * \param screen Virtual screen.
303 * \param type Widget type to invalidate.
305 void
306 widget_invalidate_bytype(screen_t *screen, widget_constructor_t *type)
308 foreach(wibox, screen->wiboxes)
309 foreach(wnode, (*wibox)->widgets)
310 if(wnode->widget->type == type)
312 (*wibox)->need_update = true;
313 break;
317 /** Set a wibox needs update because it has widget, or redraw a titlebar.
318 * \todo Probably needs more optimization.
319 * \param widget The widget to look for.
321 void
322 widget_invalidate_bywidget(widget_t *widget)
324 foreach(screen, globalconf.screens)
325 foreach(wibox, screen->wiboxes)
326 if(!(*wibox)->need_update)
327 foreach(wnode, (*wibox)->widgets)
328 if(wnode->widget == widget)
330 (*wibox)->need_update = true;
331 break;
334 foreach(_c, globalconf.clients)
336 client_t *c = *_c;
337 if(c->titlebar && !c->titlebar->need_update)
338 for(int j = 0; j < c->titlebar->widgets.len; j++)
339 if(c->titlebar->widgets.tab[j].widget == widget)
341 c->titlebar->need_update = true;
342 break;
347 /** Create a new widget.
348 * \param L The Lua VM state.
350 * \luastack
351 * \lparam A table with at least a type value. Optional attributes
352 * are: align.
353 * \lreturn A brand new widget.
355 static int
356 luaA_widget_new(lua_State *L)
358 const char *align, *type;
359 widget_t *w;
360 widget_constructor_t *wc;
361 size_t len;
363 luaA_checktable(L, 2);
365 type = luaA_getopt_lstring(L, 2, "type", NULL, &len);
367 if((wc = name_func_lookup(type, len, WidgetList)))
369 w = widget_new(L);
370 wc(w);
372 else
374 luaA_warn(L, "unkown widget type: %s", type);
375 return 0;
378 w->type = wc;
380 align = luaA_getopt_lstring(L, 2, "align", "left", &len);
381 w->align_supported |= AlignLeft | AlignRight | AlignFixed;
382 w->align = draw_align_fromstr(align, len);
384 /* Set visible by default. */
385 w->isvisible = true;
387 w->mouse_enter = w->mouse_leave = LUA_REFNIL;
389 return 1;
392 /** Get or set mouse buttons bindings to a widget.
393 * \param L The Lua VM state.
395 * \luastack
396 * \lvalue A widget.
397 * \lparam An array of mouse button bindings objects, or nothing.
398 * \return The array of mouse button bindings objects of this widget.
400 static int
401 luaA_widget_buttons(lua_State *L)
403 widget_t *widget = luaL_checkudata(L, 1, "widget");
404 button_array_t *buttons = &widget->buttons;
406 if(lua_gettop(L) == 2)
408 luaA_button_array_set(L, 2, buttons);
409 return 1;
412 return luaA_button_array_get(L, buttons);
415 /** Generic widget.
416 * \param L The Lua VM state.
417 * \return The number of elements pushed on stack.
418 * \luastack
419 * \lfield align The widget alignment.
420 * \lfield visible The widget visibility.
421 * \lfield mouse_enter A function to execute when the mouse enter the widget.
422 * \lfield mouse_leave A function to execute when the mouse leave the widget.
424 static int
425 luaA_widget_index(lua_State *L)
427 size_t len;
428 widget_t *widget = luaL_checkudata(L, 1, "widget");
429 const char *buf = luaL_checklstring(L, 2, &len);
430 awesome_token_t token;
432 if(luaA_usemetatable(L, 1, 2))
433 return 1;
435 switch((token = a_tokenize(buf, len)))
437 case A_TK_ALIGN:
438 lua_pushstring(L, draw_align_tostr(widget->align));
439 return 1;
440 case A_TK_VISIBLE:
441 lua_pushboolean(L, widget->isvisible);
442 return 1;
443 case A_TK_MOUSE_ENTER:
444 if(widget->mouse_enter != LUA_REFNIL)
445 lua_rawgeti(L, LUA_REGISTRYINDEX, widget->mouse_enter);
446 else
447 return 0;
448 return 1;
449 case A_TK_MOUSE_LEAVE:
450 if(widget->mouse_leave != LUA_REFNIL)
451 lua_rawgeti(L, LUA_REGISTRYINDEX, widget->mouse_leave);
452 else
453 return 0;
454 return 1;
455 default:
456 break;
459 return widget->index ? widget->index(L, token) : 0;
462 /** Generic widget newindex.
463 * \param L The Lua VM state.
464 * \return The number of elements pushed on stack.
466 static int
467 luaA_widget_newindex(lua_State *L)
469 size_t len;
470 widget_t *widget = luaL_checkudata(L, 1, "widget");
471 const char *buf = luaL_checklstring(L, 2, &len);
472 awesome_token_t token;
474 switch((token = a_tokenize(buf, len)))
476 case A_TK_ALIGN:
477 buf = luaL_checklstring(L, 3, &len);
478 widget->align = draw_align_fromstr(buf, len);
479 break;
480 case A_TK_VISIBLE:
481 widget->isvisible = luaA_checkboolean(L, 3);
482 break;
483 case A_TK_MOUSE_ENTER:
484 luaA_registerfct(L, 3, &widget->mouse_enter);
485 return 0;
486 case A_TK_MOUSE_LEAVE:
487 luaA_registerfct(L, 3, &widget->mouse_leave);
488 return 0;
489 default:
490 return widget->newindex ? widget->newindex(L, token) : 0;
493 widget_invalidate_bywidget(widget);
495 return 0;
498 const struct luaL_reg awesome_widget_methods[] =
500 { "__call", luaA_widget_new },
501 { NULL, NULL }
503 const struct luaL_reg awesome_widget_meta[] =
505 { "buttons", luaA_widget_buttons },
506 { "__index", luaA_widget_index },
507 { "__newindex", luaA_widget_newindex },
508 { "__gc", luaA_widget_gc },
509 { "__tostring", luaA_widget_tostring },
510 { NULL, NULL }
513 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80