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_atom.h>
26 #include <xcb/xcb_icccm.h>
39 #include "layouts/floating.h"
40 #include "common/markup.h"
41 #include "common/atoms.h"
43 extern awesome_t globalconf
;
44 extern const name_func_link_t FloatingPlacementList
[];
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
*));
57 return luaA_settype(L
, "client");
60 DO_LUA_EQ(client_t
, client
, "client")
61 DO_LUA_GC(client_t
, client
, "client", client_unref
)
63 /** Load windows properties, restoring client's tag
64 * and floating state before awesome was restarted if any.
65 * \param c A client pointer.
66 * \param screen A virtual screen.
67 * \return True if client had property, false otherwise.
70 client_loadprops(client_t
* c
, screen_t
*screen
)
73 tag_array_t
*tags
= &screen
->tags
;
76 if(!xutil_gettextprop(globalconf
.connection
, c
->win
, _AWESOME_PROPERTIES
, &prop
, &len
))
79 if(len
!= tags
->len
+ 2)
81 /* ignore property if the tag count isn't matching */
86 for(int i
= 0; i
< tags
->len
; i
++)
88 tag_client(c
, tags
->tab
[i
]);
90 untag_client(c
, tags
->tab
[i
]);
92 client_setlayer(c
, prop
[tags
->len
+ 1] - '0');
93 client_setfloating(c
, prop
[tags
->len
] == '1');
98 /** Check if client supports protocol WM_DELETE_WINDOW.
99 * \param win The window.
100 * \return True if client has WM_DELETE_WINDOW, false otherwise.
103 window_isprotodel(xcb_window_t win
)
106 xcb_atom_t
*protocols
;
109 if(xcb_get_wm_protocols(globalconf
.connection
, win
, &n
, &protocols
))
111 for(i
= 0; !ret
&& i
< n
; i
++)
112 if(protocols
[i
] == WM_DELETE_WINDOW
)
114 p_delete(&protocols
);
119 /** Returns true if a client is tagged
120 * with one of the tags of the specified screen.
121 * \param c The client to check.
122 * \param screen Virtual screen number.
123 * \return true if the client is visible, false otherwise.
126 client_isvisible(client_t
*c
, int screen
)
128 if(c
&& !c
->ishidden
&& c
->screen
== screen
)
130 tag_array_t
*tags
= &globalconf
.screens
[screen
].tags
;
131 for(int i
= 0; i
< tags
->len
; i
++)
132 if(tags
->tab
[i
]->selected
&& is_client_tagged(c
, tags
->tab
[i
]))
138 /** Get a client by its window.
139 * \param w The client window to find.
140 * \return A client pointer if found, NULL otherwise.
143 client_getbywin(xcb_window_t w
)
146 for(c
= globalconf
.clients
; c
&& c
->win
!= w
; c
= c
->next
);
150 /** Update client name attribute with its new title.
151 * \param c The client.
152 * \param Return true if it has been updated.
155 client_updatetitle(client_t
*c
)
160 if(!xutil_gettextprop(globalconf
.connection
, c
->win
, _NET_WM_NAME
, &name
, &len
))
161 if(!xutil_gettextprop(globalconf
.connection
, c
->win
, WM_NAME
, &name
, &len
))
166 if((utf8
= draw_iso2utf8(name
, len
)))
172 luaA_client_userdata_new(globalconf
.L
, c
);
173 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.titleupdate
, 1, 0);
175 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
180 /** Unfocus a client.
181 * \param c The client.
184 client_unfocus(client_t
*c
)
186 globalconf
.screens
[c
->screen
].client_focus
= NULL
;
189 luaA_client_userdata_new(globalconf
.L
, c
);
190 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.unfocus
, 1, 0);
192 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
193 ewmh_update_net_active_window(c
->phys_screen
);
196 /** Ban client and unmap it.
197 * \param c The client.
200 client_ban(client_t
*c
)
202 if(globalconf
.screen_focus
->client_focus
== c
)
204 xcb_unmap_window(globalconf
.connection
, c
->win
);
206 window_setstate(c
->win
, XCB_WM_ICONIC_STATE
);
208 window_setstate(c
->win
, XCB_WM_WITHDRAWN_STATE
);
209 if(c
->titlebar
&& c
->titlebar
->position
&& c
->titlebar
->sw
)
210 xcb_unmap_window(globalconf
.connection
, c
->titlebar
->sw
->window
);
213 /** Give focus to client, or to first client if client is NULL.
214 * \param c The client or NULL.
215 * \param screen Virtual screen number.
216 * \return True if a window (even root) has received focus, false otherwise.
219 client_focus(client_t
*c
, int screen
)
223 /* if c is NULL or invisible, take next client in the focus history */
224 if((!c
|| (c
&& (!client_isvisible(c
, screen
))))
225 && !(c
= globalconf
.screens
[screen
].client_focus
))
226 /* if c is still NULL take next client in the stack */
227 for(c
= globalconf
.clients
; c
&& (c
->skip
|| !client_isvisible(c
, screen
)); c
= c
->next
);
229 /* unfocus current selected client */
230 if(globalconf
.screen_focus
->client_focus
&& c
!= globalconf
.screen_focus
->client_focus
)
231 client_unfocus(globalconf
.screen_focus
->client_focus
);
235 /* unban the client before focusing or it will fail */
238 globalconf
.screen_focus
= &globalconf
.screens
[c
->screen
];
239 globalconf
.screen_focus
->client_focus
= c
;
241 xcb_set_input_focus(globalconf
.connection
, XCB_INPUT_FOCUS_POINTER_ROOT
,
242 c
->win
, XCB_CURRENT_TIME
);
243 phys_screen
= c
->phys_screen
;
245 /* Some layouts use focused client differently, so call them back. */
246 globalconf
.screens
[c
->screen
].need_arrange
= true;
249 luaA_client_userdata_new(globalconf
.L
, globalconf
.screen_focus
->client_focus
);
250 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.focus
, 1, 0);
254 phys_screen
= screen_virttophys(screen
);
255 xcb_set_input_focus(globalconf
.connection
,
256 XCB_INPUT_FOCUS_POINTER_ROOT
,
257 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
261 ewmh_update_net_active_window(phys_screen
);
262 widget_invalidate_cache(screen
, WIDGET_CACHE_CLIENTS
);
266 * \todo It might be worth stopping to restack everyone and only stack `c'
267 * relatively to the first matching in the list.
272 uint32_t config_win_vals
[2];
277 xembed_window_t
*emwin
;
279 config_win_vals
[0] = XCB_NONE
;
280 config_win_vals
[1] = XCB_STACK_MODE_BELOW
;
282 /* first stack fullscreen and modal windows */
283 for(layer
= LAYER_OUTOFSPACE
- 1; layer
>= LAYER_FULLSCREEN
; layer
--)
284 for(node
= globalconf
.stack
; node
; node
= node
->next
)
285 if(node
->client
->layer
== layer
)
287 if(node
->client
->titlebar
288 && node
->client
->titlebar
->sw
289 && node
->client
->titlebar
->position
)
291 xcb_configure_window(globalconf
.connection
,
292 node
->client
->titlebar
->sw
->window
,
293 XCB_CONFIG_WINDOW_SIBLING
| XCB_CONFIG_WINDOW_STACK_MODE
,
295 config_win_vals
[0] = node
->client
->titlebar
->sw
->window
;
297 xcb_configure_window(globalconf
.connection
, node
->client
->win
,
298 XCB_CONFIG_WINDOW_SIBLING
| XCB_CONFIG_WINDOW_STACK_MODE
,
300 config_win_vals
[0] = node
->client
->win
;
303 /* then stack systray windows */
304 for(emwin
= globalconf
.embedded
; emwin
; emwin
= emwin
->next
)
306 xcb_configure_window(globalconf
.connection
,
308 XCB_CONFIG_WINDOW_SIBLING
| XCB_CONFIG_WINDOW_STACK_MODE
,
310 config_win_vals
[0] = emwin
->win
;
313 /* then stack statusbar window */
314 for(screen
= 0; screen
< globalconf
.screens_info
->nscreen
; screen
++)
315 for(sb
= globalconf
.screens
[screen
].statusbar
; sb
; sb
= sb
->next
)
318 xcb_configure_window(globalconf
.connection
,
320 XCB_CONFIG_WINDOW_SIBLING
| XCB_CONFIG_WINDOW_STACK_MODE
,
322 config_win_vals
[0] = sb
->sw
->window
;
325 /* finally stack everything else */
326 for(layer
= LAYER_FULLSCREEN
- 1; layer
>= LAYER_DESKTOP
; layer
--)
327 for(node
= globalconf
.stack
; node
; node
= node
->next
)
328 if(node
->client
->layer
== layer
)
330 if(node
->client
->titlebar
331 && node
->client
->titlebar
->sw
332 && node
->client
->titlebar
->position
)
334 xcb_configure_window(globalconf
.connection
,
335 node
->client
->titlebar
->sw
->window
,
336 XCB_CONFIG_WINDOW_SIBLING
| XCB_CONFIG_WINDOW_STACK_MODE
,
338 config_win_vals
[0] = node
->client
->titlebar
->sw
->window
;
340 xcb_configure_window(globalconf
.connection
, node
->client
->win
,
341 XCB_CONFIG_WINDOW_SIBLING
| XCB_CONFIG_WINDOW_STACK_MODE
,
343 config_win_vals
[0] = node
->client
->win
;
347 /** Put client on top of the stack
348 * \param c The client to raise.
351 client_raise(client_t
*c
)
353 /* Push c on top of the stack. */
354 stack_client_push(c
);
358 /** Manage a new client.
359 * \param w The window.
360 * \param wgeom Window geometry.
361 * \param screen Virtual screen number where to manage client.
364 client_manage(xcb_window_t w
, xcb_get_geometry_reply_t
*wgeom
, int screen
)
366 client_t
*c
, *t
= NULL
;
368 bool rettrans
, retloadprops
;
369 xcb_size_hints_t
*u_size_hints
;
370 const uint32_t select_input_val
[] =
372 XCB_EVENT_MASK_STRUCTURE_NOTIFY
373 | XCB_EVENT_MASK_PROPERTY_CHANGE
374 | XCB_EVENT_MASK_ENTER_WINDOW
377 xcb_change_window_attributes(globalconf
.connection
, w
, XCB_CW_EVENT_MASK
, select_input_val
);
379 if(systray_iskdedockapp(w
))
381 systray_request_handle(w
, screen_virttophys(screen
), NULL
);
385 c
= p_new(client_t
, 1);
387 c
->screen
= screen_get_bycoord(globalconf
.screens_info
, screen
, wgeom
->x
, wgeom
->y
);
389 if(globalconf
.screens_info
->xinerama_is_active
)
390 c
->phys_screen
= globalconf
.default_screen
;
392 c
->phys_screen
= c
->screen
;
396 c
->geometry
.x
= c
->f_geometry
.x
= c
->m_geometry
.x
= wgeom
->x
;
397 c
->geometry
.y
= c
->f_geometry
.y
= c
->m_geometry
.y
= wgeom
->y
;
398 c
->geometry
.width
= c
->f_geometry
.width
= c
->m_geometry
.width
= wgeom
->width
;
399 c
->geometry
.height
= c
->f_geometry
.height
= c
->m_geometry
.height
= wgeom
->height
;
400 c
->layer
= c
->oldlayer
= LAYER_TILE
;
401 client_setborder(c
, wgeom
->border_width
);
404 u_size_hints
= client_updatesizehints(c
);
405 client_updatewmhints(c
);
407 /* Try to load props if any */
408 if(!(retloadprops
= client_loadprops(c
, &globalconf
.screens
[screen
])))
409 screen_client_moveto(c
, screen
, true);
411 /* Then check clients hints */
412 ewmh_check_client_hints(c
);
414 /* check for transient and set tags like its parent */
415 if((rettrans
= xcb_get_wm_transient_for(globalconf
.connection
, w
, &trans
))
416 && (t
= client_getbywin(trans
)))
418 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
419 for(int i
= 0; i
< tags
->len
; i
++)
420 if(is_client_tagged(t
, tags
->tab
[i
]))
421 tag_client(c
, tags
->tab
[i
]);
424 /* should be floating if transsient or fixed */
425 if(rettrans
|| c
->isfixed
)
426 client_setfloating(c
, true);
428 /* Push client in client list */
429 client_list_push(&globalconf
.clients
, c
);
431 /* Push client in stack */
434 /* update window title */
435 client_updatetitle(c
);
437 ewmh_update_net_client_list(c
->phys_screen
);
438 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
441 luaA_client_userdata_new(globalconf
.L
, c
);
442 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.manage
, 1, 0);
444 if(c
->floating_placement
447 && !(xcb_size_hints_get_flags(u_size_hints
) & (XCB_SIZE_US_POSITION_HINT
448 | XCB_SIZE_P_POSITION_HINT
)))
451 client_resize(c
, c
->floating_placement(c
), false);
453 c
->f_geometry
= c
->floating_placement(c
);
457 xcb_free_size_hints(u_size_hints
);
460 /** Compute client geometry with respect to its geometry hints.
461 * \param c The client.
462 * \param geometry The geometry that the client might receive.
463 * \return The geometry the client must take respecting its hints.
466 client_geometry_hints(client_t
*c
, area_t geometry
)
468 double dx
, dy
, max
, min
, ratio
;
470 if(c
->minay
> 0 && c
->maxay
> 0 && (geometry
.height
- c
->baseh
) > 0
471 && (geometry
.width
- c
->basew
) > 0)
473 dx
= (double) (geometry
.width
- c
->basew
);
474 dy
= (double) (geometry
.height
- c
->baseh
);
475 min
= (double) (c
->minax
) / (double) (c
->minay
);
476 max
= (double) (c
->maxax
) / (double) (c
->maxay
);
478 if(max
> 0 && min
> 0 && ratio
> 0)
482 dy
= (dx
* min
+ dy
) / (min
* min
+ 1);
484 geometry
.width
= (int) dx
+ c
->basew
;
485 geometry
.height
= (int) dy
+ c
->baseh
;
489 dy
= (dx
* min
+ dy
) / (max
* max
+ 1);
491 geometry
.width
= (int) dx
+ c
->basew
;
492 geometry
.height
= (int) dy
+ c
->baseh
;
496 if(c
->minw
&& geometry
.width
< c
->minw
)
497 geometry
.width
= c
->minw
;
498 if(c
->minh
&& geometry
.height
< c
->minh
)
499 geometry
.height
= c
->minh
;
500 if(c
->maxw
&& geometry
.width
> c
->maxw
)
501 geometry
.width
= c
->maxw
;
502 if(c
->maxh
&& geometry
.height
> c
->maxh
)
503 geometry
.height
= c
->maxh
;
505 geometry
.width
-= (geometry
.width
- c
->basew
) % c
->incw
;
507 geometry
.height
-= (geometry
.height
- c
->baseh
) % c
->inch
;
512 /** Resize client window.
513 * \param c Client to resize.
514 * \param geometry New window geometry.
515 * \param hints Use size hints.
516 * \return True if the client has been resized.
519 client_resize(client_t
*c
, area_t geometry
, bool hints
)
523 layout_t
*layout
= layout_get_current(c
->screen
);
524 bool resized
= false;
525 /* Values to configure a window is an array where values are
526 * stored according to 'value_mask' */
529 if(c
->titlebar
&& !c
->ismoving
&& !c
->isfloating
&& layout
!= layout_floating
)
530 geometry
= titlebar_geometry_remove(c
->titlebar
, c
->border
, geometry
);
533 geometry
= client_geometry_hints(c
, geometry
);
535 if(geometry
.width
<= 0 || geometry
.height
<= 0)
538 /* offscreen appearance fixes */
539 area
= display_area_get(c
->phys_screen
, NULL
,
540 &globalconf
.screens
[c
->screen
].padding
);
542 if(geometry
.x
> area
.width
)
543 geometry
.x
= area
.width
- geometry
.width
- 2 * c
->border
;
544 if(geometry
.y
> area
.height
)
545 geometry
.y
= area
.height
- geometry
.height
- 2 * c
->border
;
546 if(geometry
.x
+ geometry
.width
+ 2 * c
->border
< 0)
548 if(geometry
.y
+ geometry
.height
+ 2 * c
->border
< 0)
551 if(c
->geometry
.x
!= geometry
.x
|| c
->geometry
.y
!= geometry
.y
552 || c
->geometry
.width
!= geometry
.width
|| c
->geometry
.height
!= geometry
.height
)
555 screen_get_bycoord(globalconf
.screens_info
, c
->screen
, geometry
.x
, geometry
.y
);
557 c
->geometry
.x
= values
[0] = geometry
.x
;
558 c
->geometry
.width
= values
[2] = geometry
.width
;
559 c
->geometry
.y
= values
[1] = geometry
.y
;
560 c
->geometry
.height
= values
[3] = geometry
.height
;
561 values
[4] = c
->border
;
563 /* save the floating geometry if the window is floating but not
565 if(c
->ismoving
|| c
->isfloating
566 || layout_get_current(new_screen
) == layout_floating
)
568 titlebar_update_geometry_floating(c
);
570 c
->f_geometry
= geometry
;
573 xcb_configure_window(globalconf
.connection
, c
->win
,
574 XCB_CONFIG_WINDOW_X
| XCB_CONFIG_WINDOW_Y
|
575 XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
|
576 XCB_CONFIG_WINDOW_BORDER_WIDTH
,
578 window_configure(c
->win
, geometry
, c
->border
);
580 if(c
->screen
!= new_screen
)
581 screen_client_moveto(c
, new_screen
, false);
586 /* call it again like it was floating,
587 * we want it to be sticked to the window */
588 if(!c
->ismoving
&& !c
->isfloating
&& layout
!= layout_floating
)
589 titlebar_update_geometry_floating(c
);
594 /* Set the client layer.
595 * \param c The client.
596 * \param layer The layer.
599 client_setlayer(client_t
*c
, layer_t layer
)
606 /** Set a clinet floating.
607 * \param c The client.
608 * \param floating Set floating, or not.
609 * \param layer Layer to put the floating window onto.
612 client_setfloating(client_t
*c
, bool floating
)
614 if(c
->isfloating
!= floating
)
616 if((c
->isfloating
= floating
))
618 client_setlayer(c
, MAX(c
->layer
, LAYER_FLOAT
));
619 client_resize(c
, c
->f_geometry
, false);
620 titlebar_update_geometry_floating(c
);
625 client_setlayer(c
, c
->oldlayer
);
626 client_resize(c
, c
->m_geometry
, false);
629 client_setlayer(c
, c
->oldlayer
);
630 if(client_isvisible(c
, c
->screen
))
631 globalconf
.screens
[c
->screen
].need_arrange
= true;
632 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
637 /** Save client properties as an X property.
638 * \param c The client.
641 client_saveprops(client_t
*c
)
643 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
644 unsigned char *prop
= p_alloca(unsigned char, tags
->len
+ 3);
647 for(i
= 0; i
< tags
->len
; i
++)
648 prop
[i
] = is_client_tagged(c
, tags
->tab
[i
]) ? '1' : '0';
650 prop
[i
++] = c
->isfloating
? '1' : '0';
651 prop
[i
++] = '0' + c
->layer
;
653 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
, c
->win
, _AWESOME_PROPERTIES
, STRING
, 8, i
, prop
);
657 * \param c The client.
660 client_unban(client_t
*c
)
662 xcb_map_window(globalconf
.connection
, c
->win
);
663 window_setstate(c
->win
, XCB_WM_NORMAL_STATE
);
664 if(c
->titlebar
&& c
->titlebar
->sw
&& c
->titlebar
->position
)
665 xcb_map_window(globalconf
.connection
, c
->titlebar
->sw
->window
);
668 /** Unmanage a client.
669 * \param c The client.
672 client_unmanage(client_t
*c
)
674 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
676 if(globalconf
.screens
[c
->screen
].client_focus
== c
)
680 luaA_client_userdata_new(globalconf
.L
, c
);
681 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.unmanage
, 1, 0);
683 /* The server grab construct avoids race conditions. */
684 xcb_grab_server(globalconf
.connection
);
686 xcb_configure_window(globalconf
.connection
, c
->win
,
687 XCB_CONFIG_WINDOW_BORDER_WIDTH
,
688 (uint32_t *) &c
->oldborder
);
690 xcb_ungrab_button(globalconf
.connection
, XCB_BUTTON_INDEX_ANY
, c
->win
, ANY_MODIFIER
);
691 window_setstate(c
->win
, XCB_WM_WITHDRAWN_STATE
);
693 xcb_aux_sync(globalconf
.connection
);
694 xcb_ungrab_server(globalconf
.connection
);
696 /* remove client everywhere */
697 client_list_detach(&globalconf
.clients
, c
);
698 stack_client_delete(c
);
699 for(int i
= 0; i
< tags
->len
; i
++)
700 untag_client(c
, tags
->tab
[i
]);
704 simplewindow_delete(&c
->titlebar
->sw
);
705 titlebar_unref(&c
->titlebar
);
708 ewmh_update_net_client_list(c
->phys_screen
);
710 /* delete properties */
711 xcb_delete_property(globalconf
.connection
, c
->win
, _AWESOME_PROPERTIES
);
713 /* set client as invalid */
719 /** Update the WM hints of a client.
720 * \param c The client.
723 client_updatewmhints(client_t
*c
)
726 uint32_t wm_hints_flags
;
728 if((wmh
= xcb_get_wm_hints(globalconf
.connection
, c
->win
)))
730 wm_hints_flags
= xcb_wm_hints_get_flags(wmh
);
731 if((c
->isurgent
= xcb_wm_hints_get_urgency(wmh
)))
734 luaA_client_userdata_new(globalconf
.L
, c
);
735 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.urgent
, 1, 0);
737 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
739 if((wm_hints_flags
& XCB_WM_STATE_HINT
) &&
740 (xcb_wm_hints_get_initial_state(wmh
) == XCB_WM_WITHDRAWN_STATE
))
742 client_setborder(c
, 0);
745 xcb_free_wm_hints(wmh
);
749 /** Update the size hints of a client.
750 * \param c The client.
751 * \return A pointer to a xcb_size_hints_t.
754 client_updatesizehints(client_t
*c
)
757 xcb_size_hints_t
*size
;
760 if(!(size
= xcb_get_wm_normal_hints(globalconf
.connection
, c
->win
, &msize
)))
763 size_flags
= xcb_size_hints_get_flags(size
);
765 if((size_flags
& XCB_SIZE_P_SIZE_HINT
))
766 xcb_size_hints_get_base_size(size
, &c
->basew
, &c
->baseh
);
767 else if((size_flags
& XCB_SIZE_P_MIN_SIZE_HINT
))
768 xcb_size_hints_get_min_size(size
, &c
->basew
, &c
->baseh
);
770 c
->basew
= c
->baseh
= 0;
771 if((size_flags
& XCB_SIZE_P_RESIZE_INC_HINT
))
772 xcb_size_hints_get_increase(size
, &c
->incw
, &c
->inch
);
774 c
->incw
= c
->inch
= 0;
776 if((size_flags
& XCB_SIZE_P_MAX_SIZE_HINT
))
777 xcb_size_hints_get_max_size(size
, &c
->maxw
, &c
->maxh
);
779 c
->maxw
= c
->maxh
= 0;
781 if((size_flags
& XCB_SIZE_P_MIN_SIZE_HINT
))
782 xcb_size_hints_get_min_size(size
, &c
->minw
, &c
->minh
);
783 else if((size_flags
& XCB_SIZE_BASE_SIZE_HINT
))
784 xcb_size_hints_get_base_size(size
, &c
->minw
, &c
->minh
);
786 c
->minw
= c
->minh
= 0;
788 if((size_flags
& XCB_SIZE_P_ASPECT_HINT
))
790 xcb_size_hints_get_min_aspect(size
, &c
->minax
, &c
->minay
);
791 xcb_size_hints_get_max_aspect(size
, &c
->maxax
, &c
->maxay
);
794 c
->minax
= c
->maxax
= c
->minay
= c
->maxay
= 0;
796 if(c
->maxw
&& c
->minw
&& c
->maxh
&& c
->minh
797 && c
->maxw
== c
->minw
&& c
->maxh
== c
->minh
)
800 c
->hassizehints
= !(!c
->basew
&& !c
->baseh
&& !c
->incw
&& !c
->inch
801 && !c
->maxw
&& !c
->maxh
&& !c
->minw
&& !c
->minh
802 && !c
->minax
&& !c
->maxax
&& !c
->minax
&& !c
->minay
);
806 /** Kill a client via a WM_DELETE_WINDOW request or XKillClient if not
808 * \param c The client to kill.
811 client_kill(client_t
*c
)
813 xcb_client_message_event_t ev
;
815 if(window_isprotodel(c
->win
))
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
= WM_PROTOCOLS
;
825 ev
.data
.data32
[0] = WM_DELETE_WINDOW
;
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
)
923 if(globalconf
.screen_focus
->client_focus
)
924 return luaA_client_userdata_new(L
, globalconf
.screen_focus
->client_focus
);
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
);
944 if(c
->isfloating
|| layout_get_current(c
->screen
) == layout_floating
)
945 titlebar_update_geometry_floating(c
);
947 globalconf
.screens
[c
->screen
].need_arrange
= true;
951 * \param L The Lua VM state.
957 luaA_client_kill(lua_State
*L
)
959 client_t
**c
= luaA_checkudata(L
, 1, "client");
964 /** Swap a client with another one.
965 * \param L The Lua VM state.
968 * \lparam A client to swap with.
971 luaA_client_swap(lua_State
*L
)
973 client_t
**c
= luaA_checkudata(L
, 1, "client");
974 client_t
**swap
= luaA_checkudata(L
, 2, "client");
975 client_list_swap(&globalconf
.clients
, *swap
, *c
);
976 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
977 globalconf
.screens
[(*swap
)->screen
].need_arrange
= true;
978 widget_invalidate_cache((*c
)->screen
, WIDGET_CACHE_CLIENTS
);
979 widget_invalidate_cache((*swap
)->screen
, WIDGET_CACHE_CLIENTS
);
984 * \param L The Lua VM state.
990 luaA_client_focus_set(lua_State
*L
)
992 client_t
**c
= luaA_checkudata(L
, 1, "client");
993 client_focus(*c
, (*c
)->screen
);
997 /** Raise a client on top of others which are on the same layer.
998 * \param L The Lua VM state.
1004 luaA_client_raise(lua_State
*L
)
1006 client_t
**c
= luaA_checkudata(L
, 1, "client");
1011 /** Redraw a client by unmapping and mapping it quickly.
1012 * \param L The Lua VM state.
1018 luaA_client_redraw(lua_State
*L
)
1020 client_t
**c
= luaA_checkudata(L
, 1, "client");
1021 xcb_unmap_window(globalconf
.connection
, (*c
)->win
);
1022 xcb_map_window(globalconf
.connection
, (*c
)->win
);
1026 /** Return a formated string for a client.
1027 * \param L The Lua VM state.
1030 * \lreturn A string.
1033 luaA_client_tostring(lua_State
*L
)
1035 client_t
**p
= luaA_checkudata(L
, 1, "client");
1036 lua_pushfstring(L
, "[client udata(%p) name(%s)]", *p
, (*p
)->name
);
1040 /** Stop managing a client.
1041 * \param L The Lua VM state.
1046 luaA_client_unmanage(lua_State
*L
)
1048 client_t
**c
= luaA_checkudata(L
, 1, "client");
1049 client_unmanage(*c
);
1053 /** Client newindex.
1054 * \param L The Lua VM state.
1055 * \return The number of elements pushed on stack.
1058 luaA_client_newindex(lua_State
*L
)
1061 client_t
**c
= luaA_checkudata(L
, 1, "client");
1062 const char *buf
= luaL_checklstring(L
, 2, &len
);
1066 titlebar_t
**t
= NULL
;
1071 luaL_error(L
, "client is invalid\n");
1073 switch(a_tokenize(buf
, len
))
1076 buf
= luaL_checklstring(L
, 3, &len
);
1077 p_delete(&(*c
)->name
);
1078 a_iso2utf8(&(*c
)->name
, buf
, len
);
1079 widget_invalidate_cache((*c
)->screen
, WIDGET_CACHE_CLIENTS
);
1081 case A_TK_FLOATING_PLACEMENT
:
1082 (*c
)->floating_placement
= name_func_lookup(luaL_checkstring(L
, 3),
1083 FloatingPlacementList
);
1086 if(globalconf
.screens_info
->xinerama_is_active
)
1088 i
= luaL_checknumber(L
, 3) - 1;
1089 luaA_checkscreen(i
);
1090 if(i
!= (*c
)->screen
)
1091 screen_client_moveto(*c
, i
, true);
1095 b
= luaA_checkboolean(L
, 3);
1096 if(b
!= (*c
)->ishidden
)
1099 if(client_isvisible(*c
, (*c
)->screen
))
1100 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1103 case A_TK_ICON_PATH
:
1104 buf
= luaL_checkstring(L
, 3);
1105 p_delete(&(*c
)->icon_path
);
1106 (*c
)->icon_path
= a_strdup(buf
);
1107 widget_invalidate_cache((*c
)->screen
, WIDGET_CACHE_CLIENTS
);
1110 d
= luaL_checknumber(L
, 3);
1111 if(d
== -1 || (d
>= 0 && d
<= 1))
1112 window_settrans((*c
)->win
, d
);
1115 client_setfloating(*c
, luaA_checkboolean(L
, 3));
1117 case A_TK_HONORSIZEHINTS
:
1118 (*c
)->honorsizehints
= luaA_checkboolean(L
, 3);
1119 if(client_isvisible(*c
, (*c
)->screen
))
1120 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1122 case A_TK_BORDER_WIDTH
:
1123 client_setborder(*c
, luaL_checknumber(L
, 3));
1125 case A_TK_BORDER_COLOR
:
1126 if((buf
= luaL_checklstring(L
, 3, &len
))
1127 && xcolor_init(&(*c
)->border_color
, globalconf
.connection
, (*c
)->phys_screen
, buf
, len
))
1128 xcb_change_window_attributes(globalconf
.connection
, (*c
)->win
,
1129 XCB_CW_BORDER_PIXEL
, &(*c
)->border_color
.pixel
);
1132 if((*c
)->isfloating
|| layout_get_current((*c
)->screen
) == layout_floating
)
1135 luaA_checktable(L
, 3);
1136 geometry
.x
= luaA_getopt_number(L
, 3, "x", (*c
)->geometry
.x
);
1137 geometry
.y
= luaA_getopt_number(L
, 3, "y", (*c
)->geometry
.y
);
1138 geometry
.width
= luaA_getopt_number(L
, 3, "width", (*c
)->geometry
.width
);
1139 geometry
.height
= luaA_getopt_number(L
, 3, "height", (*c
)->geometry
.height
);
1140 client_resize(*c
, geometry
, false);
1144 if(!lua_isnil(L
, 3))
1146 t
= luaA_checkudata(L
, 3, "titlebar");
1147 if(client_getbytitlebar(*t
))
1148 luaL_error(L
, "titlebar is already used by another client");
1151 /* If client had a titlebar, unref it */
1154 simplewindow_delete(&(*c
)->titlebar
->sw
);
1155 titlebar_unref(&(*c
)->titlebar
);
1156 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1161 /* Attach titlebar to client */
1162 (*c
)->titlebar
= *t
;
1168 luaA_checktable(L
, 3);
1169 tags
= &globalconf
.screens
[(*c
)->screen
].tags
;
1170 for(i
= 0; i
< tags
->len
; i
++)
1171 untag_client(*c
, tags
->tab
[i
]);
1173 while(lua_next(L
, 3))
1175 tag
= luaA_checkudata(L
, -1, "tag");
1176 tag_client(*c
, *tag
);
1188 * \param L The Lua VM state.
1189 * \return The number of elements pushed on stack.
1191 * \lfield name The client title.
1192 * \lfield class The client class.
1193 * \lfield instance The client instance.
1194 * \lfield pid The client PID, if available.
1195 * \lfield floating_placement The floating placement used for this client.
1196 * \lfield screen Client screen number.
1197 * \lfield hide Define if the client must be hidden, i.e. never mapped.
1198 * \lfield icon_path Path to the icon used to identify.
1199 * \lfield floating True always floating.
1200 * \lfield honorsizehints Honor size hints, i.e. respect size ratio.
1201 * \lfield border_width The client border width.
1202 * \lfield border_color The client border color.
1203 * \lfield coords The client coordinates.
1204 * \lfield titlebar The client titlebar.
1205 * \lfield urgent The client urgent state.
1206 * \lfield tags The clients tags.
1209 luaA_client_index(lua_State
*L
)
1212 client_t
**c
= luaA_checkudata(L
, 1, "client");
1213 const char *buf
= luaL_checklstring(L
, 2, &len
);
1215 xutil_class_hint_t hint
;
1216 xcb_get_property_cookie_t prop_c
;
1217 xcb_get_property_reply_t
*prop_r
= NULL
;
1222 luaL_error(L
, "client is invalid\n");
1224 if(luaA_usemetatable(L
, 1, 2))
1227 switch(a_tokenize(buf
, len
))
1230 lua_pushstring(L
, (*c
)->name
);
1233 if(!xutil_get_class_hint(globalconf
.connection
, (*c
)->win
, &hint
))
1235 lua_pushstring(L
, hint
.res_class
);
1238 if(!xutil_get_class_hint(globalconf
.connection
, (*c
)->win
, &hint
))
1240 lua_pushstring(L
, hint
.res_name
);
1243 prop_c
= xcb_get_property_unchecked(globalconf
.connection
, false, (*c
)->win
, _NET_WM_PID
, CARDINAL
, 0L, 1L);
1244 prop_r
= xcb_get_property_reply(globalconf
.connection
, prop_c
, NULL
);
1246 if(prop_r
&& prop_r
->value_len
&& (data
= xcb_get_property_value(prop_r
)))
1247 lua_pushnumber(L
, *(uint32_t *)data
);
1254 case A_TK_FLOATING_PLACEMENT
:
1255 lua_pushstring(L
, name_func_rlookup((*c
)->floating_placement
,
1256 FloatingPlacementList
));
1259 lua_pushnumber(L
, 1 + (*c
)->screen
);
1262 lua_pushboolean(L
, (*c
)->ishidden
);
1264 case A_TK_ICON_PATH
:
1265 lua_pushstring(L
, (*c
)->icon_path
);
1268 lua_pushboolean(L
, (*c
)->isfloating
);
1270 case A_TK_HONORSIZEHINTS
:
1271 lua_pushboolean(L
, (*c
)->honorsizehints
);
1273 case A_TK_BORDER_WIDTH
:
1274 lua_pushnumber(L
, (*c
)->border
);
1276 case A_TK_BORDER_COLOR
:
1277 luaA_pushcolor(L
, &(*c
)->border_color
);
1281 lua_pushnumber(L
, (*c
)->geometry
.width
);
1282 lua_setfield(L
, -2, "width");
1283 lua_pushnumber(L
, (*c
)->geometry
.height
);
1284 lua_setfield(L
, -2, "height");
1285 lua_pushnumber(L
, (*c
)->geometry
.x
);
1286 lua_setfield(L
, -2, "x");
1287 lua_pushnumber(L
, (*c
)->geometry
.y
);
1288 lua_setfield(L
, -2, "y");
1292 return luaA_titlebar_userdata_new(globalconf
.L
, (*c
)->titlebar
);
1295 lua_pushboolean(L
, (*c
)->isurgent
);
1298 tags
= &globalconf
.screens
[(*c
)->screen
].tags
;
1300 for(i
= 0; i
< tags
->len
; i
++)
1301 if(is_client_tagged(*c
, tags
->tab
[i
]))
1303 luaA_tag_userdata_new(L
, tags
->tab
[i
]);
1304 luaA_tag_userdata_new(L
, tags
->tab
[i
]);
1315 const struct luaL_reg awesome_client_methods
[] =
1317 { "get", luaA_client_get
},
1318 { "focus_get", luaA_client_focus_get
},
1319 { "visible_get", luaA_client_visible_get
},
1322 const struct luaL_reg awesome_client_meta
[] =
1324 { "kill", luaA_client_kill
},
1325 { "swap", luaA_client_swap
},
1326 { "focus_set", luaA_client_focus_set
},
1327 { "raise", luaA_client_raise
},
1328 { "redraw", luaA_client_redraw
},
1329 { "mouse_resize", luaA_client_mouse_resize
},
1330 { "mouse_move", luaA_client_mouse_move
},
1331 { "unmanage", luaA_client_unmanage
},
1332 { "mouse_add", luaA_client_mouse_add
},
1333 { "mouse_remove", luaA_client_mouse_remove
},
1334 { "__index", luaA_client_index
},
1335 { "__newindex", luaA_client_newindex
},
1336 { "__eq", luaA_client_eq
},
1337 { "__gc", luaA_client_gc
},
1338 { "__tostring", luaA_client_tostring
},
1342 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80