Add a fix for the keyboard focus in Bitwig 3.2

This commit is contained in:
Robbert van der Helm
2020-05-26 18:49:44 +02:00
parent be969a69d0
commit ab4d35886e
3 changed files with 78 additions and 51 deletions
+2
View File
@@ -23,6 +23,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).
### Fixed ### Fixed
- Steal keyboard focus when clicking on the plugin editor window to account for
the new keyboard focus behavior in Bitwig Studio 3.2.
- Fixed large amount of empty lines in the log file when the Wine process closes - Fixed large amount of empty lines in the log file when the Wine process closes
unexpectedly. unexpectedly.
- Made the plugin and host detection slightly more robust. - Made the plugin and host detection slightly more robust.
+69 -51
View File
@@ -100,6 +100,10 @@ Editor::Editor(const std::string& window_class_name,
xcb_change_window_attributes(x11_connection.get(), topmost_window, xcb_change_window_attributes(x11_connection.get(), topmost_window,
XCB_CW_EVENT_MASK, &topmost_event_mask); XCB_CW_EVENT_MASK, &topmost_event_mask);
xcb_flush(x11_connection.get()); xcb_flush(x11_connection.get());
const uint32_t parent_event_mask = XCB_EVENT_MASK_FOCUS_CHANGE;
xcb_change_window_attributes(x11_connection.get(), parent_window,
XCB_CW_EVENT_MASK, &parent_event_mask);
xcb_flush(x11_connection.get());
// Embed the Win32 window into the window provided by the host. Instead of // Embed the Win32 window into the window provided by the host. Instead of
// using the XEmbed protocol, we'll register a few events and manage the // using the XEmbed protocol, we'll register a few events and manage the
@@ -172,65 +176,79 @@ void Editor::handle_x11_events() {
// before the root window, i.e. the window that's actually going to // before the root window, i.e. the window that's actually going to
// get dragged around the by the user. In most cases this is the // get dragged around the by the user. In most cases this is the
// same as `parent_window`. // same as `parent_window`.
case XCB_CONFIGURE_NOTIFY: { case XCB_CONFIGURE_NOTIFY:
// We're purposely not using XEmbed. This has the consequence fix_window_coordinates();
// that wine still thinks that any X and Y coordinates are break;
// relative to the x11 window root instead of the parent window // The coordinates should also be reset when the user clicks on the
// provided by the DAW, causing all sorts of GUI interactions to // window. This is sometimes necessary when opening multiple editors
// break. To alleviate this we'll just lie to Wine and tell it // in a single plugin group.
// that it's located at the parent window's location on the root case XCB_FOCUS_IN:
// window. We also will keep the child window at its largest fix_window_coordinates();
// possible size to allow for smooth resizing. This works
// because the embedding hierarchy is DAW window -> Win32 window
// (created in this class) -> VST plugin window created by the
// plugin itself. In this case it doesn't matter that the Win32
// window is larger than the part of the client area the plugin
// draws to since any excess will be clipped off by the parent
// window.
const auto query_cookie =
xcb_query_tree(x11_connection.get(), parent_window);
xcb_window_t root = xcb_query_tree_reply(x11_connection.get(),
query_cookie, nullptr)
->root;
// We can't directly use the `event.x` and `event.y` coordinates // Explicitely request input focus when the user clicks on the
// because the parent window may also be embedded inside another // window. This is needed for Bitwig Studio 3.2, as the parent
// window. // window now captures all keyboard events and forwards them to
// TODO: This seems to get clamped at (0, 0), causing large // the main Bitwig Studio window instead of allowing the child
// windows dragged off screen on the top or the left // window to handle those events.
// borders to act up xcb_set_input_focus(x11_connection.get(),
const auto translate_cookie = xcb_translate_coordinates( XCB_INPUT_FOCUS_PARENT, child_window,
x11_connection.get(), parent_window, root, 0, 0); XCB_CURRENT_TIME);
const xcb_translate_coordinates_reply_t*
translated_coordiantes = xcb_translate_coordinates_reply(
x11_connection.get(), translate_cookie, nullptr);
xcb_configure_notify_event_t translated_event{};
translated_event.response_type = XCB_CONFIGURE_NOTIFY;
translated_event.event = child_window;
translated_event.window = child_window;
// This should be set to the same sizes the window was created
// on. Since we're not using `SetWindowPos` to resize the
// Window, Wine can get a bit confused when we suddenly report a
// different client area size. Without this certain plugins
// (such as those by Valhalla DSP) would break.
translated_event.width = client_area.width;
translated_event.height = client_area.height;
translated_event.x = translated_coordiantes->dst_x;
translated_event.y = translated_coordiantes->dst_y;
xcb_send_event(x11_connection.get(), false, child_window,
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
reinterpret_cast<char*>(&translated_event));
xcb_flush(x11_connection.get()); xcb_flush(x11_connection.get());
} break;
break;
} }
free(generic_event); free(generic_event);
} }
} }
void Editor::fix_window_coordinates() {
// We're purposely not using XEmbed. This has the consequence that wine
// still thinks that any X and Y coordinates are relative to the x11 window
// root instead of the parent window provided by the DAW, causing all sorts
// of GUI interactions to break. To alleviate this we'll just lie to Wine
// and tell it that it's located at the parent window's location on the root
// window. We also will keep the child window at its largest possible size
// to allow for smooth resizing. This works because the embedding hierarchy
// is DAW window -> Win32 window (created in this class) -> VST plugin
// window created by the plugin itself. In this case it doesn't matter that
// the Win32 window is larger than the part of the client area the plugin
// draws to since any excess will be clipped off by the parent window.
const auto query_cookie =
xcb_query_tree(x11_connection.get(), parent_window);
xcb_window_t root =
xcb_query_tree_reply(x11_connection.get(), query_cookie, nullptr)->root;
// We can't directly use the `event.x` and `event.y` coordinates because the
// parent window may also be embedded inside another window.
// TODO: This seems to get clamped at (0, 0), causing large windows dragged
// off screen on the top or the left borders to act up
const auto translate_cookie = xcb_translate_coordinates(
x11_connection.get(), parent_window, root, 0, 0);
const xcb_translate_coordinates_reply_t* translated_coordiantes =
xcb_translate_coordinates_reply(x11_connection.get(), translate_cookie,
nullptr);
xcb_configure_notify_event_t translated_event{};
translated_event.response_type = XCB_CONFIGURE_NOTIFY;
translated_event.event = child_window;
translated_event.window = child_window;
// This should be set to the same sizes the window was created on. Since
// we're not using `SetWindowPos` to resize the Window, Wine can get a bit
// confused when we suddenly report a different client area size. Without
// this certain plugins (such as those by Valhalla DSP) would break.
translated_event.width = client_area.width;
translated_event.height = client_area.height;
translated_event.x = translated_coordiantes->dst_x;
translated_event.y = translated_coordiantes->dst_y;
xcb_send_event(
x11_connection.get(), false, child_window,
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
reinterpret_cast<char*>(&translated_event));
xcb_flush(x11_connection.get());
}
LRESULT CALLBACK window_proc(HWND handle, LRESULT CALLBACK window_proc(HWND handle,
UINT message, UINT message,
WPARAM wParam, WPARAM wParam,
+7
View File
@@ -113,6 +113,13 @@ class Editor {
void handle_x11_events(); void handle_x11_events();
private: private:
/**
* Lie to the Wine window about its coordinates on the screen for
* reparenting without using XEmbed. See the comment at the top of the
* implementation on why this is needed.
*/
void fix_window_coordinates();
/** /**
* A pointer to the currently active window. Will be a null pointer if no * A pointer to the currently active window. Will be a null pointer if no
* window is active. * window is active.