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_text_prop_get(globalconf
.connection
, c
->win
, _AWESOME_PROPERTIES
,
80 if(len
!= tags
->len
+ 2)
82 /* ignore property if the tag count isn't matching */
87 for(int i
= 0; i
< tags
->len
; i
++)
89 tag_client(c
, tags
->tab
[i
]);
91 untag_client(c
, tags
->tab
[i
]);
93 client_setlayer(c
, prop
[tags
->len
+ 1] - '0');
94 client_setfloating(c
, prop
[tags
->len
] == '1');
99 /** Check if client supports protocol WM_DELETE_WINDOW.
100 * \param win The window.
101 * \return True if client has WM_DELETE_WINDOW, false otherwise.
104 window_isprotodel(xcb_window_t win
)
107 xcb_atom_t
*protocols
;
110 if(xcb_get_wm_protocols(globalconf
.connection
, win
, &n
, &protocols
))
112 for(i
= 0; !ret
&& i
< n
; i
++)
113 if(protocols
[i
] == WM_DELETE_WINDOW
)
115 p_delete(&protocols
);
120 /** Returns true if a client is tagged
121 * with one of the tags of the specified screen.
122 * \param c The client to check.
123 * \param screen Virtual screen number.
124 * \return true if the client is visible, false otherwise.
127 client_maybevisible(client_t
*c
, int screen
)
129 if(c
->screen
== screen
)
131 tag_array_t
*tags
= &globalconf
.screens
[screen
].tags
;
132 for(int i
= 0; i
< tags
->len
; i
++)
133 if(tags
->tab
[i
]->selected
&& is_client_tagged(c
, tags
->tab
[i
]))
139 /** Returns true if a client is tagged
140 * with one of the tags of the specified screen and is not hidden.
141 * \param c The client to check.
142 * \param screen Virtual screen number.
143 * \return true if the client is visible, false otherwise.
146 client_isvisible(client_t
*c
, int screen
)
148 return (!c
->ishidden
&& client_maybevisible(c
, screen
));
151 /** Get a client by its window.
152 * \param w The client window to find.
153 * \return A client pointer if found, NULL otherwise.
156 client_getbywin(xcb_window_t w
)
159 for(c
= globalconf
.clients
; c
&& c
->win
!= w
; c
= c
->next
);
163 /** Update client name attribute with its new title.
164 * \param c The client.
165 * \param Return true if it has been updated.
168 client_updatetitle(client_t
*c
)
173 if(!xutil_text_prop_get(globalconf
.connection
, c
->win
, _NET_WM_NAME
, &name
, &len
))
174 if(!xutil_text_prop_get(globalconf
.connection
, c
->win
, WM_NAME
, &name
, &len
))
179 if((utf8
= draw_iso2utf8(name
, len
)))
185 luaA_client_userdata_new(globalconf
.L
, c
);
186 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.titleupdate
, 1, 0);
188 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
193 /** Unfocus a client.
194 * \param c The client.
197 client_unfocus(client_t
*c
)
199 globalconf
.screens
[c
->screen
].client_focus
= NULL
;
202 luaA_client_userdata_new(globalconf
.L
, c
);
203 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.unfocus
, 1, 0);
205 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
206 ewmh_update_net_active_window(c
->phys_screen
);
209 /** Ban client and unmap it.
210 * \param c The client.
213 client_ban(client_t
*c
)
215 if(globalconf
.screen_focus
->client_focus
== c
)
217 xcb_unmap_window(globalconf
.connection
, c
->win
);
219 window_state_set(c
->win
, XCB_WM_ICONIC_STATE
);
221 window_state_set(c
->win
, XCB_WM_WITHDRAWN_STATE
);
222 if(c
->titlebar
&& c
->titlebar
->position
&& c
->titlebar
->sw
)
223 xcb_unmap_window(globalconf
.connection
, c
->titlebar
->sw
->window
);
226 /** Give focus to client, or to first client if client is NULL.
227 * \param c The client or NULL.
228 * \return True if a window (even root) has received focus, false otherwise.
231 client_focus(client_t
*c
)
233 if(!client_maybevisible(c
, c
->screen
))
236 /* unfocus current selected client */
237 if(globalconf
.screen_focus
->client_focus
238 && c
!= globalconf
.screen_focus
->client_focus
)
239 client_unfocus(globalconf
.screen_focus
->client_focus
);
244 /* unban the client before focusing or it will fail */
247 globalconf
.screen_focus
= &globalconf
.screens
[c
->screen
];
248 globalconf
.screen_focus
->client_focus
= c
;
250 xcb_set_input_focus(globalconf
.connection
, XCB_INPUT_FOCUS_POINTER_ROOT
,
251 c
->win
, XCB_CURRENT_TIME
);
253 /* Some layouts use focused client differently, so call them back.
254 * And anyway, we have maybe unhidden */
255 globalconf
.screens
[c
->screen
].need_arrange
= true;
258 luaA_client_userdata_new(globalconf
.L
, globalconf
.screen_focus
->client_focus
);
259 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.focus
, 1, 0);
261 ewmh_update_net_active_window(c
->phys_screen
);
262 widget_invalidate_cache(c
->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 xcb_get_property_cookie_t ewmh_icon_cookie
;
367 client_t
*c
, *t
= NULL
;
369 bool rettrans
, retloadprops
;
370 xcb_size_hints_t
*u_size_hints
;
371 const uint32_t select_input_val
[] =
373 XCB_EVENT_MASK_STRUCTURE_NOTIFY
374 | XCB_EVENT_MASK_PROPERTY_CHANGE
375 | XCB_EVENT_MASK_ENTER_WINDOW
378 /* Send request to get NET_WM_ICON property as soon as possible... */
379 ewmh_icon_cookie
= ewmh_window_icon_get_unchecked(w
);
380 xcb_change_window_attributes(globalconf
.connection
, w
, XCB_CW_EVENT_MASK
, select_input_val
);
382 if(systray_iskdedockapp(w
))
384 systray_request_handle(w
, screen_virttophys(screen
), NULL
);
388 c
= p_new(client_t
, 1);
390 c
->screen
= screen_get_bycoord(globalconf
.screens_info
, screen
, wgeom
->x
, wgeom
->y
);
392 if(globalconf
.screens_info
->xinerama_is_active
)
393 c
->phys_screen
= globalconf
.default_screen
;
395 c
->phys_screen
= c
->screen
;
399 c
->geometry
.x
= c
->f_geometry
.x
= c
->m_geometry
.x
= wgeom
->x
;
400 c
->geometry
.y
= c
->f_geometry
.y
= c
->m_geometry
.y
= wgeom
->y
;
401 c
->geometry
.width
= c
->f_geometry
.width
= c
->m_geometry
.width
= wgeom
->width
;
402 c
->geometry
.height
= c
->f_geometry
.height
= c
->m_geometry
.height
= wgeom
->height
;
403 c
->layer
= c
->oldlayer
= LAYER_TILE
;
404 client_setborder(c
, wgeom
->border_width
);
405 c
->icon
= ewmh_window_icon_get_reply(ewmh_icon_cookie
);
408 u_size_hints
= client_updatesizehints(c
);
409 client_updatewmhints(c
);
411 /* Try to load props if any */
412 if(!(retloadprops
= client_loadprops(c
, &globalconf
.screens
[screen
])))
413 screen_client_moveto(c
, screen
, true);
415 /* Then check clients hints */
416 ewmh_check_client_hints(c
);
418 /* check for transient and set tags like its parent */
419 if((rettrans
= xcb_get_wm_transient_for(globalconf
.connection
, w
, &trans
))
420 && (t
= client_getbywin(trans
)))
422 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
423 for(int i
= 0; i
< tags
->len
; i
++)
424 if(is_client_tagged(t
, tags
->tab
[i
]))
425 tag_client(c
, tags
->tab
[i
]);
428 /* should be floating if transsient or fixed */
429 if(rettrans
|| c
->isfixed
)
430 client_setfloating(c
, true);
432 /* Push client in client list */
433 client_list_push(&globalconf
.clients
, c
);
435 /* Push client in stack */
438 /* update window title */
439 client_updatetitle(c
);
441 ewmh_update_net_client_list(c
->phys_screen
);
442 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
445 luaA_client_userdata_new(globalconf
.L
, c
);
446 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.manage
, 1, 0);
448 if(c
->floating_placement
451 && !(xcb_size_hints_get_flags(u_size_hints
) & (XCB_SIZE_US_POSITION_HINT
452 | XCB_SIZE_P_POSITION_HINT
)))
455 client_resize(c
, c
->floating_placement(c
), false);
457 c
->f_geometry
= c
->floating_placement(c
);
461 xcb_free_size_hints(u_size_hints
);
464 /** Compute client geometry with respect to its geometry hints.
465 * \param c The client.
466 * \param geometry The geometry that the client might receive.
467 * \return The geometry the client must take respecting its hints.
470 client_geometry_hints(client_t
*c
, area_t geometry
)
472 double dx
, dy
, max
, min
, ratio
;
474 if(c
->minay
> 0 && c
->maxay
> 0 && (geometry
.height
- c
->baseh
) > 0
475 && (geometry
.width
- c
->basew
) > 0)
477 dx
= (double) (geometry
.width
- c
->basew
);
478 dy
= (double) (geometry
.height
- c
->baseh
);
479 min
= (double) (c
->minax
) / (double) (c
->minay
);
480 max
= (double) (c
->maxax
) / (double) (c
->maxay
);
482 if(max
> 0 && min
> 0 && ratio
> 0)
486 dy
= (dx
* min
+ dy
) / (min
* min
+ 1);
488 geometry
.width
= (int) dx
+ c
->basew
;
489 geometry
.height
= (int) dy
+ c
->baseh
;
493 dy
= (dx
* min
+ dy
) / (max
* max
+ 1);
495 geometry
.width
= (int) dx
+ c
->basew
;
496 geometry
.height
= (int) dy
+ c
->baseh
;
500 if(c
->minw
&& geometry
.width
< c
->minw
)
501 geometry
.width
= c
->minw
;
502 if(c
->minh
&& geometry
.height
< c
->minh
)
503 geometry
.height
= c
->minh
;
504 if(c
->maxw
&& geometry
.width
> c
->maxw
)
505 geometry
.width
= c
->maxw
;
506 if(c
->maxh
&& geometry
.height
> c
->maxh
)
507 geometry
.height
= c
->maxh
;
509 geometry
.width
-= (geometry
.width
- c
->basew
) % c
->incw
;
511 geometry
.height
-= (geometry
.height
- c
->baseh
) % c
->inch
;
516 /** Resize client window.
517 * \param c Client to resize.
518 * \param geometry New window geometry.
519 * \param hints Use size hints.
520 * \return True if the client has been resized.
523 client_resize(client_t
*c
, area_t geometry
, bool hints
)
527 layout_t
*layout
= layout_get_current(c
->screen
);
528 bool resized
= false;
529 /* Values to configure a window is an array where values are
530 * stored according to 'value_mask' */
533 if(c
->titlebar
&& !c
->ismoving
&& !c
->isfloating
&& layout
!= layout_floating
)
534 geometry
= titlebar_geometry_remove(c
->titlebar
, c
->border
, geometry
);
537 geometry
= client_geometry_hints(c
, geometry
);
539 if(geometry
.width
<= 0 || geometry
.height
<= 0)
542 /* offscreen appearance fixes */
543 area
= display_area_get(c
->phys_screen
, NULL
,
544 &globalconf
.screens
[c
->screen
].padding
);
546 if(geometry
.x
> area
.width
)
547 geometry
.x
= area
.width
- geometry
.width
- 2 * c
->border
;
548 if(geometry
.y
> area
.height
)
549 geometry
.y
= area
.height
- geometry
.height
- 2 * c
->border
;
550 if(geometry
.x
+ geometry
.width
+ 2 * c
->border
< 0)
552 if(geometry
.y
+ geometry
.height
+ 2 * c
->border
< 0)
555 if(c
->geometry
.x
!= geometry
.x
|| c
->geometry
.y
!= geometry
.y
556 || c
->geometry
.width
!= geometry
.width
|| c
->geometry
.height
!= geometry
.height
)
559 screen_get_bycoord(globalconf
.screens_info
, c
->screen
, geometry
.x
, geometry
.y
);
561 c
->geometry
.x
= values
[0] = geometry
.x
;
562 c
->geometry
.width
= values
[2] = geometry
.width
;
563 c
->geometry
.y
= values
[1] = geometry
.y
;
564 c
->geometry
.height
= values
[3] = geometry
.height
;
565 values
[4] = c
->border
;
567 /* save the floating geometry if the window is floating but not
569 if(c
->ismoving
|| c
->isfloating
570 || layout_get_current(new_screen
) == layout_floating
)
572 titlebar_update_geometry_floating(c
);
574 c
->f_geometry
= geometry
;
577 xcb_configure_window(globalconf
.connection
, c
->win
,
578 XCB_CONFIG_WINDOW_X
| XCB_CONFIG_WINDOW_Y
|
579 XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
|
580 XCB_CONFIG_WINDOW_BORDER_WIDTH
,
582 window_configure(c
->win
, geometry
, c
->border
);
584 if(c
->screen
!= new_screen
)
585 screen_client_moveto(c
, new_screen
, false);
590 /* call it again like it was floating,
591 * we want it to be sticked to the window */
592 if(!c
->ismoving
&& !c
->isfloating
&& layout
!= layout_floating
)
593 titlebar_update_geometry_floating(c
);
598 /* Set the client layer.
599 * \param c The client.
600 * \param layer The layer.
603 client_setlayer(client_t
*c
, layer_t layer
)
610 /** Set a clinet floating.
611 * \param c The client.
612 * \param floating Set floating, or not.
613 * \param layer Layer to put the floating window onto.
616 client_setfloating(client_t
*c
, bool floating
)
618 if(c
->isfloating
!= floating
)
620 if((c
->isfloating
= floating
))
622 client_setlayer(c
, MAX(c
->layer
, LAYER_FLOAT
));
623 client_resize(c
, c
->f_geometry
, false);
624 titlebar_update_geometry_floating(c
);
629 client_setlayer(c
, c
->oldlayer
);
630 client_resize(c
, c
->m_geometry
, false);
633 client_setlayer(c
, c
->oldlayer
);
634 if(client_isvisible(c
, c
->screen
))
635 globalconf
.screens
[c
->screen
].need_arrange
= true;
636 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
641 /** Save client properties as an X property.
642 * \param c The client.
645 client_saveprops(client_t
*c
)
647 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
648 unsigned char *prop
= p_alloca(unsigned char, tags
->len
+ 3);
651 for(i
= 0; i
< tags
->len
; i
++)
652 prop
[i
] = is_client_tagged(c
, tags
->tab
[i
]) ? '1' : '0';
654 prop
[i
++] = c
->isfloating
? '1' : '0';
655 prop
[i
++] = '0' + c
->layer
;
657 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
, c
->win
, _AWESOME_PROPERTIES
, STRING
, 8, i
, prop
);
661 * \param c The client.
664 client_unban(client_t
*c
)
666 xcb_map_window(globalconf
.connection
, c
->win
);
667 window_state_set(c
->win
, XCB_WM_NORMAL_STATE
);
668 if(c
->titlebar
&& c
->titlebar
->sw
&& c
->titlebar
->position
)
669 xcb_map_window(globalconf
.connection
, c
->titlebar
->sw
->window
);
672 /** Unmanage a client.
673 * \param c The client.
676 client_unmanage(client_t
*c
)
678 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
680 if(globalconf
.screens
[c
->screen
].client_focus
== c
)
684 luaA_client_userdata_new(globalconf
.L
, c
);
685 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.unmanage
, 1, 0);
687 /* The server grab construct avoids race conditions. */
688 xcb_grab_server(globalconf
.connection
);
690 xcb_configure_window(globalconf
.connection
, c
->win
,
691 XCB_CONFIG_WINDOW_BORDER_WIDTH
,
692 (uint32_t *) &c
->oldborder
);
694 xcb_ungrab_button(globalconf
.connection
, XCB_BUTTON_INDEX_ANY
, c
->win
,
695 XCB_BUTTON_MASK_ANY
);
696 window_state_set(c
->win
, XCB_WM_WITHDRAWN_STATE
);
698 xcb_aux_sync(globalconf
.connection
);
699 xcb_ungrab_server(globalconf
.connection
);
701 /* remove client everywhere */
702 client_list_detach(&globalconf
.clients
, c
);
703 stack_client_delete(c
);
704 for(int i
= 0; i
< tags
->len
; i
++)
705 untag_client(c
, tags
->tab
[i
]);
709 simplewindow_delete(&c
->titlebar
->sw
);
710 titlebar_unref(&c
->titlebar
);
714 ewmh_update_net_client_list(c
->phys_screen
);
716 /* delete properties */
717 xcb_delete_property(globalconf
.connection
, c
->win
, _AWESOME_PROPERTIES
);
719 /* set client as invalid */
725 /** Update the WM hints of a client.
726 * \param c The client.
729 client_updatewmhints(client_t
*c
)
732 uint32_t wm_hints_flags
;
734 if((wmh
= xcb_get_wm_hints(globalconf
.connection
, c
->win
)))
736 wm_hints_flags
= xcb_wm_hints_get_flags(wmh
);
737 if((c
->isurgent
= xcb_wm_hints_get_urgency(wmh
)))
740 luaA_client_userdata_new(globalconf
.L
, c
);
741 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.urgent
, 1, 0);
743 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
745 if((wm_hints_flags
& XCB_WM_STATE_HINT
) &&
746 (xcb_wm_hints_get_initial_state(wmh
) == XCB_WM_WITHDRAWN_STATE
))
748 client_setborder(c
, 0);
751 xcb_free_wm_hints(wmh
);
755 /** Update the size hints of a client.
756 * \param c The client.
757 * \return A pointer to a xcb_size_hints_t.
760 client_updatesizehints(client_t
*c
)
763 xcb_size_hints_t
*size
;
766 if(!(size
= xcb_get_wm_normal_hints(globalconf
.connection
, c
->win
, &msize
)))
769 size_flags
= xcb_size_hints_get_flags(size
);
771 if((size_flags
& XCB_SIZE_P_SIZE_HINT
))
772 xcb_size_hints_get_base_size(size
, &c
->basew
, &c
->baseh
);
773 else if((size_flags
& XCB_SIZE_P_MIN_SIZE_HINT
))
774 xcb_size_hints_get_min_size(size
, &c
->basew
, &c
->baseh
);
776 c
->basew
= c
->baseh
= 0;
777 if((size_flags
& XCB_SIZE_P_RESIZE_INC_HINT
))
778 xcb_size_hints_get_increase(size
, &c
->incw
, &c
->inch
);
780 c
->incw
= c
->inch
= 0;
782 if((size_flags
& XCB_SIZE_P_MAX_SIZE_HINT
))
783 xcb_size_hints_get_max_size(size
, &c
->maxw
, &c
->maxh
);
785 c
->maxw
= c
->maxh
= 0;
787 if((size_flags
& XCB_SIZE_P_MIN_SIZE_HINT
))
788 xcb_size_hints_get_min_size(size
, &c
->minw
, &c
->minh
);
789 else if((size_flags
& XCB_SIZE_BASE_SIZE_HINT
))
790 xcb_size_hints_get_base_size(size
, &c
->minw
, &c
->minh
);
792 c
->minw
= c
->minh
= 0;
794 if((size_flags
& XCB_SIZE_P_ASPECT_HINT
))
796 xcb_size_hints_get_min_aspect(size
, &c
->minax
, &c
->minay
);
797 xcb_size_hints_get_max_aspect(size
, &c
->maxax
, &c
->maxay
);
800 c
->minax
= c
->maxax
= c
->minay
= c
->maxay
= 0;
802 if(c
->maxw
&& c
->minw
&& c
->maxh
&& c
->minh
803 && c
->maxw
== c
->minw
&& c
->maxh
== c
->minh
)
806 c
->hassizehints
= !(!c
->basew
&& !c
->baseh
&& !c
->incw
&& !c
->inch
807 && !c
->maxw
&& !c
->maxh
&& !c
->minw
&& !c
->minh
808 && !c
->minax
&& !c
->maxax
&& !c
->minax
&& !c
->minay
);
812 /** Kill a client via a WM_DELETE_WINDOW request or XKillClient if not
814 * \param c The client to kill.
817 client_kill(client_t
*c
)
819 xcb_client_message_event_t ev
;
821 if(window_isprotodel(c
->win
))
823 /* Initialize all of event's fields first */
826 ev
.response_type
= XCB_CLIENT_MESSAGE
;
829 ev
.data
.data32
[1] = XCB_CURRENT_TIME
;
830 ev
.type
= WM_PROTOCOLS
;
831 ev
.data
.data32
[0] = WM_DELETE_WINDOW
;
833 xcb_send_event(globalconf
.connection
, false, c
->win
,
834 XCB_EVENT_MASK_NO_EVENT
, (char *) &ev
);
837 xcb_kill_client(globalconf
.connection
, c
->win
);
840 /** Get all clients into a table.
841 * \param L The Lua VM state.
843 * \lreturn A table with all clients.
846 luaA_client_get(lua_State
*L
)
853 for(c
= globalconf
.clients
; c
; c
= c
->next
)
855 luaA_client_userdata_new(globalconf
.L
, c
);
856 lua_rawseti(L
, -2, i
++);
862 /** Add mouse bindings over clients's window.
863 * \param L The Lua VM state.
866 * \lparam A button binding.
869 luaA_client_mouse_add(lua_State
*L
)
871 client_t
**c
= luaA_checkudata(L
, 1, "client");
872 button_t
**b
= luaA_checkudata(L
, 2, "mouse");
873 button_list_push(&(*c
)->buttons
, *b
);
878 /** Remove mouse bindings over clients's window.
879 * \param L The Lua VM state.
882 * \lparam A button binding.
885 luaA_client_mouse_remove(lua_State
*L
)
887 client_t
**c
= luaA_checkudata(L
, 1, "client");
888 button_t
**b
= luaA_checkudata(L
, 1, "mouse");
889 button_list_detach(&(*c
)->buttons
, *b
);
894 /** Get only visible clients for a screen.
895 * \param L The Lua VM state.
897 * \lparam A screen number.
898 * \lreturn A table with all visible clients for this screen.
901 luaA_client_visible_get(lua_State
*L
)
905 int screen
= luaL_checknumber(L
, 1) - 1;
907 luaA_checkscreen(screen
);
911 for(c
= globalconf
.clients
; c
; c
= c
->next
)
912 if(!c
->skip
&& !c
->ishidden
&& client_isvisible(c
, screen
))
914 luaA_client_userdata_new(globalconf
.L
, c
);
915 lua_rawseti(L
, -2, i
++);
921 /** Get the currently focused client (DEPRECATED).
922 * \param L The Lua VM state.
924 * \lreturn The currently focused client.
927 luaA_client_focus_get(lua_State
*L
)
929 if(globalconf
.screen_focus
->client_focus
)
930 return luaA_client_userdata_new(L
, globalconf
.screen_focus
->client_focus
);
935 /** Set client border width.
936 * \param c The client.
937 * \param width The border width.
940 client_setborder(client_t
*c
, int width
)
944 if((c
->noborder
&& width
> 0) || width
== c
->border
|| width
< 0)
948 xcb_configure_window(globalconf
.connection
, c
->win
,
949 XCB_CONFIG_WINDOW_BORDER_WIDTH
, &w
);
951 if(c
->isfloating
|| layout_get_current(c
->screen
) == layout_floating
)
952 titlebar_update_geometry_floating(c
);
954 globalconf
.screens
[c
->screen
].need_arrange
= true;
958 * \param L The Lua VM state.
964 luaA_client_kill(lua_State
*L
)
966 client_t
**c
= luaA_checkudata(L
, 1, "client");
971 /** Swap a client with another one.
972 * \param L The Lua VM state.
975 * \lparam A client to swap with.
978 luaA_client_swap(lua_State
*L
)
980 client_t
**c
= luaA_checkudata(L
, 1, "client");
981 client_t
**swap
= luaA_checkudata(L
, 2, "client");
982 client_list_swap(&globalconf
.clients
, *swap
, *c
);
983 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
984 globalconf
.screens
[(*swap
)->screen
].need_arrange
= true;
985 widget_invalidate_cache((*c
)->screen
, WIDGET_CACHE_CLIENTS
);
986 widget_invalidate_cache((*swap
)->screen
, WIDGET_CACHE_CLIENTS
);
990 /** Access or set the client tags.
991 * \param L The Lua VM state.
992 * \return The number of elements pushed on stack.
993 * \lparam A table with tags to set, or none to get the current tags table.
994 * \return The clients tag.
997 luaA_client_tags(lua_State
*L
)
1001 client_t
**c
= luaA_checkudata(L
, 1, "client");
1004 if(lua_gettop(L
) == 2)
1006 luaA_checktable(L
, 2);
1007 tags
= &globalconf
.screens
[(*c
)->screen
].tags
;
1008 for(int i
= 0; i
< tags
->len
; i
++)
1009 untag_client(*c
, tags
->tab
[i
]);
1011 while(lua_next(L
, 2))
1013 tag
= luaA_checkudata(L
, -1, "tag");
1014 tag_client(*c
, *tag
);
1020 tags
= &globalconf
.screens
[(*c
)->screen
].tags
;
1022 for(int i
= 0; i
< tags
->len
; i
++)
1023 if(is_client_tagged(*c
, tags
->tab
[i
]))
1025 luaA_tag_userdata_new(L
, tags
->tab
[i
]);
1026 lua_rawseti(L
, -2, ++j
);
1032 /** Focus a client (DEPRECATED).
1033 * \param L The Lua VM state.
1039 luaA_client_focus_set(lua_State
*L
)
1041 client_t
**c
= luaA_checkudata(L
, 1, "client");
1047 /** Raise a client on top of others which are on the same layer.
1048 * \param L The Lua VM state.
1054 luaA_client_raise(lua_State
*L
)
1056 client_t
**c
= luaA_checkudata(L
, 1, "client");
1061 /** Redraw a client by unmapping and mapping it quickly.
1062 * \param L The Lua VM state.
1068 luaA_client_redraw(lua_State
*L
)
1070 client_t
**c
= luaA_checkudata(L
, 1, "client");
1071 xcb_unmap_window(globalconf
.connection
, (*c
)->win
);
1072 xcb_map_window(globalconf
.connection
, (*c
)->win
);
1076 /** Return a formated string for a client.
1077 * \param L The Lua VM state.
1080 * \lreturn A string.
1083 luaA_client_tostring(lua_State
*L
)
1085 client_t
**p
= luaA_checkudata(L
, 1, "client");
1086 lua_pushfstring(L
, "[client udata(%p) name(%s)]", *p
, (*p
)->name
);
1090 /** Stop managing a client.
1091 * \param L The Lua VM state.
1096 luaA_client_unmanage(lua_State
*L
)
1098 client_t
**c
= luaA_checkudata(L
, 1, "client");
1099 client_unmanage(*c
);
1103 /** Client newindex.
1104 * \param L The Lua VM state.
1105 * \return The number of elements pushed on stack.
1108 luaA_client_newindex(lua_State
*L
)
1111 client_t
**c
= luaA_checkudata(L
, 1, "client");
1112 const char *buf
= luaL_checklstring(L
, 2, &len
);
1116 titlebar_t
**t
= NULL
;
1119 luaL_error(L
, "client is invalid\n");
1121 switch(a_tokenize(buf
, len
))
1124 buf
= luaL_checklstring(L
, 3, &len
);
1125 p_delete(&(*c
)->name
);
1126 a_iso2utf8(&(*c
)->name
, buf
, len
);
1127 widget_invalidate_cache((*c
)->screen
, WIDGET_CACHE_CLIENTS
);
1129 case A_TK_FLOATING_PLACEMENT
:
1130 (*c
)->floating_placement
= name_func_lookup(luaL_checkstring(L
, 3),
1131 FloatingPlacementList
);
1134 if(globalconf
.screens_info
->xinerama_is_active
)
1136 i
= luaL_checknumber(L
, 3) - 1;
1137 luaA_checkscreen(i
);
1138 if(i
!= (*c
)->screen
)
1139 screen_client_moveto(*c
, i
, true);
1143 b
= luaA_checkboolean(L
, 3);
1144 if(b
!= (*c
)->ishidden
)
1147 if(client_isvisible(*c
, (*c
)->screen
))
1148 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1151 case A_TK_ICON_PATH
:
1152 buf
= luaL_checkstring(L
, 3);
1153 p_delete(&(*c
)->icon_path
);
1154 (*c
)->icon_path
= a_strdup(buf
);
1155 widget_invalidate_cache((*c
)->screen
, WIDGET_CACHE_CLIENTS
);
1159 window_opacity_set((*c
)->win
, -1);
1162 d
= luaL_checknumber(L
, 3);
1163 if(d
>= 0 && d
<= 1)
1164 window_opacity_set((*c
)->win
, d
);
1168 client_setfloating(*c
, luaA_checkboolean(L
, 3));
1170 case A_TK_HONORSIZEHINTS
:
1171 (*c
)->honorsizehints
= luaA_checkboolean(L
, 3);
1172 if(client_isvisible(*c
, (*c
)->screen
))
1173 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1175 case A_TK_BORDER_WIDTH
:
1176 client_setborder(*c
, luaL_checknumber(L
, 3));
1178 case A_TK_BORDER_COLOR
:
1179 if((buf
= luaL_checklstring(L
, 3, &len
))
1180 && xcolor_init_reply(globalconf
.connection
,
1181 xcolor_init_unchecked(globalconf
.connection
,
1182 &(*c
)->border_color
,
1183 (*c
)->phys_screen
, buf
,
1185 xcb_change_window_attributes(globalconf
.connection
, (*c
)->win
,
1186 XCB_CW_BORDER_PIXEL
, &(*c
)->border_color
.pixel
);
1189 if((*c
)->isfloating
|| layout_get_current((*c
)->screen
) == layout_floating
)
1192 luaA_checktable(L
, 3);
1193 geometry
.x
= luaA_getopt_number(L
, 3, "x", (*c
)->geometry
.x
);
1194 geometry
.y
= luaA_getopt_number(L
, 3, "y", (*c
)->geometry
.y
);
1195 geometry
.width
= luaA_getopt_number(L
, 3, "width", (*c
)->geometry
.width
);
1196 geometry
.height
= luaA_getopt_number(L
, 3, "height", (*c
)->geometry
.height
);
1197 client_resize(*c
, geometry
, false);
1201 if(!lua_isnil(L
, 3))
1203 t
= luaA_checkudata(L
, 3, "titlebar");
1204 if(client_getbytitlebar(*t
))
1205 luaL_error(L
, "titlebar is already used by another client");
1208 /* If client had a titlebar, unref it */
1211 simplewindow_delete(&(*c
)->titlebar
->sw
);
1212 titlebar_unref(&(*c
)->titlebar
);
1213 (*c
)->titlebar
= NULL
;
1214 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1219 /* Attach titlebar to client */
1220 (*c
)->titlebar
= *t
;
1233 * \param L The Lua VM state.
1234 * \return The number of elements pushed on stack.
1236 * \lfield name The client title.
1237 * \lfield class The client class.
1238 * \lfield instance The client instance.
1239 * \lfield pid The client PID, if available.
1240 * \lfield machine The machine client is running on.
1241 * \lfield icon_name The client name when iconified.
1242 * \lfield floating_placement The floating placement used for this client.
1243 * \lfield screen Client screen number.
1244 * \lfield hide Define if the client must be hidden, i.e. never mapped.
1245 * \lfield icon_path Path to the icon used to identify.
1246 * \lfield floating True always floating.
1247 * \lfield honorsizehints Honor size hints, i.e. respect size ratio.
1248 * \lfield border_width The client border width.
1249 * \lfield border_color The client border color.
1250 * \lfield coords The client coordinates.
1251 * \lfield titlebar The client titlebar.
1252 * \lfield urgent The client urgent state.
1253 * \lfield focus The focused client.
1254 * \lfield opacity The client opacity between 0 and 1.
1257 luaA_client_index(lua_State
*L
)
1261 client_t
**c
= luaA_checkudata(L
, 1, "client");
1262 const char *buf
= luaL_checklstring(L
, 2, &len
);
1265 xutil_class_hint_t hint
;
1266 xcb_get_property_cookie_t prop_c
;
1267 xcb_get_property_reply_t
*prop_r
= NULL
;
1271 luaL_error(L
, "client is invalid\n");
1273 if(luaA_usemetatable(L
, 1, 2))
1276 switch(a_tokenize(buf
, len
))
1279 lua_pushstring(L
, (*c
)->name
);
1282 if(!xutil_class_hint_get(globalconf
.connection
, (*c
)->win
, &hint
))
1284 lua_pushstring(L
, hint
.res_class
);
1287 if(!xutil_class_hint_get(globalconf
.connection
, (*c
)->win
, &hint
))
1289 lua_pushstring(L
, hint
.res_name
);
1292 prop_c
= xcb_get_property_unchecked(globalconf
.connection
, false, (*c
)->win
, _NET_WM_PID
, CARDINAL
, 0L, 1L);
1293 prop_r
= xcb_get_property_reply(globalconf
.connection
, prop_c
, NULL
);
1295 if(prop_r
&& prop_r
->value_len
&& (data
= xcb_get_property_value(prop_r
)))
1296 lua_pushnumber(L
, *(uint32_t *)data
);
1304 if(!xutil_text_prop_get(globalconf
.connection
, (*c
)->win
,
1305 WM_CLIENT_MACHINE
, &value
, &slen
))
1307 lua_pushlstring(L
, value
, slen
);
1310 case A_TK_ICON_NAME
:
1311 if(!xutil_text_prop_get(globalconf
.connection
, (*c
)->win
,
1312 _NET_WM_ICON_NAME
, &value
, &slen
))
1313 if(!xutil_text_prop_get(globalconf
.connection
, (*c
)->win
,
1314 WM_ICON_NAME
, &value
, &slen
))
1316 lua_pushlstring(L
, value
, slen
);
1319 case A_TK_FLOATING_PLACEMENT
:
1320 lua_pushstring(L
, name_func_rlookup((*c
)->floating_placement
,
1321 FloatingPlacementList
));
1324 lua_pushnumber(L
, 1 + (*c
)->screen
);
1327 lua_pushboolean(L
, (*c
)->ishidden
);
1329 case A_TK_ICON_PATH
:
1330 lua_pushstring(L
, (*c
)->icon_path
);
1333 if((d
= window_opacity_get((*c
)->win
)) >= 0)
1334 lua_pushnumber(L
, d
);
1339 lua_pushboolean(L
, (*c
)->isfloating
);
1341 case A_TK_HONORSIZEHINTS
:
1342 lua_pushboolean(L
, (*c
)->honorsizehints
);
1344 case A_TK_BORDER_WIDTH
:
1345 lua_pushnumber(L
, (*c
)->border
);
1347 case A_TK_BORDER_COLOR
:
1348 luaA_pushcolor(L
, &(*c
)->border_color
);
1352 lua_pushnumber(L
, (*c
)->geometry
.width
);
1353 lua_setfield(L
, -2, "width");
1354 lua_pushnumber(L
, (*c
)->geometry
.height
);
1355 lua_setfield(L
, -2, "height");
1356 lua_pushnumber(L
, (*c
)->geometry
.x
);
1357 lua_setfield(L
, -2, "x");
1358 lua_pushnumber(L
, (*c
)->geometry
.y
);
1359 lua_setfield(L
, -2, "y");
1363 return luaA_titlebar_userdata_new(globalconf
.L
, (*c
)->titlebar
);
1366 lua_pushboolean(L
, (*c
)->isurgent
);
1376 * \param L The Lua VM state.
1377 * \return The number of pushed elements.
1380 luaA_client_module_index(lua_State
*L
)
1383 const char *buf
= luaL_checklstring(L
, 2, &len
);
1385 switch(a_tokenize(buf
, len
))
1388 if(globalconf
.screen_focus
->client_focus
)
1389 luaA_client_userdata_new(L
, globalconf
.screen_focus
->client_focus
);
1400 /* Client module new index.
1401 * \param L The Lua VM state.
1402 * \return The number of pushed elements.
1405 luaA_client_module_newindex(lua_State
*L
)
1408 const char *buf
= luaL_checklstring(L
, 2, &len
);
1411 switch(a_tokenize(buf
, len
))
1414 c
= luaA_checkudata(L
, 3, "client");
1424 const struct luaL_reg awesome_client_methods
[] =
1426 { "get", luaA_client_get
},
1427 { "focus_get", luaA_client_focus_get
},
1428 { "visible_get", luaA_client_visible_get
},
1429 { "__index", luaA_client_module_index
},
1430 { "__newindex", luaA_client_module_newindex
},
1433 const struct luaL_reg awesome_client_meta
[] =
1435 { "tags", luaA_client_tags
},
1436 { "kill", luaA_client_kill
},
1437 { "swap", luaA_client_swap
},
1438 { "focus_set", luaA_client_focus_set
},
1439 { "raise", luaA_client_raise
},
1440 { "redraw", luaA_client_redraw
},
1441 { "mouse_resize", luaA_client_mouse_resize
},
1442 { "mouse_move", luaA_client_mouse_move
},
1443 { "unmanage", luaA_client_unmanage
},
1444 { "mouse_add", luaA_client_mouse_add
},
1445 { "mouse_remove", luaA_client_mouse_remove
},
1446 { "__index", luaA_client_index
},
1447 { "__newindex", luaA_client_newindex
},
1448 { "__eq", luaA_client_eq
},
1449 { "__gc", luaA_client_gc
},
1450 { "__tostring", luaA_client_tostring
},
1454 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80