2016-08-20 18:59:01 +02:00
|
|
|
/*
|
2007-10-03 17:26:14 +02:00
|
|
|
* client.c - client management
|
|
|
|
*
|
2009-04-11 00:27:06 +02:00
|
|
|
* Copyright © 2007-2009 Julien Danjou <julien@danjou.info>
|
2007-10-03 17:26:14 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*
|
2007-09-12 14:29:51 +02:00
|
|
|
*/
|
2007-09-05 20:15:00 +02:00
|
|
|
|
2016-03-31 10:10:52 +02:00
|
|
|
/** A process window.
|
|
|
|
*
|
2016-11-16 11:14:19 +01:00
|
|
|
* Clients are the name used by Awesome (and X11) to refer to a window.
|
|
|
|
*
|
|
|
|
* A program can have multiple clients (e.g. for dialogs) or none at all (e.g.
|
|
|
|
* command line applications).
|
|
|
|
* Clients are usually grouped by classes.
|
|
|
|
* A class is the name used by X11 to help the window manager distinguish
|
|
|
|
* between windows and write rules for them. A client's behavior is also
|
|
|
|
* defined by its `type` and `size_hints` properties.
|
|
|
|
* See the `xprop` command line application to query properties for a client.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* ![Client geometry](../images/client_geo.svg)
|
|
|
|
*
|
2016-11-16 11:14:19 +01:00
|
|
|
* The client's `:geometry()` function returns a table with *x*, *y*, *width*
|
|
|
|
* and *height*. The area returned **excludes the border width**.
|
|
|
|
* All clients also have a `shape_bounding` and `shape_clip` used to "crop" the
|
|
|
|
* client's content.
|
|
|
|
* Finally, each clients can have titlebars (see `awful.titlebar`).
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2016-11-16 11:14:19 +01:00
|
|
|
* Additionally to the classes described here, one can also use signals as
|
2015-02-27 00:24:23 +01:00
|
|
|
* described in @{signals} and X properties as described in @{xproperties}.
|
|
|
|
*
|
|
|
|
* Some signal names are starting with a dot. These dots are artefacts from
|
|
|
|
* the documentation generation, you get the real signal name by
|
|
|
|
* removing the starting dot.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* Accessing client objects can be done in multiple ways depending on the
|
2016-11-16 11:14:19 +01:00
|
|
|
* context.
|
|
|
|
* To get the currently focused client:
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* local c = client.focus
|
|
|
|
* if c then
|
|
|
|
* -- do something
|
|
|
|
* end
|
|
|
|
*
|
2016-11-16 11:14:19 +01:00
|
|
|
* To get a list of all clients, use `client:get`:
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* for _, c in ipairs(client.get()) do
|
|
|
|
* -- do something
|
|
|
|
* end
|
|
|
|
*
|
2016-11-16 11:14:19 +01:00
|
|
|
* To execute a callback when a new client is added, use the `manage` signal:
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2019-11-10 07:12:43 +01:00
|
|
|
* client.connect_signal("request::manage", function(c)
|
2016-03-31 10:10:52 +02:00
|
|
|
* -- do something
|
2016-08-20 18:59:01 +02:00
|
|
|
* end)
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2016-11-16 11:14:19 +01:00
|
|
|
* To be notified when a property of a client changed:
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* client.connect_signal("property::name", function(c)
|
|
|
|
* -- do something
|
2016-08-20 18:59:01 +02:00
|
|
|
* end)
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2016-11-16 11:14:19 +01:00
|
|
|
* To be notified when a property of a specific client `c` changed:
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* c:connect_signal("property::name", function()
|
|
|
|
* -- do something
|
2016-08-20 18:59:01 +02:00
|
|
|
* end)
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2016-11-16 11:14:19 +01:00
|
|
|
* To get all the clients for a screen use either `screen.clients` or
|
|
|
|
* `screen.tiled_clients`.
|
2016-04-07 06:09:20 +02:00
|
|
|
*
|
2019-06-27 08:14:17 +02:00
|
|
|
* @DOC_uml_nav_tables_client_EXAMPLE@
|
|
|
|
*
|
2015-02-27 00:24:23 +01:00
|
|
|
* @author Julien Danjou <julien@danjou.info>
|
|
|
|
* @copyright 2008-2009 Julien Danjou
|
2019-06-06 08:40:00 +02:00
|
|
|
* @coreclassmod client
|
2015-02-27 00:24:23 +01:00
|
|
|
*/
|
|
|
|
|
2014-03-30 20:07:48 +02:00
|
|
|
#include "objects/client.h"
|
|
|
|
#include "common/atoms.h"
|
|
|
|
#include "common/xutil.h"
|
|
|
|
#include "event.h"
|
2007-12-27 18:01:36 +01:00
|
|
|
#include "ewmh.h"
|
2014-03-30 20:07:48 +02:00
|
|
|
#include "objects/drawable.h"
|
|
|
|
#include "objects/screen.h"
|
|
|
|
#include "objects/tag.h"
|
2008-09-16 14:09:56 +02:00
|
|
|
#include "property.h"
|
2009-04-03 16:30:18 +02:00
|
|
|
#include "spawn.h"
|
2014-03-30 20:07:48 +02:00
|
|
|
#include "systray.h"
|
2009-09-17 17:51:06 +02:00
|
|
|
#include "xwindow.h"
|
2014-03-30 20:07:48 +02:00
|
|
|
|
2016-04-18 07:15:15 +02:00
|
|
|
#include "math.h"
|
|
|
|
|
2014-03-30 20:07:48 +02:00
|
|
|
#include <xcb/xcb_atom.h>
|
|
|
|
#include <xcb/shape.h>
|
|
|
|
#include <cairo-xcb.h>
|
2007-09-05 20:15:00 +02:00
|
|
|
|
2020-04-17 19:25:40 +02:00
|
|
|
lua_class_t client_class;
|
|
|
|
|
2015-07-25 00:00:07 +02:00
|
|
|
/** Client class.
|
|
|
|
*
|
2018-10-07 01:40:36 +02:00
|
|
|
* This table allow to add more dynamic properties to the clients. For example,
|
|
|
|
* doing:
|
|
|
|
*
|
|
|
|
* function awful.client.object.set_my_cool_property(c, value)
|
|
|
|
* -- Some logic code
|
|
|
|
* c._my_secret_my_cool_property = value
|
|
|
|
* c:emit_signal("property::my_cool_property)
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* function awful.client.object.get_my_cool_property()
|
|
|
|
* return c._my_secret_my_cool_property
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* Will add a new "my_cool_property" dyanmic property to all client. These
|
|
|
|
* methods will be called when an user does `c.my_cool_property = "something"`
|
|
|
|
* or set them in `awdul.rules`.
|
|
|
|
*
|
|
|
|
* Note that doing this isn't required to set random properties to the client,
|
|
|
|
* it is only useful when setting or getting these properties require code to
|
|
|
|
* executed.
|
|
|
|
*
|
2019-06-27 08:14:17 +02:00
|
|
|
* @table awful.client.object
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
/** AwesomeWM is about to scan for existing clients.
|
|
|
|
*
|
|
|
|
* Connect to this signal when code needs to be executed after screens are
|
|
|
|
* initialized, but before clients are added.
|
|
|
|
*
|
|
|
|
* @signal scanning
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2018-08-06 22:43:01 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** AwesomeWM is done scanning for clients.
|
|
|
|
*
|
|
|
|
* This is emitted before the `startup` signal and after the `scanning` signal.
|
|
|
|
*
|
|
|
|
* @signal scanned
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2018-08-06 22:43:01 +02:00
|
|
|
*/
|
|
|
|
|
2016-06-04 17:39:14 +02:00
|
|
|
/** When a client gains focus.
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal focus
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** Before manage, after unmanage, and when clients swap.
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal list
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** When 2 clients are swapped
|
|
|
|
* @tparam client client The other client
|
|
|
|
* @tparam boolean is_source If self is the source or the destination of the swap
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal swapped
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
2017-08-20 19:37:03 +02:00
|
|
|
/** When a new client appears and gets managed by Awesome.
|
2019-11-10 07:12:43 +01:00
|
|
|
*
|
|
|
|
* This request should be implemented by code which track the client. It isn't
|
|
|
|
* recommended to use this to initialize the client content. This use case is
|
|
|
|
* a better fit for `ruled.client`, which has built-in dependency management.
|
|
|
|
* Using this request to mutate the client state will likely conflict with
|
|
|
|
* `ruled.client`.
|
|
|
|
*
|
|
|
|
* @signal request::manage
|
|
|
|
* @tparam client c The client.
|
|
|
|
* @tparam string context What created the client. It is currently either "new"
|
|
|
|
* or "startup".
|
|
|
|
* @tparam table hints More metadata (currently empty, it exists for compliance
|
|
|
|
* with the other `request::` signals).
|
2019-12-01 07:53:12 +01:00
|
|
|
* @request client border added granted When a new client needs a its initial
|
|
|
|
* border settings.
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2019-11-10 07:12:43 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** When a client is going away.
|
|
|
|
*
|
|
|
|
* Each places which store `client` objects in non-weak table or whose state
|
|
|
|
* depend on the current client should answer this request.
|
|
|
|
*
|
|
|
|
* The contexts are:
|
|
|
|
*
|
|
|
|
* * **user**: `c:unmanage()` was called.
|
|
|
|
* * **reparented**: The window was reparented to another window. It is no
|
|
|
|
* longer a stand alone client.
|
|
|
|
* * **destroyed**: The window was closed.
|
|
|
|
*
|
|
|
|
* @signal request::unmanage
|
|
|
|
* @tparam client c The client.
|
|
|
|
* @tparam string context Why was the client unmanaged.
|
|
|
|
* @tparam table hints More metadata (currently empty, it exists for compliance
|
|
|
|
* with the other `request::` signals).
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2019-11-10 07:12:43 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** Use `request::manage`.
|
|
|
|
* @deprecatedsignal manage
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** Use `request::unmanage`.
|
|
|
|
* @deprecatedsignal unmanage
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
2019-12-31 00:17:23 +01:00
|
|
|
/** When a mouse button is pressed in a client.
|
2016-06-04 17:39:14 +02:00
|
|
|
* @signal button::press
|
|
|
|
*/
|
|
|
|
|
2019-12-31 00:17:23 +01:00
|
|
|
/** When a mouse button is released in a client.
|
|
|
|
*
|
2016-06-04 17:39:14 +02:00
|
|
|
* @signal button::release
|
|
|
|
*/
|
|
|
|
|
2019-12-31 00:17:23 +01:00
|
|
|
/** When the mouse enters a client.
|
|
|
|
*
|
2016-06-04 17:39:14 +02:00
|
|
|
* @signal mouse::enter
|
|
|
|
*/
|
|
|
|
|
2019-12-31 00:17:23 +01:00
|
|
|
/** When the mouse leaves a client.
|
|
|
|
*
|
2016-06-04 17:39:14 +02:00
|
|
|
* @signal mouse::leave
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2019-12-31 00:17:23 +01:00
|
|
|
* When the mouse moves within a client.
|
|
|
|
*
|
2016-06-04 17:39:14 +02:00
|
|
|
* @signal mouse::move
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** When a client should get activated (focused and/or raised).
|
2016-10-04 06:40:34 +02:00
|
|
|
*
|
|
|
|
* **Contexts are:**
|
|
|
|
*
|
2016-11-22 01:15:31 +01:00
|
|
|
* * *ewmh*: When a client asks for focus (from `X11` events).
|
|
|
|
* * *autofocus.check_focus*: When autofocus is enabled (from
|
|
|
|
* `awful.autofocus`).
|
2016-10-04 06:40:34 +02:00
|
|
|
* * *autofocus.check_focus_tag*: When autofocus is enabled
|
2016-11-22 01:15:31 +01:00
|
|
|
* (from `awful.autofocus`).
|
|
|
|
* * *client.jumpto*: When a custom lua extension asks a client to be focused
|
|
|
|
* (from `client.jump_to`).
|
|
|
|
* * *client.swap.global_bydirection*: When client swapping requires a focus
|
|
|
|
* change (from `awful.client.swap.bydirection`).
|
2016-10-04 06:40:34 +02:00
|
|
|
* * *client.movetotag*: When a client is moved to a new tag
|
2016-11-22 01:15:31 +01:00
|
|
|
* (from `client.move_to_tag`).
|
2016-10-04 06:40:34 +02:00
|
|
|
* * *client.movetoscreen*: When the client is moved to a new screen
|
2016-11-22 01:15:31 +01:00
|
|
|
* (from `client.move_to_screen`).
|
2016-10-04 06:40:34 +02:00
|
|
|
* * *client.focus.byidx*: When selecting a client using its index
|
2016-11-22 01:15:31 +01:00
|
|
|
* (from `awful.client.focus.byidx`).
|
2016-10-04 06:40:34 +02:00
|
|
|
* * *client.focus.history.previous*: When cycling through history
|
2016-11-22 01:15:31 +01:00
|
|
|
* (from `awful.client.focus.history.previous`).
|
|
|
|
* * *menu.clients*: When using the builtin client menu
|
|
|
|
* (from `awful.menu.clients`).
|
2019-10-07 03:53:08 +02:00
|
|
|
* * *rules*: When a new client is focused from a rule (from `ruled.client`).
|
2016-11-22 01:15:31 +01:00
|
|
|
* * *screen.focus*: When a screen is focused (from `awful.screen.focus`).
|
2016-06-04 17:39:14 +02:00
|
|
|
*
|
|
|
|
* Default implementation: `awful.ewmh.activate`.
|
2016-10-04 06:40:34 +02:00
|
|
|
*
|
|
|
|
* To implement focus stealing filters see `awful.ewmh.add_activate_filter`.
|
|
|
|
*
|
2016-06-04 17:39:14 +02:00
|
|
|
* @signal request::activate
|
|
|
|
* @tparam string context The context where this signal was used.
|
|
|
|
* @tparam[opt] table hints A table with additional hints:
|
|
|
|
* @tparam[opt=false] boolean hints.raise should the client be raised?
|
2019-12-01 07:53:12 +01:00
|
|
|
* @request client activate ewmh granted When the client asks to be activated.
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
2019-11-17 00:51:15 +01:00
|
|
|
/** When an event could lead to the client being activated.
|
|
|
|
*
|
|
|
|
* This is an layer "on top" of `request::activate` for event which are not
|
|
|
|
* actual request for activation/focus, but where "it would be nice" if the
|
|
|
|
* client got the focus. This includes the focus-follow-mouse model and focusing
|
|
|
|
* previous clients when the selected tag changes.
|
|
|
|
*
|
|
|
|
* This idea is that `request::autoactivate` will emit `request::activate`.
|
|
|
|
* However it is much easier to replace the handler for `request::autoactivate`
|
|
|
|
* than it is to replace the handler for `request::activate`. Thus it provides
|
|
|
|
* a nice abstraction to simplify handling the focus when switching tags or
|
|
|
|
* moving the mouse.
|
|
|
|
*
|
|
|
|
* @signal request::autoactivate
|
|
|
|
* @tparam string context The context where this signal was used.
|
|
|
|
* @tparam[opt] table hints A table with additional hints:
|
|
|
|
* @tparam[opt=false] boolean hints.raise should the client be raised?
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2019-11-17 00:51:15 +01:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-12-01 07:53:12 +01:00
|
|
|
/** When something request a client geometry to be modified.
|
|
|
|
*
|
2016-06-04 17:39:14 +02:00
|
|
|
* @signal request::geometry
|
|
|
|
* @tparam client c The client
|
|
|
|
* @tparam string context Why and what to resize. This is used for the
|
2016-11-16 11:14:19 +01:00
|
|
|
* handlers to know if they are capable of applying the new geometry.
|
2016-06-04 17:39:14 +02:00
|
|
|
* @tparam[opt={}] table Additional arguments. Each context handler may
|
2016-11-16 11:14:19 +01:00
|
|
|
* interpret this differently.
|
2019-12-01 07:53:12 +01:00
|
|
|
* @request client geometry client_maximize_horizontal granted When a client
|
|
|
|
* (programmatically) asks for the maximization to be changed.
|
|
|
|
* @request client geometry client_maximize_vertical granted When a client
|
|
|
|
* (programmatically) asks for the maximization to be changed.
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
2019-12-31 00:17:23 +01:00
|
|
|
/** When the tag requests to be moved to a tag or needs a new tag.
|
|
|
|
*
|
2016-06-04 17:39:14 +02:00
|
|
|
* @signal request::tag
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
2019-12-31 00:17:23 +01:00
|
|
|
/** When the client requests to become urgent.
|
|
|
|
*
|
2016-06-04 17:39:14 +02:00
|
|
|
* @signal request::urgent
|
2019-12-31 00:17:23 +01:00
|
|
|
* @classsignal
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
2019-10-16 08:49:05 +02:00
|
|
|
/** Emitted during startup to gather the default client mousebindings.
|
|
|
|
*
|
|
|
|
* This signals gives a chance to all module to register new client keybindings.
|
|
|
|
* Assuming the client rules does not overwrite them with the `keys` property,
|
|
|
|
* they will be added to all clients.
|
|
|
|
*
|
|
|
|
* @signal request::default_mousebindings
|
|
|
|
* @tparam string context The reason why the signal was sent (currently always
|
|
|
|
* `startup`).
|
2019-12-01 06:41:06 +01:00
|
|
|
* @classsignal
|
2019-10-16 08:49:05 +02:00
|
|
|
*/
|
|
|
|
|
2019-10-16 08:54:15 +02:00
|
|
|
/** Emitted during startup to gather the default client keybindings.
|
|
|
|
*
|
|
|
|
* This signals gives a chance to all module to register new client keybindings.
|
|
|
|
* Assuming the client rules does not overwrite them with the `keys` property,
|
|
|
|
* they will be added to all clients.
|
|
|
|
*
|
|
|
|
* @signal request::default_keybindings
|
|
|
|
* @tparam string context The reason why the signal was sent (currently always
|
|
|
|
* `startup`).
|
2019-12-01 06:41:06 +01:00
|
|
|
* @classsignal
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** Sent once when AwesomeWM starts to add default keybindings.
|
|
|
|
*
|
|
|
|
* Keybindings can be set directly on clients. Actually, older version of
|
|
|
|
* AwesomeWM did that through the rules. However this makes it impossible for
|
|
|
|
* auto-configured modules to add their own keybindings. Using the signals,
|
|
|
|
* `rc.lua` or any module can cleanly manage keybindings.
|
|
|
|
*
|
|
|
|
* @signal request::default_keybindings
|
|
|
|
* @tparam string context The context (currently always "startup").
|
|
|
|
* @classsignal
|
2019-12-01 07:53:12 +01:00
|
|
|
* @request client default_keybindings startup granted Sent when AwesomeWM starts.
|
2019-10-16 08:54:15 +02:00
|
|
|
*/
|
|
|
|
|
2016-06-04 17:39:14 +02:00
|
|
|
/** When a client gets tagged.
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal tagged
|
2019-12-31 07:09:39 +01:00
|
|
|
* @tparam tag t The tag object.
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** When a client gets unfocused.
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal unfocus
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** When a client gets untagged.
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal untagged
|
2019-12-31 07:09:39 +01:00
|
|
|
* @tparam tag t The tag object.
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2019-12-31 00:17:23 +01:00
|
|
|
* Emitted when the client is raised within its layer.
|
|
|
|
*
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal raised
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see below
|
|
|
|
* @see above
|
|
|
|
* @see ontop
|
|
|
|
* @see raise
|
|
|
|
* @see lower
|
|
|
|
* @see lowered
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
2019-12-31 00:17:23 +01:00
|
|
|
/** Emitted when the client is lowered within its layer.
|
|
|
|
*
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal lowered
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see below
|
|
|
|
* @see above
|
|
|
|
* @see ontop
|
|
|
|
* @see raise
|
|
|
|
* @see lower
|
|
|
|
* @see raised
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
2016-03-31 10:10:52 +02:00
|
|
|
/**
|
|
|
|
* The focused `client` or nil (in case there is none).
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* It is not recommanded to set the focused client using
|
|
|
|
* this property. Please use `c:activate{}` instead of
|
|
|
|
* `client.focus = c`. Setting the focus directly bypasses
|
|
|
|
* all the filters and emits fewer signals, which tend to
|
|
|
|
* cause unwanted side effects and make it harder to alter
|
|
|
|
* the code behavior in the future. It usually takes *more*
|
|
|
|
* code to use this rather than `:activate{}` because all
|
|
|
|
* the boilerplate code (such as `c:raise()`) needs to be
|
|
|
|
* added everywhere.
|
|
|
|
*
|
|
|
|
* The main use case for this field is to check *when* there
|
|
|
|
* is an active client.
|
|
|
|
*
|
|
|
|
* if client.focus ~= nil then
|
|
|
|
* -- do something
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* If you want to check if a client is active, use:
|
|
|
|
*
|
|
|
|
* if c.active then
|
|
|
|
* -- do something
|
|
|
|
* end
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @tfield client focus
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see active
|
|
|
|
* @see activate
|
|
|
|
* @see request::activate
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The X window id.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* This is rarely useful, but some DBus protocols will
|
|
|
|
* have this ID in their API, so it can be useful when
|
|
|
|
* writing AwesomeWM bindings for them.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property window
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam integer window
|
|
|
|
* @propemits false false
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client title.
|
|
|
|
*
|
2020-01-19 01:34:10 +01:00
|
|
|
* This is the text which will be shown in `awful.widget.tasklist`
|
|
|
|
* and `awful.titlebar.widget.titlewidget`.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property name
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam string name
|
|
|
|
* @propemits false false
|
|
|
|
* @see awful.titlebar
|
|
|
|
* @see awful.widget.tasklist
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if the client does not want to be in taskbar.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* Some clients, like docked bars or some `sticky` clients
|
|
|
|
* such as wallpaper sensors like Conky have no value in
|
|
|
|
* the `awful.widget.tasklist` and should not be shown there.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* The default value of this property reflects the value of the
|
2020-01-19 01:34:10 +01:00
|
|
|
* `_NET_WM_STATE_SKIP_TASKBAR` X11 protocol xproperty. Clients can modify this
|
|
|
|
* state through this property.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property skip_taskbar
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam[opt=false] boolean skip_taskbar
|
|
|
|
* @propemits false false
|
|
|
|
* @see sticky
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The window type.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* This is useful in, among other places, the `ruled.client` rules to apply
|
|
|
|
* different properties depending on the client types. It is also used
|
|
|
|
* throughout the API to alter the client (and `wibox`) behavior depending on
|
|
|
|
* the `type`. For example, clients with the `dock` type are placed on the side
|
|
|
|
* of the screen while other like `combo` are totally ignored and never
|
|
|
|
* considered `client`s in the first place.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* Valid types are:
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* <table class='widget_list' border=1>
|
|
|
|
* <tr style='font-weight: bold;'>
|
|
|
|
* <th align='center'>Name</th>
|
|
|
|
* <th align='center'>Description</th>
|
|
|
|
* </tr>
|
|
|
|
* <tr><td><b>desktop</b></td><td>The root client, it cannot be moved or resized.</td></tr>
|
|
|
|
* <tr><td><b>dock</b></td><td>A client attached to the side of the screen.</td></tr>
|
|
|
|
* <tr><td><b>splash</b></td><td>A client, usually without titlebar shown when an application starts.</td></tr>
|
|
|
|
* <tr><td><b>dialog</b></td><td>A dialog, see `transient_for`.</td></tr>
|
|
|
|
* <tr><td><b>menu</b></td><td>A context menu.</td></tr>
|
|
|
|
* <tr><td><b>toolbar</b></td><td>A floating toolbar.</td></tr>
|
|
|
|
* <tr><td><b>utility</b></td><td></td></tr>
|
|
|
|
* <tr><td><b>dropdown_menu</b></td><td>A context menu attached to a parent position.</td></tr>
|
|
|
|
* <tr><td><b>popup_menu</b></td><td>A context menu.</td></tr>
|
|
|
|
* <tr><td><b>notification</b></td><td>A notification popup.</td></tr>
|
|
|
|
* <tr><td><b>combo</b></td><td>A combobox list menu.</td></tr>
|
|
|
|
* <tr><td><b>dnd</b></td><td>A drag and drop indicator.</td></tr>
|
|
|
|
* <tr><td><b>normal</b></td><td>A normal application main window.</td></tr>
|
|
|
|
* </table>
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* More information can be found [here](https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472629520)
|
|
|
|
*
|
|
|
|
* @property type
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam string type
|
|
|
|
* @propemits false false
|
|
|
|
* @see ruled.client
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client class.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* A class usually maps to the application name. It is useful in, among other
|
|
|
|
* places, the rules to apply different properties to different clients. It
|
2020-01-19 01:34:10 +01:00
|
|
|
* is also useful, along with `instance`, to implement "windows counter"
|
2019-12-31 00:17:23 +01:00
|
|
|
* used in many popular docks and Alt-Tab like popups.
|
|
|
|
*
|
|
|
|
* To get a client class from the command line, use the command:
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* xprop WM_CLASS
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* The class will be the second string.
|
|
|
|
*
|
|
|
|
* This *should* never change after the client is created, but some
|
|
|
|
* buggy application like the Spotify desktop client are known to
|
|
|
|
* violate the specification and do it anyway. There *is* a signal for
|
|
|
|
* this property, but it should hopefully never be useful. If your
|
2020-01-19 01:34:10 +01:00
|
|
|
* applications change their classes, please report a bug to them
|
|
|
|
* and point to ICCCM §4.1.2.5.
|
2019-12-31 00:17:23 +01:00
|
|
|
* It tends to break `ruled.client` and other AwesomeWM APIs.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property class
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam string class
|
|
|
|
* @propemits false false
|
|
|
|
* @see instance
|
|
|
|
* @see ruled.client
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client instance.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* The `instance` is a subtype of the `class`. Each `class` can have
|
|
|
|
* multiple instances. This is useful in the `ruled.client` rules to
|
|
|
|
* filter clients and apply different properties to them.
|
|
|
|
*
|
|
|
|
* To get a client instance from the command line, use the command:
|
2018-11-21 12:51:19 +01:00
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* xprop WM_CLASS
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* The instance will be the first string.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2020-01-19 01:34:10 +01:00
|
|
|
* This *should* never change after the client is created. There
|
|
|
|
* *is* a signal for * this property, but it should hopefully never
|
|
|
|
* be useful. If your applications change their classes, please
|
|
|
|
* report a bug to them and point to ICCCM §4.1.2.5.
|
|
|
|
* It tends to break `ruled.client` and other AwesomeWM APIs.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property instance
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam string instance
|
|
|
|
* @propemits false false
|
|
|
|
* @see class
|
|
|
|
* @see ruled.client
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client PID, if available.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* This will never change.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property pid
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam integer pid
|
|
|
|
* @propemits false false
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The window role, if available.
|
|
|
|
*
|
|
|
|
* @property role
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam string role
|
|
|
|
* @propemits false false
|
|
|
|
* @see instance
|
|
|
|
* @see class
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The machine client is running on.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* X11 windows can "live" in another computer but shown
|
|
|
|
* in another one. This is called "network transparency"
|
|
|
|
* and is either used directly by allowing remote windows
|
|
|
|
* using the `xhosts` command for using proxies such as
|
|
|
|
* `ssh -X` or `ssh -Y`.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2020-01-19 01:34:10 +01:00
|
|
|
* According to EWMH, this property contains the value
|
|
|
|
* returned by `gethostname()` on the computer that the
|
|
|
|
* client is running on.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property machine
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam string machine
|
|
|
|
* @propemits false false
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client name when iconified.
|
|
|
|
*
|
|
|
|
* @property icon_name
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam string icon_name
|
|
|
|
* @propemits false false
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2017-10-21 01:54:36 +02:00
|
|
|
* The client icon as a surface.
|
|
|
|
*
|
|
|
|
* This property holds the client icon closest to the size configured via
|
|
|
|
* @{awesome.set_preferred_icon_size}.
|
|
|
|
*
|
|
|
|
* It is not a path or an "real" file. Rather, it is already a bitmap surface.
|
|
|
|
*
|
|
|
|
* Typically you would want to use @{awful.widget.clienticon} to get this as a
|
|
|
|
* widget.
|
|
|
|
*
|
|
|
|
* Working with icons is tricky because their surfaces do not use reference
|
|
|
|
* counting correctly. If `gears.surface(c.icon)` is called multiple time on
|
|
|
|
* the same icon, it will cause a double-free error and Awesome will crash. To
|
|
|
|
* get a copy of the icon, you can use:
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* local s = gears.surface(c.icon)
|
|
|
|
* local img = cairo.ImageSurface.create(cairo.Format.ARGB32, s:get_width(), s:get_height())
|
|
|
|
* local cr = cairo.Context(img)
|
|
|
|
* cr:set_source_surface(s, 0, 0)
|
|
|
|
* cr:paint()
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property icon
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam surface icon
|
|
|
|
* @propemits false false
|
2017-10-21 01:54:36 +02:00
|
|
|
* @usage local ib = wibox.widget.imagebox(c.icon)
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see awful.widget.clienticon
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
2017-03-06 16:18:15 +01:00
|
|
|
/**
|
|
|
|
* The available sizes of client icons. This is a table where each entry
|
|
|
|
* contains the width and height of an icon.
|
|
|
|
*
|
|
|
|
* @property icon_sizes
|
|
|
|
* @tparam table sizes
|
2019-12-31 00:17:23 +01:00
|
|
|
* @propemits false false
|
|
|
|
* @see awful.widget.clienticon
|
2017-03-12 11:45:41 +01:00
|
|
|
* @see get_icon
|
2017-03-06 16:18:15 +01:00
|
|
|
*/
|
|
|
|
|
2016-03-31 10:10:52 +02:00
|
|
|
/**
|
|
|
|
* Client screen.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* The `screen` corresponds to the top-left corner of the window.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* Please note that clients can only be on one screen at once. X11
|
|
|
|
* does not natively allow clients to be in multiple locations at
|
|
|
|
* once. Changing the screen directly will affect the tags and may
|
2020-01-19 01:34:10 +01:00
|
|
|
* cause several other changes to the state in order to ensure that
|
|
|
|
* a client's position and its screen are consistent.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2021-03-22 07:54:51 +01:00
|
|
|
* @DOC_sequences_client_screen_EXAMPLE@
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property screen
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam screen screen
|
|
|
|
* @propemits false false
|
|
|
|
* @see move_to_screen
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Define if the client must be hidden, i.e. never mapped,
|
2015-02-27 00:24:23 +01:00
|
|
|
* invisible in taskbar.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property hidden
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean hidden
|
|
|
|
* @propemits false false
|
|
|
|
* @see minimized
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Define it the client must be iconify, i.e. only visible in
|
2015-02-27 00:24:23 +01:00
|
|
|
* taskbar.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property minimized
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean minimized
|
|
|
|
* @propemits false false
|
|
|
|
* @see hidden
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Honor size hints, e.g. respect size ratio.
|
|
|
|
*
|
|
|
|
* For example, a terminal such as `xterm` require the client size to be a
|
|
|
|
* multiple of the character size. Honoring size hints will cause the terminal
|
|
|
|
* window to have a small gap at the bottom.
|
|
|
|
*
|
2019-10-07 03:53:08 +02:00
|
|
|
* This is enabled by default. To disable it by default, see `ruled.client`.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property size_hints_honor
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean size_hints_honor
|
|
|
|
* @propemits false false
|
2016-03-31 10:10:52 +02:00
|
|
|
* @see size_hints
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client border width.
|
2019-12-31 00:17:23 +01:00
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property border_width
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam integer border_width
|
2019-11-11 02:47:43 +01:00
|
|
|
* @propemits false false
|
2021-03-22 07:54:51 +01:00
|
|
|
* @usebeautiful beautiful.border_width_active
|
|
|
|
* @usebeautiful beautiful.border_width_normal
|
|
|
|
* @usebeautiful beautiful.border_width_new
|
|
|
|
* @usebeautiful beautiful.border_width_urgent
|
|
|
|
* @usebeautiful beautiful.border_width_floating
|
|
|
|
* @usebeautiful beautiful.border_width_floating_active
|
|
|
|
* @usebeautiful beautiful.border_width_floating_normal
|
|
|
|
* @usebeautiful beautiful.border_width_floating_new
|
|
|
|
* @usebeautiful beautiful.border_width_floating_urgent
|
|
|
|
* @usebeautiful beautiful.border_width_maximized
|
|
|
|
* @usebeautiful beautiful.border_width_maximized_active
|
|
|
|
* @usebeautiful beautiful.border_width_maximized_normal
|
|
|
|
* @usebeautiful beautiful.border_width_maximized_new
|
|
|
|
* @usebeautiful beautiful.border_width_maximized_urgent
|
|
|
|
* @usebeautiful beautiful.border_width_fullscreen
|
|
|
|
* @usebeautiful beautiful.border_width_fullscreen_active
|
|
|
|
* @usebeautiful beautiful.border_width_fullscreen_normal
|
|
|
|
* @usebeautiful beautiful.border_width_fullscreen_new
|
|
|
|
* @usebeautiful beautiful.border_width_fullscreen_urgent
|
|
|
|
* @usebeautiful beautiful.fullscreen_hide_border Hide the border on fullscreen clients.
|
|
|
|
* @usebeautiful beautiful.maximized_hide_border Hide the border on maximized clients.
|
2019-11-11 02:47:43 +01:00
|
|
|
* @see request::border
|
2021-03-22 07:54:51 +01:00
|
|
|
* @see awful.permissions.update_border
|
|
|
|
* @see border_color
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client border color.
|
|
|
|
*
|
2021-03-22 07:54:51 +01:00
|
|
|
* @DOC_awful_client_border_width_EXAMPLE@
|
|
|
|
*
|
|
|
|
* Note that setting this directly will override and disable all related theme
|
|
|
|
* variables.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property border_color
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam color border_color Any string, gradients and patterns will be converted to a
|
2016-03-31 10:10:52 +02:00
|
|
|
* cairo pattern.
|
2019-11-11 02:47:43 +01:00
|
|
|
* @propemits false false
|
2021-03-22 07:54:51 +01:00
|
|
|
* @usebeautiful beautiful.border_color_marked The fallback color when the
|
|
|
|
* client is marked.
|
|
|
|
* @usebeautiful beautiful.border_color_active The fallback color when the
|
|
|
|
* client is active (focused).
|
|
|
|
* @usebeautiful beautiful.border_color_normal The fallback color when the
|
|
|
|
* client isn't active/floating/new/urgent/maximized/floating/fullscreen.
|
|
|
|
* @usebeautiful beautiful.border_color_new The fallback color when the
|
|
|
|
* client is new.
|
|
|
|
* @usebeautiful beautiful.border_color_urgent The fallback color when the
|
|
|
|
* client is urgent.
|
|
|
|
* @usebeautiful beautiful.border_color_floating The fallback color when the
|
|
|
|
* client is floating and the other colors are not set.
|
|
|
|
* @usebeautiful beautiful.border_color_floating_active The color when the
|
|
|
|
* client is floating and is active (focused).
|
|
|
|
* @usebeautiful beautiful.border_color_floating_normal The color when the
|
|
|
|
* client is floating and not new/urgent/active.
|
|
|
|
* @usebeautiful beautiful.border_color_floating_new
|
|
|
|
* @usebeautiful beautiful.border_color_floating_urgent The color when the
|
|
|
|
* client is floating and urgent.
|
|
|
|
* @usebeautiful beautiful.border_color_maximized
|
|
|
|
* @usebeautiful beautiful.border_color_maximized_active
|
|
|
|
* @usebeautiful beautiful.border_color_maximized_normal
|
|
|
|
* @usebeautiful beautiful.border_color_maximized_new
|
|
|
|
* @usebeautiful beautiful.border_color_maximized_urgent The color when the
|
|
|
|
* client is urbent and maximized.
|
|
|
|
* @usebeautiful beautiful.border_color_fullscreen
|
|
|
|
* @usebeautiful beautiful.border_color_fullscreen_active
|
|
|
|
* @usebeautiful beautiful.border_color_fullscreen_normal
|
|
|
|
* @usebeautiful beautiful.border_color_fullscreen_new
|
|
|
|
* @usebeautiful beautiful.border_color_fullscreen_urgent The color when the
|
|
|
|
* client is fullscreen and urgent.
|
2019-11-11 02:47:43 +01:00
|
|
|
* @see request::border
|
2021-03-22 07:54:51 +01:00
|
|
|
* @see awful.permissions.update_border
|
2019-11-11 02:47:43 +01:00
|
|
|
* @see gears.color
|
2021-03-22 07:54:51 +01:00
|
|
|
* @see border_width
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client urgent state.
|
|
|
|
*
|
|
|
|
* @property urgent
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean urgent
|
2019-11-11 02:47:43 +01:00
|
|
|
* @propemits false false
|
|
|
|
* @see request::border
|
2021-03-22 07:54:51 +01:00
|
|
|
* @usebeautiful beautiful.border_color_urgent The fallback color when the
|
|
|
|
* client is urgent.
|
|
|
|
* @usebeautiful beautiful.border_color_floating_urgent The color when the
|
|
|
|
* client is floating and urgent.
|
|
|
|
* @usebeautiful beautiful.border_color_maximized_urgent The color when the
|
|
|
|
* client is urbent and maximized.
|
|
|
|
* @usebeautiful beautiful.border_color_fullscreen_urgent The color when the
|
|
|
|
* client is fullscreen and urgent.
|
|
|
|
* @usebeautiful beautiful.border_width_urgent The fallback border width when
|
|
|
|
* the client is urgent.
|
|
|
|
* @usebeautiful beautiful.border_width_floating_urgent The border width when
|
|
|
|
* the client is floating and urgent.
|
|
|
|
* @usebeautiful beautiful.border_width_maximized_urgent The border width when
|
|
|
|
* the client is maximized and urgent.
|
|
|
|
* @usebeautiful beautiful.border_width_fullscreen_urgent The border width when
|
|
|
|
* the client is fullscreen and urgent.
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A cairo surface for the client window content.
|
|
|
|
*
|
|
|
|
* To get the screenshot, use:
|
|
|
|
*
|
|
|
|
* gears.surface(c.content)
|
|
|
|
*
|
|
|
|
* To save it, use:
|
|
|
|
*
|
|
|
|
* gears.surface(c.content):write_to_png(path)
|
|
|
|
*
|
2020-01-19 01:34:10 +01:00
|
|
|
* Please note that this only creates a new cairo surface
|
|
|
|
* referring to the client's content. This means that
|
|
|
|
* changes to the client's content may or may not become
|
|
|
|
* visible in the returned surface. If you want to take a
|
|
|
|
* screenshot, a copy of the surface's content needs to
|
|
|
|
* be taken. Note that the content of parts of a window
|
|
|
|
* that are currently not visible are undefined.
|
|
|
|
*
|
|
|
|
* The only way to get an animated client screenshot widget is to poll this
|
|
|
|
* property multiple time per seconds. This is obviously a bad idea.
|
2019-12-31 00:17:23 +01:00
|
|
|
*
|
|
|
|
* This property has no signals when the content changes.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property content
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam surface content
|
|
|
|
* @see gears.surface
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client opacity.
|
|
|
|
*
|
|
|
|
* @property opacity
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam number opacity Between 0 (transparent) to 1 (opaque).
|
2019-11-11 02:47:43 +01:00
|
|
|
* @propemits false false
|
|
|
|
* @see request::border
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client is on top of every other windows.
|
2019-12-31 00:17:23 +01:00
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property ontop
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean ontop
|
|
|
|
* @propemits false false
|
|
|
|
* @see below
|
|
|
|
* @see above
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client is above normal windows.
|
|
|
|
*
|
|
|
|
* @property above
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean above
|
|
|
|
* @propemits false false
|
|
|
|
* @see below
|
|
|
|
* @see ontop
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client is below normal windows.
|
|
|
|
*
|
|
|
|
* @property below
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean below
|
|
|
|
* @propemits false false
|
|
|
|
* @see above
|
|
|
|
* @see ontop
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client is fullscreen or not.
|
|
|
|
*
|
2019-09-30 01:52:13 +02:00
|
|
|
* @DOC_sequences_client_fullscreen_EXAMPLE@
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property fullscreen
|
2019-12-01 07:53:12 +01:00
|
|
|
* @tparam boolean fullscreen
|
|
|
|
* @propemits false false
|
|
|
|
* @request client geometry fullscreen granted When the client must be resized
|
|
|
|
* because it became (or stop being) fullscreen.
|
2020-09-06 02:55:34 +02:00
|
|
|
* @see maximized_horizontal
|
|
|
|
* @see maximized_vertical
|
|
|
|
* @see immobilized_horizontal
|
|
|
|
* @see immobilized_vertical
|
|
|
|
* @see maximized
|
|
|
|
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client is maximized (horizontally and vertically) or not.
|
|
|
|
*
|
2019-09-30 01:52:13 +02:00
|
|
|
* @DOC_sequences_client_maximized_EXAMPLE@
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property maximized
|
2019-12-01 07:53:12 +01:00
|
|
|
* @tparam boolean maximized
|
2019-11-11 02:47:43 +01:00
|
|
|
* @propemits false false
|
2019-12-01 07:53:12 +01:00
|
|
|
* @request client geometry maximized granted When the client must be resized
|
|
|
|
* because it became (or stop being) maximized.
|
2019-11-11 02:47:43 +01:00
|
|
|
* @see request::border
|
2020-09-06 02:55:34 +02:00
|
|
|
* @see maximized_horizontal
|
|
|
|
* @see maximized_vertical
|
|
|
|
* @see fullscreen
|
|
|
|
* @see immobilized_horizontal
|
|
|
|
* @see immobilized_vertical
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client is maximized horizontally or not.
|
|
|
|
*
|
2019-09-30 01:52:13 +02:00
|
|
|
* @DOC_sequences_client_maximized_horizontal_EXAMPLE@
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property maximized_horizontal
|
2019-12-01 07:53:12 +01:00
|
|
|
* @tparam boolean maximized_horizontal
|
|
|
|
* @propemits false false
|
|
|
|
* @request client geometry maximized_horizontal granted When the client must be resized
|
|
|
|
* because it became (or stop being) maximized horizontally.
|
2020-09-06 02:55:34 +02:00
|
|
|
* @see maximized_vertical
|
|
|
|
* @see fullscreen
|
|
|
|
* @see immobilized_horizontal
|
|
|
|
* @see immobilized_vertical
|
|
|
|
* @see maximized
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client is maximized vertically or not.
|
|
|
|
*
|
2019-09-30 01:52:13 +02:00
|
|
|
* @DOC_sequences_client_maximized_vertical_EXAMPLE@
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property maximized_vertical
|
2019-12-01 07:53:12 +01:00
|
|
|
* @tparam boolean maximized_vertical
|
|
|
|
* @propemits false false
|
|
|
|
* @request client geometry maximized_vertical granted When the client must be resized
|
|
|
|
* because it became (or stop being) maximized vertically.
|
2020-09-06 02:55:34 +02:00
|
|
|
* @see maximized_horizontal
|
|
|
|
* @see fullscreen
|
|
|
|
* @see immobilized_horizontal
|
|
|
|
* @see immobilized_vertical
|
|
|
|
* @see maximized
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client the window is transient for.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* A transient window is a client that "belongs" to another
|
2020-01-19 01:34:10 +01:00
|
|
|
* client. If the client is also `modal`, then the parent client
|
2019-12-31 00:17:23 +01:00
|
|
|
* cannot be focused while the child client exists.
|
2020-01-19 01:34:10 +01:00
|
|
|
* This is common for "Save as" dialogs or other dialogs where it
|
|
|
|
* is not possible to modify the content of the "parent" client
|
2019-12-31 00:17:23 +01:00
|
|
|
* while the dialog is open.
|
|
|
|
*
|
2020-01-19 01:34:10 +01:00
|
|
|
* However, `modal` is not a requirement for using the `transient_for`
|
2019-12-31 00:17:23 +01:00
|
|
|
* concept. "Tools" such as popup palette in canvas-and-palettes
|
|
|
|
* applications can belong to each other without being modal.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property transient_for
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam client transient_for
|
2019-12-01 07:53:12 +01:00
|
|
|
* @propemits false false
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see modal
|
|
|
|
* @see type
|
|
|
|
* @see is_transient_for
|
|
|
|
* @see get_transient_for_matching
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Window identification unique to a group of windows.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* This is the ID of the group window, not a client object.
|
2020-01-19 01:34:10 +01:00
|
|
|
* The group window is most likely not a visible client, but
|
|
|
|
* only an invisible and internal window.
|
2019-12-31 00:17:23 +01:00
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property group_window
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam integer group_window
|
2019-12-01 07:53:12 +01:00
|
|
|
* @propemits false false
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see leader_window
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Identification unique to windows spawned by the same command.
|
2019-12-31 00:17:23 +01:00
|
|
|
*
|
|
|
|
* This is the ID of the group window, not a client object.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property leader_window
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam number leader_window
|
|
|
|
* @propemits false false
|
|
|
|
* @see transient_for
|
|
|
|
* @see modal
|
|
|
|
* @see group_window
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A table with size hints of the client.
|
|
|
|
*
|
2020-01-19 01:34:10 +01:00
|
|
|
* For details on the meaning of the fields, refer to ICCCM § 4.1.2.3
|
|
|
|
* `WM_NORMAL_HINTS`.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* Please note that most fields are optional and may or may not be set.
|
|
|
|
*
|
|
|
|
* When the client is tiled, the `size_hints` usually get in the way and
|
|
|
|
* cause the layouts to behave incorrectly. To mitigate this, it is often
|
|
|
|
* advised to set `size_hints_honor` to `false` in the `ruled.client` rules.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property size_hints
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam[opt] table|nil hints The hints.
|
|
|
|
* @tparam[opt] table|nil hints.user_position A table with `x` and `y` keys. It
|
|
|
|
* contains the preferred position of the client. This is set when the
|
|
|
|
* position has been modified by the user. See `program_position`.
|
|
|
|
* @tparam[opt] table|nil hints.program_position A table with `x` and `y` keys. It
|
|
|
|
* contains the preferred position of the client. This is set when the
|
|
|
|
* application itself requests a specific position. See `user_position`.
|
|
|
|
* @tparam[opt] table|nil hints.user_size A table with `width` and `height`. This
|
|
|
|
* contains the client preferred size when it has previously been set by
|
|
|
|
* the user. See `program_size` for the equivalent when the applications
|
|
|
|
* itself wants to specify its preferred size.
|
|
|
|
* @tparam[opt] table|nil hints.program_size A table with `width` and `height`. This
|
|
|
|
* contains the client preferred size as specified by the application.
|
|
|
|
* @tparam[opt] integer|nil hints.max_width The maximum width (in pixels).
|
|
|
|
* @tparam[opt] integer|nil hints.max_height The maximum height (in pixels).
|
|
|
|
* @tparam[opt] integer|nil hints.min_width The minimum width (in pixels).
|
|
|
|
* @tparam[opt] integer|nil hints.min_height The minimum height (in pixels).
|
|
|
|
* @tparam[opt] integer|nil hints.width_inc The number of pixels by which the
|
|
|
|
* client width may be increased or decreased. For example, for terminals,
|
|
|
|
* the size has to be proportional with the monospace font size.
|
|
|
|
* @tparam[opt] integer|nil hints.height_inc The number of pixels by which the
|
|
|
|
* client height may be increased or decreased. For example, for terminals,
|
|
|
|
* the size has to be proportional with the monospace font size.
|
|
|
|
* @tparam[opt] string|nil hints.win_gravity The client `gravity` defines the corder
|
|
|
|
* from which the size is computed. For most clients, it is `north_west`, which
|
|
|
|
* corresponds to the top-left of the window. This will affect how the client
|
|
|
|
* is resized and other size related operations.
|
|
|
|
* @tparam[opt] integer|nil hints.min_aspect_num
|
|
|
|
* @tparam[opt] integer|nil hints.min_aspect_den
|
|
|
|
* @tparam[opt] integer|nil hints.max_aspect_num
|
|
|
|
* @tparam[opt] integer|nil hints.max_aspect_den
|
2020-01-19 01:34:10 +01:00
|
|
|
* @tparam[opt] integer|nil hints.base_width
|
|
|
|
* @tparam[opt] integer|nil hints.base_height
|
2019-12-01 07:53:12 +01:00
|
|
|
* @propemits false false
|
2016-03-31 10:10:52 +02:00
|
|
|
* @see size_hints_honor
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see geometry
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
2018-08-03 19:19:43 +02:00
|
|
|
/**
|
|
|
|
* The motif WM hints of the client.
|
|
|
|
*
|
|
|
|
* This is nil if the client has no motif hints. Otherwise, this is a table that
|
|
|
|
* contains the present properties. Note that awesome provides these properties
|
|
|
|
* as-is and does not interpret them for you. For example, if the function table
|
|
|
|
* only has "resize" set to true, this means that the window requests to be only
|
|
|
|
* resizable, but asks for the other functions not to be able. If however both
|
|
|
|
* "resize" and "all" are set, this means that all but the resize function
|
|
|
|
* should be enabled.
|
|
|
|
*
|
|
|
|
* @property motif_wm_hints
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam table hints The hints.
|
|
|
|
* @tparam[opt] boolean hints.functions.all
|
|
|
|
* @tparam[opt] boolean hints.functions.resize
|
|
|
|
* @tparam[opt] boolean hints.functions.move
|
|
|
|
* @tparam[opt] boolean hints.functions.minimize
|
|
|
|
* @tparam[opt] boolean hints.functions.maximize
|
|
|
|
* @tparam[opt] boolean hints.functions.close
|
|
|
|
* @tparam[opt] boolean hints.decorations.all
|
|
|
|
* @tparam[opt] boolean hints.decorations.border
|
|
|
|
* @tparam[opt] boolean hints.decorations.resizeh
|
|
|
|
* @tparam[opt] boolean hints.decorations.title
|
|
|
|
* @tparam[opt] boolean hints.decorations.menu
|
|
|
|
* @tparam[opt] boolean hints.decorations.minimize
|
|
|
|
* @tparam[opt] boolean hints.decorations.maximize
|
|
|
|
* @tparam[opt] string hints.input_mode This is either `modeless`,
|
|
|
|
* `primary_application_modal`, `system_modal`,
|
|
|
|
* `full_application_modal` or `unknown`.
|
|
|
|
* @tparam[opt] boolean hints.status.tearoff_window
|
2019-12-01 07:53:12 +01:00
|
|
|
* @propemits false false
|
2018-08-03 19:19:43 +02:00
|
|
|
*/
|
|
|
|
|
2016-03-31 10:10:52 +02:00
|
|
|
/**
|
|
|
|
* Set the client sticky, i.e. available on all tags.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* Please note that AwesomeWM implements `sticky` clients
|
|
|
|
* per screens rather than globally like some other
|
|
|
|
* implementations.
|
|
|
|
*
|
2020-09-06 02:55:34 +02:00
|
|
|
* @DOC_sequences_client_sticky_EXAMPLE@
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property sticky
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean sticky
|
2019-12-01 07:53:12 +01:00
|
|
|
* @propemits false false
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see skip_taskbar
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicate if the client is modal.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* A transient window is a client that "belongs" to another
|
|
|
|
* client. If the client is also `modal`, then it always has
|
|
|
|
* to be on top of the other window *and* the parent client
|
|
|
|
* cannot be focused while the child client exists.
|
|
|
|
* This is common for "Save as" dialogs or other dialogs where
|
2020-01-19 01:34:10 +01:00
|
|
|
* is not possible to modify the content of the "parent" client
|
2019-12-31 00:17:23 +01:00
|
|
|
* while the dialog is open.
|
|
|
|
*
|
2020-01-19 01:34:10 +01:00
|
|
|
* However, `modal` is not a requirement for using the `transient_for`
|
2019-12-31 00:17:23 +01:00
|
|
|
* concept. "Tools" such as popup palette in canvas-and-palettes
|
|
|
|
* applications can belong to each other without being modal.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property modal
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean modal
|
2019-12-01 07:53:12 +01:00
|
|
|
* @propemits false false
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see transient_for
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if the client can receive the input focus.
|
|
|
|
*
|
2020-01-19 01:34:10 +01:00
|
|
|
* The client will not get focused even when the user
|
2019-12-31 00:17:23 +01:00
|
|
|
* click on it.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property focusable
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean focusable
|
2019-12-01 07:53:12 +01:00
|
|
|
* @propemits false false
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see shape_input
|
|
|
|
* @see client.focus
|
|
|
|
* @see active
|
|
|
|
* @see activate
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client's bounding shape as set by awesome as a (native) cairo surface.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* The bounding shape is the outer shape of the client. It is outside of the
|
|
|
|
* border.
|
|
|
|
*
|
|
|
|
* Do not use this directly unless you want total control over the shape (such
|
|
|
|
* as shape with holes). Even then, it is usually recommended to use transparency
|
2020-01-19 01:34:10 +01:00
|
|
|
* in the titlebars and a compositing manager. For the vast majority of use
|
2019-12-31 00:17:23 +01:00
|
|
|
* cases, use the `shape` property.
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property shape_bounding
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam surface shape_bounding
|
2019-12-01 07:53:12 +01:00
|
|
|
* @propemits false false
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see shape
|
|
|
|
* @see gears.surface.apply_shape_bounding
|
|
|
|
* @see gears.shape
|
|
|
|
* @see shape_clip
|
|
|
|
* @see shape_input
|
|
|
|
* @see client_shape_bounding
|
|
|
|
* @see client_shape_clip
|
|
|
|
* @see gears.surface
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client's clip shape as set by awesome as a (native) cairo surface.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* The shape_clip is the shape of the client *content*. It is *inside* the
|
|
|
|
* border.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property shape_clip
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam surface shape_clip
|
|
|
|
* @propemits false false
|
|
|
|
* @see shape_bounding
|
|
|
|
* @see shape_input
|
|
|
|
* @see shape
|
|
|
|
* @see gears.surface.apply_shape_bounding
|
|
|
|
* @see gears.shape
|
|
|
|
* @see client_shape_bounding
|
|
|
|
* @see client_shape_clip
|
|
|
|
* @see gears.surface
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
2017-01-26 11:12:41 +01:00
|
|
|
/**
|
|
|
|
* The client's input shape as set by awesome as a (native) cairo surface.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* The input shape is the shape where mouse input will be passed to the
|
|
|
|
* client rather than propagated below it.
|
2017-01-26 11:12:41 +01:00
|
|
|
*
|
|
|
|
* @property shape_input
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam surface shape_input
|
|
|
|
* @propemits false false
|
|
|
|
* @see shape_bounding
|
|
|
|
* @see shape_clip
|
|
|
|
* @see shape
|
|
|
|
* @see gears.surface.apply_shape_bounding
|
|
|
|
* @see gears.shape
|
|
|
|
* @see client_shape_bounding
|
|
|
|
* @see client_shape_clip
|
|
|
|
* @see gears.surface
|
2017-01-26 11:12:41 +01:00
|
|
|
*/
|
|
|
|
|
2016-03-31 10:10:52 +02:00
|
|
|
/**
|
|
|
|
* The client's bounding shape as set by the program as a (native) cairo surface.
|
|
|
|
*
|
2017-10-31 15:25:21 +01:00
|
|
|
* @property client_shape_bounding
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam surface client_shape_bounding
|
|
|
|
* @propemits false false
|
|
|
|
* @see shape_bounding
|
|
|
|
* @see shape_clip
|
|
|
|
* @see shape_input
|
|
|
|
* @see shape
|
|
|
|
* @see gears.surface.apply_shape_bounding
|
|
|
|
* @see gears.shape
|
|
|
|
* @see client_shape_clip
|
|
|
|
* @see gears.surface
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The client's clip shape as set by the program as a (native) cairo surface.
|
|
|
|
*
|
2017-10-31 15:25:21 +01:00
|
|
|
* @property client_shape_clip
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam surface client_shape_clip
|
|
|
|
* @propemits false false
|
|
|
|
* @see shape_bounding
|
|
|
|
* @see shape_clip
|
|
|
|
* @see shape_input
|
|
|
|
* @see shape
|
|
|
|
* @see gears.surface.apply_shape_bounding
|
|
|
|
* @see gears.shape
|
|
|
|
* @see client_shape_bounding
|
|
|
|
* @see gears.surface
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The FreeDesktop StartId.
|
|
|
|
*
|
|
|
|
* When a client is spawned (like using a terminal or `awful.spawn`, a startup
|
|
|
|
* notification identifier is created. When the client is created, this
|
|
|
|
* identifier remain the same. This allow to match a spawn event to an actual
|
|
|
|
* client.
|
|
|
|
*
|
2018-07-23 10:04:07 +02:00
|
|
|
* This is used to display a different mouse cursor when the application is
|
|
|
|
* loading and also to attach some properties to the newly created client (like
|
|
|
|
* a `tag` or `floating` state).
|
|
|
|
*
|
|
|
|
* Some applications, like `xterm`, don't support startup notification. While
|
|
|
|
* not perfect, the addition the following code to `rc.lua` will mitigate the
|
|
|
|
* issue. Please note that this code is Linux specific.
|
|
|
|
*
|
|
|
|
* local blacklisted_snid = setmetatable({}, {__mode = "v" })
|
|
|
|
*
|
|
|
|
* --- Make startup notification work for some clients like XTerm. This is ugly
|
|
|
|
* -- but works often enough to be useful.
|
|
|
|
* local function fix_startup_id(c)
|
|
|
|
* -- Prevent "broken" sub processes created by `c` to inherit its SNID
|
|
|
|
* if c.startup_id then
|
|
|
|
* blacklisted_snid[c.startup_id] = blacklisted_snid[c.startup_id] or c
|
|
|
|
* return
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* if not c.pid then return end
|
|
|
|
*
|
|
|
|
* -- Read the process environment variables
|
|
|
|
* local f = io.open("/proc/"..c.pid.."/environ", "rb")
|
|
|
|
*
|
|
|
|
* -- It will only work on Linux, that's already 99% of the userbase.
|
|
|
|
* if not f then return end
|
|
|
|
*
|
|
|
|
* local value = _VERSION <= "Lua 5.1" and "([^\z]*)\0" or "([^\0]*)\0"
|
|
|
|
* local snid = f:read("*all"):match("STARTUP_ID=" .. value)
|
|
|
|
* f:close()
|
|
|
|
*
|
|
|
|
* -- If there is already a client using this SNID, it means it's either a
|
|
|
|
* -- subprocess or another window for the same process. While it makes sense
|
|
|
|
* -- in some case to apply the same rules, it is not always the case, so
|
|
|
|
* -- better doing nothing rather than something stupid.
|
|
|
|
* if blacklisted_snid[snid] then return end
|
|
|
|
*
|
|
|
|
* c.startup_id = snid
|
|
|
|
*
|
|
|
|
* blacklisted_snid[snid] = c
|
|
|
|
* end
|
|
|
|
*
|
2019-10-07 03:53:08 +02:00
|
|
|
* ruled.client.add_rule_source(
|
|
|
|
* "snid", fix_startup_id, {}, {"awful.spawn", "ruled.client"}
|
2018-07-23 10:04:07 +02:00
|
|
|
* )
|
|
|
|
*
|
2016-03-31 10:10:52 +02:00
|
|
|
* @property startup_id
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam string startup_id
|
|
|
|
* @propemits false false
|
2018-07-23 10:04:07 +02:00
|
|
|
* @see awful.spawn
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If the client that this object refers to is still managed by awesome.
|
|
|
|
*
|
|
|
|
* To avoid errors, use:
|
|
|
|
*
|
|
|
|
* local is_valid = pcall(function() return c.valid end) and c.valid
|
|
|
|
*
|
|
|
|
* @property valid
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam boolean valid
|
|
|
|
* @propemits false false
|
|
|
|
* @see kill
|
2016-03-31 10:10:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2019-12-31 00:17:23 +01:00
|
|
|
* The first tag of the client.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* Optimized form of `c:tags()[1]`. Not every workflow uses the
|
|
|
|
* ability to set multiple tags to a client. It is often enough
|
|
|
|
* to only get the first tag and ignore everything else.
|
2016-03-31 10:10:52 +02:00
|
|
|
*
|
|
|
|
* @property first_tag
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam tag first_tag
|
|
|
|
* @propemits false false
|
|
|
|
* @see tags
|
2017-01-30 13:42:54 +01:00
|
|
|
*/
|
|
|
|
|
2015-02-27 00:24:23 +01:00
|
|
|
/** Return client struts (reserved space at the edge of the screen).
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* The struts area is a table with a `left`, `right`, `top` and `bottom`
|
|
|
|
* keys to define how much space of the screen `workarea` this client
|
|
|
|
* should reserve for itself.
|
|
|
|
*
|
2020-01-19 01:34:10 +01:00
|
|
|
* This corresponds to EWMH's `_NET_WM_STRUT` and `_NET_WM_STRUT_PARTIAL`.
|
|
|
|
*
|
2020-08-10 08:33:59 +02:00
|
|
|
* In the example below, 2 object affect the workarea (using their struts):
|
|
|
|
*
|
|
|
|
* * The top wibar add a `top=24`
|
|
|
|
* * The bottom-left client add `bottom=100, left=100`
|
|
|
|
*
|
|
|
|
* @DOC_screen_struts_EXAMPLE@
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam table struts A table with new strut values, or none.
|
|
|
|
* @treturn table A table with strut values.
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method struts
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see geometry
|
|
|
|
* @see screen.workarea
|
2015-02-27 00:24:23 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** Get or set mouse buttons bindings for a client.
|
|
|
|
*
|
2018-12-27 03:16:33 +01:00
|
|
|
* @property buttons
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam table buttons
|
|
|
|
* @propemits false false
|
2018-12-27 03:16:33 +01:00
|
|
|
* @see awful.button
|
2015-02-27 00:24:23 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** Get the number of instances.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* @treturn integer The number of client objects alive.
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct instances
|
2015-02-27 00:24:23 +01:00
|
|
|
*/
|
|
|
|
|
2016-04-07 06:50:54 +02:00
|
|
|
/* Set a __index metamethod for all client instances.
|
2015-03-14 10:06:53 +01:00
|
|
|
* @tparam function cb The meta-method
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct set_index_miss_handler
|
2015-03-14 10:06:53 +01:00
|
|
|
*/
|
|
|
|
|
2016-04-07 06:50:54 +02:00
|
|
|
/* Set a __newindex metamethod for all client instances.
|
2015-03-14 10:06:53 +01:00
|
|
|
* @tparam function cb The meta-method
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct set_newindex_miss_handler
|
2015-03-14 10:06:53 +01:00
|
|
|
*/
|
|
|
|
|
2017-01-24 10:19:45 +01:00
|
|
|
typedef enum {
|
|
|
|
CLIENT_MAXIMIZED_NONE = 0 << 0,
|
|
|
|
CLIENT_MAXIMIZED_V = 1 << 0,
|
|
|
|
CLIENT_MAXIMIZED_H = 1 << 1,
|
2017-01-24 11:41:22 +01:00
|
|
|
CLIENT_MAXIMIZED_BOTH = 1 << 2, /* V|H == BOTH, but ~(V|H) != ~(BOTH)... */
|
2017-01-24 10:19:45 +01:00
|
|
|
} client_maximized_t;
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
static area_t titlebar_get_area(client_t *c, client_titlebar_t bar);
|
|
|
|
static drawable_t *titlebar_get_drawable(lua_State *L, client_t *c, int cl_idx, client_titlebar_t bar);
|
2016-09-15 18:28:46 +02:00
|
|
|
static void client_resize_do(client_t *c, area_t geometry);
|
2017-01-24 10:19:45 +01:00
|
|
|
static void client_set_maximized_common(lua_State *L, int cidx, bool s, const char* type, const int val);
|
2012-10-14 17:20:24 +02:00
|
|
|
|
2009-04-10 15:23:55 +02:00
|
|
|
/** Collect a client.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \return The number of element pushed on stack.
|
|
|
|
*/
|
2009-10-02 15:48:32 +02:00
|
|
|
static void
|
|
|
|
client_wipe(client_t *c)
|
2009-04-10 15:23:55 +02:00
|
|
|
{
|
2009-08-14 16:46:35 +02:00
|
|
|
key_array_wipe(&c->keys);
|
2011-04-27 06:49:56 +02:00
|
|
|
xcb_icccm_get_wm_protocols_reply_wipe(&c->protocols);
|
2017-03-06 15:50:51 +01:00
|
|
|
cairo_surface_array_wipe(&c->icons);
|
2009-09-08 11:21:47 +02:00
|
|
|
p_delete(&c->machine);
|
2009-04-10 15:23:55 +02:00
|
|
|
p_delete(&c->class);
|
|
|
|
p_delete(&c->instance);
|
|
|
|
p_delete(&c->icon_name);
|
2009-08-25 16:39:10 +02:00
|
|
|
p_delete(&c->alt_icon_name);
|
2009-04-10 15:23:55 +02:00
|
|
|
p_delete(&c->name);
|
2009-08-25 16:39:10 +02:00
|
|
|
p_delete(&c->alt_name);
|
2014-03-15 23:09:33 +01:00
|
|
|
p_delete(&c->startup_id);
|
2009-04-10 15:23:55 +02:00
|
|
|
}
|
2008-06-18 18:31:35 +02:00
|
|
|
|
2009-02-11 18:41:58 +01:00
|
|
|
/** Change the clients urgency flag.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index on the stack.
|
|
|
|
* \param urgent The new flag state.
|
2009-02-11 18:41:58 +01:00
|
|
|
*/
|
|
|
|
void
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_urgent(lua_State *L, int cidx, bool urgent)
|
2009-02-11 18:41:58 +01:00
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
2009-08-17 17:02:45 +02:00
|
|
|
|
|
|
|
if(c->urgent != urgent)
|
2009-02-11 18:41:58 +01:00
|
|
|
{
|
2009-08-17 17:02:45 +02:00
|
|
|
c->urgent = urgent;
|
2009-02-11 18:41:58 +01:00
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::urgent", 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-08 15:07:19 +02:00
|
|
|
#define DO_CLIENT_SET_PROPERTY(prop) \
|
|
|
|
void \
|
|
|
|
client_set_##prop(lua_State *L, int cidx, fieldtypeof(client_t, prop) value) \
|
|
|
|
{ \
|
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class); \
|
|
|
|
if(c->prop != value) \
|
|
|
|
{ \
|
|
|
|
c->prop = value; \
|
|
|
|
luaA_object_emit_signal(L, cidx, "property::" #prop, 0); \
|
|
|
|
} \
|
2009-02-11 18:41:58 +01:00
|
|
|
}
|
2009-10-08 15:07:19 +02:00
|
|
|
DO_CLIENT_SET_PROPERTY(group_window)
|
|
|
|
DO_CLIENT_SET_PROPERTY(type)
|
|
|
|
DO_CLIENT_SET_PROPERTY(transient_for)
|
|
|
|
DO_CLIENT_SET_PROPERTY(pid)
|
|
|
|
DO_CLIENT_SET_PROPERTY(skip_taskbar)
|
|
|
|
#undef DO_CLIENT_SET_PROPERTY
|
2009-08-17 17:02:45 +02:00
|
|
|
|
2011-02-10 17:04:26 +01:00
|
|
|
#define DO_CLIENT_SET_STRING_PROPERTY2(prop, signal) \
|
2009-10-08 15:07:19 +02:00
|
|
|
void \
|
|
|
|
client_set_##prop(lua_State *L, int cidx, char *value) \
|
|
|
|
{ \
|
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class); \
|
2013-08-01 11:48:03 +02:00
|
|
|
if (A_STREQ(c->prop, value)) \
|
|
|
|
{ \
|
|
|
|
p_delete(&value); \
|
|
|
|
return; \
|
|
|
|
} \
|
2009-10-08 15:07:19 +02:00
|
|
|
p_delete(&c->prop); \
|
|
|
|
c->prop = value; \
|
2011-02-10 17:04:26 +01:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::" #signal, 0); \
|
2009-10-08 15:07:19 +02:00
|
|
|
}
|
2011-02-10 17:04:26 +01:00
|
|
|
#define DO_CLIENT_SET_STRING_PROPERTY(prop) \
|
|
|
|
DO_CLIENT_SET_STRING_PROPERTY2(prop, prop)
|
2009-10-08 15:07:19 +02:00
|
|
|
DO_CLIENT_SET_STRING_PROPERTY(name)
|
2011-02-10 17:04:26 +01:00
|
|
|
DO_CLIENT_SET_STRING_PROPERTY2(alt_name, name)
|
2009-10-08 15:07:19 +02:00
|
|
|
DO_CLIENT_SET_STRING_PROPERTY(icon_name)
|
2011-02-10 17:04:26 +01:00
|
|
|
DO_CLIENT_SET_STRING_PROPERTY2(alt_icon_name, icon_name)
|
2018-07-26 00:03:09 +02:00
|
|
|
DO_CLIENT_SET_STRING_PROPERTY(startup_id)
|
2009-10-08 15:07:19 +02:00
|
|
|
DO_CLIENT_SET_STRING_PROPERTY(role)
|
|
|
|
DO_CLIENT_SET_STRING_PROPERTY(machine)
|
|
|
|
#undef DO_CLIENT_SET_STRING_PROPERTY
|
2009-08-17 17:02:45 +02:00
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
void
|
|
|
|
client_emit_scanned(void)
|
|
|
|
{
|
|
|
|
lua_State *L = globalconf_get_lua_State();
|
|
|
|
luaA_class_emit_signal(L, &client_class, "scanned", 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
client_emit_scanning(void)
|
|
|
|
{
|
|
|
|
lua_State *L = globalconf_get_lua_State();
|
|
|
|
luaA_class_emit_signal(L, &client_class, "scanning", 0);
|
|
|
|
}
|
|
|
|
|
2018-08-03 19:19:43 +02:00
|
|
|
void
|
|
|
|
client_set_motif_wm_hints(lua_State *L, int cidx, motif_wm_hints_t hints)
|
|
|
|
{
|
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
|
|
|
if (memcmp(&c->motif_wm_hints, &hints, sizeof(c->motif_wm_hints)) == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
memcpy(&c->motif_wm_hints, &hints, sizeof(c->motif_wm_hints));
|
|
|
|
luaA_object_emit_signal(L, cidx, "property::motif_wm_hints", 0);
|
|
|
|
}
|
|
|
|
|
2016-02-22 21:02:47 +01:00
|
|
|
void
|
2016-02-22 21:09:25 +01:00
|
|
|
client_find_transient_for(client_t *c)
|
2016-02-22 21:02:47 +01:00
|
|
|
{
|
|
|
|
int counter;
|
|
|
|
client_t *tc, *tmp;
|
2018-08-10 16:39:46 +02:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
2016-02-22 21:02:47 +01:00
|
|
|
|
2018-08-10 16:39:46 +02:00
|
|
|
/* This might return NULL, in which case we unset transient_for */
|
2016-02-22 21:09:25 +01:00
|
|
|
tmp = tc = client_getbywin(c->transient_for_window);
|
2016-02-22 21:02:47 +01:00
|
|
|
|
|
|
|
/* Verify that there are no loops in the transient_for relation after we are done */
|
|
|
|
for(counter = 0; tmp != NULL && counter <= globalconf.stack.len; counter++)
|
|
|
|
{
|
|
|
|
if (tmp == c)
|
|
|
|
/* We arrived back at the client we started from, so there is a loop */
|
|
|
|
counter = globalconf.stack.len+1;
|
|
|
|
tmp = tmp->transient_for;
|
|
|
|
}
|
|
|
|
|
2018-08-10 16:39:46 +02:00
|
|
|
if (counter > globalconf.stack.len)
|
|
|
|
{
|
|
|
|
/* There was a loop, so unset .transient_for */
|
|
|
|
tc = NULL;
|
2016-02-22 21:02:47 +01:00
|
|
|
}
|
2018-08-10 16:39:46 +02:00
|
|
|
|
|
|
|
luaA_object_push(L, c);
|
|
|
|
client_set_transient_for(L, -1, tc);
|
|
|
|
lua_pop(L, 1);
|
2016-02-22 21:02:47 +01:00
|
|
|
}
|
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
void
|
|
|
|
client_set_class_instance(lua_State *L, int cidx, const char *class, const char *instance)
|
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
2009-08-17 17:02:45 +02:00
|
|
|
p_delete(&c->class);
|
|
|
|
p_delete(&c->instance);
|
|
|
|
c->class = a_strdup(class);
|
|
|
|
luaA_object_emit_signal(L, cidx, "property::class", 0);
|
2009-10-08 15:07:19 +02:00
|
|
|
c->instance = a_strdup(instance);
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::instance", 0);
|
|
|
|
}
|
|
|
|
|
2015-09-25 00:43:52 +02:00
|
|
|
/** Returns true if a client is tagged with one of the active tags.
|
2008-04-29 09:14:46 +02:00
|
|
|
* \param c The client to check.
|
2008-06-09 21:43:09 +02:00
|
|
|
* \return true if the client is visible, false otherwise.
|
2008-04-09 17:33:47 +02:00
|
|
|
*/
|
|
|
|
bool
|
2015-09-25 00:43:52 +02:00
|
|
|
client_on_selected_tags(client_t *c)
|
2008-04-09 17:33:47 +02:00
|
|
|
{
|
2011-10-04 17:22:43 +02:00
|
|
|
if(c->sticky)
|
2011-03-27 20:07:14 +02:00
|
|
|
return true;
|
|
|
|
|
2012-10-20 17:33:32 +02:00
|
|
|
foreach(tag, globalconf.tags)
|
2011-03-27 20:07:14 +02:00
|
|
|
if(tag_get_selected(*tag) && is_client_tagged(c, *tag))
|
2008-08-21 16:29:53 +02:00
|
|
|
return true;
|
|
|
|
|
2008-04-09 17:33:47 +02:00
|
|
|
return false;
|
|
|
|
}
|
2008-06-10 19:29:53 +02:00
|
|
|
|
2008-06-05 09:25:38 +02:00
|
|
|
/** Get a client by its window.
|
|
|
|
* \param w The client window to find.
|
2008-04-29 09:14:46 +02:00
|
|
|
* \return A client pointer if found, NULL otherwise.
|
2007-12-14 14:29:32 +01:00
|
|
|
*/
|
2008-04-11 11:35:11 +02:00
|
|
|
client_t *
|
2008-06-05 09:25:38 +02:00
|
|
|
client_getbywin(xcb_window_t w)
|
2007-12-14 14:29:32 +01:00
|
|
|
{
|
2009-04-10 15:23:55 +02:00
|
|
|
foreach(c, globalconf.clients)
|
2010-07-30 22:07:24 +02:00
|
|
|
if((*c)->window == w)
|
|
|
|
return *c;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-02-28 13:29:25 +01:00
|
|
|
client_t *
|
|
|
|
client_getbynofocuswin(xcb_window_t w)
|
|
|
|
{
|
|
|
|
foreach(c, globalconf.clients)
|
|
|
|
if((*c)->nofocus_window == w)
|
|
|
|
return *c;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-07-30 22:07:24 +02:00
|
|
|
/** Get a client by its frame window.
|
|
|
|
* \param w The client window to find.
|
|
|
|
* \return A client pointer if found, NULL otherwise.
|
|
|
|
*/
|
|
|
|
client_t *
|
|
|
|
client_getbyframewin(xcb_window_t w)
|
|
|
|
{
|
|
|
|
foreach(c, globalconf.clients)
|
|
|
|
if((*c)->frame_window == w)
|
2009-04-10 15:23:55 +02:00
|
|
|
return *c;
|
|
|
|
|
2009-04-09 17:17:10 +02:00
|
|
|
return NULL;
|
2007-12-14 14:29:32 +01:00
|
|
|
}
|
|
|
|
|
2012-07-06 13:33:27 +02:00
|
|
|
/** Unfocus a client (internal).
|
2010-10-10 14:59:02 +02:00
|
|
|
* \param c The client.
|
2009-04-07 14:20:22 +02:00
|
|
|
*/
|
2009-10-13 17:58:25 +02:00
|
|
|
static void
|
2012-07-06 13:33:27 +02:00
|
|
|
client_unfocus_internal(client_t *c)
|
2009-04-07 14:20:22 +02:00
|
|
|
{
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
2010-10-10 14:46:56 +02:00
|
|
|
globalconf.focus.client = NULL;
|
2009-04-07 14:20:22 +02:00
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_push(L, c);
|
2019-11-11 01:42:52 +01:00
|
|
|
|
|
|
|
lua_pushboolean(L, false);
|
|
|
|
luaA_object_emit_signal(L, -2, "property::active", 1);
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_emit_signal(L, -1, "unfocus", 0);
|
|
|
|
lua_pop(L, 1);
|
2009-02-11 18:32:27 +01:00
|
|
|
}
|
|
|
|
|
2012-07-06 13:33:27 +02:00
|
|
|
/** Unfocus a client.
|
|
|
|
* \param c The client.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
client_unfocus(client_t *c)
|
|
|
|
{
|
|
|
|
client_unfocus_internal(c);
|
|
|
|
globalconf.focus.need_update = true;
|
|
|
|
}
|
|
|
|
|
2009-08-30 07:26:19 +02:00
|
|
|
/** Check if client supports atom a protocol in WM_PROTOCOL.
|
2009-06-24 18:10:55 +02:00
|
|
|
* \param c The client.
|
2009-07-27 06:20:00 +02:00
|
|
|
* \param atom The protocol atom to check for.
|
2009-06-24 18:10:55 +02:00
|
|
|
* \return True if client has the atom in protocol, false otherwise.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
client_hasproto(client_t *c, xcb_atom_t atom)
|
|
|
|
{
|
2009-08-24 10:48:18 +02:00
|
|
|
for(uint32_t i = 0; i < c->protocols.atoms_len; i++)
|
2009-06-24 18:17:31 +02:00
|
|
|
if(c->protocols.atoms[i] == atom)
|
|
|
|
return true;
|
|
|
|
return false;
|
2009-06-24 18:10:55 +02:00
|
|
|
}
|
|
|
|
|
2009-09-27 09:21:04 +02:00
|
|
|
/** Prepare banning a client by running all needed lua events.
|
|
|
|
* \param c The client.
|
|
|
|
*/
|
|
|
|
void client_ban_unfocus(client_t *c)
|
|
|
|
{
|
|
|
|
/* Wait until the last moment to take away the focus from the window. */
|
2010-10-10 14:46:56 +02:00
|
|
|
if(globalconf.focus.client == c)
|
2009-09-27 09:21:04 +02:00
|
|
|
client_unfocus(c);
|
|
|
|
}
|
|
|
|
|
2008-11-20 22:11:13 +01:00
|
|
|
/** Ban client and move it out of the viewport.
|
2008-06-10 19:29:53 +02:00
|
|
|
* \param c The client.
|
2007-09-05 20:15:00 +02:00
|
|
|
*/
|
|
|
|
void
|
2008-04-11 11:35:11 +02:00
|
|
|
client_ban(client_t *c)
|
2007-09-05 20:15:00 +02:00
|
|
|
{
|
2008-11-20 22:11:13 +01:00
|
|
|
if(!c->isbanned)
|
|
|
|
{
|
2017-04-21 18:53:25 +02:00
|
|
|
client_ignore_enterleave_events();
|
2010-07-30 20:54:20 +02:00
|
|
|
xcb_unmap_window(globalconf.connection, c->frame_window);
|
2017-04-21 18:53:25 +02:00
|
|
|
client_restore_enterleave_events();
|
2008-11-20 22:11:13 +01:00
|
|
|
|
|
|
|
c->isbanned = true;
|
2008-12-06 23:41:33 +01:00
|
|
|
|
2009-09-27 09:21:04 +02:00
|
|
|
client_ban_unfocus(c);
|
2009-03-03 17:24:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-30 07:26:19 +02:00
|
|
|
/** This is part of The Bob Marley Algorithm: we ignore enter and leave window
|
2009-08-03 15:57:10 +02:00
|
|
|
* in certain cases, like map/unmap or move, so we don't get spurious events.
|
2016-09-25 07:04:47 +02:00
|
|
|
* The implementation works by noting the range of sequence numbers for which we
|
|
|
|
* should ignore events. We grab the server to make sure that only we could
|
|
|
|
* generate events in this range.
|
2009-08-03 15:57:10 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
client_ignore_enterleave_events(void)
|
|
|
|
{
|
2017-05-13 23:22:15 +02:00
|
|
|
check(globalconf.pending_enter_leave_begin.sequence == 0);
|
2016-09-25 07:04:47 +02:00
|
|
|
globalconf.pending_enter_leave_begin = xcb_grab_server(globalconf.connection);
|
|
|
|
/* If the connection is broken, we get a request with sequence number 0
|
|
|
|
* which would then trigger an assertion in
|
|
|
|
* client_restore_enterleave_events(). Handle this nicely.
|
|
|
|
*/
|
|
|
|
if(xcb_connection_has_error(globalconf.connection))
|
|
|
|
fatal("X server connection broke (error %d)",
|
|
|
|
xcb_connection_has_error(globalconf.connection));
|
2017-05-13 23:22:15 +02:00
|
|
|
check(globalconf.pending_enter_leave_begin.sequence != 0);
|
2009-08-03 15:57:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
client_restore_enterleave_events(void)
|
|
|
|
{
|
2016-09-25 07:04:47 +02:00
|
|
|
sequence_pair_t pair;
|
|
|
|
|
2017-05-13 23:22:15 +02:00
|
|
|
check(globalconf.pending_enter_leave_begin.sequence != 0);
|
2016-09-25 07:04:47 +02:00
|
|
|
pair.begin = globalconf.pending_enter_leave_begin;
|
|
|
|
pair.end = xcb_no_operation(globalconf.connection);
|
2019-02-27 10:20:22 +01:00
|
|
|
xutil_ungrab_server(globalconf.connection);
|
2016-09-25 07:04:47 +02:00
|
|
|
globalconf.pending_enter_leave_begin.sequence = 0;
|
|
|
|
sequence_pair_array_append(&globalconf.ignore_enter_leave_events, pair);
|
2009-08-03 15:57:10 +02:00
|
|
|
}
|
|
|
|
|
2009-04-07 14:20:22 +02:00
|
|
|
/** Record that a client got focus.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param c The client.
|
2014-11-06 19:48:35 +01:00
|
|
|
* \return true if the client focus changed, false otherwise.
|
2007-09-05 20:15:00 +02:00
|
|
|
*/
|
2014-11-06 19:48:35 +01:00
|
|
|
bool
|
2009-04-07 14:20:22 +02:00
|
|
|
client_focus_update(client_t *c)
|
2007-09-05 20:15:00 +02:00
|
|
|
{
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
|
|
|
|
2014-11-06 19:48:35 +01:00
|
|
|
if(globalconf.focus.client && globalconf.focus.client != c)
|
2009-04-28 10:54:10 +02:00
|
|
|
{
|
2012-07-06 13:33:27 +02:00
|
|
|
/* When we are called due to a FocusIn event (=old focused client
|
|
|
|
* already unfocused), we don't want to cause a SetInputFocus,
|
|
|
|
* because the client which has focus now could be using globally
|
|
|
|
* active input model (or 'no input').
|
|
|
|
*/
|
|
|
|
client_unfocus_internal(globalconf.focus.client);
|
2009-04-28 10:54:10 +02:00
|
|
|
}
|
2008-07-31 15:51:28 +02:00
|
|
|
|
2014-11-06 19:48:35 +01:00
|
|
|
bool focused_new = globalconf.focus.client != c;
|
2010-10-10 14:46:56 +02:00
|
|
|
globalconf.focus.client = c;
|
2008-06-09 21:43:09 +02:00
|
|
|
|
2014-11-06 19:48:35 +01:00
|
|
|
/* According to EWMH, we have to remove the urgent state from a client.
|
|
|
|
* This should be done also for the current/focused client (FS#1310). */
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_push(L, c);
|
|
|
|
client_set_urgent(L, -1, false);
|
2009-02-11 18:41:58 +01:00
|
|
|
|
2019-11-11 01:42:52 +01:00
|
|
|
if(focused_new) {
|
|
|
|
lua_pushboolean(L, true);
|
|
|
|
luaA_object_emit_signal(L, -2, "property::active", 1);
|
2014-11-06 19:48:35 +01:00
|
|
|
luaA_object_emit_signal(L, -1, "focus", 0);
|
2019-11-11 01:42:52 +01:00
|
|
|
}
|
2014-11-06 19:48:35 +01:00
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_pop(L, 1);
|
2014-11-06 19:48:35 +01:00
|
|
|
|
|
|
|
return focused_new;
|
2009-04-07 14:20:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Give focus to client, or to first client if client is NULL.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param c The client.
|
2009-04-07 14:20:22 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
client_focus(client_t *c)
|
|
|
|
{
|
|
|
|
/* We have to set focus on first client */
|
2009-04-10 15:23:55 +02:00
|
|
|
if(!c && globalconf.clients.len && !(c = globalconf.clients.tab[0]))
|
|
|
|
return;
|
2009-04-07 14:20:22 +02:00
|
|
|
|
2014-11-06 19:48:35 +01:00
|
|
|
if(client_focus_update(c))
|
|
|
|
globalconf.focus.need_update = true;
|
2010-10-10 14:50:35 +02:00
|
|
|
}
|
2010-08-12 14:32:27 +02:00
|
|
|
|
2016-02-28 13:29:25 +01:00
|
|
|
static xcb_window_t
|
|
|
|
client_get_nofocus_window(client_t *c)
|
|
|
|
{
|
|
|
|
if (c->nofocus_window == XCB_NONE) {
|
|
|
|
c->nofocus_window = xcb_generate_id(globalconf.connection);
|
|
|
|
xcb_create_window(globalconf.connection, globalconf.default_depth, c->nofocus_window, c->frame_window,
|
|
|
|
-2, -2, 1, 1, 0, XCB_COPY_FROM_PARENT, globalconf.visual->visual_id,
|
|
|
|
0, NULL);
|
|
|
|
xcb_map_window(globalconf.connection, c->nofocus_window);
|
|
|
|
xwindow_grabkeys(c->nofocus_window, &c->keys);
|
|
|
|
}
|
|
|
|
return c->nofocus_window;
|
|
|
|
}
|
|
|
|
|
2010-10-10 14:50:35 +02:00
|
|
|
void
|
|
|
|
client_focus_refresh(void)
|
|
|
|
{
|
|
|
|
client_t *c = globalconf.focus.client;
|
2015-09-23 20:48:56 +02:00
|
|
|
xcb_window_t win = globalconf.focus.window_no_focus;
|
2009-10-13 17:36:07 +02:00
|
|
|
|
2010-10-10 14:50:35 +02:00
|
|
|
if(!globalconf.focus.need_update)
|
|
|
|
return;
|
|
|
|
|
2015-09-25 00:43:52 +02:00
|
|
|
if(c && client_on_selected_tags(c))
|
2010-10-10 14:50:35 +02:00
|
|
|
{
|
2011-01-09 10:40:42 +01:00
|
|
|
/* Make sure this window is unbanned and e.g. not minimized */
|
|
|
|
client_unban(c);
|
2010-10-10 14:50:35 +02:00
|
|
|
/* Sets focus on window - using xcb_set_input_focus or WM_TAKE_FOCUS */
|
|
|
|
if(!c->nofocus)
|
2012-04-06 12:20:57 +02:00
|
|
|
win = c->window;
|
|
|
|
else
|
2016-02-28 13:29:25 +01:00
|
|
|
win = client_get_nofocus_window(c);
|
2009-10-13 17:36:07 +02:00
|
|
|
|
2010-10-10 14:50:35 +02:00
|
|
|
if(client_hasproto(c, WM_TAKE_FOCUS))
|
|
|
|
xwindow_takefocus(c->window);
|
|
|
|
}
|
2012-04-06 12:20:57 +02:00
|
|
|
|
2015-07-29 04:14:39 +02:00
|
|
|
/* If nothing has the focus or the currently focused client does not want
|
2012-04-06 12:20:57 +02:00
|
|
|
* us to focus it, this sets the focus to the root window. This makes sure
|
|
|
|
* the previously focused client actually gets unfocused. Alternatively, the
|
|
|
|
* new client gets the input focus.
|
|
|
|
*/
|
|
|
|
xcb_set_input_focus(globalconf.connection, XCB_INPUT_FOCUS_PARENT,
|
|
|
|
win, globalconf.timestamp);
|
Fix a race with setting the focus
There are two ways in which the input focus can change: Lua can request
a change and the X11 server can inform us that the input focus changed
(because some application changed it).
In the first case, we still have to inform the X11 server about the
desired change, in the second case we must not to avoid races due to
X11's asynchronous nature.
However, there was a case where we screwed up: When a focus change is
still pending, meaning that Lua assigned the focus elsewhere, but we
have not yet sent this focus change to the X11 server, we could get an
event from the X11 server telling us that the focus changed. To make
sure that the pending focus change is not lost, we sent the focus change
out in this case (call to client_focus_refresh() in
event_handle_focusin()). After sending out this pending call, we updated
the internal state to record that whatever the X11 server just told us
had the focus. The intention was that our just sent-out focus change
will cause the X11 server to send a new event and our to-be-focused
client then has the focus.
However, if the pending focus change was for a client which only showed
up in this event loop iteration, the client was still banned. This means
that client_focus_refresh() would call client_unban() to be able to give
the focus to this client. However, since awesome (partly) allows to
"focus" currently banned clients, client_unban() recorded that there is
a pending focus change. This caused confusion later on.
In this specific bug, a main window opened a dialog, and when this
dialog was closed, a new dialog window was opened immediately. When the
first dialog was closed, Lua (the focus history) gave the input focus to
the main window. Now, a new dialog showed up and Lua focused it. Next,
we received the event from the X11 server telling us that the main
window was focused. Because there was still a pending focus change to
the new dialog window, event_handle_focusin() called
client_focus_refresh() to send out this focus change. This set
globalconf.focus.need_update to false and continued. However, because
the new dialog only just now appeared, it was still banned, meaning that
client_focus_refresh() had to call client_unban(). This set
globalconf.focus.need_update to true. Thus, when client_focus_refresh()
returned, globalconf.focus.need_update was incorrectly true. Next,
event_handle_focusin() recorded that the main window had the focus.
Thus, it now appeared as if there was a pending focus change for the
main window. Next, we got the event from the X11 server telling us that
the dialog is now focused, and because focus.need_update was set,
awesome now send out a focus change request for the main window.
Fix this race by unsetting globalconf.focus.need_update at the end of
client_focus_refresh() and not at the beginning, thus making sure that
client_unban() cannot set this flag again.
Fixes: https://github.com/awesomeWM/awesome/issues/2220
Signed-off-by: Uli Schlachter <psychon@znc.in>
2018-08-16 18:03:22 +02:00
|
|
|
|
|
|
|
/* Do this last, because client_unban() might set it to true */
|
|
|
|
globalconf.focus.need_update = false;
|
2007-09-05 20:15:00 +02:00
|
|
|
}
|
|
|
|
|
2016-09-15 18:31:29 +02:00
|
|
|
static void
|
2016-02-28 16:24:30 +01:00
|
|
|
client_border_refresh(void)
|
|
|
|
{
|
|
|
|
foreach(c, globalconf.clients)
|
|
|
|
window_border_refresh((window_t *) *c);
|
|
|
|
}
|
|
|
|
|
2016-09-15 18:48:56 +02:00
|
|
|
static void
|
|
|
|
client_geometry_refresh(void)
|
|
|
|
{
|
2016-09-23 10:51:24 +02:00
|
|
|
bool ignored_enterleave = false;
|
2016-09-15 18:48:56 +02:00
|
|
|
foreach(_c, globalconf.clients)
|
|
|
|
{
|
|
|
|
client_t *c = *_c;
|
|
|
|
|
|
|
|
/* Compute the client window's and frame window's geometry */
|
|
|
|
area_t geometry = c->geometry;
|
|
|
|
area_t real_geometry = c->geometry;
|
|
|
|
if (!c->fullscreen)
|
|
|
|
{
|
2017-01-08 15:25:54 +01:00
|
|
|
if ((real_geometry.width < c->titlebar[CLIENT_TITLEBAR_LEFT].size
|
|
|
|
+ c->titlebar[CLIENT_TITLEBAR_RIGHT].size) ||
|
|
|
|
(real_geometry.height < c->titlebar[CLIENT_TITLEBAR_TOP].size
|
|
|
|
+ c->titlebar[CLIENT_TITLEBAR_BOTTOM].size))
|
|
|
|
warn("Resizing a window to a negative size!? Have width %d-%d-%d=%d"
|
|
|
|
" and height %d-%d-%d=%d", real_geometry.width,
|
|
|
|
c->titlebar[CLIENT_TITLEBAR_LEFT].size,
|
|
|
|
c->titlebar[CLIENT_TITLEBAR_RIGHT].size,
|
|
|
|
real_geometry.width -
|
|
|
|
c->titlebar[CLIENT_TITLEBAR_LEFT].size -
|
|
|
|
c->titlebar[CLIENT_TITLEBAR_RIGHT].size,
|
|
|
|
real_geometry.height,
|
|
|
|
c->titlebar[CLIENT_TITLEBAR_TOP].size,
|
|
|
|
c->titlebar[CLIENT_TITLEBAR_BOTTOM].size,
|
|
|
|
real_geometry.height -
|
|
|
|
c->titlebar[CLIENT_TITLEBAR_TOP].size -
|
|
|
|
c->titlebar[CLIENT_TITLEBAR_BOTTOM].size);
|
|
|
|
|
2016-09-15 18:48:56 +02:00
|
|
|
real_geometry.x = c->titlebar[CLIENT_TITLEBAR_LEFT].size;
|
|
|
|
real_geometry.y = c->titlebar[CLIENT_TITLEBAR_TOP].size;
|
|
|
|
real_geometry.width -= c->titlebar[CLIENT_TITLEBAR_LEFT].size;
|
|
|
|
real_geometry.width -= c->titlebar[CLIENT_TITLEBAR_RIGHT].size;
|
|
|
|
real_geometry.height -= c->titlebar[CLIENT_TITLEBAR_TOP].size;
|
|
|
|
real_geometry.height -= c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
|
2017-01-08 15:25:54 +01:00
|
|
|
|
|
|
|
if (real_geometry.width == 0 || real_geometry.height == 0)
|
|
|
|
warn("Resizing a window to size zero!?");
|
2016-09-15 18:48:56 +02:00
|
|
|
} else {
|
|
|
|
real_geometry.x = 0;
|
|
|
|
real_geometry.y = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Is there anything to do? */
|
|
|
|
if (AREA_EQUAL(geometry, c->x11_frame_geometry)
|
2017-01-12 11:19:01 +01:00
|
|
|
&& AREA_EQUAL(real_geometry, c->x11_client_geometry)) {
|
|
|
|
if (c->got_configure_request) {
|
|
|
|
/* ICCCM 4.1.5 / 4.2.3, if nothing was changed, send an event saying so */
|
|
|
|
client_send_configure(c);
|
|
|
|
c->got_configure_request = false;
|
|
|
|
}
|
2016-09-15 18:48:56 +02:00
|
|
|
continue;
|
2017-01-12 11:19:01 +01:00
|
|
|
}
|
2016-09-15 18:48:56 +02:00
|
|
|
|
2016-09-23 10:51:24 +02:00
|
|
|
if (!ignored_enterleave) {
|
|
|
|
client_ignore_enterleave_events();
|
|
|
|
ignored_enterleave = true;
|
|
|
|
}
|
|
|
|
|
2016-09-15 18:48:56 +02:00
|
|
|
xcb_configure_window(globalconf.connection, c->frame_window,
|
|
|
|
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
|
|
|
(uint32_t[]) { geometry.x, geometry.y, geometry.width, geometry.height });
|
|
|
|
xcb_configure_window(globalconf.connection, c->window,
|
|
|
|
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
|
|
|
(uint32_t[]) { real_geometry.x, real_geometry.y, real_geometry.width, real_geometry.height });
|
|
|
|
|
|
|
|
c->x11_frame_geometry = geometry;
|
|
|
|
c->x11_client_geometry = real_geometry;
|
|
|
|
|
|
|
|
/* ICCCM 4.2.3 says something else, but Java always needs this... */
|
|
|
|
client_send_configure(c);
|
2017-01-12 11:19:01 +01:00
|
|
|
c->got_configure_request = false;
|
2016-09-15 18:48:56 +02:00
|
|
|
}
|
2016-09-23 10:51:24 +02:00
|
|
|
if (ignored_enterleave)
|
|
|
|
client_restore_enterleave_events();
|
2016-09-15 18:48:56 +02:00
|
|
|
}
|
|
|
|
|
2016-09-15 18:31:29 +02:00
|
|
|
void
|
|
|
|
client_refresh(void)
|
|
|
|
{
|
2016-09-15 18:48:56 +02:00
|
|
|
client_geometry_refresh();
|
2016-09-15 18:31:29 +02:00
|
|
|
client_border_refresh();
|
|
|
|
client_focus_refresh();
|
|
|
|
}
|
|
|
|
|
2016-10-07 00:46:57 +02:00
|
|
|
void
|
|
|
|
client_destroy_later(void)
|
|
|
|
{
|
|
|
|
bool ignored_enterleave = false;
|
|
|
|
foreach(window, globalconf.destroy_later_windows)
|
|
|
|
{
|
|
|
|
if (!ignored_enterleave) {
|
|
|
|
client_ignore_enterleave_events();
|
|
|
|
ignored_enterleave = true;
|
|
|
|
}
|
|
|
|
xcb_destroy_window(globalconf.connection, *window);
|
|
|
|
}
|
|
|
|
if (ignored_enterleave)
|
|
|
|
client_restore_enterleave_events();
|
|
|
|
|
|
|
|
/* Everything's done, clear the list */
|
|
|
|
globalconf.destroy_later_windows.len = 0;
|
|
|
|
}
|
|
|
|
|
2015-10-10 17:45:24 +02:00
|
|
|
static void
|
|
|
|
border_width_callback(client_t *c, uint16_t old_width, uint16_t new_width)
|
|
|
|
{
|
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)
|
|
|
|
{
|
|
|
|
area_t geometry = c->geometry;
|
|
|
|
int16_t diff = new_width - old_width;
|
|
|
|
xwindow_translate_for_gravity(c->size_hints.win_gravity,
|
|
|
|
diff, diff, diff, diff,
|
2018-08-17 09:58:30 +02:00
|
|
|
&geometry.x, &geometry.y);
|
2016-09-15 18:28:46 +02:00
|
|
|
/* inform client about changes */
|
|
|
|
client_resize_do(c, geometry);
|
2015-10-10 17:45:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-09 13:54:49 +02:00
|
|
|
static void
|
2014-12-06 11:07:20 +01:00
|
|
|
client_update_properties(lua_State *L, int cidx, client_t *c)
|
2010-08-09 13:54:49 +02:00
|
|
|
{
|
|
|
|
/* get all hints */
|
|
|
|
xcb_get_property_cookie_t wm_normal_hints = property_get_wm_normal_hints(c);
|
|
|
|
xcb_get_property_cookie_t wm_hints = property_get_wm_hints(c);
|
|
|
|
xcb_get_property_cookie_t wm_transient_for = property_get_wm_transient_for(c);
|
|
|
|
xcb_get_property_cookie_t wm_client_leader = property_get_wm_client_leader(c);
|
|
|
|
xcb_get_property_cookie_t wm_client_machine = property_get_wm_client_machine(c);
|
|
|
|
xcb_get_property_cookie_t wm_window_role = property_get_wm_window_role(c);
|
|
|
|
xcb_get_property_cookie_t net_wm_pid = property_get_net_wm_pid(c);
|
|
|
|
xcb_get_property_cookie_t net_wm_icon = property_get_net_wm_icon(c);
|
|
|
|
xcb_get_property_cookie_t wm_name = property_get_wm_name(c);
|
|
|
|
xcb_get_property_cookie_t net_wm_name = property_get_net_wm_name(c);
|
|
|
|
xcb_get_property_cookie_t wm_icon_name = property_get_wm_icon_name(c);
|
|
|
|
xcb_get_property_cookie_t net_wm_icon_name = property_get_net_wm_icon_name(c);
|
|
|
|
xcb_get_property_cookie_t wm_class = property_get_wm_class(c);
|
|
|
|
xcb_get_property_cookie_t wm_protocols = property_get_wm_protocols(c);
|
2018-08-03 19:19:43 +02:00
|
|
|
xcb_get_property_cookie_t motif_wm_hints = property_get_motif_wm_hints(c);
|
2010-08-09 13:54:49 +02:00
|
|
|
xcb_get_property_cookie_t opacity = xwindow_get_opacity_unchecked(c->window);
|
|
|
|
|
|
|
|
/* update strut */
|
|
|
|
ewmh_process_client_strut(c);
|
|
|
|
|
|
|
|
/* Now process all replies */
|
|
|
|
property_update_wm_normal_hints(c, wm_normal_hints);
|
|
|
|
property_update_wm_hints(c, wm_hints);
|
|
|
|
property_update_wm_transient_for(c, wm_transient_for);
|
|
|
|
property_update_wm_client_leader(c, wm_client_leader);
|
|
|
|
property_update_wm_client_machine(c, wm_client_machine);
|
|
|
|
property_update_wm_window_role(c, wm_window_role);
|
|
|
|
property_update_net_wm_pid(c, net_wm_pid);
|
|
|
|
property_update_net_wm_icon(c, net_wm_icon);
|
|
|
|
property_update_wm_name(c, wm_name);
|
|
|
|
property_update_net_wm_name(c, net_wm_name);
|
|
|
|
property_update_wm_icon_name(c, wm_icon_name);
|
|
|
|
property_update_net_wm_icon_name(c, net_wm_icon_name);
|
|
|
|
property_update_wm_class(c, wm_class);
|
|
|
|
property_update_wm_protocols(c, wm_protocols);
|
2018-08-03 19:19:43 +02:00
|
|
|
property_update_motif_wm_hints(c, motif_wm_hints);
|
2014-12-06 11:07:20 +01:00
|
|
|
window_set_opacity(L, cidx, xwindow_get_opacity_from_cookie(opacity));
|
2010-08-09 13:54:49 +02:00
|
|
|
}
|
|
|
|
|
2008-06-05 09:25:38 +02:00
|
|
|
/** Manage a new client.
|
|
|
|
* \param w The window.
|
|
|
|
* \param wgeom Window geometry.
|
2008-12-29 12:23:37 +01:00
|
|
|
* \param startup True if we are managing at startup time.
|
2007-09-20 21:27:43 +02:00
|
|
|
*/
|
2007-09-05 20:15:00 +02:00
|
|
|
void
|
2014-03-16 20:15:02 +01:00
|
|
|
client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, xcb_get_window_attributes_reply_t *wattr)
|
2007-09-05 20:15:00 +02:00
|
|
|
{
|
2018-07-22 22:05:11 +02:00
|
|
|
xcb_void_cookie_t reparent_cookie;
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
2009-03-06 14:01:29 +01:00
|
|
|
const uint32_t select_input_val[] = { CLIENT_SELECT_INPUT_EVENT_MASK };
|
2007-09-05 20:15:00 +02:00
|
|
|
|
2008-06-30 13:09:24 +02:00
|
|
|
if(systray_iskdedockapp(w))
|
|
|
|
{
|
2016-05-29 14:29:23 +02:00
|
|
|
systray_request_handle(w);
|
2008-06-30 13:09:24 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-08-24 10:16:52 +02:00
|
|
|
/* If this is a new client that just has been launched, then request its
|
|
|
|
* startup id. */
|
2014-03-16 20:15:02 +01:00
|
|
|
xcb_get_property_cookie_t startup_id_q = xcb_get_property(globalconf.connection, false,
|
|
|
|
w, _NET_STARTUP_ID,
|
|
|
|
XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
|
2009-08-24 10:16:52 +02:00
|
|
|
|
2010-07-14 21:35:06 +02:00
|
|
|
/* Make sure the window is automatically mapped if awesome exits or dies. */
|
|
|
|
xcb_change_save_set(globalconf.connection, XCB_SET_MODE_INSERT, w);
|
2014-01-03 16:51:16 +01:00
|
|
|
if (globalconf.have_shape)
|
|
|
|
xcb_shape_select_input(globalconf.connection, w, 1);
|
2010-07-14 21:35:06 +02:00
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
client_t *c = client_new(L);
|
2010-08-16 14:10:58 +02:00
|
|
|
xcb_screen_t *s = globalconf.screen;
|
2015-10-10 17:45:24 +02:00
|
|
|
c->border_width_callback = (void (*) (void *, uint16_t, uint16_t)) border_width_callback;
|
2008-01-22 09:50:24 +01:00
|
|
|
|
2009-05-19 14:49:22 +02:00
|
|
|
/* consider the window banned */
|
|
|
|
c->isbanned = true;
|
2014-03-15 11:56:57 +01:00
|
|
|
/* Store window and visual */
|
2009-08-17 17:02:45 +02:00
|
|
|
c->window = w;
|
2014-03-15 11:56:57 +01:00
|
|
|
c->visualtype = draw_find_visual(globalconf.screen, wattr->visual);
|
2010-07-30 20:54:20 +02:00
|
|
|
c->frame_window = xcb_generate_id(globalconf.connection);
|
2010-09-30 12:55:58 +02:00
|
|
|
xcb_create_window(globalconf.connection, globalconf.default_depth, c->frame_window, s->root,
|
2010-07-30 20:54:20 +02:00
|
|
|
wgeom->x, wgeom->y, wgeom->width, wgeom->height,
|
2010-09-30 12:55:58 +02:00
|
|
|
wgeom->border_width, XCB_COPY_FROM_PARENT, globalconf.visual->visual_id,
|
2014-03-30 14:58:03 +02:00
|
|
|
XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY | XCB_CW_WIN_GRAVITY
|
|
|
|
| XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP,
|
2010-07-30 20:54:20 +02:00
|
|
|
(const uint32_t [])
|
|
|
|
{
|
2010-10-06 20:01:44 +02:00
|
|
|
globalconf.screen->black_pixel,
|
2010-07-30 20:54:20 +02:00
|
|
|
XCB_GRAVITY_NORTH_WEST,
|
|
|
|
XCB_GRAVITY_NORTH_WEST,
|
|
|
|
1,
|
2010-09-30 14:26:30 +02:00
|
|
|
FRAME_SELECT_INPUT_EVENT_MASK,
|
|
|
|
globalconf.default_cmap
|
2010-07-30 20:54:20 +02:00
|
|
|
});
|
2011-10-19 15:11:11 +02:00
|
|
|
|
2014-03-16 20:15:02 +01:00
|
|
|
/* The client may already be mapped, thus we must be sure that we don't send
|
|
|
|
* ourselves an UnmapNotify due to the xcb_reparent_window().
|
|
|
|
*
|
|
|
|
* Grab the server to make sure we don't lose any events.
|
|
|
|
*/
|
|
|
|
uint32_t no_event[] = { 0 };
|
|
|
|
xcb_grab_server(globalconf.connection);
|
2011-10-19 15:11:11 +02:00
|
|
|
|
2014-03-16 20:15:02 +01:00
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
globalconf.screen->root,
|
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
no_event);
|
2018-07-22 22:05:11 +02:00
|
|
|
reparent_cookie = xcb_reparent_window_checked(globalconf.connection, w, c->frame_window, 0, 0);
|
2010-07-30 20:54:20 +02:00
|
|
|
xcb_map_window(globalconf.connection, w);
|
2014-03-16 20:15:02 +01:00
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
globalconf.screen->root,
|
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
ROOT_WINDOW_EVENT_MASK);
|
2019-02-27 10:20:22 +01:00
|
|
|
xutil_ungrab_server(globalconf.connection);
|
2011-10-19 15:11:11 +02:00
|
|
|
|
2010-08-09 13:33:08 +02:00
|
|
|
/* Do this now so that we don't get any events for the above
|
|
|
|
* (Else, reparent could cause an UnmapNotify) */
|
|
|
|
xcb_change_window_attributes(globalconf.connection, w, XCB_CW_EVENT_MASK, select_input_val);
|
|
|
|
|
2010-08-01 11:18:37 +02:00
|
|
|
/* The frame window gets the border, not the real client window */
|
|
|
|
xcb_configure_window(globalconf.connection, w,
|
|
|
|
XCB_CONFIG_WINDOW_BORDER_WIDTH,
|
|
|
|
(uint32_t[]) { 0 });
|
|
|
|
|
2010-07-31 10:42:22 +02:00
|
|
|
/* Move this window to the bottom of the stack. Without this we would force
|
|
|
|
* other windows which will be above this one to redraw themselves because
|
|
|
|
* this window occludes them for a tiny moment. The next stack_refresh()
|
|
|
|
* will fix this up and move the window to its correct place. */
|
|
|
|
xcb_configure_window(globalconf.connection, c->frame_window,
|
|
|
|
XCB_CONFIG_WINDOW_STACK_MODE,
|
|
|
|
(uint32_t[]) { XCB_STACK_MODE_BELOW});
|
|
|
|
|
2009-09-22 15:52:06 +02:00
|
|
|
/* Duplicate client and push it in client list */
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_pushvalue(L, -1);
|
|
|
|
client_array_push(&globalconf.clients, luaA_object_ref(L, -1));
|
2009-09-22 15:52:06 +02:00
|
|
|
|
|
|
|
/* Set the right screen */
|
2010-08-16 14:25:12 +02:00
|
|
|
screen_client_moveto(c, screen_getbycoord(wgeom->x, wgeom->y), false);
|
2009-09-22 15:52:06 +02:00
|
|
|
|
|
|
|
/* Store initial geometry and emits signals so we inform that geometry have
|
|
|
|
* been set. */
|
|
|
|
|
2017-02-04 00:38:25 +01:00
|
|
|
c->geometry.x = wgeom->x;
|
|
|
|
c->geometry.y = wgeom->y;
|
|
|
|
c->geometry.width = wgeom->width;
|
|
|
|
c->geometry.height = wgeom->height;
|
|
|
|
|
|
|
|
luaA_object_emit_signal(L, -1, "property::x", 0);
|
|
|
|
luaA_object_emit_signal(L, -1, "property::y", 0);
|
|
|
|
luaA_object_emit_signal(L, -1, "property::width", 0);
|
|
|
|
luaA_object_emit_signal(L, -1, "property::height", 0);
|
|
|
|
luaA_object_emit_signal(L, -1, "property::window", 0);
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_emit_signal(L, -1, "property::geometry", 0);
|
2009-04-09 11:38:56 +02:00
|
|
|
|
2009-10-06 19:04:36 +02:00
|
|
|
/* Set border width */
|
2014-12-06 11:56:58 +01:00
|
|
|
window_set_border_width(L, -1, wgeom->border_width);
|
2007-10-22 16:25:27 +02:00
|
|
|
|
2008-09-03 20:22:33 +02:00
|
|
|
/* we honor size hints by default */
|
2008-12-09 11:38:29 +01:00
|
|
|
c->size_hints_honor = true;
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_emit_signal(L, -1, "property::size_hints_honor", 0);
|
2008-09-03 20:22:33 +02:00
|
|
|
|
2010-08-09 13:54:49 +02:00
|
|
|
/* update all properties */
|
2014-12-06 11:56:58 +01:00
|
|
|
client_update_properties(L, -1, c);
|
2009-08-10 11:59:17 +02:00
|
|
|
|
2016-02-22 21:09:25 +01:00
|
|
|
/* check if this is a TRANSIENT_FOR of another client */
|
|
|
|
foreach(oc, globalconf.clients)
|
|
|
|
if ((*oc)->transient_for_window == w)
|
|
|
|
client_find_transient_for(*oc);
|
|
|
|
|
2017-10-31 15:20:34 +01:00
|
|
|
/* Put the window in normal state. */
|
|
|
|
xwindow_set_state(c->window, XCB_ICCCM_WM_STATE_NORMAL);
|
|
|
|
|
2008-03-15 14:46:45 +01:00
|
|
|
/* Then check clients hints */
|
2008-09-18 13:51:10 +02:00
|
|
|
ewmh_client_check_hints(c);
|
2008-01-13 16:30:43 +01:00
|
|
|
|
2008-05-25 19:47:19 +02:00
|
|
|
/* Push client in stack */
|
2014-03-08 03:35:35 +01:00
|
|
|
stack_client_push(c);
|
2007-10-22 16:25:27 +02:00
|
|
|
|
2014-03-16 20:15:02 +01:00
|
|
|
/* Request our response */
|
|
|
|
xcb_get_property_reply_t *reply =
|
|
|
|
xcb_get_property_reply(globalconf.connection, startup_id_q, NULL);
|
|
|
|
/* Say spawn that a client has been started, with startup id as argument */
|
|
|
|
char *startup_id = xutil_get_text_property_from_reply(reply);
|
|
|
|
p_delete(&reply);
|
2016-05-05 18:29:35 +02:00
|
|
|
|
|
|
|
if (startup_id == NULL && c->leader_window != XCB_NONE) {
|
|
|
|
/* GTK hides this property elsewhere. No idea why. */
|
|
|
|
startup_id_q = xcb_get_property(globalconf.connection, false,
|
|
|
|
c->leader_window, _NET_STARTUP_ID,
|
|
|
|
XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
|
|
|
|
reply = xcb_get_property_reply(globalconf.connection, startup_id_q, NULL);
|
|
|
|
startup_id = xutil_get_text_property_from_reply(reply);
|
|
|
|
p_delete(&reply);
|
|
|
|
}
|
|
|
|
c->startup_id = startup_id;
|
|
|
|
|
2014-03-16 20:15:02 +01:00
|
|
|
spawn_start_notify(c, startup_id);
|
2009-04-03 16:30:18 +02:00
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_class_emit_signal(L, &client_class, "list", 0);
|
2009-07-11 09:02:25 +02:00
|
|
|
|
2019-11-10 07:12:43 +01:00
|
|
|
/* Add the context */
|
|
|
|
if (globalconf.loop == NULL)
|
|
|
|
lua_pushstring(L, "startup");
|
|
|
|
else
|
|
|
|
lua_pushstring(L, "new");
|
|
|
|
|
|
|
|
/* Hints */
|
|
|
|
lua_newtable(L);
|
|
|
|
|
2014-03-16 20:15:02 +01:00
|
|
|
/* client is still on top of the stack; emit signal */
|
2019-11-10 07:12:43 +01:00
|
|
|
luaA_object_emit_signal(L, -3, "request::manage", 2);
|
|
|
|
|
|
|
|
/*TODO v6: remove this*/
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_emit_signal(L, -1, "manage", 0);
|
2018-07-22 22:05:11 +02:00
|
|
|
|
|
|
|
xcb_generic_error_t *error = xcb_request_check(globalconf.connection, reparent_cookie);
|
|
|
|
if (error != NULL) {
|
|
|
|
warn("Failed to manage window with name '%s', class '%s', instance '%s', because reparenting failed.",
|
|
|
|
NONULL(c->name), NONULL(c->class), NONULL(c->instance));
|
|
|
|
event_handle((xcb_generic_event_t *) error);
|
|
|
|
p_delete(&error);
|
2019-11-10 07:12:43 +01:00
|
|
|
client_unmanage(c, CLIENT_UNMANAGE_FAILED);
|
2018-07-22 22:05:11 +02:00
|
|
|
}
|
|
|
|
|
2009-10-13 15:41:31 +02:00
|
|
|
/* pop client */
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_pop(L, 1);
|
2007-09-05 20:15:00 +02:00
|
|
|
}
|
|
|
|
|
2013-03-10 12:13:32 +01:00
|
|
|
static void
|
|
|
|
client_remove_titlebar_geometry(client_t *c, area_t *geometry)
|
|
|
|
{
|
|
|
|
geometry->x += c->titlebar[CLIENT_TITLEBAR_LEFT].size;
|
|
|
|
geometry->y += c->titlebar[CLIENT_TITLEBAR_TOP].size;
|
|
|
|
geometry->width -= c->titlebar[CLIENT_TITLEBAR_LEFT].size;
|
|
|
|
geometry->width -= c->titlebar[CLIENT_TITLEBAR_RIGHT].size;
|
|
|
|
geometry->height -= c->titlebar[CLIENT_TITLEBAR_TOP].size;
|
|
|
|
geometry->height -= c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
client_add_titlebar_geometry(client_t *c, area_t *geometry)
|
|
|
|
{
|
|
|
|
geometry->x -= c->titlebar[CLIENT_TITLEBAR_LEFT].size;
|
|
|
|
geometry->y -= c->titlebar[CLIENT_TITLEBAR_TOP].size;
|
|
|
|
geometry->width += c->titlebar[CLIENT_TITLEBAR_LEFT].size;
|
|
|
|
geometry->width += c->titlebar[CLIENT_TITLEBAR_RIGHT].size;
|
|
|
|
geometry->height += c->titlebar[CLIENT_TITLEBAR_TOP].size;
|
|
|
|
geometry->height += c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
|
|
|
|
}
|
|
|
|
|
2012-10-29 10:20:03 +01:00
|
|
|
/** Send a synthetic configure event to a window.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
client_send_configure(client_t *c)
|
|
|
|
{
|
|
|
|
area_t geometry = c->geometry;
|
|
|
|
|
2013-03-13 21:20:13 +01:00
|
|
|
if (!c->fullscreen)
|
|
|
|
client_remove_titlebar_geometry(c, &geometry);
|
2012-10-29 10:20:03 +01:00
|
|
|
xwindow_configure(c->window, geometry, c->border_width);
|
|
|
|
}
|
|
|
|
|
2013-03-10 12:13:32 +01:00
|
|
|
/** Apply size hints to the client's new geometry.
|
|
|
|
*/
|
|
|
|
static area_t
|
|
|
|
client_apply_size_hints(client_t *c, area_t geometry)
|
|
|
|
{
|
|
|
|
int32_t minw = 0, minh = 0;
|
|
|
|
int32_t basew = 0, baseh = 0, real_basew = 0, real_baseh = 0;
|
|
|
|
|
|
|
|
if (c->fullscreen)
|
|
|
|
return geometry;
|
|
|
|
|
|
|
|
/* Size hints are applied to the window without any decoration */
|
|
|
|
client_remove_titlebar_geometry(c, &geometry);
|
|
|
|
|
2015-10-10 21:13:40 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE)
|
2013-03-10 12:13:32 +01:00
|
|
|
{
|
|
|
|
basew = c->size_hints.base_width;
|
|
|
|
baseh = c->size_hints.base_height;
|
|
|
|
real_basew = basew;
|
|
|
|
real_baseh = baseh;
|
|
|
|
}
|
|
|
|
else if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
|
|
|
|
{
|
|
|
|
/* base size is substituted with min size if not specified */
|
|
|
|
basew = c->size_hints.min_width;
|
|
|
|
baseh = c->size_hints.min_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
|
|
|
|
{
|
|
|
|
minw = c->size_hints.min_width;
|
|
|
|
minh = c->size_hints.min_height;
|
|
|
|
}
|
2015-10-10 21:13:40 +02:00
|
|
|
else if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE)
|
2013-03-10 12:13:32 +01:00
|
|
|
{
|
|
|
|
/* min size is substituted with base size if not specified */
|
|
|
|
minw = c->size_hints.base_width;
|
|
|
|
minh = c->size_hints.base_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle the size aspect ratio */
|
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT
|
|
|
|
&& c->size_hints.min_aspect_den > 0
|
|
|
|
&& c->size_hints.max_aspect_den > 0
|
|
|
|
&& geometry.height > real_baseh
|
|
|
|
&& geometry.width > real_basew)
|
|
|
|
{
|
|
|
|
/* ICCCM mandates:
|
|
|
|
* If a base size is provided along with the aspect ratio fields, the base size should be subtracted from the
|
|
|
|
* window size prior to checking that the aspect ratio falls in range. If a base size is not provided, nothing
|
|
|
|
* should be subtracted from the window size. (The minimum size is not to be used in place of the base size for
|
|
|
|
* this purpose.)
|
|
|
|
*/
|
|
|
|
double dx = geometry.width - real_basew;
|
|
|
|
double dy = geometry.height - real_baseh;
|
|
|
|
double ratio = dx / dy;
|
|
|
|
double min = c->size_hints.min_aspect_num / (double) c->size_hints.min_aspect_den;
|
|
|
|
double max = c->size_hints.max_aspect_num / (double) c->size_hints.max_aspect_den;
|
|
|
|
|
|
|
|
if(max > 0 && min > 0 && ratio > 0)
|
|
|
|
{
|
|
|
|
if(ratio < min)
|
|
|
|
{
|
|
|
|
/* dx is lower than allowed, make dy lower to compensate this (+ 0.5 to force proper rounding). */
|
|
|
|
dy = dx / min + 0.5;
|
|
|
|
geometry.width = dx + real_basew;
|
|
|
|
geometry.height = dy + real_baseh;
|
|
|
|
} else if(ratio > max)
|
|
|
|
{
|
|
|
|
/* dx is too high, lower it (+0.5 for proper rounding) */
|
|
|
|
dx = dy * max + 0.5;
|
|
|
|
geometry.width = dx + real_basew;
|
|
|
|
geometry.height = dy + real_baseh;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle the minimum size */
|
|
|
|
geometry.width = MAX(geometry.width, minw);
|
|
|
|
geometry.height = MAX(geometry.height, minh);
|
|
|
|
|
|
|
|
/* Handle the maximum size */
|
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
|
|
|
|
{
|
|
|
|
if(c->size_hints.max_width)
|
|
|
|
geometry.width = MIN(geometry.width, c->size_hints.max_width);
|
|
|
|
if(c->size_hints.max_height)
|
|
|
|
geometry.height = MIN(geometry.height, c->size_hints.max_height);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle the size increment */
|
|
|
|
if(c->size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_RESIZE_INC | XCB_ICCCM_SIZE_HINT_BASE_SIZE)
|
|
|
|
&& c->size_hints.width_inc && c->size_hints.height_inc)
|
|
|
|
{
|
|
|
|
uint16_t t1 = geometry.width, t2 = geometry.height;
|
|
|
|
unsigned_subtract(t1, basew);
|
|
|
|
unsigned_subtract(t2, baseh);
|
|
|
|
geometry.width -= t1 % c->size_hints.width_inc;
|
|
|
|
geometry.height -= t2 % c->size_hints.height_inc;
|
|
|
|
}
|
|
|
|
|
|
|
|
client_add_titlebar_geometry(c, &geometry);
|
|
|
|
return geometry;
|
|
|
|
}
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
static void
|
2016-09-15 18:28:46 +02:00
|
|
|
client_resize_do(client_t *c, area_t geometry)
|
2012-10-14 17:20:24 +02:00
|
|
|
{
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
2015-07-21 15:16:37 +02:00
|
|
|
|
|
|
|
screen_t *new_screen = c->screen;
|
2016-09-02 21:15:00 +02:00
|
|
|
if(!screen_area_in_screen(new_screen, geometry))
|
2015-07-21 15:16:37 +02:00
|
|
|
new_screen = screen_getbycoord(geometry.x, geometry.y);
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
/* Also store geometry including border */
|
|
|
|
area_t old_geometry = c->geometry;
|
|
|
|
c->geometry = geometry;
|
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_push(L, c);
|
2015-10-14 19:40:18 +02:00
|
|
|
if (!AREA_EQUAL(old_geometry, geometry))
|
|
|
|
luaA_object_emit_signal(L, -1, "property::geometry", 0);
|
2017-01-30 13:42:54 +01:00
|
|
|
if (old_geometry.x != geometry.x || old_geometry.y != geometry.y)
|
|
|
|
{
|
|
|
|
luaA_object_emit_signal(L, -1, "property::position", 0);
|
|
|
|
if (old_geometry.x != geometry.x)
|
|
|
|
luaA_object_emit_signal(L, -1, "property::x", 0);
|
|
|
|
else
|
|
|
|
luaA_object_emit_signal(L, -1, "property::y", 0);
|
|
|
|
}
|
|
|
|
if (old_geometry.width != geometry.width || old_geometry.height != geometry.height)
|
|
|
|
{
|
|
|
|
luaA_object_emit_signal(L, -1, "property::size", 0);
|
|
|
|
if (old_geometry.width != geometry.width)
|
|
|
|
luaA_object_emit_signal(L, -1, "property::width", 0);
|
|
|
|
else
|
|
|
|
luaA_object_emit_signal(L, -1, "property::height", 0);
|
|
|
|
}
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_pop(L, 1);
|
2012-10-14 17:20:24 +02:00
|
|
|
|
2014-03-06 18:08:21 +01:00
|
|
|
screen_client_moveto(c, new_screen, false);
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
/* Update all titlebars */
|
|
|
|
for (client_titlebar_t bar = CLIENT_TITLEBAR_TOP; bar < CLIENT_TITLEBAR_COUNT; bar++) {
|
|
|
|
if (c->titlebar[bar].drawable == NULL && c->titlebar[bar].size == 0)
|
|
|
|
continue;
|
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_push(L, c);
|
|
|
|
drawable_t *drawable = titlebar_get_drawable(L, c, -1, bar);
|
|
|
|
luaA_object_push_item(L, -1, drawable);
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
area_t area = titlebar_get_area(c, bar);
|
|
|
|
|
|
|
|
/* Convert to global coordinates */
|
|
|
|
area.x += geometry.x;
|
|
|
|
area.y += geometry.y;
|
2016-09-15 18:29:56 +02:00
|
|
|
if (c->fullscreen)
|
2012-11-03 19:34:16 +01:00
|
|
|
area.width = area.height = 0;
|
2014-12-06 11:56:58 +01:00
|
|
|
drawable_set_geometry(L, -1, area);
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
/* Pop the client and the drawable */
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_pop(L, 2);
|
2012-10-14 17:20:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-27 20:17:33 +02:00
|
|
|
/** Resize client window.
|
2009-09-17 15:05:53 +02:00
|
|
|
* The sizes given as parameters are with borders!
|
2008-05-27 20:17:33 +02:00
|
|
|
* \param c Client to resize.
|
|
|
|
* \param geometry New window geometry.
|
2013-03-10 12:13:32 +01:00
|
|
|
* \param honor_hints Use size hints.
|
2009-02-08 13:48:17 +01:00
|
|
|
* \return true if an actual resize occurred.
|
2007-12-30 15:08:38 +01:00
|
|
|
*/
|
2009-02-08 13:48:17 +01:00
|
|
|
bool
|
2013-03-10 12:13:32 +01:00
|
|
|
client_resize(client_t *c, area_t geometry, bool honor_hints)
|
2007-09-05 20:15:00 +02:00
|
|
|
{
|
2017-01-08 15:23:18 +01:00
|
|
|
if (honor_hints) {
|
|
|
|
/* We could get integer underflows in client_remove_titlebar_geometry()
|
|
|
|
* without these checks here.
|
|
|
|
*/
|
|
|
|
if(geometry.width < c->titlebar[CLIENT_TITLEBAR_LEFT].size + c->titlebar[CLIENT_TITLEBAR_RIGHT].size)
|
|
|
|
return false;
|
|
|
|
if(geometry.height < c->titlebar[CLIENT_TITLEBAR_TOP].size + c->titlebar[CLIENT_TITLEBAR_BOTTOM].size)
|
|
|
|
return false;
|
|
|
|
geometry = client_apply_size_hints(c, geometry);
|
|
|
|
}
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
if(geometry.width < c->titlebar[CLIENT_TITLEBAR_LEFT].size + c->titlebar[CLIENT_TITLEBAR_RIGHT].size)
|
|
|
|
return false;
|
|
|
|
if(geometry.height < c->titlebar[CLIENT_TITLEBAR_TOP].size + c->titlebar[CLIENT_TITLEBAR_BOTTOM].size)
|
|
|
|
return false;
|
|
|
|
|
2009-10-07 14:24:39 +02:00
|
|
|
if(geometry.width == 0 || geometry.height == 0)
|
2009-02-08 13:48:17 +01:00
|
|
|
return false;
|
2008-12-12 00:01:43 +01:00
|
|
|
|
2016-09-15 18:36:34 +02:00
|
|
|
if(!AREA_EQUAL(c->geometry, geometry))
|
2007-09-05 20:15:00 +02:00
|
|
|
{
|
2016-09-15 18:28:46 +02:00
|
|
|
client_resize_do(c, geometry);
|
2009-09-08 16:09:47 +02:00
|
|
|
|
2009-02-08 13:48:17 +01:00
|
|
|
return true;
|
2008-09-24 12:13:21 +02:00
|
|
|
}
|
2009-02-08 13:48:17 +01:00
|
|
|
|
|
|
|
return false;
|
2007-09-05 20:15:00 +02:00
|
|
|
}
|
|
|
|
|
2008-09-18 13:51:10 +02:00
|
|
|
/** Set a client minimized, or not.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
2008-09-18 13:51:10 +02:00
|
|
|
* \param s Set or not the client minimized.
|
|
|
|
*/
|
|
|
|
void
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_minimized(lua_State *L, int cidx, bool s)
|
2008-09-18 13:51:10 +02:00
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
2009-08-17 17:02:45 +02:00
|
|
|
|
|
|
|
if(c->minimized != s)
|
2008-09-18 13:51:10 +02:00
|
|
|
{
|
2009-08-17 17:02:45 +02:00
|
|
|
c->minimized = s;
|
2011-03-27 19:57:06 +02:00
|
|
|
banning_need_update();
|
2009-06-05 14:59:51 +02:00
|
|
|
if(s)
|
2015-01-10 00:04:11 +01:00
|
|
|
{
|
|
|
|
/* ICCCM: To transition from ICONIC to NORMAL state, the client
|
|
|
|
* should just map the window. Thus, iconic clients need to be
|
|
|
|
* unmapped, else the MapWindow request doesn't have any effect.
|
|
|
|
*/
|
2011-04-27 06:49:56 +02:00
|
|
|
xwindow_set_state(c->window, XCB_ICCCM_WM_STATE_ICONIC);
|
2015-01-10 00:04:11 +01:00
|
|
|
|
|
|
|
uint32_t no_event[] = { 0 };
|
2015-02-25 21:52:03 +01:00
|
|
|
const uint32_t client_select_input_val[] = { CLIENT_SELECT_INPUT_EVENT_MASK };
|
|
|
|
const uint32_t frame_select_input_val[] = { FRAME_SELECT_INPUT_EVENT_MASK };
|
2015-01-10 00:04:11 +01:00
|
|
|
xcb_grab_server(globalconf.connection);
|
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
globalconf.screen->root,
|
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
no_event);
|
2015-02-25 21:52:03 +01:00
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
c->frame_window,
|
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
no_event);
|
2015-01-10 00:04:11 +01:00
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
c->window,
|
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
no_event);
|
|
|
|
xcb_unmap_window(globalconf.connection, c->window);
|
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
globalconf.screen->root,
|
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
ROOT_WINDOW_EVENT_MASK);
|
2015-02-25 21:52:03 +01:00
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
c->frame_window,
|
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
frame_select_input_val);
|
2015-01-10 00:04:11 +01:00
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
c->window,
|
|
|
|
XCB_CW_EVENT_MASK,
|
2015-02-25 21:52:03 +01:00
|
|
|
client_select_input_val);
|
2019-02-27 10:20:22 +01:00
|
|
|
xutil_ungrab_server(globalconf.connection);
|
2015-01-10 00:04:11 +01:00
|
|
|
}
|
2009-06-05 14:59:51 +02:00
|
|
|
else
|
2015-01-10 00:04:11 +01:00
|
|
|
{
|
2011-04-27 06:49:56 +02:00
|
|
|
xwindow_set_state(c->window, XCB_ICCCM_WM_STATE_NORMAL);
|
2015-01-10 00:04:11 +01:00
|
|
|
xcb_map_window(globalconf.connection, c->window);
|
|
|
|
}
|
2009-08-28 17:49:28 +02:00
|
|
|
if(strut_has_value(&c->strut))
|
2016-05-08 16:30:37 +02:00
|
|
|
screen_update_workarea(c->screen);
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::minimized", 0);
|
2008-09-18 13:51:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-05 10:13:37 +02:00
|
|
|
/** Set a client hidden, or not.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
|
|
|
* \param s Set or not the client hidden.
|
|
|
|
*/
|
2012-05-05 12:13:08 +02:00
|
|
|
static void
|
2012-05-05 10:13:37 +02:00
|
|
|
client_set_hidden(lua_State *L, int cidx, bool s)
|
|
|
|
{
|
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
|
|
|
|
|
|
|
if(c->hidden != s)
|
|
|
|
{
|
|
|
|
c->hidden = s;
|
|
|
|
banning_need_update();
|
|
|
|
if(strut_has_value(&c->strut))
|
2016-05-08 16:30:37 +02:00
|
|
|
screen_update_workarea(c->screen);
|
2012-05-05 10:13:37 +02:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::hidden", 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-21 16:29:53 +02:00
|
|
|
/** Set a client sticky, or not.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
2008-08-21 16:29:53 +02:00
|
|
|
* \param s Set or not the client sticky.
|
|
|
|
*/
|
|
|
|
void
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_sticky(lua_State *L, int cidx, bool s)
|
2008-08-21 16:29:53 +02:00
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
2009-08-17 17:02:45 +02:00
|
|
|
|
|
|
|
if(c->sticky != s)
|
2008-08-21 16:29:53 +02:00
|
|
|
{
|
2009-08-17 17:02:45 +02:00
|
|
|
c->sticky = s;
|
2011-03-27 19:57:06 +02:00
|
|
|
banning_need_update();
|
2019-02-16 19:51:29 +01:00
|
|
|
ewmh_client_update_desktop(c);
|
2016-12-31 15:31:41 +01:00
|
|
|
if(strut_has_value(&c->strut))
|
|
|
|
screen_update_workarea(c->screen);
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::sticky", 0);
|
2008-08-21 16:29:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-21 17:30:51 +02:00
|
|
|
/** Set a client focusable, or not.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
|
|
|
* \param s Set or not the client's focusable property.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
client_set_focusable(lua_State *L, int cidx, bool s)
|
|
|
|
{
|
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
|
|
|
|
|
|
|
if(c->focusable != s || !c->focusable_set)
|
|
|
|
{
|
|
|
|
c->focusable = s;
|
|
|
|
c->focusable_set = true;
|
|
|
|
luaA_object_emit_signal(L, cidx, "property::focusable", 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-12 11:00:19 +02:00
|
|
|
/** Unset a client's focusable property and make it use the default again.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
client_unset_focusable(lua_State *L, int cidx)
|
|
|
|
{
|
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
|
|
|
|
|
|
|
if(c->focusable_set)
|
|
|
|
{
|
|
|
|
c->focusable_set = false;
|
|
|
|
luaA_object_emit_signal(L, cidx, "property::focusable", 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-21 17:52:44 +02:00
|
|
|
/** Set a client fullscreen, or not.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
2008-08-21 17:52:44 +02:00
|
|
|
* \param s Set or not the client fullscreen.
|
|
|
|
*/
|
|
|
|
void
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_fullscreen(lua_State *L, int cidx, bool s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
2009-08-17 17:02:45 +02:00
|
|
|
|
|
|
|
if(c->fullscreen != s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
|
|
|
/* become fullscreen! */
|
2009-08-20 10:37:21 +02:00
|
|
|
if(s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-02-08 23:55:30 +01:00
|
|
|
/* You can only be part of one of the special layers. */
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_below(L, cidx, false);
|
|
|
|
client_set_above(L, cidx, false);
|
|
|
|
client_set_ontop(L, cidx, false);
|
2008-08-21 17:52:44 +02:00
|
|
|
}
|
2009-10-05 17:13:29 +02:00
|
|
|
int abs_cidx = luaA_absindex(L, cidx); \
|
2016-04-15 11:09:41 +02:00
|
|
|
lua_pushstring(L, "fullscreen");
|
2009-10-05 17:13:29 +02:00
|
|
|
c->fullscreen = s;
|
2016-04-15 11:09:41 +02:00
|
|
|
luaA_object_emit_signal(L, abs_cidx, "request::geometry", 1);
|
2009-10-05 17:13:29 +02:00
|
|
|
luaA_object_emit_signal(L, abs_cidx, "property::fullscreen", 0);
|
2012-11-09 18:50:38 +01:00
|
|
|
/* Force a client resize, so that titlebars get shown/hidden */
|
2016-09-15 18:28:46 +02:00
|
|
|
client_resize_do(c, c->geometry);
|
2010-06-18 11:08:06 +02:00
|
|
|
stack_windows();
|
2008-08-21 17:52:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-05 17:13:29 +02:00
|
|
|
/** Set a client horizontally|vertically maximized.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
2008-11-25 11:54:38 +01:00
|
|
|
* \param s The maximized status.
|
|
|
|
*/
|
2017-01-24 10:19:45 +01:00
|
|
|
void
|
|
|
|
client_set_maximized_common(lua_State *L, int cidx, bool s, const char* type, const int val)
|
|
|
|
{
|
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
|
|
|
|
|
|
|
/* Store the current and next state on 2 bit */
|
|
|
|
const client_maximized_t current = (
|
2017-01-24 11:41:22 +01:00
|
|
|
(c->maximized_vertical ? CLIENT_MAXIMIZED_V : CLIENT_MAXIMIZED_NONE)|
|
|
|
|
(c->maximized_horizontal ? CLIENT_MAXIMIZED_H : CLIENT_MAXIMIZED_NONE)|
|
|
|
|
(c->maximized ? CLIENT_MAXIMIZED_BOTH : CLIENT_MAXIMIZED_NONE)
|
2017-01-24 10:19:45 +01:00
|
|
|
);
|
2017-01-24 11:41:22 +01:00
|
|
|
client_maximized_t next = s ? (val | current) : (current & (~val));
|
|
|
|
|
|
|
|
/* When both are already set during startup, assume `maximized` is true*/
|
|
|
|
if (next == (CLIENT_MAXIMIZED_H|CLIENT_MAXIMIZED_V) && !globalconf.loop)
|
|
|
|
next = CLIENT_MAXIMIZED_BOTH;
|
2017-01-24 10:19:45 +01:00
|
|
|
|
|
|
|
if(current != next)
|
|
|
|
{
|
|
|
|
int abs_cidx = luaA_absindex(L, cidx);
|
2017-01-24 11:41:22 +01:00
|
|
|
int max_before = c->maximized;
|
2017-01-24 10:19:45 +01:00
|
|
|
int h_before = c->maximized_horizontal;
|
|
|
|
int v_before = c->maximized_vertical;
|
|
|
|
|
|
|
|
/*Update the client properties */
|
2017-01-24 11:41:22 +01:00
|
|
|
c->maximized_horizontal = !!(next & CLIENT_MAXIMIZED_H );
|
|
|
|
c->maximized_vertical = !!(next & CLIENT_MAXIMIZED_V );
|
|
|
|
c->maximized = !!(next & CLIENT_MAXIMIZED_BOTH);
|
|
|
|
|
2017-01-24 10:19:45 +01:00
|
|
|
|
|
|
|
/* Request the changes to be applied */
|
|
|
|
lua_pushstring(L, type);
|
|
|
|
luaA_object_emit_signal(L, abs_cidx, "request::geometry", 1);
|
|
|
|
|
|
|
|
/* Notify changes in the relevant properties */
|
|
|
|
if (h_before != c->maximized_horizontal)
|
|
|
|
luaA_object_emit_signal(L, abs_cidx, "property::maximized_horizontal", 0);
|
|
|
|
if (v_before != c->maximized_vertical)
|
|
|
|
luaA_object_emit_signal(L, abs_cidx, "property::maximized_vertical", 0);
|
2017-01-24 11:41:22 +01:00
|
|
|
if(max_before != c->maximized)
|
2017-01-24 10:19:45 +01:00
|
|
|
luaA_object_emit_signal(L, abs_cidx, "property::maximized", 0);
|
|
|
|
|
|
|
|
stack_windows();
|
2008-11-25 11:54:38 +01:00
|
|
|
}
|
2017-01-24 10:19:45 +01:00
|
|
|
}
|
2008-11-25 11:54:38 +01:00
|
|
|
|
2014-03-14 04:14:03 +01:00
|
|
|
void
|
|
|
|
client_set_maximized(lua_State *L, int cidx, bool s)
|
|
|
|
{
|
2017-01-24 10:19:45 +01:00
|
|
|
return client_set_maximized_common(
|
2017-01-24 11:41:22 +01:00
|
|
|
L, cidx, s, "maximized", CLIENT_MAXIMIZED_BOTH
|
2017-01-24 10:19:45 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
client_set_maximized_horizontal(lua_State *L, int cidx, bool s)
|
|
|
|
{
|
|
|
|
return client_set_maximized_common(
|
|
|
|
L, cidx, s, "maximized_horizontal", CLIENT_MAXIMIZED_H
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
client_set_maximized_vertical(lua_State *L, int cidx, bool s)
|
|
|
|
{
|
|
|
|
return client_set_maximized_common(
|
|
|
|
L, cidx, s, "maximized_vertical", CLIENT_MAXIMIZED_V
|
|
|
|
);
|
2014-03-14 04:14:03 +01:00
|
|
|
}
|
|
|
|
|
2008-08-21 17:52:44 +02:00
|
|
|
/** Set a client above, or not.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
2008-08-21 17:52:44 +02:00
|
|
|
* \param s Set or not the client above.
|
|
|
|
*/
|
|
|
|
void
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_above(lua_State *L, int cidx, bool s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
2009-08-17 17:02:45 +02:00
|
|
|
|
|
|
|
if(c->above != s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-02-08 23:55:30 +01:00
|
|
|
/* You can only be part of one of the special layers. */
|
|
|
|
if(s)
|
|
|
|
{
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_below(L, cidx, false);
|
|
|
|
client_set_ontop(L, cidx, false);
|
|
|
|
client_set_fullscreen(L, cidx, false);
|
2009-02-08 23:55:30 +01:00
|
|
|
}
|
2009-08-17 17:02:45 +02:00
|
|
|
c->above = s;
|
2009-10-05 20:36:02 +02:00
|
|
|
stack_windows();
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::above", 0);
|
2008-08-21 17:52:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Set a client below, or not.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
2008-08-21 17:52:44 +02:00
|
|
|
* \param s Set or not the client below.
|
|
|
|
*/
|
|
|
|
void
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_below(lua_State *L, int cidx, bool s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
2009-08-17 17:02:45 +02:00
|
|
|
|
|
|
|
if(c->below != s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-02-08 23:55:30 +01:00
|
|
|
/* You can only be part of one of the special layers. */
|
|
|
|
if(s)
|
|
|
|
{
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_above(L, cidx, false);
|
|
|
|
client_set_ontop(L, cidx, false);
|
|
|
|
client_set_fullscreen(L, cidx, false);
|
2009-02-08 23:55:30 +01:00
|
|
|
}
|
2009-08-17 17:02:45 +02:00
|
|
|
c->below = s;
|
2009-10-05 20:36:02 +02:00
|
|
|
stack_windows();
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::below", 0);
|
2008-08-21 17:52:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Set a client modal, or not.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
|
|
|
* \param s Set or not the client modal attribute.
|
2008-08-21 17:52:44 +02:00
|
|
|
*/
|
|
|
|
void
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_modal(lua_State *L, int cidx, bool s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
2009-08-17 17:02:45 +02:00
|
|
|
|
|
|
|
if(c->modal != s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-08-17 17:02:45 +02:00
|
|
|
c->modal = s;
|
2009-10-05 20:36:02 +02:00
|
|
|
stack_windows();
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::modal", 0);
|
2008-08-21 17:52:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Set a client ontop, or not.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index.
|
|
|
|
* \param s Set or not the client ontop attribute.
|
2008-08-21 17:52:44 +02:00
|
|
|
*/
|
|
|
|
void
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_ontop(lua_State *L, int cidx, bool s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, cidx, &client_class);
|
2009-08-17 17:02:45 +02:00
|
|
|
|
|
|
|
if(c->ontop != s)
|
2008-08-21 17:52:44 +02:00
|
|
|
{
|
2009-02-08 23:55:30 +01:00
|
|
|
/* You can only be part of one of the special layers. */
|
|
|
|
if(s)
|
|
|
|
{
|
2009-08-17 17:02:45 +02:00
|
|
|
client_set_above(L, cidx, false);
|
|
|
|
client_set_below(L, cidx, false);
|
|
|
|
client_set_fullscreen(L, cidx, false);
|
2009-02-08 23:55:30 +01:00
|
|
|
}
|
2009-08-17 17:02:45 +02:00
|
|
|
c->ontop = s;
|
2009-10-05 20:36:02 +02:00
|
|
|
stack_windows();
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_object_emit_signal(L, cidx, "property::ontop", 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-20 22:11:13 +01:00
|
|
|
/** Unban a client and move it back into the viewport.
|
2008-06-10 19:29:53 +02:00
|
|
|
* \param c The client.
|
|
|
|
*/
|
2007-09-05 20:15:00 +02:00
|
|
|
void
|
2008-04-11 11:35:11 +02:00
|
|
|
client_unban(client_t *c)
|
2007-09-05 20:15:00 +02:00
|
|
|
{
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
2008-11-20 22:11:13 +01:00
|
|
|
if(c->isbanned)
|
2008-09-20 11:28:50 +02:00
|
|
|
{
|
2017-04-21 18:53:25 +02:00
|
|
|
client_ignore_enterleave_events();
|
2010-07-30 20:54:20 +02:00
|
|
|
xcb_map_window(globalconf.connection, c->frame_window);
|
2017-04-21 18:53:25 +02:00
|
|
|
client_restore_enterleave_events();
|
2008-11-20 22:11:13 +01:00
|
|
|
|
|
|
|
c->isbanned = false;
|
2010-08-12 14:32:27 +02:00
|
|
|
|
2012-05-05 10:13:37 +02:00
|
|
|
/* An unbanned client shouldn't be minimized or hidden */
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_push(L, c);
|
|
|
|
client_set_minimized(L, -1, false);
|
|
|
|
client_set_hidden(L, -1, false);
|
|
|
|
lua_pop(L, 1);
|
2015-02-14 14:09:10 +01:00
|
|
|
|
|
|
|
if (globalconf.focus.client == c)
|
|
|
|
globalconf.focus.need_update = true;
|
2008-09-20 11:28:50 +02:00
|
|
|
}
|
2007-09-05 20:15:00 +02:00
|
|
|
}
|
|
|
|
|
2008-06-10 19:29:53 +02:00
|
|
|
/** Unmanage a client.
|
|
|
|
* \param c The client.
|
2019-11-10 07:12:43 +01:00
|
|
|
* \param reason Why was the unmanage done.
|
2008-06-10 19:29:53 +02:00
|
|
|
*/
|
2007-09-05 20:15:00 +02:00
|
|
|
void
|
2019-11-10 07:12:43 +01:00
|
|
|
client_unmanage(client_t *c, client_unmanage_t reason)
|
2007-09-05 20:15:00 +02:00
|
|
|
{
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
|
|
|
|
2014-03-15 02:56:58 +01:00
|
|
|
/* Reset transient_for attributes of windows that might be referring to us */
|
2009-04-09 17:17:10 +02:00
|
|
|
foreach(_tc, globalconf.clients)
|
|
|
|
{
|
|
|
|
client_t *tc = *_tc;
|
2009-02-08 14:00:30 +01:00
|
|
|
if(tc->transient_for == c)
|
|
|
|
tc->transient_for = NULL;
|
2009-04-09 17:17:10 +02:00
|
|
|
}
|
2009-02-08 14:00:30 +01:00
|
|
|
|
2010-10-10 14:46:56 +02:00
|
|
|
if(globalconf.focus.client == c)
|
2008-07-31 17:29:05 +02:00
|
|
|
client_unfocus(c);
|
|
|
|
|
2009-04-10 15:23:55 +02:00
|
|
|
/* remove client from global list and everywhere else */
|
|
|
|
foreach(elem, globalconf.clients)
|
|
|
|
if(*elem == c)
|
2009-04-09 17:17:10 +02:00
|
|
|
{
|
2009-04-10 15:23:55 +02:00
|
|
|
client_array_remove(&globalconf.clients, elem);
|
2009-04-09 17:17:10 +02:00
|
|
|
break;
|
|
|
|
}
|
2009-04-17 23:26:26 +02:00
|
|
|
stack_client_remove(c);
|
2012-10-20 17:33:32 +02:00
|
|
|
for(int i = 0; i < globalconf.tags.len; i++)
|
|
|
|
untag_client(c, globalconf.tags.tab[i]);
|
2008-10-20 15:01:16 +02:00
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_push(L, c);
|
2019-11-10 07:12:43 +01:00
|
|
|
|
|
|
|
/* Give the context to Lua */
|
|
|
|
switch (reason)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
case CLIENT_UNMANAGE_USER:
|
|
|
|
lua_pushstring(L, "user");
|
|
|
|
break;
|
|
|
|
case CLIENT_UNMANAGE_REPARENT:
|
|
|
|
lua_pushstring(L, "reparented");
|
|
|
|
break;
|
|
|
|
case CLIENT_UNMANAGE_UNMAP:
|
|
|
|
case CLIENT_UNMANAGE_FAILED:
|
|
|
|
case CLIENT_UNMANAGE_DESTROYED:
|
|
|
|
lua_pushstring(L, "destroyed");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Hints */
|
|
|
|
lua_newtable(L);
|
|
|
|
|
|
|
|
luaA_object_emit_signal(L, -3, "request::unmanage", 2);
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_emit_signal(L, -1, "unmanage", 0);
|
|
|
|
lua_pop(L, 1);
|
2009-07-10 14:38:37 +02:00
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_class_emit_signal(L, &client_class, "list", 0);
|
2009-07-11 09:02:25 +02:00
|
|
|
|
2009-08-28 17:49:28 +02:00
|
|
|
if(strut_has_value(&c->strut))
|
2016-05-08 16:30:37 +02:00
|
|
|
screen_update_workarea(c->screen);
|
2009-08-28 17:49:28 +02:00
|
|
|
|
2012-10-23 10:17:29 +02:00
|
|
|
/* Get rid of all titlebars */
|
|
|
|
for (client_titlebar_t bar = CLIENT_TITLEBAR_TOP; bar < CLIENT_TITLEBAR_COUNT; bar++) {
|
|
|
|
if (c->titlebar[bar].drawable == NULL)
|
|
|
|
continue;
|
|
|
|
|
2016-01-10 19:27:55 +01:00
|
|
|
if (globalconf.drawable_under_mouse == c->titlebar[bar].drawable) {
|
|
|
|
/* Leave drawable before we invalidate the client */
|
|
|
|
lua_pushnil(L);
|
|
|
|
event_drawable_under_mouse(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
|
2014-03-16 17:08:36 +01:00
|
|
|
/* Forget about the drawable */
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_push(L, c);
|
|
|
|
luaA_object_unref_item(L, -1, c->titlebar[bar].drawable);
|
2012-10-23 10:17:29 +02:00
|
|
|
c->titlebar[bar].drawable = NULL;
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_pop(L, 1);
|
2012-10-23 10:17:29 +02:00
|
|
|
}
|
|
|
|
|
2010-08-09 11:51:49 +02:00
|
|
|
/* Clear our event mask so that we don't receive any events from now on,
|
|
|
|
* especially not for the following requests. */
|
2019-11-10 07:12:43 +01:00
|
|
|
if(reason != CLIENT_UNMANAGE_DESTROYED)
|
2010-10-07 12:05:43 +02:00
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
c->window,
|
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
(const uint32_t []) { 0 });
|
2010-08-11 11:57:44 +02:00
|
|
|
xcb_change_window_attributes(globalconf.connection,
|
|
|
|
c->frame_window,
|
|
|
|
XCB_CW_EVENT_MASK,
|
|
|
|
(const uint32_t []) { 0 });
|
2010-08-09 11:51:49 +02:00
|
|
|
|
2019-11-10 07:12:43 +01:00
|
|
|
if(reason != CLIENT_UNMANAGE_DESTROYED)
|
2010-10-07 12:05:43 +02:00
|
|
|
{
|
|
|
|
xcb_unmap_window(globalconf.connection, c->window);
|
|
|
|
xcb_reparent_window(globalconf.connection, c->window, globalconf.screen->root,
|
|
|
|
c->geometry.x, c->geometry.y);
|
|
|
|
}
|
2014-03-28 13:15:24 +01:00
|
|
|
|
2016-02-28 13:29:25 +01:00
|
|
|
if (c->nofocus_window != XCB_NONE)
|
2016-10-07 00:46:57 +02:00
|
|
|
window_array_append(&globalconf.destroy_later_windows, c->nofocus_window);
|
|
|
|
window_array_append(&globalconf.destroy_later_windows, c->frame_window);
|
2010-07-31 10:21:22 +02:00
|
|
|
|
2019-11-10 07:12:43 +01:00
|
|
|
if(reason != CLIENT_UNMANAGE_DESTROYED)
|
2010-10-07 12:05:43 +02:00
|
|
|
{
|
|
|
|
/* Remove this window from the save set since this shouldn't be made visible
|
|
|
|
* after a restart anymore. */
|
|
|
|
xcb_change_save_set(globalconf.connection, XCB_SET_MODE_DELETE, c->window);
|
2014-01-03 16:51:16 +01:00
|
|
|
if (globalconf.have_shape)
|
|
|
|
xcb_shape_select_input(globalconf.connection, c->window, 0);
|
2010-07-24 19:44:28 +02:00
|
|
|
|
2010-10-07 12:05:43 +02:00
|
|
|
/* Do this last to avoid races with clients. According to ICCCM, clients
|
|
|
|
* arent allowed to re-use the window until after this. */
|
2011-04-27 06:49:56 +02:00
|
|
|
xwindow_set_state(c->window, XCB_ICCCM_WM_STATE_WITHDRAWN);
|
2010-10-07 12:05:43 +02:00
|
|
|
}
|
2010-08-09 11:27:13 +02:00
|
|
|
|
2008-07-31 17:29:05 +02:00
|
|
|
/* set client as invalid */
|
2009-10-13 15:43:50 +02:00
|
|
|
c->window = XCB_NONE;
|
2008-07-31 17:29:05 +02:00
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_unref(L, c);
|
2007-09-05 20:15:00 +02:00
|
|
|
}
|
|
|
|
|
2008-10-11 09:38:48 +02:00
|
|
|
/** Kill a client via a WM_DELETE_WINDOW request or KillClient if not
|
2008-03-31 20:07:13 +02:00
|
|
|
* supported.
|
2008-06-05 09:25:38 +02:00
|
|
|
* \param c The client to kill.
|
2008-03-31 20:07:13 +02:00
|
|
|
*/
|
2007-10-01 20:42:59 +02:00
|
|
|
void
|
2008-04-11 11:35:11 +02:00
|
|
|
client_kill(client_t *c)
|
2007-10-01 20:42:59 +02:00
|
|
|
{
|
2009-06-24 18:10:55 +02:00
|
|
|
if(client_hasproto(c, WM_DELETE_WINDOW))
|
2007-10-01 20:42:59 +02:00
|
|
|
{
|
2008-10-11 09:38:48 +02:00
|
|
|
xcb_client_message_event_t ev;
|
|
|
|
|
2008-03-28 14:48:05 +01:00
|
|
|
/* Initialize all of event's fields first */
|
2008-06-05 09:25:38 +02:00
|
|
|
p_clear(&ev, 1);
|
2008-03-28 14:48:05 +01:00
|
|
|
|
|
|
|
ev.response_type = XCB_CLIENT_MESSAGE;
|
2009-08-17 17:02:45 +02:00
|
|
|
ev.window = c->window;
|
2008-03-28 14:48:05 +01:00
|
|
|
ev.format = 32;
|
2010-08-12 14:52:23 +02:00
|
|
|
ev.data.data32[1] = globalconf.timestamp;
|
2008-06-30 18:55:14 +02:00
|
|
|
ev.type = WM_PROTOCOLS;
|
|
|
|
ev.data.data32[0] = WM_DELETE_WINDOW;
|
2008-03-21 16:50:17 +01:00
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
xcb_send_event(globalconf.connection, false, c->window,
|
2008-03-21 16:50:17 +01:00
|
|
|
XCB_EVENT_MASK_NO_EVENT, (char *) &ev);
|
2007-10-01 20:42:59 +02:00
|
|
|
}
|
|
|
|
else
|
2009-08-17 17:02:45 +02:00
|
|
|
xcb_kill_client(globalconf.connection, c->window);
|
2007-12-27 20:49:38 +01:00
|
|
|
}
|
|
|
|
|
2008-06-05 09:25:38 +02:00
|
|
|
/** Get all clients into a table.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2015-07-23 16:41:16 +02:00
|
|
|
* @tparam[opt] integer screen A screen number to filter clients on.
|
|
|
|
* @tparam[opt] boolean stacked Return clients in stacking order? (ordered from
|
|
|
|
* top to bottom).
|
|
|
|
* @treturn table A table with clients.
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct get
|
2019-12-31 00:17:23 +01:00
|
|
|
* @usage for _, c in client.get() do
|
|
|
|
* -- do something
|
|
|
|
* end
|
2008-06-05 09:25:38 +02:00
|
|
|
*/
|
2008-05-20 15:39:47 +02:00
|
|
|
static int
|
|
|
|
luaA_client_get(lua_State *L)
|
2007-12-27 20:49:38 +01:00
|
|
|
{
|
2014-03-30 17:55:42 +02:00
|
|
|
int i = 1;
|
|
|
|
screen_t *screen = NULL;
|
2015-07-23 16:41:16 +02:00
|
|
|
bool stacked = false;
|
2007-12-27 20:49:38 +01:00
|
|
|
|
2014-03-30 17:55:42 +02:00
|
|
|
if(!lua_isnoneornil(L, 1))
|
|
|
|
screen = luaA_checkscreen(L, 1);
|
2008-09-18 15:11:18 +02:00
|
|
|
|
2015-07-23 16:41:16 +02:00
|
|
|
if(!lua_isnoneornil(L, 2))
|
|
|
|
stacked = luaA_checkboolean(L, 2);
|
|
|
|
|
2008-05-20 15:39:47 +02:00
|
|
|
lua_newtable(L);
|
2015-07-23 16:41:16 +02:00
|
|
|
if(stacked)
|
|
|
|
{
|
|
|
|
foreach_reverse(c, globalconf.stack)
|
|
|
|
if(screen == NULL || (*c)->screen == screen)
|
|
|
|
{
|
|
|
|
luaA_object_push(L, *c);
|
|
|
|
lua_rawseti(L, -2, i++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
foreach(c, globalconf.clients)
|
|
|
|
if(screen == NULL || (*c)->screen == screen)
|
|
|
|
{
|
|
|
|
luaA_object_push(L, *c);
|
|
|
|
lua_rawseti(L, -2, i++);
|
|
|
|
}
|
|
|
|
}
|
2008-05-23 13:17:02 +02:00
|
|
|
|
2008-05-20 15:39:47 +02:00
|
|
|
return 1;
|
2007-10-01 20:42:59 +02:00
|
|
|
}
|
2008-01-01 17:37:16 +01:00
|
|
|
|
2008-09-18 15:07:34 +02:00
|
|
|
/** Check if a client is visible on its screen.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* @treturn boolean A boolean value, true if the client is visible, false otherwise.
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method isvisible
|
2008-09-18 15:07:34 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_isvisible(lua_State *L)
|
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
2011-03-27 20:07:14 +02:00
|
|
|
lua_pushboolean(L, client_isvisible(c));
|
2008-09-18 15:07:34 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-03-06 15:50:51 +01:00
|
|
|
/** Set client icons.
|
2009-08-17 17:02:45 +02:00
|
|
|
* \param L The Lua VM state.
|
2017-03-06 15:50:51 +01:00
|
|
|
* \param array Array of icons to set.
|
2009-08-17 17:02:45 +02:00
|
|
|
*/
|
|
|
|
void
|
2017-03-06 15:50:51 +01:00
|
|
|
client_set_icons(client_t *c, cairo_surface_array_t array)
|
2009-08-17 17:02:45 +02:00
|
|
|
{
|
2017-03-06 15:50:51 +01:00
|
|
|
cairo_surface_array_wipe(&c->icons);
|
|
|
|
c->icons = array;
|
2012-05-28 09:29:47 +02:00
|
|
|
|
2017-03-06 15:50:51 +01:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_push(L, c);
|
|
|
|
luaA_object_emit_signal(L, -1, "property::icon", 0);
|
2017-03-14 19:38:25 +01:00
|
|
|
luaA_object_emit_signal(L, -1, "property::icon_sizes", 0);
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_pop(L, 1);
|
2008-05-28 14:33:45 +02:00
|
|
|
}
|
|
|
|
|
2017-03-06 15:50:51 +01:00
|
|
|
/** Set a client icon.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param cidx The client index on the stack.
|
|
|
|
* \param iidx The image index on the stack.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
client_set_icon(client_t *c, cairo_surface_t *s)
|
|
|
|
{
|
|
|
|
cairo_surface_array_t array;
|
|
|
|
cairo_surface_array_init(&array);
|
|
|
|
if (s && cairo_surface_status(s) == CAIRO_STATUS_SUCCESS)
|
|
|
|
cairo_surface_array_push(&array, draw_dup_image_surface(s));
|
|
|
|
client_set_icons(c, array);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-07 14:09:35 +01:00
|
|
|
/** Set a client icon.
|
|
|
|
* \param c The client to change.
|
|
|
|
* \param icon A bitmap containing the icon.
|
|
|
|
* \param mask A mask for the bitmap (optional)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
client_set_icon_from_pixmaps(client_t *c, xcb_pixmap_t icon, xcb_pixmap_t mask)
|
|
|
|
{
|
|
|
|
xcb_get_geometry_cookie_t geom_icon_c, geom_mask_c;
|
|
|
|
xcb_get_geometry_reply_t *geom_icon_r, *geom_mask_r = NULL;
|
|
|
|
cairo_surface_t *s_icon, *result;
|
|
|
|
|
|
|
|
geom_icon_c = xcb_get_geometry_unchecked(globalconf.connection, icon);
|
|
|
|
if (mask)
|
|
|
|
geom_mask_c = xcb_get_geometry_unchecked(globalconf.connection, mask);
|
|
|
|
geom_icon_r = xcb_get_geometry_reply(globalconf.connection, geom_icon_c, NULL);
|
|
|
|
if (mask)
|
|
|
|
geom_mask_r = xcb_get_geometry_reply(globalconf.connection, geom_mask_c, NULL);
|
|
|
|
|
|
|
|
if (!geom_icon_r || (mask && !geom_mask_r))
|
|
|
|
goto out;
|
|
|
|
if ((geom_icon_r->depth != 1 && geom_icon_r->depth != globalconf.screen->root_depth)
|
|
|
|
|| (geom_mask_r && geom_mask_r->depth != 1))
|
|
|
|
{
|
|
|
|
warn("Got pixmaps with depth (%d, %d) while processing icon, but only depth 1 and %d are allowed",
|
|
|
|
geom_icon_r->depth, geom_mask_r ? geom_mask_r->depth : 0, globalconf.screen->root_depth);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (geom_icon_r->depth == 1)
|
|
|
|
s_icon = cairo_xcb_surface_create_for_bitmap(globalconf.connection,
|
|
|
|
globalconf.screen, icon, geom_icon_r->width, geom_icon_r->height);
|
|
|
|
else
|
|
|
|
s_icon = cairo_xcb_surface_create(globalconf.connection, icon, globalconf.default_visual,
|
|
|
|
geom_icon_r->width, geom_icon_r->height);
|
|
|
|
result = s_icon;
|
|
|
|
|
|
|
|
if (mask)
|
|
|
|
{
|
|
|
|
cairo_surface_t *s_mask;
|
|
|
|
cairo_t *cr;
|
|
|
|
|
|
|
|
result = cairo_surface_create_similar(s_icon, CAIRO_CONTENT_COLOR_ALPHA, geom_icon_r->width, geom_icon_r->height);
|
|
|
|
s_mask = cairo_xcb_surface_create_for_bitmap(globalconf.connection,
|
|
|
|
globalconf.screen, mask, geom_icon_r->width, geom_icon_r->height);
|
|
|
|
cr = cairo_create(result);
|
|
|
|
|
|
|
|
cairo_set_source_surface(cr, s_icon, 0, 0);
|
|
|
|
cairo_mask_surface(cr, s_mask, 0, 0);
|
|
|
|
cairo_surface_destroy(s_mask);
|
|
|
|
cairo_destroy(cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
client_set_icon(c, result);
|
|
|
|
|
|
|
|
cairo_surface_destroy(result);
|
|
|
|
if (result != s_icon)
|
|
|
|
cairo_surface_destroy(s_icon);
|
|
|
|
|
|
|
|
out:
|
|
|
|
p_delete(&geom_icon_r);
|
|
|
|
p_delete(&geom_mask_r);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-06-10 19:29:53 +02:00
|
|
|
/** Kill a client.
|
2019-12-31 00:17:23 +01:00
|
|
|
*
|
|
|
|
* This method can be used to close (kill) a **client** using the
|
|
|
|
* X11 protocol. To use the POSIX way to kill a **process**, use
|
|
|
|
* `awesome.kill`.
|
2008-06-11 02:46:30 +02:00
|
|
|
*
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method kill
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see awesome.kill
|
2008-06-10 19:29:53 +02:00
|
|
|
*/
|
2008-05-20 15:39:47 +02:00
|
|
|
static int
|
|
|
|
luaA_client_kill(lua_State *L)
|
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
2009-04-10 15:23:55 +02:00
|
|
|
client_kill(c);
|
2008-05-20 15:39:47 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2008-02-06 08:49:31 +01:00
|
|
|
|
2015-02-27 00:24:23 +01:00
|
|
|
/** Swap a client with another one in global client list.
|
2019-12-31 07:09:39 +01:00
|
|
|
* @tparam client c A client to swap with.
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method swap
|
2019-12-31 00:17:23 +01:00
|
|
|
* @emits swapped
|
|
|
|
* @emitstparam swapped client The other client.
|
|
|
|
* @emitstparam swapped boolean `true` when `:swap()` was called
|
|
|
|
* on *self* rather than the other client. `false` when
|
|
|
|
* `:swap()` was called on the other client.
|
|
|
|
* @emits list
|
|
|
|
* @see swapped
|
|
|
|
* @see awful.client.swap.bydirection
|
|
|
|
* @see awful.client.swap.global_bydirection
|
|
|
|
* @see awful.client.swap.byidx
|
|
|
|
* @see awful.client.cycle
|
2008-06-10 19:29:53 +02:00
|
|
|
*/
|
2008-05-20 15:39:47 +02:00
|
|
|
static int
|
|
|
|
luaA_client_swap(lua_State *L)
|
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
|
|
|
client_t *swap = luaA_checkudata(L, 2, &client_class);
|
2008-10-20 15:01:16 +02:00
|
|
|
|
2009-04-10 15:23:55 +02:00
|
|
|
if(c != swap)
|
2008-11-14 10:36:15 +01:00
|
|
|
{
|
2009-04-10 15:23:55 +02:00
|
|
|
client_t **ref_c = NULL, **ref_swap = NULL;
|
|
|
|
foreach(item, globalconf.clients)
|
|
|
|
{
|
|
|
|
if(*item == c)
|
|
|
|
ref_c = item;
|
|
|
|
else if(*item == swap)
|
|
|
|
ref_swap = item;
|
|
|
|
if(ref_c && ref_swap)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* swap ! */
|
|
|
|
*ref_c = swap;
|
|
|
|
*ref_swap = c;
|
|
|
|
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_class_emit_signal(L, &client_class, "list", 0);
|
2016-01-03 09:29:21 +01:00
|
|
|
|
|
|
|
luaA_object_push(L, swap);
|
|
|
|
lua_pushboolean(L, true);
|
|
|
|
luaA_object_emit_signal(L, -4, "swapped", 2);
|
|
|
|
|
|
|
|
luaA_object_push(L, swap);
|
|
|
|
luaA_object_push(L, c);
|
|
|
|
lua_pushboolean(L, false);
|
|
|
|
luaA_object_emit_signal(L, -3, "swapped", 2);
|
2008-11-14 10:36:15 +01:00
|
|
|
}
|
2008-10-20 15:01:16 +02:00
|
|
|
|
2008-05-20 15:39:47 +02:00
|
|
|
return 0;
|
2008-02-06 08:49:31 +01:00
|
|
|
}
|
|
|
|
|
2008-08-13 17:49:57 +02:00
|
|
|
/** Access or set the client tags.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2015-07-14 00:27:26 +02:00
|
|
|
* Use the `first_tag` field to access the first tag of a client directly.
|
|
|
|
*
|
|
|
|
* @tparam table tags_table A table with tags to set, or `nil` to get the
|
|
|
|
* current tags.
|
|
|
|
* @treturn table A table with all tags.
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method tags
|
2019-12-31 00:17:23 +01:00
|
|
|
* @emits property::tags
|
|
|
|
* @see first_tag
|
|
|
|
* @see toggle_tag
|
2008-08-13 17:49:57 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_tags(lua_State *L)
|
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
2008-08-17 07:52:04 +02:00
|
|
|
int j = 0;
|
2008-08-13 17:49:57 +02:00
|
|
|
|
|
|
|
if(lua_gettop(L) == 2)
|
|
|
|
{
|
|
|
|
luaA_checktable(L, 2);
|
2012-10-20 17:33:32 +02:00
|
|
|
for(int i = 0; i < globalconf.tags.len; i++)
|
2011-01-18 14:21:37 +01:00
|
|
|
{
|
|
|
|
/* Only untag if we aren't going to add this tag again */
|
|
|
|
bool found = false;
|
|
|
|
lua_pushnil(L);
|
|
|
|
while(lua_next(L, 2))
|
|
|
|
{
|
|
|
|
tag_t *t = lua_touserdata(L, -1);
|
|
|
|
/* Pop the value from lua_next */
|
|
|
|
lua_pop(L, 1);
|
2012-10-20 17:33:32 +02:00
|
|
|
if (t != globalconf.tags.tab[i])
|
2011-01-18 14:21:37 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Pop the key from lua_next */
|
|
|
|
lua_pop(L, 1);
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(!found)
|
2012-10-20 17:33:32 +02:00
|
|
|
untag_client(c, globalconf.tags.tab[i]);
|
2011-01-18 14:21:37 +01:00
|
|
|
}
|
2008-08-13 17:49:57 +02:00
|
|
|
lua_pushnil(L);
|
|
|
|
while(lua_next(L, 2))
|
2014-12-06 11:14:59 +01:00
|
|
|
tag_client(L, c);
|
2016-08-29 04:18:53 +02:00
|
|
|
|
2008-08-17 07:52:04 +02:00
|
|
|
lua_pop(L, 1);
|
2016-08-29 04:18:53 +02:00
|
|
|
|
|
|
|
luaA_object_emit_signal(L, -1, "property::tags", 0);
|
2008-08-13 17:49:57 +02:00
|
|
|
}
|
2008-08-17 07:52:04 +02:00
|
|
|
|
2009-03-14 13:47:50 +01:00
|
|
|
lua_newtable(L);
|
2012-10-20 17:33:32 +02:00
|
|
|
foreach(tag, globalconf.tags)
|
2009-04-11 11:45:55 +02:00
|
|
|
if(is_client_tagged(c, *tag))
|
2008-08-17 07:52:04 +02:00
|
|
|
{
|
2009-06-29 11:02:13 +02:00
|
|
|
luaA_object_push(L, *tag);
|
2008-08-17 07:52:04 +02:00
|
|
|
lua_rawseti(L, -2, ++j);
|
|
|
|
}
|
2008-08-13 17:49:57 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-07-14 00:27:26 +02:00
|
|
|
/** Get the first tag of a client.
|
|
|
|
*/
|
|
|
|
static int
|
2018-07-21 10:19:32 +02:00
|
|
|
luaA_client_get_first_tag(lua_State *L, client_t *c)
|
2015-07-14 00:27:26 +02:00
|
|
|
{
|
|
|
|
foreach(tag, globalconf.tags)
|
|
|
|
if(is_client_tagged(c, *tag))
|
|
|
|
{
|
|
|
|
luaA_object_push(L, *tag);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-05-26 16:17:57 +02:00
|
|
|
/** Raise a client on top of others which are on the same layer.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method raise
|
2019-12-31 00:17:23 +01:00
|
|
|
* @emits raised
|
|
|
|
* @see above
|
|
|
|
* @see below
|
|
|
|
* @see ontop
|
|
|
|
* @see lower
|
2008-05-26 16:17:57 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_raise(lua_State *L)
|
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
2016-01-03 09:29:21 +01:00
|
|
|
|
|
|
|
/* Avoid sending the signal if nothing was done */
|
2016-08-30 12:59:45 +02:00
|
|
|
if (c->transient_for == NULL &&
|
|
|
|
globalconf.stack.len &&
|
|
|
|
globalconf.stack.tab[globalconf.stack.len-1] == c
|
|
|
|
)
|
2016-01-03 09:29:21 +01:00
|
|
|
return 0;
|
|
|
|
|
2009-04-10 15:23:55 +02:00
|
|
|
client_raise(c);
|
2016-01-03 09:29:21 +01:00
|
|
|
|
2008-05-20 15:39:47 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2008-05-11 18:41:04 +02:00
|
|
|
|
2008-11-12 11:27:58 +01:00
|
|
|
/** Lower a client on bottom of others which are on the same layer.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method lower
|
2019-12-31 00:17:23 +01:00
|
|
|
* @emits lowered
|
|
|
|
* @see above
|
|
|
|
* @see below
|
|
|
|
* @see ontop
|
|
|
|
* @see raise
|
2008-11-12 11:27:58 +01:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_lower(lua_State *L)
|
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
2009-08-28 17:53:48 +02:00
|
|
|
|
2016-01-03 09:29:21 +01:00
|
|
|
/* Avoid sending the signal if nothing was done */
|
|
|
|
if (globalconf.stack.len && globalconf.stack.tab[0] == c)
|
|
|
|
return 0;
|
|
|
|
|
2009-08-28 17:53:48 +02:00
|
|
|
stack_client_push(c);
|
|
|
|
|
|
|
|
/* Traverse all transient layers. */
|
|
|
|
for(client_t *tc = c->transient_for; tc; tc = tc->transient_for)
|
|
|
|
stack_client_push(tc);
|
|
|
|
|
2016-01-03 09:29:21 +01:00
|
|
|
/* Notify the listeners */
|
|
|
|
luaA_object_push(L, c);
|
|
|
|
luaA_object_emit_signal(L, -1, "lowered", 0);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
2008-11-12 11:27:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-06-02 08:31:54 +02:00
|
|
|
/** Stop managing a client.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method unmanage
|
2008-06-02 08:31:54 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_unmanage(lua_State *L)
|
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
2019-11-10 07:12:43 +01:00
|
|
|
client_unmanage(c, CLIENT_UNMANAGE_USER);
|
2008-06-02 08:31:54 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
static area_t
|
|
|
|
titlebar_get_area(client_t *c, client_titlebar_t bar)
|
|
|
|
{
|
|
|
|
area_t result = c->geometry;
|
|
|
|
result.x = result.y = 0;
|
|
|
|
|
|
|
|
// Let's try some ascii art:
|
|
|
|
// ---------------------------
|
|
|
|
// | Top |
|
|
|
|
// |-------------------------|
|
|
|
|
// |L| |R|
|
|
|
|
// |e| |i|
|
|
|
|
// |f| |g|
|
|
|
|
// |t| |h|
|
|
|
|
// | | |t|
|
|
|
|
// |-------------------------|
|
|
|
|
// | Bottom |
|
|
|
|
// ---------------------------
|
|
|
|
|
|
|
|
switch (bar) {
|
|
|
|
case CLIENT_TITLEBAR_BOTTOM:
|
|
|
|
result.y = c->geometry.height - c->titlebar[bar].size;
|
|
|
|
/* Fall through */
|
|
|
|
case CLIENT_TITLEBAR_TOP:
|
|
|
|
result.height = c->titlebar[bar].size;
|
|
|
|
break;
|
|
|
|
case CLIENT_TITLEBAR_RIGHT:
|
|
|
|
result.x = c->geometry.width - c->titlebar[bar].size;
|
|
|
|
/* Fall through */
|
|
|
|
case CLIENT_TITLEBAR_LEFT:
|
|
|
|
result.y = c->titlebar[CLIENT_TITLEBAR_TOP].size;
|
|
|
|
result.width = c->titlebar[bar].size;
|
|
|
|
result.height -= c->titlebar[CLIENT_TITLEBAR_TOP].size;
|
|
|
|
result.height -= c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("Unknown titlebar kind %d\n", (int) bar);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
drawable_t *
|
|
|
|
client_get_drawable_offset(client_t *c, int *x, int *y)
|
|
|
|
{
|
|
|
|
for (client_titlebar_t bar = CLIENT_TITLEBAR_TOP; bar < CLIENT_TITLEBAR_COUNT; bar++) {
|
|
|
|
area_t area = titlebar_get_area(c, bar);
|
|
|
|
if (AREA_LEFT(area) > *x || AREA_RIGHT(area) <= *x)
|
|
|
|
continue;
|
|
|
|
if (AREA_TOP(area) > *y || AREA_BOTTOM(area) <= *y)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
*x -= area.x;
|
|
|
|
*y -= area.y;
|
|
|
|
return c->titlebar[bar].drawable;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
drawable_t *
|
|
|
|
client_get_drawable(client_t *c, int x, int y)
|
|
|
|
{
|
|
|
|
return client_get_drawable_offset(c, &x, &y);
|
|
|
|
}
|
|
|
|
|
2014-03-16 15:29:11 +01:00
|
|
|
static void
|
|
|
|
client_refresh_titlebar_partial(client_t *c, client_titlebar_t bar, int16_t x, int16_t y, uint16_t width, uint16_t height)
|
|
|
|
{
|
2014-03-17 16:27:10 +01:00
|
|
|
if(c->titlebar[bar].drawable == NULL
|
|
|
|
|| c->titlebar[bar].drawable->pixmap == XCB_NONE
|
|
|
|
|| !c->titlebar[bar].drawable->refreshed)
|
2014-03-16 15:29:11 +01:00
|
|
|
return;
|
2014-03-17 16:27:10 +01:00
|
|
|
|
2014-03-16 15:29:11 +01:00
|
|
|
/* Is the titlebar part of the area that should get redrawn? */
|
2014-03-17 16:27:10 +01:00
|
|
|
area_t area = titlebar_get_area(c, bar);
|
2014-03-16 15:29:11 +01:00
|
|
|
if (AREA_LEFT(area) >= x + width || AREA_RIGHT(area) <= x)
|
|
|
|
return;
|
|
|
|
if (AREA_TOP(area) >= y + height || AREA_BOTTOM(area) <= y)
|
|
|
|
return;
|
2014-03-17 16:27:10 +01:00
|
|
|
|
2014-03-16 15:29:11 +01:00
|
|
|
/* Redraw the affected parts */
|
|
|
|
cairo_surface_flush(c->titlebar[bar].drawable->surface);
|
2014-03-16 17:08:36 +01:00
|
|
|
xcb_copy_area(globalconf.connection, c->titlebar[bar].drawable->pixmap, c->frame_window,
|
2014-03-16 15:29:11 +01:00
|
|
|
globalconf.gc, x - area.x, y - area.y, x, y, width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define HANDLE_TITLEBAR_REFRESH(name, index) \
|
|
|
|
static void \
|
|
|
|
client_refresh_titlebar_ ## name(client_t *c) \
|
|
|
|
{ \
|
|
|
|
area_t area = titlebar_get_area(c, index); \
|
|
|
|
client_refresh_titlebar_partial(c, index, area.x, area.y, area.width, area.height); \
|
|
|
|
}
|
|
|
|
HANDLE_TITLEBAR_REFRESH(top, CLIENT_TITLEBAR_TOP)
|
|
|
|
HANDLE_TITLEBAR_REFRESH(right, CLIENT_TITLEBAR_RIGHT)
|
|
|
|
HANDLE_TITLEBAR_REFRESH(bottom, CLIENT_TITLEBAR_BOTTOM)
|
|
|
|
HANDLE_TITLEBAR_REFRESH(left, CLIENT_TITLEBAR_LEFT)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Refresh all titlebars that are in the specified rectangle
|
|
|
|
*/
|
2012-10-14 17:20:24 +02:00
|
|
|
void
|
2014-03-16 15:29:11 +01:00
|
|
|
client_refresh_partial(client_t *c, int16_t x, int16_t y, uint16_t width, uint16_t height)
|
2012-10-14 17:20:24 +02:00
|
|
|
{
|
|
|
|
for (client_titlebar_t bar = CLIENT_TITLEBAR_TOP; bar < CLIENT_TITLEBAR_COUNT; bar++) {
|
2014-03-16 15:29:11 +01:00
|
|
|
client_refresh_titlebar_partial(c, bar, x, y, width, height);
|
2012-10-14 17:20:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static drawable_t *
|
|
|
|
titlebar_get_drawable(lua_State *L, client_t *c, int cl_idx, client_titlebar_t bar)
|
|
|
|
{
|
|
|
|
if (c->titlebar[bar].drawable == NULL)
|
|
|
|
{
|
|
|
|
cl_idx = luaA_absindex(L, cl_idx);
|
2014-03-16 15:29:11 +01:00
|
|
|
switch (bar) {
|
|
|
|
case CLIENT_TITLEBAR_TOP:
|
|
|
|
drawable_allocator(L, (drawable_refresh_callback *) client_refresh_titlebar_top, c);
|
|
|
|
break;
|
|
|
|
case CLIENT_TITLEBAR_BOTTOM:
|
|
|
|
drawable_allocator(L, (drawable_refresh_callback *) client_refresh_titlebar_bottom, c);
|
|
|
|
break;
|
|
|
|
case CLIENT_TITLEBAR_RIGHT:
|
|
|
|
drawable_allocator(L, (drawable_refresh_callback *) client_refresh_titlebar_right, c);
|
|
|
|
break;
|
|
|
|
case CLIENT_TITLEBAR_LEFT:
|
|
|
|
drawable_allocator(L, (drawable_refresh_callback *) client_refresh_titlebar_left, c);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("Unknown titlebar kind %d\n", (int) bar);
|
|
|
|
}
|
2012-10-14 17:20:24 +02:00
|
|
|
c->titlebar[bar].drawable = luaA_object_ref_item(L, cl_idx, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return c->titlebar[bar].drawable;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2014-10-18 21:41:38 +02:00
|
|
|
titlebar_resize(lua_State *L, int cidx, client_t *c, client_titlebar_t bar, int size)
|
2012-10-14 17:20:24 +02:00
|
|
|
{
|
2014-10-18 21:41:38 +02:00
|
|
|
const char *property_name;
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
if (size < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (size == c->titlebar[bar].size)
|
|
|
|
return;
|
|
|
|
|
2012-11-03 18:44:44 +01:00
|
|
|
/* Now resize the client (and titlebars!) suitably (the client without
|
2013-10-06 10:22:49 +02:00
|
|
|
* titlebars should keep its current size!) */
|
2012-11-03 18:44:44 +01:00
|
|
|
area_t geometry = c->geometry;
|
|
|
|
int change = size - c->titlebar[bar].size;
|
2015-10-10 17:44:11 +02:00
|
|
|
int16_t diff_top = 0, diff_bottom = 0, diff_right = 0, diff_left = 0;
|
2012-11-03 18:44:44 +01:00
|
|
|
switch (bar) {
|
|
|
|
case CLIENT_TITLEBAR_TOP:
|
2014-10-18 21:41:38 +02:00
|
|
|
geometry.height += change;
|
2015-10-10 17:44:11 +02:00
|
|
|
diff_top = change;
|
2014-10-18 21:41:38 +02:00
|
|
|
property_name = "property::titlebar_top";
|
|
|
|
break;
|
2012-11-03 18:44:44 +01:00
|
|
|
case CLIENT_TITLEBAR_BOTTOM:
|
|
|
|
geometry.height += change;
|
2015-10-10 17:44:11 +02:00
|
|
|
diff_bottom = change;
|
2014-10-18 21:41:38 +02:00
|
|
|
property_name = "property::titlebar_bottom";
|
2012-11-03 18:44:44 +01:00
|
|
|
break;
|
2013-10-04 16:12:06 +02:00
|
|
|
case CLIENT_TITLEBAR_RIGHT:
|
2014-10-18 21:41:38 +02:00
|
|
|
geometry.width += change;
|
2015-10-10 17:44:11 +02:00
|
|
|
diff_right = change;
|
2014-10-18 21:41:38 +02:00
|
|
|
property_name = "property::titlebar_right";
|
|
|
|
break;
|
2013-10-06 10:22:49 +02:00
|
|
|
case CLIENT_TITLEBAR_LEFT:
|
2012-11-03 18:44:44 +01:00
|
|
|
geometry.width += change;
|
2015-10-10 17:44:11 +02:00
|
|
|
diff_left = change;
|
2014-10-18 21:41:38 +02:00
|
|
|
property_name = "property::titlebar_left";
|
2012-11-03 18:44:44 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("Unknown titlebar kind %d\n", (int) bar);
|
|
|
|
}
|
|
|
|
|
2015-10-10 17:44:11 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)
|
|
|
|
{
|
|
|
|
xwindow_translate_for_gravity(c->size_hints.win_gravity,
|
|
|
|
diff_left, diff_top,
|
|
|
|
diff_right, diff_bottom,
|
2018-08-17 09:58:30 +02:00
|
|
|
&geometry.x, &geometry.y);
|
2015-10-10 17:44:11 +02:00
|
|
|
}
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
c->titlebar[bar].size = size;
|
2016-09-15 18:28:46 +02:00
|
|
|
client_resize_do(c, geometry);
|
2014-10-18 21:41:38 +02:00
|
|
|
|
|
|
|
luaA_object_emit_signal(L, cidx, property_name, 0);
|
2012-10-14 17:20:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#define HANDLE_TITLEBAR(name, index) \
|
|
|
|
static int \
|
|
|
|
luaA_client_titlebar_ ## name(lua_State *L) \
|
|
|
|
{ \
|
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class); \
|
|
|
|
\
|
|
|
|
if (lua_gettop(L) == 2) \
|
|
|
|
{ \
|
|
|
|
if (lua_isnil(L, 2)) \
|
2014-10-18 21:41:38 +02:00
|
|
|
titlebar_resize(L, 1, c, index, 0); \
|
2012-10-14 17:20:24 +02:00
|
|
|
else \
|
2016-04-18 07:15:15 +02:00
|
|
|
titlebar_resize(L, 1, c, index, ceil(luaA_checknumber_range(L, 2, 0, MAX_X11_SIZE))); \
|
2012-10-14 17:20:24 +02:00
|
|
|
} \
|
|
|
|
\
|
|
|
|
luaA_object_push_item(L, 1, titlebar_get_drawable(L, c, 1, index)); \
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->titlebar[index].size); \
|
2012-10-14 17:20:24 +02:00
|
|
|
return 2; \
|
|
|
|
}
|
|
|
|
HANDLE_TITLEBAR(top, CLIENT_TITLEBAR_TOP)
|
|
|
|
HANDLE_TITLEBAR(right, CLIENT_TITLEBAR_RIGHT)
|
|
|
|
HANDLE_TITLEBAR(bottom, CLIENT_TITLEBAR_BOTTOM)
|
|
|
|
HANDLE_TITLEBAR(left, CLIENT_TITLEBAR_LEFT)
|
|
|
|
|
2014-03-15 02:56:58 +01:00
|
|
|
/** Return or set client geometry.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2015-07-27 01:05:01 +02:00
|
|
|
* @tparam table|nil geo A table with new coordinates, or nil.
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam integer geo.x The horizontal position.
|
|
|
|
* @tparam integer geo.y The vertical position.
|
|
|
|
* @tparam integer geo.width The width.
|
2020-09-19 16:28:00 +02:00
|
|
|
* @tparam integer geo.height The height.
|
2015-07-27 01:05:01 +02:00
|
|
|
* @treturn table A table with client geometry and coordinates.
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method geometry
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see struts
|
|
|
|
* @see x
|
|
|
|
* @see y
|
|
|
|
* @see width
|
|
|
|
* @see height
|
2008-08-26 16:03:01 +02:00
|
|
|
*/
|
2008-08-20 12:00:22 +02:00
|
|
|
static int
|
2008-12-12 00:01:43 +01:00
|
|
|
luaA_client_geometry(lua_State *L)
|
2008-08-20 12:00:22 +02:00
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
2008-08-20 12:00:22 +02:00
|
|
|
|
2009-09-04 13:55:21 +02:00
|
|
|
if(lua_gettop(L) == 2 && !lua_isnil(L, 2))
|
2008-11-25 17:01:06 +01:00
|
|
|
{
|
|
|
|
area_t geometry;
|
2008-08-20 12:00:22 +02:00
|
|
|
|
2008-11-25 17:01:06 +01:00
|
|
|
luaA_checktable(L, 2);
|
2016-04-18 07:15:15 +02:00
|
|
|
geometry.x = round(luaA_getopt_number_range(L, 2, "x", c->geometry.x, MIN_X11_COORDINATE, MAX_X11_COORDINATE));
|
|
|
|
geometry.y = round(luaA_getopt_number_range(L, 2, "y", c->geometry.y, MIN_X11_COORDINATE, MAX_X11_COORDINATE));
|
2009-04-10 15:23:55 +02:00
|
|
|
if(client_isfixed(c))
|
2008-11-25 17:01:06 +01:00
|
|
|
{
|
2009-04-10 15:23:55 +02:00
|
|
|
geometry.width = c->geometry.width;
|
|
|
|
geometry.height = c->geometry.height;
|
2008-11-25 17:01:06 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-18 07:15:15 +02:00
|
|
|
geometry.width = ceil(luaA_getopt_number_range(L, 2, "width", c->geometry.width, MIN_X11_SIZE, MAX_X11_SIZE));
|
|
|
|
geometry.height = ceil(luaA_getopt_number_range(L, 2, "height", c->geometry.height, MIN_X11_SIZE, MAX_X11_SIZE));
|
2008-08-20 12:00:22 +02:00
|
|
|
}
|
|
|
|
|
2013-03-12 11:33:05 +01:00
|
|
|
client_resize(c, geometry, c->size_hints_honor);
|
2008-11-25 17:01:06 +01:00
|
|
|
}
|
|
|
|
|
2009-04-10 15:23:55 +02:00
|
|
|
return luaA_pusharea(L, c->geometry);
|
2008-08-20 12:00:22 +02:00
|
|
|
}
|
|
|
|
|
2015-02-27 00:24:23 +01:00
|
|
|
/** Apply size hints to a size.
|
|
|
|
*
|
2019-12-31 00:17:23 +01:00
|
|
|
* This method applies the client size hints. The client
|
|
|
|
* will be resized according to the size hints as long
|
|
|
|
* as `size_hints_honor` is true. Regardless of the
|
|
|
|
* status of `size_hints_honor`, this method will
|
|
|
|
* return the size with the size hints applied.
|
|
|
|
*
|
|
|
|
* @tparam integer width Desired width of client
|
|
|
|
* @tparam integer height Desired height of client
|
|
|
|
* @treturn integer Actual width of client
|
|
|
|
* @treturn integer Actual height of client
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method apply_size_hints
|
2019-12-31 00:17:23 +01:00
|
|
|
* @see size_hints
|
|
|
|
* @see size_hints_honor
|
2015-02-14 15:12:46 +01:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_apply_size_hints(lua_State *L)
|
|
|
|
{
|
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
|
|
|
area_t geometry = c->geometry;
|
|
|
|
if(!client_isfixed(c))
|
|
|
|
{
|
2016-04-18 07:15:15 +02:00
|
|
|
geometry.width = ceil(luaA_checknumber_range(L, 2, MIN_X11_SIZE, MAX_X11_SIZE));
|
|
|
|
geometry.height = ceil(luaA_checknumber_range(L, 3, MIN_X11_SIZE, MAX_X11_SIZE));
|
2015-02-14 15:12:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (c->size_hints_honor)
|
|
|
|
geometry = client_apply_size_hints(c, geometry);
|
|
|
|
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, geometry.width);
|
|
|
|
lua_pushinteger(L, geometry.height);
|
2015-02-14 15:12:46 +01:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2009-04-10 15:23:55 +02:00
|
|
|
static int
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_client_set_screen(lua_State *L, client_t *c)
|
2008-07-01 19:37:10 +02:00
|
|
|
{
|
2014-03-30 17:55:42 +02:00
|
|
|
screen_client_moveto(c, luaA_checkscreen(L, -1), true);
|
2009-08-17 17:02:45 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2008-07-01 19:37:10 +02:00
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
static int
|
|
|
|
luaA_client_set_hidden(lua_State *L, client_t *c)
|
|
|
|
{
|
2012-05-05 10:13:37 +02:00
|
|
|
client_set_hidden(L, -3, luaA_checkboolean(L, -1));
|
2009-08-17 17:02:45 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2008-07-01 19:37:10 +02:00
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
static int
|
|
|
|
luaA_client_set_minimized(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_minimized(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_fullscreen(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_fullscreen(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_modal(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_modal(L, -3, luaA_checkboolean(L, -1));
|
2008-07-01 19:37:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-14 04:14:03 +01:00
|
|
|
static int
|
|
|
|
luaA_client_set_maximized(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_maximized(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-01 19:37:10 +02:00
|
|
|
static int
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_client_set_maximized_horizontal(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_maximized_horizontal(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_maximized_vertical(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_maximized_vertical(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_icon(lua_State *L, client_t *c)
|
|
|
|
{
|
2012-05-28 09:29:47 +02:00
|
|
|
cairo_surface_t *surf = NULL;
|
|
|
|
if(!lua_isnil(L, -1))
|
|
|
|
surf = (cairo_surface_t *)lua_touserdata(L, -1);
|
|
|
|
client_set_icon(c, surf);
|
2009-08-17 17:02:45 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-21 17:30:51 +02:00
|
|
|
static int
|
|
|
|
luaA_client_set_focusable(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
if(lua_isnil(L, -1))
|
2015-06-12 11:00:19 +02:00
|
|
|
client_unset_focusable(L, -3);
|
2015-05-21 17:30:51 +02:00
|
|
|
else
|
|
|
|
client_set_focusable(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
static int
|
|
|
|
luaA_client_set_sticky(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_sticky(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_size_hints_honor(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
c->size_hints_honor = luaA_checkboolean(L, -1);
|
|
|
|
luaA_object_emit_signal(L, -3, "property::size_hints_honor", 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_ontop(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_ontop(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_below(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_below(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_above(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_above(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_urgent(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_urgent(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_set_skip_taskbar(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
client_set_skip_taskbar(L, -3, luaA_checkboolean(L, -1));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-25 16:39:10 +02:00
|
|
|
static int
|
|
|
|
luaA_client_get_name(lua_State *L, client_t *c)
|
|
|
|
{
|
2021-03-01 08:02:54 +01:00
|
|
|
lua_pushstring(L, NONULL(c->name ? c->name : c->alt_name));
|
2009-08-25 16:39:10 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-02-02 09:26:19 +01:00
|
|
|
/** Set the client name.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param client The client to name.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_set_name(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
const char *name = luaL_checkstring(L, -1);
|
|
|
|
client_set_name(L, 1, a_strdup(name));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-25 16:39:10 +02:00
|
|
|
static int
|
|
|
|
luaA_client_get_icon_name(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
lua_pushstring(L, c->icon_name ? c->icon_name : c->alt_icon_name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-07-23 10:04:07 +02:00
|
|
|
static int
|
|
|
|
luaA_client_set_startup_id(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
const char *startup_id = luaL_checkstring(L, -1);
|
|
|
|
client_set_startup_id(L, 1, a_strdup(startup_id));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-04 00:51:23 +02:00
|
|
|
LUA_OBJECT_EXPORT_OPTIONAL_PROPERTY(client, client_t, screen, luaA_object_push, NULL)
|
2009-08-17 17:02:45 +02:00
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, class, lua_pushstring)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, instance, lua_pushstring)
|
2016-06-04 00:51:23 +02:00
|
|
|
LUA_OBJECT_EXPORT_OPTIONAL_PROPERTY(client, client_t, machine, lua_pushstring, NULL)
|
2009-08-17 17:02:45 +02:00
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, role, lua_pushstring)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, transient_for, luaA_object_push)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, skip_taskbar, lua_pushboolean)
|
2015-05-26 07:09:12 +02:00
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, leader_window, lua_pushinteger)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, group_window, lua_pushinteger)
|
2016-06-04 00:51:23 +02:00
|
|
|
LUA_OBJECT_EXPORT_OPTIONAL_PROPERTY(client, client_t, pid, lua_pushinteger, 0)
|
2009-08-17 17:02:45 +02:00
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, hidden, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, minimized, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, fullscreen, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, modal, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, ontop, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, urgent, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, above, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, below, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, sticky, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, size_hints_honor, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, maximized_horizontal, lua_pushboolean)
|
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, maximized_vertical, lua_pushboolean)
|
2017-01-24 11:41:22 +01:00
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, maximized, lua_pushboolean)
|
2014-03-15 23:09:33 +01:00
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, startup_id, lua_pushstring)
|
2009-08-17 17:02:45 +02:00
|
|
|
|
2018-08-03 19:19:43 +02:00
|
|
|
static int
|
|
|
|
luaA_client_get_motif_wm_hints(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
if (!(c->motif_wm_hints.hints & MWM_HINTS_AWESOME_SET))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
|
|
|
|
#define HANDLE_BIT(field, flag, name) do { \
|
|
|
|
lua_pushboolean(L, c->motif_wm_hints.field & flag); \
|
|
|
|
lua_setfield(L, -2, name); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
if (c->motif_wm_hints.hints & MWM_HINTS_FUNCTIONS)
|
|
|
|
{
|
|
|
|
lua_newtable(L);
|
|
|
|
HANDLE_BIT(functions, MWM_FUNC_ALL, "all");
|
|
|
|
HANDLE_BIT(functions, MWM_FUNC_RESIZE, "resize");
|
|
|
|
HANDLE_BIT(functions, MWM_FUNC_MOVE, "move");
|
|
|
|
HANDLE_BIT(functions, MWM_FUNC_MINIMIZE, "minimize");
|
|
|
|
HANDLE_BIT(functions, MWM_FUNC_MAXIMIZE, "maximize");
|
|
|
|
HANDLE_BIT(functions, MWM_FUNC_CLOSE, "close");
|
|
|
|
lua_setfield(L, -2, "functions");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->motif_wm_hints.hints & MWM_HINTS_DECORATIONS)
|
|
|
|
{
|
|
|
|
lua_newtable(L);
|
|
|
|
HANDLE_BIT(decorations, MWM_DECOR_ALL, "all");
|
|
|
|
HANDLE_BIT(decorations, MWM_DECOR_BORDER, "border");
|
|
|
|
HANDLE_BIT(decorations, MWM_DECOR_RESIZEH, "resizeh");
|
|
|
|
HANDLE_BIT(decorations, MWM_DECOR_TITLE, "title");
|
|
|
|
HANDLE_BIT(decorations, MWM_DECOR_MENU, "menu");
|
|
|
|
HANDLE_BIT(decorations, MWM_DECOR_MINIMIZE, "minimize");
|
|
|
|
HANDLE_BIT(decorations, MWM_DECOR_MAXIMIZE, "maximize");
|
|
|
|
lua_setfield(L, -2, "decorations");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->motif_wm_hints.hints & MWM_HINTS_INPUT_MODE)
|
|
|
|
{
|
|
|
|
switch (c->motif_wm_hints.input_mode) {
|
|
|
|
case MWM_INPUT_MODELESS:
|
|
|
|
lua_pushliteral(L, "modeless");
|
|
|
|
break;
|
|
|
|
case MWM_INPUT_PRIMARY_APPLICATION_MODAL:
|
|
|
|
lua_pushliteral(L, "primary_application_modal");
|
|
|
|
break;
|
|
|
|
case MWM_INPUT_SYSTEM_MODAL:
|
|
|
|
lua_pushliteral(L, "system_modal");
|
|
|
|
break;
|
|
|
|
case MWM_INPUT_FULL_APPLICATION_MODAL:
|
|
|
|
lua_pushliteral(L, "full_application_modal");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
lua_pushfstring(L, "unknown (%d)", (int) c->motif_wm_hints.input_mode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lua_setfield(L, -2, "input_mode");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->motif_wm_hints.hints & MWM_HINTS_STATUS)
|
|
|
|
{
|
|
|
|
lua_newtable(L);
|
|
|
|
HANDLE_BIT(status, MWM_TEAROFF_WINDOW, "tearoff_window");
|
|
|
|
lua_setfield(L, -2, "status");
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef HANDLE_BIT
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
static int
|
|
|
|
luaA_client_get_content(lua_State *L, client_t *c)
|
|
|
|
{
|
2013-09-16 12:09:40 +02:00
|
|
|
cairo_surface_t *surface;
|
2013-09-16 12:06:10 +02:00
|
|
|
int width = c->geometry.width;
|
|
|
|
int height = c->geometry.height;
|
|
|
|
|
2013-09-16 12:09:40 +02:00
|
|
|
/* Just the client size without decorations */
|
2013-09-16 12:06:10 +02:00
|
|
|
width -= c->titlebar[CLIENT_TITLEBAR_LEFT].size + c->titlebar[CLIENT_TITLEBAR_RIGHT].size;
|
|
|
|
height -= c->titlebar[CLIENT_TITLEBAR_TOP].size + c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
|
2009-08-17 17:02:45 +02:00
|
|
|
|
2013-09-16 12:09:40 +02:00
|
|
|
surface = cairo_xcb_surface_create(globalconf.connection, c->window,
|
2014-03-15 11:56:57 +01:00
|
|
|
c->visualtype, width, height);
|
2013-09-16 12:09:40 +02:00
|
|
|
|
2012-05-28 09:29:47 +02:00
|
|
|
/* lua has to make sure to free the ref or we have a leak */
|
|
|
|
lua_pushlightuserdata(L, surface);
|
|
|
|
return 1;
|
2009-08-17 17:02:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_client_get_icon(lua_State *L, client_t *c)
|
|
|
|
{
|
2017-03-06 15:50:51 +01:00
|
|
|
if(c->icons.len == 0)
|
2012-05-27 19:20:34 +02:00
|
|
|
return 0;
|
2017-03-06 15:50:51 +01:00
|
|
|
|
|
|
|
/* Pick the closest available size, only picking a smaller icon if no bigger
|
|
|
|
* one is available.
|
|
|
|
*/
|
|
|
|
cairo_surface_t *found = NULL;
|
|
|
|
int found_size = 0;
|
|
|
|
int preferred_size = globalconf.preferred_icon_size;
|
|
|
|
|
|
|
|
foreach(surf, c->icons)
|
|
|
|
{
|
|
|
|
int width = cairo_image_surface_get_width(*surf);
|
|
|
|
int height = cairo_image_surface_get_height(*surf);
|
|
|
|
int size = MAX(width, height);
|
|
|
|
|
|
|
|
/* pick the icon if it's a better match than the one we already have */
|
|
|
|
bool found_icon_too_small = found_size < preferred_size;
|
|
|
|
bool found_icon_too_large = found_size > preferred_size;
|
|
|
|
bool icon_empty = width == 0 || height == 0;
|
|
|
|
bool better_because_bigger = found_icon_too_small && size > found_size;
|
|
|
|
bool better_because_smaller = found_icon_too_large &&
|
|
|
|
size >= preferred_size && size < found_size;
|
|
|
|
if (!icon_empty && (better_because_bigger || better_because_smaller || found_size == 0))
|
|
|
|
{
|
|
|
|
found = *surf;
|
|
|
|
found_size = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-27 19:20:34 +02:00
|
|
|
/* lua gets its own reference which it will have to destroy */
|
2017-03-06 15:50:51 +01:00
|
|
|
lua_pushlightuserdata(L, cairo_surface_reference(found));
|
2012-05-27 19:20:34 +02:00
|
|
|
return 1;
|
2009-08-17 17:02:45 +02:00
|
|
|
}
|
2008-12-09 13:59:50 +01:00
|
|
|
|
2010-08-12 13:08:55 +02:00
|
|
|
static int
|
|
|
|
luaA_client_get_focusable(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
bool ret;
|
|
|
|
|
2015-05-21 17:30:51 +02:00
|
|
|
if (c->focusable_set)
|
|
|
|
ret = c->focusable;
|
|
|
|
|
2010-08-12 13:08:55 +02:00
|
|
|
/* A client can be focused if it doesnt have the "nofocus" hint...*/
|
2015-05-21 17:30:51 +02:00
|
|
|
else if (!c->nofocus)
|
2010-08-12 13:08:55 +02:00
|
|
|
ret = true;
|
|
|
|
else
|
|
|
|
/* ...or if it knows the WM_TAKE_FOCUS protocol */
|
|
|
|
ret = client_hasproto(c, WM_TAKE_FOCUS);
|
|
|
|
|
|
|
|
lua_pushboolean(L, ret);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
static int
|
|
|
|
luaA_client_get_size_hints(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
const char *u_or_p = NULL;
|
2008-12-09 13:59:50 +01:00
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_createtable(L, 0, 1);
|
2008-12-09 13:59:50 +01:00
|
|
|
|
2011-04-27 06:49:56 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_US_POSITION)
|
2009-08-17 17:02:45 +02:00
|
|
|
u_or_p = "user_position";
|
2011-04-27 06:49:56 +02:00
|
|
|
else if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_POSITION)
|
2009-08-17 17:02:45 +02:00
|
|
|
u_or_p = "program_position";
|
2008-12-09 13:59:50 +01:00
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
if(u_or_p)
|
|
|
|
{
|
|
|
|
lua_createtable(L, 0, 2);
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.x);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "x");
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.y);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "y");
|
|
|
|
lua_setfield(L, -2, u_or_p);
|
|
|
|
u_or_p = NULL;
|
|
|
|
}
|
2008-12-09 13:59:50 +01:00
|
|
|
|
2011-04-27 06:49:56 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_US_SIZE)
|
2009-08-17 17:02:45 +02:00
|
|
|
u_or_p = "user_size";
|
2011-04-27 06:49:56 +02:00
|
|
|
else if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_SIZE)
|
2009-08-17 17:02:45 +02:00
|
|
|
u_or_p = "program_size";
|
2008-12-09 13:59:50 +01:00
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
if(u_or_p)
|
|
|
|
{
|
|
|
|
lua_createtable(L, 0, 2);
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.width);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "width");
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.height);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "height");
|
|
|
|
lua_setfield(L, -2, u_or_p);
|
|
|
|
}
|
2008-12-09 13:59:50 +01:00
|
|
|
|
2011-04-27 06:49:56 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
|
2009-08-17 17:02:45 +02:00
|
|
|
{
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.min_width);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "min_width");
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.min_height);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "min_height");
|
|
|
|
}
|
2008-12-09 13:59:50 +01:00
|
|
|
|
2011-04-27 06:49:56 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
|
2009-08-17 17:02:45 +02:00
|
|
|
{
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.max_width);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "max_width");
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.max_height);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "max_height");
|
|
|
|
}
|
|
|
|
|
2011-04-27 06:49:56 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)
|
2009-08-17 17:02:45 +02:00
|
|
|
{
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.width_inc);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "width_inc");
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.height_inc);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "height_inc");
|
|
|
|
}
|
|
|
|
|
2011-04-27 06:49:56 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT)
|
2009-08-17 17:02:45 +02:00
|
|
|
{
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.min_aspect_num);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "min_aspect_num");
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.min_aspect_den);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "min_aspect_den");
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.max_aspect_num);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "max_aspect_num");
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.max_aspect_den);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "max_aspect_den");
|
|
|
|
}
|
|
|
|
|
2011-04-27 06:49:56 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE)
|
2009-08-17 17:02:45 +02:00
|
|
|
{
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.base_width);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "base_width");
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, c->size_hints.base_height);
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "base_height");
|
|
|
|
}
|
|
|
|
|
2011-04-27 06:49:56 +02:00
|
|
|
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)
|
2009-08-17 17:02:45 +02:00
|
|
|
{
|
|
|
|
switch(c->size_hints.win_gravity)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
lua_pushliteral(L, "north_west");
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_NORTH:
|
|
|
|
lua_pushliteral(L, "north");
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_NORTH_EAST:
|
|
|
|
lua_pushliteral(L, "north_east");
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_WEST:
|
|
|
|
lua_pushliteral(L, "west");
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_CENTER:
|
|
|
|
lua_pushliteral(L, "center");
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_EAST:
|
|
|
|
lua_pushliteral(L, "east");
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_SOUTH_WEST:
|
|
|
|
lua_pushliteral(L, "south_west");
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_SOUTH:
|
|
|
|
lua_pushliteral(L, "south");
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_SOUTH_EAST:
|
|
|
|
lua_pushliteral(L, "south_east");
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_STATIC:
|
|
|
|
lua_pushliteral(L, "static");
|
|
|
|
break;
|
2008-12-09 13:59:50 +01:00
|
|
|
}
|
2009-08-17 17:02:45 +02:00
|
|
|
lua_setfield(L, -2, "win_gravity");
|
2008-07-01 19:37:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-01-03 16:51:16 +01:00
|
|
|
/** Get the client's child window bounding shape.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param client The client object.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_get_client_shape_bounding(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surf = xwindow_get_shape(c->window, XCB_SHAPE_SK_BOUNDING);
|
|
|
|
if (!surf)
|
|
|
|
return 0;
|
|
|
|
/* lua has to make sure to free the ref or we have a leak */
|
|
|
|
lua_pushlightuserdata(L, surf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get the client's frame window bounding shape.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param client The client object.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_get_shape_bounding(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surf = xwindow_get_shape(c->frame_window, XCB_SHAPE_SK_BOUNDING);
|
|
|
|
if (!surf)
|
|
|
|
return 0;
|
|
|
|
/* lua has to make sure to free the ref or we have a leak */
|
|
|
|
lua_pushlightuserdata(L, surf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Set the client's frame window bounding shape.
|
2012-11-06 20:51:54 +01:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param client The client object.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_set_shape_bounding(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surf = NULL;
|
|
|
|
if(!lua_isnil(L, -1))
|
|
|
|
surf = (cairo_surface_t *)lua_touserdata(L, -1);
|
2012-11-12 17:20:11 +01:00
|
|
|
xwindow_set_shape(c->frame_window,
|
|
|
|
c->geometry.width + (c->border_width * 2),
|
|
|
|
c->geometry.height + (c->border_width * 2),
|
2012-11-06 20:51:54 +01:00
|
|
|
XCB_SHAPE_SK_BOUNDING, surf, -c->border_width);
|
2014-01-03 16:51:16 +01:00
|
|
|
luaA_object_emit_signal(L, -3, "property::shape_bounding", 0);
|
2012-11-06 20:51:54 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-03 16:51:16 +01:00
|
|
|
/** Get the client's child window clip shape.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param client The client object.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_get_client_shape_clip(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surf = xwindow_get_shape(c->window, XCB_SHAPE_SK_CLIP);
|
|
|
|
if (!surf)
|
|
|
|
return 0;
|
|
|
|
/* lua has to make sure to free the ref or we have a leak */
|
|
|
|
lua_pushlightuserdata(L, surf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get the client's frame window clip shape.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param client The client object.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_get_shape_clip(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surf = xwindow_get_shape(c->frame_window, XCB_SHAPE_SK_CLIP);
|
|
|
|
if (!surf)
|
|
|
|
return 0;
|
|
|
|
/* lua has to make sure to free the ref or we have a leak */
|
|
|
|
lua_pushlightuserdata(L, surf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Set the client's frame window clip shape.
|
2012-11-06 20:51:54 +01:00
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param client The client object.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_set_shape_clip(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surf = NULL;
|
|
|
|
if(!lua_isnil(L, -1))
|
|
|
|
surf = (cairo_surface_t *)lua_touserdata(L, -1);
|
|
|
|
xwindow_set_shape(c->frame_window, c->geometry.width, c->geometry.height,
|
|
|
|
XCB_SHAPE_SK_CLIP, surf, 0);
|
2014-01-03 16:51:16 +01:00
|
|
|
luaA_object_emit_signal(L, -3, "property::shape_clip", 0);
|
2012-11-06 20:51:54 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-26 11:12:41 +01:00
|
|
|
/** Get the client's frame window input shape.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param client The client object.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_get_shape_input(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surf = xwindow_get_shape(c->frame_window, XCB_SHAPE_SK_INPUT);
|
|
|
|
if (!surf)
|
|
|
|
return 0;
|
|
|
|
/* lua has to make sure to free the ref or we have a leak */
|
|
|
|
lua_pushlightuserdata(L, surf);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Set the client's frame window input shape.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \param client The client object.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_set_shape_input(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surf = NULL;
|
|
|
|
if(!lua_isnil(L, -1))
|
|
|
|
surf = (cairo_surface_t *)lua_touserdata(L, -1);
|
|
|
|
xwindow_set_shape(c->frame_window,
|
|
|
|
c->geometry.width + (c->border_width * 2),
|
|
|
|
c->geometry.height + (c->border_width * 2),
|
|
|
|
XCB_SHAPE_SK_INPUT, surf, -c->border_width);
|
|
|
|
luaA_object_emit_signal(L, -3, "property::shape_input", 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-14 16:46:35 +02:00
|
|
|
/** Get or set keys bindings for a client.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
2018-12-27 03:58:34 +01:00
|
|
|
* @property keys
|
2019-12-31 00:17:23 +01:00
|
|
|
* @tparam table keys
|
|
|
|
* @propemits false false
|
2018-12-27 03:58:34 +01:00
|
|
|
* @see awful.key
|
2009-08-14 16:46:35 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_keys(lua_State *L)
|
|
|
|
{
|
2009-09-30 11:55:43 +02:00
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
2009-08-14 16:46:35 +02:00
|
|
|
key_array_t *keys = &c->keys;
|
|
|
|
|
|
|
|
if(lua_gettop(L) == 2)
|
2009-08-14 16:52:22 +02:00
|
|
|
{
|
2009-08-14 16:46:35 +02:00
|
|
|
luaA_key_array_set(L, 1, 2, keys);
|
2009-08-17 17:02:45 +02:00
|
|
|
luaA_object_emit_signal(L, 1, "property::keys", 0);
|
2015-10-16 17:30:46 +02:00
|
|
|
xwindow_grabkeys(c->window, keys);
|
2016-02-28 13:29:25 +01:00
|
|
|
if (c->nofocus_window)
|
|
|
|
xwindow_grabkeys(c->nofocus_window, &c->keys);
|
2009-08-14 16:52:22 +02:00
|
|
|
}
|
2009-08-14 16:46:35 +02:00
|
|
|
|
|
|
|
return luaA_key_array_get(L, 1, keys);
|
|
|
|
}
|
|
|
|
|
2017-03-06 16:18:15 +01:00
|
|
|
static int
|
2018-07-21 10:19:32 +02:00
|
|
|
luaA_client_get_icon_sizes(lua_State *L, client_t *c)
|
2017-03-06 16:18:15 +01:00
|
|
|
{
|
|
|
|
int index = 1;
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
foreach (s, c->icons) {
|
|
|
|
/* Create a table { width, height } and append it to the table */
|
|
|
|
lua_createtable(L, 2, 0);
|
|
|
|
|
|
|
|
lua_pushinteger(L, cairo_image_surface_get_width(*s));
|
|
|
|
lua_rawseti(L, -2, 1);
|
|
|
|
|
|
|
|
lua_pushinteger(L, cairo_image_surface_get_height(*s));
|
|
|
|
lua_rawseti(L, -2, 2);
|
|
|
|
|
|
|
|
lua_rawseti(L, -2, index++);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get the client's n-th icon.
|
|
|
|
*
|
|
|
|
* @tparam interger index The index in the list of icons to get.
|
|
|
|
* @treturn surface A lightuserdata for a cairo surface. This reference must be
|
|
|
|
* destroyed!
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method get_icon
|
2017-03-06 16:18:15 +01:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_get_some_icon(lua_State *L)
|
|
|
|
{
|
|
|
|
client_t *c = luaA_checkudata(L, 1, &client_class);
|
|
|
|
int index = luaL_checkinteger(L, 2);
|
|
|
|
luaL_argcheck(L, (index >= 1 && index <= c->icons.len), 2,
|
|
|
|
"invalid icon index");
|
|
|
|
lua_pushlightuserdata(L, cairo_surface_reference(c->icons.tab[index-1]));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-12-06 18:16:05 +01:00
|
|
|
static int
|
|
|
|
client_tostring(lua_State *L, client_t *c)
|
|
|
|
{
|
|
|
|
char *name = c->name ? c->name : c->alt_name;
|
|
|
|
ssize_t len = a_strlen(name);
|
|
|
|
ssize_t limit = 20;
|
|
|
|
|
|
|
|
lua_pushlstring(L, name, MIN(len, limit));
|
|
|
|
if (len > limit)
|
|
|
|
lua_pushstring(L, "...");
|
|
|
|
return len > limit ? 2 : 1;
|
|
|
|
}
|
|
|
|
|
2008-08-12 12:08:20 +02:00
|
|
|
/* Client module.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \return The number of pushed elements.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_module_index(lua_State *L)
|
|
|
|
{
|
2010-09-02 19:13:27 +02:00
|
|
|
const char *buf = luaL_checkstring(L, 2);
|
2008-08-12 12:08:20 +02:00
|
|
|
|
2011-11-17 16:38:39 +01:00
|
|
|
if (A_STREQ(buf, "focus"))
|
2014-12-06 11:56:58 +01:00
|
|
|
return luaA_object_push(L, globalconf.focus.client);
|
2010-09-02 19:13:27 +02:00
|
|
|
return 0;
|
2008-08-12 12:08:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Client module new index.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \return The number of pushed elements.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_client_module_newindex(lua_State *L)
|
|
|
|
{
|
2010-09-02 19:13:27 +02:00
|
|
|
const char *buf = luaL_checkstring(L, 2);
|
2009-04-10 15:23:55 +02:00
|
|
|
client_t *c;
|
2008-08-12 12:08:20 +02:00
|
|
|
|
2011-11-17 16:38:39 +01:00
|
|
|
if (A_STREQ(buf, "focus"))
|
2008-08-12 12:08:20 +02:00
|
|
|
{
|
2015-03-14 09:06:12 +01:00
|
|
|
c = luaA_checkudataornil(L, 3, &client_class);
|
|
|
|
if (c)
|
|
|
|
client_focus(c);
|
|
|
|
else if (globalconf.focus.client)
|
|
|
|
client_unfocus(globalconf.focus.client);
|
2008-08-12 12:08:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-30 11:55:43 +02:00
|
|
|
static bool
|
|
|
|
client_checker(client_t *c)
|
|
|
|
{
|
2009-10-13 15:43:50 +02:00
|
|
|
return c->window != XCB_NONE;
|
2009-09-30 11:55:43 +02:00
|
|
|
}
|
|
|
|
|
2009-08-17 17:02:45 +02:00
|
|
|
void
|
|
|
|
client_class_setup(lua_State *L)
|
|
|
|
{
|
2012-06-06 23:07:07 +02:00
|
|
|
static const struct luaL_Reg client_methods[] =
|
2009-08-17 17:02:45 +02:00
|
|
|
{
|
|
|
|
LUA_CLASS_METHODS(client)
|
|
|
|
{ "get", luaA_client_get },
|
|
|
|
{ "__index", luaA_client_module_index },
|
|
|
|
{ "__newindex", luaA_client_module_newindex },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
2012-06-06 23:07:07 +02:00
|
|
|
static const struct luaL_Reg client_meta[] =
|
2009-08-17 17:02:45 +02:00
|
|
|
{
|
|
|
|
LUA_OBJECT_META(client)
|
|
|
|
LUA_CLASS_META
|
2018-12-27 03:58:34 +01:00
|
|
|
{ "_keys", luaA_client_keys },
|
2009-08-17 17:02:45 +02:00
|
|
|
{ "isvisible", luaA_client_isvisible },
|
|
|
|
{ "geometry", luaA_client_geometry },
|
2015-02-14 15:12:46 +01:00
|
|
|
{ "apply_size_hints", luaA_client_apply_size_hints },
|
2009-08-17 17:02:45 +02:00
|
|
|
{ "tags", luaA_client_tags },
|
|
|
|
{ "kill", luaA_client_kill },
|
|
|
|
{ "swap", luaA_client_swap },
|
|
|
|
{ "raise", luaA_client_raise },
|
|
|
|
{ "lower", luaA_client_lower },
|
|
|
|
{ "unmanage", luaA_client_unmanage },
|
2012-10-14 17:20:24 +02:00
|
|
|
{ "titlebar_top", luaA_client_titlebar_top },
|
|
|
|
{ "titlebar_right", luaA_client_titlebar_right },
|
|
|
|
{ "titlebar_bottom", luaA_client_titlebar_bottom },
|
|
|
|
{ "titlebar_left", luaA_client_titlebar_left },
|
2017-03-06 16:18:15 +01:00
|
|
|
{ "get_icon", luaA_client_get_some_icon },
|
2009-08-17 17:02:45 +02:00
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
2009-09-30 16:04:42 +02:00
|
|
|
luaA_class_setup(L, &client_class, "client", &window_class,
|
2009-09-30 14:21:25 +02:00
|
|
|
(lua_class_allocator_t) client_new,
|
2009-10-02 15:48:32 +02:00
|
|
|
(lua_class_collector_t) client_wipe,
|
2009-09-30 11:55:43 +02:00
|
|
|
(lua_class_checker_t) client_checker,
|
2009-08-20 17:37:46 +02:00
|
|
|
luaA_class_index_miss_property, luaA_class_newindex_miss_property,
|
2009-08-17 17:02:45 +02:00
|
|
|
client_methods, client_meta);
|
2014-12-06 18:16:05 +01:00
|
|
|
luaA_class_set_tostring(&client_class, (lua_class_propfunc_t) client_tostring);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "name",
|
2015-02-02 09:26:19 +01:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_name,
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_get_name,
|
2015-02-02 09:26:19 +01:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_name);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "transient_for",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_transient_for,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "skip_taskbar",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_skip_taskbar,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_skip_taskbar,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_skip_taskbar);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "content",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_content,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "type",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
2010-09-27 21:16:30 +02:00
|
|
|
(lua_class_propfunc_t) luaA_window_get_type,
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "class",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_class,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "instance",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_instance,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "role",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_role,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "pid",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_pid,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "leader_window",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_leader_window,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "machine",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_machine,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "icon_name",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_icon_name,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "screen",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_screen,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_screen);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "hidden",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_hidden,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_hidden,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_hidden);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "minimized",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_minimized,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_minimized,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_minimized);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "fullscreen",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_fullscreen,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_fullscreen,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_fullscreen);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "modal",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_modal,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_modal,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_modal);
|
2018-08-03 19:19:43 +02:00
|
|
|
luaA_class_add_property(&client_class, "motif_wm_hints",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_motif_wm_hints,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "group_window",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_group_window,
|
|
|
|
NULL);
|
2014-03-14 04:14:03 +01:00
|
|
|
luaA_class_add_property(&client_class, "maximized",
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_maximized,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_maximized,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_maximized);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "maximized_horizontal",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_maximized_horizontal,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_maximized_horizontal,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_maximized_horizontal);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "maximized_vertical",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_maximized_vertical,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_maximized_vertical,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_maximized_vertical);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "icon",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_icon,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_icon,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_icon);
|
2017-03-06 16:18:15 +01:00
|
|
|
luaA_class_add_property(&client_class, "icon_sizes",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_icon_sizes,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "ontop",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_ontop,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_ontop,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_ontop);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "above",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_above,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_above,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_above);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "below",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_below,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_below,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_below);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "sticky",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_sticky,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_sticky,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_sticky);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "size_hints_honor",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_size_hints_honor,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_size_hints_honor,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_size_hints_honor);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "urgent",
|
2009-08-17 17:02:45 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_urgent,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_urgent,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_urgent);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "size_hints",
|
2009-08-17 17:02:45 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_size_hints,
|
|
|
|
NULL);
|
2010-09-01 15:41:41 +02:00
|
|
|
luaA_class_add_property(&client_class, "focusable",
|
2015-05-21 17:30:51 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_focusable,
|
2010-08-12 13:08:55 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_get_focusable,
|
2015-05-21 17:30:51 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_focusable);
|
2012-11-06 20:51:54 +01:00
|
|
|
luaA_class_add_property(&client_class, "shape_bounding",
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_shape_bounding,
|
2014-01-03 16:51:16 +01:00
|
|
|
(lua_class_propfunc_t) luaA_client_get_shape_bounding,
|
2012-11-06 20:51:54 +01:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_shape_bounding);
|
|
|
|
luaA_class_add_property(&client_class, "shape_clip",
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_shape_clip,
|
2014-01-03 16:51:16 +01:00
|
|
|
(lua_class_propfunc_t) luaA_client_get_shape_clip,
|
2012-11-06 20:51:54 +01:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_shape_clip);
|
2017-01-26 11:12:41 +01:00
|
|
|
luaA_class_add_property(&client_class, "shape_input",
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_shape_input,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_shape_input,
|
|
|
|
(lua_class_propfunc_t) luaA_client_set_shape_input);
|
2014-03-17 16:34:35 +01:00
|
|
|
luaA_class_add_property(&client_class, "startup_id",
|
2018-07-23 10:04:07 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_startup_id,
|
2014-03-15 23:09:33 +01:00
|
|
|
(lua_class_propfunc_t) luaA_client_get_startup_id,
|
2018-07-23 10:04:07 +02:00
|
|
|
(lua_class_propfunc_t) luaA_client_set_startup_id);
|
2014-01-03 16:51:16 +01:00
|
|
|
luaA_class_add_property(&client_class, "client_shape_bounding",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_client_shape_bounding,
|
|
|
|
NULL);
|
|
|
|
luaA_class_add_property(&client_class, "client_shape_clip",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_client_shape_clip,
|
|
|
|
NULL);
|
2015-07-14 00:27:26 +02:00
|
|
|
luaA_class_add_property(&client_class, "first_tag",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_client_get_first_tag,
|
|
|
|
NULL);
|
2009-08-17 17:02:45 +02:00
|
|
|
}
|
2008-05-20 15:39:47 +02:00
|
|
|
|
2017-10-21 01:54:36 +02:00
|
|
|
/* @DOC_cobject_COMMON@ */
|
|
|
|
|
2019-11-11 02:47:43 +01:00
|
|
|
/* @DOC_client_theme_COMMON@ */
|
|
|
|
|
2011-09-11 16:50:01 +02:00
|
|
|
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|