2 * client.c - client management
4 * Copyright © 2007-2009 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <xcb/xcb_atom.h>
23 #include <xcb/xcb_image.h>
24 #include <cairo-xcb.h>
26 #include "objects/tag.h"
34 #include "common/atoms.h"
35 #include "common/xutil.h"
37 static area_t
titlebar_get_area(client_t
*c
, client_titlebar_t bar
);
38 static drawable_t
*titlebar_get_drawable(lua_State
*L
, client_t
*c
, int cl_idx
, client_titlebar_t bar
);
41 * \param L The Lua VM state.
42 * \return The number of element pushed on stack.
45 client_wipe(client_t
*c
)
47 key_array_wipe(&c
->keys
);
48 xcb_icccm_get_wm_protocols_reply_wipe(&c
->protocols
);
49 p_delete(&c
->machine
);
51 p_delete(&c
->instance
);
52 p_delete(&c
->icon_name
);
53 p_delete(&c
->alt_icon_name
);
55 p_delete(&c
->alt_name
);
57 cairo_surface_destroy(c
->icon
);
60 /** Change the clients urgency flag.
61 * \param L The Lua VM state.
62 * \param cidx The client index on the stack.
63 * \param urgent The new flag state.
66 client_set_urgent(lua_State
*L
, int cidx
, bool urgent
)
68 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
70 if(c
->urgent
!= urgent
)
72 xcb_get_property_cookie_t hints
=
73 xcb_icccm_get_wm_hints_unchecked(globalconf
.connection
, c
->window
);
77 /* update ICCCM hints */
78 xcb_icccm_wm_hints_t wmh
;
79 xcb_icccm_get_wm_hints_reply(globalconf
.connection
, hints
, &wmh
, NULL
);
82 wmh
.flags
|= XCB_ICCCM_WM_HINT_X_URGENCY
;
84 wmh
.flags
&= ~XCB_ICCCM_WM_HINT_X_URGENCY
;
86 xcb_icccm_set_wm_hints(globalconf
.connection
, c
->window
, &wmh
);
88 luaA_object_emit_signal(L
, cidx
, "property::urgent", 0);
92 #define DO_CLIENT_SET_PROPERTY(prop) \
94 client_set_##prop(lua_State *L, int cidx, fieldtypeof(client_t, prop) value) \
96 client_t *c = luaA_checkudata(L, cidx, &client_class); \
97 if(c->prop != value) \
100 luaA_object_emit_signal(L, cidx, "property::" #prop, 0); \
103 DO_CLIENT_SET_PROPERTY(group_window
)
104 DO_CLIENT_SET_PROPERTY(type
)
105 DO_CLIENT_SET_PROPERTY(transient_for
)
106 DO_CLIENT_SET_PROPERTY(pid
)
107 DO_CLIENT_SET_PROPERTY(skip_taskbar
)
108 #undef DO_CLIENT_SET_PROPERTY
110 #define DO_CLIENT_SET_STRING_PROPERTY2(prop, signal) \
112 client_set_##prop(lua_State *L, int cidx, char *value) \
114 client_t *c = luaA_checkudata(L, cidx, &client_class); \
115 p_delete(&c->prop); \
117 luaA_object_emit_signal(L, cidx, "property::" #signal, 0); \
119 #define DO_CLIENT_SET_STRING_PROPERTY(prop) \
120 DO_CLIENT_SET_STRING_PROPERTY2(prop, prop)
121 DO_CLIENT_SET_STRING_PROPERTY(name
)
122 DO_CLIENT_SET_STRING_PROPERTY2(alt_name
, name
)
123 DO_CLIENT_SET_STRING_PROPERTY(icon_name
)
124 DO_CLIENT_SET_STRING_PROPERTY2(alt_icon_name
, icon_name
)
125 DO_CLIENT_SET_STRING_PROPERTY(role
)
126 DO_CLIENT_SET_STRING_PROPERTY(machine
)
127 #undef DO_CLIENT_SET_STRING_PROPERTY
130 client_set_class_instance(lua_State
*L
, int cidx
, const char *class, const char *instance
)
132 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
134 p_delete(&c
->instance
);
135 c
->class = a_strdup(class);
136 luaA_object_emit_signal(L
, cidx
, "property::class", 0);
137 c
->instance
= a_strdup(instance
);
138 luaA_object_emit_signal(L
, cidx
, "property::instance", 0);
141 /** Returns true if a client is tagged
142 * with one of the tags of the specified screen.
143 * \param c The client to check.
144 * \param screen Virtual screen.
145 * \return true if the client is visible, false otherwise.
148 client_maybevisible(client_t
*c
)
153 foreach(tag
, globalconf
.tags
)
154 if(tag_get_selected(*tag
) && is_client_tagged(c
, *tag
))
160 /** Get a client by its window.
161 * \param w The client window to find.
162 * \return A client pointer if found, NULL otherwise.
165 client_getbywin(xcb_window_t w
)
167 foreach(c
, globalconf
.clients
)
168 if((*c
)->window
== w
)
174 /** Get a client by its frame window.
175 * \param w The client window to find.
176 * \return A client pointer if found, NULL otherwise.
179 client_getbyframewin(xcb_window_t w
)
181 foreach(c
, globalconf
.clients
)
182 if((*c
)->frame_window
== w
)
188 /** Unfocus a client (internal).
189 * \param c The client.
192 client_unfocus_internal(client_t
*c
)
194 globalconf
.focus
.client
= NULL
;
196 luaA_object_push(globalconf
.L
, c
);
197 luaA_object_emit_signal(globalconf
.L
, -1, "unfocus", 0);
198 lua_pop(globalconf
.L
, 1);
201 /** Unfocus a client.
202 * \param c The client.
205 client_unfocus(client_t
*c
)
207 client_unfocus_internal(c
);
208 globalconf
.focus
.need_update
= true;
211 /** Check if client supports atom a protocol in WM_PROTOCOL.
212 * \param c The client.
213 * \param atom The protocol atom to check for.
214 * \return True if client has the atom in protocol, false otherwise.
217 client_hasproto(client_t
*c
, xcb_atom_t atom
)
219 for(uint32_t i
= 0; i
< c
->protocols
.atoms_len
; i
++)
220 if(c
->protocols
.atoms
[i
] == atom
)
225 /** Prepare banning a client by running all needed lua events.
226 * \param c The client.
228 void client_ban_unfocus(client_t
*c
)
230 /* Wait until the last moment to take away the focus from the window. */
231 if(globalconf
.focus
.client
== c
)
235 /** Ban client and move it out of the viewport.
236 * \param c The client.
239 client_ban(client_t
*c
)
243 xcb_unmap_window(globalconf
.connection
, c
->frame_window
);
247 client_ban_unfocus(c
);
251 /** This is part of The Bob Marley Algorithm: we ignore enter and leave window
252 * in certain cases, like map/unmap or move, so we don't get spurious events.
255 client_ignore_enterleave_events(void)
257 foreach(c
, globalconf
.clients
)
259 xcb_change_window_attributes(globalconf
.connection
,
262 (const uint32_t []) { CLIENT_SELECT_INPUT_EVENT_MASK
& ~(XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_LEAVE_WINDOW
) });
263 xcb_change_window_attributes(globalconf
.connection
,
266 (const uint32_t []) { FRAME_SELECT_INPUT_EVENT_MASK
& ~(XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_LEAVE_WINDOW
) });
271 client_restore_enterleave_events(void)
273 foreach(c
, globalconf
.clients
)
275 xcb_change_window_attributes(globalconf
.connection
,
278 (const uint32_t []) { CLIENT_SELECT_INPUT_EVENT_MASK
});
279 xcb_change_window_attributes(globalconf
.connection
,
282 (const uint32_t []) { FRAME_SELECT_INPUT_EVENT_MASK
});
286 /** Record that a client got focus.
287 * \param c The client.
290 client_focus_update(client_t
*c
)
292 if(!client_maybevisible(c
))
295 if(globalconf
.focus
.client
)
297 if (globalconf
.focus
.client
== c
)
298 /* Already focused */
301 /* When we are called due to a FocusIn event (=old focused client
302 * already unfocused), we don't want to cause a SetInputFocus,
303 * because the client which has focus now could be using globally
304 * active input model (or 'no input').
306 client_unfocus_internal(globalconf
.focus
.client
);
309 globalconf
.focus
.client
= c
;
311 /* according to EWMH, we have to remove the urgent state from a client */
312 luaA_object_push(globalconf
.L
, c
);
313 client_set_urgent(globalconf
.L
, -1, false);
315 luaA_object_emit_signal(globalconf
.L
, -1, "focus", 0);
316 lua_pop(globalconf
.L
, 1);
319 /** Give focus to client, or to first client if client is NULL.
320 * \param c The client.
323 client_focus(client_t
*c
)
325 /* We have to set focus on first client */
326 if(!c
&& globalconf
.clients
.len
&& !(c
= globalconf
.clients
.tab
[0]))
329 if(!client_maybevisible(c
) || c
== globalconf
.focus
.client
)
332 client_focus_update(c
);
333 globalconf
.focus
.need_update
= true;
337 client_focus_refresh(void)
339 client_t
*c
= globalconf
.focus
.client
;
340 xcb_window_t win
= globalconf
.screen
->root
;
342 if(!globalconf
.focus
.need_update
)
344 globalconf
.focus
.need_update
= false;
348 /* Make sure this window is unbanned and e.g. not minimized */
350 /* Sets focus on window - using xcb_set_input_focus or WM_TAKE_FOCUS */
354 /* Focus the root window to make sure the previously focused client
355 * doesn't get any input in case WM_TAKE_FOCUS gets ignored.
357 win
= globalconf
.screen
->root
;
359 if(client_hasproto(c
, WM_TAKE_FOCUS
))
360 xwindow_takefocus(c
->window
);
363 /* If nothing has the focused or the currently focused client doesn't want
364 * us to focus it, this sets the focus to the root window. This makes sure
365 * the previously focused client actually gets unfocused. Alternatively, the
366 * new client gets the input focus.
368 xcb_set_input_focus(globalconf
.connection
, XCB_INPUT_FOCUS_PARENT
,
369 win
, globalconf
.timestamp
);
373 client_update_properties(client_t
*c
)
376 xcb_get_property_cookie_t wm_normal_hints
= property_get_wm_normal_hints(c
);
377 xcb_get_property_cookie_t wm_hints
= property_get_wm_hints(c
);
378 xcb_get_property_cookie_t wm_transient_for
= property_get_wm_transient_for(c
);
379 xcb_get_property_cookie_t wm_client_leader
= property_get_wm_client_leader(c
);
380 xcb_get_property_cookie_t wm_client_machine
= property_get_wm_client_machine(c
);
381 xcb_get_property_cookie_t wm_window_role
= property_get_wm_window_role(c
);
382 xcb_get_property_cookie_t net_wm_pid
= property_get_net_wm_pid(c
);
383 xcb_get_property_cookie_t net_wm_icon
= property_get_net_wm_icon(c
);
384 xcb_get_property_cookie_t wm_name
= property_get_wm_name(c
);
385 xcb_get_property_cookie_t net_wm_name
= property_get_net_wm_name(c
);
386 xcb_get_property_cookie_t wm_icon_name
= property_get_wm_icon_name(c
);
387 xcb_get_property_cookie_t net_wm_icon_name
= property_get_net_wm_icon_name(c
);
388 xcb_get_property_cookie_t wm_class
= property_get_wm_class(c
);
389 xcb_get_property_cookie_t wm_protocols
= property_get_wm_protocols(c
);
390 xcb_get_property_cookie_t opacity
= xwindow_get_opacity_unchecked(c
->window
);
393 ewmh_process_client_strut(c
);
395 /* Now process all replies */
396 property_update_wm_normal_hints(c
, wm_normal_hints
);
397 property_update_wm_hints(c
, wm_hints
);
398 property_update_wm_transient_for(c
, wm_transient_for
);
399 property_update_wm_client_leader(c
, wm_client_leader
);
400 property_update_wm_client_machine(c
, wm_client_machine
);
401 property_update_wm_window_role(c
, wm_window_role
);
402 property_update_net_wm_pid(c
, net_wm_pid
);
403 property_update_net_wm_icon(c
, net_wm_icon
);
404 property_update_wm_name(c
, wm_name
);
405 property_update_net_wm_name(c
, net_wm_name
);
406 property_update_wm_icon_name(c
, wm_icon_name
);
407 property_update_net_wm_icon_name(c
, net_wm_icon_name
);
408 property_update_wm_class(c
, wm_class
);
409 property_update_wm_protocols(c
, wm_protocols
);
410 window_set_opacity(globalconf
.L
, -1, xwindow_get_opacity_from_cookie(opacity
));
413 /** Manage a new client.
414 * \param w The window.
415 * \param wgeom Window geometry.
416 * \param startup True if we are managing at startup time.
419 client_manage(xcb_window_t w
, xcb_get_geometry_reply_t
*wgeom
, bool startup
)
421 const uint32_t select_input_val
[] = { CLIENT_SELECT_INPUT_EVENT_MASK
};
423 if(systray_iskdedockapp(w
))
425 systray_request_handle(w
, NULL
);
429 /* If this is a new client that just has been launched, then request its
431 xcb_get_property_cookie_t startup_id_q
= { 0 };
433 startup_id_q
= xcb_get_property(globalconf
.connection
, false, w
,
434 _NET_STARTUP_ID
, XCB_GET_PROPERTY_TYPE_ANY
, 0, UINT_MAX
);
436 /* Make sure the window is automatically mapped if awesome exits or dies. */
437 xcb_change_save_set(globalconf
.connection
, XCB_SET_MODE_INSERT
, w
);
439 client_t
*c
= client_new(globalconf
.L
);
440 xcb_screen_t
*s
= globalconf
.screen
;
442 /* consider the window banned */
446 c
->frame_window
= xcb_generate_id(globalconf
.connection
);
447 xcb_create_window(globalconf
.connection
, globalconf
.default_depth
, c
->frame_window
, s
->root
,
448 wgeom
->x
, wgeom
->y
, wgeom
->width
, wgeom
->height
,
449 wgeom
->border_width
, XCB_COPY_FROM_PARENT
, globalconf
.visual
->visual_id
,
450 XCB_CW_BACK_PIXEL
| XCB_CW_BORDER_PIXEL
| XCB_CW_BIT_GRAVITY
451 | XCB_CW_WIN_GRAVITY
| XCB_CW_OVERRIDE_REDIRECT
| XCB_CW_EVENT_MASK
455 globalconf
.screen
->black_pixel
,
456 globalconf
.screen
->black_pixel
,
457 XCB_GRAVITY_NORTH_WEST
,
458 XCB_GRAVITY_NORTH_WEST
,
460 FRAME_SELECT_INPUT_EVENT_MASK
,
461 globalconf
.default_cmap
466 /* The client is already mapped, thus we must be sure that we don't send
467 * ourselves an UnmapNotify due to the xcb_reparent_window().
469 * Grab the server to make sure we don't lose any events.
471 uint32_t no_event
[] = { 0 };
472 xcb_grab_server(globalconf
.connection
);
474 xcb_change_window_attributes(globalconf
.connection
,
475 globalconf
.screen
->root
,
480 xcb_reparent_window(globalconf
.connection
, w
, c
->frame_window
, 0, 0);
481 xcb_map_window(globalconf
.connection
, w
);
485 xcb_change_window_attributes(globalconf
.connection
,
486 globalconf
.screen
->root
,
488 ROOT_WINDOW_EVENT_MASK
);
489 xcb_ungrab_server(globalconf
.connection
);
492 /* Do this now so that we don't get any events for the above
493 * (Else, reparent could cause an UnmapNotify) */
494 xcb_change_window_attributes(globalconf
.connection
, w
, XCB_CW_EVENT_MASK
, select_input_val
);
496 luaA_object_emit_signal(globalconf
.L
, -1, "property::window", 0);
498 /* The frame window gets the border, not the real client window */
499 xcb_configure_window(globalconf
.connection
, w
,
500 XCB_CONFIG_WINDOW_BORDER_WIDTH
,
503 /* Move this window to the bottom of the stack. Without this we would force
504 * other windows which will be above this one to redraw themselves because
505 * this window occludes them for a tiny moment. The next stack_refresh()
506 * will fix this up and move the window to its correct place. */
507 xcb_configure_window(globalconf
.connection
, c
->frame_window
,
508 XCB_CONFIG_WINDOW_STACK_MODE
,
509 (uint32_t[]) { XCB_STACK_MODE_BELOW
});
511 /* Duplicate client and push it in client list */
512 lua_pushvalue(globalconf
.L
, -1);
513 client_array_push(&globalconf
.clients
, luaA_object_ref(globalconf
.L
, -1));
515 /* Set the right screen */
516 screen_client_moveto(c
, screen_getbycoord(wgeom
->x
, wgeom
->y
), false);
518 /* Store initial geometry and emits signals so we inform that geometry have
520 #define HANDLE_GEOM(attr) \
521 c->geometry.attr = wgeom->attr; \
522 luaA_object_emit_signal(globalconf.L, -1, "property::" #attr, 0);
529 luaA_object_emit_signal(globalconf
.L
, -1, "property::geometry", 0);
531 /* Set border width */
532 window_set_border_width(globalconf
.L
, -1, wgeom
->border_width
);
534 /* we honor size hints by default */
535 c
->size_hints_honor
= true;
536 luaA_object_emit_signal(globalconf
.L
, -1, "property::size_hints_honor", 0);
538 /* update all properties */
539 client_update_properties(c
);
541 /* Then check clients hints */
542 ewmh_client_check_hints(c
);
544 /* Push client in stack */
547 /* Always stay in NORMAL_STATE. Even though iconified seems more
548 * appropriate sometimes. The only possible loss is that clients not using
549 * visibility events may continue to process data (when banned).
550 * Without any exposes or other events the cost should be fairly limited though.
552 * Some clients may expect the window to be unmapped when STATE_ICONIFIED.
553 * Two conflicting parts of the ICCCM v2.0 (section 4.1.4):
555 * "Normal -> Iconic - The client should send a ClientMessage event as described later in this section."
556 * (note no explicit mention of unmapping, while Normal->Widthdrawn does mention that)
558 * "Once a client's window has left the Withdrawn state, the window will be mapped
559 * if it is in the Normal state and the window will be unmapped if it is in the Iconic state."
561 * At this stage it's just safer to keep it in normal state and avoid confusion.
563 xwindow_set_state(c
->window
, XCB_ICCCM_WM_STATE_NORMAL
);
567 /* Request our response */
568 xcb_get_property_reply_t
*reply
=
569 xcb_get_property_reply(globalconf
.connection
, startup_id_q
, NULL
);
570 /* Say spawn that a client has been started, with startup id as argument */
571 char *startup_id
= xutil_get_text_property_from_reply(reply
);
573 spawn_start_notify(c
, startup_id
);
574 p_delete(&startup_id
);
577 luaA_class_emit_signal(globalconf
.L
, &client_class
, "list", 0);
579 /* client is still on top of the stack; push startup value,
580 * and emit signals with one arg */
581 lua_pushboolean(globalconf
.L
, startup
);
582 luaA_object_emit_signal(globalconf
.L
, -2, "manage", 1);
584 lua_pop(globalconf
.L
, 1);
587 /** Send a synthetic configure event to a window.
590 client_send_configure(client_t
*c
)
592 area_t geometry
= c
->geometry
;
593 geometry
.x
+= c
->titlebar
[CLIENT_TITLEBAR_LEFT
].size
;
594 geometry
.y
+= c
->titlebar
[CLIENT_TITLEBAR_TOP
].size
;
595 geometry
.width
-= c
->titlebar
[CLIENT_TITLEBAR_LEFT
].size
;
596 geometry
.width
-= c
->titlebar
[CLIENT_TITLEBAR_RIGHT
].size
;
597 geometry
.height
-= c
->titlebar
[CLIENT_TITLEBAR_TOP
].size
;
598 geometry
.height
-= c
->titlebar
[CLIENT_TITLEBAR_BOTTOM
].size
;
600 xwindow_configure(c
->window
, geometry
, c
->border_width
);
604 client_resize_do(client_t
*c
, area_t geometry
, bool force_notice
)
606 bool send_notice
= force_notice
;
607 bool hide_titlebars
= c
->fullscreen
;
608 screen_t
*new_screen
= screen_getbycoord(geometry
.x
, geometry
.y
);
610 if(c
->geometry
.width
== geometry
.width
611 && c
->geometry
.height
== geometry
.height
)
614 /* Also store geometry including border */
615 area_t old_geometry
= c
->geometry
;
616 c
->geometry
= geometry
;
618 /* Ignore all spurious enter/leave notify events */
619 client_ignore_enterleave_events();
621 /* Configure the client for its new size */
622 area_t real_geometry
= geometry
;
625 real_geometry
.x
= c
->titlebar
[CLIENT_TITLEBAR_LEFT
].size
;
626 real_geometry
.y
= c
->titlebar
[CLIENT_TITLEBAR_TOP
].size
;
627 real_geometry
.width
-= c
->titlebar
[CLIENT_TITLEBAR_LEFT
].size
;
628 real_geometry
.width
-= c
->titlebar
[CLIENT_TITLEBAR_RIGHT
].size
;
629 real_geometry
.height
-= c
->titlebar
[CLIENT_TITLEBAR_TOP
].size
;
630 real_geometry
.height
-= c
->titlebar
[CLIENT_TITLEBAR_BOTTOM
].size
;
633 xcb_configure_window(globalconf
.connection
, c
->frame_window
,
634 XCB_CONFIG_WINDOW_X
| XCB_CONFIG_WINDOW_Y
| XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
,
635 (uint32_t[]) { geometry
.x
, geometry
.y
, geometry
.width
, geometry
.height
});
636 xcb_configure_window(globalconf
.connection
, c
->window
,
637 XCB_CONFIG_WINDOW_X
| XCB_CONFIG_WINDOW_Y
| XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
,
638 (uint32_t[]) { real_geometry
.x
, real_geometry
.y
, real_geometry
.width
, real_geometry
.height
});
641 /* We are moving without changing the size, see ICCCM 4.2.3 */
642 client_send_configure(c
);
644 client_restore_enterleave_events();
646 screen_client_moveto(c
, new_screen
, false);
648 luaA_object_push(globalconf
.L
, c
);
649 luaA_object_emit_signal(globalconf
.L
, -1, "property::geometry", 0);
650 if (old_geometry
.x
!= geometry
.x
)
651 luaA_object_emit_signal(globalconf
.L
, -1, "property::x", 0);
652 if (old_geometry
.y
!= geometry
.y
)
653 luaA_object_emit_signal(globalconf
.L
, -1, "property::y", 0);
654 if (old_geometry
.width
!= geometry
.width
)
655 luaA_object_emit_signal(globalconf
.L
, -1, "property::width", 0);
656 if (old_geometry
.height
!= geometry
.height
)
657 luaA_object_emit_signal(globalconf
.L
, -1, "property::height", 0);
658 lua_pop(globalconf
.L
, 1);
660 /* Update all titlebars */
661 for (client_titlebar_t bar
= CLIENT_TITLEBAR_TOP
; bar
< CLIENT_TITLEBAR_COUNT
; bar
++) {
662 if (c
->titlebar
[bar
].drawable
== NULL
&& c
->titlebar
[bar
].size
== 0)
665 luaA_object_push(globalconf
.L
, c
);
666 drawable_t
*drawable
= titlebar_get_drawable(globalconf
.L
, c
, -1, bar
);
668 /* Get rid of the old state */
669 luaA_object_push_item(globalconf
.L
, -1, drawable
);
670 drawable_set_surface(drawable
, -1, NULL
);
671 if (c
->titlebar
[bar
].pixmap
!= XCB_NONE
)
672 xcb_free_pixmap(globalconf
.connection
, c
->titlebar
[bar
].pixmap
);
673 c
->titlebar
[bar
].pixmap
= XCB_NONE
;
675 /* And get us some new state */
676 area_t area
= titlebar_get_area(c
, bar
);
677 if (c
->titlebar
[bar
].size
!= 0 && !hide_titlebars
)
679 c
->titlebar
[bar
].pixmap
= xcb_generate_id(globalconf
.connection
);
680 xcb_create_pixmap(globalconf
.connection
, globalconf
.default_depth
, c
->titlebar
[bar
].pixmap
,
681 globalconf
.screen
->root
, area
.width
, area
.height
);
682 cairo_surface_t
*surface
= cairo_xcb_surface_create(globalconf
.connection
,
683 c
->titlebar
[bar
].pixmap
, globalconf
.visual
,
684 area
.width
, area
.height
);
685 drawable_set_surface(drawable
, -1, surface
);
688 /* Convert to global coordinates */
689 area
.x
+= geometry
.x
;
690 area
.y
+= geometry
.y
;
692 area
.width
= area
.height
= 0;
693 drawable_set_geometry(drawable
, -1, area
);
695 /* Pop the client and the drawable */
696 lua_pop(globalconf
.L
, 2);
700 /** Resize client window.
701 * The sizes given as parameters are with borders!
702 * \param c Client to resize.
703 * \param geometry New window geometry.
704 * \param hints Use size hints.
705 * \return true if an actual resize occurred.
708 client_resize(client_t
*c
, area_t geometry
)
712 /* offscreen appearance fixes */
713 area
= display_area_get();
715 if(geometry
.x
> area
.width
)
716 geometry
.x
= area
.width
- geometry
.width
;
717 if(geometry
.y
> area
.height
)
718 geometry
.y
= area
.height
- geometry
.height
;
719 if(geometry
.x
+ geometry
.width
< 0)
721 if(geometry
.y
+ geometry
.height
< 0)
724 if(geometry
.width
< c
->titlebar
[CLIENT_TITLEBAR_LEFT
].size
+ c
->titlebar
[CLIENT_TITLEBAR_RIGHT
].size
)
726 if(geometry
.height
< c
->titlebar
[CLIENT_TITLEBAR_TOP
].size
+ c
->titlebar
[CLIENT_TITLEBAR_BOTTOM
].size
)
729 if(geometry
.width
== 0 || geometry
.height
== 0)
732 if(c
->geometry
.x
!= geometry
.x
733 || c
->geometry
.y
!= geometry
.y
734 || c
->geometry
.width
!= geometry
.width
735 || c
->geometry
.height
!= geometry
.height
)
737 client_resize_do(c
, geometry
, false);
745 /** Set a client minimized, or not.
746 * \param L The Lua VM state.
747 * \param cidx The client index.
748 * \param s Set or not the client minimized.
751 client_set_minimized(lua_State
*L
, int cidx
, bool s
)
753 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
755 if(c
->minimized
!= s
)
758 banning_need_update();
760 xwindow_set_state(c
->window
, XCB_ICCCM_WM_STATE_ICONIC
);
762 xwindow_set_state(c
->window
, XCB_ICCCM_WM_STATE_NORMAL
);
763 if(strut_has_value(&c
->strut
))
764 screen_emit_signal(globalconf
.L
, c
->screen
, "property::workarea", 0);
765 luaA_object_emit_signal(L
, cidx
, "property::minimized", 0);
769 /** Set a client hidden, or not.
770 * \param L The Lua VM state.
771 * \param cidx The client index.
772 * \param s Set or not the client hidden.
775 client_set_hidden(lua_State
*L
, int cidx
, bool s
)
777 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
782 banning_need_update();
783 if(strut_has_value(&c
->strut
))
784 screen_emit_signal(globalconf
.L
, c
->screen
, "property::workarea", 0);
785 luaA_object_emit_signal(L
, cidx
, "property::hidden", 0);
789 /** Set a client sticky, or not.
790 * \param L The Lua VM state.
791 * \param cidx The client index.
792 * \param s Set or not the client sticky.
795 client_set_sticky(lua_State
*L
, int cidx
, bool s
)
797 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
802 banning_need_update();
803 luaA_object_emit_signal(L
, cidx
, "property::sticky", 0);
807 /** Set a client fullscreen, or not.
808 * \param L The Lua VM state.
809 * \param cidx The client index.
810 * \param s Set or not the client fullscreen.
813 client_set_fullscreen(lua_State
*L
, int cidx
, bool s
)
815 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
817 if(c
->fullscreen
!= s
)
819 /* become fullscreen! */
822 /* remove any max state */
823 client_set_maximized_horizontal(L
, cidx
, false);
824 client_set_maximized_vertical(L
, cidx
, false);
825 /* You can only be part of one of the special layers. */
826 client_set_below(L
, cidx
, false);
827 client_set_above(L
, cidx
, false);
828 client_set_ontop(L
, cidx
, false);
830 int abs_cidx
= luaA_absindex(L
, cidx
); \
831 lua_pushboolean(L
, s
);
833 luaA_object_emit_signal(L
, abs_cidx
, "request::fullscreen", 1);
834 luaA_object_emit_signal(L
, abs_cidx
, "property::fullscreen", 0);
839 /** Set a client horizontally|vertically maximized.
840 * \param L The Lua VM state.
841 * \param cidx The client index.
842 * \param s The maximized status.
844 #define DO_FUNCTION_CLIENT_MAXIMIZED(type) \
846 client_set_maximized_##type(lua_State *L, int cidx, bool s) \
848 client_t *c = luaA_checkudata(L, cidx, &client_class); \
849 if(c->maximized_##type != s) \
851 int abs_cidx = luaA_absindex(L, cidx); \
853 client_set_fullscreen(L, abs_cidx, false); \
854 lua_pushboolean(L, s); \
855 c->maximized_##type = s; \
856 luaA_object_emit_signal(L, abs_cidx, "request::maximized_" #type, 1); \
857 luaA_object_emit_signal(L, abs_cidx, "property::maximized_" #type, 0); \
861 DO_FUNCTION_CLIENT_MAXIMIZED(vertical
)
862 DO_FUNCTION_CLIENT_MAXIMIZED(horizontal
)
863 #undef DO_FUNCTION_CLIENT_MAXIMIZED
865 /** Set a client above, or not.
866 * \param L The Lua VM state.
867 * \param cidx The client index.
868 * \param s Set or not the client above.
871 client_set_above(lua_State
*L
, int cidx
, bool s
)
873 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
877 /* You can only be part of one of the special layers. */
880 client_set_below(L
, cidx
, false);
881 client_set_ontop(L
, cidx
, false);
882 client_set_fullscreen(L
, cidx
, false);
886 luaA_object_emit_signal(L
, cidx
, "property::above", 0);
890 /** Set a client below, or not.
891 * \param L The Lua VM state.
892 * \param cidx The client index.
893 * \param s Set or not the client below.
896 client_set_below(lua_State
*L
, int cidx
, bool s
)
898 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
902 /* You can only be part of one of the special layers. */
905 client_set_above(L
, cidx
, false);
906 client_set_ontop(L
, cidx
, false);
907 client_set_fullscreen(L
, cidx
, false);
911 luaA_object_emit_signal(L
, cidx
, "property::below", 0);
915 /** Set a client modal, or not.
916 * \param L The Lua VM state.
917 * \param cidx The client index.
918 * \param s Set or not the client modal attribute.
921 client_set_modal(lua_State
*L
, int cidx
, bool s
)
923 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
929 luaA_object_emit_signal(L
, cidx
, "property::modal", 0);
933 /** Set a client ontop, or not.
934 * \param L The Lua VM state.
935 * \param cidx The client index.
936 * \param s Set or not the client ontop attribute.
939 client_set_ontop(lua_State
*L
, int cidx
, bool s
)
941 client_t
*c
= luaA_checkudata(L
, cidx
, &client_class
);
945 /* You can only be part of one of the special layers. */
948 client_set_above(L
, cidx
, false);
949 client_set_below(L
, cidx
, false);
950 client_set_fullscreen(L
, cidx
, false);
954 luaA_object_emit_signal(L
, cidx
, "property::ontop", 0);
958 /** Unban a client and move it back into the viewport.
959 * \param c The client.
962 client_unban(client_t
*c
)
966 xcb_map_window(globalconf
.connection
, c
->frame_window
);
970 /* An unbanned client shouldn't be minimized or hidden */
971 luaA_object_push(globalconf
.L
, c
);
972 client_set_minimized(globalconf
.L
, -1, false);
973 client_set_hidden(globalconf
.L
, -1, false);
974 lua_pop(globalconf
.L
, 1);
978 /** Unmanage a client.
979 * \param c The client.
980 * \param window_valid Is the client's window still valid?
983 client_unmanage(client_t
*c
, bool window_valid
)
985 /* Reset transient_for attributes of widows that maybe referring to us */
986 foreach(_tc
, globalconf
.clients
)
989 if(tc
->transient_for
== c
)
990 tc
->transient_for
= NULL
;
993 if(globalconf
.focus
.client
== c
)
996 /* remove client from global list and everywhere else */
997 foreach(elem
, globalconf
.clients
)
1000 client_array_remove(&globalconf
.clients
, elem
);
1003 stack_client_remove(c
);
1004 for(int i
= 0; i
< globalconf
.tags
.len
; i
++)
1005 untag_client(c
, globalconf
.tags
.tab
[i
]);
1007 luaA_object_push(globalconf
.L
, c
);
1008 luaA_object_emit_signal(globalconf
.L
, -1, "unmanage", 0);
1009 lua_pop(globalconf
.L
, 1);
1011 luaA_class_emit_signal(globalconf
.L
, &client_class
, "list", 0);
1013 if(strut_has_value(&c
->strut
))
1014 screen_emit_signal(globalconf
.L
, c
->screen
, "property::workarea", 0);
1016 /* Get rid of all titlebars */
1017 for (client_titlebar_t bar
= CLIENT_TITLEBAR_TOP
; bar
< CLIENT_TITLEBAR_COUNT
; bar
++) {
1018 if (c
->titlebar
[bar
].drawable
== NULL
)
1021 luaA_object_push(globalconf
.L
, c
);
1022 luaA_object_push_item(globalconf
.L
, -1, c
->titlebar
[bar
].drawable
);
1024 /* Make the drawable unusable */
1025 drawable_set_surface(c
->titlebar
[bar
].drawable
, -1, NULL
);
1026 if (c
->titlebar
[bar
].pixmap
!= XCB_NONE
)
1027 xcb_free_pixmap(globalconf
.connection
, c
->titlebar
[bar
].pixmap
);
1029 /* And forget about it */
1030 luaA_object_unref_item(globalconf
.L
, -2, c
->titlebar
[bar
].drawable
);
1031 c
->titlebar
[bar
].drawable
= NULL
;
1032 lua_pop(globalconf
.L
, 2);
1035 /* Clear our event mask so that we don't receive any events from now on,
1036 * especially not for the following requests. */
1038 xcb_change_window_attributes(globalconf
.connection
,
1041 (const uint32_t []) { 0 });
1042 xcb_change_window_attributes(globalconf
.connection
,
1045 (const uint32_t []) { 0 });
1049 xcb_unmap_window(globalconf
.connection
, c
->window
);
1050 xcb_reparent_window(globalconf
.connection
, c
->window
, globalconf
.screen
->root
,
1051 c
->geometry
.x
, c
->geometry
.y
);
1053 xcb_destroy_window(globalconf
.connection
, c
->frame_window
);
1057 /* Remove this window from the save set since this shouldn't be made visible
1058 * after a restart anymore. */
1059 xcb_change_save_set(globalconf
.connection
, XCB_SET_MODE_DELETE
, c
->window
);
1061 /* Do this last to avoid races with clients. According to ICCCM, clients
1062 * arent allowed to re-use the window until after this. */
1063 xwindow_set_state(c
->window
, XCB_ICCCM_WM_STATE_WITHDRAWN
);
1066 /* set client as invalid */
1067 c
->window
= XCB_NONE
;
1069 luaA_object_unref(globalconf
.L
, c
);
1072 /** Kill a client via a WM_DELETE_WINDOW request or KillClient if not
1074 * \param c The client to kill.
1077 client_kill(client_t
*c
)
1079 if(client_hasproto(c
, WM_DELETE_WINDOW
))
1081 xcb_client_message_event_t ev
;
1083 /* Initialize all of event's fields first */
1086 ev
.response_type
= XCB_CLIENT_MESSAGE
;
1087 ev
.window
= c
->window
;
1089 ev
.data
.data32
[1] = globalconf
.timestamp
;
1090 ev
.type
= WM_PROTOCOLS
;
1091 ev
.data
.data32
[0] = WM_DELETE_WINDOW
;
1093 xcb_send_event(globalconf
.connection
, false, c
->window
,
1094 XCB_EVENT_MASK_NO_EVENT
, (char *) &ev
);
1097 xcb_kill_client(globalconf
.connection
, c
->window
);
1100 /** Get all clients into a table.
1101 * \param L The Lua VM state.
1102 * \return The number of elements pushed on stack.
1104 * \lparam An optional screen number.
1105 * \lreturn A table with all clients.
1108 luaA_client_get(lua_State
*L
)
1112 screen
= luaL_optnumber(L
, 1, 0) - 1;
1117 foreach(c
, globalconf
.clients
)
1119 luaA_object_push(L
, *c
);
1120 lua_rawseti(L
, -2, i
++);
1124 luaA_checkscreen(screen
);
1125 foreach(c
, globalconf
.clients
)
1126 if((*c
)->screen
== &globalconf
.screens
.tab
[screen
])
1128 luaA_object_push(L
, *c
);
1129 lua_rawseti(L
, -2, i
++);
1136 /** Check if a client is visible on its screen.
1137 * \param L The Lua VM state.
1138 * \return The number of elements pushed on stack.
1141 * \lreturn A boolean value, true if the client is visible, false otherwise.
1144 luaA_client_isvisible(lua_State
*L
)
1146 client_t
*c
= luaA_checkudata(L
, 1, &client_class
);
1147 lua_pushboolean(L
, client_isvisible(c
));
1151 /** Set a client icon.
1152 * \param L The Lua VM state.
1153 * \param cidx The client index on the stack.
1154 * \param iidx The image index on the stack.
1157 client_set_icon(client_t
*c
, cairo_surface_t
*s
)
1160 s
= draw_dup_image_surface(s
);
1162 cairo_surface_destroy(c
->icon
);
1165 luaA_object_push(globalconf
.L
, c
);
1166 luaA_object_emit_signal(globalconf
.L
, -1, "property::icon", 0);
1167 lua_pop(globalconf
.L
, 1);
1171 * \param L The Lua VM state.
1177 luaA_client_kill(lua_State
*L
)
1179 client_t
*c
= luaA_checkudata(L
, 1, &client_class
);
1184 /** Swap a client with another one.
1185 * \param L The Lua VM state.
1188 * \lparam A client to swap with.
1191 luaA_client_swap(lua_State
*L
)
1193 client_t
*c
= luaA_checkudata(L
, 1, &client_class
);
1194 client_t
*swap
= luaA_checkudata(L
, 2, &client_class
);
1198 client_t
**ref_c
= NULL
, **ref_swap
= NULL
;
1199 foreach(item
, globalconf
.clients
)
1203 else if(*item
== swap
)
1205 if(ref_c
&& ref_swap
)
1212 luaA_class_emit_signal(globalconf
.L
, &client_class
, "list", 0);
1218 /** Access or set the client tags.
1219 * \param L The Lua VM state.
1220 * \return The number of elements pushed on stack.
1221 * \lparam A table with tags to set, or none to get the current tags table.
1222 * \return The clients tag.
1225 luaA_client_tags(lua_State
*L
)
1227 client_t
*c
= luaA_checkudata(L
, 1, &client_class
);
1230 if(lua_gettop(L
) == 2)
1232 luaA_checktable(L
, 2);
1233 for(int i
= 0; i
< globalconf
.tags
.len
; i
++)
1235 /* Only untag if we aren't going to add this tag again */
1238 while(lua_next(L
, 2))
1240 tag_t
*t
= lua_touserdata(L
, -1);
1241 /* Pop the value from lua_next */
1243 if (t
!= globalconf
.tags
.tab
[i
])
1246 /* Pop the key from lua_next */
1252 untag_client(c
, globalconf
.tags
.tab
[i
]);
1255 while(lua_next(L
, 2))
1261 foreach(tag
, globalconf
.tags
)
1262 if(is_client_tagged(c
, *tag
))
1264 luaA_object_push(L
, *tag
);
1265 lua_rawseti(L
, -2, ++j
);
1271 /** Raise a client on top of others which are on the same layer.
1272 * \param L The Lua VM state.
1277 luaA_client_raise(lua_State
*L
)
1279 client_t
*c
= luaA_checkudata(L
, 1, &client_class
);
1284 /** Lower a client on bottom of others which are on the same layer.
1285 * \param L The Lua VM state.
1290 luaA_client_lower(lua_State
*L
)
1292 client_t
*c
= luaA_checkudata(L
, 1, &client_class
);
1294 stack_client_push(c
);
1296 /* Traverse all transient layers. */
1297 for(client_t
*tc
= c
->transient_for
; tc
; tc
= tc
->transient_for
)
1298 stack_client_push(tc
);
1303 /** Stop managing a client.
1304 * \param L The Lua VM state.
1305 * \return The number of elements pushed on stack.
1310 luaA_client_unmanage(lua_State
*L
)
1312 client_t
*c
= luaA_checkudata(L
, 1, &client_class
);
1313 client_unmanage(c
, true);
1318 titlebar_get_area(client_t
*c
, client_titlebar_t bar
)
1320 area_t result
= c
->geometry
;
1321 result
.x
= result
.y
= 0;
1323 // Let's try some ascii art:
1324 // ---------------------------
1326 // |-------------------------|
1332 // |-------------------------|
1334 // ---------------------------
1337 case CLIENT_TITLEBAR_BOTTOM
:
1338 result
.y
= c
->geometry
.height
- c
->titlebar
[bar
].size
;
1340 case CLIENT_TITLEBAR_TOP
:
1341 result
.height
= c
->titlebar
[bar
].size
;
1343 case CLIENT_TITLEBAR_RIGHT
:
1344 result
.x
= c
->geometry
.width
- c
->titlebar
[bar
].size
;
1346 case CLIENT_TITLEBAR_LEFT
:
1347 result
.y
= c
->titlebar
[CLIENT_TITLEBAR_TOP
].size
;
1348 result
.width
= c
->titlebar
[bar
].size
;
1349 result
.height
-= c
->titlebar
[CLIENT_TITLEBAR_TOP
].size
;
1350 result
.height
-= c
->titlebar
[CLIENT_TITLEBAR_BOTTOM
].size
;
1353 fatal("Unknown titlebar kind %d\n", (int) bar
);
1360 client_get_drawable_offset(client_t
*c
, int *x
, int *y
)
1362 for (client_titlebar_t bar
= CLIENT_TITLEBAR_TOP
; bar
< CLIENT_TITLEBAR_COUNT
; bar
++) {
1363 area_t area
= titlebar_get_area(c
, bar
);
1364 if (AREA_LEFT(area
) > *x
|| AREA_RIGHT(area
) <= *x
)
1366 if (AREA_TOP(area
) > *y
|| AREA_BOTTOM(area
) <= *y
)
1371 return c
->titlebar
[bar
].drawable
;
1378 client_get_drawable(client_t
*c
, int x
, int y
)
1380 return client_get_drawable_offset(c
, &x
, &y
);
1384 client_refresh(client_t
*c
)
1386 for (client_titlebar_t bar
= CLIENT_TITLEBAR_TOP
; bar
< CLIENT_TITLEBAR_COUNT
; bar
++) {
1387 if (c
->titlebar
[bar
].drawable
== NULL
|| c
->titlebar
[bar
].drawable
->surface
== NULL
)
1390 area_t area
= titlebar_get_area(c
, bar
);
1391 cairo_surface_flush(c
->titlebar
[bar
].drawable
->surface
);
1392 xcb_copy_area(globalconf
.connection
, c
->titlebar
[bar
].pixmap
, c
->frame_window
,
1393 globalconf
.gc
, 0, 0, area
.x
, area
.y
, area
.width
, area
.height
);
1398 titlebar_get_drawable(lua_State
*L
, client_t
*c
, int cl_idx
, client_titlebar_t bar
)
1400 if (c
->titlebar
[bar
].drawable
== NULL
)
1402 cl_idx
= luaA_absindex(L
, cl_idx
);
1403 drawable_allocator(L
, (drawable_refresh_callback
*) client_refresh
, c
);
1404 c
->titlebar
[bar
].drawable
= luaA_object_ref_item(L
, cl_idx
, -1);
1407 return c
->titlebar
[bar
].drawable
;
1411 titlebar_resize(client_t
*c
, client_titlebar_t bar
, int size
)
1416 if (size
== c
->titlebar
[bar
].size
)
1419 /* Now resize the client (and titlebars!) suitably (the client without
1420 * titlebars should keep its current size!) */
1421 area_t geometry
= c
->geometry
;
1422 int change
= size
- c
->titlebar
[bar
].size
;
1424 case CLIENT_TITLEBAR_TOP
:
1425 case CLIENT_TITLEBAR_BOTTOM
:
1426 geometry
.height
+= change
;
1428 case CLIENT_TITLEBAR_RIGHT
:
1429 case CLIENT_TITLEBAR_LEFT
:
1430 geometry
.width
+= change
;
1433 fatal("Unknown titlebar kind %d\n", (int) bar
);
1436 c
->titlebar
[bar
].size
= size
;
1437 client_resize_do(c
, geometry
, true);
1440 #define HANDLE_TITLEBAR(name, index) \
1442 luaA_client_titlebar_ ## name(lua_State *L) \
1444 client_t *c = luaA_checkudata(L, 1, &client_class); \
1446 if (lua_gettop(L) == 2) \
1448 if (lua_isnil(L, 2)) \
1449 titlebar_resize(c, index, 0); \
1451 titlebar_resize(c, index, luaL_checknumber(L, 2)); \
1454 luaA_object_push_item(L, 1, titlebar_get_drawable(L, c, 1, index)); \
1455 lua_pushnumber(L, c->titlebar[index].size); \
1458 HANDLE_TITLEBAR(top
, CLIENT_TITLEBAR_TOP
)
1459 HANDLE_TITLEBAR(right
, CLIENT_TITLEBAR_RIGHT
)
1460 HANDLE_TITLEBAR(bottom
, CLIENT_TITLEBAR_BOTTOM
)
1461 HANDLE_TITLEBAR(left
, CLIENT_TITLEBAR_LEFT
)
1463 /** Return client geometry.
1464 * \param L The Lua VM state.
1465 * \return The number of elements pushed on stack.
1467 * \lparam A table with new coordinates, or none.
1468 * \lreturn A table with client coordinates.
1471 luaA_client_geometry(lua_State
*L
)
1473 client_t
*c
= luaA_checkudata(L
, 1, &client_class
);
1475 if(lua_gettop(L
) == 2 && !lua_isnil(L
, 2))
1479 luaA_checktable(L
, 2);
1480 geometry
.x
= luaA_getopt_number(L
, 2, "x", c
->geometry
.x
);
1481 geometry
.y
= luaA_getopt_number(L
, 2, "y", c
->geometry
.y
);
1482 if(client_isfixed(c
))
1484 geometry
.width
= c
->geometry
.width
;
1485 geometry
.height
= c
->geometry
.height
;
1489 geometry
.width
= luaA_getopt_number(L
, 2, "width", c
->geometry
.width
);
1490 geometry
.height
= luaA_getopt_number(L
, 2, "height", c
->geometry
.height
);
1493 client_resize(c
, geometry
);
1496 return luaA_pusharea(L
, c
->geometry
);
1500 luaA_client_set_screen(lua_State
*L
, client_t
*c
)
1502 int screen
= luaL_checknumber(L
, -1) - 1;
1503 luaA_checkscreen(screen
);
1504 screen_client_moveto(c
, &globalconf
.screens
.tab
[screen
], true);
1510 luaA_client_set_hidden(lua_State
*L
, client_t
*c
)
1512 client_set_hidden(L
, -3, luaA_checkboolean(L
, -1));
1517 luaA_client_set_minimized(lua_State
*L
, client_t
*c
)
1519 client_set_minimized(L
, -3, luaA_checkboolean(L
, -1));
1524 luaA_client_set_fullscreen(lua_State
*L
, client_t
*c
)
1526 client_set_fullscreen(L
, -3, luaA_checkboolean(L
, -1));
1531 luaA_client_set_modal(lua_State
*L
, client_t
*c
)
1533 client_set_modal(L
, -3, luaA_checkboolean(L
, -1));
1538 luaA_client_set_maximized_horizontal(lua_State
*L
, client_t
*c
)
1540 client_set_maximized_horizontal(L
, -3, luaA_checkboolean(L
, -1));
1545 luaA_client_set_maximized_vertical(lua_State
*L
, client_t
*c
)
1547 client_set_maximized_vertical(L
, -3, luaA_checkboolean(L
, -1));
1552 luaA_client_set_icon(lua_State
*L
, client_t
*c
)
1554 cairo_surface_t
*surf
= NULL
;
1555 if(!lua_isnil(L
, -1))
1556 surf
= (cairo_surface_t
*)lua_touserdata(L
, -1);
1557 client_set_icon(c
, surf
);
1562 luaA_client_set_sticky(lua_State
*L
, client_t
*c
)
1564 client_set_sticky(L
, -3, luaA_checkboolean(L
, -1));
1569 luaA_client_set_size_hints_honor(lua_State
*L
, client_t
*c
)
1571 c
->size_hints_honor
= luaA_checkboolean(L
, -1);
1572 luaA_object_emit_signal(L
, -3, "property::size_hints_honor", 0);
1577 luaA_client_set_ontop(lua_State
*L
, client_t
*c
)
1579 client_set_ontop(L
, -3, luaA_checkboolean(L
, -1));
1584 luaA_client_set_below(lua_State
*L
, client_t
*c
)
1586 client_set_below(L
, -3, luaA_checkboolean(L
, -1));
1591 luaA_client_set_above(lua_State
*L
, client_t
*c
)
1593 client_set_above(L
, -3, luaA_checkboolean(L
, -1));
1598 luaA_client_set_urgent(lua_State
*L
, client_t
*c
)
1600 client_set_urgent(L
, -3, luaA_checkboolean(L
, -1));
1605 luaA_client_set_skip_taskbar(lua_State
*L
, client_t
*c
)
1607 client_set_skip_taskbar(L
, -3, luaA_checkboolean(L
, -1));
1612 luaA_client_get_name(lua_State
*L
, client_t
*c
)
1614 lua_pushstring(L
, c
->name
? c
->name
: c
->alt_name
);
1619 luaA_client_get_icon_name(lua_State
*L
, client_t
*c
)
1621 lua_pushstring(L
, c
->icon_name
? c
->icon_name
: c
->alt_icon_name
);
1625 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, class, lua_pushstring
)
1626 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, instance
, lua_pushstring
)
1627 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, machine
, lua_pushstring
)
1628 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, role
, lua_pushstring
)
1629 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, transient_for
, luaA_object_push
)
1630 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, skip_taskbar
, lua_pushboolean
)
1631 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, leader_window
, lua_pushnumber
)
1632 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, group_window
, lua_pushnumber
)
1633 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, pid
, lua_pushnumber
)
1634 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, hidden
, lua_pushboolean
)
1635 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, minimized
, lua_pushboolean
)
1636 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, fullscreen
, lua_pushboolean
)
1637 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, modal
, lua_pushboolean
)
1638 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, ontop
, lua_pushboolean
)
1639 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, urgent
, lua_pushboolean
)
1640 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, above
, lua_pushboolean
)
1641 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, below
, lua_pushboolean
)
1642 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, sticky
, lua_pushboolean
)
1643 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, size_hints_honor
, lua_pushboolean
)
1644 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, maximized_horizontal
, lua_pushboolean
)
1645 LUA_OBJECT_EXPORT_PROPERTY(client
, client_t
, maximized_vertical
, lua_pushboolean
)
1648 luaA_client_get_content(lua_State
*L
, client_t
*c
)
1650 xcb_image_t
*ximage
= xcb_image_get(globalconf
.connection
,
1655 ~0, XCB_IMAGE_FORMAT_Z_PIXMAP
);
1656 cairo_surface_t
*surface
= NULL
;
1660 if(ximage
->bpp
>= 24)
1662 uint32_t *data
= p_new(uint32_t, ximage
->width
* ximage
->height
);
1664 for(int y
= 0; y
< ximage
->height
; y
++)
1665 for(int x
= 0; x
< ximage
->width
; x
++)
1667 data
[y
* ximage
->width
+ x
] = xcb_image_get_pixel(ximage
, x
, y
);
1668 data
[y
* ximage
->width
+ x
] |= 0xff000000; /* set alpha to 0xff */
1671 surface
= draw_surface_from_data(ximage
->width
, ximage
->height
, data
);
1674 xcb_image_destroy(ximage
);
1680 /* lua has to make sure to free the ref or we have a leak */
1681 lua_pushlightuserdata(L
, surface
);
1686 luaA_client_get_screen(lua_State
*L
, client_t
*c
)
1690 lua_pushnumber(L
, 1 + screen_array_indexof(&globalconf
.screens
, c
->screen
));
1695 luaA_client_get_icon(lua_State
*L
, client_t
*c
)
1699 /* lua gets its own reference which it will have to destroy */
1700 lua_pushlightuserdata(L
, cairo_surface_reference(c
->icon
));
1705 luaA_client_get_focusable(lua_State
*L
, client_t
*c
)
1709 /* A client can be focused if it doesnt have the "nofocus" hint...*/
1713 /* ...or if it knows the WM_TAKE_FOCUS protocol */
1714 ret
= client_hasproto(c
, WM_TAKE_FOCUS
);
1716 lua_pushboolean(L
, ret
);
1721 luaA_client_get_size_hints(lua_State
*L
, client_t
*c
)
1723 const char *u_or_p
= NULL
;
1725 lua_createtable(L
, 0, 1);
1727 if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_US_POSITION
)
1728 u_or_p
= "user_position";
1729 else if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_P_POSITION
)
1730 u_or_p
= "program_position";
1734 lua_createtable(L
, 0, 2);
1735 lua_pushnumber(L
, c
->size_hints
.x
);
1736 lua_setfield(L
, -2, "x");
1737 lua_pushnumber(L
, c
->size_hints
.y
);
1738 lua_setfield(L
, -2, "y");
1739 lua_setfield(L
, -2, u_or_p
);
1743 if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_US_SIZE
)
1744 u_or_p
= "user_size";
1745 else if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_P_SIZE
)
1746 u_or_p
= "program_size";
1750 lua_createtable(L
, 0, 2);
1751 lua_pushnumber(L
, c
->size_hints
.width
);
1752 lua_setfield(L
, -2, "width");
1753 lua_pushnumber(L
, c
->size_hints
.height
);
1754 lua_setfield(L
, -2, "height");
1755 lua_setfield(L
, -2, u_or_p
);
1758 if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_P_MIN_SIZE
)
1760 lua_pushnumber(L
, c
->size_hints
.min_width
);
1761 lua_setfield(L
, -2, "min_width");
1762 lua_pushnumber(L
, c
->size_hints
.min_height
);
1763 lua_setfield(L
, -2, "min_height");
1766 if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_P_MAX_SIZE
)
1768 lua_pushnumber(L
, c
->size_hints
.max_width
);
1769 lua_setfield(L
, -2, "max_width");
1770 lua_pushnumber(L
, c
->size_hints
.max_height
);
1771 lua_setfield(L
, -2, "max_height");
1774 if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_P_RESIZE_INC
)
1776 lua_pushnumber(L
, c
->size_hints
.width_inc
);
1777 lua_setfield(L
, -2, "width_inc");
1778 lua_pushnumber(L
, c
->size_hints
.height_inc
);
1779 lua_setfield(L
, -2, "height_inc");
1782 if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_P_ASPECT
)
1784 lua_pushnumber(L
, c
->size_hints
.min_aspect_num
);
1785 lua_setfield(L
, -2, "min_aspect_num");
1786 lua_pushnumber(L
, c
->size_hints
.min_aspect_den
);
1787 lua_setfield(L
, -2, "min_aspect_den");
1788 lua_pushnumber(L
, c
->size_hints
.max_aspect_num
);
1789 lua_setfield(L
, -2, "max_aspect_num");
1790 lua_pushnumber(L
, c
->size_hints
.max_aspect_den
);
1791 lua_setfield(L
, -2, "max_aspect_den");
1794 if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_BASE_SIZE
)
1796 lua_pushnumber(L
, c
->size_hints
.base_width
);
1797 lua_setfield(L
, -2, "base_width");
1798 lua_pushnumber(L
, c
->size_hints
.base_height
);
1799 lua_setfield(L
, -2, "base_height");
1802 if(c
->size_hints
.flags
& XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY
)
1804 switch(c
->size_hints
.win_gravity
)
1807 lua_pushliteral(L
, "north_west");
1809 case XCB_GRAVITY_NORTH
:
1810 lua_pushliteral(L
, "north");
1812 case XCB_GRAVITY_NORTH_EAST
:
1813 lua_pushliteral(L
, "north_east");
1815 case XCB_GRAVITY_WEST
:
1816 lua_pushliteral(L
, "west");
1818 case XCB_GRAVITY_CENTER
:
1819 lua_pushliteral(L
, "center");
1821 case XCB_GRAVITY_EAST
:
1822 lua_pushliteral(L
, "east");
1824 case XCB_GRAVITY_SOUTH_WEST
:
1825 lua_pushliteral(L
, "south_west");
1827 case XCB_GRAVITY_SOUTH
:
1828 lua_pushliteral(L
, "south");
1830 case XCB_GRAVITY_SOUTH_EAST
:
1831 lua_pushliteral(L
, "south_east");
1833 case XCB_GRAVITY_STATIC
:
1834 lua_pushliteral(L
, "static");
1837 lua_setfield(L
, -2, "win_gravity");
1843 /** Get or set keys bindings for a client.
1844 * \param L The Lua VM state.
1845 * \return The number of element pushed on stack.
1848 * \lparam An array of key bindings objects, or nothing.
1849 * \return The array of key bindings objects of this client.
1852 luaA_client_keys(lua_State
*L
)
1854 client_t
*c
= luaA_checkudata(L
, 1, &client_class
);
1855 key_array_t
*keys
= &c
->keys
;
1857 if(lua_gettop(L
) == 2)
1859 luaA_key_array_set(L
, 1, 2, keys
);
1860 luaA_object_emit_signal(L
, 1, "property::keys", 0);
1861 xcb_ungrab_key(globalconf
.connection
, XCB_GRAB_ANY
, c
->frame_window
, XCB_BUTTON_MASK_ANY
);
1862 xwindow_grabkeys(c
->frame_window
, keys
);
1865 return luaA_key_array_get(L
, 1, keys
);
1869 * \param L The Lua VM state.
1870 * \return The number of pushed elements.
1873 luaA_client_module_index(lua_State
*L
)
1875 const char *buf
= luaL_checkstring(L
, 2);
1877 if (A_STREQ(buf
, "focus"))
1878 return luaA_object_push(globalconf
.L
, globalconf
.focus
.client
);
1882 /* Client module new index.
1883 * \param L The Lua VM state.
1884 * \return The number of pushed elements.
1887 luaA_client_module_newindex(lua_State
*L
)
1889 const char *buf
= luaL_checkstring(L
, 2);
1892 if (A_STREQ(buf
, "focus"))
1894 c
= luaA_checkudata(L
, 3, &client_class
);
1902 client_checker(client_t
*c
)
1904 return c
->window
!= XCB_NONE
;
1908 client_class_setup(lua_State
*L
)
1910 static const struct luaL_Reg client_methods
[] =
1912 LUA_CLASS_METHODS(client
)
1913 { "get", luaA_client_get
},
1914 { "__index", luaA_client_module_index
},
1915 { "__newindex", luaA_client_module_newindex
},
1919 static const struct luaL_Reg client_meta
[] =
1921 LUA_OBJECT_META(client
)
1923 { "keys", luaA_client_keys
},
1924 { "isvisible", luaA_client_isvisible
},
1925 { "geometry", luaA_client_geometry
},
1926 { "tags", luaA_client_tags
},
1927 { "kill", luaA_client_kill
},
1928 { "swap", luaA_client_swap
},
1929 { "raise", luaA_client_raise
},
1930 { "lower", luaA_client_lower
},
1931 { "unmanage", luaA_client_unmanage
},
1932 { "titlebar_top", luaA_client_titlebar_top
},
1933 { "titlebar_right", luaA_client_titlebar_right
},
1934 { "titlebar_bottom", luaA_client_titlebar_bottom
},
1935 { "titlebar_left", luaA_client_titlebar_left
},
1939 luaA_class_setup(L
, &client_class
, "client", &window_class
,
1940 (lua_class_allocator_t
) client_new
,
1941 (lua_class_collector_t
) client_wipe
,
1942 (lua_class_checker_t
) client_checker
,
1943 luaA_class_index_miss_property
, luaA_class_newindex_miss_property
,
1944 client_methods
, client_meta
);
1945 luaA_class_add_property(&client_class
, "name",
1947 (lua_class_propfunc_t
) luaA_client_get_name
,
1949 luaA_class_add_property(&client_class
, "transient_for",
1951 (lua_class_propfunc_t
) luaA_client_get_transient_for
,
1953 luaA_class_add_property(&client_class
, "skip_taskbar",
1954 (lua_class_propfunc_t
) luaA_client_set_skip_taskbar
,
1955 (lua_class_propfunc_t
) luaA_client_get_skip_taskbar
,
1956 (lua_class_propfunc_t
) luaA_client_set_skip_taskbar
);
1957 luaA_class_add_property(&client_class
, "content",
1959 (lua_class_propfunc_t
) luaA_client_get_content
,
1961 luaA_class_add_property(&client_class
, "type",
1963 (lua_class_propfunc_t
) luaA_window_get_type
,
1965 luaA_class_add_property(&client_class
, "class",
1967 (lua_class_propfunc_t
) luaA_client_get_class
,
1969 luaA_class_add_property(&client_class
, "instance",
1971 (lua_class_propfunc_t
) luaA_client_get_instance
,
1973 luaA_class_add_property(&client_class
, "role",
1975 (lua_class_propfunc_t
) luaA_client_get_role
,
1977 luaA_class_add_property(&client_class
, "pid",
1979 (lua_class_propfunc_t
) luaA_client_get_pid
,
1981 luaA_class_add_property(&client_class
, "leader_window",
1983 (lua_class_propfunc_t
) luaA_client_get_leader_window
,
1985 luaA_class_add_property(&client_class
, "machine",
1987 (lua_class_propfunc_t
) luaA_client_get_machine
,
1989 luaA_class_add_property(&client_class
, "icon_name",
1991 (lua_class_propfunc_t
) luaA_client_get_icon_name
,
1993 luaA_class_add_property(&client_class
, "screen",
1995 (lua_class_propfunc_t
) luaA_client_get_screen
,
1996 (lua_class_propfunc_t
) luaA_client_set_screen
);
1997 luaA_class_add_property(&client_class
, "hidden",
1998 (lua_class_propfunc_t
) luaA_client_set_hidden
,
1999 (lua_class_propfunc_t
) luaA_client_get_hidden
,
2000 (lua_class_propfunc_t
) luaA_client_set_hidden
);
2001 luaA_class_add_property(&client_class
, "minimized",
2002 (lua_class_propfunc_t
) luaA_client_set_minimized
,
2003 (lua_class_propfunc_t
) luaA_client_get_minimized
,
2004 (lua_class_propfunc_t
) luaA_client_set_minimized
);
2005 luaA_class_add_property(&client_class
, "fullscreen",
2006 (lua_class_propfunc_t
) luaA_client_set_fullscreen
,
2007 (lua_class_propfunc_t
) luaA_client_get_fullscreen
,
2008 (lua_class_propfunc_t
) luaA_client_set_fullscreen
);
2009 luaA_class_add_property(&client_class
, "modal",
2010 (lua_class_propfunc_t
) luaA_client_set_modal
,
2011 (lua_class_propfunc_t
) luaA_client_get_modal
,
2012 (lua_class_propfunc_t
) luaA_client_set_modal
);
2013 luaA_class_add_property(&client_class
, "group_window",
2015 (lua_class_propfunc_t
) luaA_client_get_group_window
,
2017 luaA_class_add_property(&client_class
, "maximized_horizontal",
2018 (lua_class_propfunc_t
) luaA_client_set_maximized_horizontal
,
2019 (lua_class_propfunc_t
) luaA_client_get_maximized_horizontal
,
2020 (lua_class_propfunc_t
) luaA_client_set_maximized_horizontal
);
2021 luaA_class_add_property(&client_class
, "maximized_vertical",
2022 (lua_class_propfunc_t
) luaA_client_set_maximized_vertical
,
2023 (lua_class_propfunc_t
) luaA_client_get_maximized_vertical
,
2024 (lua_class_propfunc_t
) luaA_client_set_maximized_vertical
);
2025 luaA_class_add_property(&client_class
, "icon",
2026 (lua_class_propfunc_t
) luaA_client_set_icon
,
2027 (lua_class_propfunc_t
) luaA_client_get_icon
,
2028 (lua_class_propfunc_t
) luaA_client_set_icon
);
2029 luaA_class_add_property(&client_class
, "ontop",
2030 (lua_class_propfunc_t
) luaA_client_set_ontop
,
2031 (lua_class_propfunc_t
) luaA_client_get_ontop
,
2032 (lua_class_propfunc_t
) luaA_client_set_ontop
);
2033 luaA_class_add_property(&client_class
, "above",
2034 (lua_class_propfunc_t
) luaA_client_set_above
,
2035 (lua_class_propfunc_t
) luaA_client_get_above
,
2036 (lua_class_propfunc_t
) luaA_client_set_above
);
2037 luaA_class_add_property(&client_class
, "below",
2038 (lua_class_propfunc_t
) luaA_client_set_below
,
2039 (lua_class_propfunc_t
) luaA_client_get_below
,
2040 (lua_class_propfunc_t
) luaA_client_set_below
);
2041 luaA_class_add_property(&client_class
, "sticky",
2042 (lua_class_propfunc_t
) luaA_client_set_sticky
,
2043 (lua_class_propfunc_t
) luaA_client_get_sticky
,
2044 (lua_class_propfunc_t
) luaA_client_set_sticky
);
2045 luaA_class_add_property(&client_class
, "size_hints_honor",
2046 (lua_class_propfunc_t
) luaA_client_set_size_hints_honor
,
2047 (lua_class_propfunc_t
) luaA_client_get_size_hints_honor
,
2048 (lua_class_propfunc_t
) luaA_client_set_size_hints_honor
);
2049 luaA_class_add_property(&client_class
, "urgent",
2050 (lua_class_propfunc_t
) luaA_client_set_urgent
,
2051 (lua_class_propfunc_t
) luaA_client_get_urgent
,
2052 (lua_class_propfunc_t
) luaA_client_set_urgent
);
2053 luaA_class_add_property(&client_class
, "size_hints",
2055 (lua_class_propfunc_t
) luaA_client_get_size_hints
,
2057 luaA_class_add_property(&client_class
, "focusable",
2059 (lua_class_propfunc_t
) luaA_client_get_focusable
,
2062 signal_add(&client_class
.signals
, "focus");
2063 signal_add(&client_class
.signals
, "list");
2064 signal_add(&client_class
.signals
, "manage");
2065 signal_add(&client_class
.signals
, "button::press");
2066 signal_add(&client_class
.signals
, "button::release");
2067 signal_add(&client_class
.signals
, "mouse::enter");
2068 signal_add(&client_class
.signals
, "mouse::leave");
2069 signal_add(&client_class
.signals
, "mouse::move");
2070 signal_add(&client_class
.signals
, "property::above");
2071 signal_add(&client_class
.signals
, "property::below");
2072 signal_add(&client_class
.signals
, "property::class");
2073 signal_add(&client_class
.signals
, "property::fullscreen");
2074 signal_add(&client_class
.signals
, "property::geometry");
2075 signal_add(&client_class
.signals
, "property::group_window");
2076 signal_add(&client_class
.signals
, "property::height");
2077 signal_add(&client_class
.signals
, "property::hidden");
2078 signal_add(&client_class
.signals
, "property::icon");
2079 signal_add(&client_class
.signals
, "property::icon_name");
2080 signal_add(&client_class
.signals
, "property::instance");
2081 signal_add(&client_class
.signals
, "property::keys");
2082 signal_add(&client_class
.signals
, "property::machine");
2083 signal_add(&client_class
.signals
, "property::maximized_horizontal");
2084 signal_add(&client_class
.signals
, "property::maximized_vertical");
2085 signal_add(&client_class
.signals
, "property::minimized");
2086 signal_add(&client_class
.signals
, "property::modal");
2087 signal_add(&client_class
.signals
, "property::name");
2088 signal_add(&client_class
.signals
, "property::ontop");
2089 signal_add(&client_class
.signals
, "property::pid");
2090 signal_add(&client_class
.signals
, "property::role");
2091 signal_add(&client_class
.signals
, "property::screen");
2092 signal_add(&client_class
.signals
, "property::size_hints_honor");
2093 signal_add(&client_class
.signals
, "property::skip_taskbar");
2094 signal_add(&client_class
.signals
, "property::sticky");
2095 signal_add(&client_class
.signals
, "property::struts");
2096 signal_add(&client_class
.signals
, "property::transient_for");
2097 signal_add(&client_class
.signals
, "property::type");
2098 signal_add(&client_class
.signals
, "property::urgent");
2099 signal_add(&client_class
.signals
, "property::width");
2100 signal_add(&client_class
.signals
, "property::window");
2101 signal_add(&client_class
.signals
, "property::x");
2102 signal_add(&client_class
.signals
, "property::y");
2103 signal_add(&client_class
.signals
, "request::fullscreen");
2104 signal_add(&client_class
.signals
, "request::maximized_horizontal");
2105 signal_add(&client_class
.signals
, "request::maximized_vertical");
2106 signal_add(&client_class
.signals
, "tagged");
2107 signal_add(&client_class
.signals
, "unfocus");
2108 signal_add(&client_class
.signals
, "unmanage");
2109 signal_add(&client_class
.signals
, "untagged");
2112 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80