Qt Widgets; Convince embedded native window it's always focused
-
Hello there. I am embedding a native window of an external application into my Qt app using
QWidget::createWindowContainer
andQWindow::fromWinId
. The external application is very finnicky when it comes to its "state"; when it's minimized or unfocused, it doesn't run. I think this is why it freezes when I click outside of the app's window's widget (making it "lose focus"). Theoretically this would be solved by clicking back into the area the application's window's widget resides in, or navigating back into it with the keyboard, but I can't seem to get that to work out.Is there a way for me to convince the application that it is always focused? I've tried ignoring all events in the containing widget's
bool event(QEvent *)
, which (I think) would stop events from propagating to its children, but that doesn't work. I've also played around with window flags in theQWindow::fromWinId
call, includingQt::WindowStaysOnTopHint
,Qt::WindowTransparentForInput
,Qt::WindowDoesNotAcceptFocus
, and some others I won't bother naming (they were kind of a hail mary).I can provide more info if needed.
-
Only one window can have focus at one time, so there's no "convincing it" it has focus. It either has it or it doesn't. Focus is not just some bool variable passed between windows that you can fake. It also routs events and filters among other things. You can't have two windows handling input at the same time for example.
You can make other widgets of your app to reject focus by setting their focusPolicy to none. You can also set a focus proxy of other widgets to pass focus back to that embedded window.
Theoretically you could also connect to QApplication::focusChanged signal and when the window looses focus get it back, but this is very heavy handed and super unfriendly and annoying to the user.
The best, obviously, if you have any control over that, would be to change the behavior of the embedded app, so that it doesn't react to loss of focus. Most apps that do that usually just try to be nice and not eat resources when not being used. What sort of app are you embedding?
-
I'm embedding an old game. Unfortunately there's not really any nice way to make modifications like these to it, not that I'd be interested in at least; my app is a modding tool for this game, what good is this feature if it only works on a modified version?
Knowing absolutely nothing on how event propagation works to window containers, it is my understanding that Qt is routing window manager events to the application which owns the embedded window based on Qt-focused policies & mechanisms (e.g. focus, resize, repaint, etc). I don't think it would actually be a matter of top-level WM focus here; just a way to tell that widget, and therefore its embedded window that it is focused & should be running.
I'm not entirely sure setting widget focus will solve this issue, anyway: though it would be nice, I think that my attempts at clicking on it & whatnot have shown that it doesn't work that way. I'll report back on whether any of your ideas work.
-
@Ewan-Green said:
it is my understanding that Qt is routing window manager events to the application which owns the embedded window based on Qt-focused policies & mechanisms (e.g. focus, resize, repaint, etc).
Yes, but not quite. The embedded window is native, with its own event queue and processing thread, so if it has focus it will receive events, not Qt. If the Qt wrapper has focus it will receive events, but it doesn't have to forward them most of the cases. If it gets resized or repainted it just sends native messages to that embedded window to do so too.
Assuming you're on Windows my guess would be that the game is using something like GetActiveWindow and checks if it is a child of it. Probably does so on WM_SETFOCUS or WM_KILLFOCUS message. If you feel adventurous you can try to get a thread id of your embedded window, then install a system event hook with SetWindowsHookEx and replace the focus change messages with e.g. WM_NULL, so that the embedded window doesn't get those messages. I don't know if something similar exists on other platforms.
-
Thanks. I was able to make this very simple solution:
SendMessage(hwnd, WM_ACTIVATE, true, 0); SetFocus(hwnd);
The
SendMessage
call resumes the game and theSetFocus
call gives it keyboard input. This gets run every time I want it to resume.
I just looked at the specific message that the game takes and sent it; it would be ideal if I could do all this without any Win32 calls, since the native window things work on the platform plugins that support it, and this binds me to Windows.QWidget::activateWindow
seems like it would be promising, but it doesn't seem to do anything.I would be quite surprised if there was no way to get what I'm after without using system-specific APIs (which I would be open to if there were direct counterparts, but for some reason I doubt that).
-
There are
QWidget::raise
andQWidget::setFocus
, but is that really the behavior you want? If a user clicks outside of your app they probably did that to use something else, and your app stealing the focus back is not very nice.