@@ -63,6 +63,12 @@ constexpr uint32_t parent_event_mask =
63
63
host_event_mask | XCB_EVENT_MASK_FOCUS_CHANGE |
64
64
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW;
65
65
66
+ /* *
67
+ * The X11 event mask for the Wine window. We'll use this to detect if the
68
+ * Window manager somehow steals the Wine window.
69
+ */
70
+ constexpr uint32_t wine_event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
71
+
66
72
/* *
67
73
* The name of the X11 property on the root window used to denote the active
68
74
* window in EWMH compliant window managers.
@@ -325,13 +331,18 @@ Editor::Editor(MainContext& main_context,
325
331
// or resized, and when the user moves his mouse over the window because
326
332
// this is sometimes needed for plugin groups. We also listen for
327
333
// EnterNotify and LeaveNotify events on the Wine window so we can grab and
328
- // release input focus as necessary.
334
+ // release input focus as necessary. And lastly we'll look out for
335
+ // reparents, so we can make sure that the window does not get stolen by the
336
+ // window manager and that we correctly handle the host reparenting
337
+ // `parent_window` themselves.
329
338
// If we do enable XEmbed support, we'll also listen for visibility changes
330
339
// and trigger the embedding when the window becomes visible
331
340
xcb_change_window_attributes (x11_connection.get (), host_window,
332
341
XCB_CW_EVENT_MASK, &host_event_mask);
333
342
xcb_change_window_attributes (x11_connection.get (), parent_window,
334
343
XCB_CW_EVENT_MASK, &parent_event_mask);
344
+ xcb_change_window_attributes (x11_connection.get (), wine_window,
345
+ XCB_CW_EVENT_MASK, &wine_event_mask);
335
346
xcb_flush (x11_connection.get ());
336
347
337
348
std::cerr << " DEBUG: host_window: " << host_window << std::endl;
@@ -349,52 +360,6 @@ Editor::Editor(MainContext& main_context,
349
360
// of using the XEmbed protocol, we'll register a few events and manage
350
361
// the child window ourselves. This is a hack to work around the issue's
351
362
// described in `Editor`'s docstring'.
352
- auto do_reparent = [&]() {
353
- const xcb_void_cookie_t reparent_cookie =
354
- xcb_reparent_window_checked (x11_connection.get (), wine_window,
355
- parent_window, 0 , 0 );
356
- if (std::unique_ptr<xcb_generic_error_t > reparent_error (
357
- xcb_request_check (x11_connection.get (), reparent_cookie));
358
- reparent_error) {
359
- std::cerr << " DEBUG: Reparent failed:" << std::endl;
360
- std::cerr << " Error code: " << reparent_error->error_code
361
- << std::endl;
362
- std::cerr << " Major code: " << reparent_error->major_code
363
- << std::endl;
364
- std::cerr << " Minor code: " << reparent_error->minor_code
365
- << std::endl;
366
-
367
- // Let's just check all of the reasons why the reparent could
368
- // fail according to the spec in advance
369
- xcb_generic_error_t * error = nullptr ;
370
- const xcb_query_pointer_cookie_t query_pointer_cookie =
371
- xcb_query_pointer (x11_connection.get (), wine_window);
372
- const std::unique_ptr<xcb_query_pointer_reply_t >
373
- query_pointer_reply (xcb_query_pointer_reply (
374
- x11_connection.get (), query_pointer_cookie, &error));
375
- if (error) {
376
- free (error);
377
- std::cerr << " DEBUG: Could not query pointer location"
378
- << std::endl;
379
- } else {
380
- if (query_pointer_reply->same_screen ) {
381
- std::cerr
382
- << " DEBUG: Pointer is on the same screen as the "
383
- " Wine window, good"
384
- << std::endl;
385
- } else {
386
- std::cerr
387
- << " DEBUG: Pointer is not on the same screen as "
388
- " the Wine window, oh no"
389
- << std::endl;
390
- }
391
- }
392
- } else {
393
- std::cerr << " DEBUG: Reparent succeeded" << std::endl;
394
- }
395
- xcb_flush (x11_connection.get ());
396
- };
397
-
398
363
do_reparent ();
399
364
400
365
// If we're using the double embedding option, then the child window
@@ -414,17 +379,6 @@ Editor::Editor(MainContext& main_context,
414
379
415
380
ShowWindow (win32_child_window->handle , SW_SHOWNORMAL);
416
381
}
417
-
418
- // HACK: I can't seem to figure why the initial reparent would fail on
419
- // this particular i3 config in a way that I'm unable to
420
- // reproduce, but if it doesn't work the first time, just keep
421
- // trying!
422
- //
423
- // https://github.com/robbert-vdh/yabridge/issues/40
424
- std::cerr
425
- << " DEBUG: Reparent 2 is allowed to fail if the first one succeeded"
426
- << std::endl;
427
- do_reparent ();
428
382
}
429
383
}
430
384
@@ -463,6 +417,17 @@ void Editor::handle_x11_events() noexcept {
463
417
<< event->event << std::endl;
464
418
465
419
redetect_host_window ();
420
+
421
+ // NOTE: Some window managers like to steal the window, so
422
+ // we must prevent that. This situation is easily
423
+ // recognized since the window will then cover the
424
+ // entire screen (since that's what the client area
425
+ // has been set to).
426
+ if (event->window == parent_window ||
427
+ (event->window == wine_window &&
428
+ event->parent != parent_window)) {
429
+ do_reparent ();
430
+ }
466
431
} break ;
467
432
// We're listening for `ConfigureNotify` events on the host's
468
433
// window (i.e. the window that's actually going to get dragged
@@ -816,6 +781,48 @@ void Editor::send_xembed_message(xcb_window_t window,
816
781
reinterpret_cast <char *>(&event));
817
782
}
818
783
784
+ void Editor::do_reparent () const {
785
+ // TODO: When rebasing this, we should keep in the error logging for when
786
+ // the reparent fails
787
+ const xcb_void_cookie_t reparent_cookie = xcb_reparent_window_checked (
788
+ x11_connection.get (), wine_window, parent_window, 0 , 0 );
789
+ if (std::unique_ptr<xcb_generic_error_t > reparent_error (
790
+ xcb_request_check (x11_connection.get (), reparent_cookie));
791
+ reparent_error) {
792
+ std::cerr << " DEBUG: Reparent failed:" << std::endl;
793
+ std::cerr << " Error code: " << reparent_error->error_code << std::endl;
794
+ std::cerr << " Major code: " << reparent_error->major_code << std::endl;
795
+ std::cerr << " Minor code: " << reparent_error->minor_code << std::endl;
796
+
797
+ // Let's just check all of the reasons why the reparent could
798
+ // fail according to the spec in advance
799
+ xcb_generic_error_t * error = nullptr ;
800
+ const xcb_query_pointer_cookie_t query_pointer_cookie =
801
+ xcb_query_pointer (x11_connection.get (), wine_window);
802
+ const std::unique_ptr<xcb_query_pointer_reply_t > query_pointer_reply (
803
+ xcb_query_pointer_reply (x11_connection.get (), query_pointer_cookie,
804
+ &error));
805
+ if (error) {
806
+ free (error);
807
+ std::cerr << " DEBUG: Could not query pointer location" << std::endl;
808
+ } else {
809
+ if (query_pointer_reply->same_screen ) {
810
+ std::cerr << " DEBUG: Pointer is on the same screen as the "
811
+ " Wine window, good"
812
+ << std::endl;
813
+ } else {
814
+ std::cerr << " DEBUG: Pointer is not on the same screen as "
815
+ " the Wine window, oh no"
816
+ << std::endl;
817
+ }
818
+ }
819
+ } else {
820
+ std::cerr << " DEBUG: Reparent succeeded" << std::endl;
821
+ }
822
+
823
+ xcb_flush (x11_connection.get ());
824
+ }
825
+
819
826
void Editor::do_xembed () const {
820
827
if (!use_xembed) {
821
828
return ;
0 commit comments