diff --git a/common/atoms.list b/common/atoms.list index 011e295a..4dd304ba 100644 --- a/common/atoms.list +++ b/common/atoms.list @@ -64,3 +64,4 @@ XSEL_DATA WM_TAKE_FOCUS AWESOME_CLIENT_ORDER _XKB_RULES_NAMES +_MOTIF_WM_HINTS diff --git a/objects/client.c b/objects/client.c index 81720563..edc866af 100644 --- a/objects/client.c +++ b/objects/client.c @@ -646,6 +646,43 @@ * @see size_hints_honor */ +/** + * 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. + * + * **Signal:** + * + * * *property::motif\_wm\_hints* + * + * @property motif_wm_hints + * @param table + * @tfield[opt] table table.functions + * @tfield[opt] boolean table.functions.all + * @tfield[opt] boolean table.functions.resize + * @tfield[opt] boolean table.functions.move + * @tfield[opt] boolean table.functions.minimize + * @tfield[opt] boolean table.functions.maximize + * @tfield[opt] boolean table.functions.close + * @tfield[opt] table table.decorations + * @tfield[opt] boolean table.decorations.all + * @tfield[opt] boolean table.decorations.border + * @tfield[opt] boolean table.decorations.resizeh + * @tfield[opt] boolean table.decorations.title + * @tfield[opt] boolean table.decorations.menu + * @tfield[opt] boolean table.decorations.minimize + * @tfield[opt] boolean table.decorations.maximize + * @tfield[opt] string table.input_mode + * @tfield[opt] table table.status + * @tfield[opt] boolean table.status.tearoff_window + */ + /** * Set the client sticky, i.e. available on all tags. * @@ -978,6 +1015,17 @@ DO_CLIENT_SET_STRING_PROPERTY(role) DO_CLIENT_SET_STRING_PROPERTY(machine) #undef DO_CLIENT_SET_STRING_PROPERTY +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); +} + void client_find_transient_for(client_t *c) { @@ -1416,6 +1464,7 @@ client_update_properties(lua_State *L, int cidx, client_t *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); + xcb_get_property_cookie_t motif_wm_hints = property_get_motif_wm_hints(c); xcb_get_property_cookie_t opacity = xwindow_get_opacity_unchecked(c->window); /* update strut */ @@ -1436,6 +1485,7 @@ client_update_properties(lua_State *L, int cidx, client_t *c) 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); + property_update_motif_wm_hints(c, motif_wm_hints); window_set_opacity(L, cidx, xwindow_get_opacity_from_cookie(opacity)); } @@ -3156,6 +3206,78 @@ LUA_OBJECT_EXPORT_PROPERTY(client, client_t, maximized_vertical, lua_pushboolean LUA_OBJECT_EXPORT_PROPERTY(client, client_t, maximized, lua_pushboolean) LUA_OBJECT_EXPORT_PROPERTY(client, client_t, startup_id, lua_pushstring) +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; +} + static int luaA_client_get_content(lua_State *L, client_t *c) { @@ -3721,6 +3843,10 @@ client_class_setup(lua_State *L) (lua_class_propfunc_t) luaA_client_set_modal, (lua_class_propfunc_t) luaA_client_get_modal, (lua_class_propfunc_t) luaA_client_set_modal); + luaA_class_add_property(&client_class, "motif_wm_hints", + NULL, + (lua_class_propfunc_t) luaA_client_get_motif_wm_hints, + NULL); luaA_class_add_property(&client_class, "group_window", NULL, (lua_class_propfunc_t) luaA_client_get_group_window, diff --git a/objects/client.h b/objects/client.h index f9115961..45bc9338 100644 --- a/objects/client.h +++ b/objects/client.h @@ -47,6 +47,48 @@ typedef enum { CLIENT_TITLEBAR_COUNT = 4 } client_titlebar_t; +/* Special bit we invented to "fake" unset hints */ +#define MWM_HINTS_AWESOME_SET (1L << 15) + +/* The following is taken from MwmUtil.h and slightly adapted, which is + * copyright (c) 1987-2012, The Open Group. + * It is licensed under GPLv2 or later. + */ +#define MWM_HINTS_FUNCTIONS (1L << 0) +#define MWM_HINTS_DECORATIONS (1L << 1) +#define MWM_HINTS_INPUT_MODE (1L << 2) +#define MWM_HINTS_STATUS (1L << 3) + +#define MWM_FUNC_ALL (1L << 0) +#define MWM_FUNC_RESIZE (1L << 1) +#define MWM_FUNC_MOVE (1L << 2) +#define MWM_FUNC_MINIMIZE (1L << 3) +#define MWM_FUNC_MAXIMIZE (1L << 4) +#define MWM_FUNC_CLOSE (1L << 5) + +#define MWM_DECOR_ALL (1L << 0) +#define MWM_DECOR_BORDER (1L << 1) +#define MWM_DECOR_RESIZEH (1L << 2) +#define MWM_DECOR_TITLE (1L << 3) +#define MWM_DECOR_MENU (1L << 4) +#define MWM_DECOR_MINIMIZE (1L << 5) +#define MWM_DECOR_MAXIMIZE (1L << 6) + +#define MWM_INPUT_MODELESS 0 +#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 +#define MWM_INPUT_SYSTEM_MODAL 2 +#define MWM_INPUT_FULL_APPLICATION_MODAL 3 + +#define MWM_TEAROFF_WINDOW (1L<<0) + +typedef struct { + uint32_t hints; + uint32_t functions; + uint32_t decorations; + int32_t input_mode; + uint32_t status; +} motif_wm_hints_t; + /** client_t type */ struct client_t { @@ -143,6 +185,8 @@ struct client_t /** The drawable for this bar. */ drawable_t *drawable; } titlebar[CLIENT_TITLEBAR_COUNT]; + /** Motif WM hints, with an additional MWM_HINTS_AWESOME_SET bit */ + motif_wm_hints_t motif_wm_hints; }; ARRAY_FUNCS(client_t *, client, DO_NOTHING) @@ -190,6 +234,7 @@ void client_set_group_window(lua_State *, int, xcb_window_t); void client_set_icons(client_t *, cairo_surface_array_t); void client_set_icon_from_pixmaps(client_t *, xcb_pixmap_t, xcb_pixmap_t); void client_set_skip_taskbar(lua_State *, int, bool); +void client_set_motif_wm_hints(lua_State *, int, motif_wm_hints_t); void client_focus(client_t *); bool client_focus_update(client_t *); bool client_hasproto(client_t *, xcb_atom_t); diff --git a/property.c b/property.c index b5c5de14..6a0ea43f 100644 --- a/property.c +++ b/property.c @@ -91,6 +91,7 @@ HANDLE_PROPERTY(wm_hints) HANDLE_PROPERTY(wm_class) HANDLE_PROPERTY(net_wm_icon) HANDLE_PROPERTY(net_wm_pid) +HANDLE_PROPERTY(motif_wm_hints) #undef HANDLE_PROPERTY @@ -304,6 +305,41 @@ property_update_net_wm_pid(client_t *c, xcb_get_property_cookie_t cookie) p_delete(&reply); } +xcb_get_property_cookie_t +property_get_motif_wm_hints(client_t *c) +{ + return xcb_get_property_unchecked(globalconf.connection, false, c->window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 0L, 5L); +} + +void +property_update_motif_wm_hints(client_t *c, xcb_get_property_cookie_t cookie) +{ + motif_wm_hints_t hints; + xcb_get_property_reply_t *reply; + lua_State *L = globalconf_get_lua_State(); + + /* Clear the hints */ + p_clear(&hints, 1); + + reply = xcb_get_property_reply(globalconf.connection, cookie, NULL); + + if(reply && reply->value_len == 5) + { + uint32_t *rdata = xcb_get_property_value(reply); + if(rdata) + { + memcpy(&hints, rdata, sizeof(hints)); + hints.hints |= MWM_HINTS_AWESOME_SET; + } + } + + p_delete(&reply); + + luaA_object_push(L, c); + client_set_motif_wm_hints(L, -1, hints); + lua_pop(L, 1); +} + xcb_get_property_cookie_t property_get_wm_protocols(client_t *c) { @@ -478,6 +514,9 @@ property_handle_propertynotify(xcb_property_notify_event_t *ev) HANDLE(_NET_WM_PID, property_handle_net_wm_pid) HANDLE(_NET_WM_WINDOW_OPACITY, property_handle_net_wm_opacity) + /* MOTIF hints */ + HANDLE(_MOTIF_WM_HINTS, property_handle_motif_wm_hints) + /* background change */ HANDLE(_XROOTPMAP_ID, property_handle_xrootpmap_id) diff --git a/property.h b/property.h index 588fb000..b7c833e5 100644 --- a/property.h +++ b/property.h @@ -42,6 +42,7 @@ PROPERTY(wm_class); PROPERTY(wm_protocols); PROPERTY(net_wm_pid); PROPERTY(net_wm_icon); +PROPERTY(motif_wm_hints); #undef PROPERTY