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.
25 #include <xcb/xcb_aux.h>
26 #include <xcb/xcb_atom.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.
52 luaA_client_userdata_new(lua_State
*L
, client_t
*p
)
54 client_t
**pp
= lua_newuserdata(L
, sizeof(client_t
*));
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.
69 client_loadprops(client_t
* c
, screen_t
*screen
)
76 for(tag
= screen
->tags
; tag
; tag
= tag
->next
)
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
,
83 "_AWESOME_PROPERTIES")),
86 for(i
= 0, tag
= screen
->tags
; tag
&& i
< ntags
&& prop
[i
]; i
++, tag
= tag
->next
)
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
);
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.
110 window_isprotodel(xcb_window_t win
)
113 xcb_atom_t wm_delete_win_atom
;
114 xcb_atom_t
*protocols
;
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
,
122 "WM_DELETE_WINDOW"));
124 for(i
= 0; !ret
&& i
< n
; i
++)
125 if(protocols
[i
] == wm_delete_win_atom
)
127 p_delete(&protocols
);
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.
139 client_isvisible(client_t
*c
, int screen
)
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
))
150 /** Get a client by its window.
151 * \param w The client window to find.
152 * \return A client pointer if found, NULL otherwise.
155 client_getbywin(xcb_window_t w
)
158 for(c
= globalconf
.clients
; c
&& c
->win
!= w
; c
= c
->next
);
162 /** Update client name attribute with its new title.
163 * \param c The client.
166 client_updatetitle(client_t
*c
)
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
))
180 a_iso2utf8(name
, &c
->name
);
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.
193 client_unfocus(client_t
*c
)
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.
208 client_ban(client_t
*c
)
210 if(globalconf
.focus
->client
== c
)
212 xcb_unmap_window(globalconf
.connection
, c
->win
);
214 window_setstate(c
->win
, XCB_WM_ICONIC_STATE
);
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.
227 client_focus(client_t
*c
, int 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
);
243 /* unban the client before focusing or it will fail */
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;
255 luaA_client_userdata_new(globalconf
.L
, globalconf
.focus
->client
);
256 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.focus
, 1);
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
,
267 ewmh_update_net_active_window(phys_screen
);
268 widget_invalidate_cache(screen
, WIDGET_CACHE_CLIENTS
);
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.
279 client_raise(client_t
*c
)
281 uint32_t config_win_vals
[2];
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
,
298 XCB_CONFIG_WINDOW_SIBLING
| XCB_CONFIG_WINDOW_STACK_MODE
,
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
,
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
,
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.
330 client_manage(xcb_window_t w
, xcb_get_geometry_reply_t
*wgeom
, int screen
)
332 client_t
*c
, *t
= NULL
;
334 bool rettrans
, retloadprops
;
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
;
348 c
->phys_screen
= c
->screen
;
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
;
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
))
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
384 && !(xcb_size_hints_get_flags(u_size_hints
) & (XCB_SIZE_US_POSITION_HINT
|
385 XCB_SIZE_P_POSITION_HINT
)))
388 client_resize(c
, globalconf
.floating_placement(c
), false);
390 c
->f_geometry
= globalconf
.floating_placement(c
);
394 xcb_free_size_hints(u_size_hints
);
396 xcb_change_window_attributes(globalconf
.connection
, w
, XCB_CW_EVENT_MASK
,
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 */
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
);
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.
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
);
435 if(max
> 0 && min
> 0 && ratio
> 0)
439 dy
= (dx
* min
+ dy
) / (min
* min
+ 1);
441 geometry
.width
= (int) dx
+ c
->basew
;
442 geometry
.height
= (int) dy
+ c
->baseh
;
446 dy
= (dx
* min
+ dy
) / (max
* max
+ 1);
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
;
462 geometry
.width
-= (geometry
.width
- c
->basew
) % c
->incw
;
464 geometry
.height
-= (geometry
.height
- c
->baseh
) % c
->inch
;
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.
476 client_resize(client_t
*c
, area_t geometry
, bool hints
)
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' */
486 if(c
->titlebar
&& !c
->ismoving
&& !c
->isfloating
&& layout
!= layout_floating
)
487 geometry
= titlebar_geometry_remove(c
->titlebar
, c
->border
, geometry
);
490 geometry
= client_geometry_hints(c
, geometry
);
492 if(geometry
.width
<= 0 || geometry
.height
<= 0)
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)
505 if(geometry
.y
+ geometry
.height
+ 2 * c
->border
< 0)
508 if(c
->geometry
.x
!= geometry
.x
|| c
->geometry
.y
!= geometry
.y
509 || c
->geometry
.width
!= geometry
.width
|| c
->geometry
.height
!= geometry
.height
)
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
522 if(c
->ismoving
|| c
->isfloating
523 || layout_get_current(new_screen
) == layout_floating
)
525 titlebar_update_geometry_floating(c
);
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
,
535 window_configure(c
->win
, geometry
, c
->border
);
537 if(c
->screen
!= new_screen
)
538 screen_client_moveto(c
, new_screen
, false);
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
);
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.
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);
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
);
573 c
->oldlayer
= c
->layer
;
577 c
->layer
= c
->oldlayer
;
583 /** Save client properties as an X property.
584 * \param c The client.
587 client_saveprops(client_t
*c
)
589 int i
= 0, ntags
= 0;
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
)
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
);
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
);
618 * \param c The client.
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.
633 client_unmanage(client_t
*c
)
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
);
666 simplewindow_delete(&c
->titlebar
->sw
);
667 titlebar_unref(&c
->titlebar
);
670 ewmh_update_net_client_list(c
->phys_screen
);
675 /** Update the WM hints of a client.
676 * \param c The client.
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
)))
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);
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.
710 client_updatesizehints(client_t
*c
)
713 xcb_size_hints_t
*size
;
716 if(!(size
= xcb_get_wm_normal_hints(globalconf
.connection
, c
->win
, &msize
)))
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
);
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
);
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
);
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
);
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
);
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
)
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
);
762 /** Markup parsing hook.
763 * For now only substitute <title />
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.
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
,
786 .on_element
= client_markup_on_elem
,
790 markup_parser_data_init(&p
);
792 if(markup_parse(&p
, str
, len
))
793 ret
= buffer_detach(&p
.text
);
797 markup_parser_data_wipe(&p
);
802 /** Kill a client via a WM_DELETE_WINDOW request or XKillClient if not
804 * \param c The client to kill.
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 */
820 ev
.response_type
= XCB_CLIENT_MESSAGE
;
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
);
831 xcb_kill_client(globalconf
.connection
, c
->win
);
834 /** Get all clients into a table.
835 * \param L The Lua VM state.
837 * \lreturn A table with all clients.
840 luaA_client_get(lua_State
*L
)
847 for(c
= globalconf
.clients
; c
; c
= c
->next
)
849 luaA_client_userdata_new(globalconf
.L
, c
);
850 lua_rawseti(L
, -2, i
++);
856 /** Add mouse bindings over clients's window.
857 * \param L The Lua VM state.
860 * \lparam A button binding.
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
);
872 /** Remove mouse bindings over clients's window.
873 * \param L The Lua VM state.
876 * \lparam A button binding.
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
);
888 /** Get only visible clients for a screen.
889 * \param L The Lua VM state.
891 * \lparam A screen number.
892 * \lreturn A table with all visible clients for this screen.
895 luaA_client_visible_get(lua_State
*L
)
899 int screen
= luaL_checknumber(L
, 1) - 1;
901 luaA_checkscreen(screen
);
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
++);
915 /** Get the currently focused client.
916 * \param L The Lua VM state.
918 * \lreturn The currently focused client.
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
);
928 /** Set client border width.
929 * \param c The client.
930 * \param width The border width.
933 client_setborder(client_t
*c
, int width
)
937 if((c
->noborder
&& width
> 0) || width
== c
->border
|| width
< 0)
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.
950 * \lparam A table with `width' key for the border width in pixel and `color' key
951 * for the border color.
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
);
961 client_setborder(*c
, width
);
964 && xcolor_new(globalconf
.connection
, (*c
)->phys_screen
, colorstr
, &color
))
965 xcb_change_window_attributes(globalconf
.connection
, (*c
)->win
, XCB_CW_BORDER_PIXEL
,
971 /** Move the client to another screen.
972 * \param L The Lua VM state.
975 * \lparam A screen number.
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);
987 /** Get the screen number the client is onto.
988 * \param L The Lua VM state.
991 * \lreturn A screen number.
994 luaA_client_screen_get(lua_State
*L
)
996 client_t
**c
= luaA_checkudata(L
, 1, "client");
997 lua_pushnumber(L
, 1 + (*c
)->screen
);
1001 /** Tag a client with a specified tag.
1002 * \param L The Lua VM state.
1005 * \lparam A tag object.
1006 * \lparam A boolean value: true to add this tag to clients, false to remove.
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");
1019 tag_client(*c
, *tag
);
1021 untag_client(*c
, *tag
);
1026 /** Check if a client is tagged with the specified tag.
1027 * \param L The Lua VM state.
1030 * \lparam A tag object.
1031 * \lreturn A boolean value, true if the client is tagged with this tag, false
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
));
1043 /** Get the client coordinates on the display.
1044 * \param L The Lua VM state.
1047 * \lreturn A table with keys `width', `height', `x' and `y'.
1050 luaA_client_coords_get(lua_State
*L
)
1052 client_t
**c
= luaA_checkudata(L
, 1, "client");
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");
1065 /** Set client coordinates. This only operates if the client is floating.
1066 * \param L The Lua VM state.
1069 * \lparam A table with keys: x, y, width, height.
1072 luaA_client_coords_set(lua_State
*L
)
1074 client_t
**c
= luaA_checkudata(L
, 1, "client");
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);
1090 /** Set the client opacity.
1091 * Note: this requires an external composite manager.
1092 * \param L The Lua VM state.
1095 * \lparam A floating value between 0 and 1.
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
);
1109 * \param L The Lua VM state.
1115 luaA_client_kill(lua_State
*L
)
1117 client_t
**c
= luaA_checkudata(L
, 1, "client");
1122 /** Swap a client with another one.
1123 * \param L The Lua VM state.
1126 * \lparam A client to swap with.
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
);
1142 * \param L The Lua VM state.
1148 luaA_client_focus_set(lua_State
*L
)
1150 client_t
**c
= luaA_checkudata(L
, 1, "client");
1151 client_focus(*c
, (*c
)->screen
);
1155 /** Raise a client on top of others which are on the same layer.
1156 * \param L The Lua VM state.
1162 luaA_client_raise(lua_State
*L
)
1164 client_t
**c
= luaA_checkudata(L
, 1, "client");
1169 /** Set the client floating attribute.
1170 * \param L The Lua VM state.
1173 * \lparam A boolean, true to set, false to unset.
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
);
1184 /** Check if a client has the floating attribute.
1185 * \param L The Lua VM state.
1188 * \lreturn A boolean, true if the client has the floating attribute set, false
1192 luaA_client_floating_get(lua_State
*L
)
1194 client_t
**c
= luaA_checkudata(L
, 1, "client");
1195 lua_pushboolean(L
, (*c
)->isfloating
);
1199 /** Redraw a client by unmapping and mapping it quickly.
1200 * \param L The Lua VM state.
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
);
1214 /** Return a formated string for a client.
1215 * \param L The Lua VM state.
1218 * \lreturn A string.
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
);
1228 /** Get the client name.
1229 * \param L The Lua VM state.
1232 * \lreturn A string with the client class.
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
);
1240 lua_pushstring(L
, hint
->res_class
);
1242 luaL_error(L
, "Unable to get the class property for client");
1246 /** Set the default icon for this client.
1247 * \param L The Lua VM state.
1250 * \lparam A path to an icon image, or nil to remove.
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
);
1264 /** Get the client name.
1265 * \param L The Lua VM state.
1268 * \lreturn A string with the client name.
1271 luaA_client_name_get(lua_State
*L
)
1273 client_t
**c
= luaA_checkudata(L
, 1, "client");
1274 lua_pushstring(L
, (*c
)->name
);
1278 /** Change the client name. It'll change it only from awesome point of view.
1279 * \param L The Lua VM state.
1282 * \lparam A string with the new client name.
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
);
1294 /** Set the client's titlebar.
1295 * \param L The Lua VM state.
1298 * \lparam A titlebar.
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 */
1316 simplewindow_delete(&(*c
)->titlebar
->sw
);
1317 titlebar_unref(&(*c
)->titlebar
);
1322 /* Attach titlebar to client */
1323 (*c
)->titlebar
= *t
;
1328 (*c
)->titlebar
= NULL
;
1330 if((*c
)->isfloating
|| layout_get_current((*c
)->screen
) == layout_floating
)
1331 titlebar_update_geometry_floating(*c
);
1333 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1338 /** Get the titlebar of a client.
1339 * \param L The Lua VM state.
1342 * \lreturn A titlebar or nil if the client has no titlebar.
1345 luaA_client_titlebar_get(lua_State
*L
)
1347 client_t
**c
= luaA_checkudata(L
, 1, "client");
1350 return luaA_titlebar_userdata_new(globalconf
.L
, (*c
)->titlebar
);
1355 /** Stop managing a client.
1356 * \param L The Lua VM state.
1361 luaA_client_unmanage(lua_State
*L
)
1363 client_t
**c
= luaA_checkudata(L
, 1, "client");
1364 client_unmanage(*c
);
1369 * \param L The Lua VM state.
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;
1383 /** Unhide a client.
1384 * \param L The Lua VM state.
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;
1398 /** Guess if a client has been hidden.
1399 * \param L The Lua VM state.
1403 * \lreturn A boolean, true if the client has been hidden with hide(), false
1407 luaA_client_ishidden(lua_State
*L
)
1409 client_t
**c
= luaA_checkudata(L
, 1, "client");
1410 lua_pushboolean(L
, (*c
)->ishidden
);
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
},
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
},
1457 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80