Skip to content

Instantly share code, notes, and snippets.

@paulhoux
Created November 5, 2011 03:57
Show Gist options
  • Save paulhoux/1341083 to your computer and use it in GitHub Desktop.
Save paulhoux/1341083 to your computer and use it in GitHub Desktop.
Cinder: improved frame rate and low CPU usage on Windows PC's
/*
Copyright (c) 2010, The Barbarian Group
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that
the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "cinder/app/AppImplMswBasic.h"
#include "cinder/app/AppBasic.h"
#include "cinder/app/AppImplMswRenderer.h"
#include "cinder/app/Renderer.h"
#include "cinder/Utilities.h"
#include <windowsx.h>
#include <winuser.h>
using std::vector;
using std::string;
#define LOSHORT(l) ((SHORT)(l))
#define HISHORT(l) ((SHORT)(((DWORD)(l) >> 16) & 0xFFFF))
namespace cinder { namespace app {
static const wchar_t *WINDOWED_WIN_CLASS_NAME = TEXT("CinderWinClass");
static const wchar_t *FULLSCREEN_WIN_CLASS_NAME = TEXT("CinderWinFSClass");
AppImplMswBasic::AppImplMswBasic( AppBasic *aApp )
: AppImplMsw( aApp ), mApp( aApp ), mHasBeenInitialized( false )
{
mShouldQuit = false;
mIsDragging = false;
}
void AppImplMswBasic::run()
{
mDisplay = mApp->getSettings().getDisplay();
if( ! mDisplay )
mDisplay = cinder::Display::getMainDisplay().get();
if( mApp->getSettings().isFullScreen() ) {
mFullScreen = true;
mWindowWidth = mApp->getSettings().getFullScreenWidth();
mWindowHeight = mApp->getSettings().getFullScreenHeight();
}
else {
mFullScreen = false;
mWindowWidth = mApp->getSettings().getWindowWidth();
mWindowHeight = mApp->getSettings().getWindowHeight();
}
mFrameRate = mApp->getSettings().getFrameRate();
createWindow( &mWindowWidth, &mWindowHeight );
mApp->privateSetup__();
mHasBeenInitialized = true;
mApp->privateResize__( ResizeEvent( Vec2i( mWindowWidth, mWindowHeight ) ) );
::ShowWindow( mWnd, SW_SHOW );
::SetForegroundWindow( mWnd );
::SetFocus( mWnd );
// initialize our next frame time
mNextFrameTime = getElapsedSeconds();
// game loop
while( ! mShouldQuit ) {
// update and draw
mApp->privateUpdate__();
::RedrawWindow( mWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW );
// get current time in seconds
double currentSeconds = mApp->getElapsedSeconds();
// calculate time per frame in seconds
double secondsPerFrame = 1.0 / mFrameRate;
// determine if application was frozen for a while and adjust next frame time
double elapsedSeconds = currentSeconds - mNextFrameTime;
if(elapsedSeconds > 1.0) {
int numSkipFrames = (int)(elapsedSeconds / secondsPerFrame);
mNextFrameTime += (numSkipFrames * secondsPerFrame);
}
// determine when next frame should be drawn
mNextFrameTime += secondsPerFrame;
// sleep and process messages until next frame
if(mNextFrameTime > currentSeconds)
sleep(mNextFrameTime - currentSeconds);
else {
MSG msg;
while( ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
}
}
killWindow( mFullScreen );
mApp->privateShutdown__();
delete mApp;
}
void AppImplMswBasic::sleep( double seconds )
{
// create waitable timer
static HANDLE timer = CreateWaitableTimer( NULL, FALSE, NULL );
// specify relative wait time in units of 100 nanoseconds
LARGE_INTEGER waitTime;
waitTime.QuadPart = (LONGLONG)(seconds * -10000000);
if(waitTime.QuadPart >= 0) return;
// activate waitable timer
if ( !::SetWaitableTimer( timer, &waitTime, 0, NULL, NULL, FALSE ) )
return;
// handle events until specified time has elapsed
DWORD result;
MSG msg;
while( ! mShouldQuit ) {
result = ::MsgWaitForMultipleObjects( 1, &timer, false, INFINITE, QS_ALLINPUT );
if ( result == (WAIT_OBJECT_0 + 1) ) {
// execute messages as soon as they arrive
while( ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
// resume waiting
}
else return; // time has elapsed
}
}
bool AppImplMswBasic::createWindow( int *width, int *height )
{
int bits = 32;
if( *width <= 0 ) {
*width = ::GetSystemMetrics( SM_CXSCREEN );
*height = ::GetSystemMetrics( SM_CYSCREEN );
}
WNDCLASS wc; // Windows Class Structure
RECT WindowRect; // Grabs Rectangle Upper Left / Lower Right Values
if( mFullScreen ) {
WindowRect.left = 0L;
WindowRect.right = (long)*width;
WindowRect.top = 0L;
WindowRect.bottom = (long)*height;
}
else { // center the window on the display if windowed
WindowRect.left = ( getDisplay()->getWidth() - *width ) / 2;
WindowRect.right = ( getDisplay()->getWidth() - *width ) / 2 + *width;
WindowRect.top = ( getDisplay()->getHeight() - *height ) / 2;
WindowRect.bottom = ( getDisplay()->getHeight() - *height ) / 2 + *height;
}
mInstance = ::GetModuleHandle( NULL ); // Grab An Instance For Our Window
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw On Size, And Own DC For Window.
wc.lpfnWndProc = WndProc; // WndProc Handles Messages
wc.cbClsExtra = 0; // No Extra Window Data
wc.cbWndExtra = 0; // No Extra Window Data
wc.hInstance = mInstance; // Set The Instance
wc.hIcon = ::LoadIcon( NULL, IDI_WINLOGO ); // Load The Default Icon
wc.hCursor = ::LoadCursor( NULL, IDC_ARROW ); // Load The Arrow Pointer
wc.hbrBackground = NULL; // No Background Required For GL
wc.lpszMenuName = NULL; // We Don't Want A Menu
wc.lpszClassName = ( mFullScreen ) ? FULLSCREEN_WIN_CLASS_NAME : WINDOWED_WIN_CLASS_NAME;
if( ! ::RegisterClass( &wc ) ) { // Attempt To Register The Window Class
DWORD err = ::GetLastError();
return false;
}
if( mFullScreen ) {
DEVMODE dmScreenSettings;
memset( &dmScreenSettings, 0, sizeof(dmScreenSettings) ); // Makes Sure Memory's Cleared
dmScreenSettings.dmSize = sizeof( dmScreenSettings );
dmScreenSettings.dmPelsWidth = *width;
dmScreenSettings.dmPelsHeight = *height;
dmScreenSettings.dmBitsPerPel = bits;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
// Try To Set Selected Mode And Get Results. NOTE: CDS_mFullscreen Gets Rid Of Start Bar.
if( ::ChangeDisplaySettings( &dmScreenSettings, CDS_FULLSCREEN ) != DISP_CHANGE_SUCCESSFUL )
return false;
mWindowExStyle = WS_EX_APPWINDOW; // Window Extended Style
mWindowStyle = WS_POPUP; // Windows Style
::ShowCursor( TRUE ); // Hide Mouse Pointer
}
else {
mWindowExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style
mWindowStyle = ( mApp->getSettings().isResizable() ) ? WS_OVERLAPPEDWINDOW
: ( WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME ); // Windows Style
}
::AdjustWindowRectEx( &WindowRect, mWindowStyle, FALSE, mWindowExStyle ); // Adjust Window To True Requested Size
std::wstring unicodeTitle = toUtf16( mApp->getSettings().getTitle() );
// Create The Window
if( ! ( mWnd = ::CreateWindowEx( mWindowExStyle, // Extended Style For The Window
( mFullScreen ) ? FULLSCREEN_WIN_CLASS_NAME : WINDOWED_WIN_CLASS_NAME,
unicodeTitle.c_str(), // Window Title
mWindowStyle, // Required Window Style
WindowRect.left, WindowRect.top, // Window Position
WindowRect.right-WindowRect.left, // Calculate Window Width
WindowRect.bottom-WindowRect.top, // Calculate Window Height
NULL, // No Parent Window
NULL, // No Menu
mInstance, // Instance
reinterpret_cast<LPVOID>( this ) )) )
{
//killWindow(); // Reset The Display
return false;
}
mDC = ::GetDC( mWnd );
if( ! mDC ) {
killWindow( mFullScreen );
return false;
}
mApp->getRenderer()->setup( mApp, mWnd, mDC );
::DragAcceptFiles( mWnd, TRUE );
enableMultiTouch();
return true; // Success
}
void AppImplMswBasic::killWindow( bool wasFullScreen )
{
mApp->getRenderer()->kill();
if( wasFullScreen )
::ChangeDisplaySettings( NULL, 0 );
if( mDC )
::ReleaseDC( mWnd, mDC );
if( mWnd )
::DestroyWindow( mWnd );
if( wasFullScreen )
::UnregisterClass( FULLSCREEN_WIN_CLASS_NAME, mInstance );
else
::UnregisterClass( WINDOWED_WIN_CLASS_NAME, mInstance );
mWnd = 0;
}
void AppImplMswBasic::toggleFullScreen()
{
bool prevFullScreen = mFullScreen;
HDC oldDC = mDC;
HWND oldWnd = mWnd;
mFullScreen = ! mFullScreen;
int windowWidth, windowHeight;
if( mApp->isFullScreen() ) {
windowWidth = mApp->getSettings().getFullScreenWidth();
windowHeight = mApp->getSettings().getFullScreenHeight();
}
else {
windowWidth = mApp->getSettings().getWindowWidth();
windowHeight = mApp->getSettings().getWindowHeight();
}
// prepare for a new one
if( prevFullScreen ) {
::ChangeDisplaySettings( NULL, 0 );
}
mApp->getRenderer()->prepareToggleFullScreen();
createWindow( &windowWidth, &windowHeight );
mApp->getRenderer()->finishToggleFullScreen();
::ReleaseDC( oldWnd, oldDC );
::DestroyWindow( oldWnd );
if( prevFullScreen )
::UnregisterClass( FULLSCREEN_WIN_CLASS_NAME, mInstance );
else
::UnregisterClass( WINDOWED_WIN_CLASS_NAME, mInstance );
mWindowWidth = windowWidth;
mWindowHeight = windowHeight;
::ShowWindow( mWnd, SW_SHOW );
::SetForegroundWindow( mWnd );
::SetFocus( mWnd );
::DragAcceptFiles( mWnd, TRUE );
enableMultiTouch();
mApp->privateResize__( ResizeEvent( Vec2i( mApp->getWindowWidth(), mApp->getWindowHeight() ) ) );
}
void AppImplMswBasic::enableMultiTouch()
{
if( mApp->getSettings().isMultiTouchEnabled() ) {
// we need to make sure this version of User32 even has MultiTouch symbols, so we'll do that with GetProcAddress
BOOL (WINAPI *RegisterTouchWindow)( HWND, ULONG);
*(DWORD *)&RegisterTouchWindow = (DWORD)::GetProcAddress( ::GetModuleHandle(TEXT("user32.dll")), "RegisterTouchWindow" );
if( RegisterTouchWindow ) {
(*RegisterTouchWindow)( mWnd, 0 );
}
}
}
void AppImplMswBasic::setWindowWidth( int aWindowWidth )
{
int screenWidth, screenHeight;
getScreenSize( aWindowWidth, mApp->getWindowHeight(), &screenWidth, &screenHeight );
::SetWindowPos( mWnd, HWND_TOP, 0, 0, screenWidth, screenHeight, SWP_NOMOVE );
}
void AppImplMswBasic::setWindowHeight( int aWindowHeight )
{
int screenWidth, screenHeight;
getScreenSize( mApp->getWindowWidth(), aWindowHeight, &screenWidth, &screenHeight );
::SetWindowPos( mWnd, HWND_TOP, 0, 0, screenWidth, screenHeight, SWP_NOMOVE );
}
void AppImplMswBasic::setWindowSize( int aWindowWidth, int aWindowHeight )
{
int screenWidth, screenHeight;
getScreenSize( aWindowWidth, aWindowHeight, &screenWidth, &screenHeight );
::SetWindowPos( mWnd, HWND_TOP, 0, 0, screenWidth, screenHeight, SWP_NOMOVE );
}
float AppImplMswBasic::setFrameRate( float aFrameRate )
{
mFrameRate = aFrameRate; // fix
return aFrameRate;
}
void AppImplMswBasic::getScreenSize( int clientWidth, int clientHeight, int *resultWidth, int *resultHeight )
{
RECT windowRect;
windowRect.left = windowRect.top = 0;
windowRect.right = clientWidth;
windowRect.bottom = clientHeight;
::AdjustWindowRectEx( &windowRect, mWindowStyle, FALSE, mWindowExStyle );
*resultWidth = windowRect.right - windowRect.left;
*resultHeight = windowRect.bottom - windowRect.top;
}
void AppImplMswBasic::onTouch( HWND hWnd, WPARAM wParam, LPARAM lParam )
{
// pull these symbols dynamically out of the user32.dll
static BOOL (WINAPI *GetTouchInputInfo)( HTOUCHINPUT, UINT, PTOUCHINPUT, int ) = NULL;
if( ! GetTouchInputInfo )
*(size_t *)&GetTouchInputInfo = (size_t)::GetProcAddress( ::GetModuleHandle(TEXT("user32.dll")), "GetTouchInputInfo" );
static BOOL (WINAPI *CloseTouchInputHandle)( HTOUCHINPUT ) = NULL;
if( ! CloseTouchInputHandle )
*(size_t *)&CloseTouchInputHandle = (size_t)::GetProcAddress( ::GetModuleHandle(TEXT("user32.dll")), "CloseTouchInputHandle" );
bool handled = false;
double currentTime = getApp()->getElapsedSeconds(); // we don't trust the device's sense of time
unsigned int numInputs = LOWORD( wParam );
std::shared_ptr<TOUCHINPUT> pInputs = std::shared_ptr<TOUCHINPUT>( new TOUCHINPUT[numInputs], checked_array_deleter<TOUCHINPUT>() );
if( pInputs ) {
vector<TouchEvent::Touch> beganTouches, movedTouches, endTouches, activeTouches;
if( GetTouchInputInfo((HTOUCHINPUT)lParam, numInputs, pInputs.get(), sizeof(TOUCHINPUT) ) ) {
for( unsigned int i = 0; i < numInputs; i++ ) {
const TOUCHINPUT &ti = pInputs.get()[i];
if( ti.dwID != 0 ) {
POINT pt;
// this has a small problem, which is that we lose the subpixel precision of the touch points.
// However ScreenToClient doesn't support floating or fixed point either, so we're stuck
// unless we write our own ScreenToClient, which actually should be doable
pt.x = TOUCH_COORD_TO_PIXEL( ti.x );
pt.y = TOUCH_COORD_TO_PIXEL( ti.y );
::ScreenToClient( hWnd, &pt );
if( ti.dwFlags & 0x0004/*TOUCHEVENTF_UP*/ ) {
Vec2f prevPos = mMultiTouchPrev[ti.dwID];
endTouches.push_back( TouchEvent::Touch( Vec2f( (float)pt.x, (float)pt.y ), prevPos, ti.dwID, currentTime, &pInputs.get()[i] ) );
mMultiTouchPrev.erase( ti.dwID );
}
else if( ti.dwFlags & 0x0002/*TOUCHEVENTF_DOWN*/ ) {
beganTouches.push_back( TouchEvent::Touch( Vec2f( (float)pt.x, (float)pt.y ), Vec2f( (float)pt.x, (float)pt.y ), ti.dwID, currentTime, &pInputs.get()[i] ) );
mMultiTouchPrev[ti.dwID] = Vec2f( (float)pt.x, (float)pt.y );
activeTouches.push_back( beganTouches.back() );
}
else if( ti.dwFlags & 0x0001/*TOUCHEVENTF_MOVE*/ ) {
movedTouches.push_back( TouchEvent::Touch( Vec2f( (float)pt.x, (float)pt.y ), mMultiTouchPrev[ti.dwID], ti.dwID, currentTime, &pInputs.get()[i] ) );
activeTouches.push_back( movedTouches.back() );
mMultiTouchPrev[ti.dwID] = Vec2f( (float)pt.x, (float)pt.y );
}
}
}
getApp()->privateSetActiveTouches__( activeTouches );
// we need to post the event here so that our pInputs array is still valid since we've passed addresses into it as the native pointers
if( ! beganTouches.empty() )
getApp()->privateTouchesBegan__( beganTouches );
if( ! movedTouches.empty() )
getApp()->privateTouchesMoved__( movedTouches );
if( ! endTouches.empty() )
getApp()->privateTouchesEnded__( endTouches );
handled = ( ! beganTouches.empty() ) || ( ! movedTouches.empty() ) || ( ! endTouches.empty() );
CloseTouchInputHandle( (HTOUCHINPUT)lParam ); // this is exception-unsafe; we need some RAII goin' on there
}
else {
// for now we'll just ignore an error
}
}
if( ! handled ) {
// if we didn't handle the message, let DefWindowProc handle it
::DefWindowProc( hWnd, WM_TOUCH, wParam, lParam );
}
}
unsigned int prepMouseEventModifiers( WPARAM wParam )
{
unsigned int result = 0;
if( wParam & MK_CONTROL ) result |= MouseEvent::CTRL_DOWN;
if( wParam & MK_LBUTTON ) result |= MouseEvent::LEFT_DOWN;
if( wParam & MK_MBUTTON ) result |= MouseEvent::MIDDLE_DOWN;
if( wParam & MK_RBUTTON ) result |= MouseEvent::RIGHT_DOWN;
if( wParam & MK_SHIFT ) result |= MouseEvent::SHIFT_DOWN;
if( ::GetKeyState( VK_MENU ) < 0 ) result |= MouseEvent::ALT_DOWN;
if( (::GetKeyState( VK_LWIN ) < 0) || (::GetKeyState( VK_RWIN ) < 0) ) result |= MouseEvent::META_DOWN;
return result;
}
// Certain key codes need to be refined, for example VK_MENU needs to be
// converted into VK_LALT or VK_RALT
int prepNativeKeyCode( WPARAM wParam )
{
unsigned int result = (int)wParam;
if( wParam == VK_MENU ) {
result = ( ::GetKeyState( VK_RMENU ) ) ? VK_RMENU : VK_LMENU;
}
else if( wParam == VK_SHIFT ) {
result = ( ::GetKeyState( VK_RSHIFT ) ) ? VK_RSHIFT : VK_LSHIFT;
}
else if( wParam == VK_CONTROL ) {
result = ( ::GetKeyState( VK_RCONTROL ) ) ? VK_RCONTROL : VK_LCONTROL;
}
return result;
}
char mapVirtualKey( WPARAM wParam )
{
BYTE keyboardState[256];
::GetKeyboardState( keyboardState );
WORD result[4];
// the control key messes up the ToAscii result, so we zero it out
keyboardState[VK_CONTROL] = 0;
int resultLength = ::ToAscii( wParam, ::MapVirtualKey( wParam, 0 ), keyboardState, result, 0 );
if( resultLength == 1 )
return (char)result[0];
else
return 0;
}
unsigned int prepKeyEventModifiers()
{
unsigned int result = 0;
if( ::GetKeyState( VK_CONTROL ) & 0x8000 ) result |= KeyEvent::CTRL_DOWN;
if( ::GetKeyState( VK_SHIFT ) & 0x8000 ) result |= KeyEvent::SHIFT_DOWN;
if( ( ::GetKeyState( VK_LMENU ) & 0x8000 ) || ( ::GetKeyState( VK_RMENU ) & 0x8000 ) ) result |= KeyEvent::ALT_DOWN;
if( ( ::GetKeyState( VK_LWIN ) < 0 ) || ( ::GetKeyState( VK_RWIN ) < 0 ) ) result |= KeyEvent::META_DOWN;
return result;
}
extern "C" {
LRESULT CALLBACK WndProc( HWND mWnd, // Handle For This Window
UINT uMsg, // Message For This Window
WPARAM wParam, // Additional Message Information
LPARAM lParam) // Additional Message Information
{
AppImplMswBasic* impl = 0;
// if the message is WM_NCCREATE we need to hide 'this' in the window long
if( uMsg == WM_NCCREATE ) {
impl = reinterpret_cast<AppImplMswBasic*>(((LPCREATESTRUCT)lParam)->lpCreateParams);
::SetWindowLongPtr( mWnd, GWL_USERDATA, (__int3264)(LONG_PTR)impl );
}
else // the warning on this line is harmless
impl = reinterpret_cast<AppImplMswBasic*>( ::GetWindowLongPtr( mWnd, GWL_USERDATA ) );
if( ! impl )
return DefWindowProc( mWnd, uMsg, wParam, lParam );
switch( uMsg ) { // Check For Windows Messages
case WM_SYSCOMMAND: // Intercept System Commands
switch( wParam ) { // Check System Calls
case SC_SCREENSAVE: // Screensaver Trying To Start?
case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
if( impl->getApp()->getSettings().getPowerManagement() )
return 0; // prevent
}
break;
case WM_CLOSE: // Did We Receive A Close Message?
::PostQuitMessage(0); // Send A Quit Message
impl->quit();
return 0;
break;
case WM_SYSKEYDOWN:
case WM_KEYDOWN: // Is A Key Being Held Down?
impl->getApp()->privateKeyDown__( KeyEvent( KeyEvent::translateNativeKeyCode( prepNativeKeyCode( (int)wParam ) ),
mapVirtualKey( wParam ), prepKeyEventModifiers(), (int)wParam ) );
return 0; // Jump Back
break;
case WM_SYSKEYUP:
case WM_KEYUP: // Has A Key Been Released?
impl->getApp()->privateKeyUp__( KeyEvent( KeyEvent::translateNativeKeyCode( prepNativeKeyCode( (int)wParam ) ),
mapVirtualKey( wParam ), prepKeyEventModifiers(), (int)wParam ) );
return 0; // Jump Back
break;
// mouse events
case WM_LBUTTONDOWN:
::SetCapture( mWnd );
impl->mIsDragging = true;
impl->getApp()->privateMouseDown__( MouseEvent( MouseEvent::LEFT_DOWN, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ), 0.0f, static_cast<unsigned int>( wParam ) ) );
return 0;
break;
case WM_RBUTTONDOWN:
::SetCapture( mWnd );
impl->mIsDragging = true;
impl->getApp()->privateMouseDown__( MouseEvent( MouseEvent::RIGHT_DOWN, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ), 0.0f, static_cast<unsigned int>( wParam ) ) );
return 0;
break;
case WM_MBUTTONDOWN:
::SetCapture( mWnd );
impl->mIsDragging = true;
impl->getApp()->privateMouseDown__( MouseEvent( MouseEvent::MIDDLE_DOWN, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ), 0.0f, static_cast<unsigned int>( wParam ) ) );
return 0;
break;
case WM_LBUTTONUP:
::ReleaseCapture();
impl->mIsDragging = false;
impl->getApp()->privateMouseUp__( MouseEvent( MouseEvent::LEFT_DOWN, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ), 0.0f, static_cast<unsigned int>( wParam ) ) );
return 0;
break;
case WM_RBUTTONUP:
::ReleaseCapture();
impl->mIsDragging = false;
impl->getApp()->privateMouseUp__( MouseEvent( MouseEvent::RIGHT_DOWN, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ), 0.0f, static_cast<unsigned int>( wParam ) ) );
return 0;
break;
case WM_MBUTTONUP:
::ReleaseCapture();
impl->mIsDragging = false;
impl->getApp()->privateMouseUp__( MouseEvent( MouseEvent::MIDDLE_DOWN, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ), 0.0f, static_cast<unsigned int>( wParam ) ) );
return 0;
break;
case WM_MOUSEWHEEL:
impl->getApp()->privateMouseWheel__( MouseEvent( 0, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ),
GET_WHEEL_DELTA_WPARAM( wParam ) / 120.0f, static_cast<unsigned int>( wParam ) ) );
break;
case WM_KILLFOCUS:
// if we lose capture during a drag, post a mouseup event as a notifier
if( impl->mIsDragging ) {
impl->getApp()->privateMouseUp__( MouseEvent( 0, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ), 0.0f, static_cast<unsigned int>( wParam ) ) );
}
impl->mIsDragging = false;
break;
case WM_MOUSEMOVE:
if( impl->mIsDragging ) {
impl->getApp()->privateMouseDrag__( MouseEvent( 0, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ),
0.0f, static_cast<unsigned int>( wParam ) ) );
}
else
impl->getApp()->privateMouseMove__( MouseEvent( 0, LOSHORT(lParam), HISHORT(lParam), prepMouseEventModifiers( wParam ),
0.0f, static_cast<unsigned int>( wParam ) ) );
break;
case WM_SIZE:
if( impl->mHasBeenInitialized ) {
impl->mWindowWidth = LOWORD(lParam);
impl->mWindowHeight = HIWORD(lParam);
impl->getApp()->privateResize__( ResizeEvent( Vec2i( impl->mWindowWidth, impl->mWindowHeight ) ) );
}
return 0;
break;
case WM_MOVE:
if( impl->mHasBeenInitialized ) {
impl->privateSetWindowOffset__( Vec2i( LOSHORT(lParam), HISHORT(lParam) ) );
}
return 0;
break;
case WM_DROPFILES: {
HDROP dropH = (HDROP)wParam;
POINT dropPoint;
char fileName[8192];
vector<string> files;
int droppedFileCount = ::DragQueryFile( dropH, 0xFFFFFFFF, 0, 0 );
for( int i = 0; i < droppedFileCount; ++i ) {
::DragQueryFileA( dropH, i, fileName, 8192 );
files.push_back( std::string( fileName ) );
}
::DragQueryPoint( dropH, &dropPoint );
::DragFinish( dropH );
FileDropEvent dropEvent( dropPoint.x, dropPoint.y, files );
impl->getApp()->privateFileDrop__( dropEvent );
return 0;
}
break;
case WM_PAINT:
impl->getApp()->getRenderer()->startDraw();
impl->getApp()->draw();
impl->getApp()->getRenderer()->finishDraw();
break;
case WM_TOUCH:
impl->onTouch( mWnd, wParam, lParam );
break;
}
// unhandled messages To DefWindowProc
return DefWindowProc( mWnd, uMsg, wParam, lParam );
}
} // extern "C"
} } // namespace cinder::app
/*
Copyright (c) 2010, The Barbarian Group
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that
the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <windows.h>
#undef min
#undef max
#include "cinder/app/AppImplMsw.h"
#include "cinder/app/AppImplMswRenderer.h"
#include "cinder/Display.h"
namespace cinder { namespace app {
extern "C" LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
class AppImplMswBasic : public AppImplMsw {
public:
AppImplMswBasic( class AppBasic *aApp );
void run();
class AppBasic* getApp() { return mApp; }
void quit() { mShouldQuit = true; }
void setWindowWidth( int aWindowWidth );
void setWindowHeight( int aWindowHeight );
void setWindowSize( int aWindowWidth, int aWindowHeight );
float setFrameRate( float aFrameRate );
void toggleFullScreen();
std::string getAppPath() const;
Display* getDisplay() { return mDisplay; }
protected:
void sleep( double seconds );
bool createWindow( int *width, int *height );
void killWindow( bool wasFullScreen );
void enableMultiTouch();
void getScreenSize( int clientWidth, int clientHeight, int *resultWidth, int *resultHeight );
void onTouch( HWND hWnd, WPARAM wParam, LPARAM lParam );
bool mShouldQuit;
bool mIsDragging;
bool mHasBeenInitialized;
class AppBasic *mApp;
// Windows window variables
HWND mWnd;
HDC mDC;
HINSTANCE mInstance;
DWORD mWindowStyle, mWindowExStyle;
double mNextFrameTime;
Display *mDisplay;
std::map<DWORD,Vec2f> mMultiTouchPrev;
friend LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
};
} } // namespace cinder::app
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment