build: set minimal cmake version to 2.4.7
[awesome.git] / client.c
blob09a70933ee7676bcdbc5be9134a0408cfadcc239
1 /*
2 * client.c - client management
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 <stdio.h>
24 #include <xcb/xcb.h>
25 #include <xcb/xcb_aux.h>
26 #include <xcb/xcb_atom.h>
28 #include "client.h"
29 #include "tag.h"
30 #include "window.h"
31 #include "focus.h"
32 #include "ewmh.h"
33 #include "widget.h"
34 #include "screen.h"
35 #include "titlebar.h"
36 #include "lua.h"
37 #include "stack.h"
38 #include "mouse.h"
39 #include "layouts/floating.h"
40 #include "common/markup.h"
41 #include "common/xutil.h"
42 #include "common/xscreen.h"
44 extern awesome_t globalconf;
46 /** Create a new client userdata.
47 * \param L The Lua VM state.
48 * \param p A client pointer.
49 * \param The number of elements pushed on the stack.
51 int
52 luaA_client_userdata_new(lua_State *L, client_t *p)
54 client_t **pp = lua_newuserdata(L, sizeof(client_t *));
55 *pp = p;
56 return luaA_settype(L, "client");
59 DO_LUA_EQ(client_t, client, "client")
61 /** Load windows properties, restoring client's tag
62 * and floating state before awesome was restarted if any.
63 * \todo This may bug if number of tags is != than before.
64 * \param c A client pointer.
65 * \param screen A virtual screen.
66 * \return True if client had property, false otherwise.
68 static bool
69 client_loadprops(client_t * c, screen_t *screen)
71 int i, ntags = 0;
72 tag_t *tag;
73 char *prop = NULL;
74 bool result = false;
76 for(tag = screen->tags; tag; tag = tag->next)
77 ntags++;
79 if(xutil_gettextprop(globalconf.connection, c->win, &globalconf.atoms,
80 xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms,
81 xutil_intern_atom(globalconf.connection,
82 &globalconf.atoms,
83 "_AWESOME_PROPERTIES")),
84 &prop))
86 for(i = 0, tag = screen->tags; tag && i < ntags && prop[i]; i++, tag = tag->next)
87 if(prop[i] == '1')
89 tag_client(c, tag);
90 result = true;
92 else
93 untag_client(c, tag);
95 if(prop[i])
96 client_setfloating(c, prop[i] == '1',
97 (prop[i + 1] >= 0 && prop[i + 1] <= LAYER_FULLSCREEN) ? atoi(&prop[i + 1]) : prop[i] == '1' ? LAYER_FLOAT : LAYER_TILE);
100 p_delete(&prop);
102 return result;
105 /** Check if client supports protocol WM_DELETE_WINDOW.
106 * \param win The window.
107 * \return True if client has WM_DELETE_WINDOW, false otherwise.
109 static bool
110 window_isprotodel(xcb_window_t win)
112 uint32_t i, n;
113 xcb_atom_t wm_delete_win_atom;
114 xcb_atom_t *protocols;
115 bool ret = false;
117 if(xcb_get_wm_protocols(globalconf.connection, win, &n, &protocols))
119 wm_delete_win_atom = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms,
120 xutil_intern_atom(globalconf.connection,
121 &globalconf.atoms,
122 "WM_DELETE_WINDOW"));
124 for(i = 0; !ret && i < n; i++)
125 if(protocols[i] == wm_delete_win_atom)
126 ret = true;
127 p_delete(&protocols);
129 return ret;
132 /** Returns true if a client is tagged
133 * with one of the tags of the specified screen.
134 * \param c The client to check.
135 * \param screen Virtual screen number.
136 * \return true if the client is visible, false otherwise.
138 bool
139 client_isvisible(client_t *c, int screen)
141 tag_t *tag;
143 if(c && !c->ishidden && c->screen == screen)
144 for(tag = globalconf.screens[screen].tags; tag; tag = tag->next)
145 if(tag->selected && is_client_tagged(c, tag))
146 return true;
147 return false;
150 /** Get a client by its window.
151 * \param w The client window to find.
152 * \return A client pointer if found, NULL otherwise.
154 client_t *
155 client_getbywin(xcb_window_t w)
157 client_t *c;
158 for(c = globalconf.clients; c && c->win != w; c = c->next);
159 return c;
162 /** Update client name attribute with its new title.
163 * \param c The client.
165 void
166 client_updatetitle(client_t *c)
168 char *name;
169 xutil_intern_atom_request_t net_wm_name_q;
170 xcb_atom_t net_wm_name;
172 net_wm_name_q = xutil_intern_atom(globalconf.connection, &globalconf.atoms, "_NET_WM_NAME");
173 net_wm_name = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, net_wm_name_q);
175 if(!xutil_gettextprop(globalconf.connection, c->win, &globalconf.atoms, net_wm_name, &name))
176 if(!xutil_gettextprop(globalconf.connection, c->win, &globalconf.atoms, WM_NAME, &name))
177 return;
179 p_delete(&c->name);
180 a_iso2utf8(name, &c->name);
182 /* call hook */
183 luaA_client_userdata_new(globalconf.L, c);
184 luaA_dofunction(globalconf.L, globalconf.hooks.titleupdate, 1);
186 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
189 /** Unfocus a client.
190 * \param c The client.
192 static void
193 client_unfocus(client_t *c)
195 /* Call hook */
196 luaA_client_userdata_new(globalconf.L, globalconf.focus->client);
197 luaA_dofunction(globalconf.L, globalconf.hooks.unfocus, 1);
199 focus_client_push(NULL);
200 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
201 ewmh_update_net_active_window(c->phys_screen);
204 /** Ban client and unmap it.
205 * \param c The client.
207 void
208 client_ban(client_t *c)
210 if(globalconf.focus->client == c)
211 client_unfocus(c);
212 xcb_unmap_window(globalconf.connection, c->win);
213 if(c->ishidden)
214 window_setstate(c->win, XCB_WM_ICONIC_STATE);
215 else
216 window_setstate(c->win, XCB_WM_WITHDRAWN_STATE);
217 if(c->titlebar && c->titlebar->position && c->titlebar->sw)
218 xcb_unmap_window(globalconf.connection, c->titlebar->sw->window);
221 /** Give focus to client, or to first client if client is NULL.
222 * \param c The client or NULL.
223 * \param screen Virtual screen number.
224 * \return True if a window (even root) has received focus, false otherwise.
226 bool
227 client_focus(client_t *c, int screen)
229 int phys_screen;
231 /* if c is NULL or invisible, take next client in the focus history */
232 if((!c || (c && (!client_isvisible(c, screen))))
233 && !(c = focus_get_current_client(screen)))
234 /* if c is still NULL take next client in the stack */
235 for(c = globalconf.clients; c && (c->skip || !client_isvisible(c, screen)); c = c->next);
237 /* unfocus current selected client */
238 if(globalconf.focus->client)
239 client_unfocus(globalconf.focus->client);
241 if(c)
243 /* unban the client before focusing or it will fail */
244 client_unban(c);
245 /* save sel in focus history */
246 focus_client_push(c);
247 xcb_set_input_focus(globalconf.connection, XCB_INPUT_FOCUS_POINTER_ROOT,
248 c->win, XCB_CURRENT_TIME);
249 phys_screen = c->phys_screen;
251 /* Some layouts use focused client differently, so call them back. */
252 globalconf.screens[c->screen].need_arrange = true;
254 /* execute hook */
255 luaA_client_userdata_new(globalconf.L, globalconf.focus->client);
256 luaA_dofunction(globalconf.L, globalconf.hooks.focus, 1);
258 else
260 phys_screen = screen_virttophys(screen);
261 xcb_set_input_focus(globalconf.connection,
262 XCB_INPUT_FOCUS_POINTER_ROOT,
263 xutil_screen_get(globalconf.connection, phys_screen)->root,
264 XCB_CURRENT_TIME);
267 ewmh_update_net_active_window(phys_screen);
268 widget_invalidate_cache(screen, WIDGET_CACHE_CLIENTS);
270 return true;
273 /** Restack clients and puts c in top of its layer.
274 * \param c The client to stack on top of others.
275 * \todo It might be worth stopping to restack everyone and only stack `c'
276 * relatively to the first matching in the list.
278 void
279 client_raise(client_t *c)
281 uint32_t config_win_vals[2];
282 client_node_t *node;
283 layer_t layer;
284 statusbar_t *sb;
285 int screen;
287 config_win_vals[0] = XCB_NONE;
288 config_win_vals[1] = XCB_STACK_MODE_BELOW;
290 /* Push c on top of the stack. */
291 stack_client_push(c);
293 for(screen = 0; screen < globalconf.screens_info->nscreen; screen++)
294 for(sb = globalconf.screens[screen].statusbar; sb; sb = sb->next)
296 xcb_configure_window(globalconf.connection,
297 sb->sw->window,
298 XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE,
299 config_win_vals);
300 config_win_vals[0] = sb->sw->window;
303 for(layer = LAYER_OUTOFSPACE - 1; layer >= LAYER_DESKTOP; layer--)
304 for(node = globalconf.stack; node; node = node->next)
305 if(node->client->layer == layer)
307 if(node->client->titlebar
308 && node->client->titlebar->sw
309 && node->client->titlebar->position)
311 xcb_configure_window(globalconf.connection,
312 node->client->titlebar->sw->window,
313 XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE,
314 config_win_vals);
315 config_win_vals[0] = node->client->titlebar->sw->window;
317 xcb_configure_window(globalconf.connection, node->client->win,
318 XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE,
319 config_win_vals);
320 config_win_vals[0] = node->client->win;
324 /** Manage a new client.
325 * \param w The window.
326 * \param wgeom Window geometry.
327 * \param screen Virtual screen number where to manage client.
329 void
330 client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, int screen)
332 client_t *c, *t = NULL;
333 xcb_window_t trans;
334 bool rettrans, retloadprops;
335 tag_t *tag;
336 xcb_size_hints_t *u_size_hints;
337 const uint32_t select_input_val[] = {
338 XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE |
339 XCB_EVENT_MASK_ENTER_WINDOW };
341 c = p_new(client_t, 1);
343 c->screen = screen_get_bycoord(globalconf.screens_info, screen, wgeom->x, wgeom->y);
345 if(globalconf.screens_info->xinerama_is_active)
346 c->phys_screen = globalconf.default_screen;
347 else
348 c->phys_screen = c->screen;
350 /* Initial values */
351 c->win = w;
352 c->geometry.x = c->f_geometry.x = c->m_geometry.x = wgeom->x;
353 c->geometry.y = c->f_geometry.y = c->m_geometry.y = wgeom->y;
354 c->geometry.width = c->f_geometry.width = c->m_geometry.width = wgeom->width;
355 c->geometry.height = c->f_geometry.height = c->m_geometry.height = wgeom->height;
356 c->oldborder = wgeom->border_width;
357 c->layer = c->oldlayer = LAYER_TILE;
359 /* update hints */
360 u_size_hints = client_updatesizehints(c);
361 client_updatewmhints(c);
363 /* Try to load props if any */
364 if(!(retloadprops = client_loadprops(c, &globalconf.screens[screen])))
365 screen_client_moveto(c, screen, true);
367 /* Then check clients hints */
368 ewmh_check_client_hints(c);
370 /* check for transient and set tags like its parent */
371 if((rettrans = xutil_get_transient_for_hint(globalconf.connection, w, &trans))
372 && (t = client_getbywin(trans)))
373 for(tag = globalconf.screens[c->screen].tags; tag; tag = tag->next)
374 if(is_client_tagged(t, tag))
375 tag_client(c, tag);
377 /* should be floating if transsient or fixed */
378 if(rettrans || c->isfixed)
379 client_setfloating(c, true, c->layer != LAYER_TILE ? c->layer : LAYER_FLOAT);
381 if(globalconf.floating_placement
382 && !retloadprops
383 && u_size_hints
384 && !(xcb_size_hints_get_flags(u_size_hints) & (XCB_SIZE_US_POSITION_HINT |
385 XCB_SIZE_P_POSITION_HINT)))
387 if(c->isfloating)
388 client_resize(c, globalconf.floating_placement(c), false);
389 else
390 c->f_geometry = globalconf.floating_placement(c);
393 if(u_size_hints)
394 xcb_free_size_hints(u_size_hints);
396 xcb_change_window_attributes(globalconf.connection, w, XCB_CW_EVENT_MASK,
397 select_input_val);
399 /* Push client in client list */
400 client_list_push(&globalconf.clients, c);
401 /* Append client in history: it'll be last. */
402 focus_client_append(c);
403 /* Push client in stack */
404 client_raise(c);
406 /* update window title */
407 client_updatetitle(c);
409 ewmh_update_net_client_list(c->phys_screen);
410 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
412 /* call hook */
413 luaA_client_userdata_new(globalconf.L, c);
414 luaA_dofunction(globalconf.L, globalconf.hooks.manage, 1);
417 /** Compute client geometry with respect to its geometry hints.
418 * \param c The client.
419 * \param geometry The geometry that the client might receive.
420 * \return The geometry the client must take respecting its hints.
422 area_t
423 client_geometry_hints(client_t *c, area_t geometry)
425 double dx, dy, max, min, ratio;
427 if(c->minay > 0 && c->maxay > 0 && (geometry.height - c->baseh) > 0
428 && (geometry.width - c->basew) > 0)
430 dx = (double) (geometry.width - c->basew);
431 dy = (double) (geometry.height - c->baseh);
432 min = (double) (c->minax) / (double) (c->minay);
433 max = (double) (c->maxax) / (double) (c->maxay);
434 ratio = dx / dy;
435 if(max > 0 && min > 0 && ratio > 0)
437 if(ratio < min)
439 dy = (dx * min + dy) / (min * min + 1);
440 dx = dy * min;
441 geometry.width = (int) dx + c->basew;
442 geometry.height = (int) dy + c->baseh;
444 else if(ratio > max)
446 dy = (dx * min + dy) / (max * max + 1);
447 dx = dy * min;
448 geometry.width = (int) dx + c->basew;
449 geometry.height = (int) dy + c->baseh;
453 if(c->minw && geometry.width < c->minw)
454 geometry.width = c->minw;
455 if(c->minh && geometry.height < c->minh)
456 geometry.height = c->minh;
457 if(c->maxw && geometry.width > c->maxw)
458 geometry.width = c->maxw;
459 if(c->maxh && geometry.height > c->maxh)
460 geometry.height = c->maxh;
461 if(c->incw)
462 geometry.width -= (geometry.width - c->basew) % c->incw;
463 if(c->inch)
464 geometry.height -= (geometry.height - c->baseh) % c->inch;
466 return geometry;
469 /** Resize client window.
470 * \param c Client to resize.
471 * \param geometry New window geometry.
472 * \param hints Use size hints.
473 * \return True if the client has been resized.
475 bool
476 client_resize(client_t *c, area_t geometry, bool hints)
478 int new_screen;
479 area_t area;
480 layout_t *layout = layout_get_current(c->screen);
481 bool resized = false;
482 /* Values to configure a window is an array where values are
483 * stored according to 'value_mask' */
484 uint32_t values[5];
486 if(c->titlebar && !c->ismoving && !c->isfloating && layout != layout_floating)
487 geometry = titlebar_geometry_remove(c->titlebar, c->border, geometry);
489 if(hints)
490 geometry = client_geometry_hints(c, geometry);
492 if(geometry.width <= 0 || geometry.height <= 0)
493 return false;
495 /* offscreen appearance fixes */
496 area = display_area_get(c->phys_screen, NULL,
497 &globalconf.screens[c->screen].padding);
499 if(geometry.x > area.width)
500 geometry.x = area.width - geometry.width - 2 * c->border;
501 if(geometry.y > area.height)
502 geometry.y = area.height - geometry.height - 2 * c->border;
503 if(geometry.x + geometry.width + 2 * c->border < 0)
504 geometry.x = 0;
505 if(geometry.y + geometry.height + 2 * c->border < 0)
506 geometry.y = 0;
508 if(c->geometry.x != geometry.x || c->geometry.y != geometry.y
509 || c->geometry.width != geometry.width || c->geometry.height != geometry.height)
511 new_screen =
512 screen_get_bycoord(globalconf.screens_info, c->screen, geometry.x, geometry.y);
514 c->geometry.x = values[0] = geometry.x;
515 c->geometry.width = values[2] = geometry.width;
516 c->geometry.y = values[1] = geometry.y;
517 c->geometry.height = values[3] = geometry.height;
518 values[4] = c->border;
520 /* save the floating geometry if the window is floating but not
521 * maximized */
522 if(c->ismoving || c->isfloating
523 || layout_get_current(new_screen) == layout_floating)
525 titlebar_update_geometry_floating(c);
526 if(!c->ismax)
527 c->f_geometry = geometry;
530 xcb_configure_window(globalconf.connection, c->win,
531 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
532 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
533 XCB_CONFIG_WINDOW_BORDER_WIDTH,
534 values);
535 window_configure(c->win, geometry, c->border);
537 if(c->screen != new_screen)
538 screen_client_moveto(c, new_screen, false);
540 resized = true;
543 /* call it again like it was floating,
544 * we want it to be sticked to the window */
545 if(!c->ismoving && !c->isfloating && layout != layout_floating)
546 titlebar_update_geometry_floating(c);
548 return resized;
551 /** Set a clinet floating.
552 * \param c The client.
553 * \param floating Set floating, or not.
554 * \param layer Layer to put the floating window onto.
556 void
557 client_setfloating(client_t *c, bool floating, layer_t layer)
559 if(c->isfloating != floating)
561 if((c->isfloating = floating))
562 client_resize(c, c->f_geometry, false);
563 else if(c->ismax)
565 c->ismax = false;
566 client_resize(c, c->m_geometry, false);
568 if(client_isvisible(c, c->screen))
569 globalconf.screens[c->screen].need_arrange = true;
570 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
571 if(floating)
573 c->oldlayer = c->layer;
574 c->layer = layer;
576 else
577 c->layer = c->oldlayer;
578 client_raise(c);
579 client_saveprops(c);
583 /** Save client properties as an X property.
584 * \param c The client.
586 void
587 client_saveprops(client_t *c)
589 int i = 0, ntags = 0;
590 char *prop;
591 tag_t *tag;
592 xutil_intern_atom_request_t atom_q;
594 atom_q = xutil_intern_atom(globalconf.connection, &globalconf.atoms, "_AWESOME_PROPERTIES");
596 for(tag = globalconf.screens[c->screen].tags; tag; tag = tag->next)
597 ntags++;
599 prop = p_new(char, ntags + 3);
601 for(tag = globalconf.screens[c->screen].tags; tag; tag = tag->next, i++)
602 prop[i] = is_client_tagged(c, tag) ? '1' : '0';
604 prop[i] = c->isfloating ? '1' : '0';
606 sprintf(&prop[++i], "%d", c->layer);
608 prop[++i] = '\0';
610 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, c->win,
611 xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, atom_q),
612 STRING, 8, i, (unsigned char *) prop);
614 p_delete(&prop);
617 /** Unban a client.
618 * \param c The client.
620 void
621 client_unban(client_t *c)
623 xcb_map_window(globalconf.connection, c->win);
624 window_setstate(c->win, XCB_WM_NORMAL_STATE);
625 if(c->titlebar && c->titlebar->sw && c->titlebar->position)
626 xcb_map_window(globalconf.connection, c->titlebar->sw->window);
629 /** Unmanage a client.
630 * \param c The client.
632 void
633 client_unmanage(client_t *c)
635 tag_t *tag;
637 /* call hook */
638 luaA_client_userdata_new(globalconf.L, c);
639 luaA_dofunction(globalconf.L, globalconf.hooks.unmanage, 1);
641 /* The server grab construct avoids race conditions. */
642 xcb_grab_server(globalconf.connection);
644 xcb_configure_window(globalconf.connection, c->win,
645 XCB_CONFIG_WINDOW_BORDER_WIDTH,
646 (uint32_t *) &c->oldborder);
648 /* remove client everywhere */
649 client_list_detach(&globalconf.clients, c);
650 focus_client_delete(c);
651 stack_client_delete(c);
652 for(tag = globalconf.screens[c->screen].tags; tag; tag = tag->next)
653 untag_client(c, tag);
655 if(globalconf.focus->client == c)
656 client_focus(NULL, c->screen);
658 xcb_ungrab_button(globalconf.connection, XCB_BUTTON_INDEX_ANY, c->win, ANY_MODIFIER);
659 window_setstate(c->win, XCB_WM_WITHDRAWN_STATE);
661 xcb_aux_sync(globalconf.connection);
662 xcb_ungrab_server(globalconf.connection);
664 if(c->titlebar)
666 simplewindow_delete(&c->titlebar->sw);
667 titlebar_unref(&c->titlebar);
670 ewmh_update_net_client_list(c->phys_screen);
672 p_delete(&c);
675 /** Update the WM hints of a client.
676 * \param c The client.
678 void
679 client_updatewmhints(client_t *c)
681 xcb_wm_hints_t *wmh = NULL;
682 uint32_t wm_hints_flags;
684 if((wmh = xcb_get_wm_hints(globalconf.connection, c->win)))
686 wm_hints_flags = xcb_wm_hints_get_flags(wmh);
687 if((c->isurgent = (wm_hints_flags & XCB_WM_X_URGENCY_HINT)))
689 /* execute hook */
690 luaA_client_userdata_new(globalconf.L, c);
691 luaA_dofunction(globalconf.L, globalconf.hooks.urgent, 1);
693 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
695 if((wm_hints_flags & XCB_WM_STATE_HINT) &&
696 (xcb_wm_hints_get_initial_state(wmh) == XCB_WM_WITHDRAWN_STATE))
698 client_setborder(c, 0);
699 c->skip = true;
701 xcb_free_wm_hints(wmh);
705 /** Update the size hintz of a client.
706 * \param c The client.
707 * \return A pointer to a xcb_size_hints_t.
709 xcb_size_hints_t *
710 client_updatesizehints(client_t *c)
712 long msize;
713 xcb_size_hints_t *size;
714 uint32_t size_flags;
716 if(!(size = xcb_get_wm_normal_hints(globalconf.connection, c->win, &msize)))
717 return NULL;
719 size_flags = xcb_size_hints_get_flags(size);
721 if((size_flags & XCB_SIZE_P_SIZE_HINT))
722 xcb_size_hints_get_base_size(size, &c->basew, &c->baseh);
723 else if((size_flags & XCB_SIZE_P_MIN_SIZE_HINT))
724 xcb_size_hints_get_min_size(size, &c->basew, &c->baseh);
725 else
726 c->basew = c->baseh = 0;
727 if((size_flags & XCB_SIZE_P_RESIZE_INC_HINT))
728 xcb_size_hints_get_increase(size, &c->incw, &c->inch);
729 else
730 c->incw = c->inch = 0;
732 if((size_flags & XCB_SIZE_P_MAX_SIZE_HINT))
733 xcb_size_hints_get_max_size(size, &c->maxw, &c->maxh);
734 else
735 c->maxw = c->maxh = 0;
737 if((size_flags & XCB_SIZE_P_MIN_SIZE_HINT))
738 xcb_size_hints_get_min_size(size, &c->minw, &c->minh);
739 else if((size_flags & XCB_SIZE_BASE_SIZE_HINT))
740 xcb_size_hints_get_base_size(size, &c->minw, &c->minh);
741 else
742 c->minw = c->minh = 0;
744 if((size_flags & XCB_SIZE_P_ASPECT_HINT))
746 xcb_size_hints_get_min_aspect(size, &c->minax, &c->minay);
747 xcb_size_hints_get_max_aspect(size, &c->maxax, &c->maxay);
749 else
750 c->minax = c->maxax = c->minay = c->maxay = 0;
752 if(c->maxw && c->minw && c->maxh && c->minh
753 && c->maxw == c->minw && c->maxh == c->minh)
754 c->isfixed = true;
756 c->hassizehints = !(!c->basew && !c->baseh && !c->incw && !c->inch
757 && !c->maxw && !c->maxh && !c->minw && !c->minh
758 && !c->minax && !c->maxax && !c->minax && !c->minay);
759 return size;
762 /** Markup parsing hook.
763 * For now only substitute <title />
765 static void
766 client_markup_on_elem(markup_parser_data_t *p, const char *elem,
767 const char **names, const char **values)
769 assert (!strcmp(elem, "title"));
770 buffer_add_xmlescaped(&p->text, p->priv);
773 /** Parse a markup string which contains special markup sequence relative to a
774 * client, i.e. its title, etc.
775 * \param c The client concerned by the markup string.
776 * \param str The markup string.
777 * \param len The string length.
779 char *
780 client_markup_parse(client_t *c, const char *str, ssize_t len)
782 static char const * const elements[] = { "title", NULL };
783 markup_parser_data_t p = {
784 .elements = elements,
785 .priv = c->name,
786 .on_element = client_markup_on_elem,
788 char *ret;
790 markup_parser_data_init(&p);
792 if(markup_parse(&p, str, len))
793 ret = buffer_detach(&p.text);
794 else
795 ret = a_strdup(str);
797 markup_parser_data_wipe(&p);
799 return ret;
802 /** Kill a client via a WM_DELETE_WINDOW request or XKillClient if not
803 * supported.
804 * \param c The client to kill.
806 void
807 client_kill(client_t *c)
809 xcb_client_message_event_t ev;
810 xutil_intern_atom_request_t wm_protocols_q, wm_delete_window_q;
812 if(window_isprotodel(c->win))
814 wm_protocols_q = xutil_intern_atom(globalconf.connection, &globalconf.atoms, "WM_PROTOCOLS");
815 wm_delete_window_q = xutil_intern_atom(globalconf.connection, &globalconf.atoms, "WM_DELETE_WINDOW");
817 /* Initialize all of event's fields first */
818 p_clear(&ev, 1);
820 ev.response_type = XCB_CLIENT_MESSAGE;
821 ev.window = c->win;
822 ev.format = 32;
823 ev.data.data32[1] = XCB_CURRENT_TIME;
824 ev.type = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, wm_protocols_q);
825 ev.data.data32[0] = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, wm_delete_window_q);
827 xcb_send_event(globalconf.connection, false, c->win,
828 XCB_EVENT_MASK_NO_EVENT, (char *) &ev);
830 else
831 xcb_kill_client(globalconf.connection, c->win);
834 /** Get all clients into a table.
835 * \param L The Lua VM state.
836 * \luastack
837 * \lreturn A table with all clients.
839 static int
840 luaA_client_get(lua_State *L)
842 int i = 1;
843 client_t *c;
845 lua_newtable(L);
847 for(c = globalconf.clients; c; c = c->next)
849 luaA_client_userdata_new(globalconf.L, c);
850 lua_rawseti(L, -2, i++);
853 return 1;
856 /** Add mouse bindings over clients's window.
857 * \param L The Lua VM state.
858 * \luastack
859 * \lvalue A client.
860 * \lparam A button binding.
862 static int
863 luaA_client_mouse_add(lua_State *L)
865 client_t **c = luaA_checkudata(L, 1, "client");
866 button_t **b = luaA_checkudata(L, 2, "mouse");
867 button_list_push(&(*c)->buttons, *b);
868 button_ref(b);
869 return 0;
872 /** Remove mouse bindings over clients's window.
873 * \param L The Lua VM state.
874 * \luastack
875 * \lvalue A client.
876 * \lparam A button binding.
878 static int
879 luaA_client_mouse_remove(lua_State *L)
881 client_t **c = luaA_checkudata(L, 1, "client");
882 button_t **b = luaA_checkudata(L, 1, "mouse");
883 button_list_detach(&(*c)->buttons, *b);
884 button_unref(b);
885 return 0;
888 /** Get only visible clients for a screen.
889 * \param L The Lua VM state.
890 * \luastack
891 * \lparam A screen number.
892 * \lreturn A table with all visible clients for this screen.
894 static int
895 luaA_client_visible_get(lua_State *L)
897 int i = 1;
898 client_t *c;
899 int screen = luaL_checknumber(L, 1) - 1;
901 luaA_checkscreen(screen);
903 lua_newtable(L);
905 for(c = globalconf.clients; c; c = c->next)
906 if(!c->skip && !c->ishidden && client_isvisible(c, screen))
908 luaA_client_userdata_new(globalconf.L, c);
909 lua_rawseti(L, -2, i++);
912 return 1;
915 /** Get the currently focused client.
916 * \param L The Lua VM state.
917 * \luastack
918 * \lreturn The currently focused client.
920 static int
921 luaA_client_focus_get(lua_State *L __attribute__ ((unused)))
923 if(globalconf.focus->client)
924 return luaA_client_userdata_new(globalconf.L, globalconf.focus->client);
925 return 0;
928 /** Set client border width.
929 * \param c The client.
930 * \param width The border width.
932 void
933 client_setborder(client_t *c, int width)
935 uint32_t w = width;
937 if((c->noborder && width > 0) || width == c->border || width < 0)
938 return;
940 c->border = width;
941 xcb_configure_window(globalconf.connection, c->win,
942 XCB_CONFIG_WINDOW_BORDER_WIDTH, &w);
943 globalconf.screens[c->screen].need_arrange = true;
946 /** Set the client border width and color.
947 * \param L The Lua VM state.
948 * \luastack
949 * \lvalue A client.
950 * \lparam A table with `width' key for the border width in pixel and `color' key
951 * for the border color.
953 static int
954 luaA_client_border_set(lua_State *L)
956 client_t **c = luaA_checkudata(L, 1, "client");
957 int width = luaA_getopt_number(L, 2, "width", (*c)->border);
958 const char *colorstr = luaA_getopt_string(L, 2, "color", NULL);
959 xcolor_t color;
961 client_setborder(*c, width);
963 if(colorstr
964 && xcolor_new(globalconf.connection, (*c)->phys_screen, colorstr, &color))
965 xcb_change_window_attributes(globalconf.connection, (*c)->win, XCB_CW_BORDER_PIXEL,
966 &color.pixel);
968 return 0;
971 /** Move the client to another screen.
972 * \param L The Lua VM state.
973 * \luastack
974 * \lvalue A client.
975 * \lparam A screen number.
977 static int
978 luaA_client_screen_set(lua_State *L)
980 client_t **c = luaA_checkudata(L, 1, "client");
981 int screen = luaL_checknumber(L, 2) - 1;
982 luaA_checkscreen(screen);
983 screen_client_moveto(*c, screen, true);
984 return 0;
987 /** Get the screen number the client is onto.
988 * \param L The Lua VM state.
989 * \luastack
990 * \lvalue A client.
991 * \lreturn A screen number.
993 static int
994 luaA_client_screen_get(lua_State *L)
996 client_t **c = luaA_checkudata(L, 1, "client");
997 lua_pushnumber(L, 1 + (*c)->screen);
998 return 1;
1001 /** Tag a client with a specified tag.
1002 * \param L The Lua VM state.
1003 * \luastack
1004 * \lvalue A client.
1005 * \lparam A tag object.
1006 * \lparam A boolean value: true to add this tag to clients, false to remove.
1008 static int
1009 luaA_client_tag(lua_State *L)
1011 client_t **c = luaA_checkudata(L, 1, "client");
1012 tag_t **tag = luaA_checkudata(L, 2, "tag");
1013 bool tag_the_client = luaA_checkboolean(L, 3);
1015 if((*tag)->screen != (*c)->screen)
1016 luaL_error(L, "tag and client are on different screens");
1018 if(tag_the_client)
1019 tag_client(*c, *tag);
1020 else
1021 untag_client(*c, *tag);
1023 return 0;
1026 /** Check if a client is tagged with the specified tag.
1027 * \param L The Lua VM state.
1028 * \luastack
1029 * \lvalue A client.
1030 * \lparam A tag object.
1031 * \lreturn A boolean value, true if the client is tagged with this tag, false
1032 * otherwise.
1034 static int
1035 luaA_client_istagged(lua_State *L)
1037 client_t **c = luaA_checkudata(L, 1, "client");
1038 tag_t **tag = luaA_checkudata(L, 2, "tag");
1039 lua_pushboolean(L, is_client_tagged(*c, *tag));
1040 return 1;
1043 /** Get the client coordinates on the display.
1044 * \param L The Lua VM state.
1045 * \luastack
1046 * \lvalue A client.
1047 * \lreturn A table with keys `width', `height', `x' and `y'.
1049 static int
1050 luaA_client_coords_get(lua_State *L)
1052 client_t **c = luaA_checkudata(L, 1, "client");
1053 lua_newtable(L);
1054 lua_pushnumber(L, (*c)->geometry.width);
1055 lua_setfield(L, -2, "width");
1056 lua_pushnumber(L, (*c)->geometry.height);
1057 lua_setfield(L, -2, "height");
1058 lua_pushnumber(L, (*c)->geometry.x);
1059 lua_setfield(L, -2, "x");
1060 lua_pushnumber(L, (*c)->geometry.y);
1061 lua_setfield(L, -2, "y");
1062 return 1;
1065 /** Set client coordinates. This only operates if the client is floating.
1066 * \param L The Lua VM state.
1067 * \luastack
1068 * \lvalue A client.
1069 * \lparam A table with keys: x, y, width, height.
1071 static int
1072 luaA_client_coords_set(lua_State *L)
1074 client_t **c = luaA_checkudata(L, 1, "client");
1075 area_t geometry;
1077 if((*c)->isfloating || layout_get_current((*c)->screen) == layout_floating)
1079 luaA_checktable(L, 2);
1080 geometry.x = luaA_getopt_number(L, 2, "x", (*c)->geometry.x);
1081 geometry.y = luaA_getopt_number(L, 2, "y", (*c)->geometry.y);
1082 geometry.width = luaA_getopt_number(L, 2, "width", (*c)->geometry.width);
1083 geometry.height = luaA_getopt_number(L, 2, "height", (*c)->geometry.height);
1084 client_resize(*c, geometry, false);
1087 return 0;
1090 /** Set the client opacity.
1091 * Note: this requires an external composite manager.
1092 * \param L The Lua VM state.
1093 * \luastack
1094 * \lvalue A client.
1095 * \lparam A floating value between 0 and 1.
1097 static int
1098 luaA_client_opacity_set(lua_State *L)
1100 client_t **c = luaA_checkudata(L, 1, "client");
1101 double opacity = luaL_checknumber(L, 2);
1103 if(opacity == -1 || (opacity >= 0 && opacity <= 1))
1104 window_settrans((*c)->win, opacity);
1105 return 0;
1108 /** Kill a client.
1109 * \param L The Lua VM state.
1111 * \luastack
1112 * \lvalue A client.
1114 static int
1115 luaA_client_kill(lua_State *L)
1117 client_t **c = luaA_checkudata(L, 1, "client");
1118 client_kill(*c);
1119 return 0;
1122 /** Swap a client with another one.
1123 * \param L The Lua VM state.
1124 * \luastack
1125 * \lvalue A client.
1126 * \lparam A client to swap with.
1128 static int
1129 luaA_client_swap(lua_State *L)
1131 client_t **c = luaA_checkudata(L, 1, "client");
1132 client_t **swap = luaA_checkudata(L, 2, "client");
1133 client_list_swap(&globalconf.clients, *swap, *c);
1134 globalconf.screens[(*c)->screen].need_arrange = true;
1135 globalconf.screens[(*swap)->screen].need_arrange = true;
1136 widget_invalidate_cache((*c)->screen, WIDGET_CACHE_CLIENTS);
1137 widget_invalidate_cache((*swap)->screen, WIDGET_CACHE_CLIENTS);
1138 return 0;
1141 /** Focus a client.
1142 * \param L The Lua VM state.
1144 * \luastack
1145 * \lvalue A client.
1147 static int
1148 luaA_client_focus_set(lua_State *L)
1150 client_t **c = luaA_checkudata(L, 1, "client");
1151 client_focus(*c, (*c)->screen);
1152 return 0;
1155 /** Raise a client on top of others which are on the same layer.
1156 * \param L The Lua VM state.
1158 * \luastack
1159 * \lvalue A client.
1161 static int
1162 luaA_client_raise(lua_State *L)
1164 client_t **c = luaA_checkudata(L, 1, "client");
1165 client_raise(*c);
1166 return 0;
1169 /** Set the client floating attribute.
1170 * \param L The Lua VM state.
1171 * \luastack
1172 * \lparam A client.
1173 * \lparam A boolean, true to set, false to unset.
1175 static int
1176 luaA_client_floating_set(lua_State *L)
1178 client_t **c = luaA_checkudata(L, 1, "client");
1179 bool f = luaA_checkboolean(L, 2);
1180 client_setfloating(*c, f, (*c)->layer == LAYER_FLOAT ? LAYER_TILE : LAYER_FLOAT);
1181 return 0;
1184 /** Check if a client has the floating attribute.
1185 * \param L The Lua VM state.
1186 * \luastack
1187 * \lvalue A client.
1188 * \lreturn A boolean, true if the client has the floating attribute set, false
1189 * otherwise.
1191 static int
1192 luaA_client_floating_get(lua_State *L)
1194 client_t **c = luaA_checkudata(L, 1, "client");
1195 lua_pushboolean(L, (*c)->isfloating);
1196 return 1;
1199 /** Redraw a client by unmapping and mapping it quickly.
1200 * \param L The Lua VM state.
1202 * \luastack
1203 * \lvalue A client.
1205 static int
1206 luaA_client_redraw(lua_State *L)
1208 client_t **c = luaA_checkudata(L, 1, "client");
1209 xcb_unmap_window(globalconf.connection, (*c)->win);
1210 xcb_map_window(globalconf.connection, (*c)->win);
1211 return 0;
1214 /** Return a formated string for a client.
1215 * \param L The Lua VM state.
1216 * \luastack
1217 * \lvalue A client.
1218 * \lreturn A string.
1220 static int
1221 luaA_client_tostring(lua_State *L)
1223 client_t **p = luaA_checkudata(L, 1, "client");
1224 lua_pushfstring(L, "[client udata(%p) name(%s)]", *p, (*p)->name);
1225 return 1;
1228 /** Get the client name.
1229 * \param L The Lua VM state.
1230 * \luastack
1231 * \lvalue A client.
1232 * \lreturn A string with the client class.
1234 static int
1235 luaA_client_class_get(lua_State *L)
1237 client_t **c = luaA_checkudata(L, 1, "client");
1238 class_hint_t *hint=xutil_get_class_hint(globalconf.connection, (*c)->win);
1239 if (hint)
1240 lua_pushstring(L, hint->res_class);
1241 else
1242 luaL_error(L, "Unable to get the class property for client");
1243 return 1;
1246 /** Set the default icon for this client.
1247 * \param L The Lua VM state.
1248 * \luastack
1249 * \lvalue A client.
1250 * \lparam A path to an icon image, or nil to remove.
1252 static int
1253 luaA_client_icon_set(lua_State *L)
1255 client_t **c = luaA_checkudata(L, 1, "client");
1256 const char *icon = luaL_optstring(L, 2, NULL);
1258 p_delete(&(*c)->icon_path);
1259 (*c)->icon_path = a_strdup(icon);
1261 return 0;
1264 /** Get the client name.
1265 * \param L The Lua VM state.
1266 * \luastack
1267 * \lvalue A client.
1268 * \lreturn A string with the client name.
1270 static int
1271 luaA_client_name_get(lua_State *L)
1273 client_t **c = luaA_checkudata(L, 1, "client");
1274 lua_pushstring(L, (*c)->name);
1275 return 1;
1278 /** Change the client name. It'll change it only from awesome point of view.
1279 * \param L The Lua VM state.
1280 * \luastack
1281 * \lvalue A client.
1282 * \lparam A string with the new client name.
1284 static int
1285 luaA_client_name_set(lua_State *L)
1287 client_t **c = luaA_checkudata(L, 1, "client");
1288 const char *name = luaL_checkstring(L, 2);
1289 p_delete(&(*c)->name);
1290 a_iso2utf8(name, &(*c)->name);
1291 return 0;
1294 /** Set the client's titlebar.
1295 * \param L The Lua VM state.
1296 * \luastack
1297 * \lvalue A client.
1298 * \lparam A titlebar.
1300 static int
1301 luaA_client_titlebar_set(lua_State *L)
1303 client_t **c = luaA_checkudata(L, 1, "client");
1304 titlebar_t **t = NULL;
1306 if(lua_gettop(L) == 2 && !lua_isnil(L, 2))
1308 t = luaA_checkudata(L, 2, "titlebar");
1309 if(client_getbytitlebar(*t))
1310 luaL_error(L, "titlebar is already used by another client");
1313 /* If client had a titlebar, unref it */
1314 if((*c)->titlebar)
1316 simplewindow_delete(&(*c)->titlebar->sw);
1317 titlebar_unref(&(*c)->titlebar);
1320 if(t)
1322 /* Attach titlebar to client */
1323 (*c)->titlebar = *t;
1324 titlebar_ref(t);
1325 titlebar_init(*c);
1327 else
1328 (*c)->titlebar = NULL;
1330 if((*c)->isfloating || layout_get_current((*c)->screen) == layout_floating)
1331 titlebar_update_geometry_floating(*c);
1332 else
1333 globalconf.screens[(*c)->screen].need_arrange = true;
1335 return 0;
1338 /** Get the titlebar of a client.
1339 * \param L The Lua VM state.
1340 * \luastack
1341 * \lvalue A client.
1342 * \lreturn A titlebar or nil if the client has no titlebar.
1344 static int
1345 luaA_client_titlebar_get(lua_State *L)
1347 client_t **c = luaA_checkudata(L, 1, "client");
1349 if((*c)->titlebar)
1350 return luaA_titlebar_userdata_new(globalconf.L, (*c)->titlebar);
1352 return 0;
1355 /** Stop managing a client.
1356 * \param L The Lua VM state.
1357 * \luastack
1358 * \lvalue A client.
1360 static int
1361 luaA_client_unmanage(lua_State *L)
1363 client_t **c = luaA_checkudata(L, 1, "client");
1364 client_unmanage(*c);
1365 return 0;
1368 /** Hide a client.
1369 * \param L The Lua VM state.
1371 * \luastack
1372 * \lvalue A client.
1374 static int
1375 luaA_client_hide(lua_State *L)
1377 client_t **c = luaA_checkudata(L, 1, "client");
1378 (*c)->ishidden = true;
1379 globalconf.screens[(*c)->screen].need_arrange = true;
1380 return 0;
1383 /** Unhide a client.
1384 * \param L The Lua VM state.
1386 * \luastack
1387 * \lvalue A client.
1389 static int
1390 luaA_client_unhide(lua_State *L)
1392 client_t **c = luaA_checkudata(L, 1, "client");
1393 (*c)->ishidden = false;
1394 globalconf.screens[(*c)->screen].need_arrange = true;
1395 return 0;
1398 /** Guess if a client has been hidden.
1399 * \param L The Lua VM state.
1401 * \luastack
1402 * \lvalue A client.
1403 * \lreturn A boolean, true if the client has been hidden with hide(), false
1404 * otherwise.
1406 static int
1407 luaA_client_ishidden(lua_State *L)
1409 client_t **c = luaA_checkudata(L, 1, "client");
1410 lua_pushboolean(L, (*c)->ishidden);
1411 return 1;
1414 const struct luaL_reg awesome_client_methods[] =
1416 { "get", luaA_client_get },
1417 { "focus_get", luaA_client_focus_get },
1418 { "visible_get", luaA_client_visible_get },
1419 { NULL, NULL }
1421 const struct luaL_reg awesome_client_meta[] =
1423 { "titlebar_set", luaA_client_titlebar_set },
1424 { "titlebar_get", luaA_client_titlebar_get },
1425 { "name_get", luaA_client_name_get },
1426 { "name_set", luaA_client_name_set },
1427 { "screen_set", luaA_client_screen_set },
1428 { "screen_get", luaA_client_screen_get },
1429 { "border_set", luaA_client_border_set },
1430 { "tag", luaA_client_tag },
1431 { "istagged", luaA_client_istagged },
1432 { "coords_get", luaA_client_coords_get },
1433 { "coords_set", luaA_client_coords_set },
1434 { "opacity_set", luaA_client_opacity_set },
1435 { "kill", luaA_client_kill },
1436 { "swap", luaA_client_swap },
1437 { "focus_set", luaA_client_focus_set },
1438 { "raise", luaA_client_raise },
1439 { "redraw", luaA_client_redraw },
1440 { "floating_set", luaA_client_floating_set },
1441 { "floating_get", luaA_client_floating_get },
1442 { "icon_set", luaA_client_icon_set },
1443 { "class_get", luaA_client_class_get },
1444 { "mouse_resize", luaA_client_mouse_resize },
1445 { "mouse_move", luaA_client_mouse_move },
1446 { "unmanage", luaA_client_unmanage },
1447 { "hide", luaA_client_hide },
1448 { "unhide", luaA_client_unhide },
1449 { "ishidden", luaA_client_ishidden },
1450 { "mouse_add", luaA_client_mouse_add },
1451 { "mouse_remove", luaA_client_mouse_remove },
1452 { "__eq", luaA_client_eq },
1453 { "__tostring", luaA_client_tostring },
1454 { NULL, NULL }
1457 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80