Implement support for the WM_Sn selection (#4374)
Closes #536 When the WM_Sn selection is already owned at startup, this now either errors out or waits for the old selection owner to exit.
This commit is contained in:
parent
36ba1043d5
commit
7b6e864823
@ -43,6 +43,8 @@ option is enabled and only then sets a screenshot as background.
|
||||
• Allow multiple output names in 'move container|workspace to output'
|
||||
• Add 'move container|workspace to output next'
|
||||
• Add 'all' window matching criterion
|
||||
• Acquire the WM_Sn selection when starting as required by ICCCM
|
||||
• Add --replace command line argument to replace an existing WM
|
||||
|
||||
┌────────────────────────────┐
|
||||
│ Bugfixes │
|
||||
|
@ -21,4 +21,5 @@ xmacro(I3_FLOATING_WINDOW) \
|
||||
xmacro(_NET_REQUEST_FRAME_EXTENTS) \
|
||||
xmacro(_NET_FRAME_EXTENTS) \
|
||||
xmacro(_MOTIF_WM_HINTS) \
|
||||
xmacro(WM_CHANGE_STATE)
|
||||
xmacro(WM_CHANGE_STATE) \
|
||||
xmacro(MANAGER)
|
||||
|
@ -39,6 +39,7 @@ extern bool debug_build;
|
||||
/** The number of file descriptors passed via socket activation. */
|
||||
extern int listen_fds;
|
||||
extern int conn_screen;
|
||||
extern xcb_atom_t wm_sn;
|
||||
/**
|
||||
* The EWMH support window that is used to indicate that an EWMH-compliant
|
||||
* window manager is present. This window is created when i3 starts and
|
||||
|
@ -44,6 +44,9 @@ Retrieve the i3 IPC socket path from X11, print it, then exit.
|
||||
Limits the size of the i3 SHM log to <limit> bytes. Setting this to 0 disables
|
||||
SHM logging entirely. The default is 0 bytes.
|
||||
|
||||
--replace::
|
||||
Replace an existing window manager.
|
||||
|
||||
== DESCRIPTION
|
||||
|
||||
=== INTRODUCTION
|
||||
|
@ -1076,6 +1076,21 @@ static void handle_configure_notify(xcb_configure_notify_event_t *event) {
|
||||
randr_query_outputs();
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles SelectionClear events for the root window, which are generated when
|
||||
* we lose ownership of a selection.
|
||||
*/
|
||||
static void handle_selection_clear(xcb_selection_clear_event_t *event) {
|
||||
if (event->selection != wm_sn) {
|
||||
DLOG("SelectionClear for unknown selection %d, ignoring\n", event->selection);
|
||||
return;
|
||||
}
|
||||
LOG("Lost WM_Sn selection, exiting.\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
/* unreachable */
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles the WM_CLASS property for assignments and criteria selection.
|
||||
*
|
||||
@ -1428,6 +1443,10 @@ void handle_event(int type, xcb_generic_event_t *event) {
|
||||
handle_configure_notify((xcb_configure_notify_event_t *)event);
|
||||
break;
|
||||
|
||||
case XCB_SELECTION_CLEAR:
|
||||
handle_selection_clear((xcb_selection_clear_event_t *)event);
|
||||
break;
|
||||
|
||||
default:
|
||||
//DLOG("Unhandled event of type %d\n", type);
|
||||
break;
|
||||
|
131
src/main.c
131
src/main.c
@ -24,6 +24,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <xcb/xcb_atom.h>
|
||||
#include <xcb/xinerama.h>
|
||||
#include <xcb/bigreq.h>
|
||||
|
||||
@ -65,6 +66,9 @@ xcb_timestamp_t last_timestamp = XCB_CURRENT_TIME;
|
||||
xcb_screen_t *root_screen;
|
||||
xcb_window_t root;
|
||||
|
||||
xcb_window_t wm_sn_selection_owner;
|
||||
xcb_atom_t wm_sn;
|
||||
|
||||
/* Color depth, visual id and colormap to use when creating windows and
|
||||
* pixmaps. Will use 32 bit depth and an appropriate visual, if available,
|
||||
* otherwise the root window’s default (usually 24 bit TrueColor). */
|
||||
@ -284,6 +288,7 @@ int main(int argc, char *argv[]) {
|
||||
char *fake_outputs = NULL;
|
||||
bool disable_signalhandler = false;
|
||||
bool only_check_config = false;
|
||||
bool replace_wm = false;
|
||||
static struct option long_options[] = {
|
||||
{"no-autostart", no_argument, 0, 'a'},
|
||||
{"config", required_argument, 0, 'c'},
|
||||
@ -306,6 +311,7 @@ int main(int argc, char *argv[]) {
|
||||
{"fake_outputs", required_argument, 0, 0},
|
||||
{"fake-outputs", required_argument, 0, 0},
|
||||
{"force-old-config-parser-v4.4-only", no_argument, 0, 0},
|
||||
{"replace", no_argument, 0, 'r'},
|
||||
{0, 0, 0, 0}};
|
||||
int option_index = 0, opt;
|
||||
|
||||
@ -330,7 +336,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
start_argv = argv;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "c:CvmaL:hld:V", long_options, &option_index)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "c:CvmaL:hld:Vr", long_options, &option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
LOG("Autostart disabled using -a\n");
|
||||
@ -368,6 +374,9 @@ int main(int argc, char *argv[]) {
|
||||
case 'l':
|
||||
/* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */
|
||||
break;
|
||||
case 'r':
|
||||
replace_wm = true;
|
||||
break;
|
||||
case 0:
|
||||
if (strcmp(long_options[option_index].name, "force-xinerama") == 0 ||
|
||||
strcmp(long_options[option_index].name, "force_xinerama") == 0) {
|
||||
@ -448,6 +457,9 @@ int main(int argc, char *argv[]) {
|
||||
"\tThe default is %d bytes.\n",
|
||||
shmlog_size);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "\t--replace\n"
|
||||
"\tReplace an existing window manager.\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "If you pass plain text arguments, i3 will interpret them as a command\n"
|
||||
"to send to a currently running i3 (like i3-msg). This allows you to\n"
|
||||
"use nice and logical commands, such as:\n"
|
||||
@ -586,6 +598,10 @@ int main(int argc, char *argv[]) {
|
||||
xcb_prefetch_extension_data(conn, &xcb_randr_id);
|
||||
}
|
||||
|
||||
/* Prepare for us to get a current timestamp as recommended by ICCCM */
|
||||
xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_PROPERTY_CHANGE});
|
||||
xcb_change_property(conn, XCB_PROP_MODE_APPEND, root, XCB_ATOM_SUPERSCRIPT_X, XCB_ATOM_CARDINAL, 32, 0, "");
|
||||
|
||||
/* Place requests for the atoms we need as soon as possible */
|
||||
#define xmacro(atom) \
|
||||
xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
|
||||
@ -627,6 +643,22 @@ int main(int argc, char *argv[]) {
|
||||
xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(conn, root);
|
||||
xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
|
||||
|
||||
/* Get the PropertyNotify event we caused above */
|
||||
xcb_flush(conn);
|
||||
{
|
||||
xcb_generic_event_t *event;
|
||||
DLOG("waiting for PropertyNotify event\n");
|
||||
while ((event = xcb_wait_for_event(conn)) != NULL) {
|
||||
if (event->response_type == XCB_PROPERTY_NOTIFY) {
|
||||
last_timestamp = ((xcb_property_notify_event_t *)event)->time;
|
||||
free(event);
|
||||
break;
|
||||
}
|
||||
free(event);
|
||||
}
|
||||
DLOG("got timestamp %d\n", last_timestamp);
|
||||
}
|
||||
|
||||
/* Setup NetWM atoms */
|
||||
#define xmacro(name) \
|
||||
do { \
|
||||
@ -656,6 +688,103 @@ int main(int argc, char *argv[]) {
|
||||
force_xinerama = true;
|
||||
}
|
||||
|
||||
/* Acquire the WM_Sn selection. */
|
||||
{
|
||||
/* Get the WM_Sn atom */
|
||||
char *atom_name = xcb_atom_name_by_screen("WM_S", conn_screen);
|
||||
wm_sn_selection_owner = xcb_generate_id(conn);
|
||||
|
||||
if (atom_name == NULL) {
|
||||
ELOG("xcb_atom_name_by_screen(\"WM_S\", %d) failed, exiting\n", conn_screen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
xcb_intern_atom_reply_t *atom_reply;
|
||||
atom_reply = xcb_intern_atom_reply(conn,
|
||||
xcb_intern_atom_unchecked(conn,
|
||||
0,
|
||||
strlen(atom_name),
|
||||
atom_name),
|
||||
NULL);
|
||||
free(atom_name);
|
||||
if (atom_reply == NULL) {
|
||||
ELOG("Failed to intern the WM_Sn atom, exiting\n");
|
||||
return 1;
|
||||
}
|
||||
wm_sn = atom_reply->atom;
|
||||
free(atom_reply);
|
||||
|
||||
/* Check if the selection is already owned */
|
||||
xcb_get_selection_owner_reply_t *selection_reply =
|
||||
xcb_get_selection_owner_reply(conn,
|
||||
xcb_get_selection_owner(conn, wm_sn),
|
||||
NULL);
|
||||
if (selection_reply && selection_reply->owner != XCB_NONE && !replace_wm) {
|
||||
ELOG("Another window manager is already running (WM_Sn is owned)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Become the selection owner */
|
||||
xcb_create_window(conn,
|
||||
root_screen->root_depth,
|
||||
wm_sn_selection_owner, /* window id */
|
||||
root_screen->root, /* parent */
|
||||
-1, -1, 1, 1, /* geometry */
|
||||
0, /* border width */
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
root_screen->root_visual,
|
||||
0, NULL);
|
||||
xcb_change_property(conn,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
wm_sn_selection_owner,
|
||||
XCB_ATOM_WM_CLASS,
|
||||
XCB_ATOM_STRING,
|
||||
8,
|
||||
(strlen("i3-WM_Sn") + 1) * 2,
|
||||
"i3-WM_Sn\0i3-WM_Sn\0");
|
||||
|
||||
xcb_set_selection_owner(conn, wm_sn_selection_owner, wm_sn, last_timestamp);
|
||||
|
||||
if (selection_reply && selection_reply->owner != XCB_NONE) {
|
||||
unsigned int usleep_time = 100000; /* 0.1 seconds */
|
||||
int check_rounds = 150; /* Wait for a maximum of 15 seconds */
|
||||
xcb_get_geometry_reply_t *geom_reply = NULL;
|
||||
|
||||
DLOG("waiting for old WM_Sn selection owner to exit");
|
||||
do {
|
||||
free(geom_reply);
|
||||
usleep(usleep_time);
|
||||
if (check_rounds-- == 0) {
|
||||
ELOG("The old window manager is not exiting");
|
||||
return 1;
|
||||
}
|
||||
geom_reply = xcb_get_geometry_reply(conn,
|
||||
xcb_get_geometry(conn, selection_reply->owner),
|
||||
NULL);
|
||||
} while (geom_reply != NULL);
|
||||
}
|
||||
free(selection_reply);
|
||||
|
||||
/* Announce that we are the new owner */
|
||||
/* Every X11 event is 32 bytes long. Therefore, XCB will copy 32 bytes.
|
||||
* In order to properly initialize these bytes, we allocate 32 bytes even
|
||||
* though we only need less for an xcb_client_message_event_t */
|
||||
union {
|
||||
xcb_client_message_event_t message;
|
||||
char storage[32];
|
||||
} event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.message.response_type = XCB_CLIENT_MESSAGE;
|
||||
event.message.window = root_screen->root;
|
||||
event.message.format = 32;
|
||||
event.message.type = A_MANAGER;
|
||||
event.message.data.data32[0] = last_timestamp;
|
||||
event.message.data.data32[1] = wm_sn;
|
||||
event.message.data.data32[2] = wm_sn_selection_owner;
|
||||
|
||||
xcb_send_event(conn, 0, root_screen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY, event.storage);
|
||||
}
|
||||
|
||||
xcb_void_cookie_t cookie;
|
||||
cookie = xcb_change_window_attributes_checked(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK});
|
||||
xcb_generic_error_t *error = xcb_request_check(conn, cookie);
|
||||
|
Loading…
x
Reference in New Issue
Block a user