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_setstate(c
->win
, XCB_WM_ICONIC_STATE
);
221 window_setstate(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 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
);
402 c
->icon
= ewmh_window_icon_get(c
->win
);
405 u_size_hints
= client_updatesizehints(c
);
406 client_updatewmhints(c
);
408 /* Try to load props if any */
409 if(!(retloadprops
= client_loadprops(c
, &globalconf
.screens
[screen
])))
410 screen_client_moveto(c
, screen
, true);
412 /* Then check clients hints */
413 ewmh_check_client_hints(c
);
415 /* check for transient and set tags like its parent */
416 if((rettrans
= xcb_get_wm_transient_for(globalconf
.connection
, w
, &trans
))
417 && (t
= client_getbywin(trans
)))
419 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
420 for(int i
= 0; i
< tags
->len
; i
++)
421 if(is_client_tagged(t
, tags
->tab
[i
]))
422 tag_client(c
, tags
->tab
[i
]);
425 /* should be floating if transsient or fixed */
426 if(rettrans
|| c
->isfixed
)
427 client_setfloating(c
, true);
429 /* Push client in client list */
430 client_list_push(&globalconf
.clients
, c
);
432 /* Push client in stack */
435 /* update window title */
436 client_updatetitle(c
);
438 ewmh_update_net_client_list(c
->phys_screen
);
439 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
442 luaA_client_userdata_new(globalconf
.L
, c
);
443 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.manage
, 1, 0);
445 if(c
->floating_placement
448 && !(xcb_size_hints_get_flags(u_size_hints
) & (XCB_SIZE_US_POSITION_HINT
449 | XCB_SIZE_P_POSITION_HINT
)))
452 client_resize(c
, c
->floating_placement(c
), false);
454 c
->f_geometry
= c
->floating_placement(c
);
458 xcb_free_size_hints(u_size_hints
);
461 /** Compute client geometry with respect to its geometry hints.
462 * \param c The client.
463 * \param geometry The geometry that the client might receive.
464 * \return The geometry the client must take respecting its hints.
467 client_geometry_hints(client_t
*c
, area_t geometry
)
469 double dx
, dy
, max
, min
, ratio
;
471 if(c
->minay
> 0 && c
->maxay
> 0 && (geometry
.height
- c
->baseh
) > 0
472 && (geometry
.width
- c
->basew
) > 0)
474 dx
= (double) (geometry
.width
- c
->basew
);
475 dy
= (double) (geometry
.height
- c
->baseh
);
476 min
= (double) (c
->minax
) / (double) (c
->minay
);
477 max
= (double) (c
->maxax
) / (double) (c
->maxay
);
479 if(max
> 0 && min
> 0 && ratio
> 0)
483 dy
= (dx
* min
+ dy
) / (min
* min
+ 1);
485 geometry
.width
= (int) dx
+ c
->basew
;
486 geometry
.height
= (int) dy
+ c
->baseh
;
490 dy
= (dx
* min
+ dy
) / (max
* max
+ 1);
492 geometry
.width
= (int) dx
+ c
->basew
;
493 geometry
.height
= (int) dy
+ c
->baseh
;
497 if(c
->minw
&& geometry
.width
< c
->minw
)
498 geometry
.width
= c
->minw
;
499 if(c
->minh
&& geometry
.height
< c
->minh
)
500 geometry
.height
= c
->minh
;
501 if(c
->maxw
&& geometry
.width
> c
->maxw
)
502 geometry
.width
= c
->maxw
;
503 if(c
->maxh
&& geometry
.height
> c
->maxh
)
504 geometry
.height
= c
->maxh
;
506 geometry
.width
-= (geometry
.width
- c
->basew
) % c
->incw
;
508 geometry
.height
-= (geometry
.height
- c
->baseh
) % c
->inch
;
513 /** Resize client window.
514 * \param c Client to resize.
515 * \param geometry New window geometry.
516 * \param hints Use size hints.
517 * \return True if the client has been resized.
520 client_resize(client_t
*c
, area_t geometry
, bool hints
)
524 layout_t
*layout
= layout_get_current(c
->screen
);
525 bool resized
= false;
526 /* Values to configure a window is an array where values are
527 * stored according to 'value_mask' */
530 if(c
->titlebar
&& !c
->ismoving
&& !c
->isfloating
&& layout
!= layout_floating
)
531 geometry
= titlebar_geometry_remove(c
->titlebar
, c
->border
, geometry
);
534 geometry
= client_geometry_hints(c
, geometry
);
536 if(geometry
.width
<= 0 || geometry
.height
<= 0)
539 /* offscreen appearance fixes */
540 area
= display_area_get(c
->phys_screen
, NULL
,
541 &globalconf
.screens
[c
->screen
].padding
);
543 if(geometry
.x
> area
.width
)
544 geometry
.x
= area
.width
- geometry
.width
- 2 * c
->border
;
545 if(geometry
.y
> area
.height
)
546 geometry
.y
= area
.height
- geometry
.height
- 2 * c
->border
;
547 if(geometry
.x
+ geometry
.width
+ 2 * c
->border
< 0)
549 if(geometry
.y
+ geometry
.height
+ 2 * c
->border
< 0)
552 if(c
->geometry
.x
!= geometry
.x
|| c
->geometry
.y
!= geometry
.y
553 || c
->geometry
.width
!= geometry
.width
|| c
->geometry
.height
!= geometry
.height
)
556 screen_get_bycoord(globalconf
.screens_info
, c
->screen
, geometry
.x
, geometry
.y
);
558 c
->geometry
.x
= values
[0] = geometry
.x
;
559 c
->geometry
.width
= values
[2] = geometry
.width
;
560 c
->geometry
.y
= values
[1] = geometry
.y
;
561 c
->geometry
.height
= values
[3] = geometry
.height
;
562 values
[4] = c
->border
;
564 /* save the floating geometry if the window is floating but not
566 if(c
->ismoving
|| c
->isfloating
567 || layout_get_current(new_screen
) == layout_floating
)
569 titlebar_update_geometry_floating(c
);
571 c
->f_geometry
= geometry
;
574 xcb_configure_window(globalconf
.connection
, c
->win
,
575 XCB_CONFIG_WINDOW_X
| XCB_CONFIG_WINDOW_Y
|
576 XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
|
577 XCB_CONFIG_WINDOW_BORDER_WIDTH
,
579 window_configure(c
->win
, geometry
, c
->border
);
581 if(c
->screen
!= new_screen
)
582 screen_client_moveto(c
, new_screen
, false);
587 /* call it again like it was floating,
588 * we want it to be sticked to the window */
589 if(!c
->ismoving
&& !c
->isfloating
&& layout
!= layout_floating
)
590 titlebar_update_geometry_floating(c
);
595 /* Set the client layer.
596 * \param c The client.
597 * \param layer The layer.
600 client_setlayer(client_t
*c
, layer_t layer
)
607 /** Set a clinet floating.
608 * \param c The client.
609 * \param floating Set floating, or not.
610 * \param layer Layer to put the floating window onto.
613 client_setfloating(client_t
*c
, bool floating
)
615 if(c
->isfloating
!= floating
)
617 if((c
->isfloating
= floating
))
619 client_setlayer(c
, MAX(c
->layer
, LAYER_FLOAT
));
620 client_resize(c
, c
->f_geometry
, false);
621 titlebar_update_geometry_floating(c
);
626 client_setlayer(c
, c
->oldlayer
);
627 client_resize(c
, c
->m_geometry
, false);
630 client_setlayer(c
, c
->oldlayer
);
631 if(client_isvisible(c
, c
->screen
))
632 globalconf
.screens
[c
->screen
].need_arrange
= true;
633 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
638 /** Save client properties as an X property.
639 * \param c The client.
642 client_saveprops(client_t
*c
)
644 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
645 unsigned char *prop
= p_alloca(unsigned char, tags
->len
+ 3);
648 for(i
= 0; i
< tags
->len
; i
++)
649 prop
[i
] = is_client_tagged(c
, tags
->tab
[i
]) ? '1' : '0';
651 prop
[i
++] = c
->isfloating
? '1' : '0';
652 prop
[i
++] = '0' + c
->layer
;
654 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
, c
->win
, _AWESOME_PROPERTIES
, STRING
, 8, i
, prop
);
658 * \param c The client.
661 client_unban(client_t
*c
)
663 xcb_map_window(globalconf
.connection
, c
->win
);
664 window_setstate(c
->win
, XCB_WM_NORMAL_STATE
);
665 if(c
->titlebar
&& c
->titlebar
->sw
&& c
->titlebar
->position
)
666 xcb_map_window(globalconf
.connection
, c
->titlebar
->sw
->window
);
669 /** Unmanage a client.
670 * \param c The client.
673 client_unmanage(client_t
*c
)
675 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
677 if(globalconf
.screens
[c
->screen
].client_focus
== c
)
681 luaA_client_userdata_new(globalconf
.L
, c
);
682 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.unmanage
, 1, 0);
684 /* The server grab construct avoids race conditions. */
685 xcb_grab_server(globalconf
.connection
);
687 xcb_configure_window(globalconf
.connection
, c
->win
,
688 XCB_CONFIG_WINDOW_BORDER_WIDTH
,
689 (uint32_t *) &c
->oldborder
);
691 xcb_ungrab_button(globalconf
.connection
, XCB_BUTTON_INDEX_ANY
, c
->win
,
693 window_setstate(c
->win
, XCB_WM_WITHDRAWN_STATE
);
695 xcb_aux_sync(globalconf
.connection
);
696 xcb_ungrab_server(globalconf
.connection
);
698 /* remove client everywhere */
699 client_list_detach(&globalconf
.clients
, c
);
700 stack_client_delete(c
);
701 for(int i
= 0; i
< tags
->len
; i
++)
702 untag_client(c
, tags
->tab
[i
]);
706 simplewindow_delete(&c
->titlebar
->sw
);
707 titlebar_unref(&c
->titlebar
);
710 ewmh_update_net_client_list(c
->phys_screen
);
712 /* delete properties */
713 xcb_delete_property(globalconf
.connection
, c
->win
, _AWESOME_PROPERTIES
);
715 /* set client as invalid */
721 /** Update the WM hints of a client.
722 * \param c The client.
725 client_updatewmhints(client_t
*c
)
728 uint32_t wm_hints_flags
;
730 if((wmh
= xcb_get_wm_hints(globalconf
.connection
, c
->win
)))
732 wm_hints_flags
= xcb_wm_hints_get_flags(wmh
);
733 if((c
->isurgent
= xcb_wm_hints_get_urgency(wmh
)))
736 luaA_client_userdata_new(globalconf
.L
, c
);
737 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.urgent
, 1, 0);
739 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
741 if((wm_hints_flags
& XCB_WM_STATE_HINT
) &&
742 (xcb_wm_hints_get_initial_state(wmh
) == XCB_WM_WITHDRAWN_STATE
))
744 client_setborder(c
, 0);
747 xcb_free_wm_hints(wmh
);
751 /** Update the size hints of a client.
752 * \param c The client.
753 * \return A pointer to a xcb_size_hints_t.
756 client_updatesizehints(client_t
*c
)
759 xcb_size_hints_t
*size
;
762 if(!(size
= xcb_get_wm_normal_hints(globalconf
.connection
, c
->win
, &msize
)))
765 size_flags
= xcb_size_hints_get_flags(size
);
767 if((size_flags
& XCB_SIZE_P_SIZE_HINT
))
768 xcb_size_hints_get_base_size(size
, &c
->basew
, &c
->baseh
);
769 else if((size_flags
& XCB_SIZE_P_MIN_SIZE_HINT
))
770 xcb_size_hints_get_min_size(size
, &c
->basew
, &c
->baseh
);
772 c
->basew
= c
->baseh
= 0;
773 if((size_flags
& XCB_SIZE_P_RESIZE_INC_HINT
))
774 xcb_size_hints_get_increase(size
, &c
->incw
, &c
->inch
);
776 c
->incw
= c
->inch
= 0;
778 if((size_flags
& XCB_SIZE_P_MAX_SIZE_HINT
))
779 xcb_size_hints_get_max_size(size
, &c
->maxw
, &c
->maxh
);
781 c
->maxw
= c
->maxh
= 0;
783 if((size_flags
& XCB_SIZE_P_MIN_SIZE_HINT
))
784 xcb_size_hints_get_min_size(size
, &c
->minw
, &c
->minh
);
785 else if((size_flags
& XCB_SIZE_BASE_SIZE_HINT
))
786 xcb_size_hints_get_base_size(size
, &c
->minw
, &c
->minh
);
788 c
->minw
= c
->minh
= 0;
790 if((size_flags
& XCB_SIZE_P_ASPECT_HINT
))
792 xcb_size_hints_get_min_aspect(size
, &c
->minax
, &c
->minay
);
793 xcb_size_hints_get_max_aspect(size
, &c
->maxax
, &c
->maxay
);
796 c
->minax
= c
->maxax
= c
->minay
= c
->maxay
= 0;
798 if(c
->maxw
&& c
->minw
&& c
->maxh
&& c
->minh
799 && c
->maxw
== c
->minw
&& c
->maxh
== c
->minh
)
802 c
->hassizehints
= !(!c
->basew
&& !c
->baseh
&& !c
->incw
&& !c
->inch
803 && !c
->maxw
&& !c
->maxh
&& !c
->minw
&& !c
->minh
804 && !c
->minax
&& !c
->maxax
&& !c
->minax
&& !c
->minay
);
808 /** Kill a client via a WM_DELETE_WINDOW request or XKillClient if not
810 * \param c The client to kill.
813 client_kill(client_t
*c
)
815 xcb_client_message_event_t ev
;
817 if(window_isprotodel(c
->win
))
819 /* Initialize all of event's fields first */
822 ev
.response_type
= XCB_CLIENT_MESSAGE
;
825 ev
.data
.data32
[1] = XCB_CURRENT_TIME
;
826 ev
.type
= WM_PROTOCOLS
;
827 ev
.data
.data32
[0] = WM_DELETE_WINDOW
;
829 xcb_send_event(globalconf
.connection
, false, c
->win
,
830 XCB_EVENT_MASK_NO_EVENT
, (char *) &ev
);
833 xcb_kill_client(globalconf
.connection
, c
->win
);
836 /** Get all clients into a table.
837 * \param L The Lua VM state.
839 * \lreturn A table with all clients.
842 luaA_client_get(lua_State
*L
)
849 for(c
= globalconf
.clients
; c
; c
= c
->next
)
851 luaA_client_userdata_new(globalconf
.L
, c
);
852 lua_rawseti(L
, -2, i
++);
858 /** Add mouse bindings over clients's window.
859 * \param L The Lua VM state.
862 * \lparam A button binding.
865 luaA_client_mouse_add(lua_State
*L
)
867 client_t
**c
= luaA_checkudata(L
, 1, "client");
868 button_t
**b
= luaA_checkudata(L
, 2, "mouse");
869 button_list_push(&(*c
)->buttons
, *b
);
874 /** Remove mouse bindings over clients's window.
875 * \param L The Lua VM state.
878 * \lparam A button binding.
881 luaA_client_mouse_remove(lua_State
*L
)
883 client_t
**c
= luaA_checkudata(L
, 1, "client");
884 button_t
**b
= luaA_checkudata(L
, 1, "mouse");
885 button_list_detach(&(*c
)->buttons
, *b
);
890 /** Get only visible clients for a screen.
891 * \param L The Lua VM state.
893 * \lparam A screen number.
894 * \lreturn A table with all visible clients for this screen.
897 luaA_client_visible_get(lua_State
*L
)
901 int screen
= luaL_checknumber(L
, 1) - 1;
903 luaA_checkscreen(screen
);
907 for(c
= globalconf
.clients
; c
; c
= c
->next
)
908 if(!c
->skip
&& !c
->ishidden
&& client_isvisible(c
, screen
))
910 luaA_client_userdata_new(globalconf
.L
, c
);
911 lua_rawseti(L
, -2, i
++);
917 /** Get the currently focused client (DEPRECATED).
918 * \param L The Lua VM state.
920 * \lreturn The currently focused client.
923 luaA_client_focus_get(lua_State
*L
)
925 if(globalconf
.screen_focus
->client_focus
)
926 return luaA_client_userdata_new(L
, globalconf
.screen_focus
->client_focus
);
931 /** Set client border width.
932 * \param c The client.
933 * \param width The border width.
936 client_setborder(client_t
*c
, int width
)
940 if((c
->noborder
&& width
> 0) || width
== c
->border
|| width
< 0)
944 xcb_configure_window(globalconf
.connection
, c
->win
,
945 XCB_CONFIG_WINDOW_BORDER_WIDTH
, &w
);
947 if(c
->isfloating
|| layout_get_current(c
->screen
) == layout_floating
)
948 titlebar_update_geometry_floating(c
);
950 globalconf
.screens
[c
->screen
].need_arrange
= true;
954 * \param L The Lua VM state.
960 luaA_client_kill(lua_State
*L
)
962 client_t
**c
= luaA_checkudata(L
, 1, "client");
967 /** Swap a client with another one.
968 * \param L The Lua VM state.
971 * \lparam A client to swap with.
974 luaA_client_swap(lua_State
*L
)
976 client_t
**c
= luaA_checkudata(L
, 1, "client");
977 client_t
**swap
= luaA_checkudata(L
, 2, "client");
978 client_list_swap(&globalconf
.clients
, *swap
, *c
);
979 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
980 globalconf
.screens
[(*swap
)->screen
].need_arrange
= true;
981 widget_invalidate_cache((*c
)->screen
, WIDGET_CACHE_CLIENTS
);
982 widget_invalidate_cache((*swap
)->screen
, WIDGET_CACHE_CLIENTS
);
986 /** Focus a client (DEPRECATED).
987 * \param L The Lua VM state.
993 luaA_client_focus_set(lua_State
*L
)
995 client_t
**c
= luaA_checkudata(L
, 1, "client");
1001 /** Raise a client on top of others which are on the same layer.
1002 * \param L The Lua VM state.
1008 luaA_client_raise(lua_State
*L
)
1010 client_t
**c
= luaA_checkudata(L
, 1, "client");
1015 /** Redraw a client by unmapping and mapping it quickly.
1016 * \param L The Lua VM state.
1022 luaA_client_redraw(lua_State
*L
)
1024 client_t
**c
= luaA_checkudata(L
, 1, "client");
1025 xcb_unmap_window(globalconf
.connection
, (*c
)->win
);
1026 xcb_map_window(globalconf
.connection
, (*c
)->win
);
1030 /** Return a formated string for a client.
1031 * \param L The Lua VM state.
1034 * \lreturn A string.
1037 luaA_client_tostring(lua_State
*L
)
1039 client_t
**p
= luaA_checkudata(L
, 1, "client");
1040 lua_pushfstring(L
, "[client udata(%p) name(%s)]", *p
, (*p
)->name
);
1044 /** Stop managing a client.
1045 * \param L The Lua VM state.
1050 luaA_client_unmanage(lua_State
*L
)
1052 client_t
**c
= luaA_checkudata(L
, 1, "client");
1053 client_unmanage(*c
);
1057 /** Client newindex.
1058 * \param L The Lua VM state.
1059 * \return The number of elements pushed on stack.
1062 luaA_client_newindex(lua_State
*L
)
1065 client_t
**c
= luaA_checkudata(L
, 1, "client");
1066 const char *buf
= luaL_checklstring(L
, 2, &len
);
1070 titlebar_t
**t
= NULL
;
1075 luaL_error(L
, "client is invalid\n");
1077 switch(a_tokenize(buf
, len
))
1080 buf
= luaL_checklstring(L
, 3, &len
);
1081 p_delete(&(*c
)->name
);
1082 a_iso2utf8(&(*c
)->name
, buf
, len
);
1083 widget_invalidate_cache((*c
)->screen
, WIDGET_CACHE_CLIENTS
);
1085 case A_TK_FLOATING_PLACEMENT
:
1086 (*c
)->floating_placement
= name_func_lookup(luaL_checkstring(L
, 3),
1087 FloatingPlacementList
);
1090 if(globalconf
.screens_info
->xinerama_is_active
)
1092 i
= luaL_checknumber(L
, 3) - 1;
1093 luaA_checkscreen(i
);
1094 if(i
!= (*c
)->screen
)
1095 screen_client_moveto(*c
, i
, true);
1099 b
= luaA_checkboolean(L
, 3);
1100 if(b
!= (*c
)->ishidden
)
1103 if(client_isvisible(*c
, (*c
)->screen
))
1104 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1107 case A_TK_ICON_PATH
:
1108 buf
= luaL_checkstring(L
, 3);
1109 p_delete(&(*c
)->icon_path
);
1110 (*c
)->icon_path
= a_strdup(buf
);
1111 widget_invalidate_cache((*c
)->screen
, WIDGET_CACHE_CLIENTS
);
1114 d
= luaL_checknumber(L
, 3);
1115 if(d
== -1 || (d
>= 0 && d
<= 1))
1116 window_settrans((*c
)->win
, d
);
1119 client_setfloating(*c
, luaA_checkboolean(L
, 3));
1121 case A_TK_HONORSIZEHINTS
:
1122 (*c
)->honorsizehints
= luaA_checkboolean(L
, 3);
1123 if(client_isvisible(*c
, (*c
)->screen
))
1124 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1126 case A_TK_BORDER_WIDTH
:
1127 client_setborder(*c
, luaL_checknumber(L
, 3));
1129 case A_TK_BORDER_COLOR
:
1130 if((buf
= luaL_checklstring(L
, 3, &len
))
1131 && xcolor_init_reply(globalconf
.connection
,
1132 xcolor_init_unchecked(globalconf
.connection
,
1133 &(*c
)->border_color
,
1134 (*c
)->phys_screen
, buf
,
1136 xcb_change_window_attributes(globalconf
.connection
, (*c
)->win
,
1137 XCB_CW_BORDER_PIXEL
, &(*c
)->border_color
.pixel
);
1140 if((*c
)->isfloating
|| layout_get_current((*c
)->screen
) == layout_floating
)
1143 luaA_checktable(L
, 3);
1144 geometry
.x
= luaA_getopt_number(L
, 3, "x", (*c
)->geometry
.x
);
1145 geometry
.y
= luaA_getopt_number(L
, 3, "y", (*c
)->geometry
.y
);
1146 geometry
.width
= luaA_getopt_number(L
, 3, "width", (*c
)->geometry
.width
);
1147 geometry
.height
= luaA_getopt_number(L
, 3, "height", (*c
)->geometry
.height
);
1148 client_resize(*c
, geometry
, false);
1152 if(!lua_isnil(L
, 3))
1154 t
= luaA_checkudata(L
, 3, "titlebar");
1155 if(client_getbytitlebar(*t
))
1156 luaL_error(L
, "titlebar is already used by another client");
1159 /* If client had a titlebar, unref it */
1162 simplewindow_delete(&(*c
)->titlebar
->sw
);
1163 titlebar_unref(&(*c
)->titlebar
);
1164 globalconf
.screens
[(*c
)->screen
].need_arrange
= true;
1169 /* Attach titlebar to client */
1170 (*c
)->titlebar
= *t
;
1176 luaA_checktable(L
, 3);
1177 tags
= &globalconf
.screens
[(*c
)->screen
].tags
;
1178 for(i
= 0; i
< tags
->len
; i
++)
1179 untag_client(*c
, tags
->tab
[i
]);
1181 while(lua_next(L
, 3))
1183 tag
= luaA_checkudata(L
, -1, "tag");
1184 tag_client(*c
, *tag
);
1196 * \param L The Lua VM state.
1197 * \return The number of elements pushed on stack.
1199 * \lfield name The client title.
1200 * \lfield class The client class.
1201 * \lfield instance The client instance.
1202 * \lfield pid The client PID, if available.
1203 * \lfield machine The machine client is running on.
1204 * \lfield icon_name The client name when iconified.
1205 * \lfield floating_placement The floating placement used for this client.
1206 * \lfield screen Client screen number.
1207 * \lfield hide Define if the client must be hidden, i.e. never mapped.
1208 * \lfield icon_path Path to the icon used to identify.
1209 * \lfield floating True always floating.
1210 * \lfield honorsizehints Honor size hints, i.e. respect size ratio.
1211 * \lfield border_width The client border width.
1212 * \lfield border_color The client border color.
1213 * \lfield coords The client coordinates.
1214 * \lfield titlebar The client titlebar.
1215 * \lfield urgent The client urgent state.
1216 * \lfield tags The clients tags.
1217 * \lfield focus The focused client.
1220 luaA_client_index(lua_State
*L
)
1224 client_t
**c
= luaA_checkudata(L
, 1, "client");
1225 const char *buf
= luaL_checklstring(L
, 2, &len
);
1228 xutil_class_hint_t hint
;
1229 xcb_get_property_cookie_t prop_c
;
1230 xcb_get_property_reply_t
*prop_r
= NULL
;
1235 luaL_error(L
, "client is invalid\n");
1237 if(luaA_usemetatable(L
, 1, 2))
1240 switch(a_tokenize(buf
, len
))
1243 lua_pushstring(L
, (*c
)->name
);
1246 if(!xutil_class_hint_get(globalconf
.connection
, (*c
)->win
, &hint
))
1248 lua_pushstring(L
, hint
.res_class
);
1251 if(!xutil_class_hint_get(globalconf
.connection
, (*c
)->win
, &hint
))
1253 lua_pushstring(L
, hint
.res_name
);
1256 prop_c
= xcb_get_property_unchecked(globalconf
.connection
, false, (*c
)->win
, _NET_WM_PID
, CARDINAL
, 0L, 1L);
1257 prop_r
= xcb_get_property_reply(globalconf
.connection
, prop_c
, NULL
);
1259 if(prop_r
&& prop_r
->value_len
&& (data
= xcb_get_property_value(prop_r
)))
1260 lua_pushnumber(L
, *(uint32_t *)data
);
1268 if(!xutil_text_prop_get(globalconf
.connection
, (*c
)->win
,
1269 WM_CLIENT_MACHINE
, &value
, &slen
))
1271 lua_pushlstring(L
, value
, slen
);
1274 case A_TK_ICON_NAME
:
1275 if(!xutil_text_prop_get(globalconf
.connection
, (*c
)->win
,
1276 _NET_WM_ICON_NAME
, &value
, &slen
))
1277 if(!xutil_text_prop_get(globalconf
.connection
, (*c
)->win
,
1278 WM_ICON_NAME
, &value
, &slen
))
1280 lua_pushlstring(L
, value
, slen
);
1283 case A_TK_FLOATING_PLACEMENT
:
1284 lua_pushstring(L
, name_func_rlookup((*c
)->floating_placement
,
1285 FloatingPlacementList
));
1288 lua_pushnumber(L
, 1 + (*c
)->screen
);
1291 lua_pushboolean(L
, (*c
)->ishidden
);
1293 case A_TK_ICON_PATH
:
1294 lua_pushstring(L
, (*c
)->icon_path
);
1297 lua_pushboolean(L
, (*c
)->isfloating
);
1299 case A_TK_HONORSIZEHINTS
:
1300 lua_pushboolean(L
, (*c
)->honorsizehints
);
1302 case A_TK_BORDER_WIDTH
:
1303 lua_pushnumber(L
, (*c
)->border
);
1305 case A_TK_BORDER_COLOR
:
1306 luaA_pushcolor(L
, &(*c
)->border_color
);
1310 lua_pushnumber(L
, (*c
)->geometry
.width
);
1311 lua_setfield(L
, -2, "width");
1312 lua_pushnumber(L
, (*c
)->geometry
.height
);
1313 lua_setfield(L
, -2, "height");
1314 lua_pushnumber(L
, (*c
)->geometry
.x
);
1315 lua_setfield(L
, -2, "x");
1316 lua_pushnumber(L
, (*c
)->geometry
.y
);
1317 lua_setfield(L
, -2, "y");
1321 return luaA_titlebar_userdata_new(globalconf
.L
, (*c
)->titlebar
);
1324 lua_pushboolean(L
, (*c
)->isurgent
);
1327 tags
= &globalconf
.screens
[(*c
)->screen
].tags
;
1329 for(i
= 0; i
< tags
->len
; i
++)
1330 if(is_client_tagged(*c
, tags
->tab
[i
]))
1332 luaA_tag_userdata_new(L
, tags
->tab
[i
]);
1333 luaA_tag_userdata_new(L
, tags
->tab
[i
]);
1345 * \param L The Lua VM state.
1346 * \return The number of pushed elements.
1349 luaA_client_module_index(lua_State
*L
)
1352 const char *buf
= luaL_checklstring(L
, 2, &len
);
1354 switch(a_tokenize(buf
, len
))
1357 if(globalconf
.screen_focus
->client_focus
)
1358 luaA_client_userdata_new(L
, globalconf
.screen_focus
->client_focus
);
1369 /* Client module new index.
1370 * \param L The Lua VM state.
1371 * \return The number of pushed elements.
1374 luaA_client_module_newindex(lua_State
*L
)
1377 const char *buf
= luaL_checklstring(L
, 2, &len
);
1380 switch(a_tokenize(buf
, len
))
1383 c
= luaA_checkudata(L
, 3, "client");
1393 const struct luaL_reg awesome_client_methods
[] =
1395 { "get", luaA_client_get
},
1396 { "focus_get", luaA_client_focus_get
},
1397 { "visible_get", luaA_client_visible_get
},
1398 { "__index", luaA_client_module_index
},
1399 { "__newindex", luaA_client_module_newindex
},
1402 const struct luaL_reg awesome_client_meta
[] =
1404 { "kill", luaA_client_kill
},
1405 { "swap", luaA_client_swap
},
1406 { "focus_set", luaA_client_focus_set
},
1407 { "raise", luaA_client_raise
},
1408 { "redraw", luaA_client_redraw
},
1409 { "mouse_resize", luaA_client_mouse_resize
},
1410 { "mouse_move", luaA_client_mouse_move
},
1411 { "unmanage", luaA_client_unmanage
},
1412 { "mouse_add", luaA_client_mouse_add
},
1413 { "mouse_remove", luaA_client_mouse_remove
},
1414 { "__index", luaA_client_index
},
1415 { "__newindex", luaA_client_newindex
},
1416 { "__eq", luaA_client_eq
},
1417 { "__gc", luaA_client_gc
},
1418 { "__tostring", luaA_client_tostring
},
1422 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80