Mouse Click Grabs keyboard focus in some hosts

I tried using setWantsFocus(false) on all my controls, but it seems like the mouse click is is grabbing key focus mouse wheel does not do this

Hi Marvin,
can you be more specific ? What Operating System, which hosts, and very important, what do you expect to happen ?

cheers
Arne

This is for both Mac 64bit and Windows 32 with the juce plugin host found here https://www.juce.com. For Mac I’m sure that it’s the vst grabbing focus on click specially the cknob since the up down left right key change the parameters I have them all set to wants focus false but they are still grabbing focus. For window it seems like cframe is grabbing focus on click.

I can send my project if necessary since it’s a modified version of the plugin host.

Oh sorry I expect the midi computer component key board to set off noteOn events it could be that it is some how giving focus I’m trying to track down the problem now.

Hi Marvin,
it’s difficult to follow you.
If you mean that the keyboard focus is shifted to the platform control of VSTGUI if you click inside of it, then this is expected behaviour.
You have to be a good citizen and only handle keyboard input events for the events you are actually using. Do you by any chance have a global keyboard hook registered on the CFrame object ? Then you must make sure to return the correct value when not handling the keyboard event.

cheers
Arne

Hi Arne,
I have no global keyboard hooks registered. My assumption was if I set setWantsKeyboardFocus(false) for each of my objects Cframe and the Controls inside the Cframe that they wouldn’t take keyboard focus when clicked on them in the host application. This doesn’t occur with mouse wheel events just mouseDown. I will look in global keyboard hooks.
Thanks,
Marvin

Hi there,

I think on Windows the problem is located in line 887 in win32frame.cpp. Once the plugin container window receives the focus it also steals the keyboard focus by calling SetFocus(). The keyboard focus isn’t returned to the DAW window until the user highlights it again (e.g. by clicking on it), even if the plugin window is closed. Keyboard events, even unprocessed ones, are consequently consumed by your plugin window procedure.

If your CFrame isn’t supposed to process any key events, you can safely comment out the SetFocus() line. A clean fix could e.g. forward unprocessed key events to the window that was in focus prior to the plugin container window - which is the window handle returned by SetFocus(), given that it’s still valid, of course (beware: Cubase seems to do all kinds of weird things to its windows once you switch between application, i.e. it seems cubase re-builds parts of its window structure when switching the context).

By the way no offense or smart-assing, VSTGUI, escpecially the 4.3 update, is exceptionally good and usable.

Ray

You maybe right. The win32 keyboard handling code is mainly the same since open sourcing VSTGUI. I will analyse it and hopefully find a good fix for it.

The described issue is particularly noticeable in Pro Tools on Windows. E.g. it’s impossible to trigger the transport via space, once a VSTGUI editor window has been opened unless you give back the focus to Pro Tools by clicking on its window.

Here’s a suggestion for a fix in win32frame.h / win32frame.cpp in the current 4.5 build:

Add a member variable to the Win32Frame declaration:

protected:
	...
	HWND oldFocusWindow;
	...

Add the following changes in the Win32Frame implementation:

In the ctor

..
, oldFocuseWindow (0)
..

In Win32Frame::proc()

Focus acquisition…

case WM_RBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
case WM_LBUTTONDBLCLK:
case WM_XBUTTONDBLCLK:
	doubleClick = true;
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONDOWN:
case WM_XBUTTONDOWN:
{
	CButtonState buttons = 0;
	if (wParam & MK_LBUTTON)
		buttons |= kLButton;
	if (wParam & MK_RBUTTON)
		buttons |= kRButton;
	if (wParam & MK_MBUTTON)
		buttons |= kMButton;
	if (wParam & MK_XBUTTON1)
		buttons |= kButton4;
	if (wParam & MK_XBUTTON2)
		buttons |= kButton5;
	if (wParam & MK_CONTROL)
		buttons |= kControl;
	if (wParam & MK_SHIFT)
		buttons |= kShift;
	if (GetAsyncKeyState (VK_MENU)    < 0)
		buttons |= kAlt;
	if (doubleClick)
		buttons |= kDoubleClick;

	// Keep track of previous focus window
	HWND oldFocus = SetFocus (getPlatformWindow ());
	if (oldFocus != hwnd)
		oldFocusWindow = oldFocus;
	
	CPoint where (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
	if (pFrame->platformOnMouseDown (where, buttons) == kMouseEventHandled && getPlatformWindow ())
		SetCapture (getPlatformWindow ());
	return 0;
}

Forward unprocessed key events to the window that was focused previously in case it’s still alive in WM_KEYDOWN and WM_KEYUP:

case WM_KEYDOWN:
{
	VstKeyCode key {};
	if (GetAsyncKeyState (VK_SHIFT)   < 0)
		key.modifier |= MODIFIER_SHIFT;
	if (GetAsyncKeyState (VK_CONTROL) < 0)
		key.modifier |= MODIFIER_CONTROL;
	if (GetAsyncKeyState (VK_MENU)    < 0)
		key.modifier |= MODIFIER_ALTERNATE;
	key.virt = translateWinVirtualKey (wParam);
	if (key.virt)
	{
		if (pFrame->platformOnKeyDown (key))
			return 0;
	}
	
	if (IsWindow (oldFocusWindow))
	{
		WNDPROC oldProc = (WNDPROC) GetWindowLongPtr (oldFocusWindow, GWLP_WNDPROC);
		if (oldProc && oldProc != WindowProc)
			return CallWindowProc (oldProc, oldFocusWindow, message, wParam, lParam);
	}
	break;
}



case WM_KEYUP:
{
	VstKeyCode key {};
	if (GetAsyncKeyState (VK_SHIFT)   < 0)
		key.modifier |= MODIFIER_SHIFT;
	if (GetAsyncKeyState (VK_CONTROL) < 0)
		key.modifier |= MODIFIER_CONTROL;
	if (GetAsyncKeyState (VK_MENU)    < 0)
		key.modifier |= MODIFIER_ALTERNATE;
		key.virt = translateWinVirtualKey (wParam);
		if (key.virt)
		{
			if (pFrame->platformOnKeyUp (key))
				return 0;
		}
		
		if (IsWindow (oldFocusWindow))
		{
				WNDPROC oldProc = (WNDPROC) GetWindowLongPtr (oldFocusWindow, GWLP_WNDPROC);
				if(oldProc && oldProc != WindowProc)
					return CallWindowProc (oldProc, oldFocusWindow, message, wParam, lParam);
		}
		break;
	}

Release focus…

case WM_KILLFOCUS:
{
	oldFocusWindow = 0;

	HWND focusWindow = GetFocus ();
	if (GetParent (focusWindow) != windowHandle)
		pFrame->platformOnActivate (false);
	break;
}

Also note that I’m using GetAsyncKeyState() instead of GetKeyState(), though admittedly not very beautiful in particular, the modifier keys will be have been consumed by Pro Tools which apparently grabs them away via a low level API before they reach our window.

It’s also a little puzzling why the VstKeyCode structure’s character member isn’t inizialized in the WM_KEYDOWN / WM_KEYUP handlers, whereas it is in other platform implementations. This basically makes it impossible to capture arbitrary keypresses in windows implementations, right?

Will a fix for keyboard + mouse focus be integrated in a forthcoming VSTGUI release?

Hey Joscha,

I hope you’re doing well.

Here’s my pull request, let’s see what happens: https://github.com/steinbergmedia/vstgui/pull/47

Thanks for the fix, Reimund. I have merged the fix into our VSTGUI 4.3.1 code base. I had to tweak it a little bit to fix returning the keyboard focus after editing a textedit control and pressing enter.

Now I encounter the problem that in all hosts I have tested so far, pressing the arrow keys does not yield a WM_KEYDOWN event. The event is always caught by the host. Does anyone encounter a similar problem? Again, we are not using the latest VSTGUI version from the master branch, but the latest released version.

Hi Joscha,

Thanks for noting the additional patch required in conjunction with text edits loosing their focus. We didn’t catch that one as none of our plugins uses text edits :slight_smile:.

Regarding your second remark: I’m not familiar with the details of your integration into 4.3.1 and the additional patch, but I have done a little debugging by setting a breakpoint right below case WM_KEYDOWN and/or case WM_KEYUP and found the patch to be transparant as to the described behavior. Maybe try reverting the patch and see if it influences whether these breakpoints are hit at all. Keep in mind that you have to click the editor window / any of the controls first in order to give keyboard focus to VSTGUI first via SetFocus() in the cases WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK,WM_LBUTTONDBLCLK,WM_XBUTTONDBLCLK, WM_RBUTTONDOWN, WM_MBUTTONDOWN, WM_LBUTTONDOWN or WM_XBUTTONDOWN which is independent of whether the patch is applied or not. Another idea: Maybe also check whether your additional text edit fix is interfering in some way.

I resolved the issue by adding

case WM_GETDLGCODE:
{
	LONG_PTR flags = DLGC_WANTALLKEYS;
	return flags;
}

to Win32Frame::WindowProc().