ewmh: store NET_WM icon
[awesome.git] / statusbar.c
blobcdf4ce78a5ab510d8975d6960a1e0c265a37da3f
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;
440 luaA_checktable(L, 2);
442 if(!(buf = luaA_getopt_string(L, 2, "name", NULL)))
443 luaL_error(L, "object statusbar must have a name");
445 sb = p_new(statusbar_t, 1);
447 sb->name = a_strdup(buf);
449 sb->colors.fg = globalconf.colors.fg;
450 if((buf = luaA_getopt_lstring(L, 2, "fg", NULL, &len)))
451 xcolor_init(&sb->colors.fg, globalconf.connection,
452 globalconf.default_screen, buf, len);
454 sb->colors.bg = globalconf.colors.bg;
455 if((buf = luaA_getopt_lstring(L, 2, "bg", NULL, &len)))
456 xcolor_init(&sb->colors.bg, globalconf.connection,
457 globalconf.default_screen, buf, len);
459 buf = luaA_getopt_lstring(L, 2, "align", "left", &len);
460 sb->align = draw_align_fromstr(buf, len);
462 sb->width = luaA_getopt_number(L, 2, "width", 0);
463 if(sb->width > 0)
464 sb->width_user = true;
465 sb->height = luaA_getopt_number(L, 2, "height", 0);
466 if(sb->height <= 0)
467 /* 1.5 as default factor, it fits nice but no one knows why */
468 sb->height = 1.5 * globalconf.font->height;
470 buf = luaA_getopt_lstring(L, 2, "position", "top", &len);
471 sb->position = position_fromstr(buf, len);
473 sb->screen = SCREEN_UNDEF;
475 return luaA_statusbar_userdata_new(L, sb);
478 /** Statusbar object.
479 * \param L The Lua VM state.
480 * \return The number of elements pushed on stack.
481 * \luastack
482 * \lfield screen Screen number.
483 * \lfield align The alignment.
484 * \lfield fg Foreground color.
485 * \lfield bg Background color.
486 * \lfield position The position.
487 * \lfield widget The statusbar widgets.
489 static int
490 luaA_statusbar_index(lua_State *L)
492 size_t len;
493 int i = 0;
494 widget_node_t *witer;
495 statusbar_t **statusbar = luaA_checkudata(L, 1, "statusbar");
496 const char *attr = luaL_checklstring(L, 2, &len);
498 if(luaA_usemetatable(L, 1, 2))
499 return 1;
501 switch(a_tokenize(attr, len))
503 case A_TK_SCREEN:
504 if((*statusbar)->screen == SCREEN_UNDEF)
505 return 0;
506 lua_pushnumber(L, (*statusbar)->screen + 1);
507 break;
508 case A_TK_ALIGN:
509 lua_pushstring(L, draw_align_tostr((*statusbar)->align));
510 break;
511 case A_TK_FG:
512 luaA_pushcolor(L, &(*statusbar)->colors.fg);
513 break;
514 case A_TK_BG:
515 luaA_pushcolor(L, &(*statusbar)->colors.bg);
516 break;
517 case A_TK_POSITION:
518 lua_pushstring(L, position_tostr((*statusbar)->position));
519 break;
520 case A_TK_WIDGETS:
521 lua_newtable(L);
522 for(witer = (*statusbar)->widgets; witer; witer = witer->next)
524 luaA_widget_userdata_new(L, witer->widget);
525 lua_rawseti(L, -2, ++i);
527 break;
528 default:
529 return 0;
532 return 1;
535 /** Remove a statubar from a screen.
536 * \param statusbar Statusbar to detach from screen.
538 static void
539 statusbar_remove(statusbar_t *statusbar)
541 if(statusbar->screen != SCREEN_UNDEF)
543 position_t p;
545 statusbar_systray_kickout(statusbar->phys_screen);
547 /* save position */
548 p = statusbar->position;
549 statusbar->position = Off;
550 statusbar_position_update(statusbar);
551 /* restore position */
552 statusbar->position = p;
554 statusbar_list_detach(&globalconf.screens[statusbar->screen].statusbar, statusbar);
555 globalconf.screens[statusbar->screen].need_arrange = true;
556 statusbar->screen = SCREEN_UNDEF;
557 statusbar_unref(&statusbar);
561 /** Statusbar newindex.
562 * \param L The Lua VM state.
563 * \return The number of elements pushed on stack.
565 static int
566 luaA_statusbar_newindex(lua_State *L)
568 size_t len;
569 statusbar_t *s, **statusbar = luaA_checkudata(L, 1, "statusbar");
570 const char *buf, *attr = luaL_checklstring(L, 2, &len);
571 position_t p;
572 int screen;
573 widget_node_t *witer;
575 switch(a_tokenize(attr, len))
577 case A_TK_SCREEN:
578 if(lua_isnil(L, 3))
579 statusbar_remove(*statusbar);
580 else
582 screen = luaL_checknumber(L, 3) - 1;
584 luaA_checkscreen(screen);
586 if((*statusbar)->screen == screen)
587 luaL_error(L, "this statusbar is already on screen %d",
588 (*statusbar)->screen + 1);
590 /* Check for uniq name and id. */
591 for(s = globalconf.screens[screen].statusbar; s; s = s->next)
592 if(!a_strcmp(s->name, (*statusbar)->name))
593 luaL_error(L, "a statusbar with that name is already on screen %d\n",
594 screen + 1);
596 statusbar_remove(*statusbar);
598 (*statusbar)->screen = screen;
599 (*statusbar)->phys_screen = screen_virttophys(screen);
601 statusbar_list_append(&globalconf.screens[screen].statusbar, *statusbar);
602 statusbar_ref(statusbar);
604 /* All the other statusbar and ourselves need to be repositioned */
605 for(s = globalconf.screens[screen].statusbar; s; s = s->next)
606 statusbar_position_update(s);
608 ewmh_update_workarea((*statusbar)->phys_screen);
610 break;
611 case A_TK_ALIGN:
612 buf = luaL_checklstring(L, 3, &len);
613 (*statusbar)->align = draw_align_fromstr(buf, len);
614 statusbar_position_update(*statusbar);
615 break;
616 case A_TK_FG:
617 if((buf = luaL_checklstring(L, 3, &len)))
618 if(xcolor_init(&(*statusbar)->colors.fg, globalconf.connection,
619 globalconf.default_screen, buf, len))
621 if((*statusbar)->ctx)
622 (*statusbar)->ctx->fg = (*statusbar)->colors.fg;
623 (*statusbar)->need_update = true;
625 break;
626 case A_TK_BG:
627 if((buf = luaL_checklstring(L, 3, &len)))
628 if(xcolor_init(&(*statusbar)->colors.bg, globalconf.connection,
629 globalconf.default_screen, buf, len))
631 if((*statusbar)->ctx)
632 (*statusbar)->ctx->bg = (*statusbar)->colors.bg;
634 (*statusbar)->need_update = true;
636 break;
637 case A_TK_POSITION:
638 buf = luaL_checklstring(L, 3, &len);
639 p = position_fromstr(buf, len);
640 if(p != (*statusbar)->position)
642 (*statusbar)->position = p;
643 for(s = globalconf.screens[(*statusbar)->screen].statusbar; s; s = s->next)
644 statusbar_position_update(s);
645 ewmh_update_workarea((*statusbar)->phys_screen);
647 break;
648 case A_TK_WIDGETS:
649 luaA_checktable(L, 3);
651 /* remove all widgets */
652 for(witer = (*statusbar)->widgets; witer; witer = (*statusbar)->widgets)
654 if(witer->widget->detach)
655 witer->widget->detach(witer->widget, *statusbar);
656 widget_unref(&witer->widget);
657 widget_node_list_detach(&(*statusbar)->widgets, witer);
658 p_delete(&witer);
661 (*statusbar)->need_update = true;
663 /* now read all widgets and add them */
664 lua_pushnil(L);
665 while(lua_next(L, 3))
667 widget_t **widget = luaA_checkudata(L, -1, "widget");
668 widget_node_t *w = p_new(widget_node_t, 1);
669 w->widget = *widget;
670 widget_node_list_append(&(*statusbar)->widgets, w);
671 widget_ref(widget);
672 lua_pop(L, 1);
674 break;
675 default:
676 return 0;
679 return 0;
682 const struct luaL_reg awesome_statusbar_methods[] =
684 { "__call", luaA_statusbar_new },
685 { NULL, NULL }
687 const struct luaL_reg awesome_statusbar_meta[] =
689 { "__index", luaA_statusbar_index },
690 { "__newindex", luaA_statusbar_newindex },
691 { "__gc", luaA_statusbar_gc },
692 { "__eq", luaA_statusbar_eq },
693 { "__tostring", luaA_statusbar_tostring },
694 { NULL, NULL },
697 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80