Mouse Click Grabs keyboard focus in some hosts

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?