-#undef I3__FILE__
-#define I3__FILE__ "handlers.c"
/*
* vim:ts=4:sw=4:expandtab
*
enter_child = true;
}
- /* If not, then the user moved their cursor to the root window. In that case, we adjust c_ws */
- if (con == NULL) {
+ /* If we cannot find the container, the user moved their cursor to the root
+ * window. In this case and if they used it to a dock, we need to focus the
+ * workspace on the correct output. */
+ if (con == NULL || con->parent->type == CT_DOCKAREA) {
DLOG("Getting screen at %d x %d\n", event->root_x, event->root_y);
check_crossing_screen_boundary(event->root_x, event->root_y);
return;
}
- if (con->parent->type == CT_DOCKAREA) {
- DLOG("Ignoring, this is a dock client\n");
- return;
- }
-
/* see if the user entered the window on a certain window decoration */
layout_t layout = (enter_child ? con->parent->layout : con->layout);
if (layout == L_DEFAULT) {
}
}
-#if 0
- if (client->workspace != c_ws && client->workspace->output == c_ws->output) {
- /* This can happen when a client gets assigned to a different workspace than
- * the current one (see src/mainx.c:reparent_window). Shortly after it was created,
- * an enter_notify will follow. */
- DLOG("enter_notify for a client on a different workspace but the same screen, ignoring\n");
- return 1;
- }
-#endif
-
if (config.disable_focus_follows_mouse)
return;
return;
}
-#if 0
-
-/*
- * Configuration notifies are only handled because we need to set up ignore for
- * the following enter notify events.
- *
- */
-int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event) {
- DLOG("configure_event, sequence %d\n", event->sequence);
- /* We ignore this sequence twice because events for child and frame should be ignored */
- add_ignore_event(event->sequence);
- add_ignore_event(event->sequence);
-
- return 1;
-}
-#endif
/*
* Gets triggered upon a RandR screen change event, that is when the user
goto ignore_end;
}
+ /* Since we close the container, we need to unset _NET_WM_DESKTOP and
+ * _NET_WM_STATE according to the spec. */
+ xcb_delete_property(conn, event->window, A__NET_WM_DESKTOP);
+ xcb_delete_property(conn, event->window, A__NET_WM_STATE);
+
tree_close_internal(con, DONT_KILL_WINDOW, false, false);
tree_render();
return true;
}
-#if 0
-/*
- * Updates the client’s WM_CLASS property
- *
- */
-static int handle_windowclass_change(void *data, xcb_connection_t *conn, uint8_t state,
- xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
- Con *con;
- if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
- return 1;
-
- window_update_class(con->window, prop, false);
-
- return 0;
-}
-#endif
-
/*
* Expose event means we should redraw our windows (= title bar)
*
con->sticky = !con->sticky;
DLOG("New sticky status for con = %p is %i.\n", con, con->sticky);
+ ewmh_update_sticky(con->window->id, con->sticky);
output_push_sticky_windows(focused);
+ ewmh_update_wm_desktop();
}
tree_render();
}
Con *ws = con_get_workspace(con);
-
if (ws == NULL) {
DLOG("Window is not being managed, ignoring _NET_ACTIVE_WINDOW\n");
return;
}
- if (con_is_internal(ws)) {
- DLOG("Workspace is internal, ignoring _NET_ACTIVE_WINDOW\n");
+ if (con_is_internal(ws) && ws != workspace_get("__i3_scratch", NULL)) {
+ DLOG("Workspace is internal but not scratchpad, ignoring _NET_ACTIVE_WINDOW\n");
return;
}
/* Always focus the con if it is from a pager, because this is most
* likely from some user action */
DLOG("This request came from a pager. Focusing con = %p\n", con);
- workspace_show(ws);
- con_focus(con);
+
+ if (con_is_internal(ws)) {
+ scratchpad_show(con);
+ } else {
+ workspace_show(ws);
+ con_focus(con);
+ }
} else {
/* Request is from an application. */
+ if (con_is_internal(ws)) {
+ DLOG("Ignoring request to make con = %p active because it's on an internal workspace.\n", con);
+ return;
+ }
if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) {
DLOG("Focusing con = %p\n", con);
* a request to focus the given workspace. See
* http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008
* */
- Con *output;
- uint32_t idx = 0;
DLOG("Request to change current desktop to index %d\n", event->data.data32[0]);
+ Con *ws = ewmh_get_workspace_by_index(event->data.data32[0]);
+ if (ws == NULL) {
+ ELOG("Could not determine workspace for this index, ignoring request.\n");
+ return;
+ }
- TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
- Con *ws;
- TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
- if (STARTS_WITH(ws->name, "__"))
- continue;
-
- if (idx == event->data.data32[0]) {
- /* data32[1] is a timestamp used to prevent focus race conditions */
- if (event->data.data32[1])
- last_timestamp = event->data.data32[1];
+ DLOG("Handling request to focus workspace %s\n", ws->name);
+ workspace_show(ws);
+ tree_render();
+ } else if (event->type == A__NET_WM_DESKTOP) {
+ uint32_t index = event->data.data32[0];
+ DLOG("Request to move window %d to EWMH desktop index %d\n", event->window, index);
- DLOG("Handling request to focus workspace %s\n", ws->name);
+ Con *con = con_by_window_id(event->window);
+ if (con == NULL) {
+ DLOG("Couldn't find con for window %d, ignoring the request.\n", event->window);
+ return;
+ }
- workspace_show(ws);
- tree_render();
+ if (index == NET_WM_DESKTOP_ALL) {
+ /* The window is requesting to be visible on all workspaces, so
+ * let's float it and make it sticky. */
+ DLOG("The window was requested to be visible on all workspaces, making it sticky and floating.\n");
- return;
- }
+ floating_enable(con, false);
- ++idx;
+ con->sticky = true;
+ ewmh_update_sticky(con->window->id, true);
+ output_push_sticky_windows(focused);
+ } else {
+ Con *ws = ewmh_get_workspace_by_index(index);
+ if (ws == NULL) {
+ ELOG("Could not determine workspace for this index, ignoring request.\n");
+ return;
}
+
+ con_move_to_workspace(con, ws, true, false, false);
}
+
+ tree_render();
+ ewmh_update_wm_desktop();
} else if (event->type == A__NET_CLOSE_WINDOW) {
/*
* Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW
break;
}
} else {
- DLOG("unhandled clientmessage\n");
- return;
+ DLOG("Skipping client message for unhandled type %d\n", event->type);
}
}
return true;
}
+/*
+ * Handles the _MOTIF_WM_HINTS property of specifing window deocration settings.
+ *
+ */
+static bool handle_motif_hints_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
+ xcb_atom_t name, xcb_get_property_reply_t *prop) {
+ Con *con;
+ if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
+ return false;
+
+ if (prop == NULL) {
+ prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
+ false, window, A__MOTIF_WM_HINTS, XCB_GET_PROPERTY_TYPE_ANY, 0, 5 * sizeof(uint64_t)),
+ NULL);
+
+ if (prop == NULL)
+ return false;
+ }
+
+ border_style_t motif_border_style;
+ window_update_motif_hints(con->window, prop, &motif_border_style);
+
+ if (motif_border_style != con->border_style && motif_border_style != BS_NORMAL) {
+ DLOG("Update border style of con %p to %d\n", con, motif_border_style);
+ con_set_border_style(con, motif_border_style, con->current_border_width);
+
+ x_push_changes(croot);
+ }
+
+ return true;
+}
+
/*
* Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients.
*
{0, 128, handle_windowrole_change},
{0, 128, handle_class_change},
{0, UINT_MAX, handle_strut_partial_change},
- {0, UINT_MAX, handle_window_type}};
+ {0, UINT_MAX, handle_window_type},
+ {0, 5 * sizeof(uint64_t), handle_motif_hints_change}};
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
/*
property_handlers[7].atom = XCB_ATOM_WM_CLASS;
property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL;
property_handlers[9].atom = A__NET_WM_WINDOW_TYPE;
+ property_handlers[10].atom = A__MOTIF_WM_HINTS;
}
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {