Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,29 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "windowManager/dedicated/dedicatedWindowStub.h"
PlatformWindowManager *CreatePlatformWindowManager()
{
return new DedicatedWindowMgr;
}

View file

@ -0,0 +1,109 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifdef TORQUE_DEDICATED
#ifndef _DEDICATED_WINDOW_STUB_H_
#define _DEDICATED_WINDOW_STUB_H_
#include "windowManager/platformWindowMgr.h"
/// The DedicatedWindowMgr is for Dedicated servers, which may not
/// even have graphics hardware. However, the WindowManager is referenced
/// (indirectly) by scripts, and therefore must exist.
class DedicatedWindowMgr : public PlatformWindowManager
{
public:
DedicatedWindowMgr() {};
virtual ~DedicatedWindowMgr()
{
}
static void processCmdLineArgs(const S32 argc, const char **argv) {}
/// Return the extents in window coordinates of the primary desktop
/// area. On dedicated systems, this returns a token value of 0.
virtual RectI getPrimaryDesktopArea() { return RectI(0, 0, 0, 0); }
/// Retrieve the currently set desktop bit depth.
/// @return -1 since there is no desktop
virtual S32 getDesktopBitDepth() { return -1; }
/// Retrieve the currently set desktop resolution
/// @return Point2I(-1,-1) since there is no desktop
virtual Point2I getDesktopResolution() { return Point2I(-1, -1); }
/// Populate a vector with the token primary desktop area value
virtual void getMonitorRegions(Vector<RectI> &regions) { regions.push_back(getPrimaryDesktopArea()); }
/// Create a new window, appropriate for the specified device and mode.
///
/// @return NULL - there is no graphics hardware available in dedicated mode
virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode) { return NULL; }
/// Produces an empty list since there are no windows in dedicated mode
virtual void getWindows(VectorPtr<PlatformWindow*> &windows) { windows.clear(); }
/// Get the window that currently has the input focus or NULL.
virtual PlatformWindow* getFocusedWindow() { return NULL; }
/// Get a window from a device ID.
///
/// @return NULL.
virtual PlatformWindow *getWindowById(WindowId id) { return NULL; }
/// Get the first window in the window list
///
/// @return The first window in the list, or NULL if no windows found
virtual PlatformWindow *getFirstWindow() { return NULL; }
/// Set the parent window
///
/// This does nothing in dedicated builds
virtual void setParentWindow(void* newParent) {}
/// Get the parent window - returns NULL for dedicated servers
virtual void* getParentWindow() { return NULL; }
/// This method cues the appearance of that window ("lowering the curtain").
virtual void lowerCurtain() {}
/// @see lowerCurtain
///
/// This method removes the curtain window.
virtual void raiseCurtain() {}
private:
/// Process command line arguments from StandardMainLoop. This is done to
/// allow web plugin functionality, where we are passed platform-specific
/// information allowing us to set ourselves up in the web browser,
/// to occur in a platform-neutral way.
virtual void _processCmdLineArgs(const S32 argc, const char **argv) {}
};
#endif // _DEDICATED_WINDOW_STUB_H_
#endif // TORQUE_DEDICATED

View file

@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _MACCURSORCONTROLLER_H_
#define _MACCURSORCONTROLLER_H_
#include "windowManager/platformCursorController.h"
class MacCursorController : public PlatformCursorController
{
public:
MacCursorController(MacWindow* owner)
: PlatformCursorController( ( PlatformWindow* ) owner )
{
pushCursor(PlatformCursorController::curArrow);
}
virtual void setCursorPosition(S32 x, S32 y);
virtual void getCursorPosition(Point2I &point);
virtual void setCursorVisible(bool visible);
virtual bool isCursorVisible();
virtual void setCursorShape(U32 cursorID);
virtual void setCursorShape( const UTF8 *fileName, bool reload );
virtual U32 getDoubleClickTime();
virtual S32 getDoubleClickWidth();
virtual S32 getDoubleClickHeight();
};
#endif

View file

@ -0,0 +1,166 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <Carbon/Carbon.h>
#include <Cocoa/Cocoa.h>
#include "windowManager/mac/macWindow.h"
#include "windowManager/mac/macCursorController.h"
void MacCursorController::setCursorPosition(S32 x, S32 y)
{
MacWindow* macWindow = dynamic_cast<MacWindow*>(mOwner);
if(!macWindow || !macWindow->isVisible())
return;
CGPoint pt = { x, y };
CGWarpMouseCursorPosition(pt);
macWindow->_skipAnotherMouseEvent();
}
void MacCursorController::getCursorPosition( Point2I &point )
{
NSPoint pos = [NSEvent mouseLocation];
point.x = pos.x;
point.y = pos.y;
//what does this do?? comment??
MacWindow* macWindow = static_cast<MacWindow*>(mOwner);
CGRect bounds = macWindow->getDisplayBounds();
CGRect mainbounds = macWindow->getMainDisplayBounds();
F32 offsetY = mainbounds.size.height - (bounds.size.height + bounds.origin.y);
point.y = bounds.size.height + offsetY - point.y;
}
void MacCursorController::setCursorVisible(bool visible)
{
visible ? [NSCursor unhide] : [NSCursor hide];
}
bool MacCursorController::isCursorVisible()
{
return CGCursorIsVisible();
}
// a repository of custom cursors.
@interface TorqueCursors : NSObject { }
+(NSCursor*)resizeAll;
+(NSCursor*)resizeNWSE;
+(NSCursor*)resizeNESW;
@end
@implementation TorqueCursors
+(NSCursor*)resizeAll
{
static NSCursor* cur = nil;
if(!cur)
cur = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"resizeAll"] hotSpot:NSMakePoint(8, 8)];
return cur;
}
+(NSCursor*)resizeNWSE
{
static NSCursor* cur = nil;
if(!cur)
cur = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"resizeNWSE"] hotSpot:NSMakePoint(8, 8)];
return cur;
}
+(NSCursor*)resizeNESW
{
static NSCursor* cur = nil;
if(!cur)
cur = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"resizeNESW"] hotSpot:NSMakePoint(8, 8)];
return cur;
}
@end
void MacCursorController::setCursorShape(U32 cursorID)
{
NSCursor *cur;
switch(cursorID)
{
case PlatformCursorController::curArrow:
[[NSCursor arrowCursor] set];
break;
case PlatformCursorController::curWait:
// hack: black-sheep carbon call
SetThemeCursor(kThemeWatchCursor);
break;
case PlatformCursorController::curPlus:
[[NSCursor crosshairCursor] set];
break;
case PlatformCursorController::curResizeVert:
[[NSCursor resizeLeftRightCursor] set];
break;
case PlatformCursorController::curIBeam:
[[NSCursor IBeamCursor] set];
break;
case PlatformCursorController::curResizeAll:
cur = [TorqueCursors resizeAll];
[cur set];
break;
case PlatformCursorController::curResizeNESW:
[[TorqueCursors resizeNESW] set];
break;
case PlatformCursorController::curResizeNWSE:
[[TorqueCursors resizeNWSE] set];
break;
case PlatformCursorController::curResizeHorz:
[[NSCursor resizeUpDownCursor] set];
break;
}
}
void MacCursorController::setCursorShape( const UTF8 *fileName, bool reload )
{
//TODO: this is untested code
NSString* strFileName = [ NSString stringWithUTF8String: fileName ];
// Load image file.
NSImage* image = [ NSImage alloc ];
if( [ image initWithContentsOfFile: strFileName ] == nil )
return;
// Allocate cursor.
NSCursor* cursor = [ NSCursor alloc ];
[ cursor initWithImage: image hotSpot: NSMakePoint( 0.5, 0.5 ) ];
}
U32 MacCursorController::getDoubleClickTime()
{
return GetDblTime() / 60.0f * 1000.0f;
}
S32 MacCursorController::getDoubleClickWidth()
{
// This is an arbitrary value.
return 10;
}
S32 MacCursorController::getDoubleClickHeight()
{
return getDoubleClickWidth();
}

View file

@ -0,0 +1,75 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _MACVIEW_H_
#define _MACVIEW_H_
#import <Cocoa/Cocoa.h>
#include "windowManager/mac/macWindow.h"
/// GGMacView handles displaying content and responding to user input.
@interface GGMacView : NSOpenGLView
{
MacWindow* mTorqueWindow;
U32 mLastMods;
bool mHandledAsCharEvent;
}
- (void)setTorqueWindow:(MacWindow*)theWindow;
- (MacWindow*)torqueWindow;
/// @name Inherited Mouse Input methods
/// @{
- (void)mouseDown:(NSEvent *)theEvent;
- (void)rightMouseDown:(NSEvent *)theEvent;
- (void)mouseDragged:(NSEvent *)theEvent;
- (void)rightMouseDragged:(NSEvent *)theEvent;
- (void)mouseUp:(NSEvent *)theEvent;
- (void)rightMouseUp:(NSEvent *)theEvent;
- (void)mouseMoved:(NSEvent *)theEvent;
- (void)scrollWheel:(NSEvent *)theEvent;
/// @}
/// @name Inherited Keyboard Input methods
/// @{
- (void)keyDown:(NSEvent *)theEvent;
- (void)keyUp:(NSEvent *)theEvent;
/// @}
/// @name Keyboard Input Common Code
/// @{
- (void)rawKeyUpDown:(NSEvent *)theEvent keyDown:(BOOL)isKeyDown;
/// @}
/// @name Mouse Input Common Code
/// @{
- (void)mouseUpDown:(NSEvent *)theEvent mouseDown:(BOOL)isMouseDown;
- (void)mouseMotion:(NSEvent *)theEvent;
/// @}
- (BOOL)acceptsFirstResponder;
- (BOOL)becomeFirstResponder;
- (BOOL)resignFirstResponder;
@end
#endif

View file

@ -0,0 +1,401 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "windowManager/mac/macView.h"
#include "platform/event.h"
#include "platform/platformInput.h"
#include "console/console.h"
#include "sim/actionMap.h"
#include "app/mainLoop.h"
// For left/right side definitions.
#include <IOKit/hidsystem/IOLLEvent.h>
#define WHEEL_DELTA ( 120 * 0.1 )
static bool smApplicationInactive = false;
extern InputModifiers convertModifierBits( const U32 in );
inline U32 NSModifiersToTorqueModifiers( NSUInteger mods )
{
U32 torqueMods = 0;
if( mods & NX_DEVICELSHIFTKEYMASK )
torqueMods |= IM_LSHIFT;
if( mods & NX_DEVICERSHIFTKEYMASK )
torqueMods |= IM_RSHIFT;
if( mods & NX_DEVICELALTKEYMASK )
torqueMods |= IM_LOPT;
if( mods & NX_DEVICERALTKEYMASK )
torqueMods |= IM_ROPT;
if( mods & NX_DEVICELCTLKEYMASK )
torqueMods |= IM_LCTRL;
if( mods & NX_DEVICERCTLKEYMASK )
torqueMods |= IM_RCTRL;
if( mods & NX_DEVICELCMDKEYMASK )
torqueMods |= IM_LALT;
if( mods & NX_DEVICERCMDKEYMASK )
torqueMods |= IM_RALT;
Input::setModifierKeys( convertModifierBits( torqueMods ) );
return torqueMods;
}
@implementation GGMacView
- (void)setTorqueWindow:(MacWindow*)theWindow
{
mTorqueWindow = theWindow;
mLastMods = 0;
}
- (MacWindow*)torqueWindow
{
return mTorqueWindow;
}
-(void)trackModState:(U32)torqueKey withMacMods:(U32)macMods event:(NSEvent*)theEvent
{
// track state:
// translate the torque key code to the torque flag that changed
// xor with existing mods for new mod state
// clear anything that the mac says both siblings are not down ( to help stay in sync, a little bit )
// ?set left sibling of anything that the mac says some sibling is down, but that we don't see as down?
U32 torqueMod = 0;
switch(torqueKey)
{
case KEY_LSHIFT: torqueMod = IM_LSHIFT; break;
case KEY_RSHIFT: torqueMod = IM_RSHIFT; break;
case KEY_LCONTROL: torqueMod = IM_LCTRL; break;
case KEY_RCONTROL: torqueMod = IM_RCTRL; break;
case KEY_MAC_LOPT: torqueMod = IM_LOPT; break;
case KEY_MAC_ROPT: torqueMod = IM_ROPT; break;
case KEY_LALT: torqueMod = IM_LALT; break;
case KEY_RALT: torqueMod = IM_RALT; break;
};
// flip the mod that we got an event for
U32 newMods = mLastMods ^ torqueMod;
// clear left and right if mac thinks both are up.
if( !(macMods & NSShiftKeyMask)) newMods &= ~IM_LSHIFT, newMods &= ~IM_RSHIFT;
if( !(macMods & NSControlKeyMask)) newMods &= ~IM_LCTRL, newMods &= ~IM_RCTRL;
if( !(macMods & NSAlternateKeyMask)) newMods &= ~IM_LOPT, newMods &= ~IM_ROPT;
if( !(macMods & NSCommandKeyMask)) newMods &= ~IM_LALT, newMods &= ~IM_RALT;
// Generate keyUp/Down event (allows us to use modifier keys for actions)
mLastMods = 0;
[self rawKeyUpDown:theEvent keyDown:(newMods & torqueMod)];
mLastMods = newMods;
Input::setModifierKeys( convertModifierBits( mLastMods ) );
}
- (Point2I)viewCoordsToTorqueCoords:(NSPoint)mousePoint
{
if(mTorqueWindow->isFullscreen())
{
CGRect bounds = mTorqueWindow->getDisplayBounds();
CGRect mainbounds = mTorqueWindow->getMainDisplayBounds();
F32 offsetY = mainbounds.size.height - (bounds.size.height + bounds.origin.y);
Point2I pt(mousePoint.x - bounds.origin.x, bounds.size.height + offsetY - mousePoint.y);
return pt;
}
return Point2I(mousePoint.x, mTorqueWindow->getClientExtent().y - mousePoint.y);
}
- (void)signalGainFocus
{
if(smApplicationInactive)
smApplicationInactive = false;
bool gainFocus = static_cast<MacWindowManager*>(WindowManager)->canWindowGainFocus(mTorqueWindow);
if(gainFocus)
mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), GainFocus);
}
#pragma mark -
#pragma mark Mouse Input
// We're funnelling all the standard cocoa event handlers to -mouseUpDown and -mouseMotion.
- (void)mouseDown:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:YES]; }
- (void)rightMouseDown:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:YES]; }
- (void)otherMouseDown:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:YES]; }
- (void)mouseUp:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:NO]; }
- (void)rightMouseUp:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:NO]; }
- (void)otherMouseUp:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:NO]; }
- (void)mouseDragged:(NSEvent *)theEvent { [self mouseMotion:theEvent]; }
- (void)rightMouseDragged:(NSEvent *)theEvent { [self mouseMotion:theEvent]; }
- (void)otherMouseDragged:(NSEvent *)theEvent { [self mouseMotion:theEvent]; }
- (void)mouseMoved:(NSEvent *)theEvent { [self mouseMotion:theEvent]; }
- (void)mouseUpDown:(NSEvent *)theEvent mouseDown:(BOOL)isMouseDown
{
Point2I eventLocation = [self viewCoordsToTorqueCoords: [theEvent locationInWindow]];
U16 buttonNumber = [theEvent buttonNumber];
U32 action = isMouseDown ? SI_MAKE : SI_BREAK;
// If the event location is negative then it occurred in the structure region (e.g. title bar, resize corner), and we don't want
// to lock the mouse/drop into fullscreen for that.
if(WindowManager->getFocusedWindow() != mTorqueWindow && eventLocation.x > 0 && eventLocation.y > 0)
[self signalGainFocus];
mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] );
mTorqueWindow->buttonEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, action, buttonNumber);
}
- (void)mouseMotion:(NSEvent *)theEvent
{
mTorqueWindow->_doMouseLockNow();
// when moving the mouse to the center of the window for mouse locking, we need
// to skip the next mouse delta event
if(mTorqueWindow->isMouseLocked() && mTorqueWindow->_skipNextMouseEvent())
{
mTorqueWindow->_skippedMouseEvent();
return;
}
Point2I eventLocation;
if(mTorqueWindow->isMouseLocked())
{
eventLocation.x = [theEvent deltaX];
eventLocation.y = [theEvent deltaY];
}
else
{
eventLocation = [self viewCoordsToTorqueCoords:[theEvent locationInWindow]];
}
mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] );
mTorqueWindow->mouseEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, eventLocation.x, eventLocation.y, mTorqueWindow->isMouseLocked());
}
- (void)scrollWheel:(NSEvent *)theEvent
{
float deltaX = [ theEvent deltaX ];
float deltaY = [ theEvent deltaY ];
if( mIsZero( deltaX ) && mIsZero( deltaY ) )
return;
mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] );
mTorqueWindow->wheelEvent.trigger( mTorqueWindow->getWindowId(), mLastMods, S32( deltaX * WHEEL_DELTA ), S32( deltaY * WHEEL_DELTA ) );
}
#pragma mark -
#pragma mark Keyboard Input
- (BOOL)performKeyEquivalent:(NSEvent *)theEvent
{
// Pass it off to the main menu. If the main menu handled it, we're done.
if([[NSApp mainMenu] performKeyEquivalent:theEvent])
return YES;
// cmd-q will quit. End of story.
if(([theEvent modifierFlags] & NSCommandKeyMask && !([theEvent modifierFlags] & NSAlternateKeyMask) && !([theEvent modifierFlags] & NSControlKeyMask)) && [theEvent keyCode] == 0x0C)
{
StandardMainLoop::shutdown();
[NSApp terminate:self];
return YES;
}
// In fullscreen we grab ALL keyboard events, including ones which would normally be handled by the system,
// like cmd-tab. Thus, we need to specifically check for cmd-tab and bail out of fullscreen and hide the app
// to switch to the next application.
// 0x30 is tab, so this grabs command-tab and command-shift-tab
if([theEvent keyCode] == 0x30 && mTorqueWindow->isFullscreen())
{
// Bail!
mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus);
[NSApp hide:nil];
return YES;
}
// All other events are uninteresting to us and can be handled by Torque.
if([theEvent type] == NSKeyDown)
[self keyDown:theEvent];
else if([theEvent type] == NSKeyUp)
[self keyUp:theEvent];
// Don't let the default handler continue processing these events, it does things we don't like.
return YES;
}
- (void)flagsChanged:(NSEvent *)theEvent
{
U32 torqueKeyCode = TranslateOSKeyCode([theEvent keyCode]);
U32 macMods = [theEvent modifierFlags];
[self trackModState:torqueKeyCode withMacMods:macMods event:theEvent];
}
- (void)keyDown:(NSEvent *)theEvent
{
// If keyboard translation is on and the key isn't bound in the
// global action map, first try turning this into one or more
// character events.
if( mTorqueWindow->getKeyboardTranslation()
&& !mTorqueWindow->shouldNotTranslate(
convertModifierBits( NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ) ),
( InputObjectInstances ) TranslateOSKeyCode( [ theEvent keyCode ] ) ) )
{
mHandledAsCharEvent = false;
[ self interpretKeyEvents: [ NSArray arrayWithObject: theEvent ] ];
if( mHandledAsCharEvent )
return;
}
// Fire as raw keyboard event.
[ self rawKeyUpDown: theEvent keyDown: YES ];
}
- (void)keyUp:(NSEvent *)theEvent
{
[self rawKeyUpDown:theEvent keyDown:NO];
}
- (void)rawKeyUpDown:(NSEvent *)theEvent keyDown:(BOOL)isKeyDown
{
U32 action;
if([theEvent type] != NSFlagsChanged)
action = isKeyDown ? ([theEvent isARepeat] ? SI_REPEAT : SI_MAKE) : SI_BREAK;
else
action = isKeyDown ? SI_MAKE : SI_BREAK;
U32 torqueKeyCode = TranslateOSKeyCode([theEvent keyCode]);
mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] );
mTorqueWindow->keyEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, action, torqueKeyCode);
}
- (void)insertText:(id)_inString
{
// input string may be an NSString or an NSAttributedString
NSString *text = [_inString isKindOfClass:[NSAttributedString class]] ? [_inString string] : _inString;
for(int i = 0; i < [text length]; i++)
{
mTorqueWindow->charEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, [text characterAtIndex:i]);
mHandledAsCharEvent = true;
}
}
#pragma mark -
#pragma mark Application Delegate
- (void)applicationDidResignActive:(NSNotification *)aNotification
{
smApplicationInactive = true;
mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus);
[NSApp setDelegate:nil];
}
- (void)applicationDidHide:(NSNotification *)aNotification
{
mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus);
}
- (void)applicationDidUnhide:(NSNotification *)aNotification
{
mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), GainFocus);
}
#ifndef TORQUE_SHARED
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
{
Platform::postQuitMessage(0);
return NSTerminateCancel;
}
#endif
#pragma mark -
#pragma mark Window Delegate
- (BOOL)windowShouldClose:(NSWindow *)sender
{
// We close the window ourselves
mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), WindowDestroy);
return NO;
}
- (void)windowWillClose:(NSNotification *) notification
{
mTorqueWindow->_disassociateCocoaWindow();
}
- (void)windowDidBecomeKey:(NSNotification *)notification
{
// when our window is the key window, we become the app delegate.
PlatformWindow* focusWindow = WindowManager->getFocusedWindow();
if(focusWindow && focusWindow != mTorqueWindow)
focusWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus);
[NSApp setDelegate:self];
[self signalGainFocus];
}
- (void)windowDidResignKey:(NSNotification*)notification
{
mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseScreen);
mTorqueWindow->_associateMouse();
mTorqueWindow->setCursorVisible(true);
[NSApp setDelegate:nil];
}
- (void)windowDidChangeScreen:(NSNotification*)notification
{
NSWindow* wnd = [notification object];
// TODO: Add a category to NSScreen to deal with this
CGDirectDisplayID disp = (CGDirectDisplayID)[[[[wnd screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
mTorqueWindow->setDisplay(disp);
}
- (void)windowDidResize:(NSNotification*)notification
{
Point2I clientExtent = mTorqueWindow->getClientExtent();
mTorqueWindow->resizeEvent.trigger(mTorqueWindow->getWindowId(), clientExtent.x, clientExtent.y);
}
#pragma mark -
#pragma mark responder status
- (BOOL)acceptsFirstResponder { return YES; }
- (BOOL)becomeFirstResponder { return YES; }
- (BOOL)resignFirstResponder { return YES; }
// Basic implementation because NSResponder's default implementation can cause infinite loops when our keyDown: method calls interpretKeyEvents:
- (void)doCommandBySelector:(SEL)aSelector
{
if([self respondsToSelector:aSelector])
[self performSelector:aSelector withObject:nil];
}
@end

View file

@ -0,0 +1,191 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TORQUE_MACWINDOW_H_
#define _TORQUE_MACWINDOW_H_
#include "windowManager/platformWindow.h"
#include "windowManager/mac/macWindowManager.h"
#include "windowManager/mac/macCursorController.h"
#ifndef _GFXTARGET_H_
#include "gfx/gfxTarget.h"
#endif
#ifndef _GFXSTRUCTS_H_
#include "gfx/gfxStructs.h"
#endif
class MacWindow : public PlatformWindow
{
public:
virtual ~MacWindow();
virtual GFXDevice *getGFXDevice() { return mDevice; }
virtual GFXWindowTarget *getGFXTarget() { return mTarget; }
virtual void setVideoMode(const GFXVideoMode &mode);
virtual const GFXVideoMode &getVideoMode() { return mCurrentMode; }
virtual WindowId getWindowId() { return mWindowId; }
void setDisplay(CGDirectDisplayID display);
CGDirectDisplayID getDisplay() { return mDisplay; }
CGRect getMainDisplayBounds() { return mMainDisplayBounds; }
CGRect getDisplayBounds() { return mDisplayBounds; }
virtual bool clearFullscreen()
{
// TODO: properly drop out of full screen
return true;
}
virtual bool isFullscreen() { return mFullscreen; }
virtual PlatformWindow * getNextWindow() const;
virtual void setMouseLocked( bool enable )
{
mShouldMouseLock = enable;
if(isFocused())
_doMouseLockNow();
}
virtual bool isMouseLocked() const { return mMouseLocked; }
virtual bool shouldLockMouse() const { return mShouldMouseLock; }
virtual bool setSize(const Point2I &newSize);
virtual void setClientExtent( const Point2I newExtent );
virtual const Point2I getClientExtent();
virtual void setBounds( const RectI &newBounds );
virtual const RectI getBounds() const;
virtual void setPosition( const Point2I newPosition );
virtual const Point2I getPosition();
virtual void centerWindow();
virtual Point2I clientToScreen( const Point2I& pos );
virtual Point2I screenToClient( const Point2I& pos );
virtual bool setCaption(const char *windowText);
virtual const char *getCaption() { return mTitle; }
virtual bool setType( S32 windowType ) { return true; }
virtual void minimize();
virtual void maximize();
virtual void restore();
virtual bool isMinimized();
virtual bool isMaximized();
virtual void show();
virtual void close();
virtual void hide();
virtual bool isOpen();
virtual bool isVisible();
virtual bool isFocused();
virtual void setFocus();
virtual void clearFocus();
virtual void* getPlatformDrawable() const;
// TODO: These should be private, but GGMacView (an Obj-C class) needs access to these and we can't friend Obj-C classes
bool _skipNextMouseEvent() { return mSkipMouseEvents != 0; }
void _skipAnotherMouseEvent() { mSkipMouseEvents++; }
void _skippedMouseEvent() { mSkipMouseEvents--; }
/// Does the work of actually locking or unlocking the mouse, based on the
/// value of shouldLockMouse().
///
/// Disassociates the cursor movement from the mouse input and hides the mouse
/// when locking. Re-associates cursor movement with mouse input and shows the
/// mouse when unlocking.
///
/// Returns true if we locked or unlocked the mouse. Returns false if the mouse
/// was already in the correct state.
void _doMouseLockNow();
// Helper methods for doMouseLockNow
void _associateMouse();
void _dissociateMouse();
void _centerMouse();
// For GGMacView
void _disassociateCocoaWindow();
// Safari support methods
static void setSafariWindow(NSWindow *window, S32 x = 0, S32 y = 0, S32 width = 0, S32 height = 0);
static void hideBrowserWindow(bool hide);
protected:
virtual void _setFullscreen(bool fullScreen);
private:
friend class MacWindowManager;
friend class MacCursorController;
struct SafariWindowInfo
{
NSWindow* safariWindow; /* The Safari Browser Window */
S32 x; /* Position of top left corner relative */
S32 y; /* to a safari page. */
U32 width; /* Maximum window size */
U32 height;
};
MacWindow(U32 windowId, const char* windowText, Point2I clientExtent);
void _initCocoaWindow(const char* windowText, Point2I clientExtent);
void setWindowId(U32 newid) { mWindowId = newid;}
void signalGainFocus();
static SafariWindowInfo* sSafariWindowInfo;
static MacWindow* sInstance;
NSWindow* mCocoaWindow;
GFXDevice *mDevice;
GFXWindowTargetRef mTarget;
GFXVideoMode mCurrentMode;
MacWindow *mNextWindow;
bool mMouseLocked;
bool mShouldMouseLock;
const char* mTitle;
bool mMouseCaptured;
MacWindowManager* mOwningWindowManager;
U32 mSkipMouseEvents;
bool mFullscreen;
bool mShouldFullscreen;
NSDictionary* mDefaultDisplayMode;
void _onAppEvent(WindowId,S32);
CGDirectDisplayID mDisplay;
CGRect mDisplayBounds;
CGRect mMainDisplayBounds;
};
#endif

View file

@ -0,0 +1,594 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <Cocoa/Cocoa.h>
#include "windowManager/mac/macWindow.h"
#include "windowManager/mac/macView.h"
#include "console/console.h"
MacWindow::SafariWindowInfo* MacWindow::sSafariWindowInfo = NULL;
MacWindow* MacWindow::sInstance = NULL;
@interface SafariBrowserWindow : NSWindow
{
}
@end
@implementation SafariBrowserWindow
// Windows created with NSBorderlessWindowMask normally can't be key, but we want ours to be
- (BOOL) canBecomeKeyWindow
{
return YES;
}
@end
MacWindow::MacWindow(U32 windowId, const char* windowText, Point2I clientExtent)
{
mMouseLocked = false;
mShouldMouseLock = false;
mTitle = NULL;
mMouseCaptured = false;
mCocoaWindow = NULL;
mCursorController = new MacCursorController( this );
mOwningWindowManager = NULL;
mFullscreen = false;
mShouldFullscreen = false;
mDefaultDisplayMode = NULL;
mSkipMouseEvents = 0;
mDisplay = kCGDirectMainDisplay;
mMainDisplayBounds = mDisplayBounds = CGDisplayBounds(mDisplay);
mWindowId = windowId;
_initCocoaWindow(windowText, clientExtent);
appEvent.notify(this, &MacWindow::_onAppEvent);
sInstance = this;
}
MacWindow::~MacWindow()
{
if(mFullscreen)
_setFullscreen(false);
appEvent.remove(this, &MacWindow::_onAppEvent);
//ensure our view isn't the delegate
[NSApp setDelegate:nil];
if( mCocoaWindow )
{
NSWindow* window = mCocoaWindow;
_disassociateCocoaWindow();
[ window close ];
}
appEvent.trigger(mWindowId, LoseFocus);
appEvent.trigger(mWindowId, WindowDestroy);
mOwningWindowManager->_removeWindow(this);
setSafariWindow(NULL);
sInstance = NULL;
}
extern "C"
{
void torque_setsafariwindow( NSWindow *window, S32 x, S32 y, S32 width, S32 height)
{
MacWindow::setSafariWindow(window, x, y, width, height);
}
}
void MacWindow::hideBrowserWindow(bool hide)
{
if (sSafariWindowInfo && sInstance && sInstance->mCocoaWindow)
{
if (hide)
{
if (sSafariWindowInfo && sSafariWindowInfo->safariWindow)
[sSafariWindowInfo->safariWindow removeChildWindow: sInstance->mCocoaWindow];
sInstance->hide();
}
else
{
if (sSafariWindowInfo && sSafariWindowInfo->safariWindow)
[sSafariWindowInfo->safariWindow addChildWindow: sInstance->mCocoaWindow ordered:NSWindowAbove];
sInstance->show();
}
}
}
void MacWindow::setSafariWindow(NSWindow *window, S32 x, S32 y, S32 width, S32 height )
{
if (!window)
{
hideBrowserWindow(true);
if (sSafariWindowInfo)
delete sSafariWindowInfo;
sSafariWindowInfo = NULL;
return;
}
if (!sSafariWindowInfo)
{
sSafariWindowInfo = new SafariWindowInfo;
sSafariWindowInfo->safariWindow = window;
sSafariWindowInfo->width = width;
sSafariWindowInfo->height = height;
sSafariWindowInfo->x = x;
sSafariWindowInfo->y = y;
if (sInstance && sInstance->mCocoaWindow)
{
[window addChildWindow: sInstance->mCocoaWindow ordered:NSWindowAbove];
hideBrowserWindow(false);
}
}
else
{
sSafariWindowInfo->width = width;
sSafariWindowInfo->height = height;
sSafariWindowInfo->x = x;
sSafariWindowInfo->y = y;
}
if (sInstance && sInstance->mCocoaWindow)
{
//update position
NSRect frame = [sSafariWindowInfo->safariWindow frame];
NSPoint o = { (float)sSafariWindowInfo->x, frame.size.height - sSafariWindowInfo->y };
NSPoint p = [sSafariWindowInfo->safariWindow convertBaseToScreen: o];
NSRect contentRect = NSMakeRect(p.x, p.y - sSafariWindowInfo->height, sSafariWindowInfo->width,sSafariWindowInfo->height);
// we have to set display to NO when resizing otherwise get hangs, perhaps add delegate to SafariBrowserWindow class?
[sInstance->mCocoaWindow setFrame:contentRect display:NO];
}
}
void MacWindow::_initCocoaWindow(const char* windowText, Point2I clientExtent)
{
// TODO: cascade windows on screen?
// create the window
NSRect contentRect;
U32 style;
if (sSafariWindowInfo)
{
NSRect frame = [sSafariWindowInfo->safariWindow frame];
NSPoint o = { (float)sSafariWindowInfo->x, frame.size.height - sSafariWindowInfo->y };
NSPoint p = [sSafariWindowInfo->safariWindow convertBaseToScreen: o];
contentRect = NSMakeRect(0, 0, sSafariWindowInfo->width,sSafariWindowInfo->height);
style = NSBorderlessWindowMask;
mCocoaWindow = [[SafariBrowserWindow alloc] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:YES screen:nil];
[mCocoaWindow setFrameTopLeftPoint: p];
[sSafariWindowInfo->safariWindow addChildWindow: mCocoaWindow ordered:NSWindowAbove];
// necessary to accept mouseMoved events
[mCocoaWindow setAcceptsMouseMovedEvents:YES];
}
else
{
contentRect = NSMakeRect(0,0,clientExtent.x, clientExtent.y);
style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask;
mCocoaWindow = [[NSWindow alloc] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:YES screen:nil];
if(windowText)
[mCocoaWindow setTitle: [NSString stringWithUTF8String: windowText]];
// necessary to accept mouseMoved events
[mCocoaWindow setAcceptsMouseMovedEvents:YES];
// correctly position the window on screen
[mCocoaWindow center];
}
// create the opengl view. we don't care about its pixel format, because we
// will be replacing its context with another one.
GGMacView* view = [[GGMacView alloc] initWithFrame:contentRect pixelFormat:[NSOpenGLView defaultPixelFormat]];
[view setTorqueWindow:this];
[mCocoaWindow setContentView:view];
[mCocoaWindow setDelegate:view];
}
void MacWindow::_disassociateCocoaWindow()
{
if( !mCocoaWindow )
return;
[mCocoaWindow setContentView:nil];
[mCocoaWindow setDelegate:nil];
if (sSafariWindowInfo)
[sSafariWindowInfo->safariWindow removeChildWindow: mCocoaWindow];
mCocoaWindow = NULL;
}
void MacWindow::setVideoMode(const GFXVideoMode &mode)
{
mCurrentMode = mode;
setSize(mCurrentMode.resolution);
if(mTarget.isValid())
mTarget->resetMode();
_setFullscreen(mCurrentMode.fullScreen);
}
void MacWindow::_onAppEvent(WindowId, S32 evt)
{
if(evt == LoseFocus && isFullscreen())
{
mShouldFullscreen = true;
GFXVideoMode mode = mCurrentMode;
mode.fullScreen = false;
setVideoMode(mode);
}
if(evt == GainFocus && !isFullscreen() && mShouldFullscreen)
{
mShouldFullscreen = false;
GFXVideoMode mode = mCurrentMode;
mode.fullScreen = true;
setVideoMode(mode);
}
}
void MacWindow::_setFullscreen(bool fullScreen)
{
if(mFullscreen == fullScreen)
return;
mFullscreen = fullScreen;
if(mFullscreen)
{
Con::printf("Capturing display %x", mDisplay);
CGDisplayCapture(mDisplay);
[mCocoaWindow setAlphaValue:0.0f];
}
else
{
if(mDefaultDisplayMode)
{
Con::printf("Restoring default display mode... width: %i height: %i bpp: %i", [[mDefaultDisplayMode valueForKey:@"Width"] intValue],
[[mDefaultDisplayMode valueForKey:@"Height"] intValue], [[mDefaultDisplayMode valueForKey:@"BitsPerPixel"] intValue]);
CGDisplaySwitchToMode(mDisplay, (CFDictionaryRef)mDefaultDisplayMode);
mDisplayBounds = CGDisplayBounds(mDisplay);
if(mDisplay == kCGDirectMainDisplay)
mMainDisplayBounds = mDisplayBounds;
}
Con::printf("Releasing display %x", mDisplay);
CGDisplayRelease(mDisplay);
[mCocoaWindow setAlphaValue:1.0f];
mDefaultDisplayMode = NULL;
}
}
void* MacWindow::getPlatformDrawable() const
{
return [mCocoaWindow contentView];
}
void MacWindow::show()
{
[mCocoaWindow makeKeyAndOrderFront:nil];
[mCocoaWindow makeFirstResponder:[mCocoaWindow contentView]];
appEvent.trigger(getWindowId(), WindowShown);
appEvent.trigger(getWindowId(), GainFocus);
}
void MacWindow::close()
{
[mCocoaWindow close];
appEvent.trigger(mWindowId, LoseFocus);
appEvent.trigger(mWindowId, WindowDestroy);
mOwningWindowManager->_removeWindow(this);
delete this;
}
void MacWindow::hide()
{
[mCocoaWindow orderOut:nil];
appEvent.trigger(getWindowId(), WindowHidden);
}
void MacWindow::setDisplay(CGDirectDisplayID display)
{
mDisplay = display;
mDisplayBounds = CGDisplayBounds(mDisplay);
}
PlatformWindow* MacWindow::getNextWindow() const
{
return mNextWindow;
}
bool MacWindow::setSize(const Point2I &newSize)
{
if (sSafariWindowInfo)
return true;
NSSize newExtent = {newSize.x, newSize.y};
[mCocoaWindow setContentSize:newExtent];
[mCocoaWindow center];
return true;
}
void MacWindow::setClientExtent( const Point2I newExtent )
{
if(!mFullscreen)
{
// Set the Client Area Extent (Resolution) of this window
NSSize newSize = {newExtent.x, newExtent.y};
[mCocoaWindow setContentSize:newSize];
}
else
{
// In fullscreen we have to resize the monitor (it'll be good to change it back too...)
if(!mDefaultDisplayMode)
mDefaultDisplayMode = (NSDictionary*)CGDisplayCurrentMode(mDisplay);
NSDictionary* newMode = (NSDictionary*)CGDisplayBestModeForParameters(mDisplay, 32, newExtent.x, newExtent.y, NULL);
Con::printf("Switching to new display mode... width: %i height: %i bpp: %i",
[[newMode valueForKey:@"Width"] intValue], [[newMode valueForKey:@"Height"] intValue], [[newMode valueForKey:@"BitsPerPixel"] intValue]);
CGDisplaySwitchToMode(mDisplay, (CFDictionaryRef)newMode);
mDisplayBounds = CGDisplayBounds(mDisplay);
if(mDisplay == kCGDirectMainDisplay)
mMainDisplayBounds = mDisplayBounds;
}
}
const Point2I MacWindow::getClientExtent()
{
if(!mFullscreen)
{
// Get the Client Area Extent (Resolution) of this window
NSSize size = [[mCocoaWindow contentView] frame].size;
return Point2I(size.width, size.height);
}
else
{
return Point2I(mDisplayBounds.size.width, mDisplayBounds.size.height);
}
}
void MacWindow::setBounds( const RectI &newBounds )
{
NSRect newFrame = NSMakeRect(newBounds.point.x, newBounds.point.y, newBounds.extent.x, newBounds.extent.y);
[mCocoaWindow setFrame:newFrame display:YES];
}
const RectI MacWindow::getBounds() const
{
if(!mFullscreen)
{
// Get the position and size (fullscreen windows are always at (0,0)).
NSRect frame = [mCocoaWindow frame];
return RectI(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
}
else
{
return RectI(0, 0, mDisplayBounds.size.width, mDisplayBounds.size.height);
}
}
void MacWindow::setPosition( const Point2I newPosition )
{
NSScreen *screen = [mCocoaWindow screen];
NSRect screenFrame = [screen frame];
NSPoint pos = {newPosition.x, newPosition.y + screenFrame.size.height};
[mCocoaWindow setFrameTopLeftPoint: pos];
}
const Point2I MacWindow::getPosition()
{
NSScreen *screen = [mCocoaWindow screen];
NSRect screenFrame = [screen frame];
NSRect frame = [mCocoaWindow frame];
return Point2I(frame.origin.x, screenFrame.size.height - (frame.origin.y + frame.size.height));
}
void MacWindow::centerWindow()
{
[mCocoaWindow center];
}
Point2I MacWindow::clientToScreen( const Point2I& pos )
{
NSPoint p = { pos.x, pos.y };
p = [ mCocoaWindow convertBaseToScreen: p ];
return Point2I( p.x, p.y );
}
Point2I MacWindow::screenToClient( const Point2I& pos )
{
NSPoint p = { pos.x, pos.y };
p = [ mCocoaWindow convertScreenToBase: p ];
return Point2I( p.x, p.y );
}
bool MacWindow::isFocused()
{
return [mCocoaWindow isKeyWindow];
}
bool MacWindow::isOpen()
{
// Maybe check if _window != NULL ?
return true;
}
bool MacWindow::isVisible()
{
return !isMinimized() && ([mCocoaWindow isVisible] == YES);
}
void MacWindow::setFocus()
{
[mCocoaWindow makeKeyAndOrderFront:nil];
}
void MacWindow::signalGainFocus()
{
if(isFocused())
[[mCocoaWindow delegate] performSelector:@selector(signalGainFocus)];
}
void MacWindow::minimize()
{
if(!isVisible())
return;
[mCocoaWindow miniaturize:nil];
appEvent.trigger(getWindowId(), WindowHidden);
}
void MacWindow::maximize()
{
if(!isVisible())
return;
// GFX2_RENDER_MERGE
//[mCocoaWindow miniaturize:nil];
//appEvent.trigger(getWindowId(), WindowHidden);
}
void MacWindow::restore()
{
if(!isMinimized())
return;
[mCocoaWindow deminiaturize:nil];
appEvent.trigger(getWindowId(), WindowShown);
}
bool MacWindow::isMinimized()
{
return [mCocoaWindow isMiniaturized] == YES;
}
bool MacWindow::isMaximized()
{
return false;
}
void MacWindow::clearFocus()
{
// Clear the focus state for this Window.
// If the Window does not have focus, nothing happens.
// If the Window has focus, it relinquishes it's focus to the Operating System
// TODO: find out if we can do anything correct here. We are instructed *not* to call [NSWindow resignKeyWindow], and we don't necessarily have another window to assign as key.
}
bool MacWindow::setCaption(const char* windowText)
{
mTitle = windowText;
[mCocoaWindow setTitle:[NSString stringWithUTF8String:mTitle]];
return true;
}
void MacWindow::_doMouseLockNow()
{
if(!isVisible())
return;
if(mShouldMouseLock == mMouseLocked && mMouseLocked != isCursorVisible())
return;
if(mShouldMouseLock)
_dissociateMouse();
else
_associateMouse();
// hide the cursor if we're locking, show it if we're unlocking
setCursorVisible(!shouldLockMouse());
mMouseLocked = mShouldMouseLock;
return;
}
void MacWindow::_associateMouse()
{
CGAssociateMouseAndMouseCursorPosition(true);
}
void MacWindow::_dissociateMouse()
{
_centerMouse();
CGAssociateMouseAndMouseCursorPosition(false);
}
void MacWindow::_centerMouse()
{
NSRect frame = [mCocoaWindow frame];
// Deal with the y flip (really fun when more than one monitor is involved)
F32 offsetY = mMainDisplayBounds.size.height - mDisplayBounds.size.height;
frame.origin.y = (mDisplayBounds.size.height + offsetY) - (S32)frame.origin.y - (S32)frame.size.height;
mCursorController->setCursorPosition(frame.origin.x + frame.size.width / 2, frame.origin.y + frame.size.height / 2);
}

View file

@ -0,0 +1,129 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _MACWINDOWMANAGER_H_
#define _MACWINDOWMANAGER_H_
#include "windowManager/platformWindowMgr.h"
#include "core/util/tVector.h"
class MacWindow;
class MacWindowManager : public PlatformWindowManager
{
private:
typedef VectorPtr<MacWindow*> WindowList;
WindowList mWindowList;
U32 mFadeToken;
Delegate<bool(void)> mNotifyShutdownDelegate;
public:
MacWindowManager();
~MacWindowManager();
virtual void setParentWindow(void* newParent) {
}
/// Get the parent window
virtual void* getParentWindow()
{
return NULL;
}
virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode);
/// @name Desktop Queries
/// @{
/// Return the extents in window coordinates of the primary desktop
/// area. On a single monitor system this is just the display extents.
/// On a multi-monitor system this is the primary monitor (which Torque should
/// launch on).
virtual RectI getPrimaryDesktopArea();
/// Populate a vector with all monitors and their extents in window space.
virtual void getMonitorRegions(Vector<RectI> &regions);
/// Retrieve the currently set desktop bit depth
/// @return The current desktop bit depth, or -1 if an error occurred
virtual S32 getDesktopBitDepth();
/// Retrieve the currently set desktop resolution
/// @return The current desktop bit depth, or Point2I(-1,-1) if an error occurred
virtual Point2I getDesktopResolution();
/// @}
/// @name Window Lookup
/// @{
/// Get the number of Window's in this system
virtual S32 getWindowCount();
/// Populate a list with references to all the windows created from this manager.
virtual void getWindows(VectorPtr<PlatformWindow*> &windows);
/// Get a window from a device ID.
///
/// @return The window associated with the specified ID, or NULL if no
/// match was found.
virtual PlatformWindow *getWindowById(WindowId id);
virtual PlatformWindow *getFirstWindow();
virtual PlatformWindow* getFocusedWindow();
/// During full-screen toggles we want to suppress ugly transition states,
/// which we do (on Win32) by showing and hiding a full-monitor black window.
///
/// This method cues the appearance of that window ("lowering the curtain").
virtual void lowerCurtain();
/// @see lowerCurtain
///
/// This method removes the curtain window.
virtual void raiseCurtain();
/// @}
/// @name Command Line Usage
/// @{
/// Process command line arguments sent to this window manager
/// to manipulate it's windows.
virtual void _processCmdLineArgs(const S32 argc, const char **argv);
/// @}
// static MacWindowManager* get() { return (MacWindowManager*)PlatformWindowManager::get(); }
void _addWindow(MacWindow* window);
void _removeWindow(MacWindow* window);
void _onAppSignal(WindowId wnd, S32 event);
bool onShutdown();
bool canWindowGainFocus(MacWindow* window);
private:
bool mIsShuttingDown;
};
#endif

View file

@ -0,0 +1,245 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <Cocoa/Cocoa.h>
#include "windowManager/mac/macWindowManager.h"
#include "windowManager/mac/macWindow.h"
#include "core/util/journal/process.h"
#include "console/console.h"
#include "gfx/gfxDevice.h"
PlatformWindowManager* CreatePlatformWindowManager()
{
return new MacWindowManager();
}
static inline RectI convertCGRectToRectI(NSRect r)
{
return RectI(r.origin.x, r.origin.y, r.size.width, r.size.height);
}
MacWindowManager::MacWindowManager() : mNotifyShutdownDelegate(this, &MacWindowManager::onShutdown), mIsShuttingDown(false)
{
mWindowList.clear();
Process::notifyShutdown(mNotifyShutdownDelegate);
}
MacWindowManager::~MacWindowManager()
{
for(U32 i = 0; i < mWindowList.size(); i++)
delete mWindowList[i];
mWindowList.clear();
CGReleaseDisplayFadeReservation(mFadeToken);
}
RectI MacWindowManager::getPrimaryDesktopArea()
{
// Get the area of the main desktop that isn't taken by the dock or menu bar.
return convertCGRectToRectI([[NSScreen mainScreen] visibleFrame]);
}
void MacWindowManager::getMonitorRegions(Vector<RectI> &regions)
{
// Populate a vector with all monitors and their extents in window space.
NSArray *screenList = [NSScreen screens];
for(U32 i = 0; i < [screenList count]; i++)
{
NSRect screenBounds = [[screenList objectAtIndex: i] frame];
regions.push_back(convertCGRectToRectI(screenBounds));
}
}
S32 MacWindowManager::getDesktopBitDepth()
{
// get the current desktop bit depth
// TODO: return -1 if an error occurred
return NSBitsPerPixelFromDepth([[NSScreen mainScreen] depth]);
}
Point2I MacWindowManager::getDesktopResolution()
{
// get the current desktop width/height
// TODO: return Point2I(-1,-1) if an error occurred
NSRect desktopBounds = [[NSScreen mainScreen] frame];
return Point2I((U32)desktopBounds.size.width, (U32)desktopBounds.size.height);
}
S32 MacWindowManager::getWindowCount()
{
// Get the number of PlatformWindow's in this manager
return mWindowList.size();
}
void MacWindowManager::getWindows(VectorPtr<PlatformWindow*> &windows)
{
// Populate a list with references to all the windows created from this manager.
windows.merge(mWindowList);
}
PlatformWindow * MacWindowManager::getFirstWindow()
{
if (mWindowList.size() > 0)
return mWindowList[0];
return NULL;
}
PlatformWindow* MacWindowManager::getFocusedWindow()
{
for (U32 i = 0; i < mWindowList.size(); i++)
{
if( mWindowList[i]->isFocused() )
return mWindowList[i];
}
return NULL;
}
PlatformWindow* MacWindowManager::getWindowById(WindowId zid)
{
// Find the window by its arbirary WindowId.
for(U32 i = 0; i < mWindowList.size(); i++)
{
PlatformWindow* w = mWindowList[i];
if( w->getWindowId() == zid)
return w;
}
return NULL;
}
void MacWindowManager::lowerCurtain()
{
// fade all displays.
CGError err;
err = CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &mFadeToken);
AssertWarn(!err, "MacWindowManager::lowerCurtain() could not get a token");
if(err) return;
err = CGDisplayFade(mFadeToken, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, true);
AssertWarn(!err, "MacWindowManager::lowerCurtain() failed the fade");
if(err) return;
// we do not release the token, because that will un-fade the screen!
// the token will last for 15 sec, and then the screen will un-fade regardless.
//CGReleaseDisplayFadeReservation(mFadeToken);
}
void MacWindowManager::raiseCurtain()
{
// release the fade on all displays
CGError err;
err = CGDisplayFade(mFadeToken, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, false);
AssertWarn(!err, "MacWindowManager::raiseCurtain() failed the fade");
err = CGReleaseDisplayFadeReservation(mFadeToken);
AssertWarn(!err, "MacWindowManager::raiseCurtain() failed releasing the token");
}
void MacWindowManager::_processCmdLineArgs(const S32 argc, const char **argv)
{
// TODO: accept command line args if necessary.
}
PlatformWindow *MacWindowManager::createWindow(GFXDevice *device, const GFXVideoMode &mode)
{
MacWindow* window = new MacWindow(getNextId(), getEngineProductString(), mode.resolution);
_addWindow(window);
// Set the video mode on the window
window->setVideoMode(mode);
// Make sure our window is shown and drawn to.
window->show();
// Bind the window to the specified device.
if(device)
{
window->mDevice = device;
window->mTarget = device->allocWindowTarget(window);
AssertISV(window->mTarget,
"MacWindowManager::createWindow - failed to get a window target back from the device.");
}
else
{
Con::warnf("MacWindowManager::createWindow - created a window with no device!");
}
return window;
}
void MacWindowManager::_addWindow(MacWindow* window)
{
#ifdef TORQUE_DEBUG
// Make sure we aren't adding the window twice
for(U32 i = 0; i < mWindowList.size(); i++)
AssertFatal(window != mWindowList[i], "MacWindowManager::_addWindow - Should not add a window more than once");
#endif
if (mWindowList.size() > 0)
window->mNextWindow = mWindowList.last();
else
window->mNextWindow = NULL;
mWindowList.push_back(window);
window->mOwningWindowManager = this;
window->appEvent.notify(this, &MacWindowManager::_onAppSignal);
}
void MacWindowManager::_removeWindow(MacWindow* window)
{
for(WindowList::iterator i = mWindowList.begin(); i != mWindowList.end(); i++)
{
if(*i == window)
{
mWindowList.erase(i);
return;
}
}
AssertFatal(false, avar("MacWindowManager::_removeWindow - Failed to remove window %x, perhaps it was already removed?", window));
}
void MacWindowManager::_onAppSignal(WindowId wnd, S32 event)
{
if(event != WindowHidden)
return;
for(U32 i = 0; i < mWindowList.size(); i++)
{
if(mWindowList[i]->getWindowId() == wnd)
continue;
mWindowList[i]->signalGainFocus();
}
}
bool MacWindowManager::onShutdown()
{
mIsShuttingDown = true;
return true;
}
bool MacWindowManager::canWindowGainFocus(MacWindow* window)
{
return !mIsShuttingDown;
}

View file

@ -0,0 +1,94 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "windowManager/platformCursorController.h"
void PlatformCursorController::pushCursor( S32 cursorID )
{
// Place the new cursor shape onto the stack
mCursors.increment();
CursorShape &shape = mCursors.last();
shape.mCursorType = CursorShape::TYPE_RESOURCE;
shape.mCursorID = cursorID;
// Now Change the Cursor Shape.
setCursorShape( shape.mCursorID );
}
void PlatformCursorController::pushCursor( const UTF8 *fileName )
{
// Place the new cursor shape onto the stack
mCursors.increment();
// Store the Details.
CursorShape &shape = mCursors.last();
shape.mCursorType = CursorShape::TYPE_FILE;
shape.mCursorFile = String::ToString( "%s", fileName );
// Now Change the Cursor Shape.
setCursorShape( shape.mCursorFile.c_str(), true );
}
void PlatformCursorController::popCursor()
{
// Before poping the stack, make sure we're not trying to remove the last cursor shape
if ( mCursors.size() <= 1 )
{
return;
}
// Clear the Last Cursor.
mCursors.pop_back();
// Now Change the Cursor Shape.
setCursorShape( mCursors.last(), true );
}
void PlatformCursorController::refreshCursor()
{
// Refresh the Cursor Shape.
setCursorShape( mCursors.last(), false );
}
void PlatformCursorController::setCursorShape( const CursorShape &shape, bool reload )
{
switch( shape.mCursorType )
{
case CursorShape::TYPE_RESOURCE :
{
// Set Resource.
setCursorShape( shape.mCursorID );
} break;
case CursorShape::TYPE_FILE :
{
// Set File.
setCursorShape( shape.mCursorFile.c_str(), reload );
} break;
}
}

View file

@ -0,0 +1,102 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _PLATFORMCURSORCONTROLLER_H_
#define _PLATFORMCURSORCONTROLLER_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class PlatformWindow;
class PlatformCursorController
{
protected:
struct CursorShape
{
enum Type
{
TYPE_RESOURCE,
TYPE_FILE,
};
Type mCursorType;
S32 mCursorID; // Points to a platform specific cursor ID
String mCursorFile; // Points to a custom cursor file
};
Vector<CursorShape> mCursors;
/// The PlatformWindow that owns this Cursor Controller
PlatformWindow *mOwner;
public:
PlatformCursorController( PlatformWindow *owner ) :
mOwner( owner )
{
VECTOR_SET_ASSOCIATION( mCursors );
};
virtual ~PlatformCursorController()
{
mOwner = NULL;
};
enum
{
curArrow = 0,
curWait,
curPlus,
curResizeVert,
curResizeHorz,
curResizeAll,
curIBeam,
curResizeNESW,
curResizeNWSE,
curHand,
};
public:
virtual void setCursorPosition(S32 x, S32 y) = 0;
virtual void getCursorPosition( Point2I &point ) = 0;
virtual void setCursorVisible( bool visible ) = 0;
virtual bool isCursorVisible() = 0;
virtual void setCursorShape( const CursorShape &shape, bool reload );
virtual void setCursorShape( U32 cursorID ) = 0;
virtual void setCursorShape( const UTF8 *filename, bool reload ) = 0;
virtual void pushCursor( S32 cursorID );
virtual void pushCursor( const UTF8 *fileName );
virtual void popCursor();
virtual void refreshCursor();
virtual U32 getDoubleClickTime() = 0;
virtual S32 getDoubleClickWidth() = 0;
virtual S32 getDoubleClickHeight() = 0;
};
#endif

View file

@ -0,0 +1,143 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/event.h"
#include "windowManager/platformWindowMgr.h"
#include "gfx/gfxInit.h"
#include "gfx/gfxDevice.h"
#include "core/util/journal/process.h"
#include "core/util/autoPtr.h"
// This file converts from the windowmanager system to the old platform and
// event interfaces. So it's a bit hackish but hopefully will not be necessary
// for very long. Because of the heavy use of globals in the old platform
// layer, this layer also has a fair number of globals.
// WindowManager
//
// PlatformWindowManager::get() wrapped in Macro WindowManager
static AutoPtr< PlatformWindowManager > smWindowManager;
PlatformWindowManager *PlatformWindowManager::get()
{
if( smWindowManager.isNull() )
smWindowManager = CreatePlatformWindowManager();
return smWindowManager.ptr();
}
void PlatformWindowManager::processCmdLineArgs( const S32 argc, const char **argv )
{
// Only call the get() routine if we have arguments on the command line
if(argc > 0)
{
PlatformWindowManager::get()->_processCmdLineArgs(argc, argv);
}
}
GFXDevice *gDevice = NULL;
PlatformWindow *gWindow = NULL;
// Conversion from window manager input conventions to Torque standard.
static struct ModifierBitMap {
U32 grendelMask,torqueMask;
} _ModifierBitMap[] = {
{ IM_LSHIFT, SI_LSHIFT },
{ IM_RSHIFT, SI_RSHIFT },
{ IM_LALT, SI_LALT },
{ IM_RALT, SI_RALT },
{ IM_LCTRL, SI_LCTRL },
{ IM_RCTRL, SI_RCTRL },
{ IM_LOPT, SI_MAC_LOPT },
{ IM_ROPT, SI_MAC_ROPT },
};
static int _ModifierBitMapCount = sizeof(_ModifierBitMap) / sizeof(ModifierBitMap);
InputModifiers convertModifierBits(const U32 in)
{
U32 out=0;
for(S32 i=0; i<_ModifierBitMapCount; i++)
if(in & _ModifierBitMap[i].grendelMask)
out |= _ModifierBitMap[i].torqueMask;
return (InputModifiers)out;
}
//------------------------------------------------------------------------------
void Platform::setWindowSize(U32 newWidth, U32 newHeight, bool fullScreen )
{
AssertISV(gWindow, "Platform::setWindowSize - no window present!");
// Grab the curent video settings and diddle them with the new values.
GFXVideoMode vm = gWindow->getVideoMode();
vm.resolution.set(newWidth, newHeight);
vm.fullScreen = fullScreen;
gWindow->setVideoMode(vm);
}
void Platform::setWindowLocked(bool locked)
{
PlatformWindow* window = WindowManager->getFirstWindow();
if( window )
window->setMouseLocked( locked );
}
void Platform::minimizeWindow()
{
// requires PlatformWindow API extension...
if(gWindow)
gWindow->minimize();
}
void Platform::closeWindow()
{
// Shutdown all our stuff.
//SAFE_DELETE(gDevice); // <-- device is already cleaned up elsewhere by now...
SAFE_DELETE(gWindow);
}
//------------------------------------------------------------------------------
#ifdef TORQUE_OS_WIN32
// Hack so we can get the HWND of the global window more easily - replacement
// for the HWND that was in the platstate.
#include "windowManager/win32/win32Window.h"
HWND getWin32WindowHandle()
{
PlatformWindow* window = WindowManager->getFocusedWindow();
if( !window )
{
window = WindowManager->getFirstWindow();
if( !window )
return NULL;
}
return ( ( Win32Window* ) window )->getHWND();
}
#endif

View file

@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "windowManager/platformWindow.h"
//-----------------------------------------------------------------------------
void PlatformWindow::setFullscreen( const bool fullscreen )
{
// Notify listeners that we will acquire the screen
if(fullscreen && !Journal::IsDispatching())
appEvent.trigger(getWindowId(),GainScreen);
// Do platform specific fullscreen code
_setFullscreen(fullscreen);
// Notify listeners that we released the screen
if(!fullscreen && !Journal::IsDispatching())
appEvent.trigger(getWindowId(),LoseScreen);
}
//-----------------------------------------------------------------------------
bool PlatformWindow::shouldNotTranslate( U32 modifiers, U32 keyCode ) const
{
if( mWindowInputGenerator )
return mWindowInputGenerator->wantAsKeyboardEvent( modifiers, keyCode );
else
return false;
}

View file

@ -0,0 +1,494 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINDOWMANAGER_PLATFORMWINDOW_H_
#define _WINDOWMANAGER_PLATFORMWINDOW_H_
#include "math/mRect.h"
#include "core/util/journal/journaledSignal.h"
#include "core/util/safeDelete.h"
#include "windowManager/platformCursorController.h"
#include "windowManager/windowInputGenerator.h"
//forward decl's
class PlatformWindowManager;
class GFXDevice;
struct GFXVideoMode;
class GFXWindowTarget;
class IProcessInput;
/// Abstract representation of a native OS window.
///
/// Every windowing system has its own representations and conventions as
/// regards the windows on-screen. In order to provide Torque with means for
/// interfacing with multiple windows, tracking their state, etc. we provide
/// this interface.
///
/// This interface also allows the app to access the render target for the
/// window it represents, as well as control mode switches, get mode info,
/// and so on.
///
/// @see PlatformWindowManager
class PlatformWindow
{
friend class PlatformWindowManager;
protected:
/// Are we enabling IME or other keyboard input translation services,
/// or concerned about raw input?
bool mEnableKeyboardTranslation;
/// When Torque GuiText input controls have focus they need to
/// disable native OS keyboard accelerator translation.
bool mEnableAccelerators;
/// Minimum allowed size for this window. When possible, we will communicate
/// this to the OS.
Point2I mMinimumSize;
/// When the resize is locked, this will be used as both minimum and maximum window size
Point2I mLockedSize;
/// When this is true, resizing is locked
bool mResizeLocked;
/// Is Idle?
bool mIsBackground;
/// Cursor Controller for this Window
PlatformCursorController *mCursorController;
/// An opaque ID used to resolve references to this Window
WindowId mWindowId;
/// Window Mouse/Key input Controller for this Window
WindowInputGenerator *mWindowInputGenerator;
/// Suppress device resets
bool mSuppressReset;
/// Offscreen Render
bool mOffscreenRender;
/// Protected constructor so that the win
PlatformWindow()
{
mIsBackground = false; // This could be toggled to true to prefer performance.
mMinimumSize.set(0,0);
mLockedSize.set(0,0);
mResizeLocked = false;
mEnableKeyboardTranslation = false;
mEnableAccelerators = true;
mCursorController = NULL;
// This controller maps window input (Mouse/Keyboard) to a generic input consumer
mWindowInputGenerator = new WindowInputGenerator( this );
mSuppressReset = false;
mOffscreenRender = false;
}
public:
/// To get rid of a window, just delete it. Make sure the GFXDevice is
/// done with it first!
virtual ~PlatformWindow()
{
SAFE_DELETE( mCursorController );
SAFE_DELETE( mWindowInputGenerator );
}
/// Get the WindowController associated with this window
virtual void setInputController( IProcessInput *controller ) { if( mWindowInputGenerator ) mWindowInputGenerator->setInputController( controller ); };
/// Get the ID that uniquely identifies this window in the context of its
/// window manager.
virtual WindowId getWindowId() { return 0; };
/// Set the flag that determines whether to suppress a GFXDevice reset
inline void setSuppressReset(bool suppress) { mSuppressReset = suppress; };
/// @name GFX State Management
///
/// @{
/// Return a pointer to the GFX device this window is bound to. A GFX
/// device may use many windows, but a window can only be used by a
/// single GFX device.
virtual GFXDevice *getGFXDevice()=0;
/// Return a pointer to this window's render target.
///
/// By setting window targets from different windows, we can effect
/// rendering to multiple windows from a single device.
virtual GFXWindowTarget *getGFXTarget()=0;
/// Set the video mode for this window.
virtual void setVideoMode(const GFXVideoMode &mode)=0;
/// Get our current video mode - if the window has been resized, it will
/// reflect this.
virtual const GFXVideoMode &getVideoMode()=0;
/// If we're fullscreen, this function returns us to desktop mode.
///
/// This will be either the last mode that we had that was not
/// fullscreen, or the equivalent mode, windowed.
virtual bool clearFullscreen()=0;
/// @return true if this window is fullscreen, false otherwise.
virtual bool isFullscreen()=0;
/// Acquire the entire screen
void setFullscreen(const bool fullscreen);
/// Set Idle State (Background)
///
/// This is called to put a window into idle state, which causes it's
/// rendering priority to be toned down to prefer performance
virtual void setBackground( bool val ) { mIsBackground = val; };
/// Get Idle State (Background)
///
/// This is called to poll the window as to it's idle state.
virtual bool getBackground() { return mIsBackground; };
/// Set whether this window is intended for offscreen rendering
/// An offscreen window will never be shown or lose focus
virtual void setOffscreenRender(bool val ) { mOffscreenRender = val; };
/// Set whether this window is intended for offscreen rendering
///
/// This is called to poll the window as to it's idle state.
virtual bool getOffscreenRender() { return mOffscreenRender; };
/// Set Focused State (Foreground)
///
/// Claim OS input focus for this window
virtual void setFocus() { }
/// @}
/// @name Caption
///
/// @{
/// Set the window's caption.
virtual bool setCaption(const char *cap)=0;
/// Get the window's caption.
virtual const char *getCaption()=0;
/// @}
/// @name Visibility
///
/// Control how the window is displayed
/// @{
/// Minimize the window on screen
virtual void minimize()=0;
/// Maximize the window on screen
virtual void maximize()=0;
/// Hide the window on screen
virtual void hide()=0;
/// Show the window on screen
virtual void show()=0;
/// Destroy the window on screen
virtual void close()=0;
/// Restore the window from a Maximized or Minimized state
virtual void restore()=0;
/// @}
/// @name Window Bounds
///
/// @{
/// The Client Rectangle or "Render Area" of a window is the area that
/// is occupied by a given client that is rendering to that window.
/// This does not include the area occupied by a title-bar, menu,
/// borders or other non-client elements.
/// @{
/// Set the Client Area Extent (Resolution) of this window
virtual void setClientExtent( const Point2I newExtent ) = 0;
/// Get the Client Area Extent (Resolution) of this window
virtual const Point2I getClientExtent() = 0;
/// @}
/// The bounds of a Window are defined as the entire area occupied by
/// that Window. This includes the area needed for a title-bar, menu,
/// borders, and other non-client elements.
///
/// @{
/// Resize the window to have some new bounds.
virtual void setBounds( const RectI &newBounds ) = 0;
/// Get the position and size (fullscreen windows are always at (0,0)).
virtual const RectI getBounds() const = 0;
/// @}
/// The Position of a window is always in relation to the very upper left
/// of the window. This means that saying setPosition at 0,0 will put the
/// position of the window title-bar (if one exists) at 0,0 and the Client
/// area will be offset from that point by the space needed for the Non-Client
/// area.
/// @{
/// Set the position of this window
virtual void setPosition( const Point2I newPosition ) = 0;
/// Get the position of this window
virtual const Point2I getPosition() = 0;
virtual void centerWindow() {};
/// Resize the window to have a new size (but be in the same position).
virtual bool setSize(const Point2I &newSize)=0;
/// @}
/// @name Coordinate Space Conversion
/// @{
/// Convert the coordinate given in this window space to screen coordinates.
virtual Point2I clientToScreen( const Point2I& point ) = 0;
/// Convert the given screen coordinates to coordinates in this window space.
virtual Point2I screenToClient( const Point2I& point ) = 0;
/// @}
/// @name Windowed state
///
/// This is only really meaningful if the window is not fullscreen.
///
/// @{
/// Returns true if the window is instantiated in the OS.
virtual bool isOpen() = 0;
/// Returns true if the window is visible.
virtual bool isVisible() = 0;
/// Returns true if the window has input focus
virtual bool isFocused() = 0;
/// Returns true if the window is minimized
virtual bool isMinimized() = 0;
/// Returns true if the window is maximized
virtual bool isMaximized() = 0;
/// @name Keyboard Translation
///
/// When keyboard translation is on, keypress events that correspond to character input
/// should be send as character input events rather than as raw key events *except* if
/// shouldNotTranslate() returns true for a specific keypress event. This enables the
/// platform layer to perform platform-specific character input mapping.
///
/// @{
/// Set if relevant keypress events should be translated into character input events.
virtual void setKeyboardTranslation(const bool enabled)
{
mEnableKeyboardTranslation = enabled;
}
/// Returns true if keyboard translation is enabled.
virtual bool getKeyboardTranslation() const
{
return mEnableKeyboardTranslation;
}
/// Returns true if the given keypress event should not be translated.
virtual bool shouldNotTranslate( U32 modifiers, U32 keyCode ) const;
/// @}
/// Used to disable native OS keyboard accelerators.
virtual void setAcceleratorsEnabled(const bool enabled)
{
mEnableAccelerators = enabled;
}
/// Returns true if native OS keyboard accelerators are enabled.
virtual bool getAcceleratorsEnabled() const
{
return mEnableAccelerators;
}
/// Sets a minimum window size. We'll work with the OS to prevent user
/// from sizing the window to less than this. Setting to (0,0) means
/// user has complete freedom of resize.
virtual void setMinimumWindowSize(Point2I minSize)
{
mMinimumSize = minSize;
}
/// Returns the current minimum window size for this window.
virtual Point2I getMinimumWindowSize()
{
return mMinimumSize;
}
/// Locks/unlocks window resizing
virtual void lockSize(bool locked)
{
mResizeLocked = locked;
if (mResizeLocked)
mLockedSize = getBounds().extent;
}
/// Returns true if the window size is locked
virtual bool isSizeLocked()
{
return mResizeLocked;
}
/// Returns the locked window size
virtual Point2I getLockedSize()
{
return mLockedSize;
}
/// @}
/// @name Window Cursor
///
/// Accessors to control a windows cursor shape and visibility
///
/// @{
/// Get the CursorController that this window owns.
virtual PlatformCursorController *getCursorController() { return mCursorController; };
/// Set the cursor position based on logical coordinates from the upper-right corner
///
/// @param x The X position of the cursor
/// @param y The Y position of the cursor
virtual void setCursorPosition(S32 x, S32 y)
{
if( mCursorController != NULL )
mCursorController->setCursorPosition(x,y);
}
/// Get the cursor position based on logical coordinates from the upper-right corner
///
/// @param point A reference to a Point2I to store the coordinates
virtual void getCursorPosition( Point2I &point )
{
if( mCursorController != NULL )
mCursorController->getCursorPosition(point);
}
/// Set the cursor visibility on this window
///
/// @param visible Whether the cursor should be visible or not
virtual void setCursorVisible(bool visible)
{
if( mCursorController != NULL )
mCursorController->setCursorVisible(visible);
}
/// Get the cursor visibility on this window
///
/// @return true if the cursor is visible or false if it's hidden
virtual bool isCursorVisible()
{
if( mCursorController != NULL )
return mCursorController->isCursorVisible();
return false;
}
/// Lock the mouse to this window.
///
/// When this is set, the mouse will always be returned to the center
/// of the client area after every mouse event. The mouse will also be
/// hidden while it is locked.
///
/// The mouse cannot be moved out of the bounds of the window, but the
/// window may lose focus (for instance by an alt-tab or other event).
/// While the window lacks focus, no mouse events will be reported.
virtual void setMouseLocked( bool enable )=0;
/// Is the mouse locked ?
virtual bool isMouseLocked() const = 0;
/// Should the mouse be locked at the next opportunity ?
///
/// This flag is set to the current state of the mouse lock
/// on a window, to specify the preferred lock status of the
/// mouse in a platform window.
///
/// This is important for situations where a call is made
/// to setMouseLocked, and the window is not in a state that
/// it can be cleanly locked. Take for example if it was called
/// while the window is in the background, then it is not appropriate
/// to lock the window, but rather the window should query this
/// state at it's next opportunity and lock the mouse if requested.
virtual bool shouldLockMouse() const = 0;
/// @}
virtual PlatformWindow * getNextWindow() const = 0;
/// @name Event Handlers
///
/// Various events that this window receives. These are all subclasses of
/// JournaledSignal, so you can subscribe to them and receive notifications
/// per the documentation for that class.
///
/// @{
///
AppEvent appEvent;
MouseEvent mouseEvent;
MouseWheelEvent wheelEvent;
ButtonEvent buttonEvent;
LinearEvent linearEvent;
KeyEvent keyEvent;
CharEvent charEvent;
DisplayEvent displayEvent;
ResizeEvent resizeEvent;
IdleEvent idleEvent;
/// @}
/// Get the platform specific object needed to create or attach an accelerated
/// graohics drawing context on or to the window
/// Win32 D3D and OpenGL typically needs an HWND
/// Mac Cocoa OpenGL typically needs an NSOpenGLView
/// Mac Carbon OpenGL typically needs a WindowRef
///
virtual void* getPlatformDrawable() const = 0;
protected:
virtual void _setFullscreen(const bool fullScreen) {};
};
#endif

View file

@ -0,0 +1,133 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _PLATFORM_PLATFORMWINDOWMGR_H_
#define _PLATFORM_PLATFORMWINDOWMGR_H_
#include "math/mRect.h"
#include "core/util/journal/journaledSignal.h"
#include "windowManager/platformWindow.h"
// Global macro
#define WindowManager PlatformWindowManager::get()
/// Abstract representation of a manager for native OS windows.
///
/// The PlatformWindowManager interface provides a variety of methods for querying
/// the current desktop configuration, as well as allocating and retrieving
/// existing windows. It may also manage application-level event handling.
class PlatformWindowManager
{
// Generator for window IDs.
S32 mIdSource;
protected:
/// Get the next available window Id
inline S32 getNextId() { return mIdSource++; }
public:
/// Get Global Singleton
static PlatformWindowManager *get();
PlatformWindowManager() : mIdSource(0) {};
virtual ~PlatformWindowManager()
{
}
static void processCmdLineArgs(const S32 argc, const char **argv);
/// Return the extents in window coordinates of the primary desktop
/// area. On a single monitor system this is just the display extents.
/// On a multimon system this is the primary monitor (which Torque should
/// launch on).
virtual RectI getPrimaryDesktopArea() = 0;
/// Retrieve the currently set desktop bit depth
/// @return The current desktop bit depth, or -1 if an error occurred
virtual S32 getDesktopBitDepth() = 0;
/// Retrieve the currently set desktop resolution
/// @return The current desktop bit depth, or Point2I(-1,-1) if an error occurred
virtual Point2I getDesktopResolution() = 0;
/// Populate a vector with all monitors and their extents in window space.
virtual void getMonitorRegions(Vector<RectI> &regions) = 0;
/// Create a new window, appropriate for the specified device and mode.
///
/// @return Pointer to the new window.
virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode) = 0;
/// Populate a list with references to all the windows created from this manager.
virtual void getWindows(VectorPtr<PlatformWindow*> &windows) = 0;
/// Get the window that currently has the input focus or NULL.
virtual PlatformWindow* getFocusedWindow() = 0;
/// Get a window from a device ID.
///
/// @return The window associated with the specified ID, or NULL if no
/// match was found.
virtual PlatformWindow *getWindowById(WindowId id)=0;
/// Get the first window in the window list
///
/// @return The first window in the list, or NULL if no windows found
virtual PlatformWindow *getFirstWindow()=0;
/// Set the parent window
///
/// This can be used to render in a child window.
virtual void setParentWindow(void* newParent) = 0;
/// Get the parent window
virtual void* getParentWindow() = 0;
/// This method cues the appearance of that window ("lowering the curtain").
virtual void lowerCurtain()=0;
/// @see lowerCurtain
///
/// This method removes the curtain window.
virtual void raiseCurtain()=0;
private:
/// Process command line arguments from StandardMainLoop. This is done to
/// allow web plugin functionality, where we are passed platform-specific
/// information allowing us to set ourselves up in the web browser,
/// to occur in a platform-neutral way.
virtual void _processCmdLineArgs(const S32 argc, const char **argv)=0;
};
/// Global function to allocate a new platform window manager.
///
/// This returns an instance of the appropriate window manager for the current OS.
///
/// Depending on situation (for instance, if we are a web plugin) we may
/// need to get the window manager from somewhere else.
PlatformWindowManager *CreatePlatformWindowManager();
#endif

View file

@ -0,0 +1,538 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "windowManager/platformWindowMgr.h"
#include "unit/test.h"
#include "core/util/tVector.h"
#include "gfx/gfxStructs.h"
#include "core/util/journal/process.h"
#include "gfx/gfxInit.h"
using namespace UnitTesting;
CreateUnitTest(TestWinMgrQueries, "WindowManager/BasicQueries")
{
void run()
{
PlatformWindowManager *pwm = CreatePlatformWindowManager();
// Check out the primary desktop area...
RectI primary = pwm->getPrimaryDesktopArea();
Con::printf("Primary desktop area is (%d,%d) (%d,%d)",
primary.point.x, primary.point.y, primary.extent.x, primary.extent.y);
test(primary.isValidRect(), "Got some sort of invalid rect from the window manager!");
// Now try to get info about all the monitors.
Vector<RectI> monitorRects;
pwm->getMonitorRegions(monitorRects);
test(monitorRects.size() > 0, "Should get at least one monitor rect back from getMonitorRegions!");
// This test is here just to detect overflow/runaway situations. -- BJG
test(monitorRects.size() < 64, "Either something's wrong, or you have a lot of monitors...");
for(S32 i=0; i<monitorRects.size(); i++)
{
Con::printf(" Monitor #%d region is (%d,%d) (%d,%d)", i,
monitorRects[i].point.x, monitorRects[i].point.y, monitorRects[i].extent.x, monitorRects[i].extent.y);
test(monitorRects[i].isValidRect(), "Got an invalid rect for this monitor - no good.");
}
}
};
CreateInteractiveTest(TestWinMgrCreate, "WindowManager/CreateAWindow")
{
void handleMouseEvent(WindowId,U32,S32 x,S32 y, bool isRelative)
{
Con::printf("Mouse at %d, %d %s", x, y, isRelative ? "(relative)" : "(absolute)");
}
void handleAppEvent(WindowId, S32 event)
{
if(event == WindowClose)
Process::requestShutdown();
}
void run()
{
PlatformWindowManager *pwm = CreatePlatformWindowManager();
GFXVideoMode vm;
vm.resolution.x = 800;
vm.resolution.y = 600;
PlatformWindow *pw = pwm->createWindow(NULL, vm);
test(pw, "Didn't get a window back from the window manager, no good.");
if(!pw)
return;
// Setup our events.
pw->mouseEvent.notify(this, &TestWinMgrCreate::handleMouseEvent);
pw->appEvent.notify(this, &TestWinMgrCreate::handleAppEvent);
// And, go on our way.
while(Process::processEvents())
;
SAFE_DELETE(pw);
}
};
CreateInteractiveTest(TestWinMgrGFXInit, "WindowManager/SimpleGFX")
{
PlatformWindow *mWindow;
GFXDevice *mDevice;
void handleDrawEvent(WindowId id)
{
mDevice->beginScene();
mDevice->setActiveRenderTarget(mWindow->getGFXTarget());
mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 255, 255, 0 ), 1.0f, 0 );
mDevice->endScene();
mWindow->getGFXTarget()->present();
}
void forceDraw()
{
handleDrawEvent(0);
}
void handleAppEvent(WindowId, S32 event)
{
if(event == WindowClose)
Process::requestShutdown();
}
void run()
{
PlatformWindowManager *pwm = CreatePlatformWindowManager();
// Create a device.
GFXAdapter a;
a.mType = Direct3D9;
a.mIndex = 0;
mDevice = GFXInit::createDevice(&a);
test(mDevice, "Unable to create d3d9 device #0.");
// Initialize the window...
GFXVideoMode vm;
vm.resolution.x = 400;
vm.resolution.y = 400;
mWindow = pwm->createWindow(mDevice, vm);
test(mWindow, "Didn't get a window back from the window manager, no good.");
if(!mWindow)
return;
// Setup our events.
mWindow->displayEvent.notify(this, &TestWinMgrGFXInit::handleDrawEvent);
mWindow->idleEvent.notify(this, &TestWinMgrGFXInit::forceDraw);
mWindow->appEvent.notify(this, &TestWinMgrGFXInit::handleAppEvent);
// And, go on our way.
while(Process::processEvents())
;
mWindow->displayEvent.remove(this, &TestWinMgrGFXInit::handleDrawEvent);
mWindow->idleEvent.remove(this, &TestWinMgrGFXInit::forceDraw);
mWindow->appEvent.remove(this, &TestWinMgrGFXInit::handleAppEvent);
// Clean up!
SAFE_DELETE(mDevice);
SAFE_DELETE(mWindow);
}
};
CreateInteractiveTest(TestWinMgrGFXInitMultiWindow, "WindowManager/GFXMultiWindow")
{
enum {
NumWindows = 4,
};
PlatformWindowManager *mWindowManager;
PlatformWindow *mWindows[NumWindows];
GFXDevice *mDevice;
void handleDrawEvent(WindowId id)
{
// Which window are we getting this event on?
PlatformWindow *w = mWindowManager->getWindowById(id);
mDevice->beginScene();
mDevice->setActiveRenderTarget(w->getGFXTarget());
// Vary clear color by window to discern which window is which.
mDevice->clear( GFXClearTarget,
ColorI( 255 - (id * 50), 255, id * 100 ), 1.0f, 0 );
mDevice->endScene();
// Call swap on the window's render target.
((GFXWindowTarget*)w->getGFXTarget())->present();
}
void handleAppEvent(WindowId, S32 event)
{
if(event == WindowClose)
Process::requestShutdown();
}
void handleIdleEvent()
{
for(S32 i=0; i<NumWindows; i++)
handleDrawEvent(mWindows[i]->getWindowId());
}
void run()
{
mWindowManager = CreatePlatformWindowManager();
// Create a device.
GFXAdapter a;
a.mType = Direct3D9;
a.mIndex = 0;
mDevice = GFXInit::createDevice(&a);
test(mDevice, "Unable to create d3d9 device #0.");
// Initialize the windows...
GFXVideoMode vm;
vm.resolution.x = 400;
vm.resolution.y = 400;
for(S32 i=0; i<NumWindows; i++)
{
mWindows[i] = mWindowManager->createWindow(mDevice, vm);
test(mWindows[i], "Didn't get a window back from the window manager, no good.");
if(!mWindows[i])
continue;
// Setup our events.
mWindows[i]->displayEvent.notify(this, &TestWinMgrGFXInitMultiWindow::handleDrawEvent);
mWindows[i]->appEvent.notify(this, &TestWinMgrGFXInitMultiWindow::handleAppEvent);
mWindows[i]->idleEvent.notify(this, &TestWinMgrGFXInitMultiWindow::handleIdleEvent);
}
// And, go on our way.
while(Process::processEvents())
;
SAFE_DELETE(mWindowManager);
SAFE_DELETE(mDevice);
}
};
CreateInteractiveTest(TestJournaledMultiWindowGFX, "WindowManager/GFXJournaledMultiWindow")
{
enum {
NumWindows = 2,
};
PlatformWindowManager *mWindowManager;
PlatformWindow *mWindows[NumWindows];
GFXDevice *mDevice;
S32 mNumDraws;
S32 mNumResize;
void drawToWindow(PlatformWindow *win)
{
// Do some simple checks to make sure we draw the same number of times
// on both runs.
if(Journal::IsPlaying())
mNumDraws--;
else
mNumDraws++;
// Render!
mDevice->beginScene();
mDevice->setActiveRenderTarget(win->getGFXTarget());
// Vary clear color by window to discern which window is which.
static S32 timeVariance = 0;
mDevice->clear( GFXClearTarget,
ColorI( 0xFF - (++timeVariance * 5), 0xFF, win->getWindowId() * 0x0F ), 1.0f, 0 );
mDevice->endScene();
// Call swap on the window's render target.
win->getGFXTarget()->present();
}
void handleDrawEvent(WindowId id)
{
// Which window are we getting this event on?
PlatformWindow *w = mWindowManager->getWindowById(id);
drawToWindow(w);
}
void handleAppEvent(WindowId, S32 event)
{
if(event == WindowClose)
Process::requestShutdown();
}
void handleIdleEvent()
{
for(S32 i=0; i<NumWindows; i++)
drawToWindow(mWindows[i]);
}
void handleResizeEvent(WindowId id, S32 width, S32 height)
{
// Do some simple checks to make sure we resize the same number of times
// on both runs.
if(Journal::IsPlaying())
{
// If we're playing back, APPLY the resize event...
mWindowManager->getWindowById(id)->setSize(Point2I(width, height));
mNumResize--;
}
else
{
// If we're not playing back, do nothing except note it.
mNumResize++;
}
// Which window are we getting this event on?
PlatformWindow *w = mWindowManager->getWindowById(id);
drawToWindow(w);
}
/// The mainloop of our app - we'll run this twice, once to create
/// a journal and again to play it back.
void mainLoop()
{
mWindowManager = CreatePlatformWindowManager();
// Create a device.
GFXAdapter a;
a.mType = Direct3D9;
a.mIndex = 0;
mDevice = GFXInit::createDevice(&a);
test(mDevice, "Unable to create ogl device #0.");
// Initialize the windows...
GFXVideoMode vm;
vm.resolution.x = 400;
vm.resolution.y = 400;
for(S32 i=0; i<NumWindows; i++)
{
mWindows[i] = mWindowManager->createWindow(mDevice, vm);
test(mWindows[i], "Didn't get a window back from the window manager, no good.");
if(!mWindows[i])
continue;
// Setup our events.
mWindows[i]->displayEvent.notify(this, &TestJournaledMultiWindowGFX::handleDrawEvent);
mWindows[i]->appEvent.notify(this, &TestJournaledMultiWindowGFX::handleAppEvent);
mWindows[i]->resizeEvent.notify(this, &TestJournaledMultiWindowGFX::handleResizeEvent);
// Only subscribe to the first idle event.
if(i==0)
mWindows[i]->idleEvent.notify(this, &TestJournaledMultiWindowGFX::handleIdleEvent);
}
// And, go on our way.
while(Process::processEvents())
;
// Finally, clean up.
for(S32 i=0; i<NumWindows; i++)
SAFE_DELETE(mWindows[i]);
SAFE_DELETE(mDevice);
SAFE_DELETE(mWindowManager);
}
void run()
{
return;
// CodeReview: this should be deleted or enabled.
#if 0
mNumDraws = 0;
mNumResize = 0;
// Record a run of the main loop.
Journal::Record("multiwindow.jrn");
mainLoop();
Journal::Stop();
test(mNumDraws > 0, "No draws occurred!");
test(mNumResize > 0, "No resizes occurred!");
// And play it back.
Journal::Play("multiwindow.jrn");
mainLoop();
Journal::Stop();
test(mNumDraws == 0, "Failed to play journal back with same number of draws.");
test(mNumResize == 0, "Failed to play journal back with same number of resizes.");
#endif
}
};
CreateInteractiveTest(GFXTestFullscreenToggle, "GFX/TestFullscreenToggle")
{
enum Constants
{
NumWindows = 1,
};
PlatformWindowManager *mWindowManager;
PlatformWindow *mWindows[NumWindows];
GFXDevice *mDevice;
void drawToWindow(PlatformWindow *win)
{
// Render!
mDevice->beginScene();
mDevice->setActiveRenderTarget(win->getGFXTarget());
// Vary clear color by window to discern which window is which.
static S32 timeVariance = 0;
mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget,
ColorI( 0xFF - (++timeVariance * 5), 0xFF, win->getWindowId() * 0x40 ), 1.0f, 0 );
mDevice->endScene();
// Call swap on the window's render target.
win->getGFXTarget()->present();
}
void handleDrawEvent(WindowId id)
{
// Which window are we getting this event on?
PlatformWindow *w = mWindowManager->getWindowById(id);
drawToWindow(w);
}
void handleAppEvent(WindowId, S32 event)
{
if(event == WindowClose)
Process::requestShutdown();
}
void handleIdleEvent()
{
// Redraw everything.
for(S32 i=0; i<NumWindows; i++)
drawToWindow(mWindows[i]);
// Don't monopolize the CPU.
Platform::sleep(10);
}
void handleButtonEvent(WindowId did,U32 modifier,U32 action,U16 button)
{
// Only respond to button down
if(action != IA_MAKE)
return;
// Get the window...
PlatformWindow *win = mWindowManager->getWindowById(did);
GFXVideoMode winVm = win->getVideoMode();
// If the window is not full screen, make it full screen 800x600x32
if(winVm.fullScreen == false)
{
winVm.fullScreen = true;
winVm.resolution.set(800,600);
}
else
{
// If the window is full screen, then bump it to 400x400x32
winVm.fullScreen = false;
winVm.resolution.set(400,400);
}
win->setVideoMode(winVm);
}
void run()
{
mWindowManager = CreatePlatformWindowManager();
// Create a device.
GFXAdapter a;
a.mType = Direct3D9;
a.mIndex = 0;
mDevice = GFXInit::createDevice(&a);
test(mDevice, "Unable to create d3d9 device #0.");
// Initialize the windows...
GFXVideoMode vm;
vm.resolution.x = 400;
vm.resolution.y = 400;
for(S32 i=0; i<NumWindows; i++)
{
mWindows[i] = mWindowManager->createWindow(mDevice, vm);
test(mWindows[i], "Didn't get a window back from the window manager, no good.");
if(!mWindows[i])
continue;
// Setup our events.
mWindows[i]->appEvent.notify(this, &GFXTestFullscreenToggle::handleAppEvent);
mWindows[i]->buttonEvent.notify(this, &GFXTestFullscreenToggle::handleButtonEvent);
mWindows[i]->displayEvent.notify(this, &GFXTestFullscreenToggle::handleDrawEvent);
// Only subscribe to the first idle event.
if(i==0)
mWindows[i]->idleEvent.notify(this, &GFXTestFullscreenToggle::handleIdleEvent);
}
// And, go on our way.
while(Process::processEvents())
;
// Finally, clean up.
for(S32 i=0; i<NumWindows; i++)
SAFE_DELETE(mWindows[i]);
mDevice->preDestroy();
SAFE_DELETE(mDevice);
SAFE_DELETE(mWindowManager);
}
};

View file

@ -0,0 +1,202 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <windows.h>
#include <tchar.h>
#include "core/strings/unicode.h"
#include "math/mMath.h"
#include "windowManager/win32/win32Window.h"
#include "windowManager/win32/win32WindowMgr.h"
#include "windowManager/win32/winDispatch.h"
#include "windowManager/win32/win32CursorController.h"
#include "platform/platformInput.h"
#include <zmouse.h>
static struct { U32 id; LPTSTR resourceID; } sgCursorShapeMap[]=
{
{ PlatformCursorController::curArrow, IDC_ARROW },
{ PlatformCursorController::curWait, IDC_WAIT },
{ PlatformCursorController::curPlus, IDC_CROSS },
{ PlatformCursorController::curResizeVert, IDC_SIZEWE },
{ PlatformCursorController::curResizeHorz, IDC_SIZENS },
{ PlatformCursorController::curResizeAll, IDC_SIZEALL },
{ PlatformCursorController::curIBeam, IDC_IBEAM },
{ PlatformCursorController::curResizeNESW, IDC_SIZENESW },
{ PlatformCursorController::curResizeNWSE, IDC_SIZENWSE },
{ PlatformCursorController::curHand, IDC_HAND },
{ 0, 0 },
};
//static const EnumTable::Enums curManagerShapesEnums[] =
//{
// { Win32CursorController::curArrow, "Arrow" },
// { Win32CursorController::curWait, "Wait" },
// { Win32CursorController::curPlus, "Plus" },
// { Win32CursorController::curResizeVert, "ResizeVert" },
// { Win32CursorController::curResizeHorz, "ResizeHorz" },
// { Win32CursorController::curResizeAll, "ResizeAll" },
// { Win32CursorController::curIBeam, "ibeam" },
// { Win32CursorController::curResizeNESW, "ResizeNESW" },
// { Win32CursorController::curResizeNWSE, "ResizeNWSE" },
//};
//
//static const EnumTable gCurManagerShapesTable(8, &curManagerShapesEnums[0]);
// CodeReview I've duplicated this 'cache' trick for system settings
// because they're unlikely to change and calling into the OS for values
// repeatedly is just silly to begin with. [6/29/2007 justind]
U32 Win32CursorController::getDoubleClickTime()
{
static S32 sPlatWinDoubleClicktime = -1;
if( sPlatWinDoubleClicktime == -1 )
sPlatWinDoubleClicktime = GetDoubleClickTime();
return sPlatWinDoubleClicktime;
}
S32 Win32CursorController::getDoubleClickWidth()
{
static S32 sPlatWinDoubleClickwidth = -1;
if( sPlatWinDoubleClickwidth == -1 )
sPlatWinDoubleClickwidth = GetSystemMetrics(SM_CXDOUBLECLK);
return sPlatWinDoubleClickwidth;
}
S32 Win32CursorController::getDoubleClickHeight()
{
static S32 sPlatWinDoubleClickheight = -1;
if( sPlatWinDoubleClickheight == -1 )
sPlatWinDoubleClickheight = GetSystemMetrics(SM_CYDOUBLECLK);
return sPlatWinDoubleClickheight;
}
void Win32CursorController::setCursorPosition( S32 x, S32 y )
{
::SetCursorPos(x, y);
}
void Win32CursorController::getCursorPosition( Point2I &point )
{
POINT rPoint;
::GetCursorPos( &rPoint );
// Return
point.x = rPoint.x;
point.y = rPoint.y;
}
void Win32CursorController::setCursorVisible( bool visible )
{
if( visible )
ShowCursor( true );
else
while( ShowCursor(false) > 0 );
}
bool Win32CursorController::isCursorVisible()
{
CURSORINFO rCursorInfo;
rCursorInfo.cbSize = sizeof(CURSORINFO);
if( !GetCursorInfo( &rCursorInfo ) )
{
//DWORD error = GetLastError();
return false;
}
// rCursorInfo.flags values :
// 0 == Cursor is hidden
// CURSOR_SHOWING == cursor is visible
return (bool)(rCursorInfo.flags == CURSOR_SHOWING);
}
void Win32CursorController::setCursorShape(U32 cursorID)
{
LPTSTR resourceID = NULL;
for(S32 i = 0;sgCursorShapeMap[i].resourceID != NULL;++i)
{
if(cursorID == sgCursorShapeMap[i].id)
{
resourceID = sgCursorShapeMap[i].resourceID;
break;
}
}
if(resourceID == NULL)
return;
HCURSOR cur = LoadCursor(NULL, resourceID);
if(cur)
SetCursor(cur);
}
static HCURSOR gCursorShape = NULL;
void Win32CursorController::setCursorShape( const UTF8 *fileName, bool reload )
{
#ifdef UNICODE
const UTF16 *lFileName = convertUTF8toUTF16( fileName );
#else
const UTF8 *lFileName = fileName;
#endif
if ( !gCursorShape || reload )
gCursorShape = LoadCursorFromFile( lFileName );
if ( gCursorShape )
SetCursor( gCursorShape );
}
// Console function to set the current cursor shape given the cursor shape
// name as defined in the enum above.
//ConsoleFunction( inputPushCursor, void, 2, 2, "inputPushCursor(cursor shape name)" )
//{
// S32 val = 0;
//
// // Find the cursor shape
// if(argc == 2)
// {
// for (S32 i = 0; i < gCurManagerShapesTable.size; i++)
// {
// if (! dStricmp(argv[1], gCurManagerShapesTable.table[i].label))
// {
// val = gCurManagerShapesTable.table[i].index;
// break;
// }
// }
// }
//
// // Now set it
// Win32CursorController* cm = Input::getCursorManager();
// if(cm)
// {
// cm->pushCursor(val);
// }
//}
//// Function to pop the current cursor shape
//ConsoleFunction( inputPopCursor, void, 1, 1, "inputPopCursor()" )
//{
// argc;
// argv;
//
// Win32CursorController* cm = Input::getCursorManager();
// if(cm)
// {
// cm->popCursor();
// }
//}

View file

@ -0,0 +1,52 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WIN32_CURSORCONTROLLER_H_
#define _WIN32_CURSORCONTROLLER_H_
#include <windows.h>
#include "windowManager/platformCursorController.h"
class Win32CursorController : public PlatformCursorController
{
public:
Win32CursorController( PlatformWindow *owner ) :
PlatformCursorController( owner )
{
pushCursor( PlatformCursorController::curArrow );
};
virtual void setCursorPosition( S32 x, S32 y );
virtual void getCursorPosition( Point2I &point );
virtual void setCursorVisible( bool visible );
virtual bool isCursorVisible();
void setCursorShape( U32 cursorID );
void setCursorShape( const UTF8 *fileName, bool reload );
U32 getDoubleClickTime();
S32 getDoubleClickWidth();
S32 getDoubleClickHeight();
};
#endif

View file

@ -0,0 +1,142 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include "platform/platform.h"
#include "console/console.h"
// from Torque.rc
#define IDI_ICON1 103
// Window Class name
static const TCHAR* c_szSplashClass = L"Torque3DSplashWindow";
static HWND gSplashWndOwner = NULL;
static HWND gSplashWnd = NULL;
static HBITMAP gSplashImage = NULL;
// Registers a window class for the splash and splash owner windows.
static void RegisterWindowClass(HINSTANCE hinst)
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = hinst;
wc.hIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_ICON1));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = c_szSplashClass;
RegisterClass(&wc);
}
static void UnregisterSplashWindowClass(HINSTANCE hinst)
{
WNDCLASSEX classInfo;
if (GetClassInfoEx(hinst,c_szSplashClass,&classInfo))
UnregisterClass(c_szSplashClass,hinst);
}
// Creates the splash owner window and the splash window.
static HWND CreateSplashWindow(HINSTANCE hinst)
{
RegisterWindowClass(hinst);
gSplashWndOwner = CreateWindow(c_szSplashClass, NULL, WS_POPUP,
0, 0, 0, 0, NULL, NULL, hinst, NULL);
return CreateWindowEx(WS_EX_LAYERED, c_szSplashClass, NULL, WS_POPUP | WS_VISIBLE,
0, 0, 0, 0, gSplashWndOwner, NULL, hinst, NULL);
}
// Calls UpdateLayeredWindow to set a bitmap (with alpha) as the content of the splash window.
static void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash)
{
// get the size of the bitmap
BITMAP bm;
GetObject(hbmpSplash, sizeof(bm), &bm);
SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };
// get the primary monitor's info
POINT ptZero = { 0 };
HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorinfo = { 0 };
monitorinfo.cbSize = sizeof(monitorinfo);
GetMonitorInfo(hmonPrimary, &monitorinfo);
// center the splash screen in the middle of the primary work area
const RECT & rcWork = monitorinfo.rcWork;
POINT ptOrigin;
ptOrigin.x = rcWork.left + (rcWork.right - rcWork.left - sizeSplash.cx) / 2;
ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;
// create a memory DC holding the splash bitmap
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);
// paint the window (in the right location) with the alpha-blended bitmap
UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,
hdcMem, &ptZero, RGB(0, 0, 0), NULL, ULW_OPAQUE);
// delete temporary objects
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
}
void CloseSplashWindow(HINSTANCE hinst)
{
if (gSplashWndOwner)
{
//ShowWindow(gSplashWnd, 0);
DestroyWindow(gSplashWndOwner);
UnregisterSplashWindowClass(hinst);
}
gSplashWndOwner = NULL;
gSplashWnd = NULL;
}
bool Platform::displaySplashWindow()
{
gSplashImage = (HBITMAP) ::LoadImage(0, L"art\\gui\\splash.bmp",
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (!gSplashImage)
return false;
gSplashWnd = CreateSplashWindow(GetModuleHandle(NULL));
if (!gSplashWnd)
return false;
SetSplashImage(gSplashWnd, gSplashImage);
return true;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,231 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINDOWMANAGER_WIN32_WIN32WINDOW_
#define _WINDOWMANAGER_WIN32_WIN32WINDOW_
#include <windows.h>
#include "windowManager/platformWindowMgr.h"
#include "gfx/gfxTarget.h"
#include "gfx/gfxStructs.h"
#include "sim/actionMap.h"
class Win32WindowManager;
/// Implementation of a window on Win32.
class Win32Window : public PlatformWindow
{
friend class Win32WindowManager;
friend class GFXPCD3D9Device;
friend class GFXPCD3D9WindowTarget;
friend class GFXD3D8WindowTarget;
public:
struct Accelerator
{
U32 mID;
EventDescriptor mDescriptor;
};
typedef Vector<Accelerator> AcceleratorList;
private:
typedef Vector<ACCEL> WinAccelList;
/// @name Active window list
///
/// Items used to track window instances.
///
/// @{
/// Which manager created us?
Win32WindowManager *mOwningManager;
/// Which window comes next in list?
Win32Window *mNextWindow;
/// @}
/// @name Window Information
///
/// @{
/// Our HWND - Win32 window handle.
HWND mWindowHandle;
/// Our former Parent HWND
HWND mOldParent;
/// The Win32 window style we want to use when windowed.
DWORD mWindowedWindowStyle;
/// The GFX device that we're tied to.
GFXDevice *mDevice;
/// Reference to the render target allocated on this window.
GFXWindowTargetRef mTarget;
/// Our current size/resolution/fullscreen status.
GFXVideoMode mVideoMode;
/// Our position on the desktop.
Point2I mPosition;
/// Windows HACCEL for accelerators
HACCEL mAccelHandle;
/// Keyboard accelerators for menus
WinAccelList mWinAccelList;
/// Is the mouse locked to this window?
bool mMouseLocked;
/// The position the cursor was at when a mouse lock occured
Point2I mMouseLockPosition;
/// Determines whether this window should lock the mouse when it has an opportunity
bool mShouldLockMouse;
/// When set, we don't trigger device resets due to sizing events.
bool mSuppressReset;
/// Menu associated with this window. This is a passive property of the window and is not required to be used at all.
HMENU mMenuHandle;
/// Do we have a fullscreen window style set?
bool mFullscreen;
/// @}
/// Helper to allocate our Win32 window class.
void _registerWindowClass();
void _unregisterWindowClass();
/// Windows message handler callback.
static LRESULT PASCAL WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
/// Add an accelerator to the list of accelerators for this window. Intended for use by addAccelerators()
void addAccelerator(Accelerator &accel);
/// Remove an accelerator from the list of accelerators for this window. Intended for use by removeAccelerators()
void removeAccelerator(Accelerator &accel);
public:
Win32Window();
~Win32Window();
/// Return the HWND (win32 window handle) for this window.
HWND &getHWND()
{
return mWindowHandle;
}
HMENU &getMenuHandle()
{
return mMenuHandle;
}
void setMenuHandle( HMENU menuHandle )
{
mMenuHandle = menuHandle;
if(!mFullscreen)
SetMenu(mWindowHandle, mMenuHandle);
}
/// Add a list of accelerators to this window
void addAccelerators(AcceleratorList &list);
/// Remove a list of accelerators from this window
void removeAccelerators(AcceleratorList &list);
/// Returns true if @p info matches an accelerator
bool isAccelerator(const InputEventInfo &info);
/// Allow windows to translate messages. Used for accelerators.
bool translateMessage(MSG &msg);
virtual GFXDevice *getGFXDevice();
virtual GFXWindowTarget *getGFXTarget();
virtual void setVideoMode(const GFXVideoMode &mode);
virtual const GFXVideoMode &getVideoMode();
virtual bool clearFullscreen();
virtual bool isFullscreen();
virtual void _setFullscreen(const bool fullscreen);
virtual bool setCaption(const char *cap);
virtual const char *getCaption();
// Window Client Area Extent
virtual void setClientExtent( const Point2I newExtent );
virtual const Point2I getClientExtent();
// Window Bounds
virtual void setBounds(const RectI &newBounds);
virtual const RectI getBounds() const;
// Window Position
virtual void setPosition( const Point2I newPosition );
virtual const Point2I getPosition();
virtual void centerWindow();
virtual bool setSize(const Point2I &newSize);
// Coordinate space conversion.
virtual Point2I clientToScreen( const Point2I& pos );
virtual Point2I screenToClient( const Point2I& pos );
virtual bool isOpen();
virtual bool isVisible();
virtual bool isFocused();
virtual bool isMinimized();
virtual bool isMaximized();
virtual void minimize();
virtual void maximize();
virtual void hide();
virtual void show();
virtual void close();
virtual void restore();
virtual void setFocus();
virtual void setMouseLocked(bool enable);
virtual bool isMouseLocked() const { return mMouseLocked; };
virtual bool shouldLockMouse() const { return mShouldLockMouse; };
virtual WindowId getWindowId();
virtual PlatformWindow * getNextWindow() const
{
return mNextWindow;
}
/// Provide a simple GDI-based render for when the game is not rendering.
virtual void defaultRender();
/// Return the class name for the windows we create with this class.
static const UTF16 *getWindowClassName();
/// Return the class name for the curtain window class.
static const UTF16 *getCurtainWindowClassName();
/// Return the platform specific object needed to create or attach an
/// accelerated graohics drawing context on or to the window
virtual void* getPlatformDrawable() const { return mWindowHandle; }
};
#endif

View file

@ -0,0 +1,453 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "windowManager/win32/win32WindowMgr.h"
#include "gfx/gfxDevice.h"
#include "windowManager/win32/winDispatch.h"
#include "core/util/journal/process.h"
#include "core/strings/unicode.h"
// ------------------------------------------------------------------------
void CloseSplashWindow(HINSTANCE hinst);
PlatformWindowManager * CreatePlatformWindowManager()
{
return new Win32WindowManager();
}
// ------------------------------------------------------------------------
Win32WindowManager::Win32WindowManager()
{
// Register in the process list.
Process::notify(this, &Win32WindowManager::_process, PROCESS_INPUT_ORDER);
// Init our list of allocated windows.
mWindowListHead = NULL;
// By default, we have no parent window.
mParentWindow = NULL;
mCurtainWindow = NULL;
mOffscreenRender = false;
}
Win32WindowManager::~Win32WindowManager()
{
// Get ourselves off the process list.
Process::remove(this, &Win32WindowManager::_process);
// Kill all our windows first.
while(mWindowListHead)
// The destructors update the list, so this works just fine.
delete mWindowListHead;
}
RectI Win32WindowManager::getPrimaryDesktopArea()
{
RECT primaryWorkRect;
SystemParametersInfo(SPI_GETWORKAREA, 0, &primaryWorkRect, 0);
RectI res;
res.point.x = primaryWorkRect.left;
res.point.y = primaryWorkRect.top;
res.extent.x = primaryWorkRect.right - primaryWorkRect.left;
res.extent.y = primaryWorkRect.bottom - primaryWorkRect.top;
return res;
}
Point2I Win32WindowManager::getDesktopResolution()
{
DEVMODE devMode;
dMemset( &devMode, 0, sizeof( devMode ) );
devMode.dmSize = sizeof( devMode );
if (!::EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &devMode))
return Point2I(-1,-1);
// Return Resolution
return Point2I(devMode.dmPelsWidth, devMode.dmPelsHeight);
}
S32 Win32WindowManager::getDesktopBitDepth()
{
DEVMODE devMode;
dMemset( &devMode, 0, sizeof( devMode ) );
devMode.dmSize = sizeof( devMode );
if (!::EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &devMode))
return -1;
// Return Bits per Pixel
return (S32)devMode.dmBitsPerPel;
}
BOOL Win32WindowManager::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData )
{
Vector<RectI> * regions = (Vector<RectI>*)dwData;
regions->increment();
regions->last().point.x = lprcMonitor->left;
regions->last().point.y = lprcMonitor->top;
regions->last().extent.x = lprcMonitor->right - lprcMonitor->left;
regions->last().extent.y = lprcMonitor->bottom - lprcMonitor->top;
return true;
}
void Win32WindowManager::getMonitorRegions(Vector<RectI> &regions)
{
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (U32)(void*)&regions);
}
void Win32WindowManager::getWindows(VectorPtr<PlatformWindow*> &windows)
{
Win32Window *win = mWindowListHead;
while(win)
{
windows.push_back(win);
win = win->mNextWindow;
}
}
PlatformWindow *Win32WindowManager::createWindow(GFXDevice *device, const GFXVideoMode &mode)
{
// Do the allocation.
Win32Window *w32w = new Win32Window();
w32w->setOffscreenRender(mOffscreenRender);
w32w->mWindowId = getNextId();
w32w->mOwningManager = this;
// Link into our list of windows.
linkWindow(w32w);
DWORD dwExStyle;
DWORD dwStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
dwStyle |= WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION;
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
// If we're parented, we want a different set of window styles.
if(mParentWindow)
dwStyle = WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CHILDWINDOW;
if (mOffscreenRender)
{
dwStyle = WS_OVERLAPPEDWINDOW;
dwExStyle = 0;
}
// Create the window handle
w32w->mWindowHandle = CreateWindowEx(
dwExStyle,
Win32Window::getWindowClassName(), //class name
String( getEngineProductString() ).utf16(), //window title
dwStyle, //style - need clip siblings/children for opengl
0,
0,
0,
0,
mParentWindow, //parent window
NULL, //menu? No.
NULL, //the hInstance
NULL ); //no funky params
// Note the style we created with so we can switch back to it when we're
// done with full-screen mode.
w32w->mWindowedWindowStyle = dwStyle;
// Set the video mode on the window
w32w->setVideoMode(mode);
// Associate our window struct with the HWND.
SetWindowLongPtrW(w32w->mWindowHandle, GWLP_USERDATA, (LONG)w32w);
// Do some error checking.
AssertFatal(w32w->mWindowHandle != NULL, "Win32WindowManager::createWindow - Could not create window!");
if(w32w->mWindowHandle == NULL)
{
Con::errorf("Win32WindowManager::createWindow - Could not create window!");
delete w32w;
return NULL;
}
// If we're not rendering offscreen, make sure our window is shown and drawn to.
if (!mOffscreenRender)
ShowWindow( w32w->mWindowHandle, SW_SHOWDEFAULT );
// Close any splash screen we created
CloseSplashWindow(winState.appInstance);
// Bind the window to the specified device.
if(device)
{
w32w->mDevice = device;
w32w->mTarget = device->allocWindowTarget(w32w);
AssertISV(w32w->mTarget,
"Win32WindowManager::createWindow - failed to get a window target back from the device.");
}
else
{
Con::warnf("Win32WindowManager::createWindow - created a window with no device!");
}
// Update it if needed.
UpdateWindow( w32w->mWindowHandle );
return w32w;
}
void Win32WindowManager::setParentWindow(void* newParent)
{
Con::printf( "Setting parent HWND: %d", newParent );
mParentWindow = (HWND)newParent;
if( mWindowListHead && mWindowListHead->mWindowHandle )
::SetParent( mWindowListHead->mWindowHandle, mParentWindow);
}
void* Win32WindowManager::getParentWindow()
{
return (void*)mParentWindow;
}
void Win32WindowManager::_process()
{
MSG msg;
bool _blocking = false;
// CodeReview [tom, 4/30/2007] Maintaining two completely separate message
// handlers that are essentially the same is silly. The first one never
// seems to run as _blocking is hard coded to false above, so is this even
// needed ? If it is, this should be rewritten to use the one loop that
// adjusts as needed based on _blocking and Journal::IsPlaying()
if (_blocking && !Journal::IsPlaying())
{
// In blocking mode, we process one message at a time.
if (GetMessage(&msg, NULL, 0, 0))
{
bool noTranslate = false;
Win32Window *w32w = mWindowListHead;
while(w32w)
{
noTranslate = w32w->translateMessage(msg);
if(noTranslate) break;
w32w = w32w->mNextWindow;
}
if(! noTranslate)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
// This should be WM_QUIT
Dispatch(ImmediateDispatch,0,msg.message,msg.wParam,msg.lParam);
}
else
{
// Process all queued up messages
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
bool translated = false;
// Win32Window *w32w = mWindowListHead;
// while(w32w)
// {
// noTranslate = w32w->translateMessage(msg);
// if(noTranslate) break;
// w32w = w32w->mNextWindow;
// }
// [tom, 4/30/2007] I think this should work, but leaving the above commented
// out just in case this is actually fubared with multiple windows.
Win32Window* window = (Win32Window*)(GetWindowLong(msg.hwnd, GWL_USERDATA));
if(window)
translated = window->translateMessage(msg);
if(! translated)
{
// Win32Window::translateMessage() will post a WM_COMMAND event for
// translated accelerator events, so dispatching again will cause a
// the input event to be dispatched, which is usually not what we want.
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (msg.message == WM_QUIT)
{
Dispatch(ImmediateDispatch,0,msg.message,msg.wParam,msg.lParam);
break;
}
}
}
// Dispatch any delayed events
while (DispatchNext());
// Fire off idle events for every window.
Win32Window *w32w = mWindowListHead;
while(w32w)
{
w32w->idleEvent.trigger();
w32w = w32w->mNextWindow;
}
}
PlatformWindow * Win32WindowManager::getWindowById( WindowId id )
{
// Walk the list and find the matching id, if any.
Win32Window *win = mWindowListHead;
while(win)
{
if(win->getWindowId() == id)
return win;
win = win->mNextWindow;
}
return NULL;
}
PlatformWindow * Win32WindowManager::getFirstWindow()
{
return mWindowListHead != NULL ? mWindowListHead : NULL;
}
PlatformWindow* Win32WindowManager::getFocusedWindow()
{
Win32Window* window = mWindowListHead;
while( window )
{
if( window->isFocused() )
return window;
window = window->mNextWindow;
}
return NULL;
}
void Win32WindowManager::linkWindow( Win32Window *w )
{
w->mNextWindow = mWindowListHead;
mWindowListHead = w;
}
void Win32WindowManager::unlinkWindow( Win32Window *w )
{
Win32Window **walk = &mWindowListHead;
while(*walk)
{
if(*walk != w)
{
// Advance to next item in list.
walk = &(*walk)->mNextWindow;
continue;
}
// Got a match - unlink and return.
*walk = (*walk)->mNextWindow;
return;
}
}
void Win32WindowManager::_processCmdLineArgs( const S32 argc, const char **argv )
{
if (argc > 1)
{
for (int i = 1; i < argc; i++)
{
if ( dStrnicmp( argv[i], "-window", 7 ) == 0 )
{
i++;
if ( i >= argc )
{
Con::errorf( "Command line error: -window requires an argument" );
break;
}
S32 hwnd = dAtoi( argv[i] );
if ( hwnd == 0 || hwnd == S32_MAX )
{
Con::errorf( "Command line error: -window requires a number, found [%s]", argv[i] );
break;
}
mParentWindow = (HWND)hwnd;
Con::printf( "HWND from command line: %d", hwnd );
}
if ( dStrnicmp( argv[i], "-offscreen", 10 ) == 0 )
{
mOffscreenRender = true;
}
}
}
}
void Win32WindowManager::lowerCurtain()
{
if(mCurtainWindow)
return;
// For now just grab monitor of the first window... we may need to
// beef this up later on, maybe by passing in the window that's entering
// leaving full-screen to lowerCurtain.
HMONITOR hMon = MonitorFromWindow(mWindowListHead->getHWND(), MONITOR_DEFAULTTOPRIMARY);
// Get the monitor's extents.
MONITORINFO monInfo;
dMemset(&monInfo, 0, sizeof MONITORINFO);
monInfo.cbSize = sizeof MONITORINFO;
GetMonitorInfo(hMon, &monInfo);
mCurtainWindow = CreateWindow(Win32Window::getCurtainWindowClassName(),
dT(""), (WS_POPUP | WS_MAXIMIZE | WS_VISIBLE),
monInfo.rcWork.left, monInfo.rcWork.top,
monInfo.rcWork.right - monInfo.rcWork.left,
monInfo.rcWork.bottom - monInfo.rcWork.top,
NULL, NULL, NULL, NULL);
if (!mOffscreenRender)
SetWindowPos(mCurtainWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
}
void Win32WindowManager::raiseCurtain()
{
if(!mCurtainWindow)
return;
DestroyWindow(mCurtainWindow);
mCurtainWindow = NULL;
}

View file

@ -0,0 +1,93 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINDOWMANAGER_WIN32_WIN32WINDOWMANAGER_
#define _WINDOWMANAGER_WIN32_WIN32WINDOWMANAGER_
#include <windows.h>
#include "math/mMath.h"
#include "gfx/gfxStructs.h"
#include "windowManager/win32/win32Window.h"
#include "core/util/tVector.h"
/// Win32 implementation of the window manager interface.
class Win32WindowManager : public PlatformWindowManager
{
friend class Win32Window;
virtual void _processCmdLineArgs(const S32 argc, const char **argv);
/// Link the specified window into the window list.
void linkWindow(Win32Window *w);
/// Remove specified window from the window list.
void unlinkWindow(Win32Window *w);
/// Callback for the process list.
void _process();
/// List of allocated windows.
Win32Window *mWindowListHead;
/// Parent window, used in window setup in web plugin scenarios.
HWND mParentWindow;
/// set via command line -offscreen option, controls whether rendering/input
// is intended for offscreen rendering
bool mOffscreenRender;
/// Callback to receive information about available monitors.
static BOOL CALLBACK MonitorEnumProc(
HMONITOR hMonitor, // handle to display monitor
HDC hdcMonitor, // handle to monitor DC
LPRECT lprcMonitor, // monitor intersection rectangle
LPARAM dwData // data
);
/// If a curtain window is present, then its HWND will be stored here.
HWND mCurtainWindow;
public:
Win32WindowManager();
~Win32WindowManager();
virtual RectI getPrimaryDesktopArea();
virtual S32 getDesktopBitDepth();
virtual Point2I getDesktopResolution();
virtual void getMonitorRegions(Vector<RectI> &regions);
virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode);
virtual void getWindows(VectorPtr<PlatformWindow*> &windows);
virtual void setParentWindow(void* newParent);
virtual void* getParentWindow();
virtual PlatformWindow *getWindowById(WindowId id);
virtual PlatformWindow *getFirstWindow();
virtual PlatformWindow* getFocusedWindow();
virtual void lowerCurtain();
virtual void raiseCurtain();
};
#endif

View file

@ -0,0 +1,594 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#define NO_MINMAX
#define WIN32_LEAN_AND_MEAN
// This is a fix for mouse wheel support on
// older versions of VC++.
#if _MSC_VER < 1500
#define _WIN32_WINNT 0x0400
#endif
#include <windows.h>
#include "platform/event.h"
#include "platform/platformInput.h"
#include "windowManager/win32/winDispatch.h"
#include "windowManager/win32/win32Window.h"
#include "windowManager/win32/win32CursorController.h"
#include "platformWin32/winDirectInput.h"
#include "core/util/journal/process.h"
#include "core/util/journal/journaledSignal.h"
static U32 _ModifierKeys=0;
static BYTE keyboardState[256];
static bool initKBState = false;
static bool sgDoubleByteEnabled = false;
// is keyboard input a standard (non-changing) VK keycode
#define dIsStandardVK(c) (((0x08 <= (c)) && ((c) <= 0x12)) || \
((c) == 0x1b) || \
((0x20 <= (c)) && ((c) <= 0x2e)) || \
((0x30 <= (c)) && ((c) <= 0x39)) || \
((0x41 <= (c)) && ((c) <= 0x5a)) || \
((0x70 <= (c)) && ((c) <= 0x7B)))
extern InputObjectInstances DIK_to_Key( U8 dikCode );
extern U8 TranslateOSKeyCode(U8 vcode );
extern InputModifiers convertModifierBits(const U32 in);
static void _keyboardEvent(Win32Window* window,UINT message, WPARAM wParam, WPARAM lParam)
{
if(!initKBState)
{
dMemset(keyboardState, 0, sizeof(keyboardState));
initKBState = true;
}
// Extract windows key info:
// S32 repeatCount = (lParam & 0xffff);
U32 scanCode = (lParam >> 16) & 0xff;
bool extended = lParam & (1 << 24); // Enhanced keyboard key
bool previous = lParam & (1 << 30); // Previously down
bool make = (message == WM_KEYDOWN || message == WM_SYSKEYDOWN);
// Translate the OS virtual key code to a Torque KEY_XXXX.
S32 nVirtkey = TranslateOSKeyCode( wParam );
S32 keyCode;
if ( wParam == VK_PROCESSKEY && sgDoubleByteEnabled )
keyCode = MapVirtualKey( scanCode, 1 ); // This is the REAL virtual key...
else
keyCode = wParam;
// Convert alt/shift/ctrl to left or right variant if needed.
S32 newVirtKey = nVirtkey;
switch(nVirtkey)
{
case KEY_ALT:
newVirtKey = extended ? KEY_RALT : KEY_LALT;
break;
case KEY_CONTROL:
newVirtKey = extended ? KEY_RCONTROL : KEY_LCONTROL;
break;
case KEY_SHIFT:
newVirtKey = (scanCode == 54) ? KEY_RSHIFT : KEY_LSHIFT;
break;
case KEY_RETURN:
if ( extended )
newVirtKey = KEY_NUMPADENTER;
break;
}
// Track modifier keys
U32 modifier = 0;
switch (newVirtKey)
{
case KEY_LALT: modifier = IM_LALT; break;
case KEY_RALT: modifier = IM_RALT; break;
case KEY_LSHIFT: modifier = IM_LSHIFT; break;
case KEY_RSHIFT: modifier = IM_RSHIFT; break;
case KEY_LCONTROL: modifier = IM_LCTRL; break;
case KEY_RCONTROL: modifier = IM_RCTRL; break;
}
if (make)
{
_ModifierKeys |= modifier;
keyboardState[keyCode] |= 0x80;
}
else
{
_ModifierKeys &= ~modifier;
keyboardState[keyCode] &= 0x7f;
}
U32 torqueMods = convertModifierBits( _ModifierKeys );
Input::setModifierKeys( torqueMods );
// If character event translation is active and this isn't a key
// mapped in the global action map, try converting the event into
// a character event first.
if( make
&& window->getKeyboardTranslation()
&& !window->shouldNotTranslate( torqueMods, newVirtKey ) )
{
U16 chars[ 64 ];
dMemset( chars, 0, sizeof( chars ) );
S32 res = ToUnicode( keyCode, scanCode, keyboardState, chars, sizeof( chars ) / sizeof( chars[ 0 ] ), 0 );
// This should only happen on Window 9x/ME systems
if( res == 0 )
res = ToAscii( keyCode, scanCode, keyboardState, chars, 0 );
if( res >= 1 )
{
// Post chars, but filter them to not be control codes... this is a bit hacky.
bool handledCharEvent = false;
for( S32 i=0; i< res; i ++ )
if( chars[i] >= 32)
{
window->charEvent.trigger(window->getWindowId(),_ModifierKeys,chars[i]);
handledCharEvent = true;
}
if( handledCharEvent )
return;
}
}
// Produce a key event.
U32 action = make ? (previous ? IA_REPEAT : IA_MAKE ) : IA_BREAK;
window->keyEvent.trigger(window->getWindowId(),_ModifierKeys,action,newVirtKey);
}
//-----------------------------------------------------------------------------
static bool _dispatch(HWND hWnd,UINT message,WPARAM wParam,WPARAM lParam)
{
static bool button[3] = {false,false,false};
static S32 mouseNCState = -1; // -1 denotes unchanged,
// 0 denotes changed but was hidden
// 1 denotes changed but was visible
Win32Window* window = hWnd?(Win32Window*)GetWindowLong(hWnd, GWL_USERDATA): 0;
const WindowId devId = window ? window->getWindowId() : 0;
// State tracking for focus/lose focus cursor management
static bool cursorLocked = false;
static bool cursorVisible = true;
switch(message)
{
case WM_MOUSEMOVE:
{
// Skip it if we have no window!
if (!window || !window->getCursorController())
break;
PlatformCursorController *pController = window->getCursorController();
// If we're locked and unfocused, ignore it.
if(window->shouldLockMouse() && !window->isFocused())
break;
// If the mouse was shown to accommodate a NC mouse move
// we need to change it back to what it was
if( mouseNCState != -1 )
{
pController->setCursorVisible( mouseNCState );
mouseNCState = -1; // reset to unchanged
}
// Let the cursor manager update the native cursor.
pController->refreshCursor();
// Grab the mouse pos so we can modify it.
S32 mouseX = S16(LOWORD(lParam));
S32 mouseY = S16(HIWORD(lParam));
// Ensure mouse lock when appropriate
window->setMouseLocked( window->shouldLockMouse() );
// Are we locked?
if(window->isMouseLocked())
{
// Always invisible when locked.
if( window->isCursorVisible() )
window->setCursorVisible( false );
RECT r;
GetWindowRect(window->getHWND(), &r);
// See Win32Window::setMouseLocked for explanation
RECT rCopy = r;
rCopy.top += 32; rCopy.bottom -= 64;
rCopy.left += 32; rCopy.right -= 64;
ClipCursor(&rCopy);
// Recenter the mouse if necessary (don't flood the message pump)
Point2I curPos;
pController->getCursorPosition( curPos );
const S32 centerX = (r.right + r.left) / 2;
const S32 centerY = (r.bottom + r.top) / 2;
if( curPos.x != centerX || curPos.y != centerY )
pController->setCursorPosition(centerX, centerY);
// Convert the incoming client pos into a screen pos, so we can
// accurately convert to relative coordinates.
POINT mousePos;
mousePos.x = mouseX;
mousePos.y = mouseY;
ClientToScreen(window->getHWND(), &mousePos);
// Now we can calculate the position relative to the center we set.
mouseX = mousePos.x - centerX;
mouseY = mousePos.y - centerY;
}
else
{
// Probably don't need to call this all the time but better
// safe than sorry...
ClipCursor(NULL);
}
window->mouseEvent.trigger(devId,_ModifierKeys,mouseX,mouseY,window->isMouseLocked());
break;
}
// We want to show the system cursor whenever we leave
// our window, and it'd be simple, except for one problem:
// showcursor isn't a toggle. so, keep hammering it until
// the cursor is *actually* going to be shown.
case WM_NCMOUSEMOVE:
{
if( window )
{
mouseNCState = ( window->isCursorVisible() ? 1 : 0);
window->setCursorVisible( true );
}
break;
}
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN: {
int index = (message - WM_LBUTTONDOWN) / 3;
button[index] = true;
// Capture the mouse on button down to allow dragging outside
// of the window boundary.
if (GetCapture() != hWnd)
SetCapture(hWnd);
if (window)
window->buttonEvent.trigger(devId,_ModifierKeys,IA_MAKE,index);
break;
}
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP: {
int index = (message - WM_LBUTTONUP) / 3;
button[index] = false;
// Release mouse capture from button down.
if (!button[0] && !button[1] && !button[2])
ReleaseCapture();
if (window)
window->buttonEvent.trigger(devId,_ModifierKeys,IA_BREAK,index);
break;
}
case WM_MOUSEWHEEL: // Vertical wheel.
if (window)
window->wheelEvent.trigger(devId,_ModifierKeys,0,GET_WHEEL_DELTA_WPARAM(wParam));
break;
#ifdef WM_MOUSEHWHEEL // Vista
case WM_MOUSEHWHEEL: // Horizontal wheel.
if( window )
window->wheelEvent.trigger( devId, _ModifierKeys, GET_WHEEL_DELTA_WPARAM( wParam ), 0 );
break;
#endif
case WM_KEYUP:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (window)
_keyboardEvent(window,message,wParam,lParam);
break;
// NOTE: if wParam is NOT equal to our window handle then we are GAINING focus
case WM_SETFOCUS:
// clear any key states
_ModifierKeys = 0;
dMemset(keyboardState, 0, 256);
Input::setModifierKeys(0);
// We must have a window present; otherwise there's nothing further
// we can do about this event.
if (window && window->getHWND() != (HWND)wParam)
{
if (cursorVisible == false)
window->setCursorVisible(false);
if (cursorLocked == true)
window->setMouseLocked(true);
// Update window state.
window->setBackground(false);
// Fire event.
window->appEvent.trigger(devId, GainFocus);
if (!Input::isActive())
Input::activate();
}
break;
// NOTE: if wParam is NOT equal to our window handle then we are LOSING focus
case WM_KILLFOCUS:
// clear any key states
_ModifierKeys = 0;
dMemset(keyboardState, 0, 256);
Input::setModifierKeys(0);
// We must have a window present; otherwise there's nothing further
// we can do about this event.
if (window && window->getHWND() != (HWND)wParam)
{
HWND hwnd = (HWND)wParam;
UTF16 classBuf[256];
if (hwnd)
GetClassName(hwnd, classBuf, sizeof(classBuf));
// We toggle the mouse lock when we become inactive
// causing the subsequent lock call to defer itself
// until the window becomes active again.
if (window && window->isMouseLocked())
{
window->setMouseLocked( false );
window->setMouseLocked( true );
}
// FIXME [tom, 5/1/2007] Hard coding this is lame since there's a const in win32Window.cpp
// CodeReview - this fails if there is a second jug app in the arena.
if (hwnd == NULL || dStrcmp(classBuf, L"TorqueJuggernaughtWindow") != 0)
{
// We are being made inactive and the window being made active isn't
// a jugg window. Thus, we need to deactivate input.
if (Input::isActive())
Input::deactivate();
}
cursorVisible = window->isCursorVisible();
if (!cursorVisible)
window->setCursorVisible(true);
cursorLocked = window->isMouseLocked();
if (cursorLocked)
window->setMouseLocked(false);
// Update window state.
window->setBackground(true);
// Fire event.
window->appEvent.trigger(devId, LoseFocus);
}
break;
case WM_ACTIVATEAPP:
if (wParam)
{
// Could extract current modifier state from windows.
_ModifierKeys = 0;
Input::setModifierKeys(_ModifierKeys);
}
break;
case WM_CLOSE:
if (window)
window->appEvent.trigger(devId,WindowClose);
// Force a quit if we're in play mode, otherwise there would be
// no way to stop a journal playback.(
if (Journal::IsPlaying())
Process::requestShutdown();
break;
case WM_TIMER: {
if (window)
window->appEvent.trigger(devId,Timer);
break;
}
case WM_DESTROY:{
// Only people who care about this currently are web plugins, because
// everyone else will just handle the WM_CLOSE app event.
if(window)
window->appEvent.trigger(devId,WindowDestroy);
break;
}
case WM_QUIT: {
// Quit indicates that we're not going to receive anymore Win32 messages.
// Therefore, it's appropriate to flag our event loop for exit as well,
// since we won't be getting any more messages.
Process::requestShutdown();
break;
}
// CodeReview - This is not used now and will incur an overhead for rendering
// since the renderThreadBlocked fix requires handling WM_PAINT and
// triggering the displayEvent. May need to revisit this at a later
// time if we want event driven rendering.
//case WM_PAINT: {
// // Checking for isOpen will keep us from generating an event
// // during the window creation process, which can cause problems
// // with the journaling.
// if (window && window->isOpen() && !winState.renderThreadBlocked )
// window->displayEvent.trigger(devId);
//}
case WM_SIZE: {
if (window && wParam != SIZE_MINIMIZED && !Journal::IsPlaying())
{
window->resizeEvent.trigger(window->getWindowId(), LOWORD(lParam),HIWORD(lParam));
// Consume all existing mouse events and those posted to our own dispatch queue
MSG msg;
PeekMessage( &msg, 0,WM_MOUSEFIRST,WM_MOUSELAST , PM_QS_POSTMESSAGE | PM_NOYIELD | PM_REMOVE );
RemoveMessages( NULL, WM_MOUSEMOVE, WM_MOUSEMOVE );
if( window->isMouseLocked())
{
RECT r;
GetWindowRect(window->getHWND(), &r);
S32 centerX = (r.right + r.left) >> 1;
S32 centerY = ((r.bottom + r.top) >> 1);
window->setCursorPosition( centerX, centerY );
// Set the CursorPos
SetCursorPos(centerX, centerY);
}
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Structure used to store Windows events for delayed dispatching
struct WinMessageQueue
{
public:
struct Message {
HWND hWnd;
UINT message;
WPARAM wparam;
WPARAM lparam;
};
WinMessageQueue()
{
VECTOR_SET_ASSOCIATION( _messageList );
}
bool isEmpty() {
return !_messageList.size();
}
void post(HWND hWnd,UINT message,WPARAM wparam,WPARAM lparam) {
Message msg;
msg.hWnd = hWnd;
msg.message = message;
msg.wparam = wparam;
msg.lparam = lparam;
_messageList.push_back(msg);
}
bool next(Message* msg) {
if (!_messageList.size())
return false;
*msg = _messageList.first();
_messageList.pop_front();
return true;
}
void remove(HWND hWnd, UINT msgBegin = -1, UINT msgEnd = -1) {
for (S32 i = 0; i < _messageList.size(); i++)
{
// Match Window
if( hWnd != NULL && _messageList[i].hWnd == hWnd)
_messageList.erase_fast(i--);
else if( msgBegin != -1 && msgEnd != -1 )
{
// CodeReview - Match Message Range [6/30/2007 justind]
//
// Word of caution : Use this only if you know what you're doing.
// I cannot be responsible for you blowing your leg off destroying
// a bunch of messages you didn't intend to if you specify a ridiculous
// range of messages values.
//
// To filter a single message, pass the message as the begin and end.
if( _messageList[i].message >= msgBegin && _messageList[i].message <= msgEnd )
_messageList.erase_fast(i--);
}
}
}
private:
Vector<Message> _messageList;
};
static WinMessageQueue _MessageQueue;
void RemoveMessages(HWND hWnd,UINT msgBegin,WPARAM msgEnd )
{
_MessageQueue.remove( hWnd, msgBegin, msgEnd );
}
// Dispatch the window event, or queue up for later
void Dispatch(DispatchType type,HWND hWnd,UINT message,WPARAM wparam,WPARAM lparam)
{
// If the message queue is not empty, then we'll need to delay
// this dispatch in order to preserve message order.
if (type == DelayedDispatch || !_MessageQueue.isEmpty())
_MessageQueue.post(hWnd,message,wparam,lparam);
else
_dispatch(hWnd,message,wparam,lparam);
}
// Dispatch next even in the queue
bool DispatchNext()
{
WinMessageQueue::Message msg;
if (!_MessageQueue.next(&msg))
return false;
_dispatch(msg.hWnd,msg.message,msg.wparam,msg.lparam);
return true;
}
// Remove events from the queue
void DispatchRemove(HWND hWnd)
{
_MessageQueue.remove(hWnd);
}

View file

@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINDOWMANAGER_WIN32_WINDISPATCH_H_
#define _WINDOWMANAGER_WIN32_WINDISPATCH_H_
//
// *** This header requires that Window.h be included before this.
//
/// Some events must be processed immediately, and others can or should be
/// processed later. This enum allows us to distinguish between the two
/// types.
enum DispatchType {
DelayedDispatch,
ImmediateDispatch,
};
/// Dispatch the event into the journaling system.
///
/// Dispatch Win32 events into the journaling system. To avoid problems
/// with journaling, events should normally use the DelayedDispatch type.
///
/// Delayed events are pushed onto a queue for later processing by DispatchNext().
void Dispatch(DispatchType,HWND hWnd,UINT message,WPARAM wparam,WPARAM lparam);
/// Remove messages from the event queue, matching a msg value range or hWnd
///
/// If no filter is specified, either HWND or MessageRange, nothing will be removed
/// You may not match HWND and MsgRange both, currently.
///
/// Message Range is calculated as follows.
/// @li Both Begin and End are specified as message values, ex WM_MOUSEMOVE
/// @li Specifying an identical set of begin/end will remove all messages matching that message value (WM_MOUSEMOVE)
/// @li If you specify a range it will remove from that beginning value through the end value
///
/// @note : The range is useful because on windows messages declared such that you can filter a block of
/// messages just by specifying the beginning value and end.
/// ex. WM_MOUSEFIRST,WM_MOUSELAST range will match all mouse messages.
///
/// @param hWnd The HWND to filter by, this cannot be combined with a msg range filter currently
/// @param msgBegin The beginning msg value to filter from
/// @param msgEnd The ending msg value to filter to
void RemoveMessages(HWND hWnd,UINT msgBegin,UINT msgEnd );
/// Dispatch the next event in the delayed dispatch queue.
/// This function should be called outside of any journaled calls.
/// Returns true if an event was dispatched.
bool DispatchNext();
/// Remove events related to the window from the dispatch queue.
void DispatchRemove(HWND hWnd);
#endif

View file

@ -0,0 +1,370 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "windowManager/windowInputGenerator.h"
#include "windowManager/platformWindow.h"
#include "sim/actionMap.h"
#include "component/interfaces/IProcessInput.h"
extern InputModifiers convertModifierBits(const U32 in);
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
WindowInputGenerator::WindowInputGenerator( PlatformWindow *window ) :
mWindow(window),
mInputController(NULL),
mLastCursorPos(0,0),
mClampToWindow(true),
mPixelsPerMickey(1.0f),
mNotifyPosition(true),
mFocused(false)
{
AssertFatal(mWindow, "NULL PlatformWindow on WindowInputGenerator creation");
#ifdef TORQUE_OS_XENON
mFocused = true;
#endif
if (mWindow->getOffscreenRender())
mFocused = true;
mWindow->appEvent.notify(this, &WindowInputGenerator::handleAppEvent);
mWindow->mouseEvent.notify(this, &WindowInputGenerator::handleMouseMove);
mWindow->wheelEvent.notify(this, &WindowInputGenerator::handleMouseWheel);
mWindow->buttonEvent.notify(this, &WindowInputGenerator::handleMouseButton);
mWindow->keyEvent.notify(this, &WindowInputGenerator::handleKeyboard);
mWindow->charEvent.notify(this, &WindowInputGenerator::handleCharInput);
// We also want to subscribe to input events.
Input::smInputEvent.notify(this, &WindowInputGenerator::handleInputEvent);
}
WindowInputGenerator::~WindowInputGenerator()
{
if( mWindow )
{
mWindow->mouseEvent.remove(this, &WindowInputGenerator::handleMouseMove);
mWindow->buttonEvent.remove(this, &WindowInputGenerator::handleMouseButton);
mWindow->wheelEvent.remove(this, &WindowInputGenerator::handleMouseWheel);
mWindow->keyEvent.remove(this, &WindowInputGenerator::handleKeyboard);
mWindow->charEvent.remove(this, &WindowInputGenerator::handleCharInput);
mWindow->appEvent.remove(this, &WindowInputGenerator::handleAppEvent);
}
Input::smInputEvent.remove(this, &WindowInputGenerator::handleInputEvent);
}
//-----------------------------------------------------------------------------
// Process an input event and pass it on.
// Respect the action map.
//-----------------------------------------------------------------------------
void WindowInputGenerator::generateInputEvent( InputEventInfo &inputEvent )
{
if( !mInputController || !mFocused )
return;
// Give the ActionMap first shot.
if (ActionMap::handleEventGlobal(&inputEvent))
return;
if( mInputController->processInputEvent( inputEvent ) )
return;
// If we get here we failed to process it with anything prior... so let
// the ActionMap handle it.
ActionMap::handleEvent(&inputEvent);
}
//-----------------------------------------------------------------------------
// Mouse Events
//-----------------------------------------------------------------------------
void WindowInputGenerator::handleMouseMove( WindowId did, U32 modifier, S32 x, S32 y, bool isRelative )
{
if( !mInputController || !mFocused )
return;
// jddTODO : Clean this up
// CodeReview currently the Torque GuiCanvas deals with mouse input
// as relative movement, even when the cursor is visible. Because
// of this there is an asinine bit of code in there that manages
// updating the cursor position on the class based on relative movement.
// Because of this we always have to generate and send off for processing
// relative events, even if the mouse is not locked.
// I'm considering removing this in the Canvas refactor, thoughts? [7/6/2007 justind]
// Generate a base Movement along and Axis event
InputEventInfo event;
event.deviceType = MouseDeviceType;
event.deviceInst = 0;
event.objType = SI_AXIS;
event.modifier = convertModifierBits(modifier);
event.ascii = 0;
// Generate delta movement along each axis
Point2F cursDelta;
if(isRelative)
{
cursDelta.x = F32(x) * mPixelsPerMickey;
cursDelta.y = F32(y) * mPixelsPerMickey;
}
else
{
cursDelta.x = F32(x - mLastCursorPos.x);
cursDelta.y = F32(y - mLastCursorPos.y);
}
// If X axis changed, generate a relative event
if(mFabs(cursDelta.x) > 0.1)
{
event.objInst = SI_XAXIS;
event.action = SI_MOVE;
event.fValue = cursDelta.x;
generateInputEvent(event);
}
// If Y axis changed, generate a relative event
if(mFabs(cursDelta.y) > 0.1)
{
event.objInst = SI_YAXIS;
event.action = SI_MOVE;
event.fValue = cursDelta.y;
generateInputEvent(event);
}
// CodeReview : If we're not relative, pass along a positional update
// so that the canvas can update it's internal cursor tracking
// point. [7/6/2007 justind]
if( !isRelative )
{
if( mClampToWindow )
{
Point2I winExtent = mWindow->getClientExtent();
x = mClampF(x, 0.0f, F32(winExtent.x - 1));
y = mClampF(y, 0.0f, F32(winExtent.y - 1));
}
// When the window gains focus, we send a cursor position event
if( mNotifyPosition )
{
mNotifyPosition = false;
// We use SI_MAKE to signify that the position is being set, not relatively moved.
event.action = SI_MAKE;
// X Axis
event.objInst = SI_XAXIS;
event.fValue = (F32)x;
generateInputEvent(event);
// Y Axis
event.objInst = SI_YAXIS;
event.fValue = (F32)y;
generateInputEvent(event);
}
mLastCursorPos = Point2I(x,y);
}
else
{
mLastCursorPos += Point2I(x,y);
mNotifyPosition = true;
}
}
void WindowInputGenerator::handleMouseButton( WindowId did, U32 modifiers, U32 action, U16 button )
{
if( !mInputController || !mFocused )
return;
InputEventInfo event;
event.deviceType = MouseDeviceType;
event.deviceInst = 0;
event.objType = SI_BUTTON;
event.objInst = (InputObjectInstances)(KEY_BUTTON0 + button);
event.modifier = convertModifierBits(modifiers);
event.ascii = 0;
event.action = (action==IA_MAKE) ? SI_MAKE : SI_BREAK;
event.fValue = (action==IA_MAKE) ? 1.0 : 0.0;
generateInputEvent(event);
}
void WindowInputGenerator::handleMouseWheel( WindowId did, U32 modifiers, S32 wheelDeltaX, S32 wheelDeltaY )
{
if( !mInputController || !mFocused )
return;
InputEventInfo event;
event.deviceType = MouseDeviceType;
event.deviceInst = 0;
event.objType = SI_AXIS;
event.modifier = convertModifierBits(modifiers);
event.ascii = 0;
event.action = SI_MOVE;
if( wheelDeltaY ) // Vertical
{
event.objInst = SI_ZAXIS;
event.fValue = (F32)wheelDeltaY;
generateInputEvent(event);
}
if( wheelDeltaX ) // Horizontal
{
event.objInst = SI_RZAXIS;
event.fValue = (F32)wheelDeltaX;
generateInputEvent(event);
}
}
//-----------------------------------------------------------------------------
// Key/Character Input
//-----------------------------------------------------------------------------
void WindowInputGenerator::handleCharInput( WindowId did, U32 modifier, U16 key )
{
if( !mInputController || !mFocused )
return;
InputEventInfo event;
event.deviceType = KeyboardDeviceType;
event.deviceInst = 0;
event.objType = SI_KEY;
event.objInst = KEY_NULL;
event.modifier = convertModifierBits(modifier);
event.ascii = key;
event.action = SI_MAKE;
event.fValue = 1.0;
generateInputEvent(event);
event.action = SI_BREAK;
event.fValue = 0.f;
generateInputEvent(event);
}
void WindowInputGenerator::handleKeyboard( WindowId did, U32 modifier, U32 action, U16 key )
{
if( !mInputController || !mFocused )
return;
InputEventInfo event;
event.deviceType = KeyboardDeviceType;
event.deviceInst = 0;
event.objType = SI_KEY;
event.objInst = (InputObjectInstances)key;
event.modifier = convertModifierBits(modifier);
event.ascii = 0;
switch(action)
{
case IA_MAKE:
event.action = SI_MAKE;
event.fValue = 1.f;
break;
case IA_REPEAT:
event.action = SI_REPEAT;
event.fValue = 1.f;
break;
case IA_BREAK:
event.action = SI_BREAK;
event.fValue = 0.f;
break;
// If we encounter an unknown don't submit the event.
default:
//Con::warnf("GuiCanvas::handleKeyboard - got an unknown action type %d!", action);
return;
}
generateInputEvent(event);
}
//-----------------------------------------------------------------------------
// Raw input
//-----------------------------------------------------------------------------
void WindowInputGenerator::handleInputEvent( U32 deviceInst,F32 fValue, U16 deviceType, U16 objType, U16 ascii, U16 objInst, U8 action, U8 modifier )
{
// Skip it if we don't have focus.
if(!mInputController || !mFocused)
return;
// Convert to an InputEventInfo and pass it around for processing.
InputEventInfo event;
event.deviceInst = deviceInst;
event.fValue = fValue;
event.deviceType = (InputDeviceTypes)deviceType;
event.objType = (InputEventType)objType;
event.ascii = ascii;
event.objInst = (InputObjectInstances)objInst;
event.action = (InputActionType)action;
event.modifier = (InputModifiers)modifier;
generateInputEvent(event);
}
//-----------------------------------------------------------------------------
// Window Events
//-----------------------------------------------------------------------------
void WindowInputGenerator::handleAppEvent( WindowId did, S32 event )
{
if(event == LoseFocus)
{
// Fire all breaks; this will prevent issues with dangling keys.
ActionMap::clearAllBreaks();
mFocused = false;
}
else if(event == GainFocus)
{
// Set an update flag to notify the consumer of the absolute mouse position next move
mNotifyPosition = true;
mFocused = true;
}
// always focused with offscreen rendering
if (mWindow->getOffscreenRender())
mFocused = true;
}
//-----------------------------------------------------------------------------
// Character Input Mapping
//-----------------------------------------------------------------------------
bool WindowInputGenerator::wantAsKeyboardEvent( U32 modifiers, U32 keyCode )
{
// Disallow translation on keys that are bound in the global action map.
return ActionMap::getGlobalMap()->isAction(
KeyboardDeviceType,
0,
modifiers,
keyCode
);
}

View file

@ -0,0 +1,77 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINDOW_INPUTGENERATOR_H_
#define _WINDOW_INPUTGENERATOR_H_
#ifndef _PLATFORMINPUT_H_
#include "platform/platformInput.h"
#endif
#ifndef _MPOINT2_H_
#include "math/mPoint2.h"
#endif
class IProcessInput;
class PlatformWindow;
class WindowInputGenerator
{
bool mNotifyPosition;
protected:
PlatformWindow *mWindow;
IProcessInput *mInputController;
Point2I mLastCursorPos;
bool mClampToWindow;
bool mFocused; ///< We store this off to avoid polling the OS constantly
/// This is the scale factor which relates mouse movement in pixels
/// (one unit of mouse movement is a mickey) to units in the GUI.
F32 mPixelsPerMickey;
// Event Handlers
void handleMouseButton(WindowId did, U32 modifier, U32 action, U16 button);
void handleMouseWheel (WindowId did, U32 modifier, S32 wheelDeltaX, S32 wheelDeltaY);
void handleMouseMove (WindowId did, U32 modifier, S32 x, S32 y, bool isRelative);
void handleKeyboard (WindowId did, U32 modifier, U32 action, U16 key);
void handleCharInput (WindowId did, U32 modifier, U16 key);
void handleAppEvent (WindowId did, S32 event);
void handleInputEvent (U32 deviceInst,F32 fValue, U16 deviceType, U16 objType, U16 ascii, U16 objInst, U8 action, U8 modifier);
void generateInputEvent( InputEventInfo &inputEvent );
public:
WindowInputGenerator( PlatformWindow *window );
virtual ~WindowInputGenerator();
void setInputController( IProcessInput *inputController ) { mInputController = inputController; };
/// Returns true if the given keypress event should be send as a raw keyboard
/// event even if it maps to a character input event.
bool wantAsKeyboardEvent( U32 modifiers, U32 key );
};
#endif // _WINDOW_INPUTGENERATOR_H_