Activated NDOF capture for Windows. Robust++ for generic NDOF manager. Removed experimental Win32 mouse and NDOF code now that good solutions are in place. Cleaned up formatting.

This commit is contained in:
Mike Erwin
2010-07-24 11:06:08 +00:00
parent a51ebf247f
commit a7ba245fe3
3 changed files with 446 additions and 594 deletions

View File

@@ -15,21 +15,26 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): none yet.
* Contributor(s): Mike Erwin, July 2010.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "GHOST_NDOFManager.h"
#include "GHOST_EventNDOF.h"
#include <string.h> // for memory functions
#include <stdio.h> // for debug tracing
GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System& sys)
: m_system(sys)
// , m_translation((short[]){0,0,0})
// , m_rotation((short[]){0,0,0})
, m_buttons(0)
, m_atRest(true)
{ }
{
// to avoid the rare situation where one triple is updated and
// the other is not, initialize them both here:
memset(m_translation, 0, sizeof(m_translation));
memset(m_rotation, 0, sizeof(m_rotation));
}
void GHOST_NDOFManager::updateTranslation(short t[3], GHOST_TUns64 time)
{
@@ -68,7 +73,9 @@ bool GHOST_NDOFManager::sendMotionEvent()
GHOST_TEventNDOFData* data = (GHOST_TEventNDOFData*) event->getData();
const float scale = 1.f / 350.f; // SpaceNavigator sends +/- 350 usually
// 350 according to their developer's guide; others recommend 500 as comfortable
// possible future enhancement
// scale *= m_sensitivity;
data->tx = scale * m_translation[0];
@@ -80,7 +87,6 @@ bool GHOST_NDOFManager::sendMotionEvent()
data->rz = scale * m_rotation[2];
printf("sending T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f)\n", data->tx, data->ty, data->tz, data->rx, data->ry, data->rz);
fflush(stdout);
m_system.pushEvent(event);

View File

@@ -27,7 +27,6 @@
*/
/**
* $Id$
* Copyright (C) 2001 NaN Technologies B.V.
* @author Maarten Gribnau
@@ -47,28 +46,12 @@
#endif
#endif
/*
* According to the docs the mouse wheel message is supported from windows 98
* upwards. Leaving WINVER at default value, the WM_MOUSEWHEEL message and the
* wheel detent value are undefined.
[mce] able to remove this too?
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 0x020A
#endif // WM_MOUSEWHEEL
#ifndef WHEEL_DELTA
#define WHEEL_DELTA 120 // Value for rolling one detent, (old convention! MS changed it)
#endif // WHEEL_DELTA
*/
#include "GHOST_Debug.h"
#include "GHOST_DisplayManagerWin32.h"
#include "GHOST_EventButton.h"
#include "GHOST_EventCursor.h"
#include "GHOST_EventKey.h"
#include "GHOST_EventWheel.h"
#include "GHOST_EventNDOF.h"
#include "GHOST_TimerTask.h"
#include "GHOST_TimerManager.h"
#include "GHOST_WindowManager.h"
@@ -130,13 +113,7 @@ GHOST_SystemWin32::GHOST_SystemWin32()
// register for RawInput devices
RAWINPUTDEVICE devices[1];
/*
// standard HID mouse
devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x02;
devices[0].dwFlags = 0; // RIDEV_NOLEGACY; // ignore legacy mouse messages
devices[0].hwndTarget = NULL;
*/
// multi-axis mouse (SpaceNavigator)
devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x08;
@@ -396,6 +373,7 @@ GHOST_TSuccess GHOST_SystemWin32::init()
GHOST_TSuccess GHOST_SystemWin32::exit()
{
// [mce] since this duplicates its super, why bother?
return GHOST_System::exit();
}
@@ -522,15 +500,10 @@ GHOST_EventButton* GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
}
GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow, int x, int y)
GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow, int x_screen, int y_screen)
{
GHOST_TInt32 x_screen, y_screen;
GHOST_SystemWin32 * system = ((GHOST_SystemWin32 * ) getSystem());
GHOST_WindowWin32 * window = ( GHOST_WindowWin32 * ) Iwindow;
// system->getCursorPosition(x_screen, y_screen);
x_screen = x;
y_screen = y;
if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
{
@@ -639,81 +612,10 @@ void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
minmax->ptMinTrackSize.y=240;
}
bool GHOST_SystemWin32::processRawInput(RAWINPUT const& raw, GHOST_WindowWin32* window /*, int& x, int& y */ )
bool GHOST_SystemWin32::processRawInput(RAWINPUT const& raw, GHOST_WindowWin32* window)
{
GHOST_IEvent* event = NULL;
bool eventSent = false;
puts("BEGIN");
#if 0 // now using the existing mouse button handlers, improved movement handler
if (raw.header.dwType == RIM_TYPEMOUSE)
{
USHORT const& buttonFlags = raw.data.mouse.usButtonFlags;
if (buttonFlags)
{
printf("button flags: %04X\n", buttonFlags);
if (buttonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
{
puts("left button down");
window->registerMouseClickEvent(true);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
}
else if (buttonFlags & RI_MOUSE_LEFT_BUTTON_UP)
{
puts("left button up");
window->registerMouseClickEvent(false);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
}
if (buttonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN)
{
puts("right button down");
window->registerMouseClickEvent(true);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
}
else if (buttonFlags & RI_MOUSE_RIGHT_BUTTON_UP)
{
puts("right button up");
window->registerMouseClickEvent(false);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
}
if (buttonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN)
{
puts("middle button down");
window->registerMouseClickEvent(true);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
}
else if (buttonFlags & RI_MOUSE_MIDDLE_BUTTON_UP)
{
puts("middle button up");
window->registerMouseClickEvent(false);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
}
// similar for BUTTON_4 and BUTTON_5
if (buttonFlags & RI_MOUSE_WHEEL)
{
signed short wheelDelta = raw.data.mouse.usButtonData;
printf("wheel moved %+d\n", wheelDelta);
}
}
int dx = raw.data.mouse.lLastX; // These might be in Mickeys, not pixels.
int dy = raw.data.mouse.lLastY;
if (dx || dy)
{
printf("mouse moved <%+d,%+d>\n", dx, dy);
x += dx;
x += dy;
event = processCursorEvent(GHOST_kEventCursorMove, window, x, y);
}
}
#endif // unused experimental mouse code
if (raw.header.dwType == RIM_TYPEHID)
{
// RID_DEVICE_INFO info;
@@ -738,16 +640,20 @@ vendor ID
No other registered devices use the c62_ space, so a simple mask will work!
*/
// The NDOF manager sends button changes immediately, and *pretends* to
// send motion. Mark as 'sent' so motion will always get dispatched.
eventSent = true;
// multiple events per RAWHID? MSDN hints at this.
printf("%d events\n", raw.data.hid.dwCount);
printf("%d events\n", (int)raw.data.hid.dwCount);
BYTE const* data = &raw.data.hid.bRawData;
// MinGW's definition (below) doesn't agree with MSDN reference for bRawData:
// typedef struct tagRAWHID {
// DWORD dwSizeHid;
// DWORD dwCount;
// BYTE bRawData;
// } RAWHID,*PRAWHID,*LPRAWHID;
// MinGW's definition (below) doesn't agree with MSDN reference for bRawData:
// typedef struct tagRAWHID {
// DWORD dwSizeHid;
// DWORD dwCount;
// BYTE bRawData; // <== isn't this s'posed to be a BYTE*?
// } RAWHID,*PRAWHID,*LPRAWHID;
BYTE packetType = data[0];
switch (packetType)
@@ -756,16 +662,20 @@ No other registered devices use the c62_ space, so a simple mask will work!
{
short t[3];
memcpy(t, data + 1, sizeof(t));
printf("T: %+5d %+5d %+5d\n", t[0], t[1], t[2]);
m_ndofManager->updateTranslation(t, getMilliseconds());
// too much noise -- disable for now
// printf("T: %+5d %+5d %+5d\n", t[0], t[1], t[2]);
m_ndofManager->updateTranslation(t, getMilliSeconds());
// wariness of alignment issues prevents me from saying it this way:
// m_ndofManager->updateTranslation((short*)(data + 1), getMilliSeconds());
// though it probably (94.7%) works fine
break;
}
case 2: // rotation
{
short r[3];
memcpy(r, data + 1, sizeof(r));
printf("R: %+5d %+5d %+5d\n", r[0], r[1], r[2]);
m_ndofManager->updateRotation(r, getMilliseconds());
// printf("R: %+5d %+5d %+5d\n", r[0], r[1], r[2]);
m_ndofManager->updateRotation(r, getMilliSeconds());
break;
}
case 3: // buttons
@@ -783,24 +693,11 @@ No other registered devices use the c62_ space, so a simple mask will work!
}
else
printf(" none\n");
m_ndofManager->updateButtons(buttons, getMilliseconds());
m_ndofManager->updateButtons(buttons, getMilliSeconds());
break;
}
}
}
// assume only one event will come from this RawInput report
// test and adjust assumptions as needed!
if (event)
{
pushEvent(event);
event = NULL;
eventSent = true;
}
puts("END");
return eventSent;
}
@@ -823,6 +720,7 @@ int GHOST_SystemWin32::getMoreMousePoints(int xLatest, int yLatest, int xPrev, i
}
// search for 'prev' point (we want only newer points)
// TODO: detect & ignore points that don't belong to our window
for (int i = 1; i < n; ++i)
if (morePoints[i].x == xPrev && morePoints[i].y == yPrev)
{
@@ -835,13 +733,14 @@ int GHOST_SystemWin32::getMoreMousePoints(int xLatest, int yLatest, int xPrev, i
signed short x = morePoints[i].x;
signed short y = morePoints[i].y;
printf("> (%d,%d)\n", x, y);
// uncomment here and in WM_MOUSEMOVE handler to show effectiveness of hi-fi input
// printf("> (%d,%d)\n", x, y);
pushEvent(processCursorEvent(GHOST_kEventCursorMove, window, x, y));
}
return n;
} // END getMoreMousePoints
}
LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
@@ -901,479 +800,426 @@ bool GHOST_SystemWin32::handleEvent(GHOST_WindowWin32* window, UINT msg, WPARAM
static int mousePosX = 0, mousePosY = 0; // track mouse position between calls
switch (msg) {
////////////////////////////////////////////////////////////////////////
// Keyboard events, processed
////////////////////////////////////////////////////////////////////////
case WM_KEYDOWN:
/* The WM_KEYDOWN message is posted to the window with the keyboard focus when a
* nonsystem key is pressed. A nonsystem key is a key that is pressed when the alt
* key is not pressed.
*/
case WM_SYSKEYDOWN:
/* The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when
* the user presses the F10 key (which activates the menu bar) or holds down the
* alt key and then presses another key. It also occurs when no window currently
* has the keyboard focus; in this case, the WM_SYSKEYDOWN message is sent to the
* active window. The window that receives the message can distinguish between these
* two contexts by checking the context code in the lKeyData parameter.
*/
switch (wParam) {
case VK_SHIFT:
case VK_CONTROL:
case VK_MENU:
if (!m_separateLeftRightInitialized) {
// Check whether this system supports separate left and right keys
switch (wParam) {
case VK_SHIFT:
m_separateLeftRight =
(HIBYTE(::GetKeyState(VK_LSHIFT)) != 0) ||
(HIBYTE(::GetKeyState(VK_RSHIFT)) != 0) ?
true : false;
break;
case VK_CONTROL:
m_separateLeftRight =
(HIBYTE(::GetKeyState(VK_LCONTROL)) != 0) ||
(HIBYTE(::GetKeyState(VK_RCONTROL)) != 0) ?
true : false;
break;
case VK_MENU:
m_separateLeftRight =
(HIBYTE(::GetKeyState(VK_LMENU)) != 0) ||
(HIBYTE(::GetKeyState(VK_RMENU)) != 0) ?
true : false;
break;
}
m_separateLeftRightInitialized = true;
}
processModifierKeys(window);
// Bypass call to DefWindowProc
return true;
default:
event = processKeyEvent(window, true, wParam, lParam);
if (!event) {
GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
GHOST_PRINT(msg)
GHOST_PRINT(" key ignored\n")
}
break;
switch (msg) {
////////////////////////////////////////////////////////////////////////
// Keyboard events, processed
////////////////////////////////////////////////////////////////////////
case WM_KEYDOWN:
/* The WM_KEYDOWN message is posted to the window with the keyboard focus when a
* nonsystem key is pressed. A nonsystem key is a key that is pressed when the alt
* key is not pressed.
*/
case WM_SYSKEYDOWN:
/* The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when
* the user presses the F10 key (which activates the menu bar) or holds down the
* alt key and then presses another key. It also occurs when no window currently
* has the keyboard focus; in this case, the WM_SYSKEYDOWN message is sent to the
* active window. The window that receives the message can distinguish between these
* two contexts by checking the context code in the lKeyData parameter.
*/
switch (wParam) {
case VK_SHIFT:
case VK_CONTROL:
case VK_MENU:
if (!m_separateLeftRightInitialized) {
// Check whether this system supports separate left and right keys
switch (wParam) {
case VK_SHIFT:
m_separateLeftRight =
(HIBYTE(::GetKeyState(VK_LSHIFT)) != 0) ||
(HIBYTE(::GetKeyState(VK_RSHIFT)) != 0) ?
true : false;
break;
case VK_CONTROL:
m_separateLeftRight =
(HIBYTE(::GetKeyState(VK_LCONTROL)) != 0) ||
(HIBYTE(::GetKeyState(VK_RCONTROL)) != 0) ?
true : false;
break;
case VK_MENU:
m_separateLeftRight =
(HIBYTE(::GetKeyState(VK_LMENU)) != 0) ||
(HIBYTE(::GetKeyState(VK_RMENU)) != 0) ?
true : false;
break;
}
break;
case WM_KEYUP:
case WM_SYSKEYUP:
switch (wParam) {
case VK_SHIFT:
case VK_CONTROL:
case VK_MENU:
processModifierKeys(window);
// Bypass call to DefWindowProc
return true;
default:
event = processKeyEvent(window, false, wParam, lParam);
if (!event) {
GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
GHOST_PRINT(msg)
GHOST_PRINT(" key ignored\n")
}
break;
m_separateLeftRightInitialized = true;
}
processModifierKeys(window);
// Bypass call to DefWindowProc
return true;
default:
event = processKeyEvent(window, true, wParam, lParam);
if (!event) {
GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
GHOST_PRINT(msg)
GHOST_PRINT(" key ignored\n")
}
break;
#if 0 // this code is illustrative; no need to compile
////////////////////////////////////////////////////////////////////////
// Keyboard events, ignored
////////////////////////////////////////////////////////////////////////
case WM_CHAR:
/* The WM_CHAR message is posted to the window with the keyboard focus when
* a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
* contains the character code of the key that was pressed.
*/
case WM_DEADCHAR:
/* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
* WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
* specifies a character code generated by a dead key. A dead key is a key that
* generates a character, such as the umlaut (double-dot), that is combined with
* another character to form a composite character. For example, the umlaut-O
* character (<28>) is generated by typing the dead key for the umlaut character, and
* then typing the O key.
*/
case WM_SYSDEADCHAR:
/* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
* a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
* WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
* a dead key that is pressed while holding down the alt key.
*/
break;
#endif // illustrative code
////////////////////////////////////////////////////////////////////////
// Tablet events, processed
////////////////////////////////////////////////////////////////////////
case WT_PACKET:
puts("WT_PACKET");
window->processWin32TabletEvent(wParam, lParam);
break;
case WT_CSRCHANGE:
case WT_PROXIMITY:
window->processWin32TabletInitEvent();
break;
////////////////////////////////////////////////////////////////////////
// Mouse events, processed
////////////////////////////////////////////////////////////////////////
case WM_LBUTTONDOWN:
window->registerMouseClickEvent(true);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
break;
case WM_MBUTTONDOWN:
window->registerMouseClickEvent(true);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
break;
case WM_RBUTTONDOWN:
window->registerMouseClickEvent(true);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
break;
case WM_XBUTTONDOWN:
window->registerMouseClickEvent(true);
if ((short) HIWORD(wParam) == XBUTTON1){
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
}else if((short) HIWORD(wParam) == XBUTTON2){
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
}
break;
case WM_LBUTTONUP:
window->registerMouseClickEvent(false);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
break;
case WM_MBUTTONUP:
window->registerMouseClickEvent(false);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
break;
case WM_RBUTTONUP:
window->registerMouseClickEvent(false);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
break;
case WM_XBUTTONUP:
window->registerMouseClickEvent(false);
if ((short) HIWORD(wParam) == XBUTTON1){
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
}else if((short) HIWORD(wParam) == XBUTTON2){
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
}
break;
//#endif // replaced mouse code
case WM_MOUSEMOVE:
{
// puts("WM_MOUSEMOVE");
// bool IsFromPen = ((GetMessageExtraInfo() & 0xFF515700) == 0xFF515700); // this only works on TabletPCs
int tabletTool = GetMessageExtraInfo() & 0x7f; // true for tablet mouse, not just pen
if (tabletTool)
puts("(from tablet)");
else
{
// these give window coords, we need view coords
// mousePosX = LOWORD(lParam);
// mousePosY = HIWORD(lParam);
// window->clientToScreen(mousePosX, mousePosY, mousePosX, mousePosY);
int xPrev = mousePosX;
int yPrev = mousePosY;
window->clientToScreen(LOWORD(lParam), HIWORD(lParam), mousePosX, mousePosY);
// if (m_input_fidelity_hint == HI_FI) // can't access hint from static function
putchar('\n');
if (m_input_fidelity_hint == HI_FI)
{
/* int n = */ getMoreMousePoints(mousePosX, mousePosY, xPrev, yPrev, window);
// printf("%d more mouse points found\n", n);
}
printf(" (%d,%d)\n", mousePosX, mousePosY);
event = processCursorEvent(GHOST_kEventCursorMove, window, mousePosX, mousePosY);
}
break;
}
case WM_INPUT:
puts("WM_INPUT");
{
/*
UINT dwSize;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
LPBYTE lpb = new BYTE[dwSize];
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
RAWINPUT* raw = (RAWINPUT*)lpb;
*/
RAWINPUT raw;
RAWINPUT* raw_ptr = &raw;
UINT rawSize = sizeof(RAWINPUT);
// UINT bufferSize = rawSize;
puts("processing first event:");
// I don't know if this is needed. Can we get by with just GetRawInputBuffer?
// Thought some mouse events were missing, so I put this in to be cautious.
// Test and remove if redundant. [mce]
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));
eventSent |= processRawInput(raw, window /*, mousePosX, mousePosY*/ );
DefRawInputProc(&raw_ptr, 1, sizeof(RAWINPUTHEADER));
// GetRawInputBuffer(NULL, &bufferSize, sizeof(RAWINPUTHEADER));
// UINT n = bufferSize / rawSize;
// printf("allocating %d bytes (room for %d events)\n", bufferSize, n);
RAWINPUT rawBuffer[10];// = new RAWINPUT[n];
rawSize *= 10;
while (true)
{
int n = GetRawInputBuffer(rawBuffer, &rawSize, sizeof(RAWINPUTHEADER));
if (n == -1)
{
printf("<!> error %d\n", (int) GetLastError());
break;
}
else if (n == 0)
{
//puts("no more events");
putchar('\n');
break;
}
else
{
printf("processing %d more events:\n", n);
for (int i = 0; i < n; ++i)
{
RAWINPUT const& raw = rawBuffer[i];
eventSent |= processRawInput(raw, window /*, mousePosX, mousePosY*/ );
}
break;
// clear processed events from the queue
DefRawInputProc((RAWINPUT**)&rawBuffer, n, sizeof(RAWINPUTHEADER));
}
} // inf. loop
}
break;
case WM_MOUSEWHEEL:
puts("WM_MOUSEWHEEL");
/* The WM_MOUSEWHEEL message is sent to the focus window
* when the mouse wheel is rotated. The DefWindowProc
* function propagates the message to the window's parent.
* There should be no internal forwarding of the message,
* since DefWindowProc propagates it up the parent chain
* until it finds a window that processes it.
*/
event = processWheelEvent(window, wParam, lParam);
break;
case WM_SETCURSOR:
/* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
* to move within a window and mouse input is not captured.
* This means we have to set the cursor shape every time the mouse moves!
* The DefWindowProc function uses this message to set the cursor to an
* arrow if it is not in the client area.
*/
if (LOWORD(lParam) == HTCLIENT) {
// Load the current cursor
window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
// Bypass call to DefWindowProc
return 0;
}
else {
// Outside of client area show standard cursor
window->loadCursor(true, GHOST_kStandardCursorDefault);
case WM_KEYUP:
case WM_SYSKEYUP:
switch (wParam) {
case VK_SHIFT:
case VK_CONTROL:
case VK_MENU:
processModifierKeys(window);
// Bypass call to DefWindowProc
return true;
default:
event = processKeyEvent(window, false, wParam, lParam);
if (!event) {
GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
GHOST_PRINT(msg)
GHOST_PRINT(" key ignored\n")
}
break;
}
break;
#if 0 // this code is illustrative; no need to compile
////////////////////////////////////////////////////////////////////////
// Mouse events, ignored
////////////////////////////////////////////////////////////////////////
case WM_NCMOUSEMOVE:
/* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
* within the nonclient area of the window. This message is posted to the window
* that contains the cursor. If a window has captured the mouse, this message is not posted.
*/
case WM_NCHITTEST:
/* The WM_NCHITTEST message is sent to a window when the cursor moves, or
* when a mouse button is pressed or released. If the mouse is not captured,
* the message is sent to the window beneath the cursor. Otherwise, the message
* is sent to the window that has captured the mouse.
*/
break;
////////////////////////////////////////////////////////////////////////
// Keyboard events, ignored
////////////////////////////////////////////////////////////////////////
case WM_CHAR:
/* The WM_CHAR message is posted to the window with the keyboard focus when
* a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
* contains the character code of the key that was pressed.
*/
case WM_DEADCHAR:
/* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
* WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
* specifies a character code generated by a dead key. A dead key is a key that
* generates a character, such as the umlaut (double-dot), that is combined with
* another character to form a composite character. For example, the umlaut-O
* character (<28>) is generated by typing the dead key for the umlaut character, and
* then typing the O key.
*/
case WM_SYSDEADCHAR:
/* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
* a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
* WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
* a dead key that is pressed while holding down the alt key.
*/
break;
#endif // illustrative code
////////////////////////////////////////////////////////////////////////
// Window events, processed
////////////////////////////////////////////////////////////////////////
case WM_CLOSE:
/* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
event = processWindowEvent(GHOST_kEventWindowClose, window);
////////////////////////////////////////////////////////////////////////
// Tablet events, processed
////////////////////////////////////////////////////////////////////////
case WT_PACKET:
puts("WT_PACKET");
window->processWin32TabletEvent(wParam, lParam);
break;
case WT_CSRCHANGE:
case WT_PROXIMITY:
window->processWin32TabletInitEvent();
break;
////////////////////////////////////////////////////////////////////////
// Mouse events, processed
////////////////////////////////////////////////////////////////////////
case WM_LBUTTONDOWN:
window->registerMouseClickEvent(true);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
break;
case WM_MBUTTONDOWN:
window->registerMouseClickEvent(true);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
break;
case WM_RBUTTONDOWN:
window->registerMouseClickEvent(true);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
break;
case WM_XBUTTONDOWN:
window->registerMouseClickEvent(true);
if ((short) HIWORD(wParam) == XBUTTON1){
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
}else if((short) HIWORD(wParam) == XBUTTON2){
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
}
break;
case WM_LBUTTONUP:
window->registerMouseClickEvent(false);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
break;
case WM_MBUTTONUP:
window->registerMouseClickEvent(false);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
break;
case WM_RBUTTONUP:
window->registerMouseClickEvent(false);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
break;
case WM_XBUTTONUP:
window->registerMouseClickEvent(false);
if ((short) HIWORD(wParam) == XBUTTON1){
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
}else if((short) HIWORD(wParam) == XBUTTON2){
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
}
break;
case WM_MOUSEMOVE:
{
// bool IsFromPen = ((GetMessageExtraInfo() & 0xFF515700) == 0xFF515700); // this only works on TabletPCs
int tabletTool = GetMessageExtraInfo() & 0x7f; // true for tablet mouse, not just pen
if (tabletTool)
puts("(from tablet)");
else
{
int xPrev = mousePosX;
int yPrev = mousePosY;
// window coordinates are passed in via lParam
window->clientToScreen(LOWORD(lParam), HIWORD(lParam), mousePosX, mousePosY);
// putchar('\n');
if (m_input_fidelity_hint == HI_FI)
{
/* int n = */ getMoreMousePoints(mousePosX, mousePosY, xPrev, yPrev, window);
// printf("%d more mouse points found\n", n);
}
// uncomment here and in getMoreMousePoints to show effectiveness of hi-fi input
// printf(" (%d,%d)\n", mousePosX, mousePosY);
event = processCursorEvent(GHOST_kEventCursorMove, window, mousePosX, mousePosY);
}
break;
}
case WM_INPUT:
{
puts("WM_INPUT");
#define RAWCOUNT 10
// just a guess that we'll receive up to 10 event reports
// the following code fetches all available, 10 at a time
RAWINPUT rawBuffer[RAWCOUNT];
while (true)
{
UINT rawSize = sizeof(rawBuffer);
int n = GetRawInputBuffer(rawBuffer, &rawSize, sizeof(RAWINPUTHEADER));
if (n == -1)
{
printf("<!> error %d\n", (int) GetLastError());
break;
case WM_ACTIVATE:
/* The WM_ACTIVATE message is sent to both the window being activated and the window being
* deactivated. If the windows use the same input queue, the message is sent synchronously,
* first to the window procedure of the top-level window being deactivated, then to the window
* procedure of the top-level window being activated. If the windows use different input queues,
* the message is sent asynchronously, so the window is activated immediately.
*/
event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
break;
case WM_PAINT:
/* An application sends the WM_PAINT message when the system or another application
* makes a request to paint a portion of an application's window. The message is sent
* when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage
* function when the application obtains a WM_PAINT message by using the GetMessage or
* PeekMessage function.
*/
event = processWindowEvent(GHOST_kEventWindowUpdate, window);
break;
case WM_GETMINMAXINFO:
/* The WM_GETMINMAXINFO message is sent to a window when the size or
* position of the window is about to change. An application can use
* this message to override the window's default maximized size and
* position, or its default minimum or maximum tracking size.
*/
processMinMaxInfo((MINMAXINFO *) lParam);
/* Let DefWindowProc handle it. */
break;
case WM_SIZE:
/* The WM_SIZE message is sent to a window after its size has changed.
* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
* WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
* to perform any move or size change processing during the WM_WINDOWPOSCHANGED
* message without calling DefWindowProc.
*/
event = processWindowEvent(GHOST_kEventWindowSize, window);
break;
case WM_CAPTURECHANGED:
window->lostMouseCapture();
break;
case WM_MOVING:
/* The WM_MOVING message is sent to a window that the user is moving. By processing
* this message, an application can monitor the size and position of the drag rectangle
* and, if needed, change its size or position.
*/
case WM_MOVE:
/* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
* WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
* to perform any move or size change processing during the WM_WINDOWPOSCHANGED
* message without calling DefWindowProc.
*/
event = processWindowEvent(GHOST_kEventWindowMove, window);
}
else if (n == 0)
{
putchar('\n');
break;
}
else
{
printf("processing %d events:\n", n);
for (int i = 0; i < n; ++i)
{
RAWINPUT const& raw = rawBuffer[i];
eventSent |= processRawInput(raw, window);
}
// clear processed events from the queue
DefRawInputProc((RAWINPUT**)&rawBuffer, n, sizeof(RAWINPUTHEADER));
}
}
break;
}
case WM_MOUSEWHEEL:
puts("WM_MOUSEWHEEL");
/* The WM_MOUSEWHEEL message is sent to the focus window
* when the mouse wheel is rotated. The DefWindowProc
* function propagates the message to the window's parent.
* There should be no internal forwarding of the message,
* since DefWindowProc propagates it up the parent chain
* until it finds a window that processes it.
*/
event = processWheelEvent(window, wParam, lParam);
break;
case WM_SETCURSOR:
/* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
* to move within a window and mouse input is not captured.
* This means we have to set the cursor shape every time the mouse moves!
* The DefWindowProc function uses this message to set the cursor to an
* arrow if it is not in the client area.
*/
if (LOWORD(lParam) == HTCLIENT) {
// Load the current cursor
window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
// Bypass call to DefWindowProc
return 0;
}
else {
// Outside of client area show standard cursor
window->loadCursor(true, GHOST_kStandardCursorDefault);
}
break;
#if 0 // this code is illustrative; no need to compile
////////////////////////////////////////////////////////////////////////
// Window events, ignored
////////////////////////////////////////////////////////////////////////
case WM_WINDOWPOSCHANGED:
/* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
* in the Z order has changed as a result of a call to the SetWindowPos function or
* another window-management function.
* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
* WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
* to perform any move or size change processing during the WM_WINDOWPOSCHANGED
* message without calling DefWindowProc.
*/
case WM_ERASEBKGND:
/* An application sends the WM_ERASEBKGND message when the window background must be
* erased (for example, when a window is resized). The message is sent to prepare an
* invalidated portion of a window for painting.
*/
case WM_NCPAINT:
/* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
case WM_NCACTIVATE:
/* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed
* to indicate an active or inactive state.
*/
case WM_DESTROY:
/* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window
* procedure of the window being destroyed after the window is removed from the screen.
* This message is sent first to the window being destroyed and then to the child windows
* (if any) as they are destroyed. During the processing of the message, it can be assumed
* that all child windows still exist.
*/
case WM_NCDESTROY:
/* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The
* DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
* message. WM_DESTROY is used to free the allocated memory object associated with the window.
*/
case WM_KILLFOCUS:
/* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. */
case WM_SHOWWINDOW:
/* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
case WM_WINDOWPOSCHANGING:
/* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
* the Z order is about to change as a result of a call to the SetWindowPos function or
* another window-management function.
*/
case WM_SETFOCUS:
/* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
case WM_ENTERSIZEMOVE:
/* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving
* or sizing modal loop. The window enters the moving or sizing modal loop when the user
* clicks the window's title bar or sizing border, or when the window passes the
* WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the
* message specifies the SC_MOVE or SC_SIZE value. The operation is complete when
* DefWindowProc returns.
*/
break;
////////////////////////////////////////////////////////////////////////
// Other events
////////////////////////////////////////////////////////////////////////
case WM_GETTEXT:
/* An application sends a WM_GETTEXT message to copy the text that
* corresponds to a window into a buffer provided by the caller.
*/
case WM_ACTIVATEAPP:
/* The WM_ACTIVATEAPP message is sent when a window belonging to a
* different application than the active window is about to be activated.
* The message is sent to the application whose window is being activated
* and to the application whose window is being deactivated.
*/
case WM_TIMER:
/* The WIN32 docs say:
* The WM_TIMER message is posted to the installing thread's message queue
* when a timer expires. You can process the message by providing a WM_TIMER
* case in the window procedure. Otherwise, the default window procedure will
* call the TimerProc callback function specified in the call to the SetTimer
* function used to install the timer.
*
* In GHOST, we let DefWindowProc call the timer callback.
*/
break;
////////////////////////////////////////////////////////////////////////
// Mouse events, ignored
////////////////////////////////////////////////////////////////////////
case WM_NCMOUSEMOVE:
/* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
* within the nonclient area of the window. This message is posted to the window
* that contains the cursor. If a window has captured the mouse, this message is not posted.
*/
case WM_NCHITTEST:
/* The WM_NCHITTEST message is sent to a window when the cursor moves, or
* when a mouse button is pressed or released. If the mouse is not captured,
* the message is sent to the window beneath the cursor. Otherwise, the message
* is sent to the window that has captured the mouse.
*/
break;
#endif // illustrative code
#if 0 // this is part of the 'old' NDOF system; new one coming soon!
case WM_BLND_NDOF_AXIS:
{
GHOST_TEventNDOFData ndofdata;
m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
m_eventManager->
pushEvent(new GHOST_EventNDOF(
getMilliSeconds(),
GHOST_kEventNDOFMotion,
window, ndofdata));
}
break;
case WM_BLND_NDOF_BTN:
{
GHOST_TEventNDOFData ndofdata;
m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
m_eventManager->
pushEvent(new GHOST_EventNDOF(
getMilliSeconds(),
GHOST_kEventNDOFButton,
window, ndofdata));
}
break;
#endif // old NDOF
}
////////////////////////////////////////////////////////////////////////
// Window events, processed
////////////////////////////////////////////////////////////////////////
case WM_CLOSE:
/* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
event = processWindowEvent(GHOST_kEventWindowClose, window);
break;
case WM_ACTIVATE:
/* The WM_ACTIVATE message is sent to both the window being activated and the window being
* deactivated. If the windows use the same input queue, the message is sent synchronously,
* first to the window procedure of the top-level window being deactivated, then to the window
* procedure of the top-level window being activated. If the windows use different input queues,
* the message is sent asynchronously, so the window is activated immediately.
*/
event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
break;
case WM_PAINT:
/* An application sends the WM_PAINT message when the system or another application
* makes a request to paint a portion of an application's window. The message is sent
* when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage
* function when the application obtains a WM_PAINT message by using the GetMessage or
* PeekMessage function.
*/
event = processWindowEvent(GHOST_kEventWindowUpdate, window);
break;
case WM_GETMINMAXINFO:
/* The WM_GETMINMAXINFO message is sent to a window when the size or
* position of the window is about to change. An application can use
* this message to override the window's default maximized size and
* position, or its default minimum or maximum tracking size.
*/
processMinMaxInfo((MINMAXINFO *) lParam);
/* Let DefWindowProc handle it. */
break;
case WM_SIZE:
/* The WM_SIZE message is sent to a window after its size has changed.
* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
* WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
* to perform any move or size change processing during the WM_WINDOWPOSCHANGED
* message without calling DefWindowProc.
*/
event = processWindowEvent(GHOST_kEventWindowSize, window);
break;
case WM_CAPTURECHANGED:
window->lostMouseCapture();
break;
case WM_MOVING:
/* The WM_MOVING message is sent to a window that the user is moving. By processing
* this message, an application can monitor the size and position of the drag rectangle
* and, if needed, change its size or position.
*/
case WM_MOVE:
/* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
* WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
* to perform any move or size change processing during the WM_WINDOWPOSCHANGED
* message without calling DefWindowProc.
*/
event = processWindowEvent(GHOST_kEventWindowMove, window);
break;
#if 0 // this code is illustrative; no need to compile
////////////////////////////////////////////////////////////////////////
// Window events, ignored
////////////////////////////////////////////////////////////////////////
case WM_WINDOWPOSCHANGED:
/* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
* in the Z order has changed as a result of a call to the SetWindowPos function or
* another window-management function.
* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
* WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
* to perform any move or size change processing during the WM_WINDOWPOSCHANGED
* message without calling DefWindowProc.
*/
case WM_ERASEBKGND:
/* An application sends the WM_ERASEBKGND message when the window background must be
* erased (for example, when a window is resized). The message is sent to prepare an
* invalidated portion of a window for painting.
*/
case WM_NCPAINT:
/* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
case WM_NCACTIVATE:
/* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed
* to indicate an active or inactive state.
*/
case WM_DESTROY:
/* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window
* procedure of the window being destroyed after the window is removed from the screen.
* This message is sent first to the window being destroyed and then to the child windows
* (if any) as they are destroyed. During the processing of the message, it can be assumed
* that all child windows still exist.
*/
case WM_NCDESTROY:
/* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The
* DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
* message. WM_DESTROY is used to free the allocated memory object associated with the window.
*/
case WM_KILLFOCUS:
/* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. */
case WM_SHOWWINDOW:
/* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
case WM_WINDOWPOSCHANGING:
/* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
* the Z order is about to change as a result of a call to the SetWindowPos function or
* another window-management function.
*/
case WM_SETFOCUS:
/* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
case WM_ENTERSIZEMOVE:
/* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving
* or sizing modal loop. The window enters the moving or sizing modal loop when the user
* clicks the window's title bar or sizing border, or when the window passes the
* WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the
* message specifies the SC_MOVE or SC_SIZE value. The operation is complete when
* DefWindowProc returns.
*/
break;
////////////////////////////////////////////////////////////////////////
// Other events
////////////////////////////////////////////////////////////////////////
case WM_GETTEXT:
/* An application sends a WM_GETTEXT message to copy the text that
* corresponds to a window into a buffer provided by the caller.
*/
case WM_ACTIVATEAPP:
/* The WM_ACTIVATEAPP message is sent when a window belonging to a
* different application than the active window is about to be activated.
* The message is sent to the application whose window is being activated
* and to the application whose window is being deactivated.
*/
case WM_TIMER:
/* The WIN32 docs say:
* The WM_TIMER message is posted to the installing thread's message queue
* when a timer expires. You can process the message by providing a WM_TIMER
* case in the window procedure. Otherwise, the default window procedure will
* call the TimerProc callback function specified in the call to the SetTimer
* function used to install the timer.
*
* In GHOST, we let DefWindowProc call the timer callback.
*/
break;
#endif // illustrative code
}
if (!eventSent)
if (event) {

View File

@@ -304,10 +304,9 @@ protected:
* Creates and sends mouse or multi-axis events.
* @param raw a single RawInput structure
* @param window The window receiving the event (the active window).
* @param x,y current mouse coordinates, which may be updated by this function
* @return Whether any events (possibly more than one) were created and sent.
*/
bool processRawInput(RAWINPUT const& raw, GHOST_WindowWin32* window /*, int& x, int& y */ );
bool processRawInput(RAWINPUT const& raw, GHOST_WindowWin32* window);
/**
* Creates and sends mouse events for mouse movements "in between" WM_MOUSEMOVEs.
@@ -333,10 +332,11 @@ protected:
/**
* Windows call back routine for our window class.
* This handles general errors, then passes control to handleEvent.
*/
static LRESULT WINAPI s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
// non-static version of WndProc
/** Non-static version of WndProc. */
bool handleEvent(GHOST_WindowWin32* window, UINT msg, WPARAM wParam, LPARAM lParam);
/** The current state of the modifier keys. */