diff --git a/src/sxwm.c b/src/sxwm.c
index de43ce3..b170567 100644
--- a/src/sxwm.c
+++ b/src/sxwm.c
@@ -1011,6 +1011,34 @@ void hdl_client_msg(XEvent *xev)
}
return;
}
+
+ if (xev->xclient.message_type == atoms[ATOM_NET_ACTIVE_WINDOW]) {
+ /* external activation request (pagers/taskbars, e.g. `wmctrl -a`)
+ * -- _NET_ACTIVE_WINDOW is advertised in _NET_SUPPORTED and we
+ * already publish it read-only to reflect our own focus, but
+ * nothing accepted requests to change it until now. Switch to
+ * the client's workspace first if it's not the current one, same
+ * as a pager would expect, then focus/raise it exactly like
+ * focus_next/focus_prev do.
+ *
+ * data.l[0] is the EWMH source indication: 2 means a pager/taskbar
+ * acting on an explicit user click (mouse already elsewhere, so
+ * warp it to the window), 1 means the application is requesting
+ * its own activation. Chromium/Electron apps (VS Code, among
+ * others) send the latter when clicked while unfocused -- warping
+ * there yanks the pointer away from right where the user just
+ * clicked, so only warp for real pager/taskbar requests. */
+ XClientMessageEvent *client_msg_ev = &xev->xclient;
+ Window w = client_msg_ev->window;
+ Client *c = find_client(find_toplevel(w));
+ if (!c)
+ return;
+
+ if (c->ws != current_ws)
+ change_workspace(c->ws);
+ set_input_focus(c, True, client_msg_ev->data.l[0] == 2);
+ return;
+ }
}
void hdl_config_ntf(XEvent *xev)