screen: implement tags set/get as function
[awesome.git] / statusbar.c
blob92b6b1facfab41c8bef81a4b0a66c2ea122dd0a9
1 /*
2 * statusbar.c - statusbar 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 <xcb/xcb.h>
24 #include "client.h"
25 #include "statusbar.h"
26 #include "screen.h"
27 #include "widget.h"
28 #include "ewmh.h"
30 extern awesome_t globalconf;
32 DO_LUA_NEW(extern, statusbar_t, statusbar, "statusbar", statusbar_ref)
33 DO_LUA_GC(statusbar_t, statusbar, "statusbar", statusbar_unref)
34 DO_LUA_EQ(statusbar_t, statusbar, "statusbar")
36 /** Kick out systray windows.
37 * \param phys_screen Physical screen number.
39 static void
40 statusbar_systray_kickout(int phys_screen)
42 xembed_window_t *em;
43 uint32_t config_win_vals_off[2] = { -512, -512 };
45 for(em = globalconf.embedded; em; em = em->next)
46 if(em->phys_screen == phys_screen)
47 xcb_configure_window(globalconf.connection, em->win,
48 XCB_CONFIG_WINDOW_X
49 | XCB_CONFIG_WINDOW_Y,
50 config_win_vals_off);
53 static void
54 statusbar_systray_refresh(statusbar_t *statusbar)
56 widget_node_t *systray;
58 if(statusbar->screen == SCREEN_UNDEF)
59 return;
61 for(systray = statusbar->widgets; systray; systray = systray->next)
62 if(systray->widget->type == systray_new)
64 uint32_t config_win_vals[4];
65 uint32_t config_win_vals_off[2] = { -512, -512 };
66 xembed_window_t *em;
67 position_t pos;
69 if(statusbar->position
70 && systray->widget->isvisible)
72 pos = statusbar->position;
73 /* width */
74 config_win_vals[2] = systray->area.height;
75 /* height */
76 config_win_vals[3] = systray->area.height;
78 else
79 /* hide */
80 pos = Off;
82 switch(pos)
84 case Left:
85 config_win_vals[0] = statusbar->sw->geometry.x + systray->area.y;
86 config_win_vals[1] = statusbar->sw->geometry.y + statusbar->sw->geometry.height
87 - systray->area.x - config_win_vals[3];
88 for(em = globalconf.embedded; em; em = em->next)
89 if(em->phys_screen == statusbar->phys_screen)
91 if(config_win_vals[1] - config_win_vals[2] >= (uint32_t) statusbar->sw->geometry.y)
93 xcb_map_window(globalconf.connection, em->win);
94 xcb_configure_window(globalconf.connection, em->win,
95 XCB_CONFIG_WINDOW_X
96 | XCB_CONFIG_WINDOW_Y
97 | XCB_CONFIG_WINDOW_WIDTH
98 | XCB_CONFIG_WINDOW_HEIGHT,
99 config_win_vals);
100 config_win_vals[1] -= config_win_vals[3];
102 else
103 xcb_configure_window(globalconf.connection, em->win,
104 XCB_CONFIG_WINDOW_X
105 | XCB_CONFIG_WINDOW_Y,
106 config_win_vals_off);
108 client_stack();
109 break;
110 case Right:
111 config_win_vals[0] = statusbar->sw->geometry.x - systray->area.y;
112 config_win_vals[1] = statusbar->sw->geometry.y + systray->area.x;
113 for(em = globalconf.embedded; em; em = em->next)
114 if(em->phys_screen == statusbar->phys_screen)
116 if(config_win_vals[1] + config_win_vals[3] <= (uint32_t) statusbar->sw->geometry.y + statusbar->ctx->width)
118 xcb_map_window(globalconf.connection, em->win);
119 xcb_configure_window(globalconf.connection, em->win,
120 XCB_CONFIG_WINDOW_X
121 | XCB_CONFIG_WINDOW_Y
122 | XCB_CONFIG_WINDOW_WIDTH
123 | XCB_CONFIG_WINDOW_HEIGHT,
124 config_win_vals);
125 config_win_vals[1] += config_win_vals[3];
127 else
128 xcb_configure_window(globalconf.connection, em->win,
129 XCB_CONFIG_WINDOW_X
130 | XCB_CONFIG_WINDOW_Y,
131 config_win_vals_off);
133 client_stack();
134 break;
135 case Top:
136 case Bottom:
137 config_win_vals[0] = statusbar->sw->geometry.x + systray->area.x;
138 config_win_vals[1] = statusbar->sw->geometry.y + systray->area.y;
139 for(em = globalconf.embedded; em; em = em->next)
140 if(em->phys_screen == statusbar->phys_screen)
142 /* if(x + width < systray.x + systray.width) */
143 if(config_win_vals[0] + config_win_vals[2] <= (uint32_t) AREA_RIGHT(systray->area) + statusbar->sw->geometry.x)
145 xcb_map_window(globalconf.connection, em->win);
146 xcb_configure_window(globalconf.connection, em->win,
147 XCB_CONFIG_WINDOW_X
148 | XCB_CONFIG_WINDOW_Y
149 | XCB_CONFIG_WINDOW_WIDTH
150 | XCB_CONFIG_WINDOW_HEIGHT,
151 config_win_vals);
152 config_win_vals[0] += config_win_vals[2];
154 else
155 xcb_configure_window(globalconf.connection, em->win,
156 XCB_CONFIG_WINDOW_X
157 | XCB_CONFIG_WINDOW_Y,
158 config_win_vals_off);
160 client_stack();
161 break;
162 default:
163 statusbar_systray_kickout(statusbar->phys_screen);
164 break;
166 break;
170 /** Draw a statusbar.
171 * \param statusbar The statusbar to draw.
173 static void
174 statusbar_draw(statusbar_t *statusbar)
176 statusbar->need_update = false;
178 if(statusbar->position)
180 widget_render(statusbar->widgets, statusbar->ctx, statusbar->sw->gc,
181 statusbar->sw->pixmap,
182 statusbar->screen, statusbar->position,
183 statusbar->sw->geometry.x, statusbar->sw->geometry.y,
184 statusbar, AWESOME_TYPE_STATUSBAR);
185 simplewindow_refresh_pixmap(statusbar->sw);
188 statusbar_systray_refresh(statusbar);
191 /** Statusbar refresh function.
193 void
194 statusbar_refresh(void)
196 int screen;
197 statusbar_t *statusbar;
199 for(screen = 0; screen < globalconf.screens_info->nscreen; screen++)
200 for(statusbar = globalconf.screens[screen].statusbar; statusbar; statusbar = statusbar->next)
201 if(statusbar->need_update)
202 statusbar_draw(statusbar);
205 /** Update the statusbar position. It deletes every statusbar resources and
206 * create them back.
207 * \param statusbar The statusbar.
209 static void
210 statusbar_position_update(statusbar_t *statusbar)
212 statusbar_t *sb;
213 area_t area;
214 xcb_pixmap_t dw;
215 xcb_screen_t *s = NULL;
216 bool ignore = false;
218 globalconf.screens[statusbar->screen].need_arrange = true;
220 simplewindow_delete(&statusbar->sw);
221 draw_context_delete(&statusbar->ctx);
223 if(statusbar->position == Off)
224 return;
226 area = screen_area_get(&globalconf.screens[statusbar->screen].geometry,
227 NULL,
228 &globalconf.screens[statusbar->screen].padding);
230 /* Top and Bottom statusbar_t have prio */
231 for(sb = globalconf.screens[statusbar->screen].statusbar; sb; sb = sb->next)
233 /* Ignore every statusbar after me that is in the same position */
234 if(statusbar == sb)
236 ignore = true;
237 continue;
239 else if(ignore && statusbar->position == sb->position)
240 continue;
241 switch(sb->position)
243 case Left:
244 switch(statusbar->position)
246 case Left:
247 area.x += statusbar->height;
248 break;
249 default:
250 break;
252 break;
253 case Right:
254 switch(statusbar->position)
256 case Right:
257 area.x -= statusbar->height;
258 break;
259 default:
260 break;
262 break;
263 case Top:
264 switch(statusbar->position)
266 case Top:
267 area.y += sb->height;
268 break;
269 case Left:
270 case Right:
271 area.height -= sb->height;
272 area.y += sb->height;
273 break;
274 default:
275 break;
277 break;
278 case Bottom:
279 switch(statusbar->position)
281 case Bottom:
282 area.y -= sb->height;
283 break;
284 case Left:
285 case Right:
286 area.height -= sb->height;
287 break;
288 default:
289 break;
291 break;
292 default:
293 break;
297 switch(statusbar->position)
299 case Right:
300 case Left:
301 if(!statusbar->width_user)
302 statusbar->width = area.height;
303 statusbar->sw =
304 simplewindow_new(globalconf.connection, statusbar->phys_screen, 0, 0,
305 statusbar->height, statusbar->width, 0);
306 s = xutil_screen_get(globalconf.connection, statusbar->phys_screen);
307 /* we need a new pixmap this way [ ] to render */
308 dw = xcb_generate_id(globalconf.connection);
309 xcb_create_pixmap(globalconf.connection,
310 s->root_depth, dw, s->root,
311 statusbar->width, statusbar->height);
312 statusbar->ctx = draw_context_new(globalconf.connection,
313 statusbar->phys_screen,
314 statusbar->width,
315 statusbar->height,
317 &statusbar->colors.fg,
318 &statusbar->colors.bg);
319 break;
320 default:
321 if(!statusbar->width_user)
322 statusbar->width = area.width;
323 statusbar->sw =
324 simplewindow_new(globalconf.connection, statusbar->phys_screen, 0, 0,
325 statusbar->width, statusbar->height, 0);
326 statusbar->ctx = draw_context_new(globalconf.connection,
327 statusbar->phys_screen,
328 statusbar->width,
329 statusbar->height,
330 statusbar->sw->pixmap,
331 &statusbar->colors.fg,
332 &statusbar->colors.bg);
333 break;
336 switch(statusbar->position)
338 default:
339 switch(statusbar->align)
341 default:
342 simplewindow_move(statusbar->sw, area.x, area.y);
343 break;
344 case AlignRight:
345 simplewindow_move(statusbar->sw,
346 area.x + area.width - statusbar->width, area.y);
347 break;
348 case AlignCenter:
349 simplewindow_move(statusbar->sw,
350 area.x + (area.width - statusbar->width) / 2, area.y);
351 break;
353 break;
354 case Bottom:
355 switch(statusbar->align)
357 default:
358 simplewindow_move(statusbar->sw,
359 area.x, (area.y + area.height) - statusbar->height);
360 break;
361 case AlignRight:
362 simplewindow_move(statusbar->sw,
363 area.x + area.width - statusbar->width,
364 (area.y + area.height) - statusbar->height);
365 break;
366 case AlignCenter:
367 simplewindow_move(statusbar->sw,
368 area.x + (area.width - statusbar->width) / 2,
369 (area.y + area.height) - statusbar->height);
370 break;
372 break;
373 case Left:
374 switch(statusbar->align)
376 default:
377 simplewindow_move(statusbar->sw, area.x,
378 (area.y + area.height) - statusbar->sw->geometry.height);
379 break;
380 case AlignRight:
381 simplewindow_move(statusbar->sw, area.x, area.y);
382 break;
383 case AlignCenter:
384 simplewindow_move(statusbar->sw, area.x, (area.y + area.height - statusbar->width) / 2);
386 break;
387 case Right:
388 switch(statusbar->align)
390 default:
391 simplewindow_move(statusbar->sw, area.x + area.width - statusbar->height, area.y);
392 break;
393 case AlignRight:
394 simplewindow_move(statusbar->sw, area.x + area.width - statusbar->height,
395 area.y + area.height - statusbar->width);
396 break;
397 case AlignCenter:
398 simplewindow_move(statusbar->sw, area.x + area.width - statusbar->height,
399 (area.y + area.height - statusbar->width) / 2);
400 break;
402 break;
405 xcb_map_window(globalconf.connection, statusbar->sw->window);
407 /* Set need update */
408 statusbar->need_update = true;
411 /** Convert a statusbar to a printable string.
412 * \param L The Lua VM state.
414 * \luastack
415 * \lvalue A statusbar.
417 static int
418 luaA_statusbar_tostring(lua_State *L)
420 statusbar_t **p = luaA_checkudata(L, 1, "statusbar");
421 lua_pushfstring(L, "[statusbar udata(%p) name(%s)]", *p, (*p)->name);
422 return 1;
425 /** Create a new statusbar.
426 * \param L The Lua VM state.
428 * \luastack
429 * \lparam A table with at least a name attribute. Optionaly defined values are:
430 * position, align, fg, bg, width and height.
431 * \lreturn A brand new statusbar.
433 static int
434 luaA_statusbar_new(lua_State *L)
436 statusbar_t *sb;
437 const char *buf;
438 size_t len;
439 xcolor_init_request_t reqs[2];
440 int8_t i, reqs_nbr = -1;
442 luaA_checktable(L, 2);
444 if(!(buf = luaA_getopt_string(L, 2, "name", NULL)))
445 luaL_error(L, "object statusbar must have a name");
447 sb = p_new(statusbar_t, 1);
449 sb->name = a_strdup(buf);
451 sb->colors.fg = globalconf.colors.fg;
452 if((buf = luaA_getopt_lstring(L, 2, "fg", NULL, &len)))
453 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
454 &sb->colors.fg,
455 globalconf.default_screen,
456 buf, len);
458 sb->colors.bg = globalconf.colors.bg;
459 if((buf = luaA_getopt_lstring(L, 2, "bg", NULL, &len)))
460 reqs[++reqs_nbr] = xcolor_init_unchecked(globalconf.connection,
461 &sb->colors.bg,
462 globalconf.default_screen,
463 buf, len);
465 buf = luaA_getopt_lstring(L, 2, "align", "left", &len);
466 sb->align = draw_align_fromstr(buf, len);
468 sb->width = luaA_getopt_number(L, 2, "width", 0);
469 if(sb->width > 0)
470 sb->width_user = true;
471 sb->height = luaA_getopt_number(L, 2, "height", 0);
472 if(sb->height <= 0)
473 /* 1.5 as default factor, it fits nice but no one knows why */
474 sb->height = 1.5 * globalconf.font->height;
476 buf = luaA_getopt_lstring(L, 2, "position", "top", &len);
477 sb->position = position_fromstr(buf, len);
479 sb->screen = SCREEN_UNDEF;
481 for(i = 0; i <= reqs_nbr; i++)
482 xcolor_init_reply(globalconf.connection, reqs[i]);
484 return luaA_statusbar_userdata_new(L, sb);
487 /** Statusbar object.
488 * \param L The Lua VM state.
489 * \return The number of elements pushed on stack.
490 * \luastack
491 * \lfield screen Screen number.
492 * \lfield align The alignment.
493 * \lfield fg Foreground color.
494 * \lfield bg Background color.
495 * \lfield position The position.
496 * \lfield widget The statusbar widgets.
498 static int
499 luaA_statusbar_index(lua_State *L)
501 size_t len;
502 int i = 0;
503 widget_node_t *witer;
504 statusbar_t **statusbar = luaA_checkudata(L, 1, "statusbar");
505 const char *attr = luaL_checklstring(L, 2, &len);
507 if(luaA_usemetatable(L, 1, 2))
508 return 1;
510 switch(a_tokenize(attr, len))
512 case A_TK_SCREEN:
513 if((*statusbar)->screen == SCREEN_UNDEF)
514 return 0;
515 lua_pushnumber(L, (*statusbar)->screen + 1);
516 break;
517 case A_TK_ALIGN:
518 lua_pushstring(L, draw_align_tostr((*statusbar)->align));
519 break;
520 case A_TK_FG:
521 luaA_pushcolor(L, &(*statusbar)->colors.fg);
522 break;
523 case A_TK_BG:
524 luaA_pushcolor(L, &(*statusbar)->colors.bg);
525 break;
526 case A_TK_POSITION:
527 lua_pushstring(L, position_tostr((*statusbar)->position));
528 break;
529 case A_TK_WIDGETS:
530 lua_newtable(L);
531 for(witer = (*statusbar)->widgets; witer; witer = witer->next)
533 luaA_widget_userdata_new(L, witer->widget);
534 lua_rawseti(L, -2, ++i);
536 break;
537 default:
538 return 0;
541 return 1;
544 /** Remove a statubar from a screen.
545 * \param statusbar Statusbar to detach from screen.
547 static void
548 statusbar_remove(statusbar_t *statusbar)
550 if(statusbar->screen != SCREEN_UNDEF)
552 position_t p;
554 statusbar_systray_kickout(statusbar->phys_screen);
556 /* save position */
557 p = statusbar->position;
558 statusbar->position = Off;
559 statusbar_position_update(statusbar);
560 /* restore position */
561 statusbar->position = p;
563 statusbar_list_detach(&globalconf.screens[statusbar->screen].statusbar, statusbar);
564 globalconf.screens[statusbar->screen].need_arrange = true;
565 statusbar->screen = SCREEN_UNDEF;
566 statusbar_unref(&statusbar);
570 /** Statusbar newindex.
571 * \param L The Lua VM state.
572 * \return The number of elements pushed on stack.
574 static int
575 luaA_statusbar_newindex(lua_State *L)
577 size_t len;
578 statusbar_t *s, **statusbar = luaA_checkudata(L, 1, "statusbar");
579 const char *buf, *attr = luaL_checklstring(L, 2, &len);
580 position_t p;
581 int screen;
582 widget_node_t *witer;
584 switch(a_tokenize(attr, len))
586 case A_TK_SCREEN:
587 if(lua_isnil(L, 3))
588 statusbar_remove(*statusbar);
589 else
591 screen = luaL_checknumber(L, 3) - 1;
593 luaA_checkscreen(screen);
595 if((*statusbar)->screen == screen)
596 luaL_error(L, "this statusbar is already on screen %d",
597 (*statusbar)->screen + 1);
599 /* Check for uniq name and id. */
600 for(s = globalconf.screens[screen].statusbar; s; s = s->next)
601 if(!a_strcmp(s->name, (*statusbar)->name))
602 luaL_error(L, "a statusbar with that name is already on screen %d\n",
603 screen + 1);
605 statusbar_remove(*statusbar);
607 (*statusbar)->screen = screen;
608 (*statusbar)->phys_screen = screen_virttophys(screen);
610 statusbar_list_append(&globalconf.screens[screen].statusbar, *statusbar);
611 statusbar_ref(statusbar);
613 /* All the other statusbar and ourselves need to be repositioned */
614 for(s = globalconf.screens[screen].statusbar; s; s = s->next)
615 statusbar_position_update(s);
617 ewmh_update_workarea((*statusbar)->phys_screen);
619 break;
620 case A_TK_ALIGN:
621 buf = luaL_checklstring(L, 3, &len);
622 (*statusbar)->align = draw_align_fromstr(buf, len);
623 statusbar_position_update(*statusbar);
624 break;
625 case A_TK_FG:
626 if((buf = luaL_checklstring(L, 3, &len)))
627 if(xcolor_init_reply(globalconf.connection,
628 xcolor_init_unchecked(globalconf.connection,
629 &(*statusbar)->colors.fg,
630 globalconf.default_screen,
631 buf, len)))
633 if((*statusbar)->ctx)
634 (*statusbar)->ctx->fg = (*statusbar)->colors.fg;
635 (*statusbar)->need_update = true;
637 break;
638 case A_TK_BG:
639 if((buf = luaL_checklstring(L, 3, &len)))
640 if(xcolor_init_reply(globalconf.connection,
641 xcolor_init_unchecked(globalconf.connection,
642 &(*statusbar)->colors.bg,
643 globalconf.default_screen, buf, len)))
645 if((*statusbar)->ctx)
646 (*statusbar)->ctx->bg = (*statusbar)->colors.bg;
648 (*statusbar)->need_update = true;
650 break;
651 case A_TK_POSITION:
652 buf = luaL_checklstring(L, 3, &len);
653 p = position_fromstr(buf, len);
654 if(p != (*statusbar)->position)
656 (*statusbar)->position = p;
657 if((*statusbar)->screen != SCREEN_UNDEF)
659 for(s = globalconf.screens[(*statusbar)->screen].statusbar; s; s = s->next)
660 statusbar_position_update(s);
661 ewmh_update_workarea((*statusbar)->phys_screen);
664 break;
665 case A_TK_WIDGETS:
666 luaA_checktable(L, 3);
668 /* remove all widgets */
669 for(witer = (*statusbar)->widgets; witer; witer = (*statusbar)->widgets)
671 if(witer->widget->detach)
672 witer->widget->detach(witer->widget, *statusbar);
673 widget_unref(&witer->widget);
674 widget_node_list_detach(&(*statusbar)->widgets, witer);
675 p_delete(&witer);
678 (*statusbar)->need_update = true;
680 /* now read all widgets and add them */
681 lua_pushnil(L);
682 while(lua_next(L, 3))
684 widget_t **widget = luaA_checkudata(L, -1, "widget");
685 widget_node_t *w = p_new(widget_node_t, 1);
686 w->widget = *widget;
687 widget_node_list_append(&(*statusbar)->widgets, w);
688 widget_ref(widget);
689 lua_pop(L, 1);
691 break;
692 default:
693 return 0;
696 return 0;
699 const struct luaL_reg awesome_statusbar_methods[] =
701 { "__call", luaA_statusbar_new },
702 { NULL, NULL }
704 const struct luaL_reg awesome_statusbar_meta[] =
706 { "__index", luaA_statusbar_index },
707 { "__newindex", luaA_statusbar_newindex },
708 { "__gc", luaA_statusbar_gc },
709 { "__eq", luaA_statusbar_eq },
710 { "__tostring", luaA_statusbar_tostring },
711 { NULL, NULL },
714 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80