mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-11 14:44:36 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
38
Engine/source/platformWin32/VFSRes.h
Normal file
38
Engine/source/platformWin32/VFSRes.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by zipCat.rc
|
||||
//
|
||||
#define IDR_ZIPFILE 500
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 501
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
81
Engine/source/platformWin32/cardProfile.cpp
Normal file
81
Engine/source/platformWin32/cardProfile.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "core/strings/stringFunctions.h"
|
||||
#include "console/console.h"
|
||||
#include "platformWin32/platformWin32.h"
|
||||
|
||||
|
||||
void initDisplayDeviceInfo()
|
||||
{
|
||||
Con::printf( "Reading Display Device information..." );
|
||||
|
||||
U8 i = 0;
|
||||
|
||||
DISPLAY_DEVICEA ddData;
|
||||
ddData.cb = sizeof( DISPLAY_DEVICEA );
|
||||
|
||||
// Search for the primary display adapter, because that is what the rendering
|
||||
// context will get created on.
|
||||
while( EnumDisplayDevicesA( NULL, i, &ddData, 0 ) != 0 )
|
||||
{
|
||||
// If we find the primary display adapter, break out
|
||||
if( ddData.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE )
|
||||
break;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
Con::printf( " Primary Display Device Found:" );
|
||||
|
||||
// Ok, now we have the primary display device. Parse the device information.
|
||||
char ven[9];
|
||||
char dev[9];
|
||||
|
||||
ven[8] = dev[8] = '\0';
|
||||
|
||||
// It may seem a bit silly here to cast, but there are two implimentations in Platform.h
|
||||
// This usage is the "const" version...
|
||||
char *pos = dStrstr( ddData.DeviceID, (const char *)"VEN_");
|
||||
|
||||
dStrncpy( ven, ( pos ) ? pos : "VEN_0000", 8 );
|
||||
|
||||
Con::printf( " Vendor Id: %s", ven );
|
||||
|
||||
pos = dStrstr( ddData.DeviceID, (const char *)"DEV_" );
|
||||
|
||||
dStrncpy( dev, ( pos ) ? pos : "DEV_0000", 8 );
|
||||
|
||||
Con::printf( " Device Id: %s", dev );
|
||||
|
||||
// We now have the information, set them to console variables so we can parse
|
||||
// the file etc in script using getField and so on.
|
||||
Con::setVariable( "$PCI_VEN", ven );
|
||||
Con::setVariable( "$PCI_DEV", dev );
|
||||
}
|
||||
|
||||
ConsoleFunction( initDisplayDeviceInfo, void, 1, 1, "()"
|
||||
"@brief Initializes variables that track device and vendor information/IDs\n\n"
|
||||
"@ingroup Rendering")
|
||||
{
|
||||
initDisplayDeviceInfo();
|
||||
}
|
||||
171
Engine/source/platformWin32/menus/menuBarWin32.cpp
Normal file
171
Engine/source/platformWin32/menus/menuBarWin32.cpp
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platform/menus/menuBar.h"
|
||||
#include "platform/menus/popupMenu.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "windowManager/platformWindowMgr.h"
|
||||
#include "windowManager/win32/win32Window.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Platform Data
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// class PlatformMenuBarData
|
||||
// {
|
||||
//
|
||||
// };
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MenuBar Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MenuBar::createPlatformPopupMenuData()
|
||||
{
|
||||
// mData = new PlatformMenuBarData;
|
||||
|
||||
// [tom, 6/4/2007] Nothing currently needed for win32
|
||||
mData = NULL;
|
||||
}
|
||||
|
||||
void MenuBar::deletePlatformPopupMenuData()
|
||||
{
|
||||
// SAFE_DELETE(mData);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MenuBar::updateMenuBar(PopupMenu *menu /* = NULL */)
|
||||
{
|
||||
if(! isAttachedToCanvas())
|
||||
return;
|
||||
|
||||
if(menu == NULL)
|
||||
{
|
||||
// [tom, 6/4/2007] Kludgetastic
|
||||
GuiCanvas *oldCanvas = mCanvas;
|
||||
S32 pos = -1;
|
||||
PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(0));
|
||||
if(mnu)
|
||||
pos = mnu->getPosOnMenuBar();
|
||||
|
||||
removeFromCanvas();
|
||||
attachToCanvas(oldCanvas, pos);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
menu->removeFromMenuBar();
|
||||
SimSet::iterator itr = find(begin(), end(), menu);
|
||||
if(itr == end())
|
||||
return;
|
||||
|
||||
menu->attachToMenuBar(mCanvas, itr - begin());
|
||||
|
||||
Win32Window *pWindow = dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow());
|
||||
if(pWindow == NULL)
|
||||
return;
|
||||
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
DrawMenuBar(hWindow);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MenuBar::attachToCanvas(GuiCanvas *owner, S32 pos)
|
||||
{
|
||||
if(owner == NULL || isAttachedToCanvas())
|
||||
return;
|
||||
|
||||
// This is set for popup menus in the onAttachToMenuBar() callback
|
||||
mCanvas = owner;
|
||||
|
||||
Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow());
|
||||
if(pWindow == NULL)
|
||||
return;
|
||||
|
||||
// Setup the native menu bar
|
||||
HMENU hWindowMenu = pWindow->getMenuHandle();
|
||||
if(hWindowMenu == NULL && !Journal::IsPlaying())
|
||||
{
|
||||
hWindowMenu = CreateMenu();
|
||||
if(hWindowMenu)
|
||||
{
|
||||
pWindow->setMenuHandle( hWindowMenu);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the items
|
||||
for(S32 i = 0;i < size();++i)
|
||||
{
|
||||
PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(i));
|
||||
if(mnu == NULL)
|
||||
{
|
||||
Con::warnf("MenuBar::attachToMenuBar - Non-PopupMenu object in set");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(mnu->isAttachedToMenuBar())
|
||||
mnu->removeFromMenuBar();
|
||||
|
||||
mnu->attachToMenuBar(owner, pos + i);
|
||||
}
|
||||
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
DrawMenuBar(hWindow);
|
||||
|
||||
}
|
||||
|
||||
void MenuBar::removeFromCanvas()
|
||||
{
|
||||
if(mCanvas == NULL || ! isAttachedToCanvas())
|
||||
return;
|
||||
|
||||
Win32Window *pWindow = dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow());
|
||||
if(pWindow == NULL)
|
||||
return;
|
||||
|
||||
// Setup the native menu bar
|
||||
HMENU hWindowMenu = pWindow->getMenuHandle();
|
||||
if(hWindowMenu == NULL)
|
||||
return;
|
||||
|
||||
// Add the items
|
||||
for(S32 i = 0;i < size();++i)
|
||||
{
|
||||
PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(i));
|
||||
if(mnu == NULL)
|
||||
{
|
||||
Con::warnf("MenuBar::removeFromMenuBar - Non-PopupMenu object in set");
|
||||
continue;
|
||||
}
|
||||
|
||||
mnu->removeFromMenuBar();
|
||||
}
|
||||
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
DrawMenuBar(hWindow);
|
||||
|
||||
mCanvas = NULL;
|
||||
}
|
||||
742
Engine/source/platformWin32/menus/popupMenuWin32.cpp
Normal file
742
Engine/source/platformWin32/menus/popupMenuWin32.cpp
Normal file
|
|
@ -0,0 +1,742 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/menus/popupMenu.h"
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "windowManager/platformWindowMgr.h"
|
||||
#include "windowManager/win32/win32Window.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
#include "sim/actionMap.h"
|
||||
#include "platform/platformInput.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Platform Menu Data
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct PlatformPopupMenuData
|
||||
{
|
||||
static U32 mLastPopupMenuID;
|
||||
static const U32 PopupMenuIDRange;
|
||||
|
||||
HMENU mMenu;
|
||||
U32 mMenuID;
|
||||
U32 mLastID;
|
||||
|
||||
Win32Window::AcceleratorList mAccelerators;
|
||||
Win32Window::AcceleratorList mDisabledAccelerators;
|
||||
|
||||
PlatformPopupMenuData()
|
||||
{
|
||||
mMenu = NULL;
|
||||
mMenuID = mLastPopupMenuID++;
|
||||
mLastID = 0;
|
||||
}
|
||||
|
||||
~PlatformPopupMenuData()
|
||||
{
|
||||
if(mMenu)
|
||||
DestroyMenu(mMenu);
|
||||
}
|
||||
|
||||
void insertAccelerator(EventDescriptor &desc, U32 id);
|
||||
void removeAccelerator(U32 id);
|
||||
void setAccelleratorEnabled(U32 id, bool enabled);
|
||||
};
|
||||
|
||||
U32 PlatformPopupMenuData::mLastPopupMenuID = 0;
|
||||
const U32 PlatformPopupMenuData::PopupMenuIDRange = 100;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PlatformPopupMenuData::insertAccelerator(EventDescriptor &desc, U32 id)
|
||||
{
|
||||
if(desc.eventType != SI_KEY)
|
||||
return;
|
||||
|
||||
Win32Window::AcceleratorList::iterator i;
|
||||
for(i = mAccelerators.begin();i != mAccelerators.end();++i)
|
||||
{
|
||||
if(i->mID == id)
|
||||
{
|
||||
// Update existing entry
|
||||
i->mDescriptor.eventType = desc.eventType;
|
||||
i->mDescriptor.eventCode = desc.eventCode;
|
||||
i->mDescriptor.flags = desc.flags;
|
||||
return;
|
||||
}
|
||||
|
||||
if(i->mDescriptor.eventType == desc.eventType && i->mDescriptor.eventCode == desc.eventCode && i->mDescriptor.flags == desc.flags)
|
||||
{
|
||||
// Already have a matching accelerator, don't add another one
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Win32Window::Accelerator accel;
|
||||
accel.mDescriptor = desc;
|
||||
accel.mID = id;
|
||||
mAccelerators.push_back(accel);
|
||||
}
|
||||
|
||||
void PlatformPopupMenuData::removeAccelerator(U32 id)
|
||||
{
|
||||
Win32Window::AcceleratorList::iterator i;
|
||||
for(i = mAccelerators.begin();i != mAccelerators.end();++i)
|
||||
{
|
||||
if(i->mID == id)
|
||||
{
|
||||
mAccelerators.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformPopupMenuData::setAccelleratorEnabled( U32 id, bool enabled )
|
||||
{
|
||||
Win32Window::AcceleratorList *src = NULL;
|
||||
Win32Window::AcceleratorList *dst = NULL;
|
||||
|
||||
if ( enabled )
|
||||
{
|
||||
src = &mDisabledAccelerators;
|
||||
dst = &mAccelerators;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = &mAccelerators;
|
||||
dst = &mDisabledAccelerators;
|
||||
}
|
||||
|
||||
Win32Window::AcceleratorList::iterator i;
|
||||
for ( i = src->begin(); i != src->end(); ++i )
|
||||
{
|
||||
if ( i->mID == id )
|
||||
{
|
||||
Win32Window::Accelerator tmp = *i;
|
||||
src->erase( i );
|
||||
dst->push_back( tmp );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PopupMenu::createPlatformPopupMenuData()
|
||||
{
|
||||
mData = new PlatformPopupMenuData;
|
||||
}
|
||||
|
||||
void PopupMenu::deletePlatformPopupMenuData()
|
||||
{
|
||||
SAFE_DELETE(mData);
|
||||
}
|
||||
void PopupMenu::createPlatformMenu()
|
||||
{
|
||||
mData->mMenu = mIsPopup ? CreatePopupMenu() : CreateMenu();
|
||||
AssertFatal(mData->mMenu, "Unable to create menu");
|
||||
|
||||
MENUINFO mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.fMask = MIM_MENUDATA;
|
||||
mi.dwMenuData = (ULONG_PTR)this;
|
||||
SetMenuInfo(mData->mMenu, &mi);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
S32 PopupMenu::insertItem(S32 pos, const char *title, const char* accelerator)
|
||||
{
|
||||
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
|
||||
bool isAttached = isAttachedToMenuBar();
|
||||
if(isAttached && pWindow == NULL)
|
||||
return -1;
|
||||
|
||||
MENUITEMINFOA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.fMask = MIIM_ID|MIIM_TYPE;
|
||||
mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1;
|
||||
mData->mLastID++;
|
||||
if(title && *title)
|
||||
mi.fType = MFT_STRING;
|
||||
else
|
||||
mi.fType = MFT_SEPARATOR;
|
||||
|
||||
char buf[1024];
|
||||
if(accelerator && *accelerator)
|
||||
{
|
||||
dSprintf(buf, sizeof(buf), "%s\t%s", title, accelerator);
|
||||
|
||||
if(isAttached)
|
||||
pWindow->removeAccelerators(mData->mAccelerators);
|
||||
|
||||
// Build entry for accelerator table
|
||||
EventDescriptor accelDesc;
|
||||
if(ActionMap::createEventDescriptor(accelerator, &accelDesc))
|
||||
mData->insertAccelerator(accelDesc, mi.wID);
|
||||
else
|
||||
Con::errorf("PopupMenu::insertItem - Could not create event descriptor for accelerator \"%s\"", accelerator);
|
||||
|
||||
if(isAttached)
|
||||
pWindow->addAccelerators(mData->mAccelerators);
|
||||
}
|
||||
else
|
||||
dSprintf(buf, sizeof(buf), "%s", title);
|
||||
|
||||
mi.dwTypeData = (LPSTR)buf;
|
||||
|
||||
if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi))
|
||||
{
|
||||
if(isAttached)
|
||||
{
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
DrawMenuBar(hWindow);
|
||||
}
|
||||
return mi.wID;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
S32 PopupMenu::insertSubMenu(S32 pos, const char *title, PopupMenu *submenu)
|
||||
{
|
||||
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
|
||||
bool isAttached = isAttachedToMenuBar();
|
||||
if(isAttached && pWindow == NULL)
|
||||
return -1;
|
||||
|
||||
for(S32 i = 0;i < mSubmenus->size();i++)
|
||||
{
|
||||
if(submenu == (*mSubmenus)[i])
|
||||
{
|
||||
Con::errorf("PopupMenu::insertSubMenu - Attempting to add submenu twice");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
MENUITEMINFOA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.fMask = MIIM_ID|MIIM_TYPE|MIIM_SUBMENU|MIIM_DATA;
|
||||
mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1;
|
||||
if(title && *title)
|
||||
mi.fType = MFT_STRING;
|
||||
else
|
||||
mi.fType = MFT_SEPARATOR;
|
||||
mi.dwTypeData = (LPSTR)title;
|
||||
mi.hSubMenu = submenu->mData->mMenu;
|
||||
mi.dwItemData = (ULONG_PTR)submenu;
|
||||
if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi))
|
||||
{
|
||||
mSubmenus->addObject(submenu);
|
||||
|
||||
if(isAttached)
|
||||
{
|
||||
pWindow->addAccelerators(submenu->mData->mAccelerators);
|
||||
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
DrawMenuBar(hWindow);
|
||||
}
|
||||
return mi.wID;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool PopupMenu::setItem(S32 pos, const char *title, const char* accelerator)
|
||||
{
|
||||
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
|
||||
bool isAttached = isAttachedToMenuBar();
|
||||
if(isAttached && pWindow == NULL)
|
||||
return false;
|
||||
|
||||
// Are we out of range?
|
||||
if ( pos >= getItemCount() )
|
||||
return false;
|
||||
|
||||
MENUITEMINFOA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.fMask = MIIM_TYPE;
|
||||
|
||||
if(title && *title)
|
||||
mi.fType = MFT_STRING;
|
||||
else
|
||||
mi.fType = MFT_SEPARATOR;
|
||||
|
||||
char buf[1024];
|
||||
if(accelerator && *accelerator)
|
||||
{
|
||||
dSprintf(buf, sizeof(buf), "%s\t%s", title, accelerator);
|
||||
|
||||
if(isAttached)
|
||||
pWindow->removeAccelerators(mData->mAccelerators);
|
||||
|
||||
// Build entry for accelerator table
|
||||
EventDescriptor accelDesc;
|
||||
if(ActionMap::createEventDescriptor(accelerator, &accelDesc))
|
||||
mData->insertAccelerator(accelDesc, pos);
|
||||
else
|
||||
Con::errorf("PopupMenu::setItem - Could not create event descriptor for accelerator \"%s\"", accelerator);
|
||||
|
||||
if(isAttached)
|
||||
pWindow->addAccelerators(mData->mAccelerators);
|
||||
}
|
||||
else
|
||||
dSprintf(buf, sizeof(buf), "%s", title);
|
||||
|
||||
mi.dwTypeData = (LPSTR)buf;
|
||||
|
||||
if(SetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi))
|
||||
{
|
||||
if(isAttached)
|
||||
{
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
DrawMenuBar(hWindow);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PopupMenu::removeItem(S32 itemPos)
|
||||
{
|
||||
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
|
||||
bool isAttached = isAttachedToMenuBar();
|
||||
if(isAttached && pWindow == NULL)
|
||||
return;
|
||||
|
||||
MENUITEMINFOA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.fMask = MIIM_DATA|MIIM_ID;
|
||||
if(GetMenuItemInfoA(mData->mMenu, itemPos, TRUE, &mi))
|
||||
{
|
||||
bool submenu = false;
|
||||
|
||||
// Update list of submenus if this is a submenu
|
||||
if(mi.fMask & MIIM_DATA)
|
||||
{
|
||||
PopupMenu *mnu = (PopupMenu *)mi.dwItemData;
|
||||
if( mnu != NULL )
|
||||
{
|
||||
if(isAttached)
|
||||
pWindow->removeAccelerators(mnu->mData->mAccelerators);
|
||||
mSubmenus->removeObject(mnu);
|
||||
|
||||
submenu = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(! submenu)
|
||||
{
|
||||
// Update accelerators if this has an accelerator and wasn't a sub menu
|
||||
for(S32 i = 0;i < mData->mAccelerators.size();++i)
|
||||
{
|
||||
if(mData->mAccelerators[i].mID == mi.wID)
|
||||
{
|
||||
if(isAttached)
|
||||
pWindow->removeAccelerators(mData->mAccelerators);
|
||||
|
||||
mData->mAccelerators.erase(i);
|
||||
|
||||
if(isAttached)
|
||||
pWindow->addAccelerators(mData->mAccelerators);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
RemoveMenu(mData->mMenu, itemPos, MF_BYPOSITION);
|
||||
|
||||
if(isAttached)
|
||||
{
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
DrawMenuBar(hWindow);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PopupMenu::enableItem( S32 pos, bool enable )
|
||||
{
|
||||
U32 flags = enable ? MF_ENABLED : MF_GRAYED;
|
||||
EnableMenuItem( mData->mMenu, pos, MF_BYPOSITION|flags );
|
||||
|
||||
// Update accelerators.
|
||||
|
||||
// NOTE: This really DOES need to happen. A disabled menu item
|
||||
// should not still have an accelerator mapped to it.
|
||||
//
|
||||
// Unfortunately, the editors currently only set menu items
|
||||
// enabled/disabled when the menu itself is selected which means our
|
||||
// accelerators would be out of synch.
|
||||
|
||||
/*
|
||||
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>( mCanvas->getPlatformWindow() ) : NULL;
|
||||
bool isAttached = isAttachedToMenuBar();
|
||||
if ( isAttached && pWindow == NULL )
|
||||
return;
|
||||
|
||||
MENUITEMINFOA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.fMask = MIIM_DATA|MIIM_ID;
|
||||
if ( !GetMenuItemInfoA( mData->mMenu, pos, TRUE, &mi) )
|
||||
return;
|
||||
|
||||
if ( isAttached )
|
||||
pWindow->removeAccelerators( mData->mAccelerators );
|
||||
|
||||
mData->setAccelleratorEnabled( mi.wID, enable );
|
||||
|
||||
if ( isAttached )
|
||||
pWindow->addAccelerators( mData->mAccelerators );
|
||||
*/
|
||||
}
|
||||
|
||||
void PopupMenu::checkItem(S32 pos, bool checked)
|
||||
{
|
||||
// U32 flags = checked ? MF_CHECKED : MF_UNCHECKED;
|
||||
// CheckMenuItem(mData->mMenu, pos, MF_BYPOSITION|flags);
|
||||
|
||||
MENUITEMINFOA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.fMask = MIIM_STATE;
|
||||
mi.fState = checked ? MFS_CHECKED : MFS_UNCHECKED;
|
||||
SetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi);
|
||||
}
|
||||
|
||||
void PopupMenu::checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos)
|
||||
{
|
||||
CheckMenuRadioItem(mData->mMenu, firstPos, lastPos, checkPos, MF_BYPOSITION);
|
||||
}
|
||||
|
||||
bool PopupMenu::isItemChecked(S32 pos)
|
||||
{
|
||||
MENUITEMINFOA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.fMask = MIIM_STATE;
|
||||
if(GetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi) && (mi.fState & MFS_CHECKED))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 PopupMenu::getItemCount()
|
||||
{
|
||||
return GetMenuItemCount( mData->mMenu );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool PopupMenu::canHandleID(U32 id)
|
||||
{
|
||||
for(S32 i = 0;i < mSubmenus->size();i++)
|
||||
{
|
||||
PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
|
||||
if(subM == NULL)
|
||||
continue;
|
||||
|
||||
if(subM->canHandleID(id))
|
||||
return true;
|
||||
}
|
||||
|
||||
if(id >= mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange &&
|
||||
id < (mData->mMenuID+1) * PlatformPopupMenuData::PopupMenuIDRange)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PopupMenu::handleSelect(U32 command, const char *text /* = NULL */)
|
||||
{
|
||||
// [tom, 8/20/2006] Pass off to a sub menu if it's for them
|
||||
for(S32 i = 0;i < mSubmenus->size();i++)
|
||||
{
|
||||
PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
|
||||
if(subM == NULL)
|
||||
continue;
|
||||
|
||||
if(subM->canHandleID(command))
|
||||
{
|
||||
return subM->handleSelect(command, text);
|
||||
}
|
||||
}
|
||||
|
||||
// [tom, 8/21/2006] Cheesey hack to find the position based on ID
|
||||
char buf[512];
|
||||
MENUITEMINFOA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.dwTypeData = NULL;
|
||||
|
||||
S32 numItems = GetMenuItemCount(mData->mMenu);
|
||||
S32 pos = -1;
|
||||
for(S32 i = 0;i < numItems;i++)
|
||||
{
|
||||
mi.fMask = MIIM_ID|MIIM_STRING|MIIM_STATE;
|
||||
if(GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi))
|
||||
{
|
||||
if(mi.wID == command)
|
||||
{
|
||||
if(text == NULL)
|
||||
{
|
||||
mi.dwTypeData = buf;
|
||||
mi.cch++;
|
||||
GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi);
|
||||
|
||||
// [tom, 5/11/2007] Don't do anything if the menu item is disabled
|
||||
if(mi.fState & MFS_DISABLED)
|
||||
return false;
|
||||
|
||||
text = StringTable->insert(mi.dwTypeData);
|
||||
}
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pos == -1)
|
||||
{
|
||||
Con::errorf("PopupMenu::handleSelect - Could not find menu item position for ID %d ... this shouldn't happen!", command);
|
||||
return false;
|
||||
}
|
||||
|
||||
// [tom, 8/20/2006] Wasn't handled by a submenu, pass off to script
|
||||
return dAtob(Con::executef(this, "onSelectItem", Con::getIntArg(pos), text ? text : ""));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PopupMenu::showPopup(GuiCanvas *owner, S32 x /* = -1 */, S32 y /* = -1 */)
|
||||
{
|
||||
if( owner == NULL )
|
||||
{
|
||||
Con::warnf("PopupMenu::showPopup - Invalid canvas supplied!");
|
||||
return;
|
||||
}
|
||||
|
||||
// [tom, 6/4/2007] showPopup() blocks until the menu is closed by the user,
|
||||
// so the canvas pointer is not needed beyond the scope of this function
|
||||
// when working with context menus. Setting mCanvas here will cause undesired
|
||||
// behavior in relation to the menu bar.
|
||||
|
||||
Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow());
|
||||
if(pWindow == NULL)
|
||||
return;
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
POINT p;
|
||||
if(x == -1 && y == -1)
|
||||
GetCursorPos(&p);
|
||||
else
|
||||
{
|
||||
p.x = x;
|
||||
p.y = y;
|
||||
ClientToScreen(hWindow, &p);
|
||||
}
|
||||
|
||||
winState.renderThreadBlocked = true;
|
||||
U32 opt = (int)TrackPopupMenu(mData->mMenu, TPM_NONOTIFY|TPM_RETURNCMD, p.x, p.y, 0, hWindow, NULL);
|
||||
if(opt > 0)
|
||||
handleSelect(opt, NULL);
|
||||
winState.renderThreadBlocked = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos, const char *title)
|
||||
{
|
||||
if(owner == NULL || isAttachedToMenuBar())
|
||||
return;
|
||||
|
||||
// This is set for sub-menus in the onAttachToMenuBar() callback
|
||||
mCanvas = owner;
|
||||
|
||||
Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow());
|
||||
if(pWindow == NULL)
|
||||
return;
|
||||
|
||||
HMENU hWindowMenu = pWindow->getMenuHandle();
|
||||
if(hWindowMenu == NULL)
|
||||
{
|
||||
hWindowMenu = CreateMenu();
|
||||
if(hWindowMenu)
|
||||
{
|
||||
pWindow->setMenuHandle( hWindowMenu);
|
||||
}
|
||||
}
|
||||
|
||||
MENUITEMINFOA mii;
|
||||
|
||||
mii.cbSize = sizeof(MENUITEMINFOA);
|
||||
|
||||
mii.fMask = MIIM_STRING|MIIM_DATA;
|
||||
mii.dwTypeData = (LPSTR)title;
|
||||
mii.fMask |= MIIM_ID;
|
||||
mii.wID = mData->mMenuID;
|
||||
mii.fMask |= MIIM_SUBMENU;
|
||||
mii.hSubMenu = mData->mMenu;
|
||||
mii.dwItemData = (ULONG_PTR)this;
|
||||
|
||||
InsertMenuItemA(hWindowMenu, pos, TRUE, &mii);
|
||||
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
DrawMenuBar(hWindow);
|
||||
|
||||
pWindow->addAccelerators(mData->mAccelerators);
|
||||
|
||||
// Add accelerators for sub menus
|
||||
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
|
||||
{
|
||||
PopupMenu *submenu = dynamic_cast<PopupMenu *>(*i);
|
||||
if(submenu == NULL)
|
||||
continue;
|
||||
|
||||
pWindow->addAccelerators(submenu->mData->mAccelerators);
|
||||
}
|
||||
|
||||
onAttachToMenuBar(owner, pos, title);
|
||||
}
|
||||
|
||||
// New version of above for use by MenuBar class. Do not use yet.
|
||||
void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos)
|
||||
{
|
||||
Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow());
|
||||
if(pWindow == NULL)
|
||||
return;
|
||||
|
||||
//When playing a journal, the system menu is not actually shown
|
||||
if (Journal::IsPlaying())
|
||||
{
|
||||
onAttachToMenuBar(owner, pos, mBarTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
HMENU hWindowMenu = pWindow->getMenuHandle();
|
||||
|
||||
MENUITEMINFOA mii;
|
||||
|
||||
mii.cbSize = sizeof(MENUITEMINFOA);
|
||||
|
||||
mii.fMask = MIIM_STRING|MIIM_DATA;
|
||||
mii.dwTypeData = (LPSTR)mBarTitle;
|
||||
mii.fMask |= MIIM_ID;
|
||||
mii.wID = mData->mMenuID;
|
||||
mii.fMask |= MIIM_SUBMENU;
|
||||
mii.hSubMenu = mData->mMenu;
|
||||
mii.dwItemData = (ULONG_PTR)this;
|
||||
|
||||
InsertMenuItemA(hWindowMenu, pos, TRUE, &mii);
|
||||
|
||||
pWindow->addAccelerators(mData->mAccelerators);
|
||||
|
||||
// Add accelerators for sub menus (have to do this here as it's platform specific)
|
||||
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
|
||||
{
|
||||
PopupMenu *submenu = dynamic_cast<PopupMenu *>(*i);
|
||||
if(submenu == NULL)
|
||||
continue;
|
||||
|
||||
pWindow->addAccelerators(submenu->mData->mAccelerators);
|
||||
}
|
||||
|
||||
onAttachToMenuBar(owner, pos, mBarTitle);
|
||||
}
|
||||
|
||||
void PopupMenu::removeFromMenuBar()
|
||||
{
|
||||
S32 pos = getPosOnMenuBar();
|
||||
if(pos == -1)
|
||||
return;
|
||||
|
||||
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
|
||||
if(pWindow == NULL)
|
||||
return;
|
||||
|
||||
HMENU hMenuHandle = pWindow->getMenuHandle();
|
||||
if(!hMenuHandle)
|
||||
return;
|
||||
|
||||
RemoveMenu(hMenuHandle, pos, MF_BYPOSITION);
|
||||
|
||||
HWND hWindow = pWindow->getHWND();
|
||||
|
||||
DrawMenuBar(hWindow);
|
||||
|
||||
pWindow->removeAccelerators(mData->mAccelerators);
|
||||
|
||||
// Remove accelerators for sub menus
|
||||
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
|
||||
{
|
||||
PopupMenu *submenu = dynamic_cast<PopupMenu *>(*i);
|
||||
if(submenu == NULL)
|
||||
continue;
|
||||
|
||||
pWindow->removeAccelerators(submenu->mData->mAccelerators);
|
||||
}
|
||||
|
||||
onRemoveFromMenuBar(mCanvas);
|
||||
}
|
||||
|
||||
S32 PopupMenu::getPosOnMenuBar()
|
||||
{
|
||||
if(mCanvas == NULL)
|
||||
return -1;
|
||||
|
||||
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
|
||||
if(pWindow == NULL)
|
||||
return -1;
|
||||
|
||||
HMENU hMenuHandle = pWindow->getMenuHandle();
|
||||
S32 numItems = GetMenuItemCount(hMenuHandle);
|
||||
S32 pos = -1;
|
||||
for(S32 i = 0;i < numItems;i++)
|
||||
{
|
||||
MENUITEMINFOA mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
mi.fMask = MIIM_DATA;
|
||||
if(GetMenuItemInfoA(hMenuHandle, i, TRUE, &mi))
|
||||
{
|
||||
if(mi.fMask & MIIM_DATA)
|
||||
{
|
||||
PopupMenu *mnu = (PopupMenu *)mi.dwItemData;
|
||||
if(mnu == this)
|
||||
{
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
337
Engine/source/platformWin32/minidump/winMiniDump.cpp
Normal file
337
Engine/source/platformWin32/minidump/winMiniDump.cpp
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
|
||||
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#include "platformWin32/minidump/winStackWalker.h"
|
||||
#include "core/fileio.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "console/console.h"
|
||||
#include "app/net/serverQuery.h"
|
||||
|
||||
#pragma pack(push,8)
|
||||
#include <DbgHelp.h>
|
||||
#include <time.h>
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
||||
extern Win32PlatState winState;
|
||||
|
||||
//Forward declarations for the dialog functions
|
||||
BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner);
|
||||
LPWORD lpwAlign(LPWORD lpIn);
|
||||
char gUserInput[4096];
|
||||
|
||||
//Console variables
|
||||
extern StringTableEntry gMiniDumpDir;
|
||||
extern StringTableEntry gMiniDumpUser;
|
||||
extern StringTableEntry gMiniDumpExec;
|
||||
extern StringTableEntry gMiniDumpParams;
|
||||
extern StringTableEntry gMiniDumpExecDir;
|
||||
|
||||
|
||||
char* dStrrstr(char* dst, const char* src, const char* findStr, char* replaceStr)
|
||||
{
|
||||
//see if str contains findStr, if not then return
|
||||
const char* findpos = strstr(src, findStr);
|
||||
if(!findpos)
|
||||
{
|
||||
strcpy(dst, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
//copy the new string to the buffer
|
||||
dst[0]='\0';
|
||||
strncat(dst, src, findpos-src);
|
||||
strcat(dst, replaceStr);
|
||||
const char* cur = findpos + strlen(findStr);
|
||||
strcat(dst, cur);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
// CreateMiniDump()
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
INT CreateMiniDump( LPEXCEPTION_POINTERS ExceptionInfo)
|
||||
{
|
||||
//Get any information we can from the user and store it in gUserInput
|
||||
try
|
||||
{
|
||||
while(ShowCursor(TRUE) < 0);
|
||||
DisplayMiniDumpDialog(winState.appInstance, winState.appWindow);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
//dSprintf(gUserInput, 4096, "The user could not enter a description of what was occurring.\n\n\n");
|
||||
}
|
||||
|
||||
//Build a Game, Date and Time stamped MiniDump folder
|
||||
time_t theTime;
|
||||
time(&theTime);
|
||||
tm* pLocalTime = localtime(&theTime);
|
||||
char crashFolder[2048];
|
||||
dSprintf(crashFolder, 2048, "%s_%02d.%02d_%02d.%02d.%02d",
|
||||
Platform::getExecutableName(),
|
||||
pLocalTime->tm_mon+1, pLocalTime->tm_mday,
|
||||
pLocalTime->tm_hour, pLocalTime->tm_min, pLocalTime->tm_sec);
|
||||
|
||||
//Builed the fully qualified MiniDump path
|
||||
char crashPath[2048];
|
||||
char fileName[2048];
|
||||
if(gMiniDumpDir==NULL)
|
||||
{
|
||||
dSprintf(crashPath, 2048, "%s/MiniDump/%s", Platform::getCurrentDirectory(), crashFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
dSprintf(crashPath, 2048, "%s/%s", gMiniDumpDir, crashFolder);
|
||||
}
|
||||
|
||||
dSprintf(fileName, 2048, "%s/Minidump.dmp",crashPath);
|
||||
if (!Platform::createPath (fileName))return false; //create the directory
|
||||
|
||||
//Save the minidump
|
||||
File fileObject;
|
||||
if(fileObject.open(fileName, File::Write) == File::Ok)
|
||||
{
|
||||
MINIDUMP_EXCEPTION_INFORMATION DumpExceptionInfo;
|
||||
DumpExceptionInfo.ThreadId = GetCurrentThreadId();
|
||||
DumpExceptionInfo.ExceptionPointers = ExceptionInfo;
|
||||
DumpExceptionInfo.ClientPointers = true;
|
||||
MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), (HANDLE)fileObject.getHandle(), MiniDumpNormal, &DumpExceptionInfo, NULL, NULL );
|
||||
fileObject.close();
|
||||
}
|
||||
|
||||
//copy over the log file
|
||||
char fromFile[2048];
|
||||
dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), "console.log" );
|
||||
dSprintf(fileName, 2048, "%s/console.log", crashPath);
|
||||
Con::setLogMode(3); //ensure that the log file is closed (so it can be copied)
|
||||
dPathCopy(fromFile, fileName, true);
|
||||
|
||||
//copy over the exe file
|
||||
char exeName[1024];
|
||||
dSprintf(exeName, 1024, Platform::getExecutableName());
|
||||
exeName[dStrlen(exeName)-4]=0;
|
||||
dSprintf(fromFile, 2048, "%s/%s.dll", Platform::getCurrentDirectory(), exeName );
|
||||
dSprintf(fileName, 2048, "%s/%s.dll", crashPath, exeName );
|
||||
dPathCopy(fromFile, fileName, true);
|
||||
|
||||
//copy over the pdb file
|
||||
char pdbName[1024];
|
||||
dStrcpy(pdbName, exeName);
|
||||
dStrncat(pdbName, ".pdb", 4);
|
||||
dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), pdbName );
|
||||
dSprintf(fileName, 2048, "%s/%s", crashPath, pdbName );
|
||||
dPathCopy(fromFile, fileName, true);
|
||||
|
||||
//save the call stack
|
||||
char traceBuffer[65536];
|
||||
traceBuffer[0] = 0;
|
||||
dGetStackTrace( traceBuffer, *static_cast<const CONTEXT *>(ExceptionInfo->ContextRecord) );
|
||||
|
||||
//save the user input and the call stack to a file
|
||||
char crashlogFile[2048];
|
||||
dSprintf(crashlogFile, 2048, "%s/crash.log", crashPath);
|
||||
if(fileObject.open(crashlogFile, File::Write) == File::Ok)
|
||||
{
|
||||
fileObject.write(strlen(gUserInput), gUserInput);
|
||||
fileObject.write(strlen(traceBuffer), traceBuffer);
|
||||
fileObject.close();
|
||||
}
|
||||
|
||||
//call the external program indicated in script
|
||||
if(gMiniDumpExec!= NULL)
|
||||
{
|
||||
//replace special variables in gMiniDumpParams
|
||||
if(gMiniDumpParams)
|
||||
{
|
||||
char updateParams[4096];
|
||||
char finalParams[4096];
|
||||
dStrrstr(finalParams, gMiniDumpParams, "%crashpath%", crashPath);
|
||||
dStrrstr(updateParams, finalParams, "%crashfolder%", crashFolder);
|
||||
dStrrstr(finalParams, updateParams, "%crashlog%", crashlogFile);
|
||||
ShellExecuteA(NULL, "", gMiniDumpExec, finalParams, gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShellExecuteA(NULL, "", gMiniDumpExec, "", gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
// MiniDumpDialogProc - Used By DisplayMiniDumpDialog
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
const int ID_TEXT=200;
|
||||
const int ID_USERTEXT=300;
|
||||
const int ID_DONE=400;
|
||||
BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
char text[128]= "";
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case WM_INITDIALOG :
|
||||
SetDlgItemTextA ( hwndDlg, ID_USERTEXT, text );
|
||||
return TRUE ;
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case ID_DONE:
|
||||
if( !GetDlgItemTextA(hwndDlg, ID_USERTEXT, gUserInput, 4096) ) gUserInput[0]='\0';
|
||||
strcat(gUserInput, "\n\n\n");
|
||||
EndDialog(hwndDlg, wParam);
|
||||
return TRUE;
|
||||
default:
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
// Helper function to DWORD align the Dialog Box components (Used in DisplayMiniDumpDialog()
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
LPWORD lpwAlign(LPWORD lpIn)
|
||||
{
|
||||
ULONG ul;
|
||||
|
||||
ul = (ULONG)lpIn;
|
||||
ul ++;
|
||||
ul >>=1;
|
||||
ul <<=1;
|
||||
return (LPWORD)ul;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
// Create the Dialog Box to get input from the user
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner)
|
||||
{
|
||||
HGLOBAL hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
|
||||
if (!hgbl) return -1;
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Define the dialog box
|
||||
//-----------------------------------------------------------------
|
||||
LPDLGTEMPLATE lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
|
||||
lpdt->style = WS_POPUP | WS_BORDER | DS_MODALFRAME | WS_CAPTION;
|
||||
lpdt->cdit = 3; // Number of controls
|
||||
lpdt->x = 100;
|
||||
lpdt->y = 100;
|
||||
lpdt->cx = 300;
|
||||
lpdt->cy = 90;
|
||||
|
||||
LPWORD lpw = (LPWORD)(lpdt + 1);
|
||||
*lpw++ = 0; // No menu
|
||||
*lpw++ = 0; // Predefined dialog box class (by default)
|
||||
|
||||
LPWSTR lpwsz = (LPWSTR)lpw;
|
||||
int nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "MiniDump Crash Report", -1, lpwsz, 50);
|
||||
lpw += nchar;
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Define a static text message
|
||||
//-----------------------------------------------------------------
|
||||
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
|
||||
LPDLGITEMTEMPLATE lpdit = (LPDLGITEMTEMPLATE)lpw;
|
||||
lpdit->x = 10;
|
||||
lpdit->y = 10;
|
||||
lpdit->cx = 290;
|
||||
lpdit->cy = 10;
|
||||
lpdit->id = ID_TEXT; // Text identifier
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
|
||||
|
||||
lpw = (LPWORD)(lpdit + 1);
|
||||
*lpw++ = 0xFFFF;
|
||||
*lpw++ = 0x0082; // Static class
|
||||
|
||||
LPSTR msg = "The program has crashed. Please describe what was happening:";
|
||||
for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*msg++;);
|
||||
lpw = (LPWORD)lpwsz;
|
||||
|
||||
*lpw++ = 0; // No creation data
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Define a DONE button
|
||||
//-----------------------------------------------------------------
|
||||
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
|
||||
lpdit = (LPDLGITEMTEMPLATE)lpw;
|
||||
lpdit->x = 265;
|
||||
lpdit->y = 75;
|
||||
lpdit->cx = 25;
|
||||
lpdit->cy = 12;
|
||||
lpdit->id = ID_DONE; // OK button identifier
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;// | BS_DEFPUSHBUTTON;
|
||||
|
||||
lpw = (LPWORD)(lpdit + 1);
|
||||
*lpw++ = 0xFFFF;
|
||||
*lpw++ = 0x0080; // Button class
|
||||
|
||||
lpwsz = (LPWSTR)lpw;
|
||||
nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Done", -1, lpwsz, 50);
|
||||
lpw += nchar;
|
||||
*lpw++ = 0; // No creation data
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Define a text entry message
|
||||
//-----------------------------------------------------------------
|
||||
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
|
||||
lpdit = (LPDLGITEMTEMPLATE)lpw;
|
||||
lpdit->x = 10;
|
||||
lpdit->y = 22;
|
||||
lpdit->cx = 280;
|
||||
lpdit->cy = 50;
|
||||
lpdit->id = ID_USERTEXT; // Text identifier
|
||||
lpdit->style = ES_LEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
|
||||
|
||||
lpw = (LPWORD)(lpdit + 1);
|
||||
*lpw++ = 0xFFFF;
|
||||
*lpw++ = 0x0081; // Text edit class
|
||||
|
||||
*lpw++ = 0; // No creation data
|
||||
|
||||
|
||||
|
||||
GlobalUnlock(hgbl);
|
||||
LRESULT ret = DialogBoxIndirect( hinst,
|
||||
(LPDLGTEMPLATE)hgbl,
|
||||
hwndOwner,
|
||||
(DLGPROC)MiniDumpDialogProc);
|
||||
GlobalFree(hgbl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
825
Engine/source/platformWin32/minidump/winStackWalker.cpp
Normal file
825
Engine/source/platformWin32/minidump/winStackWalker.cpp
Normal file
|
|
@ -0,0 +1,825 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
// Sourced from http://www.codeproject.com/threads/StackWalker.asp
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
#include "winStackWalker.h"
|
||||
|
||||
#undef UNICODE
|
||||
|
||||
#include <tchar.h>
|
||||
#include <Tlhelp32.h>
|
||||
#include <stdio.h>
|
||||
#include <psapi.h>
|
||||
|
||||
#pragma comment(lib, "version.lib") // for "VerQueryValue"
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
#pragma comment(lib, "psapi.lib")
|
||||
|
||||
|
||||
// secure-CRT_functions are only available starting with VC8
|
||||
#if _MSC_VER < 1400
|
||||
#define strcpy_s strcpy
|
||||
#define strcat_s(dst, len, src) strcat(dst, src)
|
||||
#define _snprintf_s _snprintf
|
||||
#define _tcscat_s _tcscat
|
||||
#endif
|
||||
|
||||
// Entry for each Callstack-Entry
|
||||
const int STACKWALK_MAX_NAMELEN = 1024; // max name length for symbols
|
||||
struct CallstackEntry
|
||||
{
|
||||
DWORD64 offset; // if 0, we have no valid entry
|
||||
char name[STACKWALK_MAX_NAMELEN];
|
||||
char undName[STACKWALK_MAX_NAMELEN];
|
||||
char undFullName[STACKWALK_MAX_NAMELEN];
|
||||
DWORD64 offsetFromSmybol;
|
||||
DWORD64 offsetFromLine;
|
||||
DWORD lineNumber;
|
||||
char lineFileName[STACKWALK_MAX_NAMELEN];
|
||||
DWORD symType;
|
||||
const char * symTypeString;
|
||||
char moduleName[STACKWALK_MAX_NAMELEN];
|
||||
DWORD64 baseOfImage;
|
||||
char loadedImageName[STACKWALK_MAX_NAMELEN];
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
// Implementation of platform functions
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
void dGetStackTrace(char * traceBuffer, CONTEXT const & ContextRecord)
|
||||
{
|
||||
StackWalker sw;
|
||||
sw.setOutputBuffer(traceBuffer);
|
||||
sw.ShowCallstack(GetCurrentThread(), ContextRecord);
|
||||
sw.setOutputBuffer(NULL);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//Constructor
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
StackWalker::StackWalker(DWORD options, LPCSTR szSymPath)
|
||||
: m_dwProcessId(GetCurrentProcessId()),
|
||||
m_hProcess(GetCurrentProcess()),
|
||||
m_options(options),
|
||||
m_modulesLoaded(false),
|
||||
m_pOutputBuffer(NULL)
|
||||
{
|
||||
if (szSymPath != NULL)
|
||||
{
|
||||
m_szSymPath = _strdup(szSymPath);
|
||||
m_options |= SymBuildPath;
|
||||
}
|
||||
else
|
||||
m_szSymPath = NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//Destructor
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
StackWalker::~StackWalker()
|
||||
{
|
||||
SymCleanup(m_hProcess);
|
||||
if (m_szSymPath != NULL) free(m_szSymPath);
|
||||
m_szSymPath = NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//setOutputBuffer - Points the StackWalker at a buffer where it will write out any output. If this is set to NULL then the output
|
||||
// will only be sent to DebugString
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
void StackWalker::setOutputBuffer(char * buffer)
|
||||
{
|
||||
m_pOutputBuffer = buffer;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//OnOutput
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
void StackWalker::OnOutput(LPCSTR buffer)
|
||||
{
|
||||
OutputDebugStringA(buffer);
|
||||
if(m_pOutputBuffer) strcat(m_pOutputBuffer, buffer);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//Init
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
bool StackWalker::Init(LPCSTR szSymPath)
|
||||
{
|
||||
// SymInitialize
|
||||
if (szSymPath != NULL) m_szSymPath = _strdup(szSymPath);
|
||||
if (SymInitialize(m_hProcess, m_szSymPath, FALSE) == FALSE)
|
||||
{
|
||||
this->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
|
||||
}
|
||||
|
||||
DWORD symOptions = SymGetOptions();
|
||||
symOptions |= SYMOPT_LOAD_LINES;
|
||||
symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
|
||||
symOptions = SymSetOptions(symOptions);
|
||||
|
||||
char buf[STACKWALK_MAX_NAMELEN] = {0};
|
||||
if(SymGetSearchPath(m_hProcess, buf, STACKWALK_MAX_NAMELEN) == FALSE)
|
||||
{
|
||||
this->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
|
||||
}
|
||||
|
||||
char szUserName[1024] = {0};
|
||||
DWORD dwSize = 1024;
|
||||
GetUserNameA(szUserName, &dwSize);
|
||||
this->OnSymInit(buf, symOptions, szUserName);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//LoadModules
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
bool StackWalker::LoadModules()
|
||||
{
|
||||
if (m_modulesLoaded) return true;
|
||||
|
||||
// Build the sym-path:
|
||||
char *szSymPath = NULL;
|
||||
if ( (m_options & SymBuildPath) != 0)
|
||||
{
|
||||
const size_t nSymPathLen = 4096;
|
||||
szSymPath = (char*) malloc(nSymPathLen);
|
||||
if (szSymPath == NULL)
|
||||
{
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
return false;
|
||||
}
|
||||
szSymPath[0] = 0;
|
||||
// Now first add the (optional) provided sympath:
|
||||
if (m_szSymPath != NULL)
|
||||
{
|
||||
strcat_s(szSymPath, nSymPathLen, m_szSymPath);
|
||||
strcat_s(szSymPath, nSymPathLen, ";");
|
||||
}
|
||||
|
||||
strcat_s(szSymPath, nSymPathLen, ".;");
|
||||
|
||||
const size_t nTempLen = 1024;
|
||||
CHAR szTemp[nTempLen];
|
||||
// Now add the current directory:
|
||||
if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
|
||||
{
|
||||
szTemp[nTempLen-1] = 0;
|
||||
strcat_s(szSymPath, nSymPathLen, szTemp);
|
||||
strcat_s(szSymPath, nSymPathLen, ";");
|
||||
}
|
||||
|
||||
// Now add the path for the main-module:
|
||||
if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
|
||||
{
|
||||
szTemp[nTempLen-1] = 0;
|
||||
for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p)
|
||||
{
|
||||
// locate the rightmost path separator
|
||||
if ( (*p == '\\') || (*p == '/') || (*p == ':') )
|
||||
{
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
} // for (search for path separator...)
|
||||
if (strlen(szTemp) > 0)
|
||||
{
|
||||
strcat_s(szSymPath, nSymPathLen, szTemp);
|
||||
strcat_s(szSymPath, nSymPathLen, ";");
|
||||
}
|
||||
}
|
||||
if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
|
||||
{
|
||||
szTemp[nTempLen-1] = 0;
|
||||
strcat_s(szSymPath, nSymPathLen, szTemp);
|
||||
strcat_s(szSymPath, nSymPathLen, ";");
|
||||
}
|
||||
if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
|
||||
{
|
||||
szTemp[nTempLen-1] = 0;
|
||||
strcat_s(szSymPath, nSymPathLen, szTemp);
|
||||
strcat_s(szSymPath, nSymPathLen, ";");
|
||||
}
|
||||
if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
|
||||
{
|
||||
szTemp[nTempLen-1] = 0;
|
||||
strcat_s(szSymPath, nSymPathLen, szTemp);
|
||||
strcat_s(szSymPath, nSymPathLen, ";");
|
||||
// also add the "system32"-directory:
|
||||
strcat_s(szTemp, nTempLen, "\\system32");
|
||||
strcat_s(szSymPath, nSymPathLen, szTemp);
|
||||
strcat_s(szSymPath, nSymPathLen, ";");
|
||||
}
|
||||
|
||||
if ( (m_options & SymBuildPath) != 0 )
|
||||
{
|
||||
if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
|
||||
{
|
||||
szTemp[nTempLen-1] = 0;
|
||||
strcat_s(szSymPath, nSymPathLen, "SRV*");
|
||||
strcat_s(szSymPath, nSymPathLen, szTemp);
|
||||
strcat_s(szSymPath, nSymPathLen, "\\websymbols");
|
||||
strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
|
||||
}
|
||||
else
|
||||
strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
|
||||
}
|
||||
}
|
||||
|
||||
// First Init the whole stuff...
|
||||
bool bRet = Init(szSymPath);
|
||||
if (szSymPath != NULL)
|
||||
{
|
||||
free(szSymPath);
|
||||
szSymPath = NULL;
|
||||
}
|
||||
if (bRet == false)
|
||||
{
|
||||
this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
|
||||
SetLastError(ERROR_DLL_INIT_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(GetModuleListTH32(m_hProcess, m_dwProcessId))
|
||||
{
|
||||
m_modulesLoaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// then try psapi
|
||||
if(GetModuleListPSAPI(m_hProcess))
|
||||
{
|
||||
m_modulesLoaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//LoadModule
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
DWORD StackWalker::LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
|
||||
{
|
||||
CHAR *szImg = _strdup(img);
|
||||
CHAR *szMod = _strdup(mod);
|
||||
DWORD result = ERROR_SUCCESS;
|
||||
if ( (szImg == NULL) || (szMod == NULL) )
|
||||
result = ERROR_NOT_ENOUGH_MEMORY;
|
||||
else
|
||||
{
|
||||
if (SymLoadModule64(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
|
||||
result = GetLastError();
|
||||
}
|
||||
ULONGLONG fileVersion = 0;
|
||||
if(szImg != NULL)
|
||||
{
|
||||
// try to retrieve the file-version:
|
||||
if ( (m_options & StackWalker::RetrieveFileVersion) != 0)
|
||||
{
|
||||
VS_FIXEDFILEINFO *fInfo = NULL;
|
||||
DWORD dwHandle;
|
||||
DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
|
||||
if (dwSize > 0)
|
||||
{
|
||||
LPVOID vData = malloc(dwSize);
|
||||
if (vData != NULL)
|
||||
{
|
||||
if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
|
||||
{
|
||||
UINT len;
|
||||
char szSubBlock[] = _T("\\");
|
||||
if (VerQueryValueA(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0)
|
||||
fInfo = NULL;
|
||||
else
|
||||
{
|
||||
fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
|
||||
}
|
||||
}
|
||||
free(vData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retrive some additional-infos about the module
|
||||
IMAGEHLP_MODULE64 Module;
|
||||
const char *szSymType = "-unknown-";
|
||||
if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
|
||||
{
|
||||
switch(Module.SymType)
|
||||
{
|
||||
case SymNone:
|
||||
szSymType = "-nosymbols-";
|
||||
break;
|
||||
case SymCoff:
|
||||
szSymType = "COFF";
|
||||
break;
|
||||
case SymCv:
|
||||
szSymType = "CV";
|
||||
break;
|
||||
case SymPdb:
|
||||
szSymType = "PDB";
|
||||
break;
|
||||
case SymExport:
|
||||
szSymType = "-exported-";
|
||||
break;
|
||||
case SymDeferred:
|
||||
szSymType = "-deferred-";
|
||||
break;
|
||||
case SymSym:
|
||||
szSymType = "SYM";
|
||||
break;
|
||||
case 8: //SymVirtual:
|
||||
szSymType = "Virtual";
|
||||
break;
|
||||
case 9: // SymDia:
|
||||
szSymType = "DIA";
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion);
|
||||
}
|
||||
if (szImg != NULL) free(szImg);
|
||||
if (szMod != NULL) free(szMod);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
|
||||
// This has to be done due to a problem with the "hProcess"-parameter in x64...
|
||||
// Because this class is in no case multi-threading-enabled (because of the limitations of dbghelp.dll) it is "safe" to use a static-variable
|
||||
static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL;
|
||||
static LPVOID s_readMemoryFunction_UserData = NULL;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//ShowCallstack
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
bool StackWalker::ShowCallstack(HANDLE hThread, CONTEXT const & context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData)
|
||||
{
|
||||
CONTEXT c = context;
|
||||
IMAGEHLP_SYMBOL64 *pSym = NULL;
|
||||
IMAGEHLP_MODULE64 Module;
|
||||
IMAGEHLP_LINE64 Line;
|
||||
int frameNum;
|
||||
|
||||
if (!m_modulesLoaded) LoadModules();
|
||||
|
||||
|
||||
s_readMemoryFunction = readMemoryFunction;
|
||||
s_readMemoryFunction_UserData = pUserData;
|
||||
|
||||
|
||||
|
||||
// init STACKFRAME for first call
|
||||
STACKFRAME64 s; // in/out stackframe
|
||||
memset(&s, 0, sizeof(s));
|
||||
DWORD imageType;
|
||||
#ifdef _M_IX86
|
||||
// normally, call ImageNtHeader() and use machine info from PE header
|
||||
imageType = IMAGE_FILE_MACHINE_I386;
|
||||
s.AddrPC.Offset = c.Eip;
|
||||
s.AddrPC.Mode = AddrModeFlat;
|
||||
s.AddrFrame.Offset = c.Ebp;
|
||||
s.AddrFrame.Mode = AddrModeFlat;
|
||||
s.AddrStack.Offset = c.Esp;
|
||||
s.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
s.AddrPC.Offset = c.Rip;
|
||||
s.AddrPC.Mode = AddrModeFlat;
|
||||
s.AddrFrame.Offset = c.Rsp;
|
||||
s.AddrFrame.Mode = AddrModeFlat;
|
||||
s.AddrStack.Offset = c.Rsp;
|
||||
s.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_IA64
|
||||
imageType = IMAGE_FILE_MACHINE_IA64;
|
||||
s.AddrPC.Offset = c.StIIP;
|
||||
s.AddrPC.Mode = AddrModeFlat;
|
||||
s.AddrFrame.Offset = c.IntSp;
|
||||
s.AddrFrame.Mode = AddrModeFlat;
|
||||
s.AddrBStore.Offset = c.RsBSP;
|
||||
s.AddrBStore.Mode = AddrModeFlat;
|
||||
s.AddrStack.Offset = c.IntSp;
|
||||
s.AddrStack.Mode = AddrModeFlat;
|
||||
#else
|
||||
#error "Platform not supported!"
|
||||
#endif
|
||||
|
||||
pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
|
||||
if (!pSym) goto cleanup; // not enough memory...
|
||||
memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
|
||||
pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
|
||||
|
||||
memset(&Line, 0, sizeof(Line));
|
||||
Line.SizeOfStruct = sizeof(Line);
|
||||
|
||||
memset(&Module, 0, sizeof(Module));
|
||||
Module.SizeOfStruct = sizeof(Module);
|
||||
|
||||
for (frameNum = 0; ; ++frameNum )
|
||||
{
|
||||
// get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
|
||||
// if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
|
||||
// assume that either you are done, or that the stack is so hosed that the next
|
||||
// deeper frame could not be found.
|
||||
// CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
|
||||
if ( ! StackWalk64(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, SymFunctionTableAccess64, SymGetModuleBase64, NULL) )
|
||||
{
|
||||
this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset);
|
||||
break;
|
||||
}
|
||||
|
||||
CallstackEntry csEntry;
|
||||
csEntry.offset = s.AddrPC.Offset;
|
||||
csEntry.name[0] = 0;
|
||||
csEntry.undName[0] = 0;
|
||||
csEntry.undFullName[0] = 0;
|
||||
csEntry.offsetFromSmybol = 0;
|
||||
csEntry.offsetFromLine = 0;
|
||||
csEntry.lineFileName[0] = 0;
|
||||
csEntry.lineNumber = 0;
|
||||
csEntry.loadedImageName[0] = 0;
|
||||
csEntry.moduleName[0] = 0;
|
||||
if (s.AddrPC.Offset == s.AddrReturn.Offset)
|
||||
{
|
||||
OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
|
||||
break;
|
||||
}
|
||||
if (s.AddrPC.Offset != 0)
|
||||
{
|
||||
// we seem to have a valid PC, show procedure info
|
||||
if (SymGetSymFromAddr64(m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE)
|
||||
{
|
||||
// TODO: Mache dies sicher...!
|
||||
strcpy_s(csEntry.name, pSym->Name);
|
||||
UnDecorateSymbolName( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY );
|
||||
UnDecorateSymbolName( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE );
|
||||
}
|
||||
else
|
||||
{
|
||||
this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
|
||||
}
|
||||
|
||||
// show line number info, NT5.0-method
|
||||
if (SymGetLineFromAddr64(this->m_hProcess, s.AddrPC.Offset, (PDWORD)&(csEntry.offsetFromLine), &Line) != FALSE)
|
||||
{
|
||||
csEntry.lineNumber = Line.LineNumber;
|
||||
// TODO: Mache dies sicher...!
|
||||
strcpy_s(csEntry.lineFileName, Line.FileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
|
||||
}
|
||||
|
||||
// show module info
|
||||
if( GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) )
|
||||
{
|
||||
switch ( Module.SymType )
|
||||
{
|
||||
case SymNone:
|
||||
csEntry.symTypeString = "-nosymbols-";
|
||||
break;
|
||||
case SymCoff:
|
||||
csEntry.symTypeString = "COFF";
|
||||
break;
|
||||
case SymCv:
|
||||
csEntry.symTypeString = "CV";
|
||||
break;
|
||||
case SymPdb:
|
||||
csEntry.symTypeString = "PDB";
|
||||
break;
|
||||
case SymExport:
|
||||
csEntry.symTypeString = "-exported-";
|
||||
break;
|
||||
case SymDeferred:
|
||||
csEntry.symTypeString = "-deferred-";
|
||||
break;
|
||||
case SymSym:
|
||||
csEntry.symTypeString = "SYM";
|
||||
break;
|
||||
case SymDia:
|
||||
csEntry.symTypeString = "DIA";
|
||||
break;
|
||||
case SymVirtual:
|
||||
csEntry.symTypeString = "Virtual";
|
||||
break;
|
||||
default:
|
||||
//_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
|
||||
csEntry.symTypeString = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Mache dies sicher...!
|
||||
strcpy_s(csEntry.moduleName, Module.ModuleName);
|
||||
csEntry.baseOfImage = Module.BaseOfImage;
|
||||
strcpy_s(csEntry.loadedImageName, Module.LoadedImageName);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
|
||||
}
|
||||
}
|
||||
|
||||
CallstackEntryType et = nextEntry;
|
||||
if (frameNum == 0) et = firstEntry;
|
||||
OnCallstackEntry(et, csEntry);
|
||||
|
||||
if (s.AddrReturn.Offset == 0)
|
||||
{
|
||||
OnCallstackEntry(lastEntry, csEntry);
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (pSym) free( pSym );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL __stdcall StackWalker::myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead)
|
||||
{
|
||||
if (s_readMemoryFunction == NULL)
|
||||
{
|
||||
SIZE_T st;
|
||||
BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st);
|
||||
*lpNumberOfBytesRead = (DWORD) st;
|
||||
//printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet);
|
||||
return bRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//OnLoadModule
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
|
||||
{
|
||||
CHAR buffer[STACKWALK_MAX_NAMELEN];
|
||||
if (fileVersion == 0)
|
||||
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName);
|
||||
else
|
||||
{
|
||||
DWORD v4 = (DWORD) fileVersion & 0xFFFF;
|
||||
DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF;
|
||||
DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF;
|
||||
DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF;
|
||||
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
|
||||
}
|
||||
if(OutputModules & m_options) OnOutput(buffer);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//GetModuleInfo
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
bool StackWalker::GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64 *pModuleInfo)
|
||||
{
|
||||
|
||||
pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
|
||||
void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
|
||||
if (pData == NULL)
|
||||
{
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
return false;
|
||||
}
|
||||
memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64));
|
||||
if (SymGetModuleInfo64(hProcess, baseAddr, (IMAGEHLP_MODULE64*) pData) != FALSE)
|
||||
{
|
||||
// only copy as much memory as is reserved...
|
||||
memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64));
|
||||
pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
|
||||
free(pData);
|
||||
return true;
|
||||
}
|
||||
free(pData);
|
||||
SetLastError(ERROR_DLL_INIT_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//GetModuleListTH32
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
bool StackWalker::GetModuleListTH32(HANDLE hProcess, DWORD pid)
|
||||
{
|
||||
// CreateToolhelp32Snapshot()
|
||||
typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
|
||||
// Module32First()
|
||||
typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
|
||||
// Module32Next()
|
||||
typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
|
||||
|
||||
// try both dlls...
|
||||
const char *dllname[] = { "kernel32.dll", "tlhelp32.dll" };
|
||||
HINSTANCE hToolhelp = NULL;
|
||||
tCT32S pCT32S = NULL;
|
||||
tM32F pM32F = NULL;
|
||||
tM32N pM32N = NULL;
|
||||
|
||||
HANDLE hSnap;
|
||||
MODULEENTRY32 me;
|
||||
me.dwSize = sizeof(me);
|
||||
BOOL keepGoing;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ )
|
||||
{
|
||||
hToolhelp = LoadLibraryA( dllname[i] );
|
||||
if (hToolhelp == NULL)
|
||||
continue;
|
||||
pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
|
||||
pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
|
||||
pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
|
||||
if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) )
|
||||
break; // found the functions!
|
||||
FreeLibrary(hToolhelp);
|
||||
hToolhelp = NULL;
|
||||
}
|
||||
|
||||
if (hToolhelp == NULL)
|
||||
return false;
|
||||
|
||||
hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
|
||||
if (hSnap == (HANDLE) -1)
|
||||
return false;
|
||||
|
||||
keepGoing = !!pM32F( hSnap, &me );
|
||||
int cnt = 0;
|
||||
while (keepGoing)
|
||||
{
|
||||
this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize);
|
||||
cnt++;
|
||||
keepGoing = !!pM32N( hSnap, &me );
|
||||
}
|
||||
CloseHandle(hSnap);
|
||||
FreeLibrary(hToolhelp);
|
||||
if (cnt <= 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//GetModuleListPSAPI
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
bool StackWalker::GetModuleListPSAPI(HANDLE hProcess)
|
||||
{
|
||||
DWORD i;
|
||||
//ModuleEntry e;
|
||||
DWORD cbNeeded;
|
||||
MODULEINFO mi;
|
||||
HMODULE *hMods = 0;
|
||||
char *tt = NULL;
|
||||
char *tt2 = NULL;
|
||||
const SIZE_T TTBUFLEN = 8096;
|
||||
int cnt = 0;
|
||||
|
||||
hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
|
||||
tt = (char*) malloc(sizeof(char) * TTBUFLEN);
|
||||
tt2 = (char*) malloc(sizeof(char) * TTBUFLEN);
|
||||
if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) )
|
||||
goto cleanup;
|
||||
|
||||
if ( !EnumProcessModules(hProcess, hMods, TTBUFLEN, &cbNeeded) )
|
||||
{
|
||||
//_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ( cbNeeded > TTBUFLEN )
|
||||
{
|
||||
//_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
|
||||
{
|
||||
// base address, size
|
||||
GetModuleInformation(hProcess, hMods[i], &mi, sizeof mi );
|
||||
// image file name
|
||||
tt[0] = 0;
|
||||
GetModuleFileNameExA(hProcess, hMods[i], tt, TTBUFLEN );
|
||||
// module name
|
||||
tt2[0] = 0;
|
||||
GetModuleBaseNameA(hProcess, hMods[i], tt2, TTBUFLEN );
|
||||
|
||||
DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage);
|
||||
if (dwRes != ERROR_SUCCESS)
|
||||
this->OnDbgHelpErr("LoadModule", dwRes, 0);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (tt2 != NULL) free(tt2);
|
||||
if (tt != NULL) free(tt);
|
||||
if (hMods != NULL) free(hMods);
|
||||
|
||||
return cnt != 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//OnCallstackEntry
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
|
||||
{
|
||||
CHAR buffer[STACKWALK_MAX_NAMELEN];
|
||||
if ( (eType != lastEntry) && (entry.offset != 0) )
|
||||
{
|
||||
if (entry.name[0] == 0) strcpy_s(entry.name, "(function-name not available)");
|
||||
if (entry.undName[0] != 0) strcpy_s(entry.name, entry.undName);
|
||||
if (entry.undFullName[0] != 0) strcpy_s(entry.name, entry.undFullName);
|
||||
if (entry.lineFileName[0] == 0)
|
||||
{
|
||||
strcpy_s(entry.lineFileName, "(filename not available)");
|
||||
if (entry.moduleName[0] == 0) strcpy_s(entry.moduleName, "(module-name not available)");
|
||||
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name);
|
||||
}
|
||||
OnOutput(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//OnDbgHelpErr
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
|
||||
{
|
||||
CHAR buffer[STACKWALK_MAX_NAMELEN];
|
||||
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);
|
||||
OnOutput(buffer);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
//OnSymInit
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
|
||||
{
|
||||
//Symbol Search path
|
||||
CHAR buffer[STACKWALK_MAX_NAMELEN];
|
||||
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);
|
||||
if(OutputSymPath & m_options) OnOutput(buffer);
|
||||
|
||||
//OS-version
|
||||
OSVERSIONINFOEX ver;
|
||||
ZeroMemory(&ver, sizeof(OSVERSIONINFOEX));
|
||||
ver.dwOSVersionInfoSize = sizeof(ver);
|
||||
if (GetVersionEx( (OSVERSIONINFO*) &ver) != FALSE)
|
||||
{
|
||||
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n",
|
||||
ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
|
||||
ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);
|
||||
if(OutputOS & m_options) OnOutput(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
102
Engine/source/platformWin32/minidump/winStackWalker.h
Normal file
102
Engine/source/platformWin32/minidump/winStackWalker.h
Normal 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 __WIN_PLATFORM_STACKWALKER__
|
||||
#define __WIN_PLATFORM_STACKWALKER__
|
||||
|
||||
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
class StackWalker
|
||||
{
|
||||
public:
|
||||
|
||||
typedef enum StackWalkOptions
|
||||
{
|
||||
// RetrieveNone = 0, // No additional info will be retrieved (only the address is available)
|
||||
// RetrieveSymbol = 1, // Try to get the symbol-name
|
||||
// RetrieveLine = 2, // Try to get the line for this symbol
|
||||
// RetrieveModuleInfo = 4, // Try to retrieve the module-infos
|
||||
RetrieveFileVersion = 8, // Also retrieve the version for the DLL/EXE
|
||||
RetrieveVerbose = 0xF, // Contains all of the above retrieve options
|
||||
|
||||
SymBuildPath = 0x10, // Generate a "good" symbol-search-path
|
||||
SymUseSymSrv = 0x20, // Also use a public Symbol Server
|
||||
SymAll = 0x30, // Contains all of the above Symbol options
|
||||
|
||||
OutputSymPath = 0x80, //print out the symbol path
|
||||
OutputOS = 0x100, //print out the OS path
|
||||
OutputModules = 0x200, //print out the Modules
|
||||
|
||||
OptionsDefault = 0x3F, // Less verbose output (default)
|
||||
OptionsAll = 0x2FF // Contains all options
|
||||
};
|
||||
|
||||
StackWalker(DWORD optionFlags = OptionsDefault, LPCSTR szSymPath = NULL);
|
||||
virtual ~StackWalker();
|
||||
|
||||
typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(HANDLE hProcess,
|
||||
DWORD64 qwBaseAddress,
|
||||
PVOID lpBuffer,
|
||||
DWORD nSize,
|
||||
LPDWORD lpNumberOfBytesRead,
|
||||
LPVOID pUserData // optional data, which was passed in "ShowCallstack"
|
||||
);
|
||||
|
||||
// pUserData is optional to identify some data in the 'readMemoryFunction'-callback
|
||||
bool ShowCallstack(HANDLE hThread, CONTEXT const & Context, PReadProcessMemoryRoutine readMemoryFunction = NULL, LPVOID pUserData = NULL);
|
||||
|
||||
void setOutputBuffer(char * buffer);
|
||||
|
||||
private:
|
||||
typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
|
||||
|
||||
HANDLE m_hProcess;
|
||||
DWORD m_dwProcessId;
|
||||
bool m_modulesLoaded;
|
||||
LPSTR m_szSymPath;
|
||||
int m_options;
|
||||
char * m_pOutputBuffer;
|
||||
|
||||
static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
|
||||
|
||||
bool Init(LPCSTR szSymPath);
|
||||
|
||||
virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
|
||||
virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
|
||||
virtual void OnCallstackEntry(CallstackEntryType eType, struct CallstackEntry &entry);
|
||||
virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
|
||||
virtual void OnOutput(LPCSTR szText);
|
||||
|
||||
bool LoadModules();
|
||||
DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size);
|
||||
bool GetModuleListTH32(HANDLE hProcess, DWORD pid);
|
||||
bool GetModuleListPSAPI(HANDLE hProcess);
|
||||
bool GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64 *pModuleInfo);
|
||||
};
|
||||
|
||||
void dGetStackTrace(char * traceBuffer, CONTEXT const & ContextRecord);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
948
Engine/source/platformWin32/nativeDialogs/fileDialog.cpp
Normal file
948
Engine/source/platformWin32/nativeDialogs/fileDialog.cpp
Normal file
|
|
@ -0,0 +1,948 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/simBase.h"
|
||||
#include "platform/nativeDialogs/fileDialog.h"
|
||||
#include "platform/threads/mutex.h"
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "math/mMath.h"
|
||||
#include "core/strings/unicode.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "platform/profiler.h"
|
||||
#include <ShlObj.h>
|
||||
#include <WindowsX.h>
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
#ifdef TORQUE_TOOLS
|
||||
//-----------------------------------------------------------------------------
|
||||
// PlatformFileDlgData Implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
FileDialogData::FileDialogData()
|
||||
{
|
||||
// Default Path
|
||||
//
|
||||
// Try to provide consistent experience by recalling the last file path
|
||||
// - else
|
||||
// Default to Working Directory if last path is not set or is invalid
|
||||
mDefaultPath = StringTable->insert( Con::getVariable("Tools::FileDialogs::LastFilePath") );
|
||||
if( mDefaultPath == StringTable->lookup("") || !Platform::isDirectory( mDefaultPath ) )
|
||||
mDefaultPath = Platform::getCurrentDirectory();
|
||||
|
||||
mDefaultFile = StringTable->insert("");
|
||||
mFilters = StringTable->insert("");
|
||||
mFile = StringTable->insert("");
|
||||
mTitle = StringTable->insert("");
|
||||
|
||||
mStyle = 0;
|
||||
|
||||
}
|
||||
FileDialogData::~FileDialogData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static LRESULT PASCAL OKBtnFolderHackProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
WNDPROC oldProc = (WNDPROC)GetProp(hWnd, dT("OldWndProc"));
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_COMMAND:
|
||||
if(LOWORD(wParam) == IDOK)
|
||||
{
|
||||
LPOPENFILENAME ofn = (LPOPENFILENAME)GetProp(hWnd, dT("OFN"));
|
||||
if(ofn == NULL)
|
||||
break;
|
||||
|
||||
SendMessage(hWnd, CDM_GETFILEPATH, ofn->nMaxFile, (LPARAM)ofn->lpstrFile);
|
||||
|
||||
char *filePath;
|
||||
#ifdef UNICODE
|
||||
char fileBuf[MAX_PATH];
|
||||
convertUTF16toUTF8(ofn->lpstrFile, fileBuf, sizeof(fileBuf));
|
||||
filePath = fileBuf;
|
||||
#else
|
||||
filePath = ofn->lpstrFile;
|
||||
#endif
|
||||
|
||||
if(Platform::isDirectory(filePath))
|
||||
{
|
||||
// Got a directory
|
||||
EndDialog(hWnd, IDOK);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(oldProc)
|
||||
return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
||||
else
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
static UINT_PTR CALLBACK FolderHookProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam){
|
||||
HWND hParent = GetParent(hdlg);
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
LPOPENFILENAME lpofn = (LPOPENFILENAME)lParam;
|
||||
|
||||
SendMessage(hParent, CDM_SETCONTROLTEXT, stc3, (LPARAM)dT("Folder name:"));
|
||||
SendMessage(hParent, CDM_HIDECONTROL, cmb1, 0);
|
||||
SendMessage(hParent, CDM_HIDECONTROL, stc2, 0);
|
||||
|
||||
LONG oldProc = SetWindowLong(hParent, GWL_WNDPROC, (LONG)OKBtnFolderHackProc);
|
||||
SetProp(hParent, dT("OldWndProc"), (HANDLE)oldProc);
|
||||
SetProp(hParent, dT("OFN"), (HANDLE)lpofn);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR nmhdr = (LPNMHDR)lParam;
|
||||
switch(nmhdr->code)
|
||||
{
|
||||
case CDN_FOLDERCHANGE:
|
||||
{
|
||||
LPOFNOTIFY lpofn = (LPOFNOTIFY)lParam;
|
||||
|
||||
OpenFolderDialog *ofd = (OpenFolderDialog *)lpofn->lpOFN->lCustData;
|
||||
|
||||
#ifdef UNICODE
|
||||
UTF16 buf[MAX_PATH];
|
||||
#else
|
||||
char buf[MAX_PATH];
|
||||
#endif
|
||||
|
||||
SendMessage(hParent, CDM_GETFOLDERPATH, sizeof(buf), (LPARAM)buf);
|
||||
|
||||
char filePath[MAX_PATH];
|
||||
#ifdef UNICODE
|
||||
convertUTF16toUTF8(buf, filePath, sizeof(filePath));
|
||||
#else
|
||||
dStrcpy( filePath, buf );
|
||||
#endif
|
||||
|
||||
// [tom, 12/8/2006] Hack to remove files from the list because
|
||||
// CDN_INCLUDEITEM doesn't work for regular files and folders.
|
||||
HWND shellView = GetDlgItem(hParent, lst2);
|
||||
HWND listView = FindWindowEx(shellView, 0, WC_LISTVIEW, NULL);
|
||||
if(listView)
|
||||
{
|
||||
S32 count = ListView_GetItemCount(listView);
|
||||
for(S32 i = count - 1;i >= 0;--i)
|
||||
{
|
||||
ListView_GetItemText(listView, i, 0, buf, sizeof(buf));
|
||||
|
||||
#ifdef UNICODE
|
||||
char buf2[MAX_PATH];
|
||||
convertUTF16toUTF8(buf, buf2, sizeof(buf2));
|
||||
#else
|
||||
char *buf2 = buf;
|
||||
#endif
|
||||
char full[MAX_PATH];
|
||||
dSprintf(full, sizeof(full), "%s\\%s", filePath, buf2);
|
||||
|
||||
if(!Platform::isDirectory(full))
|
||||
{
|
||||
ListView_DeleteItem(listView, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ofd->mMustExistInDir == NULL || *ofd->mMustExistInDir == 0)
|
||||
break;
|
||||
|
||||
HWND hOK = GetDlgItem(hParent, IDOK);
|
||||
if(hOK == NULL)
|
||||
break;
|
||||
|
||||
char checkPath[MAX_PATH];
|
||||
dSprintf(checkPath, sizeof(checkPath), "%s\\%s", filePath, ofd->mMustExistInDir);
|
||||
|
||||
EnableWindow(hOK, Platform::isFile(checkPath));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FileDialog Implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
IMPLEMENT_CONOBJECT(FileDialog);
|
||||
|
||||
ConsoleDocClass( FileDialog,
|
||||
"@brief Base class responsible for displaying an OS file browser.\n\n"
|
||||
|
||||
"FileDialog is a platform agnostic dialog interface for querying the user for "
|
||||
"file locations. It is designed to be used through the exposed scripting interface.\n\n"
|
||||
|
||||
"FileDialog is the base class for Native File Dialog controls in Torque. It provides these basic areas of functionality:\n\n"
|
||||
" - Inherits from SimObject and is exposed to the scripting interface\n"
|
||||
" - Provides blocking interface to allow instant return to script execution\n"
|
||||
" - Simple object configuration makes practical use easy and effective\n\n"
|
||||
|
||||
"FileDialog is *NOT* intended to be used directly in script and is only exposed to script to expose generic file dialog attributes.\n\n"
|
||||
|
||||
"This base class is usable in TorqueScript, but is does not specify what functionality is intended (open or save?). "
|
||||
"Its children, OpenFileDialog and SaveFileDialog, do make use of DialogStyle flags and do make use of specific funcationality. "
|
||||
"These are the preferred classes to use\n\n"
|
||||
|
||||
"However, the FileDialog base class does contain the key properties and important method for file browing. The most "
|
||||
"important function is Execute(). This is used by both SaveFileDialog and OpenFileDialog to initiate the browser.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// NOTE: This is not he preferred class to use, but this still works\n\n"
|
||||
"// Create the file dialog\n"
|
||||
"%baseFileDialog = new FileDialog()\n"
|
||||
"{\n"
|
||||
" // Allow browsing of all file types\n"
|
||||
" filters = \"*.*\";\n\n"
|
||||
" // No default file\n"
|
||||
" defaultFile = "";\n\n"
|
||||
" // Set default path relative to project\n"
|
||||
" defaultPath = \"./\";\n\n"
|
||||
" // Set the title\n"
|
||||
" title = \"Durpa\";\n\n"
|
||||
" // Allow changing of path you are browsing\n"
|
||||
" changePath = true;\n"
|
||||
"};\n\n"
|
||||
" // Launch the file dialog\n"
|
||||
" %baseFileDialog.Execute();\n"
|
||||
" \n"
|
||||
" // Don't forget to cleanup\n"
|
||||
" %baseFileDialog.delete();\n\n\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n"
|
||||
|
||||
"@see OpenFileDialog for a practical example on opening a file\n"
|
||||
"@see SaveFileDialog for a practical example of saving a file\n"
|
||||
|
||||
"@ingroup FileSystem\n"
|
||||
);
|
||||
|
||||
FileDialog::FileDialog() : mData()
|
||||
{
|
||||
// Default to File Must Exist Open Dialog style
|
||||
mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST;
|
||||
mChangePath = false;
|
||||
}
|
||||
|
||||
FileDialog::~FileDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void FileDialog::initPersistFields()
|
||||
{
|
||||
addProtectedField( "defaultPath", TypeString, Offset(mData.mDefaultPath, FileDialog), &setDefaultPath, &defaultProtectedGetFn,
|
||||
"The default directory path when the dialog is shown." );
|
||||
|
||||
addProtectedField( "defaultFile", TypeString, Offset(mData.mDefaultFile, FileDialog), &setDefaultFile, &defaultProtectedGetFn,
|
||||
"The default file path when the dialog is shown." );
|
||||
|
||||
addProtectedField( "fileName", TypeString, Offset(mData.mFile, FileDialog), &setFile, &defaultProtectedGetFn,
|
||||
"The default file name when the dialog is shown." );
|
||||
|
||||
addProtectedField( "filters", TypeString, Offset(mData.mFilters, FileDialog), &setFilters, &defaultProtectedGetFn,
|
||||
"The filter string for limiting the types of files visible in the dialog. It makes use of the pipe symbol '|' "
|
||||
"as a delimiter. For example:\n\n"
|
||||
"'All Files|*.*'\n\n"
|
||||
"'Image Files|*.png;*.jpg|Png Files|*.png|Jepg Files|*.jpg'" );
|
||||
|
||||
addField( "title", TypeString, Offset(mData.mTitle, FileDialog),
|
||||
"The title for the dialog." );
|
||||
|
||||
addProtectedField( "changePath", TypeBool, Offset(mChangePath, FileDialog), &setChangePath, &getChangePath,
|
||||
"True/False whether to set the working directory to the directory returned by the dialog." );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
static const U32 convertUTF16toUTF8DoubleNULL( const UTF16 *unistring, UTF8 *outbuffer, U32 len)
|
||||
{
|
||||
AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator.");
|
||||
PROFILE_START(convertUTF16toUTF8DoubleNULL);
|
||||
U32 walked, nCodeunits, codeunitLen;
|
||||
UTF32 middleman;
|
||||
|
||||
nCodeunits=0;
|
||||
while( ! (*unistring == '\0' && *(unistring + 1) == '\0') && nCodeunits + 3 < len )
|
||||
{
|
||||
walked = 1;
|
||||
middleman = oneUTF16toUTF32(unistring,&walked);
|
||||
codeunitLen = oneUTF32toUTF8(middleman, &outbuffer[nCodeunits]);
|
||||
unistring += walked;
|
||||
nCodeunits += codeunitLen;
|
||||
}
|
||||
|
||||
nCodeunits = getMin(nCodeunits,len - 1);
|
||||
outbuffer[nCodeunits] = '\0';
|
||||
outbuffer[nCodeunits+1] = '\0';
|
||||
|
||||
PROFILE_END();
|
||||
return nCodeunits;
|
||||
}
|
||||
|
||||
//
|
||||
// Execute Method
|
||||
//
|
||||
bool FileDialog::Execute()
|
||||
{
|
||||
static char pszResult[MAX_PATH];
|
||||
#ifdef UNICODE
|
||||
UTF16 pszFile[MAX_PATH];
|
||||
UTF16 pszInitialDir[MAX_PATH];
|
||||
UTF16 pszTitle[MAX_PATH];
|
||||
UTF16 pszFilter[1024];
|
||||
UTF16 pszFileTitle[MAX_PATH];
|
||||
UTF16 pszDefaultExtension[MAX_PATH];
|
||||
// Convert parameters to UTF16*'s
|
||||
convertUTF8toUTF16((UTF8 *)mData.mDefaultFile, pszFile, sizeof(pszFile));
|
||||
convertUTF8toUTF16((UTF8 *)mData.mDefaultPath, pszInitialDir, sizeof(pszInitialDir));
|
||||
convertUTF8toUTF16((UTF8 *)mData.mTitle, pszTitle, sizeof(pszTitle));
|
||||
convertUTF8toUTF16((UTF8 *)mData.mFilters, pszFilter, sizeof(pszFilter) );
|
||||
#else
|
||||
// Not Unicode, All char*'s!
|
||||
char pszFile[MAX_PATH];
|
||||
char pszFilter[1024];
|
||||
char pszFileTitle[MAX_PATH];
|
||||
dStrcpy( pszFile, mData.mDefaultFile );
|
||||
dStrcpy( pszFilter, mData.mFilters );
|
||||
const char* pszInitialDir = mData.mDefaultPath;
|
||||
const char* pszTitle = mData.mTitle;
|
||||
|
||||
#endif
|
||||
|
||||
pszFileTitle[0] = 0;
|
||||
|
||||
// Convert Filters
|
||||
U32 filterLen = dStrlen( pszFilter );
|
||||
S32 dotIndex = -1;
|
||||
for( U32 i = 0; i < filterLen; i++ )
|
||||
{
|
||||
if( pszFilter[i] == '|' )
|
||||
pszFilter[i] = '\0';
|
||||
|
||||
if( pszFilter[ i ] == '.' && dotIndex == -1 )
|
||||
dotIndex = i;
|
||||
}
|
||||
// Add second NULL terminator at the end
|
||||
pszFilter[ filterLen + 1 ] = '\0';
|
||||
|
||||
// Get default extension.
|
||||
dMemset( pszDefaultExtension, 0, sizeof( pszDefaultExtension ) );
|
||||
if( dotIndex != -1 )
|
||||
{
|
||||
for( U32 i = 0; i < MAX_PATH; ++ i )
|
||||
{
|
||||
UTF16 ch = pszFilter[ dotIndex + 1 + i ];
|
||||
if( !ch || ch == ';' || ch == '|' || dIsspace( ch ) )
|
||||
break;
|
||||
|
||||
pszDefaultExtension[ i ] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
OPENFILENAME ofn;
|
||||
dMemset(&ofn, 0, sizeof(ofn));
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.hwndOwner = getWin32WindowHandle();
|
||||
ofn.lpstrFile = pszFile;
|
||||
|
||||
|
||||
if( !dStrncmp( mData.mDefaultFile, "", 1 ) )
|
||||
ofn.lpstrFile[0] = '\0';
|
||||
|
||||
|
||||
ofn.nMaxFile = sizeof(pszFile);
|
||||
ofn.lpstrFilter = pszFilter;
|
||||
ofn.nFilterIndex = 1;
|
||||
ofn.lpstrInitialDir = pszInitialDir;
|
||||
ofn.lCustData = (LPARAM)this;
|
||||
ofn.lpstrFileTitle = pszFileTitle;
|
||||
ofn.nMaxFileTitle = sizeof(pszFileTitle);
|
||||
ofn.lpstrDefExt = pszDefaultExtension[ 0 ] ? pszDefaultExtension : NULL;
|
||||
|
||||
if( mData.mTitle != StringTable->lookup("") )
|
||||
ofn.lpstrTitle = pszTitle;
|
||||
|
||||
// Build Proper Flags.
|
||||
ofn.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY;
|
||||
|
||||
if(mData.mStyle & FileDialogData::FDS_BROWSEFOLDER)
|
||||
{
|
||||
ofn.lpfnHook = FolderHookProc;
|
||||
ofn.Flags |= OFN_ENABLEHOOK;
|
||||
}
|
||||
|
||||
if( !(mData.mStyle & FileDialogData::FDS_CHANGEPATH) )
|
||||
ofn.Flags |= OFN_NOCHANGEDIR;
|
||||
|
||||
if( mData.mStyle & FileDialogData::FDS_MUSTEXIST )
|
||||
ofn.Flags |= OFN_FILEMUSTEXIST;
|
||||
|
||||
if( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )
|
||||
ofn.Flags |= OFN_ALLOWMULTISELECT;
|
||||
|
||||
if( mData.mStyle & FileDialogData::FDS_OVERWRITEPROMPT )
|
||||
ofn.Flags |= OFN_OVERWRITEPROMPT;
|
||||
|
||||
|
||||
// Flag we're showing file browser so we can do some render hacking
|
||||
winState.renderThreadBlocked = true;
|
||||
|
||||
// Get the current working directory, so we can back up to it once Windows has
|
||||
// done its craziness and messed with it.
|
||||
StringTableEntry cwd = Platform::getCurrentDirectory();
|
||||
|
||||
// Execute Dialog (Blocking Call)
|
||||
bool dialogSuccess = false;
|
||||
if( mData.mStyle & FileDialogData::FDS_OPEN )
|
||||
dialogSuccess = GetOpenFileName(&ofn);
|
||||
else if( mData.mStyle & FileDialogData::FDS_SAVE )
|
||||
dialogSuccess = GetSaveFileName(&ofn);
|
||||
|
||||
// Dialog is gone.
|
||||
winState.renderThreadBlocked = false;
|
||||
|
||||
// Restore the working directory.
|
||||
Platform::setCurrentDirectory( cwd );
|
||||
|
||||
// Did we select a file?
|
||||
if( !dialogSuccess )
|
||||
return false;
|
||||
|
||||
// Handle Result Properly for Unicode as well as ANSI
|
||||
#ifdef UNICODE
|
||||
if(pszFileTitle[0] || ! ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ))
|
||||
convertUTF16toUTF8( (UTF16*)pszFile, (UTF8*)pszResult, sizeof(pszResult));
|
||||
else
|
||||
convertUTF16toUTF8DoubleNULL( (UTF16*)pszFile, (UTF8*)pszResult, sizeof(pszResult));
|
||||
#else
|
||||
if(pszFileTitle[0] || ! ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ))
|
||||
dStrcpy(pszResult,pszFile);
|
||||
else
|
||||
{
|
||||
// [tom, 1/4/2007] pszResult is a double-NULL terminated, NULL separated list in this case so we can't just dSstrcpy()
|
||||
char *sptr = pszFile, *dptr = pszResult;
|
||||
while(! (*sptr == 0 && *(sptr+1) == 0))
|
||||
*dptr++ = *sptr++;
|
||||
*dptr++ = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
forwardslash(pszResult);
|
||||
|
||||
// [tom, 1/5/2007] Windows is ridiculously dumb. If you select a single file in a multiple
|
||||
// select file dialog then it will return the file the same way as it would in a single
|
||||
// select dialog. The only difference is pszFileTitle is empty if multiple files
|
||||
// are selected.
|
||||
|
||||
// Store the result on our object
|
||||
if( mData.mStyle & FileDialogData::FDS_BROWSEFOLDER || ( pszFileTitle[0] && ! ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )))
|
||||
{
|
||||
// Single file selection, do it the easy way
|
||||
mData.mFile = StringTable->insert( pszResult );
|
||||
}
|
||||
else if(pszFileTitle[0] && ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ))
|
||||
{
|
||||
// Single file selection in a multiple file selection dialog
|
||||
setDataField(StringTable->insert("files"), "0", pszResult);
|
||||
setDataField(StringTable->insert("fileCount"), NULL, "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multiple file selection, break out into an array
|
||||
S32 numFiles = 0;
|
||||
const char *dir = pszResult;
|
||||
const char *file = dir + dStrlen(dir) + 1;
|
||||
char buffer[1024];
|
||||
|
||||
while(*file)
|
||||
{
|
||||
Platform::makeFullPathName(file, buffer, sizeof(buffer), dir);
|
||||
setDataField(StringTable->insert("files"), Con::getIntArg(numFiles++), buffer);
|
||||
|
||||
file = file + dStrlen(file) + 1;
|
||||
}
|
||||
|
||||
setDataField(StringTable->insert("fileCount"), NULL, Con::getIntArg(numFiles));
|
||||
}
|
||||
|
||||
// Return success.
|
||||
return true;
|
||||
|
||||
}
|
||||
DefineEngineMethod( FileDialog, Execute, bool, (),,
|
||||
"@brief Launches the OS file browser\n\n"
|
||||
|
||||
"After an Execute() call, the chosen file name and path is available in one of two areas. "
|
||||
"If only a single file selection is permitted, the results will be stored in the @a fileName "
|
||||
"attribute.\n\n"
|
||||
|
||||
"If multiple file selection is permitted, the results will be stored in the "
|
||||
"@a files array. The total number of files in the array will be stored in the "
|
||||
"@a fileCount attribute.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// NOTE: This is not he preferred class to use, but this still works\n\n"
|
||||
"// Create the file dialog\n"
|
||||
"%baseFileDialog = new FileDialog()\n"
|
||||
"{\n"
|
||||
" // Allow browsing of all file types\n"
|
||||
" filters = \"*.*\";\n\n"
|
||||
" // No default file\n"
|
||||
" defaultFile = "";\n\n"
|
||||
" // Set default path relative to project\n"
|
||||
" defaultPath = \"./\";\n\n"
|
||||
" // Set the title\n"
|
||||
" title = \"Durpa\";\n\n"
|
||||
" // Allow changing of path you are browsing\n"
|
||||
" changePath = true;\n"
|
||||
"};\n\n"
|
||||
" // Launch the file dialog\n"
|
||||
" %baseFileDialog.Execute();\n"
|
||||
" \n"
|
||||
" // Don't forget to cleanup\n"
|
||||
" %baseFileDialog.delete();\n\n\n"
|
||||
|
||||
" // A better alternative is to use the \n"
|
||||
" // derived classes which are specific to file open and save\n\n"
|
||||
" // Create a dialog dedicated to opening files\n"
|
||||
" %openFileDlg = new OpenFileDialog()\n"
|
||||
" {\n"
|
||||
" // Look for jpg image files\n"
|
||||
" // First part is the descriptor|second part is the extension\n"
|
||||
" Filters = \"Jepg Files|*.jpg\";\n"
|
||||
" // Allow browsing through other folders\n"
|
||||
" ChangePath = true;\n\n"
|
||||
" // Only allow opening of one file at a time\n"
|
||||
" MultipleFiles = false;\n"
|
||||
" };\n\n"
|
||||
" // Launch the open file dialog\n"
|
||||
" %result = %openFileDlg.Execute();\n\n"
|
||||
" // Obtain the chosen file name and path\n"
|
||||
" if ( %result )\n"
|
||||
" {\n"
|
||||
" %seletedFile = %openFileDlg.file;\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" %selectedFile = \"\";\n"
|
||||
" }\n"
|
||||
" // Cleanup\n"
|
||||
" %openFileDlg.delete();\n\n\n"
|
||||
|
||||
" // Create a dialog dedicated to saving a file\n"
|
||||
" %saveFileDlg = new SaveFileDialog()\n"
|
||||
" {\n"
|
||||
" // Only allow for saving of COLLADA files\n"
|
||||
" Filters = \"COLLADA Files (*.dae)|*.dae|\";\n\n"
|
||||
" // Default save path to where the WorldEditor last saved\n"
|
||||
" DefaultPath = $pref::WorldEditor::LastPath;\n\n"
|
||||
" // No default file specified\n"
|
||||
" DefaultFile = \"\";\n\n"
|
||||
" // Do not allow the user to change to a new directory\n"
|
||||
" ChangePath = false;\n\n"
|
||||
" // Prompt the user if they are going to overwrite an existing file\n"
|
||||
" OverwritePrompt = true;\n"
|
||||
" };\n\n"
|
||||
" // Launch the save file dialog\n"
|
||||
" %result = %saveFileDlg.Execute();\n\n"
|
||||
" // Obtain the file name\n"
|
||||
" %selectedFile = \"\";\n"
|
||||
" if ( %result )\n"
|
||||
" %selectedFile = %saveFileDlg.file;\n\n"
|
||||
" // Cleanup\n"
|
||||
" %saveFileDlg.delete();\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@return True if the file was selected was successfully found (opened) or declared (saved).")
|
||||
{
|
||||
return object->Execute();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dialog Filters
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileDialog::setFilters( void *object, const char *index, const char *data )
|
||||
{
|
||||
// Will do validate on write at some point.
|
||||
if( !data )
|
||||
return true;
|
||||
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Default Path Property - String Validated on Write
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileDialog::setDefaultPath( void *object, const char *index, const char *data )
|
||||
{
|
||||
|
||||
if( !data || !dStrncmp( data, "", 1 ) )
|
||||
return true;
|
||||
|
||||
// Copy and Backslash the path (Windows dialogs are VERY picky about this format)
|
||||
static char szPathValidate[512];
|
||||
dStrcpy( szPathValidate, data );
|
||||
|
||||
Platform::makeFullPathName( data,szPathValidate, sizeof(szPathValidate));
|
||||
backslash( szPathValidate );
|
||||
|
||||
// Remove any trailing \'s
|
||||
S8 validateLen = dStrlen( szPathValidate );
|
||||
if( szPathValidate[ validateLen - 1 ] == '\\' )
|
||||
szPathValidate[ validateLen - 1 ] = '\0';
|
||||
|
||||
// Now check
|
||||
if( Platform::isDirectory( szPathValidate ) )
|
||||
{
|
||||
// Finally, assign in proper format.
|
||||
FileDialog *pDlg = static_cast<FileDialog*>( object );
|
||||
pDlg->mData.mDefaultPath = StringTable->insert( szPathValidate );
|
||||
}
|
||||
#ifdef TORQUE_DEBUG
|
||||
else
|
||||
Con::errorf(ConsoleLogEntry::GUI, "FileDialog - Invalid Default Path Specified!");
|
||||
#endif
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Default File Property - String Validated on Write
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileDialog::setDefaultFile( void *object, const char *index, const char *data )
|
||||
{
|
||||
if( !data || !dStrncmp( data, "", 1 ) )
|
||||
return true;
|
||||
|
||||
// Copy and Backslash the path (Windows dialogs are VERY picky about this format)
|
||||
static char szPathValidate[512];
|
||||
Platform::makeFullPathName( data,szPathValidate, sizeof(szPathValidate) );
|
||||
backslash( szPathValidate );
|
||||
|
||||
// Remove any trailing \'s
|
||||
S8 validateLen = dStrlen( szPathValidate );
|
||||
if( szPathValidate[ validateLen - 1 ] == '\\' )
|
||||
szPathValidate[ validateLen - 1 ] = '\0';
|
||||
|
||||
// Finally, assign in proper format.
|
||||
FileDialog *pDlg = static_cast<FileDialog*>( object );
|
||||
pDlg->mData.mDefaultFile = StringTable->insert( szPathValidate );
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChangePath Property - Change working path on successful file selection
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FileDialog::setChangePath( void *object, const char *index, const char *data )
|
||||
{
|
||||
bool bMustExist = dAtob( data );
|
||||
|
||||
FileDialog *pDlg = static_cast<FileDialog*>( object );
|
||||
|
||||
if( bMustExist )
|
||||
pDlg->mData.mStyle |= FileDialogData::FDS_CHANGEPATH;
|
||||
else
|
||||
pDlg->mData.mStyle &= ~FileDialogData::FDS_CHANGEPATH;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const char* FileDialog::getChangePath(void* obj, const char* data)
|
||||
{
|
||||
FileDialog *pDlg = static_cast<FileDialog*>( obj );
|
||||
if( pDlg->mData.mStyle & FileDialogData::FDS_CHANGEPATH )
|
||||
return StringTable->insert("true");
|
||||
else
|
||||
return StringTable->insert("false");
|
||||
}
|
||||
|
||||
bool FileDialog::setFile( void *object, const char *index, const char *data )
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// OpenFileDialog Implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleDocClass( OpenFileDialog,
|
||||
"@brief Derived from FileDialog, this class is responsible for opening a file browser with the intention of opening a file.\n\n"
|
||||
|
||||
"The core usage of this dialog is to locate a file in the OS and return the path and name. This does not handle "
|
||||
"the actual file parsing or data manipulation. That functionality is left up to the FileObject class.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
" // Create a dialog dedicated to opening files\n"
|
||||
" %openFileDlg = new OpenFileDialog()\n"
|
||||
" {\n"
|
||||
" // Look for jpg image files\n"
|
||||
" // First part is the descriptor|second part is the extension\n"
|
||||
" Filters = \"Jepg Files|*.jpg\";\n"
|
||||
" // Allow browsing through other folders\n"
|
||||
" ChangePath = true;\n\n"
|
||||
" // Only allow opening of one file at a time\n"
|
||||
" MultipleFiles = false;\n"
|
||||
" };\n\n"
|
||||
" // Launch the open file dialog\n"
|
||||
" %result = %openFileDlg.Execute();\n\n"
|
||||
" // Obtain the chosen file name and path\n"
|
||||
" if ( %result )\n"
|
||||
" {\n"
|
||||
" %seletedFile = %openFileDlg.file;\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" %selectedFile = \"\";\n"
|
||||
" }\n\n"
|
||||
" // Cleanup\n"
|
||||
" %openFileDlg.delete();\n\n\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n"
|
||||
|
||||
"@see FileDialog\n"
|
||||
"@see SaveFileDialog\n"
|
||||
"@see FileObject\n"
|
||||
|
||||
"@ingroup FileSystem\n"
|
||||
);
|
||||
OpenFileDialog::OpenFileDialog()
|
||||
{
|
||||
// Default File Must Exist
|
||||
mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST;
|
||||
}
|
||||
|
||||
OpenFileDialog::~OpenFileDialog()
|
||||
{
|
||||
mMustExist = true;
|
||||
mMultipleFiles = false;
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT(OpenFileDialog);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console Properties
|
||||
//-----------------------------------------------------------------------------
|
||||
void OpenFileDialog::initPersistFields()
|
||||
{
|
||||
addProtectedField("MustExist", TypeBool, Offset(mMustExist, OpenFileDialog), &setMustExist, &getMustExist, "True/False whether the file returned must exist or not" );
|
||||
addProtectedField("MultipleFiles", TypeBool, Offset(mMultipleFiles, OpenFileDialog), &setMultipleFiles, &getMultipleFiles, "True/False whether multiple files may be selected and returned or not" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File Must Exist - Boolean
|
||||
//-----------------------------------------------------------------------------
|
||||
bool OpenFileDialog::setMustExist( void *object, const char *index, const char *data )
|
||||
{
|
||||
bool bMustExist = dAtob( data );
|
||||
|
||||
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( object );
|
||||
|
||||
if( bMustExist )
|
||||
pDlg->mData.mStyle |= FileDialogData::FDS_MUSTEXIST;
|
||||
else
|
||||
pDlg->mData.mStyle &= ~FileDialogData::FDS_MUSTEXIST;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const char* OpenFileDialog::getMustExist(void* obj, const char* data)
|
||||
{
|
||||
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( obj );
|
||||
if( pDlg->mData.mStyle & FileDialogData::FDS_MUSTEXIST )
|
||||
return StringTable->insert("true");
|
||||
else
|
||||
return StringTable->insert("false");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Can Select Multiple Files - Boolean
|
||||
//-----------------------------------------------------------------------------
|
||||
bool OpenFileDialog::setMultipleFiles( void *object, const char *index, const char *data )
|
||||
{
|
||||
bool bMustExist = dAtob( data );
|
||||
|
||||
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( object );
|
||||
|
||||
if( bMustExist )
|
||||
pDlg->mData.mStyle |= FileDialogData::FDS_MULTIPLEFILES;
|
||||
else
|
||||
pDlg->mData.mStyle &= ~FileDialogData::FDS_MULTIPLEFILES;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const char* OpenFileDialog::getMultipleFiles(void* obj, const char* data)
|
||||
{
|
||||
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( obj );
|
||||
if( pDlg->mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )
|
||||
return StringTable->insert("true");
|
||||
else
|
||||
return StringTable->insert("false");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SaveFileDialog Implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
ConsoleDocClass( SaveFileDialog,
|
||||
"@brief Derived from FileDialog, this class is responsible for opening a file browser with the intention of saving a file.\n\n"
|
||||
|
||||
"The core usage of this dialog is to locate a file in the OS and return the path and name. This does not handle "
|
||||
"the actual file writing or data manipulation. That functionality is left up to the FileObject class.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
" // Create a dialog dedicated to opening file\n"
|
||||
" %saveFileDlg = new SaveFileDialog()\n"
|
||||
" {\n"
|
||||
" // Only allow for saving of COLLADA files\n"
|
||||
" Filters = \"COLLADA Files (*.dae)|*.dae|\";\n\n"
|
||||
" // Default save path to where the WorldEditor last saved\n"
|
||||
" DefaultPath = $pref::WorldEditor::LastPath;\n\n"
|
||||
" // No default file specified\n"
|
||||
" DefaultFile = \"\";\n\n"
|
||||
" // Do not allow the user to change to a new directory\n"
|
||||
" ChangePath = false;\n\n"
|
||||
" // Prompt the user if they are going to overwrite an existing file\n"
|
||||
" OverwritePrompt = true;\n"
|
||||
" };\n\n"
|
||||
" // Launch the save file dialog\n"
|
||||
" %saveFileDlg.Execute();\n\n"
|
||||
" if ( %result )\n"
|
||||
" {\n"
|
||||
" %seletedFile = %openFileDlg.file;\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" %selectedFile = \"\";\n"
|
||||
" }\n\n"
|
||||
" // Cleanup\n"
|
||||
" %saveFileDlg.delete();\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n"
|
||||
|
||||
"@see FileDialog\n"
|
||||
"@see OpenFileDialog\n"
|
||||
"@see FileObject\n"
|
||||
|
||||
"@ingroup FileSystem\n"
|
||||
);
|
||||
SaveFileDialog::SaveFileDialog()
|
||||
{
|
||||
// Default File Must Exist
|
||||
mData.mStyle = FileDialogData::FDS_SAVE | FileDialogData::FDS_OVERWRITEPROMPT;
|
||||
mOverwritePrompt = true;
|
||||
}
|
||||
|
||||
SaveFileDialog::~SaveFileDialog()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT(SaveFileDialog);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console Properties
|
||||
//-----------------------------------------------------------------------------
|
||||
void SaveFileDialog::initPersistFields()
|
||||
{
|
||||
addProtectedField("OverwritePrompt", TypeBool, Offset(mOverwritePrompt, SaveFileDialog), &setOverwritePrompt, &getOverwritePrompt, "True/False whether the dialog should prompt before accepting an existing file name" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Prompt on Overwrite - Boolean
|
||||
//-----------------------------------------------------------------------------
|
||||
bool SaveFileDialog::setOverwritePrompt( void *object, const char *index, const char *data )
|
||||
{
|
||||
bool bMustExist = dAtob( data );
|
||||
|
||||
SaveFileDialog *pDlg = static_cast<SaveFileDialog*>( object );
|
||||
|
||||
if( bMustExist )
|
||||
pDlg->mData.mStyle |= FileDialogData::FDS_OVERWRITEPROMPT;
|
||||
else
|
||||
pDlg->mData.mStyle &= ~FileDialogData::FDS_OVERWRITEPROMPT;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const char* SaveFileDialog::getOverwritePrompt(void* obj, const char* data)
|
||||
{
|
||||
SaveFileDialog *pDlg = static_cast<SaveFileDialog*>( obj );
|
||||
if( pDlg->mData.mStyle & FileDialogData::FDS_OVERWRITEPROMPT )
|
||||
return StringTable->insert("true");
|
||||
else
|
||||
return StringTable->insert("false");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// OpenFolderDialog Implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
OpenFolderDialog::OpenFolderDialog()
|
||||
{
|
||||
mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_OVERWRITEPROMPT | FileDialogData::FDS_BROWSEFOLDER;
|
||||
|
||||
mMustExistInDir = "";
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT(OpenFolderDialog);
|
||||
|
||||
ConsoleDocClass( OpenFolderDialog,
|
||||
"@brief OS level dialog used for browsing folder structures.\n\n"
|
||||
|
||||
"This is essentially an OpenFileDialog, but only used for returning directory paths, not files.\n\n"
|
||||
|
||||
"@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n"
|
||||
|
||||
"@see OpenFileDialog for more details on functionality.\n\n"
|
||||
|
||||
"@ingroup FileSystem\n"
|
||||
);
|
||||
|
||||
void OpenFolderDialog::initPersistFields()
|
||||
{
|
||||
addField("fileMustExist", TypeFilename, Offset(mMustExistInDir, OpenFolderDialog), "File that must be in selected folder for it to be valid");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
#endif
|
||||
129
Engine/source/platformWin32/nativeDialogs/win32MsgBox.cpp
Normal file
129
Engine/source/platformWin32/nativeDialogs/win32MsgBox.cpp
Normal 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#include "platform/platformInput.h"
|
||||
#include "platform/nativeDialogs/msgBox.h"
|
||||
|
||||
#include "console/console.h"
|
||||
#include "core/strings/unicode.h"
|
||||
|
||||
#include "windowManager/platformWindowMgr.h"
|
||||
#include "windowManager/win32/win32Window.h"
|
||||
|
||||
struct _FlagMap
|
||||
{
|
||||
S32 num;
|
||||
U32 flag;
|
||||
};
|
||||
|
||||
static _FlagMap sgButtonMap[] =
|
||||
{
|
||||
{ MBOk, MB_OK },
|
||||
{ MBOkCancel, MB_OKCANCEL },
|
||||
{ MBRetryCancel, MB_RETRYCANCEL },
|
||||
{ MBSaveDontSave, MB_YESNO },
|
||||
{ MBSaveDontSaveCancel, MB_YESNOCANCEL },
|
||||
{ 0xffffffff, 0xffffffff }
|
||||
};
|
||||
|
||||
static _FlagMap sgIconMap[] =
|
||||
{
|
||||
{ MIWarning, MB_ICONWARNING },
|
||||
{ MIInformation, MB_ICONINFORMATION },
|
||||
{ MIQuestion, MB_ICONQUESTION },
|
||||
{ MIStop, MB_ICONSTOP },
|
||||
{ 0xffffffff, 0xffffffff }
|
||||
};
|
||||
|
||||
static _FlagMap sgMsgBoxRetMap[] =
|
||||
{
|
||||
{ IDCANCEL, MRCancel },
|
||||
{ IDNO, MRDontSave },
|
||||
{ IDOK, MROk},
|
||||
{ IDRETRY, MRRetry },
|
||||
{ IDYES, MROk },
|
||||
{ 0xffffffff, 0xffffffff }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static U32 getMaskFromID(_FlagMap *map, S32 id)
|
||||
{
|
||||
for(S32 i = 0;map[i].num != 0xffffffff && map[i].flag != 0xffffffff;++i)
|
||||
{
|
||||
if(map[i].num == id)
|
||||
return map[i].flag;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
S32 Platform::messageBox(const UTF8 *title, const UTF8 *message, MBButtons buttons, MBIcons icon)
|
||||
{
|
||||
PlatformWindow *pWindow = WindowManager->getFirstWindow();
|
||||
|
||||
// Get us rendering while we're blocking.
|
||||
winState.renderThreadBlocked = true;
|
||||
|
||||
// We don't keep a locked mouse or else we're going
|
||||
// to end up possibly locking our mouse out of the
|
||||
// message box area
|
||||
bool cursorLocked = pWindow && pWindow->isMouseLocked();
|
||||
if( cursorLocked )
|
||||
pWindow->setMouseLocked( false );
|
||||
|
||||
// Need a visible cursor to click stuff accurately
|
||||
bool cursorVisible = !pWindow || pWindow->isCursorVisible();
|
||||
if( !cursorVisible )
|
||||
pWindow->setCursorVisible(true);
|
||||
|
||||
#ifdef UNICODE
|
||||
const UTF16 *msg = convertUTF8toUTF16(message);
|
||||
const UTF16 *t = convertUTF8toUTF16(title);
|
||||
#else
|
||||
const UTF8 *msg = message;
|
||||
const UTF8 *t = title;
|
||||
#endif
|
||||
|
||||
HWND parent = pWindow ? static_cast<Win32Window*>(pWindow)->getHWND() : NULL;
|
||||
S32 ret = ::MessageBox( parent, msg, t, getMaskFromID(sgButtonMap, buttons) | getMaskFromID(sgIconMap, icon));
|
||||
|
||||
#ifdef UNICODE
|
||||
delete [] msg;
|
||||
delete [] t;
|
||||
#endif
|
||||
|
||||
// Dialog is gone.
|
||||
winState.renderThreadBlocked = false;
|
||||
|
||||
if( cursorVisible == false )
|
||||
pWindow->setCursorVisible( false );
|
||||
|
||||
if( cursorLocked == true )
|
||||
pWindow->setMouseLocked( true );
|
||||
|
||||
return getMaskFromID(sgMsgBoxRetMap, ret);
|
||||
}
|
||||
138
Engine/source/platformWin32/platformWin32.h
Normal file
138
Engine/source/platformWin32/platformWin32.h
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _PLATFORMWIN32_H_
|
||||
#define _PLATFORMWIN32_H_
|
||||
|
||||
// Sanity check for UNICODE
|
||||
#ifdef TORQUE_UNICODE
|
||||
# ifndef UNICODE
|
||||
# error "ERROR: You must have UNICODE defined in your preprocessor settings (ie, /DUNICODE) if you have TORQUE_UNICODE enabled in torqueConfig.h!"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _MRECT_H_
|
||||
#include "math/mRect.h"
|
||||
#endif
|
||||
|
||||
#if defined(TORQUE_COMPILER_CODEWARRIOR)
|
||||
# include <ansi_prefix.win32.h>
|
||||
# include <stdio.h>
|
||||
# include <string.h>
|
||||
#else
|
||||
# include <stdio.h>
|
||||
# include <string.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4996) // turn off "deprecation" warnings
|
||||
#endif
|
||||
|
||||
#define NOMINMAX
|
||||
|
||||
// Hack to get a correct HWND instead of using global state.
|
||||
extern HWND getWin32WindowHandle();
|
||||
|
||||
struct Win32PlatState
|
||||
{
|
||||
FILE *log_fp;
|
||||
HINSTANCE hinstOpenGL;
|
||||
HINSTANCE hinstGLU;
|
||||
HINSTANCE hinstOpenAL;
|
||||
HWND appWindow;
|
||||
HDC appDC;
|
||||
HINSTANCE appInstance;
|
||||
HGLRC hGLRC;
|
||||
DWORD processId;
|
||||
bool renderThreadBlocked;
|
||||
S32 nMessagesPerFrame; ///< The max number of messages to dispatch per frame
|
||||
HMENU appMenu; ///< The menu bar for the window
|
||||
#ifdef UNICODE
|
||||
//HIMC imeHandle;
|
||||
#endif
|
||||
|
||||
S32 desktopBitsPixel;
|
||||
S32 desktopWidth;
|
||||
S32 desktopHeight;
|
||||
S32 desktopClientWidth;
|
||||
S32 desktopClientHeight;
|
||||
U32 currentTime;
|
||||
|
||||
// minimum time per frame
|
||||
U32 sleepTicks;
|
||||
// are we in the background?
|
||||
bool backgrounded;
|
||||
|
||||
Win32PlatState();
|
||||
};
|
||||
|
||||
extern Win32PlatState winState;
|
||||
|
||||
extern void setModifierKeys( S32 modKeys );
|
||||
|
||||
//-------------------------------------- Helper Functions
|
||||
|
||||
template< typename T >
|
||||
inline void forwardslashT( T *str )
|
||||
{
|
||||
while(*str)
|
||||
{
|
||||
if(*str == '\\')
|
||||
*str = '/';
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void forwardslash( char* str )
|
||||
{
|
||||
forwardslashT< char >( str );
|
||||
}
|
||||
inline void forwardslash( WCHAR* str )
|
||||
{
|
||||
forwardslashT< WCHAR >( str );
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline void backslashT( T *str )
|
||||
{
|
||||
while(*str)
|
||||
{
|
||||
if(*str == '/')
|
||||
*str = '\\';
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void backslash( char* str )
|
||||
{
|
||||
backslashT< char >( str );
|
||||
}
|
||||
inline void backslash( WCHAR* str )
|
||||
{
|
||||
backslashT< WCHAR >( str );
|
||||
}
|
||||
|
||||
#endif //_PLATFORMWIN32_H_
|
||||
74
Engine/source/platformWin32/threads/mutex.cpp
Normal file
74
Engine/source/platformWin32/threads/mutex.cpp
Normal 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/threads/mutex.h"
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Mutex Data
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct PlatformMutexData
|
||||
{
|
||||
CRITICAL_SECTION mCriticalSection;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor/Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
mData = new PlatformMutexData;
|
||||
InitializeCriticalSection( &mData->mCriticalSection );
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
AssertFatal( TryEnterCriticalSection( &mData->mCriticalSection ), "Mutex::~Mutex - Critical section is locked!" );
|
||||
DeleteCriticalSection( &mData->mCriticalSection );
|
||||
SAFE_DELETE( mData );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool Mutex::lock( bool block )
|
||||
{
|
||||
AssertFatal( mData, "Mutex::lock - No data!" );
|
||||
|
||||
if( !block )
|
||||
return TryEnterCriticalSection( &mData->mCriticalSection );
|
||||
else
|
||||
{
|
||||
EnterCriticalSection( &mData->mCriticalSection );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void Mutex::unlock()
|
||||
{
|
||||
AssertFatal( mData, "Mutex::unlock - No data!" );
|
||||
LeaveCriticalSection( &mData->mCriticalSection );
|
||||
}
|
||||
208
Engine/source/platformWin32/threads/thread.cpp
Normal file
208
Engine/source/platformWin32/threads/thread.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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_OS_XENON
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#endif
|
||||
#include "platform/threads/thread.h"
|
||||
#include "platform/threads/semaphore.h"
|
||||
#include "platform/platformIntrinsics.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
#include <process.h> // [tom, 4/20/2006] for _beginthread()
|
||||
|
||||
ThreadManager::MainThreadId ThreadManager::smMainThreadId;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Thread data
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class PlatformThreadData
|
||||
{
|
||||
public:
|
||||
ThreadRunFunction mRunFunc;
|
||||
void* mRunArg;
|
||||
Thread* mThread;
|
||||
HANDLE mThreadHnd;
|
||||
Semaphore mGateway;
|
||||
U32 mThreadID;
|
||||
U32 mDead;
|
||||
|
||||
PlatformThreadData()
|
||||
{
|
||||
mRunFunc = NULL;
|
||||
mRunArg = 0;
|
||||
mThread = 0;
|
||||
mThreadHnd = 0;
|
||||
mDead = false;
|
||||
};
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Static Functions/Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Function: ThreadRunHandler
|
||||
// Summary: Calls Thread::run() with the thread's specified run argument.
|
||||
// Neccesary because Thread::run() is provided as a non-threaded
|
||||
// way to execute the thread's run function. So we have to keep
|
||||
// track of the thread's lock here.
|
||||
static unsigned int __stdcall ThreadRunHandler(void * arg)
|
||||
{
|
||||
PlatformThreadData* mData = reinterpret_cast<PlatformThreadData*>(arg);
|
||||
mData->mThreadID = GetCurrentThreadId();
|
||||
|
||||
ThreadManager::addThread(mData->mThread);
|
||||
mData->mThread->run(mData->mRunArg);
|
||||
ThreadManager::removeThread(mData->mThread);
|
||||
|
||||
bool autoDelete = mData->mThread->autoDelete;
|
||||
|
||||
mData->mThreadHnd = NULL; // mark as dead
|
||||
dCompareAndSwap( mData->mDead, false, true );
|
||||
mData->mGateway.release(); // don't access data after this.
|
||||
|
||||
if( autoDelete )
|
||||
delete mData->mThread; // Safe as we own the data.
|
||||
|
||||
_endthreadex( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor/Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Thread::Thread(ThreadRunFunction func /* = 0 */, void *arg /* = 0 */, bool start_thread /* = true */, bool autodelete /*= false*/)
|
||||
: autoDelete( autodelete )
|
||||
{
|
||||
AssertFatal( !start_thread, "Thread::Thread() - auto-starting threads from ctor has been disallowed since the run() method is virtual" );
|
||||
|
||||
mData = new PlatformThreadData;
|
||||
mData->mRunFunc = func;
|
||||
mData->mRunArg = arg;
|
||||
mData->mThread = this;
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
stop();
|
||||
if( isAlive() )
|
||||
join();
|
||||
|
||||
SAFE_DELETE(mData);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Thread::start( void* arg )
|
||||
{
|
||||
AssertFatal( !mData->mThreadHnd,
|
||||
"Thread::start() - thread already started" );
|
||||
|
||||
// cause start to block out other pthreads from using this Thread,
|
||||
// at least until ThreadRunHandler exits.
|
||||
mData->mGateway.acquire();
|
||||
|
||||
// reset the shouldStop flag, so we'll know when someone asks us to stop.
|
||||
shouldStop = false;
|
||||
|
||||
mData->mDead = false;
|
||||
|
||||
if( !mData->mRunArg )
|
||||
mData->mRunArg = arg;
|
||||
|
||||
mData->mThreadHnd = (HANDLE)_beginthreadex(0, 0, ThreadRunHandler, mData, 0, 0);
|
||||
}
|
||||
|
||||
bool Thread::join()
|
||||
{
|
||||
mData->mGateway.acquire();
|
||||
AssertFatal( !isAlive(), "Thread::join() - thread still alive after join" );
|
||||
mData->mGateway.release(); // release for further joins
|
||||
return true;
|
||||
}
|
||||
|
||||
void Thread::run(void *arg /* = 0 */)
|
||||
{
|
||||
if(mData->mRunFunc)
|
||||
mData->mRunFunc(arg);
|
||||
}
|
||||
|
||||
bool Thread::isAlive()
|
||||
{
|
||||
return ( !mData->mDead );
|
||||
}
|
||||
|
||||
U32 Thread::getId()
|
||||
{
|
||||
return mData->mThreadID;
|
||||
}
|
||||
|
||||
void Thread::_setName( const char* name )
|
||||
{
|
||||
#if defined( TORQUE_DEBUG ) && defined( TORQUE_COMPILER_VISUALC ) && defined( TORQUE_OS_WIN32 )
|
||||
|
||||
// See http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
|
||||
|
||||
#define MS_VC_EXCEPTION 0x406D1388
|
||||
|
||||
#pragma pack(push,8)
|
||||
typedef struct tagTHREADNAME_INFO
|
||||
{
|
||||
DWORD dwType; // Must be 0x1000.
|
||||
LPCSTR szName; // Pointer to name (in user addr space).
|
||||
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
||||
DWORD dwFlags; // Reserved for future use, must be zero.
|
||||
} THREADNAME_INFO;
|
||||
#pragma pack(pop)
|
||||
|
||||
Sleep(10);
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name;
|
||||
info.dwThreadID = getId();
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
{
|
||||
RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
U32 ThreadManager::getCurrentThreadId()
|
||||
{
|
||||
return GetCurrentThreadId();
|
||||
}
|
||||
|
||||
bool ThreadManager::compare(U32 threadId_1, U32 threadId_2)
|
||||
{
|
||||
return (threadId_1 == threadId_2);
|
||||
}
|
||||
579
Engine/source/platformWin32/videoInfo/wmiVideoInfo.cpp
Normal file
579
Engine/source/platformWin32/videoInfo/wmiVideoInfo.cpp
Normal file
|
|
@ -0,0 +1,579 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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_DCOM
|
||||
|
||||
//#include <comdef.h>
|
||||
#include <wbemidl.h>
|
||||
//#include <atlconv.h>
|
||||
#pragma comment(lib, "comsuppw.lib")
|
||||
#pragma comment(lib, "wbemuuid.lib")
|
||||
|
||||
#include "platformWin32/videoInfo/wmiVideoInfo.h"
|
||||
#include "core/util/safeRelease.h"
|
||||
#include "console/console.h"
|
||||
|
||||
// http://www.spectranaut.net/sourcecode/WMI.cpp
|
||||
|
||||
// Add constructor to GUID.
|
||||
struct MYGUID : public GUID
|
||||
{
|
||||
MYGUID( DWORD a, SHORT b, SHORT c, BYTE d, BYTE e, BYTE f, BYTE g, BYTE h, BYTE i, BYTE j, BYTE k )
|
||||
{
|
||||
Data1 = a;
|
||||
Data2 = b;
|
||||
Data3 = c;
|
||||
Data4[ 0 ] = d;
|
||||
Data4[ 1 ] = e;
|
||||
Data4[ 2 ] = f;
|
||||
Data4[ 3 ] = g;
|
||||
Data4[ 4 ] = h;
|
||||
Data4[ 5 ] = i;
|
||||
Data4[ 6 ] = j;
|
||||
Data4[ 7 ] = k;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DXGI decls for retrieving device info on Vista. We manually declare that
|
||||
// stuff here, so we don't depend on headers and compile on any setup. At
|
||||
// run-time, it depends on whether we can successfully load the DXGI DLL; if
|
||||
// not, nothing of this here will be used.
|
||||
|
||||
struct IDXGIObject;
|
||||
struct IDXGIFactory;
|
||||
struct IDXGIAdapter;
|
||||
struct IDXGIOutput;
|
||||
|
||||
struct DXGI_SWAP_CHAIN_DESC;
|
||||
struct DXGI_ADAPTER_DESC;
|
||||
|
||||
struct IDXGIObject : public IUnknown
|
||||
{
|
||||
virtual HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID, UINT, const void* ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID, const IUnknown* ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID, UINT*, void* ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetParent( REFIID, void** ) = 0;
|
||||
};
|
||||
|
||||
struct IDXGIFactory : public IDXGIObject
|
||||
{
|
||||
virtual HRESULT STDMETHODCALLTYPE EnumAdapters( UINT, IDXGIAdapter** ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE MakeWindowAssociation( HWND, UINT ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetWindowAssociation( HWND ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE CreateSwapChain( IUnknown*, DXGI_SWAP_CHAIN_DESC* ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter( HMODULE, IDXGIAdapter** ) = 0;
|
||||
};
|
||||
|
||||
struct IDXGIAdapter : public IDXGIObject
|
||||
{
|
||||
virtual HRESULT STDMETHODCALLTYPE EnumOutputs( UINT, IDXGIOutput** ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetDesc( DXGI_ADAPTER_DESC* ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE CheckInterfaceSupport( REFGUID, LARGE_INTEGER* ) = 0;
|
||||
};
|
||||
|
||||
struct DXGI_ADAPTER_DESC
|
||||
{
|
||||
WCHAR Description[ 128 ];
|
||||
UINT VendorId;
|
||||
UINT DeviceId;
|
||||
UINT SubSysId;
|
||||
UINT Revision;
|
||||
SIZE_T DedicatedVideoMemory;
|
||||
SIZE_T DedicatedSystemMemory;
|
||||
SIZE_T SharedSystemMemory;
|
||||
LUID AdapterLuid;
|
||||
};
|
||||
|
||||
static MYGUID IID_IDXGIFactory( 0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69 );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DXDIAG declarations.
|
||||
|
||||
struct DXDIAG_INIT_PARAMS
|
||||
{
|
||||
DWORD dwSize;
|
||||
DWORD dwDxDiagHeaderVersion;
|
||||
BOOL bAllowWHQLChecks;
|
||||
LPVOID pReserved;
|
||||
};
|
||||
|
||||
struct IDxDiagContainer : public IUnknown
|
||||
{
|
||||
virtual HRESULT STDMETHODCALLTYPE GetNumberOfChildContaiiners( DWORD* pdwCount ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE EnumChildContainerNames( DWORD dwIndex, LPWSTR pwszContainer, DWORD cchContainer ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetChildContainer( LPCWSTR pwszContainer, IDxDiagContainer** ppInstance ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetNumberOfProps( DWORD* pdwCount ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE EnumPropNames( DWORD dwIndex, LPWSTR pwszPropName, DWORD cchPropName ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetProp( LPCWSTR pwszPropName, VARIANT* pvarProp ) = 0;
|
||||
};
|
||||
|
||||
struct IDxDiagProvider : public IUnknown
|
||||
{
|
||||
virtual HRESULT STDMETHODCALLTYPE Initialize( DXDIAG_INIT_PARAMS* pParams ) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetRootContainer( IDxDiagContainer** ppInstance ) = 0;
|
||||
};
|
||||
|
||||
static MYGUID CLSID_DxDiagProvider( 0xA65B8071, 0x3BFE, 0x4213, 0x9A, 0x5B, 0x49, 0x1D, 0xA4, 0x46, 0x1C, 0xA7 );
|
||||
static MYGUID IID_IDxDiagProvider( 0x9C6B4CB0, 0x23F8, 0x49CC, 0xA3, 0xED, 0x45, 0xA5, 0x50, 0x00, 0xA6, 0xD2 );
|
||||
static MYGUID IID_IDxDiagContainer( 0x7D0F462F, 0x4064, 0x4862, 0xBC, 0x7F, 0x93, 0x3E, 0x50, 0x58, 0xC1, 0x0F );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
WCHAR *WMIVideoInfo::smPVIQueryTypeToWMIString [] =
|
||||
{
|
||||
L"MaxNumberControlled", //PVI_NumDevices
|
||||
L"Description", //PVI_Description
|
||||
L"Name", //PVI_Name
|
||||
L"VideoProcessor", //PVI_ChipSet
|
||||
L"DriverVersion", //PVI_DriverVersion
|
||||
L"AdapterRAM", //PVI_VRAM
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
WMIVideoInfo::WMIVideoInfo()
|
||||
: PlatformVideoInfo(),
|
||||
mLocator( NULL ),
|
||||
mServices( NULL ),
|
||||
mComInitialized( false ),
|
||||
mDXGIModule( NULL ),
|
||||
mDXGIFactory( NULL ),
|
||||
mDxDiagProvider( NULL )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
WMIVideoInfo::~WMIVideoInfo()
|
||||
{
|
||||
SAFE_RELEASE( mLocator );
|
||||
SAFE_RELEASE( mServices );
|
||||
|
||||
if( mDxDiagProvider )
|
||||
SAFE_RELEASE( mDxDiagProvider );
|
||||
|
||||
if( mDXGIFactory )
|
||||
SAFE_RELEASE( mDXGIFactory );
|
||||
if( mDXGIModule )
|
||||
FreeLibrary( ( HMODULE ) mDXGIModule );
|
||||
|
||||
if( mComInitialized )
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool WMIVideoInfo::_initialize()
|
||||
{
|
||||
// Init COM
|
||||
HRESULT hr = CoInitialize( NULL );
|
||||
mComInitialized = SUCCEEDED( hr );
|
||||
|
||||
if( !mComInitialized )
|
||||
return false;
|
||||
|
||||
bool success = false;
|
||||
|
||||
success |= _initializeDXGI();
|
||||
success |= _initializeDxDiag();
|
||||
success |= _initializeWMI();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool WMIVideoInfo::_initializeWMI()
|
||||
{
|
||||
//// Set security levels
|
||||
//hr = CoInitializeSecurity(
|
||||
// NULL,
|
||||
// -1, // COM authentication
|
||||
// NULL, // Authentication services
|
||||
// NULL, // Reserved
|
||||
// RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
|
||||
// RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
|
||||
// NULL, // Authentication info
|
||||
// EOAC_NONE, // Additional capabilities
|
||||
// NULL // Reserved
|
||||
// );
|
||||
|
||||
//if( FAILED( hr ) )
|
||||
//{
|
||||
// Con::errorf( "WMIVideoInfo: Failed to initialize com security." );
|
||||
// return false;
|
||||
//}
|
||||
|
||||
// Obtain the locator to WMI
|
||||
HRESULT hr = CoCreateInstance(
|
||||
CLSID_WbemLocator,
|
||||
0,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IWbemLocator,
|
||||
(void**)&mLocator
|
||||
);
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
Con::errorf( "WMIVideoInfo: Failed to create instance of IID_IWbemLocator." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect to the root\cimv2 namespace with
|
||||
// the current user and obtain pointer pSvc
|
||||
// to make IWbemServices calls.
|
||||
hr = mLocator->ConnectServer(
|
||||
BSTR(L"ROOT\\CIMV2"), // Object path of WMI namespace
|
||||
NULL, // User name. NULL = current user
|
||||
NULL, // User password. NULL = current
|
||||
0, // Locale. NULL indicates current
|
||||
NULL, // Security flags.
|
||||
0, // Authority (e.g. Kerberos)
|
||||
0, // Context object
|
||||
&mServices // pointer to IWbemServices proxy
|
||||
);
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
Con::errorf( "WMIVideoInfo: Connect server failed." );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set security levels on the proxy
|
||||
hr = CoSetProxyBlanket(
|
||||
mServices, // Indicates the proxy to set
|
||||
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
|
||||
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
|
||||
NULL, // Server principal name
|
||||
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
|
||||
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
|
||||
NULL, // client identity
|
||||
EOAC_NONE // proxy capabilities
|
||||
);
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
Con::errorf( "WMIVideoInfo: CoSetProxyBlanket failed" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WMIVideoInfo::_initializeDXGI()
|
||||
{
|
||||
// Try going for DXGI. Will only succeed on Vista.
|
||||
#if 0
|
||||
mDXGIModule = ( HMODULE ) LoadLibrary( L"dxgi.dll" );
|
||||
if( mDXGIModule != 0 )
|
||||
{
|
||||
typedef HRESULT (* CreateDXGIFactoryFuncType )( REFIID, void** );
|
||||
CreateDXGIFactoryFuncType factoryFunction =
|
||||
( CreateDXGIFactoryFuncType ) GetProcAddress( ( HMODULE ) mDXGIModule, "CreateDXGIFactory" );
|
||||
|
||||
if( factoryFunction && factoryFunction( IID_IDXGIFactory, ( void** ) &mDXGIFactory ) == S_OK )
|
||||
return true;
|
||||
else
|
||||
{
|
||||
FreeLibrary( ( HMODULE ) mDXGIModule );
|
||||
mDXGIModule = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WMIVideoInfo::_initializeDxDiag()
|
||||
{
|
||||
if( CoCreateInstance( CLSID_DxDiagProvider, NULL, CLSCTX_INPROC_SERVER, IID_IDxDiagProvider, ( void** ) &mDxDiagProvider ) == S_OK )
|
||||
{
|
||||
DXDIAG_INIT_PARAMS params;
|
||||
dMemset( ¶ms, 0, sizeof( DXDIAG_INIT_PARAMS ) );
|
||||
|
||||
params.dwSize = sizeof( DXDIAG_INIT_PARAMS );
|
||||
params.dwDxDiagHeaderVersion = 111;
|
||||
params.bAllowWHQLChecks = false;
|
||||
|
||||
HRESULT result = mDxDiagProvider->Initialize( ¶ms );
|
||||
if( result != S_OK )
|
||||
{
|
||||
Con::errorf( "WMIVideoInfo: DxDiag initialization failed (%i)", result );
|
||||
SAFE_RELEASE( mDxDiagProvider );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::printf( "WMIVideoInfo: DxDiag initialized" );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// http://msdn2.microsoft.com/en-us/library/aa394512.aspx
|
||||
//
|
||||
// The Win32_VideoController WMI class represents the capabilities and management capacity of the
|
||||
// video controller on a computer system running Windows.
|
||||
//
|
||||
// Starting with Windows Vista, hardware that is not compatible with Windows Display Driver Model (WDDM)
|
||||
// returns inaccurate property values for instances of this class.
|
||||
//
|
||||
// Windows Server 2003, Windows XP, Windows 2000, and Windows NT 4.0: This class is supported.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool WMIVideoInfo::_queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue )
|
||||
{
|
||||
if( _queryPropertyDXGI( queryType, adapterId, outValue ) )
|
||||
return true;
|
||||
else if( _queryPropertyDxDiag( queryType, adapterId, outValue ) )
|
||||
return true;
|
||||
else
|
||||
return _queryPropertyWMI( queryType, adapterId, outValue );
|
||||
}
|
||||
|
||||
bool WMIVideoInfo::_queryPropertyDxDiag( const PVIQueryType queryType, const U32 adapterId, String *outValue )
|
||||
{
|
||||
if( mDxDiagProvider != 0 )
|
||||
{
|
||||
IDxDiagContainer* rootContainer = 0;
|
||||
IDxDiagContainer* displayDevicesContainer = 0;
|
||||
IDxDiagContainer* deviceContainer = 0;
|
||||
|
||||
WCHAR adapterIdString[ 2 ];
|
||||
adapterIdString[ 0 ] = L'0' + adapterId;
|
||||
adapterIdString[ 1 ] = L'\0';
|
||||
|
||||
String value;
|
||||
if( mDxDiagProvider->GetRootContainer( &rootContainer ) == S_OK
|
||||
&& rootContainer->GetChildContainer( L"DxDiag_DisplayDevices", &displayDevicesContainer ) == S_OK
|
||||
&& displayDevicesContainer->GetChildContainer( adapterIdString, &deviceContainer ) == S_OK )
|
||||
{
|
||||
const WCHAR* propertyName = 0;
|
||||
|
||||
switch( queryType )
|
||||
{
|
||||
case PVI_Description:
|
||||
propertyName = L"szDescription";
|
||||
break;
|
||||
|
||||
case PVI_Name:
|
||||
propertyName = L"szDeviceName";
|
||||
break;
|
||||
|
||||
case PVI_ChipSet:
|
||||
propertyName = L"szChipType";
|
||||
break;
|
||||
|
||||
case PVI_DriverVersion:
|
||||
propertyName = L"szDriverVersion";
|
||||
break;
|
||||
|
||||
// Don't get VRAM via DxDiag as that won't tell us about the actual amount of dedicated
|
||||
// video memory but rather some dedicated+shared RAM value.
|
||||
}
|
||||
|
||||
if( propertyName )
|
||||
{
|
||||
VARIANT val;
|
||||
if( deviceContainer->GetProp( propertyName, &val ) == S_OK )
|
||||
switch( val.vt )
|
||||
{
|
||||
case VT_BSTR:
|
||||
value = String( val.bstrVal );
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertWarn( false, avar( "WMIVideoInfo: property type '%i' not implemented", val.vt ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rootContainer )
|
||||
SAFE_RELEASE( rootContainer );
|
||||
if( displayDevicesContainer )
|
||||
SAFE_RELEASE( displayDevicesContainer );
|
||||
if( deviceContainer )
|
||||
SAFE_RELEASE( deviceContainer );
|
||||
|
||||
if( value.isNotEmpty() )
|
||||
{
|
||||
// Try to get the DxDiag data into some canonical form. Otherwise, we
|
||||
// won't be giving the card profiler much opportunity for matching up
|
||||
// its data with profile scripts.
|
||||
|
||||
switch( queryType )
|
||||
{
|
||||
case PVI_ChipSet:
|
||||
if( value.compare( "ATI", 3, String::NoCase ) == 0 )
|
||||
value = "ATI Technologies Inc.";
|
||||
else if( value.compare( "NVIDIA", 6, String::NoCase ) == 0 )
|
||||
value = "NVIDIA";
|
||||
else if( value.compare( "INTEL", 5, String::NoCase ) == 0 )
|
||||
value = "INTEL";
|
||||
else if( value.compare( "MATROX", 6, String::NoCase ) == 0 )
|
||||
value = "MATROX";
|
||||
break;
|
||||
|
||||
case PVI_Description:
|
||||
if( value.compare( "ATI ", 4, String::NoCase ) == 0 )
|
||||
{
|
||||
value = value.substr( 4, value.length() - 4 );
|
||||
if( value.compare( " Series", 7, String::NoCase | String::Right ) == 0 )
|
||||
value = value.substr( 0, value.length() - 7 );
|
||||
}
|
||||
else if( value.compare( "NVIDIA ", 7, String::NoCase ) == 0 )
|
||||
value = value.substr( 7, value.length() - 7 );
|
||||
else if( value.compare( "INTEL ", 6, String::NoCase ) == 0 )
|
||||
value = value.substr( 6, value.length() - 6 );
|
||||
else if( value.compare( "MATROX ", 7, String::NoCase ) == 0 )
|
||||
value = value.substr( 7, value.length() - 7 );
|
||||
break;
|
||||
}
|
||||
|
||||
*outValue = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WMIVideoInfo::_queryPropertyDXGI( const PVIQueryType queryType, const U32 adapterId, String *outValue )
|
||||
{
|
||||
#if 0
|
||||
if( mDXGIFactory )
|
||||
{
|
||||
IDXGIAdapter* adapter;
|
||||
if( mDXGIFactory->EnumAdapters( adapterId, &adapter ) != S_OK )
|
||||
return false;
|
||||
|
||||
DXGI_ADAPTER_DESC desc;
|
||||
if( adapter->GetDesc( &desc ) != S_OK )
|
||||
{
|
||||
adapter->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
String value;
|
||||
switch( queryType )
|
||||
{
|
||||
case PVI_Description:
|
||||
value = String( desc.Description );
|
||||
break;
|
||||
|
||||
case PVI_Name:
|
||||
value = String( avar( "%i", desc.DeviceId ) );
|
||||
break;
|
||||
|
||||
case PVI_VRAM:
|
||||
value = String( avar( "%i", desc.DedicatedVideoMemory / 1048576 ) );
|
||||
break;
|
||||
|
||||
//RDTODO
|
||||
}
|
||||
|
||||
adapter->Release();
|
||||
*outValue = value;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WMIVideoInfo::_queryPropertyWMI( const PVIQueryType queryType, const U32 adapterId, String *outValue )
|
||||
{
|
||||
if( mServices == NULL )
|
||||
return false;
|
||||
|
||||
BSTR bstrWQL = SysAllocString(L"WQL");
|
||||
BSTR bstrPath = SysAllocString(L"select * from Win32_VideoController");
|
||||
IEnumWbemClassObject* enumerator;
|
||||
|
||||
// Use the IWbemServices pointer to make requests of WMI
|
||||
HRESULT hr = mServices->ExecQuery(bstrWQL, bstrPath, WBEM_FLAG_FORWARD_ONLY, NULL, &enumerator);
|
||||
|
||||
if( FAILED( hr ) )
|
||||
return false;
|
||||
|
||||
IWbemClassObject *adapter = NULL;
|
||||
ULONG uReturned;
|
||||
|
||||
// Get the appropriate adapter.
|
||||
for ( S32 i = 0; i <= adapterId; i++ )
|
||||
{
|
||||
hr = enumerator->Next(WBEM_INFINITE, 1, &adapter, &uReturned );
|
||||
|
||||
if ( FAILED( hr ) || uReturned == 0 )
|
||||
{
|
||||
enumerator->Release();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Now get the property
|
||||
VARIANT v;
|
||||
hr = adapter->Get( smPVIQueryTypeToWMIString[queryType], 0, &v, NULL, NULL );
|
||||
|
||||
bool result = SUCCEEDED( hr );
|
||||
|
||||
if ( result )
|
||||
{
|
||||
switch( v.vt )
|
||||
{
|
||||
case VT_I4:
|
||||
{
|
||||
LONG longVal = v.lVal;
|
||||
|
||||
if( queryType == PVI_VRAM )
|
||||
longVal = longVal >> 20; // Convert to megabytes
|
||||
|
||||
*outValue = String::ToString( (S32)longVal );
|
||||
break;
|
||||
}
|
||||
|
||||
case VT_UI4:
|
||||
{
|
||||
*outValue = String::ToString( (U32)v.ulVal );
|
||||
break;
|
||||
}
|
||||
|
||||
case VT_BSTR:
|
||||
{
|
||||
*outValue = String( v.bstrVal );
|
||||
break;
|
||||
}
|
||||
case VT_LPSTR:
|
||||
case VT_LPWSTR:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
adapter->Release();
|
||||
enumerator->Release();
|
||||
|
||||
return result;
|
||||
}
|
||||
63
Engine/source/platformWin32/videoInfo/wmiVideoInfo.h
Normal file
63
Engine/source/platformWin32/videoInfo/wmiVideoInfo.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _WMI_CARDINFO_H_
|
||||
#define _WMI_CARDINFO_H_
|
||||
|
||||
#include "platform/platformVideoInfo.h"
|
||||
|
||||
struct IWbemLocator;
|
||||
struct IWbemServices;
|
||||
|
||||
struct IDXGIFactory;
|
||||
struct IDxDiagProvider;
|
||||
|
||||
class WMIVideoInfo : public PlatformVideoInfo
|
||||
{
|
||||
private:
|
||||
IWbemLocator *mLocator;
|
||||
IWbemServices *mServices;
|
||||
bool mComInitialized;
|
||||
|
||||
void* mDXGIModule;
|
||||
IDXGIFactory* mDXGIFactory;
|
||||
IDxDiagProvider* mDxDiagProvider;
|
||||
|
||||
bool _initializeDXGI();
|
||||
bool _initializeDxDiag();
|
||||
bool _initializeWMI();
|
||||
|
||||
bool _queryPropertyDXGI( const PVIQueryType queryType, const U32 adapterId, String *outValue );
|
||||
bool _queryPropertyDxDiag( const PVIQueryType queryType, const U32 adapterId, String *outValue );
|
||||
bool _queryPropertyWMI( const PVIQueryType queryType, const U32 adapterId, String *outValue );
|
||||
|
||||
protected:
|
||||
static WCHAR *smPVIQueryTypeToWMIString [];
|
||||
bool _queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue );
|
||||
bool _initialize();
|
||||
|
||||
public:
|
||||
WMIVideoInfo();
|
||||
~WMIVideoInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
207
Engine/source/platformWin32/winAsmBlit.cpp
Normal file
207
Engine/source/platformWin32/winAsmBlit.cpp
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "math/mMath.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#include "gfx/bitmap/bitmapUtils.h"
|
||||
|
||||
#if !defined(__MWERKS__) && defined(_MSC_VER)
|
||||
#define asm _asm
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void bitmapExtrude5551_asm(const void *srcMip, void *mip, U32 height, U32 width)
|
||||
{
|
||||
const U16 *src = (const U16 *) srcMip;
|
||||
U16 *dst = (U16 *) mip;
|
||||
U32 stride = width << 1;
|
||||
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
for(U32 x = 0; x < width; x++)
|
||||
{
|
||||
U32 a = src[0];
|
||||
U32 b = src[1];
|
||||
U32 c = src[stride];
|
||||
U32 d = src[stride+1];
|
||||
dst[x] = ((((a >> 11) + (b >> 11) + (c >> 11) + (d >> 11)) >> 2) << 11) |
|
||||
((( ((a >> 6) & 0x1f) + ((b >> 6) & 0x1f) + ((c >> 6) & 0x1f) + ((d >> 6) & 0x1F) ) >> 2) << 6) |
|
||||
((( ((a >> 1) & 0x1F) + ((b >> 1) & 0x1F) + ((c >> 1) & 0x1f) + ((d >> 1) & 0x1f)) >> 2) << 1);
|
||||
src += 2;
|
||||
}
|
||||
src += stride;
|
||||
dst += width;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void bitmapExtrudeRGB_mmx(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
|
||||
{
|
||||
if (srcHeight == 1 || srcWidth == 1) {
|
||||
bitmapExtrudeRGB_c(srcMip, mip, srcHeight, srcWidth);
|
||||
return;
|
||||
}
|
||||
|
||||
U32 width = srcWidth >> 1;
|
||||
U32 height = srcHeight >> 1;
|
||||
|
||||
if (width <= 1)
|
||||
{
|
||||
bitmapExtrudeRGB_c(srcMip, mip, srcHeight, srcWidth);
|
||||
return;
|
||||
}
|
||||
|
||||
U64 ZERO = 0x0000000000000000;
|
||||
const U8 *src = (const U8 *) srcMip;
|
||||
U8 *dst = (U8 *) mip;
|
||||
U32 srcStride = (width << 1) * 3;
|
||||
U32 dstStride = width * 3;
|
||||
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
asm
|
||||
{
|
||||
mov eax, src
|
||||
mov ebx, eax
|
||||
add ebx, srcStride
|
||||
mov ecx, dst
|
||||
mov edx, width
|
||||
|
||||
//--------------------------------------
|
||||
row_loop:
|
||||
|
||||
punpcklbw mm0, [eax]
|
||||
psrlw mm0, 8
|
||||
|
||||
punpcklbw mm1, [eax+3]
|
||||
psrlw mm1, 8
|
||||
paddw mm0, mm1
|
||||
|
||||
punpcklbw mm1, [ebx]
|
||||
psrlw mm1, 8
|
||||
paddw mm0, mm1
|
||||
|
||||
punpcklbw mm1, [ebx+3]
|
||||
psrlw mm1, 8
|
||||
paddw mm0, mm1
|
||||
|
||||
psrlw mm0, 2
|
||||
//pxor mm1, mm1
|
||||
packuswb mm0, ZERO // mm1
|
||||
|
||||
movd [ecx], mm0
|
||||
add eax, 6
|
||||
add ebx, 6
|
||||
add ecx, 3
|
||||
dec edx
|
||||
jnz row_loop
|
||||
}
|
||||
src += srcStride + srcStride; // advance to next line
|
||||
dst += dstStride;
|
||||
}
|
||||
asm
|
||||
{
|
||||
emms
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void bitmapConvertRGB_to_5551_mmx(U8 *src, U32 pixels)
|
||||
{
|
||||
U64 MULFACT = 0x0008200000082000; // RGB quad word multiplier
|
||||
U64 REDBLUE = 0x00f800f800f800f8; // Red-Blue mask
|
||||
U64 GREEN = 0x0000f8000000f800; // Green mask
|
||||
U64 ALPHA = 0x0000000000010001; // 100% Alpha mask
|
||||
U64 ZERO = 0x0000000000000000;
|
||||
|
||||
U32 evenPixels = pixels >> 1; // the MMX loop can only do an even number
|
||||
U32 oddPixels = pixels & 1; // of pixels since it processes 2 at a time
|
||||
|
||||
U16 *dst = (U16*)src;
|
||||
|
||||
if (evenPixels)
|
||||
{
|
||||
asm
|
||||
{
|
||||
mov eax, src // YES, src = dst at start
|
||||
mov ebx, dst // convert image in place
|
||||
mov edx, evenPixels
|
||||
|
||||
pixel_loop2:
|
||||
movd mm0, [eax] // get first 24-bit pixel
|
||||
movd mm1, [eax+3] // get second 24-bit pixel
|
||||
punpckldq mm0, mm1 // put second in high dword
|
||||
movq mm1, mm0 // save the original data
|
||||
pand mm0, REDBLUE // mask out all but the 5MSBits of red and blue
|
||||
pmaddwd mm0, MULFACT // multiply each word by
|
||||
// 2**13, 2**3, 2**13, 2**3 and add results
|
||||
pand mm1, GREEN // mask out all but the 5MSBits of green
|
||||
por mm0, mm1 // combine the red, green, and blue bits
|
||||
psrld mm0, 6 // shift into position
|
||||
packssdw mm0, ZERO // pack into single dword
|
||||
pslld mm0, 1 // shift into final position
|
||||
por mm0, ALPHA // add the alpha bit
|
||||
movd [ebx], mm0
|
||||
|
||||
add eax, 6
|
||||
add ebx, 4
|
||||
dec edx
|
||||
jnz pixel_loop2
|
||||
|
||||
mov src, eax
|
||||
mov dst, ebx
|
||||
emms
|
||||
}
|
||||
}
|
||||
|
||||
if (oddPixels)
|
||||
{
|
||||
U32 r = src[0] >> 3;
|
||||
U32 g = src[1] >> 3;
|
||||
U32 b = src[2] >> 3;
|
||||
|
||||
*dst = (b << 1) | (g << 6) | (r << 11) | 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void PlatformBlitInit()
|
||||
{
|
||||
bitmapExtrude5551 = bitmapExtrude5551_asm;
|
||||
bitmapExtrudeRGB = bitmapExtrudeRGB_c;
|
||||
|
||||
if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX)
|
||||
{
|
||||
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
|
||||
bitmapExtrudeRGB = bitmapExtrudeRGB_mmx;
|
||||
bitmapConvertRGB_to_5551 = bitmapConvertRGB_to_5551_mmx;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
84
Engine/source/platformWin32/winAsync.cpp
Normal file
84
Engine/source/platformWin32/winAsync.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// For VS2005.
|
||||
#define _WIN32_WINNT 0x501
|
||||
#ifndef TORQUE_OS_XENON
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#endif
|
||||
#include "platform/async/asyncUpdate.h"
|
||||
|
||||
|
||||
AsyncUpdateThread::AsyncUpdateThread( String name, AsyncUpdateList* updateList )
|
||||
: Parent( 0, 0, false, false ),
|
||||
mUpdateList( updateList ),
|
||||
mName( name )
|
||||
{
|
||||
// Create an auto-reset event in non-signaled state.
|
||||
mUpdateEvent = CreateEvent( NULL, false, false, NULL );
|
||||
}
|
||||
|
||||
AsyncUpdateThread::~AsyncUpdateThread()
|
||||
{
|
||||
CloseHandle( ( HANDLE ) mUpdateEvent );
|
||||
}
|
||||
|
||||
void AsyncUpdateThread::_waitForEventAndReset()
|
||||
{
|
||||
WaitForSingleObject( ( HANDLE ) mUpdateEvent, INFINITE );
|
||||
}
|
||||
|
||||
void AsyncUpdateThread::triggerUpdate()
|
||||
{
|
||||
SetEvent( ( HANDLE ) mUpdateEvent );
|
||||
}
|
||||
|
||||
AsyncPeriodicUpdateThread::AsyncPeriodicUpdateThread( String name,
|
||||
AsyncUpdateList* updateList,
|
||||
U32 intervalMS )
|
||||
: Parent( name, updateList )
|
||||
{
|
||||
mUpdateTimer = CreateWaitableTimer( NULL, FALSE, NULL );
|
||||
|
||||
// This is a bit contrived. The 'dueTime' is in 100 nanosecond intervals
|
||||
// and relative if it is negative. The period is in milliseconds.
|
||||
|
||||
LARGE_INTEGER deltaTime;
|
||||
deltaTime.QuadPart = - LONGLONG( intervalMS * 10 /* micro */ * 1000 /* milli */ );
|
||||
|
||||
SetWaitableTimer( ( HANDLE ) mUpdateTimer, &deltaTime, intervalMS, NULL, NULL, FALSE );
|
||||
}
|
||||
|
||||
AsyncPeriodicUpdateThread::~AsyncPeriodicUpdateThread()
|
||||
{
|
||||
CloseHandle( ( HANDLE ) mUpdateTimer );
|
||||
}
|
||||
|
||||
void AsyncPeriodicUpdateThread::_waitForEventAndReset()
|
||||
{
|
||||
HANDLE handles[ 2 ];
|
||||
|
||||
handles[ 0 ] = ( HANDLE ) mUpdateEvent;
|
||||
handles[ 1 ] = ( HANDLE ) mUpdateTimer;
|
||||
|
||||
WaitForMultipleObjects( 2, handles, FALSE, INFINITE );
|
||||
}
|
||||
197
Engine/source/platformWin32/winCPUInfo.cpp
Normal file
197
Engine/source/platformWin32/winCPUInfo.cpp
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platformWin32/platformWin32.h"
|
||||
#include "console/console.h"
|
||||
#include "core/stringTable.h"
|
||||
#include <math.h>
|
||||
|
||||
Platform::SystemInfo_struct Platform::SystemInfo;
|
||||
extern void PlatformBlitInit();
|
||||
extern void SetProcessorInfo(Platform::SystemInfo_struct::Processor& pInfo,
|
||||
char* vendor, U32 processor, U32 properties, U32 properties2); // platform/platformCPU.cc
|
||||
|
||||
|
||||
#if defined(TORQUE_SUPPORTS_NASM)
|
||||
// asm cpu detection routine from platform code
|
||||
extern "C"
|
||||
{
|
||||
void detectX86CPUInfo(char *vendor, U32 *processor, U32 *properties);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void Processor::init()
|
||||
{
|
||||
// Reference:
|
||||
// www.cyrix.com
|
||||
// www.amd.com
|
||||
// www.intel.com
|
||||
// http://developer.intel.com/design/PentiumII/manuals/24512701.pdf
|
||||
|
||||
Con::printf("Processor Init:");
|
||||
|
||||
Platform::SystemInfo.processor.type = CPU_X86Compatible;
|
||||
Platform::SystemInfo.processor.name = StringTable->insert("Unknown x86 Compatible");
|
||||
Platform::SystemInfo.processor.mhz = 0;
|
||||
Platform::SystemInfo.processor.properties = CPU_PROP_C | CPU_PROP_LE;
|
||||
|
||||
char vendor[13] = {0,};
|
||||
U32 properties = 0;
|
||||
U32 processor = 0;
|
||||
U32 properties2 = 0;
|
||||
|
||||
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
|
||||
__asm
|
||||
{
|
||||
//--------------------------------------
|
||||
// is CPUID supported
|
||||
push ebx
|
||||
push edx
|
||||
push ecx
|
||||
pushfd
|
||||
pushfd // save EFLAGS to stack
|
||||
pop eax // move EFLAGS into EAX
|
||||
mov ebx, eax
|
||||
xor eax, 0x200000 // flip bit 21
|
||||
push eax
|
||||
popfd // restore EFLAGS
|
||||
pushfd
|
||||
pop eax
|
||||
cmp eax, ebx
|
||||
jz EXIT // doesn't support CPUID instruction
|
||||
|
||||
//--------------------------------------
|
||||
// Get Vendor Informaion using CPUID eax==0
|
||||
xor eax, eax
|
||||
cpuid
|
||||
|
||||
mov DWORD PTR vendor, ebx
|
||||
mov DWORD PTR vendor+4, edx
|
||||
mov DWORD PTR vendor+8, ecx
|
||||
|
||||
// get Generic Extended CPUID info
|
||||
mov eax, 1
|
||||
cpuid // eax=1, so cpuid queries feature information
|
||||
|
||||
and eax, 0x0fff3fff
|
||||
mov processor, eax // just store the model bits
|
||||
mov properties, edx
|
||||
mov properties2, ecx
|
||||
|
||||
// Want to check for 3DNow(tm). Need to see if extended cpuid functions present.
|
||||
mov eax, 0x80000000
|
||||
cpuid
|
||||
cmp eax, 0x80000000
|
||||
jbe MAYBE_3DLATER
|
||||
mov eax, 0x80000001
|
||||
cpuid
|
||||
and edx, 0x80000000 // 3DNow if bit 31 set -> put bit in our properties
|
||||
or properties, edx
|
||||
MAYBE_3DLATER:
|
||||
|
||||
|
||||
EXIT:
|
||||
popfd
|
||||
pop ecx
|
||||
pop edx
|
||||
pop ebx
|
||||
}
|
||||
#elif defined(TORQUE_SUPPORTS_NASM)
|
||||
|
||||
detectX86CPUInfo(vendor, &processor, &properties);
|
||||
|
||||
#endif
|
||||
|
||||
SetProcessorInfo(Platform::SystemInfo.processor, vendor, processor, properties, properties2);
|
||||
|
||||
// now calculate speed of processor...
|
||||
U32 nearmhz = 0; // nearest rounded mhz
|
||||
U32 mhz = 0; // calculated value.
|
||||
|
||||
LONG result;
|
||||
DWORD data = 0;
|
||||
DWORD dataSize = 4;
|
||||
HKEY hKey;
|
||||
|
||||
result = ::RegOpenKeyExA (HKEY_LOCAL_MACHINE,"Hardware\\Description\\System\\CentralProcessor\\0", 0, KEY_QUERY_VALUE, &hKey);
|
||||
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
result = ::RegQueryValueExA (hKey, "~MHz",NULL, NULL,(LPBYTE)&data, &dataSize);
|
||||
|
||||
if (result == ERROR_SUCCESS)
|
||||
nearmhz = mhz = data;
|
||||
|
||||
::RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
Platform::SystemInfo.processor.mhz = mhz;
|
||||
|
||||
if (mhz==0)
|
||||
{
|
||||
Con::printf(" %s, (Unknown) Mhz", Platform::SystemInfo.processor.name);
|
||||
// stick SOMETHING in so it isn't ZERO.
|
||||
Platform::SystemInfo.processor.mhz = 200; // seems a decent value.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nearmhz >= 1000)
|
||||
Con::printf(" %s, ~%.2f Ghz", Platform::SystemInfo.processor.name, ((float)nearmhz)/1000.0f);
|
||||
else
|
||||
Con::printf(" %s, ~%d Mhz", Platform::SystemInfo.processor.name, nearmhz);
|
||||
if (nearmhz != mhz)
|
||||
{
|
||||
if (mhz >= 1000)
|
||||
Con::printf(" (timed at roughly %.2f Ghz)", ((float)mhz)/1000.0f);
|
||||
else
|
||||
Con::printf(" (timed at roughly %d Mhz)", mhz);
|
||||
}
|
||||
}
|
||||
|
||||
if( Platform::SystemInfo.processor.numAvailableCores > 0
|
||||
|| Platform::SystemInfo.processor.numPhysicalProcessors > 0
|
||||
|| Platform::SystemInfo.processor.isHyperThreaded )
|
||||
Platform::SystemInfo.processor.properties |= CPU_PROP_MP;
|
||||
|
||||
if (Platform::SystemInfo.processor.properties & CPU_PROP_FPU)
|
||||
Con::printf( " FPU detected" );
|
||||
if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX)
|
||||
Con::printf( " MMX detected" );
|
||||
if (Platform::SystemInfo.processor.properties & CPU_PROP_3DNOW)
|
||||
Con::printf( " 3DNow detected" );
|
||||
if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE)
|
||||
Con::printf( " SSE detected" );
|
||||
if( Platform::SystemInfo.processor.properties & CPU_PROP_SSE2 )
|
||||
Con::printf( " SSE2 detected" );
|
||||
if( Platform::SystemInfo.processor.isHyperThreaded )
|
||||
Con::printf( " HT detected" );
|
||||
if( Platform::SystemInfo.processor.properties & CPU_PROP_MP )
|
||||
Con::printf( " MP detected [%i cores, %i logical, %i physical]",
|
||||
Platform::SystemInfo.processor.numAvailableCores,
|
||||
Platform::SystemInfo.processor.numLogicalProcessors,
|
||||
Platform::SystemInfo.processor.numPhysicalProcessors );
|
||||
Con::printf(" ");
|
||||
|
||||
PlatformBlitInit();
|
||||
}
|
||||
341
Engine/source/platformWin32/winConsole.cpp
Normal file
341
Engine/source/platformWin32/winConsole.cpp
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "core/util/rawData.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "core/strings/unicode.h"
|
||||
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#include "platformWin32/winConsole.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/util/journal/process.h"
|
||||
|
||||
|
||||
WinConsole *WindowsConsole = NULL;
|
||||
|
||||
namespace Con
|
||||
{
|
||||
extern bool alwaysUseDebugOutput;
|
||||
}
|
||||
|
||||
ConsoleFunction(enableWinConsole, void, 2, 2, "enableWinConsole(bool);")
|
||||
{
|
||||
argc;
|
||||
WindowsConsole->enable(dAtob(argv[1]));
|
||||
}
|
||||
|
||||
void WinConsole::create()
|
||||
{
|
||||
if( !WindowsConsole )
|
||||
WindowsConsole = new WinConsole();
|
||||
}
|
||||
|
||||
void WinConsole::destroy()
|
||||
{
|
||||
if( WindowsConsole )
|
||||
delete WindowsConsole;
|
||||
WindowsConsole = NULL;
|
||||
}
|
||||
|
||||
void WinConsole::enable(bool enabled)
|
||||
{
|
||||
winConsoleEnabled = enabled;
|
||||
if(winConsoleEnabled)
|
||||
{
|
||||
AllocConsole();
|
||||
const char *title = Con::getVariable("Con::WindowTitle");
|
||||
if (title && *title)
|
||||
{
|
||||
#ifdef UNICODE
|
||||
UTF16 buf[512];
|
||||
convertUTF8toUTF16((UTF8 *)title, buf, sizeof(buf));
|
||||
SetConsoleTitle(buf);
|
||||
#else
|
||||
SetConsoleTitle(title);
|
||||
#endif
|
||||
}
|
||||
stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
stdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
stdErr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
printf("%s", Con::getVariable("Con::Prompt"));
|
||||
}
|
||||
}
|
||||
|
||||
bool WinConsole::isEnabled()
|
||||
{
|
||||
if ( WindowsConsole )
|
||||
return WindowsConsole->winConsoleEnabled;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void winConsoleConsumer(U32 level, const char *line)
|
||||
{
|
||||
if (WindowsConsole)
|
||||
{
|
||||
WindowsConsole->processConsoleLine(line);
|
||||
#ifndef TORQUE_SHIPPING
|
||||
// see console.cpp for a description of Con::alwaysUseDebugOutput
|
||||
if( level == ConsoleLogEntry::Error || Con::alwaysUseDebugOutput)
|
||||
{
|
||||
// [rene, 04/05/2008] This is incorrect. Should do conversion from UTF8 here.
|
||||
// Skipping for the sake of speed. Not meant to be seen by user anyway.
|
||||
OutputDebugStringA( line );
|
||||
OutputDebugStringA( "\n" );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
WinConsole::WinConsole()
|
||||
{
|
||||
for (S32 iIndex = 0; iIndex < MAX_CMDS; iIndex ++)
|
||||
rgCmds[iIndex][0] = '\0';
|
||||
|
||||
iCmdIndex = 0;
|
||||
winConsoleEnabled = false;
|
||||
Con::addConsumer(winConsoleConsumer);
|
||||
inpos = 0;
|
||||
lineOutput = false;
|
||||
|
||||
Process::notify(this, &WinConsole::process, PROCESS_LAST_ORDER);
|
||||
}
|
||||
|
||||
WinConsole::~WinConsole()
|
||||
{
|
||||
Process::remove(this, &WinConsole::process);
|
||||
Con::removeConsumer(winConsoleConsumer);
|
||||
}
|
||||
|
||||
void WinConsole::printf(const char *s, ...)
|
||||
{
|
||||
// Get the line into a buffer.
|
||||
static const int BufSize = 4096;
|
||||
static char buffer[4096];
|
||||
DWORD bytes;
|
||||
va_list args;
|
||||
va_start(args, s);
|
||||
_vsnprintf(buffer, BufSize, s, args);
|
||||
// Replace tabs with carats, like the "real" console does.
|
||||
char *pos = buffer;
|
||||
while (*pos) {
|
||||
if (*pos == '\t') {
|
||||
*pos = '^';
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
// Axe the color characters.
|
||||
Con::stripColorChars(buffer);
|
||||
// Print it.
|
||||
WriteFile(stdOut, buffer, strlen(buffer), &bytes, NULL);
|
||||
FlushFileBuffers( stdOut );
|
||||
}
|
||||
|
||||
void WinConsole::processConsoleLine(const char *consoleLine)
|
||||
{
|
||||
if(winConsoleEnabled)
|
||||
{
|
||||
inbuf[inpos] = 0;
|
||||
if(lineOutput)
|
||||
printf("%s\n", consoleLine);
|
||||
else
|
||||
printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf);
|
||||
}
|
||||
}
|
||||
|
||||
void WinConsole::process()
|
||||
{
|
||||
if(winConsoleEnabled)
|
||||
{
|
||||
DWORD numEvents;
|
||||
GetNumberOfConsoleInputEvents(stdIn, &numEvents);
|
||||
if(numEvents)
|
||||
{
|
||||
INPUT_RECORD rec[20];
|
||||
char outbuf[512];
|
||||
S32 outpos = 0;
|
||||
|
||||
ReadConsoleInput(stdIn, rec, 20, &numEvents);
|
||||
DWORD i;
|
||||
for(i = 0; i < numEvents; i++)
|
||||
{
|
||||
if(rec[i].EventType == KEY_EVENT)
|
||||
{
|
||||
KEY_EVENT_RECORD *ke = &(rec[i].Event.KeyEvent);
|
||||
if(ke->bKeyDown)
|
||||
{
|
||||
switch (ke->uChar.AsciiChar)
|
||||
{
|
||||
// If no ASCII char, check if it's a handled virtual key
|
||||
case 0:
|
||||
switch (ke->wVirtualKeyCode)
|
||||
{
|
||||
// UP ARROW
|
||||
case 0x26 :
|
||||
// Go to the previous command in the cyclic array
|
||||
if ((-- iCmdIndex) < 0)
|
||||
iCmdIndex = MAX_CMDS - 1;
|
||||
|
||||
// If this command isn't empty ...
|
||||
if (rgCmds[iCmdIndex][0] != '\0')
|
||||
{
|
||||
// Obliterate current displayed text
|
||||
for (S32 i = outpos = 0; i < inpos; i ++)
|
||||
{
|
||||
outbuf[outpos ++] = '\b';
|
||||
outbuf[outpos ++] = ' ';
|
||||
outbuf[outpos ++] = '\b';
|
||||
}
|
||||
|
||||
// Copy command into command and display buffers
|
||||
for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
|
||||
{
|
||||
outbuf[outpos] = rgCmds[iCmdIndex][inpos];
|
||||
inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
|
||||
}
|
||||
}
|
||||
// If previous is empty, stay on current command
|
||||
else if ((++ iCmdIndex) >= MAX_CMDS)
|
||||
{
|
||||
iCmdIndex = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// DOWN ARROW
|
||||
case 0x28 : {
|
||||
// Go to the next command in the command array, if
|
||||
// it isn't empty
|
||||
if (rgCmds[iCmdIndex][0] != '\0' && (++ iCmdIndex) >= MAX_CMDS)
|
||||
iCmdIndex = 0;
|
||||
|
||||
// Obliterate current displayed text
|
||||
for (S32 i = outpos = 0; i < inpos; i ++)
|
||||
{
|
||||
outbuf[outpos ++] = '\b';
|
||||
outbuf[outpos ++] = ' ';
|
||||
outbuf[outpos ++] = '\b';
|
||||
}
|
||||
|
||||
// Copy command into command and display buffers
|
||||
for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
|
||||
{
|
||||
outbuf[outpos] = rgCmds[iCmdIndex][inpos];
|
||||
inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// LEFT ARROW
|
||||
case 0x25 :
|
||||
break;
|
||||
|
||||
// RIGHT ARROW
|
||||
case 0x27 :
|
||||
break;
|
||||
|
||||
default :
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '\b':
|
||||
if(inpos > 0)
|
||||
{
|
||||
outbuf[outpos++] = '\b';
|
||||
outbuf[outpos++] = ' ';
|
||||
outbuf[outpos++] = '\b';
|
||||
inpos--;
|
||||
}
|
||||
break;
|
||||
case '\t':
|
||||
// In the output buffer, we're going to have to erase the current line (in case
|
||||
// we're cycling through various completions) and write out the whole input
|
||||
// buffer, so (inpos * 3) + complen <= 512. Should be OK. The input buffer is
|
||||
// also 512 chars long so that constraint will also be fine for the input buffer.
|
||||
{
|
||||
// Erase the current line.
|
||||
U32 i;
|
||||
for (i = 0; i < inpos; i++) {
|
||||
outbuf[outpos++] = '\b';
|
||||
outbuf[outpos++] = ' ';
|
||||
outbuf[outpos++] = '\b';
|
||||
}
|
||||
// Modify the input buffer with the completion.
|
||||
U32 maxlen = 512 - (inpos * 3);
|
||||
if (ke->dwControlKeyState & SHIFT_PRESSED) {
|
||||
inpos = Con::tabComplete(inbuf, inpos, maxlen, false);
|
||||
}
|
||||
else {
|
||||
inpos = Con::tabComplete(inbuf, inpos, maxlen, true);
|
||||
}
|
||||
// Copy the input buffer to the output.
|
||||
for (i = 0; i < inpos; i++) {
|
||||
outbuf[outpos++] = inbuf[i];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
case '\r':
|
||||
outbuf[outpos++] = '\r';
|
||||
outbuf[outpos++] = '\n';
|
||||
|
||||
inbuf[inpos] = 0;
|
||||
outbuf[outpos] = 0;
|
||||
printf("%s", outbuf);
|
||||
|
||||
// Pass the line along to the console for execution.
|
||||
{
|
||||
RawData rd;
|
||||
rd.size = inpos + 1;
|
||||
rd.data = ( S8* ) inbuf;
|
||||
|
||||
Con::smConsoleInput.trigger(rd);
|
||||
}
|
||||
|
||||
// If we've gone off the end of our array, wrap
|
||||
// back to the beginning
|
||||
if (iCmdIndex >= MAX_CMDS)
|
||||
iCmdIndex %= MAX_CMDS;
|
||||
|
||||
// Put the new command into the array
|
||||
strcpy(rgCmds[iCmdIndex ++], inbuf);
|
||||
|
||||
printf("%s", Con::getVariable("Con::Prompt"));
|
||||
inpos = outpos = 0;
|
||||
break;
|
||||
default:
|
||||
inbuf[inpos++] = ke->uChar.AsciiChar;
|
||||
outbuf[outpos++] = ke->uChar.AsciiChar;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(outpos)
|
||||
{
|
||||
outbuf[outpos] = 0;
|
||||
printf("%s", outbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Engine/source/platformWin32/winConsole.h
Normal file
65
Engine/source/platformWin32/winConsole.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _WINCONSOLE_H_
|
||||
#define _WINCONSOLE_H_
|
||||
|
||||
#define MAX_CMDS 10
|
||||
#ifndef _CONSOLE_H_
|
||||
#include "console/console.h"
|
||||
#endif
|
||||
#ifndef _EVENT_H_
|
||||
#include "platform/event.h"
|
||||
#endif
|
||||
|
||||
class WinConsole
|
||||
{
|
||||
bool winConsoleEnabled;
|
||||
|
||||
HANDLE stdOut;
|
||||
HANDLE stdIn;
|
||||
HANDLE stdErr;
|
||||
char inbuf[512];
|
||||
S32 inpos;
|
||||
bool lineOutput;
|
||||
char curTabComplete[512];
|
||||
S32 tabCompleteStart;
|
||||
char rgCmds[MAX_CMDS][512];
|
||||
S32 iCmdIndex;
|
||||
|
||||
void printf(const char *s, ...);
|
||||
|
||||
public:
|
||||
WinConsole();
|
||||
~WinConsole();
|
||||
|
||||
void process();
|
||||
void enable(bool);
|
||||
void processConsoleLine(const char *consoleLine);
|
||||
static void create();
|
||||
static void destroy();
|
||||
static bool isEnabled();
|
||||
};
|
||||
|
||||
extern WinConsole *WindowsConsole;
|
||||
|
||||
#endif
|
||||
1589
Engine/source/platformWin32/winDInputDevice.cpp
Normal file
1589
Engine/source/platformWin32/winDInputDevice.cpp
Normal file
File diff suppressed because it is too large
Load diff
162
Engine/source/platformWin32/winDInputDevice.h
Normal file
162
Engine/source/platformWin32/winDInputDevice.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _WINDINPUTDEVICE_H_
|
||||
#define _WINDINPUTDEVICE_H_
|
||||
|
||||
#ifndef _PLATFORMWIN32_H_
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMINPUT_H_
|
||||
#include "platform/platformInput.h"
|
||||
#endif
|
||||
#ifndef _EVENT_H_
|
||||
#include "platform/event.h"
|
||||
#endif
|
||||
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
#include <dinput.h>
|
||||
|
||||
|
||||
class DInputDevice : public InputDevice
|
||||
{
|
||||
public:
|
||||
static LPDIRECTINPUT8 smDInputInterface;
|
||||
|
||||
protected:
|
||||
enum Constants
|
||||
{
|
||||
QUEUED_BUFFER_SIZE = 128,
|
||||
|
||||
SIZEOF_BUTTON = 1, // size of an object's data in bytes
|
||||
SIZEOF_KEY = 1,
|
||||
SIZEOF_AXIS = 4,
|
||||
SIZEOF_POV = 4,
|
||||
};
|
||||
|
||||
static U8 smDeviceCount[ NUM_INPUT_DEVICE_TYPES ];
|
||||
static bool smInitialized;
|
||||
|
||||
/// Are we an XInput device?
|
||||
bool mIsXInput;
|
||||
|
||||
//--------------------------------------
|
||||
LPDIRECTINPUTDEVICE8 mDevice;
|
||||
DIDEVICEINSTANCE mDeviceInstance;
|
||||
DIDEVCAPS mDeviceCaps;
|
||||
U8 mDeviceType;
|
||||
U8 mDeviceID;
|
||||
|
||||
bool mAcquired;
|
||||
bool mNeedSync;
|
||||
|
||||
LPDIRECTINPUTEFFECT mForceFeedbackEffect; ///< Holds our DirectInput FF Effect
|
||||
DWORD mNumForceFeedbackAxes; ///< # axes (we only support 0, 1, or 2
|
||||
DWORD mForceFeedbackAxes[2]; ///< Force Feedback axes offsets into DIOBJECTFORMAT
|
||||
|
||||
//--------------------------------------
|
||||
DIDEVICEOBJECTINSTANCE* mObjInstance;
|
||||
DIOBJECTDATAFORMAT* mObjFormat;
|
||||
ObjInfo* mObjInfo;
|
||||
U8* mObjBuffer1; // polled device input buffers
|
||||
U8* mObjBuffer2;
|
||||
U8* mPrevObjBuffer; // points to buffer 1 or 2
|
||||
|
||||
// Hack for POV
|
||||
S32 mPrevPOVPos;
|
||||
|
||||
U32 mObjBufferSize; // size of objBuffer*
|
||||
U32 mObjCount; // number of objects on this device
|
||||
U32 mObjEnumCount; // used during enumeration ONLY
|
||||
U32 mObjBufferOfs; // used during enumeration ONLY
|
||||
|
||||
static BOOL CALLBACK EnumObjectsProc( const DIDEVICEOBJECTINSTANCE *doi, LPVOID pvRef );
|
||||
|
||||
bool enumerateObjects();
|
||||
bool processAsync();
|
||||
bool processImmediate();
|
||||
|
||||
DWORD findObjInstance( DWORD offset );
|
||||
bool buildEvent( DWORD offset, S32 newData, S32 oldData );
|
||||
|
||||
public:
|
||||
DInputDevice( const DIDEVICEINSTANCE* deviceInst );
|
||||
~DInputDevice();
|
||||
|
||||
static void init();
|
||||
|
||||
bool create();
|
||||
void destroy();
|
||||
|
||||
bool acquire();
|
||||
bool unacquire();
|
||||
|
||||
bool isAcquired();
|
||||
bool isPolled();
|
||||
|
||||
U8 getDeviceType();
|
||||
U8 getDeviceID();
|
||||
|
||||
const char* getName();
|
||||
const char* getProductName();
|
||||
|
||||
// Constant Effect Force Feedback
|
||||
void rumble( float x, float y );
|
||||
|
||||
// Console interface functions:
|
||||
const char* getJoystickAxesString();
|
||||
static bool joystickDetected();
|
||||
//
|
||||
|
||||
bool process();
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
inline bool DInputDevice::isAcquired()
|
||||
{
|
||||
return mAcquired;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
inline bool DInputDevice::isPolled()
|
||||
{
|
||||
//return true;
|
||||
return ( mDeviceCaps.dwFlags & DIDC_POLLEDDEVICE ) != 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
inline U8 DInputDevice::getDeviceType()
|
||||
{
|
||||
return mDeviceType;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
inline U8 DInputDevice::getDeviceID()
|
||||
{
|
||||
return mDeviceID;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
InputObjectInstances DIK_to_Key( U8 dikCode );
|
||||
U8 Key_to_DIK( U16 keyCode );
|
||||
#endif // _H_WINDINPUTDEVICE_
|
||||
954
Engine/source/platformWin32/winDirectInput.cpp
Normal file
954
Engine/source/platformWin32/winDirectInput.cpp
Normal file
|
|
@ -0,0 +1,954 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platformWin32/winDirectInput.h"
|
||||
#include "platformWin32/winDInputDevice.h"
|
||||
#include "platform/event.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "sim/actionMap.h"
|
||||
|
||||
#include <xinput.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Static class variables:
|
||||
bool DInputManager::smJoystickEnabled = true;
|
||||
bool DInputManager::smXInputEnabled = true;
|
||||
|
||||
// Type definitions:
|
||||
typedef HRESULT (WINAPI* FN_DirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
DInputManager::DInputManager()
|
||||
{
|
||||
mEnabled = false;
|
||||
mDInputLib = NULL;
|
||||
mDInputInterface = NULL;
|
||||
mJoystickActive = mXInputActive = true;
|
||||
mXInputLib = NULL;
|
||||
|
||||
for(S32 i=0; i<4; i++)
|
||||
mLastDisconnectTime[i] = -1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::init()
|
||||
{
|
||||
Con::addVariable( "pref::Input::JoystickEnabled", TypeBool, &smJoystickEnabled,
|
||||
"@brief If true, the joystick is currently enabled.\n\n"
|
||||
"@ingroup Game");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::enable()
|
||||
{
|
||||
FN_DirectInputCreate fnDInputCreate;
|
||||
|
||||
disable();
|
||||
|
||||
// Dynamically load the XInput 9 DLL and cache function pointers to the
|
||||
// two APIs we use
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Enabling XInput...\n" );
|
||||
#endif
|
||||
mXInputLib = LoadLibrary( dT("xinput9_1_0.dll") );
|
||||
if ( mXInputLib )
|
||||
{
|
||||
mfnXInputGetState = (FN_XInputGetState) GetProcAddress( mXInputLib, "XInputGetState" );
|
||||
mfnXInputSetState = (FN_XInputSetState) GetProcAddress( mXInputLib, "XInputSetState" );
|
||||
if ( mfnXInputGetState && mfnXInputSetState )
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "XInput detected.\n" );
|
||||
#endif
|
||||
mXInputStateReset = true;
|
||||
mXInputDeadZoneOn = true;
|
||||
smXInputEnabled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "XInput was not found.\n" );
|
||||
mXInputStateReset = false;
|
||||
mXInputDeadZoneOn = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Enabling DirectInput...\n" );
|
||||
#endif
|
||||
mDInputLib = LoadLibrary( dT("DInput8.dll") );
|
||||
if ( mDInputLib )
|
||||
{
|
||||
fnDInputCreate = (FN_DirectInputCreate) GetProcAddress( mDInputLib, "DirectInput8Create" );
|
||||
if ( fnDInputCreate )
|
||||
{
|
||||
bool result = SUCCEEDED( fnDInputCreate( winState.appInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast<void**>(&mDInputInterface), NULL ));
|
||||
if ( result )
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "DirectX 8 or greater detected.\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( result )
|
||||
{
|
||||
enumerateDevices();
|
||||
mEnabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disable();
|
||||
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Failed to enable DirectInput.\n" );
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DInputManager::disable()
|
||||
{
|
||||
unacquire( SI_ANY, SI_ANY );
|
||||
|
||||
DInputDevice* dptr;
|
||||
iterator ptr = begin();
|
||||
while ( ptr != end() )
|
||||
{
|
||||
dptr = dynamic_cast<DInputDevice*>( *ptr );
|
||||
if ( dptr )
|
||||
{
|
||||
removeObject( dptr );
|
||||
//if ( dptr->getManager() )
|
||||
//dptr->getManager()->unregisterObject( dptr );
|
||||
delete dptr;
|
||||
ptr = begin();
|
||||
}
|
||||
else
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if ( mDInputInterface )
|
||||
{
|
||||
mDInputInterface->Release();
|
||||
mDInputInterface = NULL;
|
||||
}
|
||||
|
||||
if ( mDInputLib )
|
||||
{
|
||||
FreeLibrary( mDInputLib );
|
||||
mDInputLib = NULL;
|
||||
}
|
||||
|
||||
if ( mfnXInputGetState )
|
||||
{
|
||||
mXInputStateReset = true;
|
||||
mfnXInputGetState = NULL;
|
||||
mfnXInputSetState = NULL;
|
||||
}
|
||||
|
||||
if ( mXInputLib )
|
||||
{
|
||||
FreeLibrary( mXInputLib );
|
||||
mXInputLib = NULL;
|
||||
}
|
||||
|
||||
mEnabled = false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::onDeleteNotify( SimObject* object )
|
||||
{
|
||||
Parent::onDeleteNotify( object );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
acquire( SI_ANY, SI_ANY );
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::onRemove()
|
||||
{
|
||||
unacquire( SI_ANY, SI_ANY );
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::acquire( U8 deviceType, U8 deviceID )
|
||||
{
|
||||
bool anyActive = false;
|
||||
DInputDevice* dptr;
|
||||
for ( iterator ptr = begin(); ptr != end(); ptr++ )
|
||||
{
|
||||
dptr = dynamic_cast<DInputDevice*>( *ptr );
|
||||
if ( dptr
|
||||
&& ( ( deviceType == SI_ANY ) || ( dptr->getDeviceType() == deviceType ) )
|
||||
&& ( ( deviceID == SI_ANY ) || ( dptr->getDeviceID() == deviceID ) ) )
|
||||
{
|
||||
if ( dptr->acquire() )
|
||||
anyActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
return anyActive;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::unacquire( U8 deviceType, U8 deviceID )
|
||||
{
|
||||
DInputDevice* dptr;
|
||||
for ( iterator ptr = begin(); ptr != end(); ptr++ )
|
||||
{
|
||||
dptr = dynamic_cast<DInputDevice*>( *ptr );
|
||||
if ( dptr
|
||||
&& ( ( deviceType == SI_ANY ) || ( dptr->getDeviceType() == deviceType ) )
|
||||
&& ( ( deviceID == SI_ANY ) || ( dptr->getDeviceID() == deviceID ) ) )
|
||||
dptr->unacquire();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::process()
|
||||
{
|
||||
// Because the XInput APIs manage everything for all four controllers trivially,
|
||||
// we don't need the abstraction of a DInputDevice for an unknown number of devices
|
||||
// with varying buttons & whistles... nice and easy!
|
||||
if ( isXInputActive() )
|
||||
processXInput();
|
||||
|
||||
DInputDevice* dptr;
|
||||
for ( iterator ptr = begin(); ptr != end(); ptr++ )
|
||||
{
|
||||
dptr = dynamic_cast<DInputDevice*>( *ptr );
|
||||
if ( dptr )
|
||||
dptr->process();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::enumerateDevices()
|
||||
{
|
||||
if ( mDInputInterface )
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Enumerating input devices...\n" );
|
||||
#endif
|
||||
|
||||
DInputDevice::init();
|
||||
DInputDevice::smDInputInterface = mDInputInterface;
|
||||
mDInputInterface->EnumDevices( DI8DEVTYPE_KEYBOARD, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
|
||||
mDInputInterface->EnumDevices( DI8DEVTYPE_MOUSE, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
|
||||
mDInputInterface->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
BOOL CALLBACK DInputManager::EnumDevicesProc( const DIDEVICEINSTANCE* pddi, LPVOID pvRef )
|
||||
{
|
||||
DInputManager* manager = (DInputManager*) pvRef;
|
||||
DInputDevice* newDevice = new DInputDevice( pddi );
|
||||
manager->addObject( newDevice );
|
||||
if ( !newDevice->create() )
|
||||
{
|
||||
manager->removeObject( newDevice );
|
||||
delete newDevice;
|
||||
}
|
||||
|
||||
return (DIENUM_CONTINUE);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::enableJoystick()
|
||||
{
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
if ( !mgr || !mgr->isEnabled() )
|
||||
return( false );
|
||||
|
||||
if ( smJoystickEnabled && mgr->isJoystickActive() )
|
||||
return( true );
|
||||
|
||||
smJoystickEnabled = true;
|
||||
if ( Input::isActive() )
|
||||
smJoystickEnabled = mgr->activateJoystick();
|
||||
|
||||
if ( smJoystickEnabled )
|
||||
{
|
||||
Con::printf( "DirectInput joystick enabled." );
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Joystick enabled.\n" );
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::warnf( "DirectInput joystick failed to enable!" );
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Joystick failed to enable!\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
return( smJoystickEnabled );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::disableJoystick()
|
||||
{
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
if ( !mgr || !mgr->isEnabled() || !smJoystickEnabled )
|
||||
return;
|
||||
|
||||
mgr->deactivateJoystick();
|
||||
smJoystickEnabled = false;
|
||||
Con::printf( "DirectInput joystick disabled." );
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Joystick disabled.\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::isJoystickEnabled()
|
||||
{
|
||||
return( smJoystickEnabled );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::activateJoystick()
|
||||
{
|
||||
if ( !mEnabled || !Input::isActive() || !smJoystickEnabled )
|
||||
return( false );
|
||||
|
||||
mJoystickActive = acquire( JoystickDeviceType, SI_ANY );
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( mJoystickActive ? "Joystick activated.\n" : "Joystick failed to activate!\n" );
|
||||
#endif
|
||||
return( mJoystickActive );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::deactivateJoystick()
|
||||
{
|
||||
if ( mEnabled && mJoystickActive )
|
||||
{
|
||||
unacquire( JoystickDeviceType, SI_ANY );
|
||||
mJoystickActive = false;
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Joystick deactivated.\n" );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* DInputManager::getJoystickAxesString( U32 deviceID )
|
||||
{
|
||||
DInputDevice* dptr;
|
||||
for ( iterator ptr = begin(); ptr != end(); ptr++ )
|
||||
{
|
||||
dptr = dynamic_cast<DInputDevice*>( *ptr );
|
||||
if ( dptr && ( dptr->getDeviceType() == JoystickDeviceType ) && ( dptr->getDeviceID() == deviceID ) )
|
||||
return( dptr->getJoystickAxesString() );
|
||||
}
|
||||
|
||||
return( "" );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::enableXInput()
|
||||
{
|
||||
// Largely, this series of functions is identical to the Joystick versions,
|
||||
// except that XInput cannot be "activated" or "deactivated". You either have
|
||||
// the DLL or you don't. Beyond that, you have up to four controllers
|
||||
// connected at any given time
|
||||
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
if ( !mgr || !mgr->isEnabled() )
|
||||
return( false );
|
||||
|
||||
if ( mgr->isXInputActive() )
|
||||
return( true );
|
||||
|
||||
if ( Input::isActive() )
|
||||
mgr->activateXInput();
|
||||
|
||||
if ( smXInputEnabled )
|
||||
{
|
||||
Con::printf( "XInput enabled." );
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "XInput enabled.\n" );
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::warnf( "XInput failed to enable!" );
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "XInput failed to enable!\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
return( smXInputEnabled );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::disableXInput()
|
||||
{
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
if ( !mgr || !mgr->isEnabled())
|
||||
return;
|
||||
|
||||
mgr->deactivateXInput();
|
||||
Con::printf( "XInput disabled." );
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "XInput disabled.\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::isXInputEnabled()
|
||||
{
|
||||
return( smXInputEnabled );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::isXInputConnected(int controllerID)
|
||||
{
|
||||
return( mXInputStateNew[controllerID].bConnected );
|
||||
}
|
||||
|
||||
int DInputManager::getXInputState(int controllerID, int property, bool current)
|
||||
{
|
||||
int retVal;
|
||||
|
||||
switch(property)
|
||||
{
|
||||
#define CHECK_PROP_ANALOG(prop, stateTest) \
|
||||
case prop: (current) ? retVal = mXInputStateNew[controllerID].state.Gamepad.##stateTest : retVal = mXInputStateOld[controllerID].state.Gamepad.##stateTest; return retVal;
|
||||
|
||||
CHECK_PROP_ANALOG(XI_THUMBLX, sThumbLX)
|
||||
CHECK_PROP_ANALOG(XI_THUMBLY, sThumbLY)
|
||||
CHECK_PROP_ANALOG(XI_THUMBRX, sThumbRX)
|
||||
CHECK_PROP_ANALOG(XI_THUMBRY, sThumbRY)
|
||||
CHECK_PROP_ANALOG(XI_LEFT_TRIGGER, bLeftTrigger)
|
||||
CHECK_PROP_ANALOG(XI_RIGHT_TRIGGER, bRightTrigger)
|
||||
|
||||
#undef CHECK_PROP_ANALOG
|
||||
|
||||
#define CHECK_PROP_DIGITAL(prop, stateTest) \
|
||||
case prop: (current) ? retVal = (( mXInputStateNew[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ) : retVal = (( mXInputStateOld[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ); return retVal;
|
||||
|
||||
CHECK_PROP_DIGITAL(SI_UPOV, XINPUT_GAMEPAD_DPAD_UP)
|
||||
CHECK_PROP_DIGITAL(SI_DPOV, XINPUT_GAMEPAD_DPAD_DOWN)
|
||||
CHECK_PROP_DIGITAL(SI_LPOV, XINPUT_GAMEPAD_DPAD_LEFT)
|
||||
CHECK_PROP_DIGITAL(SI_RPOV, XINPUT_GAMEPAD_DPAD_RIGHT)
|
||||
CHECK_PROP_DIGITAL(XI_START, XINPUT_GAMEPAD_START)
|
||||
CHECK_PROP_DIGITAL(XI_BACK, XINPUT_GAMEPAD_BACK)
|
||||
CHECK_PROP_DIGITAL(XI_LEFT_THUMB, XINPUT_GAMEPAD_LEFT_THUMB)
|
||||
CHECK_PROP_DIGITAL(XI_RIGHT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB)
|
||||
CHECK_PROP_DIGITAL(XI_LEFT_SHOULDER, XINPUT_GAMEPAD_LEFT_SHOULDER)
|
||||
CHECK_PROP_DIGITAL(XI_RIGHT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER)
|
||||
CHECK_PROP_DIGITAL(XI_A, XINPUT_GAMEPAD_A)
|
||||
CHECK_PROP_DIGITAL(XI_B, XINPUT_GAMEPAD_B)
|
||||
CHECK_PROP_DIGITAL(XI_X, XINPUT_GAMEPAD_X)
|
||||
CHECK_PROP_DIGITAL(XI_Y, XINPUT_GAMEPAD_Y)
|
||||
|
||||
#undef CHECK_PROP_DIGITAL
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::activateXInput()
|
||||
{
|
||||
if ( !mEnabled || !Input::isActive())
|
||||
return( false );
|
||||
|
||||
mXInputActive = true; //acquire( GamepadDeviceType, SI_ANY );
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( mXInputActive ? "XInput activated.\n" : "XInput failed to activate!\n" );
|
||||
#endif
|
||||
return( mXInputActive );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void DInputManager::deactivateXInput()
|
||||
{
|
||||
if ( mEnabled && mXInputActive )
|
||||
{
|
||||
unacquire( GamepadDeviceType, SI_ANY );
|
||||
mXInputActive = false;
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "XInput deactivated.\n" );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DInputManager::rumble( const char *pDeviceName, float x, float y )
|
||||
{
|
||||
// Determine the device
|
||||
U32 deviceType;
|
||||
U32 deviceInst;
|
||||
|
||||
// Find the requested device
|
||||
if ( !ActionMap::getDeviceTypeAndInstance(pDeviceName, deviceType, deviceInst) )
|
||||
{
|
||||
Con::printf("DInputManager::rumble: unknown device: %s", pDeviceName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// clamp (x, y) to the range of [0 ... 1] each
|
||||
x = mClampF(x, 0.f, 1.f);
|
||||
y = mClampF(y, 0.f, 1.f);
|
||||
|
||||
// Easy path for xinput devices.
|
||||
if(deviceType == GamepadDeviceType)
|
||||
{
|
||||
XINPUT_VIBRATION vibration;
|
||||
vibration.wLeftMotorSpeed = static_cast<int>(x * 65535);
|
||||
vibration.wRightMotorSpeed = static_cast<int>(y * 65535);
|
||||
return ( mfnXInputSetState(deviceInst, &vibration) == ERROR_SUCCESS );
|
||||
}
|
||||
|
||||
switch ( deviceType )
|
||||
{
|
||||
case JoystickDeviceType:
|
||||
|
||||
// Find the device and shake it!
|
||||
DInputDevice* dptr;
|
||||
for ( iterator ptr = begin(); ptr != end(); ptr++ )
|
||||
{
|
||||
dptr = dynamic_cast<DInputDevice*>( *ptr );
|
||||
if ( dptr )
|
||||
{
|
||||
if (deviceType == dptr->getDeviceType() && deviceInst == dptr->getDeviceID())
|
||||
{
|
||||
dptr->rumble(x, y);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We should never get here... something's really messed up
|
||||
Con::errorf( "DInputManager::rumbleJoystick - Couldn't find device to rumble! This should never happen!\n" );
|
||||
return false;
|
||||
|
||||
default:
|
||||
Con::printf("DInputManager::rumble - only supports joystick and xinput/gamepad devices");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DInputManager::buildXInputEvent( U32 deviceInst, InputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue )
|
||||
{
|
||||
InputEventInfo newEvent;
|
||||
|
||||
newEvent.deviceType = GamepadDeviceType;
|
||||
newEvent.deviceInst = deviceInst;
|
||||
newEvent.objType = objType;
|
||||
newEvent.objInst = objInst;
|
||||
newEvent.action = action;
|
||||
newEvent.fValue = fValue;
|
||||
|
||||
newEvent.postToSignal(Input::smInputEvent);
|
||||
}
|
||||
|
||||
// The next three functions: fireXInputConnectEvent, fireXInputMoveEvent, and fireXInputButtonEvent
|
||||
// determine whether a "delta" has occurred between the last captured controller state and the
|
||||
// currently captured controller state and only if so, do we fire an event. The shortcutter
|
||||
// "mXInputStateReset" is the exception and is true whenever DirectInput gets reset (because
|
||||
// the user ALT-TABBED away, for example). That means that after every context switch,
|
||||
// you will get a full set of updates on the "true" state of the controller.
|
||||
inline void DInputManager::fireXInputConnectEvent( int controllerID, bool condition, bool connected )
|
||||
{
|
||||
if ( mXInputStateReset || condition )
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "EVENT (XInput): xinput%d CONNECT %s\n", controllerID, connected ? "make" : "break" );
|
||||
#endif
|
||||
buildXInputEvent( controllerID, SI_BUTTON, XI_CONNECT, connected ? SI_MAKE : SI_BREAK, 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline void DInputManager::fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue )
|
||||
{
|
||||
if ( mXInputStateReset || condition )
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
char *objName;
|
||||
switch (objInst)
|
||||
{
|
||||
case XI_THUMBLX: objName = "THUMBLX"; break;
|
||||
case XI_THUMBLY: objName = "THUMBLY"; break;
|
||||
case XI_THUMBRX: objName = "THUMBRX"; break;
|
||||
case XI_THUMBRY: objName = "THUMBRY"; break;
|
||||
case XI_LEFT_TRIGGER: objName = "LEFT_TRIGGER"; break;
|
||||
case XI_RIGHT_TRIGGER: objName = "RIGHT_TRIGGER"; break;
|
||||
default: objName = "UNKNOWN"; break;
|
||||
}
|
||||
|
||||
Input::log( "EVENT (XInput): xinput%d %s MOVE %.1f.\n", controllerID, objName, fValue );
|
||||
#endif
|
||||
buildXInputEvent( controllerID, SI_AXIS, objInst, SI_MOVE, fValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void DInputManager::fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst )
|
||||
{
|
||||
if ( mXInputStateReset || forceFire || ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != (mXInputStateOld[controllerID].state.Gamepad.wButtons & button)) )
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
char *objName;
|
||||
switch (objInst)
|
||||
{
|
||||
/*
|
||||
case XI_DPAD_UP: objName = "DPAD_UP"; break;
|
||||
case XI_DPAD_DOWN: objName = "DPAD_DOWN"; break;
|
||||
case XI_DPAD_LEFT: objName = "DPAD_LEFT"; break;
|
||||
case XI_DPAD_RIGHT: objName = "DPAD_RIGHT"; break;
|
||||
*/
|
||||
case XI_START: objName = "START"; break;
|
||||
case XI_BACK: objName = "BACK"; break;
|
||||
case XI_LEFT_THUMB: objName = "LEFT_THUMB"; break;
|
||||
case XI_RIGHT_THUMB: objName = "RIGHT_THUMB"; break;
|
||||
case XI_LEFT_SHOULDER: objName = "LEFT_SHOULDER"; break;
|
||||
case XI_RIGHT_SHOULDER: objName = "RIGHT_SHOULDER"; break;
|
||||
case XI_A: objName = "A"; break;
|
||||
case XI_B: objName = "B"; break;
|
||||
case XI_X: objName = "X"; break;
|
||||
case XI_Y: objName = "Y"; break;
|
||||
default: objName = "UNKNOWN"; break;
|
||||
}
|
||||
|
||||
Input::log( "EVENT (XInput): xinput%d %s %s\n", controllerID, objName, ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? "make" : "break" );
|
||||
#endif
|
||||
InputActionType action = ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? SI_MAKE : SI_BREAK;
|
||||
buildXInputEvent( controllerID, SI_BUTTON, objInst, action, ( action == SI_MAKE ? 1 : 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
void DInputManager::processXInput( void )
|
||||
{
|
||||
const U32 curTime = Platform::getRealMilliseconds();
|
||||
|
||||
// We only want to check one disconnected device per frame.
|
||||
bool foundDisconnected = false;
|
||||
|
||||
if ( mfnXInputGetState )
|
||||
{
|
||||
for ( int i=0; i<4; i++ )
|
||||
{
|
||||
// Calling XInputGetState on a disconnected controller takes a fair
|
||||
// amount of time (probably because it tries to locate it), so we
|
||||
// add a delay - only check every 250ms or so.
|
||||
if(mLastDisconnectTime[i] != -1)
|
||||
{
|
||||
// If it's not -1, then it was disconnected list time we checked.
|
||||
// So skip until it's time.
|
||||
if((curTime-mLastDisconnectTime[i]) < csmDisconnectedSkipDelay)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we already checked a disconnected controller, don't try any
|
||||
// further potentially disconnected devices.
|
||||
if(foundDisconnected)
|
||||
{
|
||||
// If we're skipping this, defer it out by the skip delay
|
||||
// so we don't get clumped checks.
|
||||
mLastDisconnectTime[i] += csmDisconnectedSkipDelay;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
mXInputStateOld[i] = mXInputStateNew[i];
|
||||
mXInputStateNew[i].bConnected = ( mfnXInputGetState( i, &mXInputStateNew[i].state ) == ERROR_SUCCESS );
|
||||
|
||||
// Update the last connected time.
|
||||
if(mXInputStateNew[i].bConnected)
|
||||
mLastDisconnectTime[i] = -1;
|
||||
else
|
||||
{
|
||||
foundDisconnected = true;
|
||||
mLastDisconnectTime[i] = curTime;
|
||||
}
|
||||
|
||||
// trim the controller's thumbsticks to zero if they are within the deadzone
|
||||
if( mXInputDeadZoneOn )
|
||||
{
|
||||
// Zero value if thumbsticks are within the dead zone
|
||||
if( (mXInputStateNew[i].state.Gamepad.sThumbLX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLX > -XINPUT_DEADZONE) &&
|
||||
(mXInputStateNew[i].state.Gamepad.sThumbLY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLY > -XINPUT_DEADZONE) )
|
||||
{
|
||||
mXInputStateNew[i].state.Gamepad.sThumbLX = 0;
|
||||
mXInputStateNew[i].state.Gamepad.sThumbLY = 0;
|
||||
}
|
||||
|
||||
if( (mXInputStateNew[i].state.Gamepad.sThumbRX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRX > -XINPUT_DEADZONE) &&
|
||||
(mXInputStateNew[i].state.Gamepad.sThumbRY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRY > -XINPUT_DEADZONE) )
|
||||
{
|
||||
mXInputStateNew[i].state.Gamepad.sThumbRX = 0;
|
||||
mXInputStateNew[i].state.Gamepad.sThumbRY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// this controller was connected or disconnected
|
||||
bool bJustConnected = ( ( mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected ) && ( mXInputStateNew[i].bConnected ) );
|
||||
fireXInputConnectEvent( i, (mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected), mXInputStateNew[i].bConnected );
|
||||
|
||||
// If this controller is disconnected, stop reporting events for it
|
||||
if ( !mXInputStateNew[i].bConnected )
|
||||
continue;
|
||||
|
||||
// == LEFT THUMBSTICK ==
|
||||
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLX != mXInputStateOld[i].state.Gamepad.sThumbLX), XI_THUMBLX, (mXInputStateNew[i].state.Gamepad.sThumbLX / 32768.0f) );
|
||||
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLY != mXInputStateOld[i].state.Gamepad.sThumbLY), XI_THUMBLY, (mXInputStateNew[i].state.Gamepad.sThumbLY / 32768.0f) );
|
||||
|
||||
// == RIGHT THUMBSTICK ==
|
||||
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRX != mXInputStateOld[i].state.Gamepad.sThumbRX), XI_THUMBRX, (mXInputStateNew[i].state.Gamepad.sThumbRX / 32768.0f) );
|
||||
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRY != mXInputStateOld[i].state.Gamepad.sThumbRY), XI_THUMBRY, (mXInputStateNew[i].state.Gamepad.sThumbRY / 32768.0f) );
|
||||
|
||||
// == LEFT & RIGHT REAR TRIGGERS ==
|
||||
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bLeftTrigger != mXInputStateOld[i].state.Gamepad.bLeftTrigger), XI_LEFT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bLeftTrigger / 255.0f) );
|
||||
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bRightTrigger != mXInputStateOld[i].state.Gamepad.bRightTrigger), XI_RIGHT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bRightTrigger / 255.0f) );
|
||||
|
||||
// == BUTTONS: DPAD ==
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_UP, SI_UPOV );
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_DOWN, SI_DPOV );
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_LEFT, SI_LPOV );
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_RIGHT, SI_RPOV );
|
||||
|
||||
// == BUTTONS: START & BACK ==
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_START, XI_START );
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_BACK, XI_BACK );
|
||||
|
||||
// == BUTTONS: LEFT AND RIGHT THUMBSTICK ==
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_THUMB, XI_LEFT_THUMB );
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_THUMB, XI_RIGHT_THUMB );
|
||||
|
||||
// == BUTTONS: LEFT AND RIGHT SHOULDERS (formerly WHITE and BLACK on Xbox 1) ==
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_SHOULDER, XI_LEFT_SHOULDER );
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_SHOULDER, XI_RIGHT_SHOULDER );
|
||||
|
||||
// == BUTTONS: A, B, X, and Y ==
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_A, XI_A );
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_B, XI_B );
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_X, XI_X );
|
||||
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_Y, XI_Y );
|
||||
}
|
||||
|
||||
if ( mXInputStateReset )
|
||||
mXInputStateReset = false;
|
||||
}
|
||||
}
|
||||
ConsoleFunction( enableJoystick, bool, 1, 1, "()"
|
||||
"@brief Enables use of the joystick.\n\n"
|
||||
"@note DirectInput must be enabled and active to use this function.\n\n"
|
||||
"@ingroup Input")
|
||||
{
|
||||
argc; argv;
|
||||
return( DInputManager::enableJoystick() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( disableJoystick, void, 1, 1,"()"
|
||||
"@brief Disables use of the joystick.\n\n"
|
||||
"@note DirectInput must be enabled and active to use this function.\n\n"
|
||||
"@ingroup Input")
|
||||
{
|
||||
argc; argv;
|
||||
DInputManager::disableJoystick();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( isJoystickEnabled, bool, 1, 1, "()"
|
||||
"@brief Queries input manager to see if a joystick is enabled\n\n"
|
||||
"@return 1 if a joystick exists and is enabled, 0 if it's not.\n"
|
||||
"@ingroup Input")
|
||||
{
|
||||
argc; argv;
|
||||
return DInputManager::isJoystickEnabled();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( enableXInput, bool, 1, 1, "()"
|
||||
"@brief Enables XInput for Xbox 360 controllers.\n\n"
|
||||
"@note XInput is enabled by default. Disable to use an Xbox 360 "
|
||||
"Controller as a joystick device.\n\n"
|
||||
"@ingroup Input")
|
||||
{
|
||||
// Although I said above that you couldn't change the "activation" of XInput,
|
||||
// you can enable and disable it. It gets enabled by default if you have the
|
||||
// DLL. You would want to disable it if you have 360 controllers and want to
|
||||
// read them as joysticks... why you'd want to do that is beyond me
|
||||
|
||||
argc; argv;
|
||||
return( DInputManager::enableXInput() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( disableXInput, void, 1, 1, "()"
|
||||
"@brief Disables XInput for Xbox 360 controllers.\n\n"
|
||||
"@ingroup Input")
|
||||
{
|
||||
argc; argv;
|
||||
DInputManager::disableXInput();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( resetXInput, void, 1, 1, "()"
|
||||
"@brief Rebuilds the XInput section of the InputManager\n\n"
|
||||
"Requests a full refresh of events for all controllers. Useful when called at the beginning "
|
||||
"of game code after actionMaps are set up to hook up all appropriate events.\n\n"
|
||||
"@ingroup Input")
|
||||
{
|
||||
// This function requests a full "refresh" of events for all controllers the
|
||||
// next time we go through the input processing loop. This is useful to call
|
||||
// at the beginning of your game code after your actionMap is set up to hook
|
||||
// all of the appropriate events
|
||||
|
||||
argc; argv;
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
if ( mgr && mgr->isEnabled() )
|
||||
mgr->resetXInput();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( isXInputConnected, bool, 2, 2, "( int controllerID )"
|
||||
"@brief Checks to see if an Xbox 360 controller is connected\n\n"
|
||||
"@param controllerID Zero-based index of the controller to check.\n"
|
||||
"@return 1 if the controller is connected, 0 if it isn't, and 205 if XInput "
|
||||
"hasn't been initialized."
|
||||
"@ingroup Input")
|
||||
{
|
||||
argc; argv;
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
if ( mgr && mgr->isEnabled() ) return mgr->isXInputConnected( atoi( argv[1] ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( getXInputState, int, 3, 4, "( int controllerID, string property, bool current )"
|
||||
"@brief Queries the current state of a connected Xbox 360 controller.\n\n"
|
||||
"XInput Properties:\n\n"
|
||||
" - XI_THUMBLX, XI_THUMBLY - X and Y axes of the left thumbstick. \n"
|
||||
" - XI_THUMBRX, XI_THUMBRY - X and Y axes of the right thumbstick. \n"
|
||||
" - XI_LEFT_TRIGGER, XI_RIGHT_TRIGGER - Left and Right triggers. \n"
|
||||
" - SI_UPOV, SI_DPOV, SI_LPOV, SI_RPOV - Up, Down, Left, and Right on the directional pad.\n"
|
||||
" - XI_START, XI_BACK - The Start and Back buttons.\n"
|
||||
" - XI_LEFT_THUMB, XI_RIGHT_THUMB - Clicking in the left and right thumbstick.\n"
|
||||
" - XI_LEFT_SHOULDER, XI_RIGHT_SHOULDER - Left and Right bumpers.\n"
|
||||
" - XI_A, XI_B , XI_X, XI_Y - The A, B, X, and Y buttons.\n\n"
|
||||
"@param controllerID Zero-based index of the controller to return information about.\n"
|
||||
"@param property Name of input action being queried, such as \"XI_THUMBLX\".\n"
|
||||
"@param current True checks current device in action.\n"
|
||||
"@return Button queried - 1 if the button is pressed, 0 if it's not.\n"
|
||||
"@return Thumbstick queried - Int representing displacement from rest position."
|
||||
"@return %Trigger queried - Int from 0 to 255 representing how far the trigger is displaced."
|
||||
"@ingroup Input")
|
||||
{
|
||||
argc; argv;
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
|
||||
if ( !mgr || !mgr->isEnabled() )
|
||||
return -1;
|
||||
|
||||
// Use a little bit of macro magic to simplify this otherwise monolothic
|
||||
// block of code.
|
||||
#define GET_XI_STATE(constName) \
|
||||
if (!dStricmp(argv[2], #constName)) \
|
||||
return mgr->getXInputState( dAtoi( argv[1] ), constName, ( dAtoi ( argv[3] ) == 1) );
|
||||
|
||||
GET_XI_STATE(XI_THUMBLX);
|
||||
GET_XI_STATE(XI_THUMBLY);
|
||||
GET_XI_STATE(XI_THUMBRX);
|
||||
GET_XI_STATE(XI_THUMBRY);
|
||||
GET_XI_STATE(XI_LEFT_TRIGGER);
|
||||
GET_XI_STATE(XI_RIGHT_TRIGGER);
|
||||
GET_XI_STATE(SI_UPOV);
|
||||
GET_XI_STATE(SI_DPOV);
|
||||
GET_XI_STATE(SI_LPOV);
|
||||
GET_XI_STATE(SI_RPOV);
|
||||
GET_XI_STATE(XI_START);
|
||||
GET_XI_STATE(XI_BACK);
|
||||
GET_XI_STATE(XI_LEFT_THUMB);
|
||||
GET_XI_STATE(XI_RIGHT_THUMB);
|
||||
GET_XI_STATE(XI_LEFT_SHOULDER);
|
||||
GET_XI_STATE(XI_RIGHT_SHOULDER);
|
||||
GET_XI_STATE(XI_A);
|
||||
GET_XI_STATE(XI_B);
|
||||
GET_XI_STATE(XI_X);
|
||||
GET_XI_STATE(XI_Y);
|
||||
#undef GET_XI_STATE
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( echoInputState, void, 1, 1, "()"
|
||||
"@brief Prints information to the console stating if DirectInput and a Joystick are enabled and active.\n\n"
|
||||
"@ingroup Input")
|
||||
{
|
||||
argc; argv;
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
if ( mgr && mgr->isEnabled() )
|
||||
{
|
||||
Con::printf( "DirectInput is enabled %s.", Input::isActive() ? "and active" : "but inactive" );
|
||||
Con::printf( "- Joystick is %sabled and %sactive.",
|
||||
mgr->isJoystickEnabled() ? "en" : "dis",
|
||||
mgr->isJoystickActive() ? "" : "in" );
|
||||
}
|
||||
else
|
||||
Con::printf( "DirectInput is not enabled." );
|
||||
}
|
||||
|
||||
ConsoleFunction( rumble, void, 4, 4, "(string device, float xRumble, float yRumble)"
|
||||
"@brief Activates the vibration motors in the specified controller.\n\n"
|
||||
"The controller will constantly at it's xRumble and yRumble intensities until "
|
||||
"changed or told to stop."
|
||||
"Valid inputs for xRumble/yRumble are [0 - 1].\n"
|
||||
"@param device Name of the device to rumble.\n"
|
||||
"@param xRumble Intensity to apply to the left motor.\n"
|
||||
"@param yRumble Intensity to apply to the right motor.\n"
|
||||
"@note in an Xbox 360 controller, the left motor is low-frequency, "
|
||||
"while the right motor is high-frequency."
|
||||
"@ingroup Input")
|
||||
{
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
if ( mgr && mgr->isEnabled() )
|
||||
{
|
||||
mgr->rumble(argv[1], dAtof(argv[2]), dAtof(argv[3]));
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::printf( "DirectInput/XInput is not enabled." );
|
||||
}
|
||||
}
|
||||
132
Engine/source/platformWin32/winDirectInput.h
Normal file
132
Engine/source/platformWin32/winDirectInput.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _WINDIRECTINPUT_H_
|
||||
#define _WINDIRECTINPUT_H_
|
||||
|
||||
#ifndef _PLATFORMWIN32_H_
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMINPUT_H_
|
||||
#include "platform/platformInput.h"
|
||||
#endif
|
||||
#ifndef _WINDINPUTDEVICE_H_
|
||||
#include "platformWin32/winDInputDevice.h"
|
||||
#endif
|
||||
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
#include <dinput.h>
|
||||
#include <xinput.h>
|
||||
|
||||
// XInput related definitions
|
||||
typedef DWORD (WINAPI* FN_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
|
||||
typedef DWORD (WINAPI* FN_XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
|
||||
#define XINPUT_MAX_CONTROLLERS 4 // XInput handles up to 4 controllers
|
||||
#define XINPUT_DEADZONE ( 0.24f * FLOAT(0x7FFF) ) // Default to 24% of the +/- 32767 range. This is a reasonable default value but can be altered if needed.
|
||||
struct XINPUT_CONTROLLER_STATE
|
||||
{
|
||||
XINPUT_STATE state;
|
||||
bool bConnected;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class DInputManager : public InputManager
|
||||
{
|
||||
private:
|
||||
typedef SimGroup Parent;
|
||||
|
||||
// XInput state
|
||||
HMODULE mXInputLib;
|
||||
FN_XInputGetState mfnXInputGetState;
|
||||
FN_XInputSetState mfnXInputSetState;
|
||||
XINPUT_CONTROLLER_STATE mXInputStateOld[XINPUT_MAX_CONTROLLERS];
|
||||
XINPUT_CONTROLLER_STATE mXInputStateNew[XINPUT_MAX_CONTROLLERS];
|
||||
U32 mLastDisconnectTime[XINPUT_MAX_CONTROLLERS];
|
||||
bool mXInputStateReset;
|
||||
bool mXInputDeadZoneOn;
|
||||
|
||||
/// Number of milliseconds to skip checking an xinput device if it was
|
||||
/// disconnected on last check.
|
||||
const static U32 csmDisconnectedSkipDelay = 250;
|
||||
|
||||
HMODULE mDInputLib;
|
||||
LPDIRECTINPUT8 mDInputInterface;
|
||||
|
||||
static bool smJoystickEnabled;
|
||||
static bool smXInputEnabled;
|
||||
|
||||
bool mJoystickActive;
|
||||
bool mXInputActive;
|
||||
|
||||
void enumerateDevices();
|
||||
|
||||
static BOOL CALLBACK EnumDevicesProc( const DIDEVICEINSTANCE *pddi, LPVOID pvRef );
|
||||
|
||||
bool acquire( U8 deviceType, U8 deviceID );
|
||||
void unacquire( U8 deviceType, U8 deviceID );
|
||||
|
||||
// XInput worker functions
|
||||
void buildXInputEvent( U32 deviceInst, InputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue );
|
||||
void fireXInputConnectEvent( int controllerID, bool condition, bool connected );
|
||||
void fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue );
|
||||
void fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst );
|
||||
void processXInput();
|
||||
|
||||
public:
|
||||
DInputManager();
|
||||
|
||||
bool enable();
|
||||
void disable();
|
||||
|
||||
void onDeleteNotify( SimObject* object );
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
void process();
|
||||
|
||||
// DirectInput functions:
|
||||
static void init();
|
||||
|
||||
static bool enableJoystick();
|
||||
static void disableJoystick();
|
||||
static bool isJoystickEnabled();
|
||||
bool activateJoystick();
|
||||
void deactivateJoystick();
|
||||
bool isJoystickActive() { return( mJoystickActive ); }
|
||||
|
||||
static bool enableXInput();
|
||||
static void disableXInput();
|
||||
static bool isXInputEnabled();
|
||||
bool activateXInput();
|
||||
void deactivateXInput();
|
||||
bool isXInputActive() { return( mXInputActive ); }
|
||||
void resetXInput() { mXInputStateReset = true; }
|
||||
bool isXInputConnected(int controllerID);
|
||||
int getXInputState(int controllerID, int property, bool current);
|
||||
|
||||
// Console interface:
|
||||
const char* getJoystickAxesString( U32 deviceID );
|
||||
|
||||
bool rumble( const char *pDeviceName, float x, float y );
|
||||
};
|
||||
|
||||
#endif // _H_WINDIRECTINPUT_
|
||||
92
Engine/source/platformWin32/winDlibrary.cpp
Normal file
92
Engine/source/platformWin32/winDlibrary.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include "platform/types.h"
|
||||
#include "platform/platformDlibrary.h"
|
||||
|
||||
class Win32DLibrary: public DLibrary
|
||||
{
|
||||
HMODULE _handle;
|
||||
public:
|
||||
Win32DLibrary();
|
||||
~Win32DLibrary();
|
||||
bool open(const char* file);
|
||||
void close();
|
||||
void *bind(const char *name);
|
||||
};
|
||||
|
||||
Win32DLibrary::Win32DLibrary()
|
||||
{
|
||||
_handle = 0;
|
||||
}
|
||||
|
||||
Win32DLibrary::~Win32DLibrary()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool Win32DLibrary::open(const char* file)
|
||||
{
|
||||
// dlopen should also include the RTLD_LOCAL flag, but it seems to be
|
||||
// missing from cygwin
|
||||
_handle = LoadLibraryA(file);
|
||||
if (!_handle)
|
||||
return false;
|
||||
bool (*open)() = (bool(*)())bind("dllopen");
|
||||
if (open && !(*open)()) {
|
||||
FreeLibrary(_handle);
|
||||
_handle = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Win32DLibrary::close()
|
||||
{
|
||||
if (_handle) {
|
||||
void (*close)() = (void(*)())bind("dllclose");
|
||||
if (close)
|
||||
(*close)();
|
||||
FreeLibrary(_handle);
|
||||
_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void* Win32DLibrary::bind(const char *name)
|
||||
{
|
||||
return _handle? (void*)GetProcAddress(_handle,name): 0;
|
||||
}
|
||||
|
||||
DLibraryRef OsLoadLibrary(const char* file)
|
||||
{
|
||||
Win32DLibrary* library = new Win32DLibrary();
|
||||
if (!library->open(file)) {
|
||||
delete library;
|
||||
return 0;
|
||||
}
|
||||
return library;
|
||||
}
|
||||
|
||||
188
Engine/source/platformWin32/winExec.cpp
Normal file
188
Engine/source/platformWin32/winExec.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "console/console.h"
|
||||
#include "console/simBase.h"
|
||||
#include "core/strings/unicode.h"
|
||||
#include "platform/threads/thread.h"
|
||||
#include "platform/threads/mutex.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "util/tempAlloc.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Thread for executing in
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class ExecuteThread : public Thread
|
||||
{
|
||||
// [tom, 12/14/2006] mProcess is only used in the constructor before the thread
|
||||
// is started and in the thread itself so we should be OK without a mutex.
|
||||
HANDLE mProcess;
|
||||
|
||||
public:
|
||||
ExecuteThread(const char *executable, const char *args = NULL, const char *directory = NULL);
|
||||
|
||||
virtual void run(void *arg = 0);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Event for cleanup
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class ExecuteCleanupEvent : public SimEvent
|
||||
{
|
||||
ExecuteThread *mThread;
|
||||
bool mOK;
|
||||
|
||||
public:
|
||||
ExecuteCleanupEvent(ExecuteThread *thread, bool ok)
|
||||
{
|
||||
mThread = thread;
|
||||
mOK = ok;
|
||||
}
|
||||
|
||||
virtual void process(SimObject *object)
|
||||
{
|
||||
if( Con::isFunction( "onExecuteDone" ) )
|
||||
Con::executef( "onExecuteDone", Con::getIntArg( mOK ) );
|
||||
SAFE_DELETE(mThread);
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ExecuteThread::ExecuteThread(const char *executable, const char *args /* = NULL */, const char *directory /* = NULL */) : Thread(0, NULL, false)
|
||||
{
|
||||
SHELLEXECUTEINFO shl;
|
||||
dMemset(&shl, 0, sizeof(shl));
|
||||
|
||||
shl.cbSize = sizeof(shl);
|
||||
shl.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||
|
||||
char exeBuf[1024];
|
||||
Platform::makeFullPathName(executable, exeBuf, sizeof(exeBuf));
|
||||
|
||||
TempAlloc< TCHAR > dirBuf( ( directory ? dStrlen( directory ) : 0 ) + 1 );
|
||||
dirBuf[ dirBuf.size - 1 ] = 0;
|
||||
|
||||
#ifdef UNICODE
|
||||
WCHAR exe[ 1024 ];
|
||||
convertUTF8toUTF16( exeBuf, exe, sizeof( exe ) / sizeof( exe[ 0 ] ) );
|
||||
|
||||
TempAlloc< WCHAR > argsBuf( ( args ? dStrlen( args ) : 0 ) + 1 );
|
||||
argsBuf[ argsBuf.size - 1 ] = 0;
|
||||
|
||||
if( args )
|
||||
convertUTF8toUTF16( args, argsBuf, argsBuf.size );
|
||||
if( directory )
|
||||
convertUTF8toUTF16( directory, dirBuf, dirBuf.size );
|
||||
#else
|
||||
char* exe = exeBuf;
|
||||
char* argsBuf = args;
|
||||
if( directory )
|
||||
dStrpcy( dirBuf, directory );
|
||||
#endif
|
||||
|
||||
backslash( exe );
|
||||
backslash( dirBuf );
|
||||
|
||||
shl.lpVerb = TEXT( "open" );
|
||||
shl.lpFile = exe;
|
||||
shl.lpParameters = argsBuf;
|
||||
shl.lpDirectory = dirBuf;
|
||||
|
||||
shl.nShow = SW_SHOWNORMAL;
|
||||
|
||||
if(ShellExecuteEx(&shl) && shl.hProcess)
|
||||
{
|
||||
mProcess = shl.hProcess;
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteThread::run(void *arg /* = 0 */)
|
||||
{
|
||||
if(mProcess == NULL)
|
||||
return;
|
||||
|
||||
DWORD wait = WAIT_OBJECT_0 - 1; // i.e., not WAIT_OBJECT_0
|
||||
while(! checkForStop() && (wait = WaitForSingleObject(mProcess, 200)) != WAIT_OBJECT_0) ;
|
||||
|
||||
Sim::postEvent(Sim::getRootGroup(), new ExecuteCleanupEvent(this, wait == WAIT_OBJECT_0), -1);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console Functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleFunction(shellExecute, bool, 2, 4, "(string executable, string args, string directory)"
|
||||
"@brief Launches an outside executable or batch file\n\n"
|
||||
"@param executable Name of the executable or batch file\n"
|
||||
"@param args Optional list of arguments, in string format, to pass to the executable\n"
|
||||
"@param directory Optional string containing path to output or shell\n"
|
||||
"@ingroup Platform")
|
||||
{
|
||||
ExecuteThread *et = new ExecuteThread(argv[1], argc > 2 ? argv[2] : NULL, argc > 3 ? argv[3] : NULL);
|
||||
if(! et->isAlive())
|
||||
{
|
||||
delete et;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Platform::openFolder(const char* path )
|
||||
{
|
||||
char filePath[1024];
|
||||
Platform::makeFullPathName(path, filePath, sizeof(filePath));
|
||||
|
||||
#ifdef UNICODE
|
||||
WCHAR p[ 1024 ];
|
||||
convertUTF8toUTF16( filePath, p, sizeof( p ) / sizeof( p[ 0 ] ) );
|
||||
#else
|
||||
char* p = filePath;
|
||||
#endif
|
||||
|
||||
backslash( p );
|
||||
|
||||
::ShellExecute( NULL,TEXT("explore"),p, NULL, NULL, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
void Platform::openFile(const char* path )
|
||||
{
|
||||
char filePath[1024];
|
||||
Platform::makeFullPathName(path, filePath, sizeof(filePath));
|
||||
|
||||
#ifdef UNICODE
|
||||
WCHAR p[ 1024 ];
|
||||
convertUTF8toUTF16( filePath, p, sizeof( p ) / sizeof( p[ 0 ] ) );
|
||||
#else
|
||||
char* p = filePath;
|
||||
#endif
|
||||
|
||||
backslash( p );
|
||||
|
||||
::ShellExecute( NULL,TEXT("open"),p, NULL, NULL, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
1477
Engine/source/platformWin32/winFileio.cpp
Normal file
1477
Engine/source/platformWin32/winFileio.cpp
Normal file
File diff suppressed because it is too large
Load diff
311
Engine/source/platformWin32/winFont.cpp
Normal file
311
Engine/source/platformWin32/winFont.cpp
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platformWin32/winFont.h"
|
||||
|
||||
#include "gfx/gFont.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#include "math/mRect.h"
|
||||
#include "console/console.h"
|
||||
#include "core/strings/unicode.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "core/stringTable.h"
|
||||
|
||||
static HDC fontHDC = NULL;
|
||||
static HBITMAP fontBMP = NULL;
|
||||
|
||||
static U32 charsetMap[]=
|
||||
{
|
||||
ANSI_CHARSET,
|
||||
SYMBOL_CHARSET,
|
||||
SHIFTJIS_CHARSET,
|
||||
HANGEUL_CHARSET,
|
||||
HANGUL_CHARSET,
|
||||
GB2312_CHARSET,
|
||||
CHINESEBIG5_CHARSET,
|
||||
OEM_CHARSET,
|
||||
JOHAB_CHARSET,
|
||||
HEBREW_CHARSET,
|
||||
ARABIC_CHARSET,
|
||||
GREEK_CHARSET,
|
||||
TURKISH_CHARSET,
|
||||
VIETNAMESE_CHARSET,
|
||||
THAI_CHARSET,
|
||||
EASTEUROPE_CHARSET,
|
||||
RUSSIAN_CHARSET,
|
||||
MAC_CHARSET,
|
||||
BALTIC_CHARSET,
|
||||
};
|
||||
#define NUMCHARSETMAP (sizeof(charsetMap) / sizeof(U32))
|
||||
|
||||
void createFontInit(void);
|
||||
void createFontShutdown(void);
|
||||
void CopyCharToBitmap(GBitmap *pDstBMP, HDC hSrcHDC, const RectI &r);
|
||||
|
||||
void createFontInit()
|
||||
{
|
||||
//shared library sets the appInstance here
|
||||
winState.appInstance = GetModuleHandle(NULL);
|
||||
fontHDC = CreateCompatibleDC(NULL);
|
||||
fontBMP = CreateCompatibleBitmap(fontHDC, 256, 256);
|
||||
}
|
||||
|
||||
void createFontShutdown()
|
||||
{
|
||||
DeleteObject(fontBMP);
|
||||
DeleteObject(fontHDC);
|
||||
}
|
||||
|
||||
void CopyCharToBitmap(GBitmap *pDstBMP, HDC hSrcHDC, const RectI &r)
|
||||
{
|
||||
for (S32 i = r.point.y; i < r.point.y + r.extent.y; i++)
|
||||
{
|
||||
for (S32 j = r.point.x; j < r.point.x + r.extent.x; j++)
|
||||
{
|
||||
COLORREF color = GetPixel(hSrcHDC, j, i);
|
||||
if (color)
|
||||
*pDstBMP->getAddress(j, i) = 255;
|
||||
else
|
||||
*pDstBMP->getAddress(j, i) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// WinFont class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
BOOL CALLBACK EnumFamCallBack(LPLOGFONT logFont, LPNEWTEXTMETRIC textMetric, DWORD fontType, LPARAM lParam)
|
||||
{
|
||||
if( !( fontType & TRUETYPE_FONTTYPE ) )
|
||||
return true;
|
||||
|
||||
Vector<StringTableEntry>* fonts = (Vector< StringTableEntry>*)lParam;
|
||||
|
||||
const U32 len = dStrlen( logFont->lfFaceName ) * 3 + 1;
|
||||
FrameTemp<UTF8> buffer( len );
|
||||
convertUTF16toUTF8( logFont->lfFaceName, buffer, len );
|
||||
|
||||
fonts->push_back( StringTable->insert( buffer ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformFont::enumeratePlatformFonts( Vector<StringTableEntry>& fonts, UTF16* fontFamily )
|
||||
{
|
||||
EnumFontFamilies( fontHDC, fontFamily, (FONTENUMPROC)EnumFamCallBack, (LPARAM)&fonts );
|
||||
}
|
||||
|
||||
PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */)
|
||||
{
|
||||
PlatformFont *retFont = new WinFont;
|
||||
|
||||
if(retFont->create(name, size, charset))
|
||||
return retFont;
|
||||
|
||||
delete retFont;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WinFont::WinFont() : mFont(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
WinFont::~WinFont()
|
||||
{
|
||||
if(mFont)
|
||||
{
|
||||
DeleteObject(mFont);
|
||||
}
|
||||
}
|
||||
|
||||
bool WinFont::create(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */)
|
||||
{
|
||||
if(name == NULL || size < 1)
|
||||
return false;
|
||||
|
||||
if(charset > NUMCHARSETMAP)
|
||||
charset = TGE_ANSI_CHARSET;
|
||||
|
||||
U32 weight = 0;
|
||||
U32 doItalic = 0;
|
||||
|
||||
String nameStr = name;
|
||||
nameStr = nameStr.trim();
|
||||
|
||||
bool haveModifier;
|
||||
do
|
||||
{
|
||||
haveModifier = false;
|
||||
if( nameStr.compare( "Bold", 4, String::NoCase | String::Right ) == 0 )
|
||||
{
|
||||
weight = 700;
|
||||
nameStr = nameStr.substr( 0, nameStr.length() - 4 ).trim();
|
||||
haveModifier = true;
|
||||
}
|
||||
if( nameStr.compare( "Italic", 6, String::NoCase | String::Right ) == 0 )
|
||||
{
|
||||
doItalic = 1;
|
||||
nameStr = nameStr.substr( 0, nameStr.length() - 6 ).trim();
|
||||
haveModifier = true;
|
||||
}
|
||||
}
|
||||
while( haveModifier );
|
||||
|
||||
#ifdef UNICODE
|
||||
const UTF16* n = nameStr.utf16();
|
||||
mFont = CreateFont(size,0,0,0,weight,doItalic,0,0,DEFAULT_CHARSET,OUT_TT_PRECIS,0,PROOF_QUALITY,0,n);
|
||||
#else
|
||||
mFont = CreateFont(size,0,0,0,weight,doItalic,0,0,charsetMap[charset],OUT_TT_PRECIS,0,PROOF_QUALITY,0,name);
|
||||
#endif
|
||||
if(mFont == NULL)
|
||||
return false;
|
||||
|
||||
SelectObject(fontHDC, fontBMP);
|
||||
SelectObject(fontHDC, mFont);
|
||||
GetTextMetrics(fontHDC, &mTextMetric);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WinFont::isValidChar(const UTF16 ch) const
|
||||
{
|
||||
return ch != 0 /* && (ch >= mTextMetric.tmFirstChar && ch <= mTextMetric.tmLastChar)*/;
|
||||
}
|
||||
|
||||
bool WinFont::isValidChar(const UTF8 *str) const
|
||||
{
|
||||
return isValidChar(oneUTF8toUTF32(str));
|
||||
}
|
||||
|
||||
|
||||
PlatformFont::CharInfo &WinFont::getCharInfo(const UTF16 ch) const
|
||||
{
|
||||
static PlatformFont::CharInfo c;
|
||||
|
||||
dMemset(&c, 0, sizeof(c));
|
||||
c.bitmapIndex = -1;
|
||||
|
||||
static U8 scratchPad[65536];
|
||||
|
||||
COLORREF backgroundColorRef = RGB( 0, 0, 0);
|
||||
COLORREF foregroundColorRef = RGB(255, 255, 255);
|
||||
SelectObject(fontHDC, fontBMP);
|
||||
SelectObject(fontHDC, mFont);
|
||||
SetBkColor(fontHDC, backgroundColorRef);
|
||||
SetTextColor(fontHDC, foregroundColorRef);
|
||||
|
||||
MAT2 matrix;
|
||||
GLYPHMETRICS metrics;
|
||||
RectI clip;
|
||||
|
||||
FIXED zero;
|
||||
zero.fract = 0;
|
||||
zero.value = 0;
|
||||
FIXED one;
|
||||
one.fract = 0;
|
||||
one.value = 1;
|
||||
|
||||
matrix.eM11 = one;
|
||||
matrix.eM12 = zero;
|
||||
matrix.eM21 = zero;
|
||||
matrix.eM22 = one;
|
||||
|
||||
|
||||
if(GetGlyphOutline(
|
||||
fontHDC, // handle of device context
|
||||
ch, // character to query
|
||||
GGO_GRAY8_BITMAP, // format of data to return
|
||||
&metrics, // address of structure for metrics
|
||||
sizeof(scratchPad), // size of buffer for data
|
||||
scratchPad, // address of buffer for data
|
||||
&matrix // address of transformation matrix structure
|
||||
) != GDI_ERROR)
|
||||
{
|
||||
U32 rowStride = (metrics.gmBlackBoxX + 3) & ~3; // DWORD aligned
|
||||
U32 size = rowStride * metrics.gmBlackBoxY;
|
||||
|
||||
// [neo, 5/7/2007 - #3055]
|
||||
// If we get large font sizes rowStride * metrics.gmBlackBoxY will
|
||||
// be larger than scratch pad size and so overwrite mem, boom!
|
||||
// Added range check < scratchPad for now but we need to review what
|
||||
// to do here - do we want to call GetGlyphOutline() first with null
|
||||
// values and get the real size to alloc buffer?
|
||||
//if( size > sizeof( scratchPad ) )
|
||||
// DebugBreak();
|
||||
|
||||
for(U32 j = 0; j < size && j < sizeof(scratchPad); j++)
|
||||
{
|
||||
U32 pad = U32(scratchPad[j]) << 2;
|
||||
if(pad > 255)
|
||||
pad = 255;
|
||||
scratchPad[j] = pad;
|
||||
}
|
||||
S32 inc = metrics.gmCellIncX;
|
||||
if(inc < 0)
|
||||
inc = -inc;
|
||||
|
||||
c.xOffset = 0;
|
||||
c.yOffset = 0;
|
||||
c.width = metrics.gmBlackBoxX;
|
||||
c.height = metrics.gmBlackBoxY;
|
||||
c.xOrigin = metrics.gmptGlyphOrigin.x;
|
||||
c.yOrigin = metrics.gmptGlyphOrigin.y;
|
||||
c.xIncrement = metrics.gmCellIncX;
|
||||
|
||||
c.bitmapData = new U8[c.width * c.height];
|
||||
AssertFatal( c.bitmapData != NULL, "Could not allocate memory for font bitmap data!");
|
||||
for(U32 y = 0; S32(y) < c.height; y++)
|
||||
{
|
||||
U32 x;
|
||||
for(x = 0; x < c.width; x++)
|
||||
{
|
||||
// [neo, 5/7/2007 - #3055]
|
||||
// See comments above about scratchPad overrun
|
||||
S32 spi = y * rowStride + x;
|
||||
|
||||
if( spi >= sizeof(scratchPad) )
|
||||
return c;
|
||||
|
||||
c.bitmapData[y * c.width + x] = scratchPad[spi];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SIZE size;
|
||||
GetTextExtentPoint32W(fontHDC, &ch, 1, &size);
|
||||
if(size.cx)
|
||||
{
|
||||
c.xIncrement = size.cx;
|
||||
c.bitmapIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
PlatformFont::CharInfo &WinFont::getCharInfo(const UTF8 *str) const
|
||||
{
|
||||
return getCharInfo(oneUTF8toUTF32(str));
|
||||
}
|
||||
60
Engine/source/platformWin32/winFont.h
Normal file
60
Engine/source/platformWin32/winFont.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _PLATFORMFONT_H_
|
||||
#include "platform/platformFont.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WINFONT_H_
|
||||
#define _WINFONT_H_
|
||||
|
||||
class WinFont : public PlatformFont
|
||||
{
|
||||
private:
|
||||
HFONT mFont;
|
||||
TEXTMETRIC mTextMetric;
|
||||
|
||||
public:
|
||||
WinFont();
|
||||
virtual ~WinFont();
|
||||
|
||||
// PlatformFont virtual methods
|
||||
virtual bool isValidChar(const UTF16 ch) const;
|
||||
virtual bool isValidChar(const UTF8 *str) const;
|
||||
|
||||
inline virtual U32 getFontHeight() const
|
||||
{
|
||||
return mTextMetric.tmHeight;
|
||||
}
|
||||
|
||||
inline virtual U32 getFontBaseLine() const
|
||||
{
|
||||
return mTextMetric.tmAscent;
|
||||
}
|
||||
|
||||
virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const;
|
||||
virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const;
|
||||
|
||||
virtual bool create(const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET);
|
||||
};
|
||||
|
||||
#endif // _WINFONT_H_
|
||||
868
Engine/source/platformWin32/winInput.cpp
Normal file
868
Engine/source/platformWin32/winInput.cpp
Normal file
|
|
@ -0,0 +1,868 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platform/platformInput.h"
|
||||
#include "platformWin32/winDirectInput.h"
|
||||
#include "platform/event.h"
|
||||
#include "console/console.h"
|
||||
#include "core/util/journal/process.h"
|
||||
#include "windowManager/platformWindowMgr.h"
|
||||
|
||||
#ifdef LOG_INPUT
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
// Static class variables:
|
||||
InputManager* Input::smManager;
|
||||
bool Input::smActive;
|
||||
U8 Input::smModifierKeys;
|
||||
bool Input::smLastKeyboardActivated;
|
||||
bool Input::smLastMouseActivated;
|
||||
bool Input::smLastJoystickActivated;
|
||||
InputEvent Input::smInputEvent;
|
||||
|
||||
#ifdef LOG_INPUT
|
||||
static HANDLE gInputLog;
|
||||
#endif
|
||||
|
||||
static void fillAsciiTable();
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// This function gets the standard ASCII code corresponding to our key code
|
||||
// and the existing modifier key state.
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
struct AsciiData
|
||||
{
|
||||
struct KeyData
|
||||
{
|
||||
U16 ascii;
|
||||
bool isDeadChar;
|
||||
};
|
||||
|
||||
KeyData upper;
|
||||
KeyData lower;
|
||||
KeyData goofy;
|
||||
};
|
||||
|
||||
|
||||
#define NUM_KEYS ( KEY_OEM_102 + 1 )
|
||||
#define KEY_FIRST KEY_ESCAPE
|
||||
static AsciiData AsciiTable[NUM_KEYS];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Input::init()
|
||||
{
|
||||
Con::printf( "Input Init:" );
|
||||
|
||||
destroy();
|
||||
|
||||
#ifdef LOG_INPUT
|
||||
struct tm* newTime;
|
||||
time_t aclock;
|
||||
time( &aclock );
|
||||
newTime = localtime( &aclock );
|
||||
asctime( newTime );
|
||||
|
||||
gInputLog = CreateFile( L"input.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
log( "Input log opened at %s\n", asctime( newTime ) );
|
||||
#endif
|
||||
|
||||
smActive = false;
|
||||
smLastKeyboardActivated = true;
|
||||
smLastMouseActivated = true;
|
||||
smLastJoystickActivated = true;
|
||||
|
||||
OSVERSIONINFO OSVersionInfo;
|
||||
dMemset( &OSVersionInfo, 0, sizeof( OSVERSIONINFO ) );
|
||||
OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
|
||||
if ( GetVersionEx( &OSVersionInfo ) )
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
log( "Operating System:\n" );
|
||||
switch ( OSVersionInfo.dwPlatformId )
|
||||
{
|
||||
case VER_PLATFORM_WIN32s:
|
||||
log( " Win32s on Windows 3.1 version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion );
|
||||
break;
|
||||
|
||||
case VER_PLATFORM_WIN32_WINDOWS:
|
||||
log( " Windows 95 version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion );
|
||||
log( " Build number %d\n", LOWORD( OSVersionInfo.dwBuildNumber ) );
|
||||
break;
|
||||
|
||||
case VER_PLATFORM_WIN32_NT:
|
||||
log( " WinNT version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion );
|
||||
log( " Build number %d\n", OSVersionInfo.dwBuildNumber );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( OSVersionInfo.szCSDVersion != NULL )
|
||||
log( " %s\n", OSVersionInfo.szCSDVersion );
|
||||
|
||||
log( "\n" );
|
||||
#endif
|
||||
|
||||
if ( !( OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && OSVersionInfo.dwMajorVersion < 5 ) )
|
||||
{
|
||||
smManager = new DInputManager;
|
||||
if ( !smManager->enable() )
|
||||
{
|
||||
Con::printf( " DirectInput not enabled." );
|
||||
delete smManager;
|
||||
smManager = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
DInputManager::init();
|
||||
Con::printf( " DirectInput enabled." );
|
||||
}
|
||||
}
|
||||
else
|
||||
Con::printf( " WinNT detected -- DirectInput not enabled." );
|
||||
}
|
||||
|
||||
// Init the current modifier keys
|
||||
setModifierKeys(0);
|
||||
fillAsciiTable();
|
||||
Con::printf( "" );
|
||||
|
||||
// Set ourselves to participate in per-frame processing.
|
||||
Process::notify(Input::process, PROCESS_INPUT_ORDER);
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( isJoystickDetected, bool, 1, 1, "isJoystickDetected()" )
|
||||
{
|
||||
argc; argv;
|
||||
return( DInputDevice::joystickDetected() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction( getJoystickAxes, const char*, 2, 2, "getJoystickAxes( instance )" )
|
||||
{
|
||||
argc;
|
||||
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
|
||||
if ( mgr )
|
||||
return( mgr->getJoystickAxesString( dAtoi( argv[1] ) ) );
|
||||
|
||||
return( "" );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void fillAsciiTable()
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
char buf[256];
|
||||
Input::log( "--- Filling the ASCII table! ---\n" );
|
||||
#endif
|
||||
|
||||
//HKL layout = GetKeyboardLayout( 0 );
|
||||
U8 state[256];
|
||||
U16 ascii[2];
|
||||
U32 dikCode, vKeyCode, keyCode;
|
||||
S32 result;
|
||||
|
||||
dMemset( &AsciiTable, 0, sizeof( AsciiTable ) );
|
||||
dMemset( &state, 0, sizeof( state ) );
|
||||
|
||||
for ( keyCode = KEY_FIRST; keyCode < NUM_KEYS; keyCode++ )
|
||||
{
|
||||
ascii[0] = ascii[1] = 0;
|
||||
dikCode = Key_to_DIK( keyCode );
|
||||
|
||||
// This is a special case for numpad keys.
|
||||
//
|
||||
// The KEY_NUMPAD# torque types represent the event generated when a
|
||||
// numpad key is pressed WITH NUMLOCK ON. Therefore it does have an ascii
|
||||
// value, but to get it from windows we must specify in the keyboard state
|
||||
// that numlock is on.
|
||||
if ( KEY_NUMPAD0 <= keyCode && keyCode <= KEY_NUMPAD9 )
|
||||
{
|
||||
state[VK_NUMLOCK] = 0x80;
|
||||
|
||||
// Also, numpad keys return completely different keycodes when
|
||||
// numlock is not pressed (home,insert,etc) and MapVirtualKey
|
||||
// appears to always return those values.
|
||||
//
|
||||
// So I'm using TranslateKeyCodeToOS instead. Really it looks
|
||||
// like we could be using this method for all of them and
|
||||
// cut out the Key_to_DIK middleman.
|
||||
//
|
||||
vKeyCode = TranslateKeyCodeToOS( keyCode );
|
||||
|
||||
result = ToAscii( vKeyCode, dikCode, state, ascii, 0 );
|
||||
|
||||
AsciiTable[keyCode].lower.ascii = ascii[0];
|
||||
AsciiTable[keyCode].upper.ascii = 0;
|
||||
AsciiTable[keyCode].goofy.ascii = 0;
|
||||
|
||||
state[VK_NUMLOCK] = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( dikCode )
|
||||
{
|
||||
//vKeyCode = MapVirtualKeyEx( dikCode, 1, layout );
|
||||
vKeyCode = MapVirtualKey( dikCode, 1 );
|
||||
#ifdef LOG_INPUT
|
||||
dSprintf( buf, sizeof( buf ), "KC: %#04X DK: %#04X VK: %#04X\n",
|
||||
keyCode, dikCode, vKeyCode );
|
||||
Input::log( buf );
|
||||
#endif
|
||||
|
||||
// Lower case:
|
||||
ascii[0] = ascii[1] = 0;
|
||||
//result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
|
||||
result = ToAscii( vKeyCode, dikCode, state, ascii, 0 );
|
||||
#ifdef LOG_INPUT
|
||||
dSprintf( buf, sizeof( buf ), " LOWER- R: %d A[0]: %#06X A[1]: %#06X\n",
|
||||
result, ascii[0], ascii[1] );
|
||||
Input::log( buf );
|
||||
#endif
|
||||
if ( result == 2 )
|
||||
AsciiTable[keyCode].lower.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 );
|
||||
else if ( result == 1 )
|
||||
AsciiTable[keyCode].lower.ascii = ascii[0];
|
||||
else if ( result < 0 )
|
||||
{
|
||||
AsciiTable[keyCode].lower.ascii = ascii[0];
|
||||
AsciiTable[keyCode].lower.isDeadChar = true;
|
||||
// Need to clear the dead character from the keyboard layout:
|
||||
//ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
|
||||
ToAscii( vKeyCode, dikCode, state, ascii, 0 );
|
||||
}
|
||||
|
||||
// Upper case:
|
||||
ascii[0] = ascii[1] = 0;
|
||||
state[VK_SHIFT] = 0x80;
|
||||
//result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
|
||||
result = ToAscii( vKeyCode, dikCode, state, ascii, 0 );
|
||||
#ifdef LOG_INPUT
|
||||
dSprintf( buf, sizeof( buf ), " UPPER- R: %d A[0]: %#06X A[1]: %#06X\n",
|
||||
result, ascii[0], ascii[1] );
|
||||
Input::log( buf );
|
||||
#endif
|
||||
if ( result == 2 )
|
||||
AsciiTable[keyCode].upper.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 );
|
||||
else if ( result == 1 )
|
||||
AsciiTable[keyCode].upper.ascii = ascii[0];
|
||||
else if ( result < 0 )
|
||||
{
|
||||
AsciiTable[keyCode].upper.ascii = ascii[0];
|
||||
AsciiTable[keyCode].upper.isDeadChar = true;
|
||||
// Need to clear the dead character from the keyboard layout:
|
||||
//ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
|
||||
ToAscii( vKeyCode, dikCode, state, ascii, 0 );
|
||||
}
|
||||
state[VK_SHIFT] = 0;
|
||||
|
||||
// Foreign mod case:
|
||||
ascii[0] = ascii[1] = 0;
|
||||
state[VK_CONTROL] = 0x80;
|
||||
state[VK_MENU] = 0x80;
|
||||
//result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
|
||||
result = ToAscii( vKeyCode, dikCode, state, ascii, 0 );
|
||||
#ifdef LOG_INPUT
|
||||
dSprintf( buf, sizeof( buf ), " GOOFY- R: %d A[0]: %#06X A[1]: %#06X\n",
|
||||
result, ascii[0], ascii[1] );
|
||||
Input::log( buf );
|
||||
#endif
|
||||
if ( result == 2 )
|
||||
AsciiTable[keyCode].goofy.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 );
|
||||
else if ( result == 1 )
|
||||
AsciiTable[keyCode].goofy.ascii = ascii[0];
|
||||
else if ( result < 0 )
|
||||
{
|
||||
AsciiTable[keyCode].goofy.ascii = ascii[0];
|
||||
AsciiTable[keyCode].goofy.isDeadChar = true;
|
||||
// Need to clear the dead character from the keyboard layout:
|
||||
//ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
|
||||
ToAscii( vKeyCode, dikCode, state, ascii, 0 );
|
||||
}
|
||||
state[VK_CONTROL] = 0;
|
||||
state[VK_MENU] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "--- Finished filling the ASCII table! ---\n\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
U16 Input::getKeyCode( U16 asciiCode )
|
||||
{
|
||||
U16 keyCode = 0;
|
||||
U16 i;
|
||||
|
||||
// This is done three times so the lowerkey will always
|
||||
// be found first. Some foreign keyboards have duplicate
|
||||
// chars on some keys.
|
||||
for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
|
||||
{
|
||||
if ( AsciiTable[i].lower.ascii == asciiCode )
|
||||
{
|
||||
keyCode = i;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
|
||||
{
|
||||
if ( AsciiTable[i].upper.ascii == asciiCode )
|
||||
{
|
||||
keyCode = i;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
|
||||
{
|
||||
if ( AsciiTable[i].goofy.ascii == asciiCode )
|
||||
{
|
||||
keyCode = i;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
return( keyCode );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
U16 Input::getAscii( U16 keyCode, KEY_STATE keyState )
|
||||
{
|
||||
if ( keyCode >= NUM_KEYS )
|
||||
return 0;
|
||||
|
||||
switch ( keyState )
|
||||
{
|
||||
case STATE_LOWER:
|
||||
return AsciiTable[keyCode].lower.ascii;
|
||||
case STATE_UPPER:
|
||||
return AsciiTable[keyCode].upper.ascii;
|
||||
case STATE_GOOFY:
|
||||
return AsciiTable[keyCode].goofy.ascii;
|
||||
default:
|
||||
return(0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Input::destroy()
|
||||
{
|
||||
Process::remove(Input::process);
|
||||
|
||||
#ifdef LOG_INPUT
|
||||
if ( gInputLog )
|
||||
{
|
||||
log( "*** CLOSING LOG ***\n" );
|
||||
CloseHandle( gInputLog );
|
||||
gInputLog = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( smManager && smManager->isEnabled() )
|
||||
{
|
||||
smManager->disable();
|
||||
delete smManager;
|
||||
smManager = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Input::enable()
|
||||
{
|
||||
if ( smManager && !smManager->isEnabled() )
|
||||
return( smManager->enable() );
|
||||
|
||||
return( false );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Input::disable()
|
||||
{
|
||||
if ( smManager && smManager->isEnabled() )
|
||||
smManager->disable();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void Input::activate()
|
||||
{
|
||||
#ifdef UNICODE
|
||||
//winState.imeHandle = ImmGetContext( getWin32WindowHandle() );
|
||||
//ImmReleaseContext( getWin32WindowHandle(), winState.imeHandle );
|
||||
#endif
|
||||
|
||||
if ( !Con::getBoolVariable( "$enableDirectInput" ) )
|
||||
return;
|
||||
|
||||
if ( smManager && smManager->isEnabled() && !smActive )
|
||||
{
|
||||
Con::printf( "Activating DirectInput..." );
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Activating DirectInput...\n" );
|
||||
#endif
|
||||
smActive = true;
|
||||
DInputManager* dInputManager = dynamic_cast<DInputManager*>( smManager );
|
||||
if ( dInputManager )
|
||||
{
|
||||
if ( dInputManager->isJoystickEnabled() && smLastJoystickActivated )
|
||||
dInputManager->activateJoystick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Input::deactivate()
|
||||
{
|
||||
if ( smManager && smManager->isEnabled() && smActive )
|
||||
{
|
||||
#ifdef LOG_INPUT
|
||||
Input::log( "Deactivating DirectInput...\n" );
|
||||
#endif
|
||||
DInputManager* dInputManager = dynamic_cast<DInputManager*>( smManager );
|
||||
|
||||
if ( dInputManager )
|
||||
{
|
||||
smLastJoystickActivated = dInputManager->isJoystickActive();
|
||||
dInputManager->deactivateJoystick();
|
||||
}
|
||||
|
||||
smActive = false;
|
||||
Con::printf( "DirectInput deactivated." );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Input::isEnabled()
|
||||
{
|
||||
if ( smManager )
|
||||
return smManager->isEnabled();
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Input::isActive()
|
||||
{
|
||||
return smActive;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Input::process()
|
||||
{
|
||||
if ( smManager && smManager->isEnabled() && smActive )
|
||||
smManager->process();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
InputManager* Input::getManager()
|
||||
{
|
||||
return( smManager );
|
||||
}
|
||||
|
||||
#ifdef LOG_INPUT
|
||||
//------------------------------------------------------------------------------
|
||||
void Input::log( const char* format, ... )
|
||||
{
|
||||
if ( !gInputLog )
|
||||
return;
|
||||
|
||||
va_list argptr;
|
||||
va_start( argptr, format );
|
||||
|
||||
char buffer[512];
|
||||
dVsprintf( buffer, 511, format, argptr );
|
||||
DWORD bytes;
|
||||
WriteFile( gInputLog, buffer, dStrlen( buffer ), &bytes, NULL );
|
||||
|
||||
va_end( argptr );
|
||||
}
|
||||
|
||||
ConsoleFunction( inputLog, void, 2, 2, "inputLog( string )" )
|
||||
{
|
||||
argc;
|
||||
Input::log( "%s\n", argv[1] );
|
||||
}
|
||||
#endif // LOG_INPUT
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
static U8 VcodeRemap[256] =
|
||||
{
|
||||
0, // 0x00
|
||||
0, // 0x01 VK_LBUTTON
|
||||
0, // 0x02 VK_RBUTTON
|
||||
0, // 0x03 VK_CANCEL
|
||||
0, // 0x04 VK_MBUTTON
|
||||
0, // 0x05
|
||||
0, // 0x06
|
||||
0, // 0x07
|
||||
KEY_BACKSPACE, // 0x08 VK_BACK
|
||||
KEY_TAB, // 0x09 VK_TAB
|
||||
0, // 0x0A
|
||||
0, // 0x0B
|
||||
0, // 0x0C VK_CLEAR
|
||||
KEY_RETURN, // 0x0D VK_RETURN
|
||||
0, // 0x0E
|
||||
0, // 0x0F
|
||||
KEY_SHIFT, // 0x10 VK_SHIFT
|
||||
KEY_CONTROL, // 0x11 VK_CONTROL
|
||||
KEY_ALT, // 0x12 VK_MENU
|
||||
KEY_PAUSE, // 0x13 VK_PAUSE
|
||||
KEY_CAPSLOCK, // 0x14 VK_CAPITAL
|
||||
0, // 0x15 VK_KANA, VK_HANGEUL, VK_HANGUL
|
||||
0, // 0x16
|
||||
0, // 0x17 VK_JUNJA
|
||||
0, // 0x18 VK_FINAL
|
||||
0, // 0x19 VK_HANJA, VK_KANJI
|
||||
0, // 0x1A
|
||||
KEY_ESCAPE, // 0x1B VK_ESCAPE
|
||||
|
||||
0, // 0x1C VK_CONVERT
|
||||
0, // 0x1D VK_NONCONVERT
|
||||
0, // 0x1E VK_ACCEPT
|
||||
0, // 0x1F VK_MODECHANGE
|
||||
|
||||
KEY_SPACE, // 0x20 VK_SPACE
|
||||
KEY_PAGE_UP, // 0x21 VK_PRIOR
|
||||
KEY_PAGE_DOWN, // 0x22 VK_NEXT
|
||||
KEY_END, // 0x23 VK_END
|
||||
KEY_HOME, // 0x24 VK_HOME
|
||||
KEY_LEFT, // 0x25 VK_LEFT
|
||||
KEY_UP, // 0x26 VK_UP
|
||||
KEY_RIGHT, // 0x27 VK_RIGHT
|
||||
KEY_DOWN, // 0x28 VK_DOWN
|
||||
0, // 0x29 VK_SELECT
|
||||
KEY_PRINT, // 0x2A VK_PRINT
|
||||
0, // 0x2B VK_EXECUTE
|
||||
0, // 0x2C VK_SNAPSHOT
|
||||
KEY_INSERT, // 0x2D VK_INSERT
|
||||
KEY_DELETE, // 0x2E VK_DELETE
|
||||
KEY_HELP, // 0x2F VK_HELP
|
||||
|
||||
KEY_0, // 0x30 VK_0 VK_0 thru VK_9 are the same as ASCII '0' thru '9' (// 0x30 - // 0x39)
|
||||
KEY_1, // 0x31 VK_1
|
||||
KEY_2, // 0x32 VK_2
|
||||
KEY_3, // 0x33 VK_3
|
||||
KEY_4, // 0x34 VK_4
|
||||
KEY_5, // 0x35 VK_5
|
||||
KEY_6, // 0x36 VK_6
|
||||
KEY_7, // 0x37 VK_7
|
||||
KEY_8, // 0x38 VK_8
|
||||
KEY_9, // 0x39 VK_9
|
||||
0, // 0x3A
|
||||
0, // 0x3B
|
||||
0, // 0x3C
|
||||
0, // 0x3D
|
||||
0, // 0x3E
|
||||
0, // 0x3F
|
||||
0, // 0x40
|
||||
|
||||
KEY_A, // 0x41 VK_A VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (// 0x41 - // 0x5A)
|
||||
KEY_B, // 0x42 VK_B
|
||||
KEY_C, // 0x43 VK_C
|
||||
KEY_D, // 0x44 VK_D
|
||||
KEY_E, // 0x45 VK_E
|
||||
KEY_F, // 0x46 VK_F
|
||||
KEY_G, // 0x47 VK_G
|
||||
KEY_H, // 0x48 VK_H
|
||||
KEY_I, // 0x49 VK_I
|
||||
KEY_J, // 0x4A VK_J
|
||||
KEY_K, // 0x4B VK_K
|
||||
KEY_L, // 0x4C VK_L
|
||||
KEY_M, // 0x4D VK_M
|
||||
KEY_N, // 0x4E VK_N
|
||||
KEY_O, // 0x4F VK_O
|
||||
KEY_P, // 0x50 VK_P
|
||||
KEY_Q, // 0x51 VK_Q
|
||||
KEY_R, // 0x52 VK_R
|
||||
KEY_S, // 0x53 VK_S
|
||||
KEY_T, // 0x54 VK_T
|
||||
KEY_U, // 0x55 VK_U
|
||||
KEY_V, // 0x56 VK_V
|
||||
KEY_W, // 0x57 VK_W
|
||||
KEY_X, // 0x58 VK_X
|
||||
KEY_Y, // 0x59 VK_Y
|
||||
KEY_Z, // 0x5A VK_Z
|
||||
|
||||
|
||||
KEY_WIN_LWINDOW, // 0x5B VK_LWIN
|
||||
KEY_WIN_RWINDOW, // 0x5C VK_RWIN
|
||||
KEY_WIN_APPS, // 0x5D VK_APPS
|
||||
0, // 0x5E
|
||||
0, // 0x5F
|
||||
|
||||
KEY_NUMPAD0, // 0x60 VK_NUMPAD0
|
||||
KEY_NUMPAD1, // 0x61 VK_NUMPAD1
|
||||
KEY_NUMPAD2, // 0x62 VK_NUMPAD2
|
||||
KEY_NUMPAD3, // 0x63 VK_NUMPAD3
|
||||
KEY_NUMPAD4, // 0x64 VK_NUMPAD4
|
||||
KEY_NUMPAD5, // 0x65 VK_NUMPAD5
|
||||
KEY_NUMPAD6, // 0x66 VK_NUMPAD6
|
||||
KEY_NUMPAD7, // 0x67 VK_NUMPAD7
|
||||
KEY_NUMPAD8, // 0x68 VK_NUMPAD8
|
||||
KEY_NUMPAD9, // 0x69 VK_NUMPAD9
|
||||
KEY_MULTIPLY, // 0x6A VK_MULTIPLY
|
||||
KEY_ADD, // 0x6B VK_ADD
|
||||
KEY_SEPARATOR, // 0x6C VK_SEPARATOR
|
||||
KEY_SUBTRACT, // 0x6D VK_SUBTRACT
|
||||
KEY_DECIMAL, // 0x6E VK_DECIMAL
|
||||
KEY_DIVIDE, // 0x6F VK_DIVIDE
|
||||
KEY_F1, // 0x70 VK_F1
|
||||
KEY_F2, // 0x71 VK_F2
|
||||
KEY_F3, // 0x72 VK_F3
|
||||
KEY_F4, // 0x73 VK_F4
|
||||
KEY_F5, // 0x74 VK_F5
|
||||
KEY_F6, // 0x75 VK_F6
|
||||
KEY_F7, // 0x76 VK_F7
|
||||
KEY_F8, // 0x77 VK_F8
|
||||
KEY_F9, // 0x78 VK_F9
|
||||
KEY_F10, // 0x79 VK_F10
|
||||
KEY_F11, // 0x7A VK_F11
|
||||
KEY_F12, // 0x7B VK_F12
|
||||
KEY_F13, // 0x7C VK_F13
|
||||
KEY_F14, // 0x7D VK_F14
|
||||
KEY_F15, // 0x7E VK_F15
|
||||
KEY_F16, // 0x7F VK_F16
|
||||
KEY_F17, // 0x80 VK_F17
|
||||
KEY_F18, // 0x81 VK_F18
|
||||
KEY_F19, // 0x82 VK_F19
|
||||
KEY_F20, // 0x83 VK_F20
|
||||
KEY_F21, // 0x84 VK_F21
|
||||
KEY_F22, // 0x85 VK_F22
|
||||
KEY_F23, // 0x86 VK_F23
|
||||
KEY_F24, // 0x87 VK_F24
|
||||
0, // 0x88
|
||||
0, // 0x89
|
||||
0, // 0x8A
|
||||
0, // 0x8B
|
||||
0, // 0x8C
|
||||
0, // 0x8D
|
||||
0, // 0x8E
|
||||
0, // 0x8F
|
||||
|
||||
KEY_NUMLOCK, // 0x90 VK_NUMLOCK
|
||||
KEY_SCROLLLOCK, // 0x91 VK_OEM_SCROLL
|
||||
0, // 0x92
|
||||
0, // 0x93
|
||||
0, // 0x94
|
||||
0, // 0x95
|
||||
0, // 0x96
|
||||
0, // 0x97
|
||||
0, // 0x98
|
||||
0, // 0x99
|
||||
0, // 0x9A
|
||||
0, // 0x9B
|
||||
0, // 0x9C
|
||||
0, // 0x9D
|
||||
0, // 0x9E
|
||||
0, // 0x9F
|
||||
|
||||
KEY_LSHIFT, // 0xA0 VK_LSHIFT
|
||||
KEY_RSHIFT, // 0xA1 VK_RSHIFT
|
||||
KEY_LCONTROL, // 0xA2 VK_LCONTROL
|
||||
KEY_RCONTROL, // 0xA3 VK_RCONTROL
|
||||
KEY_LALT, // 0xA4 VK_LMENU
|
||||
KEY_RALT, // 0xA5 VK_RMENU
|
||||
0, // 0xA6
|
||||
0, // 0xA7
|
||||
0, // 0xA8
|
||||
0, // 0xA9
|
||||
0, // 0xAA
|
||||
0, // 0xAB
|
||||
0, // 0xAC
|
||||
0, // 0xAD
|
||||
0, // 0xAE
|
||||
0, // 0xAF
|
||||
0, // 0xB0
|
||||
0, // 0xB1
|
||||
0, // 0xB2
|
||||
0, // 0xB3
|
||||
0, // 0xB4
|
||||
0, // 0xB5
|
||||
0, // 0xB6
|
||||
0, // 0xB7
|
||||
0, // 0xB8
|
||||
0, // 0xB9
|
||||
KEY_SEMICOLON, // 0xBA VK_OEM_1
|
||||
KEY_EQUALS, // 0xBB VK_OEM_PLUS
|
||||
KEY_COMMA, // 0xBC VK_OEM_COMMA
|
||||
KEY_MINUS, // 0xBD VK_OEM_MINUS
|
||||
KEY_PERIOD, // 0xBE VK_OEM_PERIOD
|
||||
KEY_SLASH, // 0xBF VK_OEM_2
|
||||
KEY_TILDE, // 0xC0 VK_OEM_3
|
||||
0, // 0xC1
|
||||
0, // 0xC2
|
||||
0, // 0xC3
|
||||
0, // 0xC4
|
||||
0, // 0xC5
|
||||
0, // 0xC6
|
||||
0, // 0xC7
|
||||
0, // 0xC8
|
||||
0, // 0xC9
|
||||
0, // 0xCA
|
||||
0, // 0xCB
|
||||
0, // 0xCC
|
||||
0, // 0xCD
|
||||
0, // 0xCE
|
||||
0, // 0xCF
|
||||
0, // 0xD0
|
||||
0, // 0xD1
|
||||
0, // 0xD2
|
||||
0, // 0xD3
|
||||
0, // 0xD4
|
||||
0, // 0xD5
|
||||
0, // 0xD6
|
||||
0, // 0xD7
|
||||
0, // 0xD8
|
||||
0, // 0xD9
|
||||
0, // 0xDA
|
||||
KEY_LBRACKET, // 0xDB VK_OEM_4
|
||||
KEY_BACKSLASH, // 0xDC VK_OEM_5
|
||||
KEY_RBRACKET, // 0xDD VK_OEM_6
|
||||
KEY_APOSTROPHE, // 0xDE VK_OEM_7
|
||||
0, // 0xDF VK_OEM_8
|
||||
0, // 0xE0
|
||||
0, // 0xE1 VK_OEM_AX AX key on Japanese AX keyboard
|
||||
KEY_OEM_102, // 0xE2 VK_OEM_102
|
||||
0, // 0xE3
|
||||
0, // 0xE4
|
||||
|
||||
0, // 0xE5 VK_PROCESSKEY
|
||||
|
||||
0, // 0xE6
|
||||
0, // 0xE7
|
||||
0, // 0xE8
|
||||
0, // 0xE9
|
||||
0, // 0xEA
|
||||
0, // 0xEB
|
||||
0, // 0xEC
|
||||
0, // 0xED
|
||||
0, // 0xEE
|
||||
0, // 0xEF
|
||||
|
||||
0, // 0xF0
|
||||
0, // 0xF1
|
||||
0, // 0xF2
|
||||
0, // 0xF3
|
||||
0, // 0xF4
|
||||
0, // 0xF5
|
||||
|
||||
0, // 0xF6 VK_ATTN
|
||||
0, // 0xF7 VK_CRSEL
|
||||
0, // 0xF8 VK_EXSEL
|
||||
0, // 0xF9 VK_EREOF
|
||||
0, // 0xFA VK_PLAY
|
||||
0, // 0xFB VK_ZOOM
|
||||
0, // 0xFC VK_NONAME
|
||||
0, // 0xFD VK_PA1
|
||||
0, // 0xFE VK_OEM_CLEAR
|
||||
0 // 0xFF
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// This function translates a virtual key code to our corresponding internal
|
||||
// key code using the preceding table.
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
U8 TranslateOSKeyCode(U8 vcode)
|
||||
{
|
||||
return VcodeRemap[vcode];
|
||||
}
|
||||
|
||||
U8 TranslateKeyCodeToOS(U8 keycode)
|
||||
{
|
||||
for(S32 i = 0;i < sizeof(VcodeRemap) / sizeof(U8);++i)
|
||||
{
|
||||
if(VcodeRemap[i] == keycode)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Clipboard functions
|
||||
const char* Platform::getClipboard()
|
||||
{
|
||||
HGLOBAL hGlobal;
|
||||
LPVOID pGlobal;
|
||||
|
||||
//make sure we can access the clipboard
|
||||
if (!IsClipboardFormatAvailable(CF_TEXT))
|
||||
return "";
|
||||
if (!OpenClipboard(NULL))
|
||||
return "";
|
||||
|
||||
hGlobal = GetClipboardData(CF_TEXT);
|
||||
pGlobal = GlobalLock(hGlobal);
|
||||
S32 cbLength = strlen((char *)pGlobal);
|
||||
char *returnBuf = Con::getReturnBuffer(cbLength + 1);
|
||||
strcpy(returnBuf, (char *)pGlobal);
|
||||
returnBuf[cbLength] = '\0';
|
||||
GlobalUnlock(hGlobal);
|
||||
CloseClipboard();
|
||||
|
||||
//note - this function never returns NULL
|
||||
return returnBuf;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool Platform::setClipboard(const char *text)
|
||||
{
|
||||
if (!text)
|
||||
return false;
|
||||
|
||||
//make sure we can access the clipboard
|
||||
if (!OpenClipboard(NULL))
|
||||
return false;
|
||||
|
||||
S32 cbLength = strlen(text);
|
||||
|
||||
HGLOBAL hGlobal;
|
||||
LPVOID pGlobal;
|
||||
|
||||
hGlobal = GlobalAlloc(GHND, cbLength + 1);
|
||||
pGlobal = GlobalLock (hGlobal);
|
||||
|
||||
strcpy((char *)pGlobal, text);
|
||||
|
||||
GlobalUnlock(hGlobal);
|
||||
|
||||
EmptyClipboard();
|
||||
SetClipboardData(CF_TEXT, hGlobal);
|
||||
CloseClipboard();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
129
Engine/source/platformWin32/winMath.cpp
Normal file
129
Engine/source/platformWin32/winMath.cpp
Normal 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "console/console.h"
|
||||
|
||||
#include "math/mMath.h"
|
||||
|
||||
|
||||
extern void mInstallLibrary_C();
|
||||
extern void mInstallLibrary_ASM();
|
||||
extern void mInstall_AMD_Math();
|
||||
extern void mInstall_Library_SSE();
|
||||
|
||||
//--------------------------------------
|
||||
ConsoleFunction( mathInit, void, 1, 10, "( ... )"
|
||||
"@brief Install the math library with specified extensions.\n\n"
|
||||
"Possible parameters are:\n\n"
|
||||
" - 'DETECT' Autodetect math lib settings.\n\n"
|
||||
" - 'C' Enable the C math routines. C routines are always enabled.\n\n"
|
||||
" - 'FPU' Enable floating point unit routines.\n\n"
|
||||
" - 'MMX' Enable MMX math routines.\n\n"
|
||||
" - '3DNOW' Enable 3dNow! math routines.\n\n"
|
||||
" - 'SSE' Enable SSE math routines.\n\n"
|
||||
"@ingroup Math")
|
||||
|
||||
|
||||
{
|
||||
U32 properties = CPU_PROP_C; // C entensions are always used
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
Math::init(0);
|
||||
return;
|
||||
}
|
||||
for (argc--, argv++; argc; argc--, argv++)
|
||||
{
|
||||
if (dStricmp(*argv, "DETECT") == 0) {
|
||||
Math::init(0);
|
||||
return;
|
||||
}
|
||||
if (dStricmp(*argv, "C") == 0) {
|
||||
properties |= CPU_PROP_C;
|
||||
continue;
|
||||
}
|
||||
if (dStricmp(*argv, "FPU") == 0) {
|
||||
properties |= CPU_PROP_FPU;
|
||||
continue;
|
||||
}
|
||||
if (dStricmp(*argv, "MMX") == 0) {
|
||||
properties |= CPU_PROP_MMX;
|
||||
continue;
|
||||
}
|
||||
if (dStricmp(*argv, "3DNOW") == 0) {
|
||||
properties |= CPU_PROP_3DNOW;
|
||||
continue;
|
||||
}
|
||||
if (dStricmp(*argv, "SSE") == 0) {
|
||||
properties |= CPU_PROP_SSE;
|
||||
continue;
|
||||
}
|
||||
Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", *argv);
|
||||
}
|
||||
Math::init(properties);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Math::init(U32 properties)
|
||||
{
|
||||
if (!properties)
|
||||
// detect what's available
|
||||
properties = Platform::SystemInfo.processor.properties;
|
||||
else
|
||||
// Make sure we're not asking for anything that's not supported
|
||||
properties &= Platform::SystemInfo.processor.properties;
|
||||
|
||||
Con::printf("Math Init:");
|
||||
Con::printf(" Installing Standard C extensions");
|
||||
mInstallLibrary_C();
|
||||
|
||||
Con::printf(" Installing Assembly extensions");
|
||||
mInstallLibrary_ASM();
|
||||
|
||||
if (properties & CPU_PROP_FPU)
|
||||
{
|
||||
Con::printf(" Installing FPU extensions");
|
||||
}
|
||||
|
||||
if (properties & CPU_PROP_MMX)
|
||||
{
|
||||
Con::printf(" Installing MMX extensions");
|
||||
if (properties & CPU_PROP_3DNOW)
|
||||
{
|
||||
Con::printf(" Installing 3DNow extensions");
|
||||
mInstall_AMD_Math();
|
||||
}
|
||||
}
|
||||
|
||||
if (properties & CPU_PROP_SSE)
|
||||
{
|
||||
Con::printf(" Installing SSE extensions");
|
||||
mInstall_Library_SSE();
|
||||
}
|
||||
|
||||
Con::printf(" ");
|
||||
}
|
||||
|
||||
|
||||
109
Engine/source/platformWin32/winMath_ASM.cpp
Normal file
109
Engine/source/platformWin32/winMath_ASM.cpp
Normal 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "math/mMath.h"
|
||||
|
||||
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
|
||||
static S32 m_mulDivS32_ASM(S32 a, S32 b, S32 c)
|
||||
{ // a * b / c
|
||||
S32 r;
|
||||
_asm
|
||||
{
|
||||
mov eax, a
|
||||
imul b
|
||||
idiv c
|
||||
mov r, eax
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static U32 m_mulDivU32_ASM(S32 a, S32 b, U32 c)
|
||||
{ // a * b / c
|
||||
S32 r;
|
||||
_asm
|
||||
{
|
||||
mov eax, a
|
||||
mov edx, 0
|
||||
mul b
|
||||
div c
|
||||
mov r, eax
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static void m_sincos_ASM( F32 angle, F32 *s, F32 *c )
|
||||
{
|
||||
_asm
|
||||
{
|
||||
fld angle
|
||||
fsincos
|
||||
mov eax, c
|
||||
fstp dword ptr [eax]
|
||||
mov eax, s
|
||||
fstp dword ptr [eax]
|
||||
}
|
||||
}
|
||||
|
||||
U32 Platform::getMathControlState()
|
||||
{
|
||||
U16 cw;
|
||||
_asm
|
||||
{
|
||||
fstcw cw
|
||||
}
|
||||
return cw;
|
||||
}
|
||||
|
||||
void Platform::setMathControlState(U32 state)
|
||||
{
|
||||
U16 cw = state;
|
||||
_asm
|
||||
{
|
||||
fldcw cw
|
||||
}
|
||||
}
|
||||
|
||||
void Platform::setMathControlStateKnown()
|
||||
{
|
||||
U16 cw = 0x27F;
|
||||
_asm
|
||||
{
|
||||
fldcw cw
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void mInstallLibrary_ASM()
|
||||
{
|
||||
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
|
||||
m_mulDivS32 = m_mulDivS32_ASM;
|
||||
m_mulDivU32 = m_mulDivU32_ASM;
|
||||
|
||||
m_sincos = m_sincos_ASM;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
74
Engine/source/platformWin32/winMemory.cpp
Normal file
74
Engine/source/platformWin32/winMemory.cpp
Normal 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#include <xmmintrin.h>
|
||||
|
||||
void* dMemcpy(void *dst, const void *src, unsigned size)
|
||||
{
|
||||
return memcpy(dst,src,size);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
void* dMemmove(void *dst, const void *src, unsigned size)
|
||||
{
|
||||
return memmove(dst,src,size);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void* dMemset(void *dst, S32 c, unsigned size)
|
||||
{
|
||||
return memset(dst,c,size);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
S32 dMemcmp(const void *ptr1, const void *ptr2, unsigned len)
|
||||
{
|
||||
return memcmp(ptr1, ptr2, len);
|
||||
}
|
||||
|
||||
#if defined(TORQUE_COMPILER_MINGW)
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
void* dRealMalloc(dsize_t s)
|
||||
{
|
||||
return malloc(s);
|
||||
}
|
||||
|
||||
void dRealFree(void* p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
|
||||
void *dMalloc_aligned(dsize_t in_size, int alignment)
|
||||
{
|
||||
return _mm_malloc(in_size, alignment);
|
||||
}
|
||||
|
||||
void dFree_aligned(void* p)
|
||||
{
|
||||
return _mm_free(p);
|
||||
}
|
||||
70
Engine/source/platformWin32/winProcessControl.cpp
Normal file
70
Engine/source/platformWin32/winProcessControl.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "core/strings/stringFunctions.h"
|
||||
|
||||
void Platform::postQuitMessage(const U32 in_quitVal)
|
||||
{
|
||||
if (!Platform::getWebDeployment())
|
||||
PostQuitMessage(in_quitVal);
|
||||
}
|
||||
|
||||
void Platform::debugBreak()
|
||||
{
|
||||
DebugBreak();
|
||||
}
|
||||
|
||||
void Platform::forceShutdown(S32 returnValue)
|
||||
{
|
||||
// Don't do an ExitProcess here or you'll wreak havoc in a multithreaded
|
||||
// environment.
|
||||
|
||||
exit( returnValue );
|
||||
}
|
||||
|
||||
void Platform::outputDebugString( const char *string, ... )
|
||||
{
|
||||
// Expand string.
|
||||
|
||||
char buffer[ 2048 ];
|
||||
|
||||
va_list args;
|
||||
va_start( args, string );
|
||||
|
||||
dVsprintf( buffer, sizeof( buffer ), string, args );
|
||||
va_end( args );
|
||||
|
||||
// Append a newline to buffer. This is better than calling OutputDebugStringA
|
||||
// twice as in a multi-threaded environment, some other thread may output some
|
||||
// stuff in between the two calls.
|
||||
|
||||
U32 length = strlen( buffer );
|
||||
if( length == ( sizeof( buffer ) - 1 ) )
|
||||
length --;
|
||||
|
||||
buffer[ length ] = '\n';
|
||||
buffer[ length + 1 ] = '\0';
|
||||
|
||||
OutputDebugStringA( buffer );
|
||||
}
|
||||
|
||||
497
Engine/source/platformWin32/winRedbook.cpp
Normal file
497
Engine/source/platformWin32/winRedbook.cpp
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platform/platformRedBook.h"
|
||||
#include "core/strings/unicode.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
|
||||
class Win32RedBookDevice : public RedBookDevice
|
||||
{
|
||||
private:
|
||||
typedef RedBookDevice Parent;
|
||||
|
||||
U32 mDeviceId;
|
||||
|
||||
void setLastError(const char *);
|
||||
void setLastError(U32);
|
||||
|
||||
MIXERCONTROLDETAILS mMixerVolumeDetails;
|
||||
MIXERCONTROLDETAILS_UNSIGNED mMixerVolumeValue;
|
||||
|
||||
union {
|
||||
HMIXEROBJ mVolumeDeviceId;
|
||||
UINT mAuxVolumeDeviceId;
|
||||
};
|
||||
|
||||
U32 mOriginalVolume;
|
||||
bool mVolumeInitialized;
|
||||
|
||||
bool mUsingMixer;
|
||||
|
||||
void openVolume();
|
||||
void closeVolume();
|
||||
|
||||
public:
|
||||
Win32RedBookDevice();
|
||||
~Win32RedBookDevice();
|
||||
|
||||
U32 getDeviceId();
|
||||
|
||||
bool open();
|
||||
bool close();
|
||||
bool play(U32);
|
||||
bool stop();
|
||||
bool getTrackCount(U32 *);
|
||||
bool getVolume(F32 *);
|
||||
bool setVolume(F32);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Win32 specific
|
||||
//------------------------------------------------------------------------------
|
||||
void installRedBookDevices()
|
||||
{
|
||||
U32 bufSize = ::GetLogicalDriveStrings(0,0);
|
||||
|
||||
char * buf = new char[bufSize];
|
||||
|
||||
::GetLogicalDriveStringsA(bufSize, buf);
|
||||
|
||||
char * str = buf;
|
||||
while(*str)
|
||||
{
|
||||
if(::GetDriveTypeA(str) == DRIVE_CDROM)
|
||||
{
|
||||
Win32RedBookDevice * device = new Win32RedBookDevice;
|
||||
device->mDeviceName = new char[dStrlen(str) + 1];
|
||||
dStrcpy(device->mDeviceName, str);
|
||||
|
||||
RedBook::installDevice(device);
|
||||
}
|
||||
str += dStrlen(str) + 1;
|
||||
}
|
||||
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
void handleRedBookCallback(U32 code, U32 deviceId)
|
||||
{
|
||||
if(code != MCI_NOTIFY_SUCCESSFUL)
|
||||
return;
|
||||
|
||||
Win32RedBookDevice * device = dynamic_cast<Win32RedBookDevice*>(RedBook::getCurrentDevice());
|
||||
if(!device)
|
||||
return;
|
||||
|
||||
if(device->getDeviceId() != deviceId)
|
||||
return;
|
||||
|
||||
// only installed callback on play (no callback if play is aborted)
|
||||
RedBook::handleCallback(RedBook::PlayFinished);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: Win32RedBookDevice
|
||||
//------------------------------------------------------------------------------
|
||||
Win32RedBookDevice::Win32RedBookDevice()
|
||||
{
|
||||
mVolumeInitialized = false;
|
||||
}
|
||||
|
||||
Win32RedBookDevice::~Win32RedBookDevice()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
U32 Win32RedBookDevice::getDeviceId()
|
||||
{
|
||||
return(mDeviceId);
|
||||
}
|
||||
|
||||
bool Win32RedBookDevice::open()
|
||||
{
|
||||
if(mAcquired)
|
||||
{
|
||||
setLastError("Device is already open.");
|
||||
return(false);
|
||||
}
|
||||
|
||||
U32 error;
|
||||
|
||||
// open the device
|
||||
MCI_OPEN_PARMS openParms;
|
||||
#ifdef UNICODE
|
||||
openParms.lpstrDeviceType = (LPCWSTR)MCI_DEVTYPE_CD_AUDIO;
|
||||
|
||||
UTF16 buf[512];
|
||||
convertUTF8toUTF16((UTF8 *)mDeviceName, buf, sizeof(buf));
|
||||
openParms.lpstrElementName = buf;
|
||||
#else
|
||||
openParms.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO;
|
||||
openParms.lpstrElementName = mDeviceName;
|
||||
#endif
|
||||
|
||||
error = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms);
|
||||
if(error)
|
||||
{
|
||||
// attempt to open as a shared device
|
||||
error = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID|MCI_OPEN_SHAREABLE, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms);
|
||||
if(error)
|
||||
{
|
||||
setLastError(error);
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
// set time mode to milliseconds
|
||||
MCI_SET_PARMS setParms;
|
||||
setParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
|
||||
|
||||
error = mciSendCommand(openParms.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPMCI_SET_PARMS)&setParms);
|
||||
if(error)
|
||||
{
|
||||
setLastError(error);
|
||||
return(false);
|
||||
}
|
||||
|
||||
//
|
||||
mDeviceId = openParms.wDeviceID;
|
||||
mAcquired = true;
|
||||
|
||||
openVolume();
|
||||
setLastError("");
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool Win32RedBookDevice::close()
|
||||
{
|
||||
if(!mAcquired)
|
||||
{
|
||||
setLastError("Device has not been acquired");
|
||||
return(false);
|
||||
}
|
||||
|
||||
stop();
|
||||
|
||||
U32 error;
|
||||
|
||||
MCI_GENERIC_PARMS closeParms;
|
||||
error = mciSendCommand(mDeviceId, MCI_CLOSE, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&closeParms);
|
||||
if(error)
|
||||
{
|
||||
setLastError(error);
|
||||
return(false);
|
||||
}
|
||||
|
||||
mAcquired = false;
|
||||
closeVolume();
|
||||
setLastError("");
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool Win32RedBookDevice::play(U32 track)
|
||||
{
|
||||
if(!mAcquired)
|
||||
{
|
||||
setLastError("Device has not been acquired");
|
||||
return(false);
|
||||
}
|
||||
|
||||
U32 numTracks;
|
||||
if(!getTrackCount(&numTracks))
|
||||
return(false);
|
||||
|
||||
if(track >= numTracks)
|
||||
{
|
||||
setLastError("Track index is out of range");
|
||||
return(false);
|
||||
}
|
||||
|
||||
MCI_STATUS_PARMS statusParms;
|
||||
|
||||
// get track start time
|
||||
statusParms.dwItem = MCI_STATUS_POSITION;
|
||||
statusParms.dwTrack = track + 1;
|
||||
|
||||
U32 error;
|
||||
error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT,
|
||||
(DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms);
|
||||
|
||||
if(error)
|
||||
{
|
||||
setLastError(error);
|
||||
return(false);
|
||||
}
|
||||
|
||||
MCI_PLAY_PARMS playParms;
|
||||
playParms.dwFrom = statusParms.dwReturn;
|
||||
|
||||
// get track end time
|
||||
statusParms.dwItem = MCI_STATUS_LENGTH;
|
||||
error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT,
|
||||
(DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms);
|
||||
|
||||
if(error)
|
||||
{
|
||||
setLastError(error);
|
||||
return(false);
|
||||
}
|
||||
|
||||
playParms.dwTo = playParms.dwFrom + statusParms.dwReturn;
|
||||
|
||||
// play the track
|
||||
playParms.dwCallback = MAKELONG(getWin32WindowHandle(), 0);
|
||||
error = mciSendCommand(mDeviceId, MCI_PLAY, MCI_FROM|MCI_TO|MCI_NOTIFY,
|
||||
(DWORD_PTR)(LPMCI_PLAY_PARMS)&playParms);
|
||||
|
||||
if(error)
|
||||
{
|
||||
setLastError(error);
|
||||
return(false);
|
||||
}
|
||||
|
||||
setLastError("");
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool Win32RedBookDevice::stop()
|
||||
{
|
||||
if(!mAcquired)
|
||||
{
|
||||
setLastError("Device has not been acquired");
|
||||
return(false);
|
||||
}
|
||||
|
||||
MCI_GENERIC_PARMS genParms;
|
||||
|
||||
U32 error = mciSendCommand(mDeviceId, MCI_STOP, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&genParms);
|
||||
if(error)
|
||||
{
|
||||
setLastError(error);
|
||||
return(false);
|
||||
}
|
||||
|
||||
setLastError("");
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool Win32RedBookDevice::getTrackCount(U32 * numTracks)
|
||||
{
|
||||
if(!mAcquired)
|
||||
{
|
||||
setLastError("Device has not been acquired");
|
||||
return(false);
|
||||
}
|
||||
|
||||
MCI_STATUS_PARMS statusParms;
|
||||
|
||||
statusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
|
||||
U32 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms);
|
||||
if(error)
|
||||
{
|
||||
setLastError(error);
|
||||
return(false);
|
||||
}
|
||||
|
||||
*numTracks = statusParms.dwReturn;
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool Win32RedBookDevice::getVolume(F32 * volume)
|
||||
{
|
||||
if(!mAcquired)
|
||||
{
|
||||
setLastError("Device has not been acquired");
|
||||
return(false);
|
||||
}
|
||||
|
||||
if(!mVolumeInitialized)
|
||||
{
|
||||
setLastError("Volume failed to initialize");
|
||||
return(false);
|
||||
}
|
||||
|
||||
U32 vol = 0;
|
||||
if(mUsingMixer)
|
||||
{
|
||||
mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE);
|
||||
vol = mMixerVolumeValue.dwValue;
|
||||
}
|
||||
else
|
||||
auxGetVolume(mAuxVolumeDeviceId, (unsigned long *)&vol);
|
||||
|
||||
vol &= 0xffff;
|
||||
*volume = F32(vol) / 65535.f;
|
||||
|
||||
setLastError("");
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool Win32RedBookDevice::setVolume(F32 volume)
|
||||
{
|
||||
if(!mAcquired)
|
||||
{
|
||||
setLastError("Device has not been acquired");
|
||||
return(false);
|
||||
}
|
||||
|
||||
if(!mVolumeInitialized)
|
||||
{
|
||||
setLastError("Volume failed to initialize");
|
||||
return(false);
|
||||
}
|
||||
|
||||
// move into a U32 - left/right U16 volumes
|
||||
U32 vol = U32(volume * 65536.f);
|
||||
if(vol > 0xffff)
|
||||
vol = 0xffff;
|
||||
|
||||
if(mUsingMixer)
|
||||
{
|
||||
mMixerVolumeValue.dwValue = vol;
|
||||
mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
vol |= vol << 16;
|
||||
auxSetVolume(mAuxVolumeDeviceId, vol);
|
||||
}
|
||||
|
||||
setLastError("");
|
||||
return(true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void Win32RedBookDevice::openVolume()
|
||||
{
|
||||
setLastError("");
|
||||
|
||||
// first attempt to get the volume control through the mixer API
|
||||
S32 i;
|
||||
for(i = mixerGetNumDevs() - 1; i >= 0; i--)
|
||||
{
|
||||
// open the mixer
|
||||
if(mixerOpen((HMIXER*)&mVolumeDeviceId, i, 0, 0, 0) == MMSYSERR_NOERROR)
|
||||
{
|
||||
MIXERLINE lineInfo;
|
||||
memset(&lineInfo, 0, sizeof(lineInfo));
|
||||
lineInfo.cbStruct = sizeof(lineInfo);
|
||||
lineInfo.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
|
||||
|
||||
// get the cdaudio line
|
||||
if(mixerGetLineInfo(mVolumeDeviceId, &lineInfo, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR)
|
||||
{
|
||||
MIXERLINECONTROLS lineControls;
|
||||
MIXERCONTROL volumeControl;
|
||||
|
||||
memset(&lineControls, 0, sizeof(lineControls));
|
||||
lineControls.cbStruct = sizeof(lineControls);
|
||||
lineControls.dwLineID = lineInfo.dwLineID;
|
||||
lineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
|
||||
lineControls.cControls = 1;
|
||||
lineControls.cbmxctrl = sizeof(volumeControl);
|
||||
lineControls.pamxctrl = &volumeControl;
|
||||
|
||||
memset(&volumeControl, 0, sizeof(volumeControl));
|
||||
volumeControl.cbStruct = sizeof(volumeControl);
|
||||
|
||||
// get the volume control
|
||||
if(mixerGetLineControls(mVolumeDeviceId, &lineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR)
|
||||
{
|
||||
memset(&mMixerVolumeDetails, 0, sizeof(mMixerVolumeDetails));
|
||||
mMixerVolumeDetails.cbStruct = sizeof(mMixerVolumeDetails);
|
||||
mMixerVolumeDetails.dwControlID = volumeControl.dwControlID;
|
||||
mMixerVolumeDetails.cChannels = 1;
|
||||
mMixerVolumeDetails.cbDetails = sizeof(mMixerVolumeValue);
|
||||
mMixerVolumeDetails.paDetails = &mMixerVolumeValue;
|
||||
|
||||
memset(&mMixerVolumeValue, 0, sizeof(mMixerVolumeValue));
|
||||
|
||||
// query the current value
|
||||
if(mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR)
|
||||
{
|
||||
mUsingMixer = true;
|
||||
mVolumeInitialized = true;
|
||||
mOriginalVolume = mMixerVolumeValue.dwValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixerClose((HMIXER)mVolumeDeviceId);
|
||||
}
|
||||
|
||||
// try aux
|
||||
for(i = auxGetNumDevs() - 1; i >= 0; i--)
|
||||
{
|
||||
AUXCAPS caps;
|
||||
auxGetDevCaps(i, &caps, sizeof(AUXCAPS));
|
||||
if((caps.wTechnology == AUXCAPS_CDAUDIO) && (caps.dwSupport & AUXCAPS_VOLUME))
|
||||
{
|
||||
mAuxVolumeDeviceId = i;
|
||||
mVolumeInitialized = true;
|
||||
mUsingMixer = false;
|
||||
auxGetVolume(i, (unsigned long *)&mOriginalVolume);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setLastError("Volume failed to initialize");
|
||||
}
|
||||
|
||||
void Win32RedBookDevice::closeVolume()
|
||||
{
|
||||
setLastError("");
|
||||
if(!mVolumeInitialized)
|
||||
return;
|
||||
|
||||
if(mUsingMixer)
|
||||
{
|
||||
mMixerVolumeValue.dwValue = mOriginalVolume;
|
||||
mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE);
|
||||
mixerClose((HMIXER)mVolumeDeviceId);
|
||||
}
|
||||
else
|
||||
auxSetVolume(mAuxVolumeDeviceId, mOriginalVolume);
|
||||
|
||||
mVolumeInitialized = false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void Win32RedBookDevice::setLastError(const char * error)
|
||||
{
|
||||
RedBook::setLastError(error);
|
||||
}
|
||||
|
||||
void Win32RedBookDevice::setLastError(U32 errorId)
|
||||
{
|
||||
char buffer[256];
|
||||
if(!mciGetErrorStringA(errorId, buffer, sizeof(buffer) - 1))
|
||||
setLastError("Failed to get MCI error string!");
|
||||
else
|
||||
setLastError(buffer);
|
||||
}
|
||||
|
||||
75
Engine/source/platformWin32/winSemaphore.cpp
Normal file
75
Engine/source/platformWin32/winSemaphore.cpp
Normal 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#include "platform/threads/semaphore.h"
|
||||
|
||||
class PlatformSemaphore
|
||||
{
|
||||
public:
|
||||
HANDLE *semaphore;
|
||||
|
||||
PlatformSemaphore(S32 initialCount)
|
||||
{
|
||||
semaphore = new HANDLE;
|
||||
*semaphore = CreateSemaphore(0, initialCount, S32_MAX, 0);
|
||||
}
|
||||
|
||||
~PlatformSemaphore()
|
||||
{
|
||||
CloseHandle(*(HANDLE*)(semaphore));
|
||||
delete semaphore;
|
||||
semaphore = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
Semaphore::Semaphore(S32 initialCount)
|
||||
{
|
||||
mData = new PlatformSemaphore(initialCount);
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
AssertFatal(mData && mData->semaphore, "Semaphore::destroySemaphore: invalid semaphore");
|
||||
delete mData;
|
||||
}
|
||||
|
||||
bool Semaphore::acquire(bool block, S32 timeoutMS)
|
||||
{
|
||||
AssertFatal(mData && mData->semaphore, "Semaphore::acquireSemaphore: invalid semaphore");
|
||||
if(block)
|
||||
{
|
||||
WaitForSingleObject(*(HANDLE*)(mData->semaphore), timeoutMS != -1 ? timeoutMS : INFINITE );
|
||||
return(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD result = WaitForSingleObject(*(HANDLE*)(mData->semaphore), 0);
|
||||
return(result == WAIT_OBJECT_0);
|
||||
}
|
||||
}
|
||||
|
||||
void Semaphore::release()
|
||||
{
|
||||
AssertFatal(mData && mData->semaphore, "Semaphore::releaseSemaphore: invalid semaphore");
|
||||
ReleaseSemaphore(*(HANDLE*)(mData->semaphore), 1, 0);
|
||||
}
|
||||
90
Engine/source/platformWin32/winTLS.cpp
Normal file
90
Engine/source/platformWin32/winTLS.cpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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/platformTLS.h"
|
||||
#include "platformWin32/platformWin32.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
#define TORQUE_ALLOC_STORAGE(member, cls, data) \
|
||||
AssertFatal(sizeof(cls) <= sizeof(data), avar("Error, storage for %s must be %d bytes.", #cls, sizeof(cls))); \
|
||||
member = (cls *) data; \
|
||||
constructInPlace(member)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct PlatformThreadStorage
|
||||
{
|
||||
DWORD mTlsIndex;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ThreadStorage::ThreadStorage()
|
||||
{
|
||||
TORQUE_ALLOC_STORAGE(mThreadStorage, PlatformThreadStorage, mStorage);
|
||||
mThreadStorage->mTlsIndex = TlsAlloc();
|
||||
}
|
||||
|
||||
ThreadStorage::~ThreadStorage()
|
||||
{
|
||||
TlsFree(mThreadStorage->mTlsIndex);
|
||||
destructInPlace(mThreadStorage);
|
||||
}
|
||||
|
||||
void *ThreadStorage::get()
|
||||
{
|
||||
return TlsGetValue(mThreadStorage->mTlsIndex);
|
||||
}
|
||||
|
||||
void ThreadStorage::set(void *value)
|
||||
{
|
||||
TlsSetValue(mThreadStorage->mTlsIndex, value);
|
||||
}
|
||||
|
||||
/* POSIX IMPLEMENTATION LOOKS LIKE THIS:
|
||||
|
||||
class PlatformThreadStorage
|
||||
{
|
||||
pthread_key_t mThreadKey;
|
||||
};
|
||||
|
||||
ThreadStorage::ThreadStorage()
|
||||
{
|
||||
TORQUE_ALLOC_STORAGE(mThreadStorage, PlatformThreadStorage, mStorage);
|
||||
pthread_key_create(&mThreadStorage->mThreadKey, NULL);
|
||||
}
|
||||
|
||||
ThreadStorage::~ThreadStorage()
|
||||
{
|
||||
pthread_key_delete(mThreadStorage->mThreadKey);
|
||||
}
|
||||
|
||||
void *ThreadStorage::get()
|
||||
{
|
||||
return pthread_getspecific(mThreadStorage->mThreadKey);
|
||||
}
|
||||
|
||||
void ThreadStorage::set(void *value)
|
||||
{
|
||||
pthread_setspecific(mThreadStorage->mThreadKey, value);
|
||||
}
|
||||
*/
|
||||
168
Engine/source/platformWin32/winTime.cpp
Normal file
168
Engine/source/platformWin32/winTime.cpp
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platformWin32/platformWin32.h"
|
||||
|
||||
#include "time.h"
|
||||
|
||||
void Platform::sleep(U32 ms)
|
||||
{
|
||||
Sleep(ms);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void Platform::getLocalTime(LocalTime <)
|
||||
{
|
||||
struct tm *systime;
|
||||
time_t long_time;
|
||||
|
||||
time( &long_time ); // Get time as long integer.
|
||||
systime = localtime( &long_time ); // Convert to local time.
|
||||
|
||||
lt.sec = systime->tm_sec;
|
||||
lt.min = systime->tm_min;
|
||||
lt.hour = systime->tm_hour;
|
||||
lt.month = systime->tm_mon;
|
||||
lt.monthday = systime->tm_mday;
|
||||
lt.weekday = systime->tm_wday;
|
||||
lt.year = systime->tm_year;
|
||||
lt.yearday = systime->tm_yday;
|
||||
lt.isdst = systime->tm_isdst;
|
||||
}
|
||||
|
||||
String Platform::localTimeToString( const LocalTime < )
|
||||
{
|
||||
// Converting a LocalTime to SYSTEMTIME
|
||||
// requires a few annoying adjustments.
|
||||
SYSTEMTIME st;
|
||||
st.wMilliseconds = 0;
|
||||
st.wSecond = lt.sec;
|
||||
st.wMinute = lt.min;
|
||||
st.wHour = lt.hour;
|
||||
st.wDay = lt.monthday;
|
||||
st.wDayOfWeek = lt.weekday;
|
||||
st.wMonth = lt.month + 1;
|
||||
st.wYear = lt.year + 1900;
|
||||
|
||||
TCHAR buffer[1024] = {0};
|
||||
|
||||
int result = 0;
|
||||
|
||||
String outStr;
|
||||
|
||||
// Note: The 'Ex' version of GetDateFormat and GetTimeFormat are preferred
|
||||
// and have better support for supplemental locales but are not supported
|
||||
// for version of windows prior to Vista.
|
||||
//
|
||||
// Would be nice if Torque was more aware of the OS version and
|
||||
// take full advantage of it.
|
||||
|
||||
result = GetDateFormat( LOCALE_USER_DEFAULT,
|
||||
DATE_SHORTDATE,
|
||||
&st,
|
||||
NULL,
|
||||
(LPTSTR)buffer,
|
||||
1024 );
|
||||
|
||||
// Also would be nice to have a standard system for torque to
|
||||
// retrieve and display windows level errors using GetLastError and
|
||||
// FormatMessage...
|
||||
AssertWarn( result != 0, "Platform::getLocalTime" );
|
||||
|
||||
outStr += buffer;
|
||||
outStr += "\t";
|
||||
|
||||
result = GetTimeFormat( LOCALE_USER_DEFAULT,
|
||||
0,
|
||||
&st,
|
||||
NULL,
|
||||
(LPTSTR)buffer,
|
||||
1024 );
|
||||
|
||||
AssertWarn( result != 0, "Platform::localTimeToString, error occured!" );
|
||||
|
||||
outStr += buffer;
|
||||
|
||||
return outStr;
|
||||
}
|
||||
|
||||
U32 Platform::getTime()
|
||||
{
|
||||
time_t long_time;
|
||||
time( &long_time );
|
||||
return long_time;
|
||||
}
|
||||
|
||||
void Platform::fileToLocalTime(const FileTime & ft, LocalTime * lt)
|
||||
{
|
||||
if(!lt)
|
||||
return;
|
||||
|
||||
dMemset(lt, 0, sizeof(LocalTime));
|
||||
|
||||
FILETIME winFileTime;
|
||||
winFileTime.dwLowDateTime = ft.v1;
|
||||
winFileTime.dwHighDateTime = ft.v2;
|
||||
|
||||
SYSTEMTIME winSystemTime;
|
||||
|
||||
// convert the filetime to local time
|
||||
FILETIME convertedFileTime;
|
||||
if(::FileTimeToLocalFileTime(&winFileTime, &convertedFileTime))
|
||||
{
|
||||
// get the time into system time struct
|
||||
if(::FileTimeToSystemTime((const FILETIME *)&convertedFileTime, &winSystemTime))
|
||||
{
|
||||
SYSTEMTIME * time = &winSystemTime;
|
||||
|
||||
// fill it in...
|
||||
lt->sec = time->wSecond;
|
||||
lt->min = time->wMinute;
|
||||
lt->hour = time->wHour;
|
||||
lt->month = time->wMonth;
|
||||
lt->monthday = time->wDay;
|
||||
lt->weekday = time->wDayOfWeek;
|
||||
lt->year = (time->wYear < 1900) ? 1900 : (time->wYear - 1900);
|
||||
|
||||
// not calculated
|
||||
lt->yearday = 0;
|
||||
lt->isdst = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
U32 Platform::getRealMilliseconds()
|
||||
{
|
||||
return GetTickCount();
|
||||
}
|
||||
|
||||
U32 Platform::getVirtualMilliseconds()
|
||||
{
|
||||
return winState.currentTime;
|
||||
}
|
||||
|
||||
void Platform::advanceTime(U32 delta)
|
||||
{
|
||||
winState.currentTime += delta;
|
||||
}
|
||||
|
||||
88
Engine/source/platformWin32/winTimer.cpp
Normal file
88
Engine/source/platformWin32/winTimer.cpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Grab the win32 headers so we can access QPC
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include "platform/platformTimer.h"
|
||||
#include "math/mMath.h"
|
||||
|
||||
class Win32Timer : public PlatformTimer
|
||||
{
|
||||
private:
|
||||
U32 mTickCountCurrent;
|
||||
U32 mTickCountNext;
|
||||
S64 mPerfCountCurrent;
|
||||
S64 mPerfCountNext;
|
||||
S64 mFrequency;
|
||||
F64 mPerfCountRemainderCurrent;
|
||||
F64 mPerfCountRemainderNext;
|
||||
bool mUsingPerfCounter;
|
||||
public:
|
||||
|
||||
Win32Timer()
|
||||
{
|
||||
mPerfCountRemainderCurrent = 0.0f;
|
||||
|
||||
// Attempt to use QPC for high res timing, otherwise fallback to GTC.
|
||||
mUsingPerfCounter = QueryPerformanceFrequency((LARGE_INTEGER *) &mFrequency);
|
||||
if(mUsingPerfCounter)
|
||||
mUsingPerfCounter = QueryPerformanceCounter((LARGE_INTEGER *) &mPerfCountCurrent);
|
||||
if(!mUsingPerfCounter)
|
||||
mTickCountCurrent = GetTickCount();
|
||||
}
|
||||
|
||||
const S32 getElapsedMs()
|
||||
{
|
||||
if(mUsingPerfCounter)
|
||||
{
|
||||
// Use QPC, update remainders so we don't leak time, and return the elapsed time.
|
||||
QueryPerformanceCounter( (LARGE_INTEGER *) &mPerfCountNext);
|
||||
F64 elapsedF64 = (1000.0 * F64(mPerfCountNext - mPerfCountCurrent) / F64(mFrequency));
|
||||
elapsedF64 += mPerfCountRemainderCurrent;
|
||||
U32 elapsed = (U32)mFloor(elapsedF64);
|
||||
mPerfCountRemainderNext = elapsedF64 - F64(elapsed);
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do something naive with GTC.
|
||||
mTickCountNext = GetTickCount();
|
||||
return mTickCountNext - mTickCountCurrent;
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
// Do some simple copying to reset the timer to 0.
|
||||
mTickCountCurrent = mTickCountNext;
|
||||
mPerfCountCurrent = mPerfCountNext;
|
||||
mPerfCountRemainderCurrent = mPerfCountRemainderNext;
|
||||
}
|
||||
};
|
||||
|
||||
PlatformTimer *PlatformTimer::create()
|
||||
{
|
||||
return new Win32Timer();
|
||||
}
|
||||
225
Engine/source/platformWin32/winUser.cpp
Normal file
225
Engine/source/platformWin32/winUser.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "console/console.h"
|
||||
#include "core/stringTable.h"
|
||||
#include "core/strings/unicode.h"
|
||||
|
||||
typedef long SHANDLE_PTR;
|
||||
#include <shlobj.h>
|
||||
#include <windows.h>
|
||||
#include <lmcons.h>
|
||||
|
||||
#define CSIDL_PROFILE 0x0028
|
||||
|
||||
const char *Platform::getUserDataDirectory()
|
||||
{
|
||||
TCHAR szBuffer[ MAX_PATH + 1 ];
|
||||
|
||||
if(! SHGetSpecialFolderPath( NULL, szBuffer, CSIDL_APPDATA, true ) )
|
||||
return "";
|
||||
|
||||
TCHAR *ptr = szBuffer;
|
||||
while(*ptr)
|
||||
{
|
||||
if(*ptr == '\\')
|
||||
*ptr = '/';
|
||||
++ptr;
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
char path[ MAX_PATH * 3 + 1 ];
|
||||
convertUTF16toUTF8( szBuffer, path, sizeof( path ) );
|
||||
#else
|
||||
char* path = szBuffer;
|
||||
#endif
|
||||
|
||||
|
||||
return StringTable->insert( path );
|
||||
}
|
||||
|
||||
const char *Platform::getUserHomeDirectory()
|
||||
{
|
||||
TCHAR szBuffer[ MAX_PATH + 1 ];
|
||||
if(! SHGetSpecialFolderPath( NULL, szBuffer, CSIDL_PERSONAL, false ) )
|
||||
if(! SHGetSpecialFolderPath( NULL, szBuffer, CSIDL_COMMON_DOCUMENTS, false ) )
|
||||
return "";
|
||||
|
||||
TCHAR *ptr = szBuffer;
|
||||
while(*ptr)
|
||||
{
|
||||
if(*ptr == '\\')
|
||||
*ptr = '/';
|
||||
++ptr;
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
char path[ MAX_PATH * 3 + 1 ];
|
||||
convertUTF16toUTF8( szBuffer, path, sizeof( path ) );
|
||||
#else
|
||||
char* path = szBuffer;
|
||||
#endif
|
||||
|
||||
return StringTable->insert( path );
|
||||
}
|
||||
|
||||
|
||||
bool Platform::getUserIsAdministrator()
|
||||
{
|
||||
BOOL fReturn = FALSE;
|
||||
DWORD dwStatus;
|
||||
DWORD dwAccessMask;
|
||||
DWORD dwAccessDesired;
|
||||
DWORD dwACLSize;
|
||||
DWORD dwStructureSize = sizeof(PRIVILEGE_SET);
|
||||
PACL pACL = NULL;
|
||||
PSID psidAdmin = NULL;
|
||||
|
||||
HANDLE hToken = NULL;
|
||||
HANDLE hImpersonationToken = NULL;
|
||||
|
||||
PRIVILEGE_SET ps;
|
||||
GENERIC_MAPPING GenericMapping;
|
||||
|
||||
PSECURITY_DESCRIPTOR psdAdmin = NULL;
|
||||
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
|
||||
|
||||
|
||||
/*
|
||||
Determine if the current thread is running as a user that is a member of
|
||||
the local admins group. To do this, create a security descriptor that
|
||||
has a DACL which has an ACE that allows only local aministrators access.
|
||||
Then, call AccessCheck with the current thread's token and the security
|
||||
descriptor. It will say whether the user could access an object if it
|
||||
had that security descriptor. Note: you do not need to actually create
|
||||
the object. Just checking access against the security descriptor alone
|
||||
will be sufficient.
|
||||
*/
|
||||
const DWORD ACCESS_READ = 1;
|
||||
const DWORD ACCESS_WRITE = 2;
|
||||
|
||||
|
||||
__try
|
||||
{
|
||||
|
||||
/*
|
||||
AccessCheck() requires an impersonation token. We first get a primary
|
||||
token and then create a duplicate impersonation token. The
|
||||
impersonation token is not actually assigned to the thread, but is
|
||||
used in the call to AccessCheck. Thus, this function itself never
|
||||
impersonates, but does use the identity of the thread. If the thread
|
||||
was impersonating already, this function uses that impersonation context.
|
||||
*/
|
||||
if (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY, TRUE, &hToken))
|
||||
{
|
||||
if (GetLastError() != ERROR_NO_TOKEN)
|
||||
__leave;
|
||||
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
|
||||
__leave;
|
||||
}
|
||||
|
||||
if (!DuplicateToken (hToken, SecurityImpersonation, &hImpersonationToken))
|
||||
__leave;
|
||||
|
||||
|
||||
/*
|
||||
Create the binary representation of the well-known SID that
|
||||
represents the local administrators group. Then create the security
|
||||
descriptor and DACL with an ACE that allows only local admins access.
|
||||
After that, perform the access check. This will determine whether
|
||||
the current user is a local admin.
|
||||
*/
|
||||
if (!AllocateAndInitializeSid(&SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
||||
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmin))
|
||||
__leave;
|
||||
|
||||
psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
|
||||
if (psdAdmin == NULL)
|
||||
__leave;
|
||||
|
||||
if (!InitializeSecurityDescriptor(psdAdmin, SECURITY_DESCRIPTOR_REVISION))
|
||||
__leave;
|
||||
|
||||
// Compute size needed for the ACL.
|
||||
dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidAdmin) - sizeof(DWORD);
|
||||
|
||||
pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
|
||||
if (pACL == NULL)
|
||||
__leave;
|
||||
|
||||
if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
|
||||
__leave;
|
||||
|
||||
dwAccessMask= ACCESS_READ | ACCESS_WRITE;
|
||||
|
||||
if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask, psidAdmin))
|
||||
__leave;
|
||||
|
||||
if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE))
|
||||
__leave;
|
||||
|
||||
/*
|
||||
AccessCheck validates a security descriptor somewhat; set the group
|
||||
and owner so that enough of the security descriptor is filled out to
|
||||
make AccessCheck happy.
|
||||
*/
|
||||
SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE);
|
||||
SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE);
|
||||
|
||||
if (!IsValidSecurityDescriptor(psdAdmin))
|
||||
__leave;
|
||||
|
||||
dwAccessDesired = ACCESS_READ;
|
||||
|
||||
/*
|
||||
Initialize GenericMapping structure even though you
|
||||
do not use generic rights.
|
||||
*/
|
||||
GenericMapping.GenericRead = ACCESS_READ;
|
||||
GenericMapping.GenericWrite = ACCESS_WRITE;
|
||||
GenericMapping.GenericExecute = 0;
|
||||
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
|
||||
|
||||
if (!AccessCheck(psdAdmin, hImpersonationToken, dwAccessDesired,
|
||||
&GenericMapping, &ps, &dwStructureSize, &dwStatus,
|
||||
&fReturn))
|
||||
{
|
||||
fReturn = FALSE;
|
||||
__leave;
|
||||
}
|
||||
}
|
||||
__finally
|
||||
{
|
||||
|
||||
// Clean up.
|
||||
if (pACL) LocalFree(pACL);
|
||||
if (psdAdmin) LocalFree(psdAdmin);
|
||||
if (psidAdmin) FreeSid(psidAdmin);
|
||||
if (hImpersonationToken) CloseHandle (hImpersonationToken);
|
||||
if (hToken) CloseHandle (hToken);
|
||||
}
|
||||
|
||||
return fReturn;
|
||||
|
||||
}
|
||||
111
Engine/source/platformWin32/winVFS.cpp
Normal file
111
Engine/source/platformWin32/winVFS.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platform/platformVFS.h"
|
||||
#include "console/console.h"
|
||||
|
||||
#include "core/stream/memstream.h"
|
||||
#include "core/util/zip/zipArchive.h"
|
||||
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
#include "VFSRes.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct Win32VFSState
|
||||
{
|
||||
S32 mRefCount;
|
||||
|
||||
HGLOBAL mResData;
|
||||
|
||||
MemStream *mZipStream;
|
||||
Zip::ZipArchive *mZip;
|
||||
|
||||
Win32VFSState() : mResData(NULL), mZip(NULL), mRefCount(0), mZipStream(NULL)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static Win32VFSState gVFSState;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Zip::ZipArchive *openEmbeddedVFSArchive()
|
||||
{
|
||||
if(gVFSState.mZip)
|
||||
{
|
||||
++gVFSState.mRefCount;
|
||||
return gVFSState.mZip;
|
||||
}
|
||||
|
||||
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_ZIPFILE), dT("RT_RCDATA"));
|
||||
|
||||
if(hRsrc == NULL)
|
||||
return NULL;
|
||||
|
||||
if((gVFSState.mResData = LoadResource(NULL, hRsrc)) == NULL)
|
||||
return NULL;
|
||||
|
||||
void * mem = LockResource(gVFSState.mResData);
|
||||
if(mem != NULL)
|
||||
{
|
||||
U32 size = SizeofResource(NULL, hRsrc);
|
||||
gVFSState.mZipStream = new MemStream(size, mem, true, false);
|
||||
gVFSState.mZip = new Zip::ZipArchive;
|
||||
|
||||
if(gVFSState.mZip->openArchive(gVFSState.mZipStream))
|
||||
{
|
||||
++gVFSState.mRefCount;
|
||||
return gVFSState.mZip;
|
||||
}
|
||||
|
||||
SAFE_DELETE(gVFSState.mZip);
|
||||
SAFE_DELETE(gVFSState.mZipStream);
|
||||
}
|
||||
|
||||
FreeResource(gVFSState.mResData);
|
||||
gVFSState.mResData = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void closeEmbeddedVFSArchive()
|
||||
{
|
||||
if(gVFSState.mRefCount == 0)
|
||||
return;
|
||||
|
||||
--gVFSState.mRefCount;
|
||||
|
||||
if(gVFSState.mRefCount < 1)
|
||||
{
|
||||
SAFE_DELETE(gVFSState.mZip);
|
||||
SAFE_DELETE(gVFSState.mZipStream);
|
||||
|
||||
if(gVFSState.mResData)
|
||||
{
|
||||
FreeResource(gVFSState.mResData);
|
||||
gVFSState.mResData = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
786
Engine/source/platformWin32/winVolume.cpp
Normal file
786
Engine/source/platformWin32/winVolume.cpp
Normal file
|
|
@ -0,0 +1,786 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "core/crc.h"
|
||||
#include "core/frameAllocator.h"
|
||||
#include "core/util/str.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "core/strings/unicode.h"
|
||||
|
||||
#include "platform/platformVolume.h"
|
||||
|
||||
#include "platformWin32/winVolume.h"
|
||||
|
||||
#include "console/console.h"
|
||||
|
||||
|
||||
#ifndef NGROUPS_UMAX
|
||||
#define NGROUPS_UMAX 32
|
||||
#endif
|
||||
|
||||
namespace Torque
|
||||
{
|
||||
namespace Win32
|
||||
{
|
||||
|
||||
// If the file is a Directory, Offline, System or Temporary then FALSE
|
||||
#define S_ISREG(Flags) \
|
||||
!((Flags) & \
|
||||
(FILE_ATTRIBUTE_DIRECTORY | \
|
||||
FILE_ATTRIBUTE_OFFLINE | \
|
||||
FILE_ATTRIBUTE_SYSTEM | \
|
||||
FILE_ATTRIBUTE_TEMPORARY))
|
||||
|
||||
#define S_ISDIR(Flags) \
|
||||
((Flags) & FILE_ATTRIBUTE_DIRECTORY)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class Win32FileSystemChangeNotifier : public FileSystemChangeNotifier
|
||||
{
|
||||
public:
|
||||
Win32FileSystemChangeNotifier( FileSystem *fs )
|
||||
: FileSystemChangeNotifier( fs )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mHandleList );
|
||||
VECTOR_SET_ASSOCIATION( mDirs );
|
||||
}
|
||||
|
||||
// for use in the thread itself
|
||||
U32 getNumHandles() const { return mHandleList.size(); }
|
||||
HANDLE *getHANDLES() { return mHandleList.address(); }
|
||||
|
||||
private:
|
||||
virtual void internalProcessOnce();
|
||||
|
||||
virtual bool internalAddNotification( const Path &dir );
|
||||
virtual bool internalRemoveNotification( const Path &dir );
|
||||
|
||||
Vector<Path> mDirs;
|
||||
Vector<HANDLE> mHandleList;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static String _BuildFileName(const String& prefix,const Path& path)
|
||||
{
|
||||
// Need to join the path (minus the root) with our
|
||||
// internal path name.
|
||||
String file = prefix;
|
||||
file = Path::Join(file, '/', path.getPath());
|
||||
file = Path::Join(file, '/', path.getFileName());
|
||||
file = Path::Join(file, '.', path.getExtension());
|
||||
return file;
|
||||
}
|
||||
|
||||
/*
|
||||
static bool _IsFile(const String& file)
|
||||
{
|
||||
// Get file info
|
||||
WIN32_FIND_DATA info;
|
||||
HANDLE handle = ::FindFirstFile(PathToOS(file).utf16(), &info);
|
||||
::FindClose(handle);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
return S_ISREG(info.dwFileAttributes);
|
||||
}
|
||||
*/
|
||||
|
||||
static bool _IsDirectory(const String& file)
|
||||
{
|
||||
// Get file info
|
||||
WIN32_FIND_DATAW info;
|
||||
HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);
|
||||
::FindClose(handle);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
return S_ISDIR(info.dwFileAttributes);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void _CopyStatAttributes(const WIN32_FIND_DATAW& info, FileNode::Attributes* attr)
|
||||
{
|
||||
// Fill in the return struct.
|
||||
attr->flags = 0;
|
||||
if (S_ISDIR(info.dwFileAttributes))
|
||||
attr->flags |= FileNode::Directory;
|
||||
if (S_ISREG(info.dwFileAttributes))
|
||||
attr->flags |= FileNode::File;
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
attr->flags |= FileNode::ReadOnly;
|
||||
|
||||
attr->size = info.nFileSizeLow;
|
||||
attr->mtime = Win32FileTimeToTime(
|
||||
info.ftLastWriteTime.dwLowDateTime,
|
||||
info.ftLastWriteTime.dwHighDateTime);
|
||||
|
||||
attr->atime = Win32FileTimeToTime(
|
||||
info.ftLastAccessTime.dwLowDateTime,
|
||||
info.ftLastAccessTime.dwHighDateTime);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool Win32FileSystemChangeNotifier::internalAddNotification( const Path &dir )
|
||||
{
|
||||
for ( U32 i = 0; i < mDirs.size(); ++i )
|
||||
{
|
||||
if ( mDirs[i] == dir )
|
||||
return false;
|
||||
}
|
||||
|
||||
Path fullFSPath = mFS->mapTo( dir );
|
||||
String osPath = PathToOS( fullFSPath );
|
||||
|
||||
// Con::printf( "[Win32FileSystemChangeNotifier::internalAddNotification] : [%s]", osPath.c_str() );
|
||||
|
||||
HANDLE changeHandle = ::FindFirstChangeNotificationW(
|
||||
osPath.utf16(), // directory to watch
|
||||
FALSE, // do not watch subtree
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES); // watch file write changes
|
||||
|
||||
if (changeHandle == INVALID_HANDLE_VALUE || changeHandle == NULL)
|
||||
{
|
||||
Con::errorf("[Win32FileSystemChangeNotifier::internalAddNotification] : failed on [%s] [%d]", osPath.c_str(), GetLastError());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
mDirs.push_back( dir );
|
||||
mHandleList.push_back( changeHandle );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Win32FileSystemChangeNotifier::internalRemoveNotification( const Path &dir )
|
||||
{
|
||||
for ( U32 i = 0; i < mDirs.size(); ++i )
|
||||
{
|
||||
if ( mDirs[i] != dir )
|
||||
continue;
|
||||
|
||||
::FindCloseChangeNotification( mHandleList[i] );
|
||||
mDirs.erase( i );
|
||||
mHandleList.erase( i );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Win32FileSystemChangeNotifier::internalProcessOnce()
|
||||
{
|
||||
// WaitForMultipleObjects has a limit of MAXIMUM_WAIT_OBJECTS (64 at
|
||||
// the moment), so we have to loop till we've handled the entire set.
|
||||
|
||||
for ( U32 i=0; i < mHandleList.size(); i += MAXIMUM_WAIT_OBJECTS )
|
||||
{
|
||||
U32 numHandles = getMin( (U32)MAXIMUM_WAIT_OBJECTS, (U32)mHandleList.size() - i );
|
||||
|
||||
DWORD dwWaitStatus = WaitForMultipleObjects( numHandles, mHandleList.address()+i, FALSE, 0);
|
||||
if ( dwWaitStatus == WAIT_FAILED || dwWaitStatus == WAIT_TIMEOUT )
|
||||
continue;
|
||||
|
||||
if ( dwWaitStatus >= WAIT_OBJECT_0 && dwWaitStatus <= (WAIT_OBJECT_0 + numHandles - 1))
|
||||
{
|
||||
U32 index = i + dwWaitStatus;
|
||||
|
||||
// reset our notification
|
||||
// NOTE: we do this before letting the volume system check mod times so we don't miss any.
|
||||
// It is going to loop over the files and check their mod time vs. the saved time.
|
||||
// This may result in extra calls to internalNotifyDirChanged(), but it will simpley check mod times again.
|
||||
::FindNextChangeNotification( mHandleList[index] );
|
||||
|
||||
internalNotifyDirChanged( mDirs[index] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Win32FileSystem::Win32FileSystem(String volume)
|
||||
{
|
||||
mVolume = volume;
|
||||
mChangeNotifier = new Win32FileSystemChangeNotifier( this );
|
||||
}
|
||||
|
||||
Win32FileSystem::~Win32FileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
FileNodeRef Win32FileSystem::resolve(const Path& path)
|
||||
{
|
||||
String file = _BuildFileName(mVolume,path);
|
||||
|
||||
WIN32_FIND_DATAW info;
|
||||
HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);
|
||||
::FindClose(handle);
|
||||
if (handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (S_ISREG(info.dwFileAttributes))
|
||||
return new Win32File(path,file);
|
||||
if (S_ISDIR(info.dwFileAttributes))
|
||||
return new Win32Directory(path,file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FileNodeRef Win32FileSystem::create(const Path& path, FileNode::Mode mode)
|
||||
{
|
||||
// The file will be created on disk when it's opened.
|
||||
if (mode & FileNode::File)
|
||||
return new Win32File(path,_BuildFileName(mVolume,path));
|
||||
|
||||
// Create with default permissions.
|
||||
if (mode & FileNode::Directory)
|
||||
{
|
||||
String file = PathToOS(_BuildFileName(mVolume,path));
|
||||
|
||||
if (::CreateDirectoryW(file.utf16(), 0))
|
||||
return new Win32Directory(path, file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Win32FileSystem::remove(const Path& path)
|
||||
{
|
||||
// Should probably check for outstanding files or directory objects.
|
||||
String file = PathToOS(_BuildFileName(mVolume,path));
|
||||
|
||||
WIN32_FIND_DATAW info;
|
||||
HANDLE handle = ::FindFirstFileW(file.utf16(), &info);
|
||||
::FindClose(handle);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
if (S_ISDIR(info.dwFileAttributes))
|
||||
return ::RemoveDirectoryW(file.utf16());
|
||||
|
||||
return ::DeleteFileW(file.utf16());
|
||||
}
|
||||
|
||||
bool Win32FileSystem::rename(const Path& from,const Path& to)
|
||||
{
|
||||
String fa = PathToOS(_BuildFileName(mVolume,from));
|
||||
String fb = PathToOS(_BuildFileName(mVolume,to));
|
||||
|
||||
return MoveFile(fa.utf16(),fb.utf16());
|
||||
}
|
||||
|
||||
Path Win32FileSystem::mapTo(const Path& path)
|
||||
{
|
||||
return _BuildFileName(mVolume,path);
|
||||
}
|
||||
|
||||
Path Win32FileSystem::mapFrom(const Path& path)
|
||||
{
|
||||
const String::SizeType volumePathLen = mVolume.length();
|
||||
|
||||
String pathStr = path.getFullPath();
|
||||
|
||||
if ( mVolume.compare( pathStr, volumePathLen, String::NoCase ))
|
||||
return Path();
|
||||
|
||||
return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Win32File::Win32File(const Path& path,String name)
|
||||
{
|
||||
mPath = path;
|
||||
mName = name;
|
||||
mStatus = Closed;
|
||||
mHandle = 0;
|
||||
}
|
||||
|
||||
Win32File::~Win32File()
|
||||
{
|
||||
if (mHandle)
|
||||
close();
|
||||
}
|
||||
|
||||
Path Win32File::getName() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
FileNode::Status Win32File::getStatus() const
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
bool Win32File::getAttributes(Attributes* attr)
|
||||
{
|
||||
WIN32_FIND_DATAW info;
|
||||
HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);
|
||||
::FindClose(handle);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
_CopyStatAttributes(info,attr);
|
||||
attr->name = mPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
U64 Win32File::getSize()
|
||||
{
|
||||
U64 size;
|
||||
|
||||
if (mStatus == Open)
|
||||
{
|
||||
// Special case if file is open (handles unflushed buffers)
|
||||
if ( !GetFileSizeEx(mHandle, (PLARGE_INTEGER)&size) )
|
||||
size = 0;
|
||||
return size;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to generic function
|
||||
size = File::getSize();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
U32 Win32File::calculateChecksum()
|
||||
{
|
||||
if (!open( Read ))
|
||||
return 0;
|
||||
|
||||
U64 fileSize = getSize();
|
||||
U32 bufSize = 1024 * 1024 * 4; // 4MB
|
||||
FrameTemp<U8> buf( bufSize );
|
||||
U32 crc = CRC::INITIAL_CRC_VALUE;
|
||||
|
||||
while ( fileSize > 0 )
|
||||
{
|
||||
U32 bytesRead = getMin( fileSize, bufSize );
|
||||
if ( read( buf, bytesRead ) != bytesRead )
|
||||
{
|
||||
close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
fileSize -= bytesRead;
|
||||
crc = CRC::calculateCRC(buf, bytesRead, crc);
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool Win32File::open(AccessMode mode)
|
||||
{
|
||||
close();
|
||||
|
||||
if (mName.isEmpty())
|
||||
return mStatus;
|
||||
|
||||
struct Mode
|
||||
{
|
||||
DWORD mode,share,open;
|
||||
} Modes[] =
|
||||
{
|
||||
{ GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING }, // Read
|
||||
{ GENERIC_WRITE,0,CREATE_ALWAYS }, // Write
|
||||
{ GENERIC_WRITE | GENERIC_READ,0,OPEN_ALWAYS }, // ReadWrite
|
||||
{ GENERIC_WRITE,0,OPEN_ALWAYS } // WriteAppend
|
||||
};
|
||||
|
||||
Mode& m = (mode == Read)? Modes[0]: (mode == Write)? Modes[1]:
|
||||
(mode == ReadWrite)? Modes[2]: Modes[3];
|
||||
|
||||
mHandle = (void*)::CreateFileW(PathToOS(mName).utf16(),
|
||||
m.mode, m.share,
|
||||
NULL, m.open,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
|
||||
NULL);
|
||||
|
||||
if ( mHandle == INVALID_HANDLE_VALUE || mHandle == NULL )
|
||||
{
|
||||
_updateStatus();
|
||||
return false;
|
||||
}
|
||||
|
||||
mStatus = Open;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Win32File::close()
|
||||
{
|
||||
if (mHandle)
|
||||
{
|
||||
::CloseHandle((HANDLE)mHandle);
|
||||
mHandle = 0;
|
||||
}
|
||||
|
||||
mStatus = Closed;
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 Win32File::getPosition()
|
||||
{
|
||||
if (mStatus == Open || mStatus == EndOfFile)
|
||||
return ::SetFilePointer((HANDLE)mHandle,0,0,FILE_CURRENT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U32 Win32File::setPosition(U32 delta, SeekMode mode)
|
||||
{
|
||||
if (mStatus != Open && mStatus != EndOfFile)
|
||||
return 0;
|
||||
|
||||
DWORD fmode;
|
||||
switch (mode)
|
||||
{
|
||||
case Begin: fmode = FILE_BEGIN; break;
|
||||
case Current: fmode = FILE_CURRENT; break;
|
||||
case End: fmode = FILE_END; break;
|
||||
default: fmode = 0; break;
|
||||
}
|
||||
|
||||
DWORD pos = ::SetFilePointer((HANDLE)mHandle,delta,0,fmode);
|
||||
if (pos == INVALID_SET_FILE_POINTER)
|
||||
{
|
||||
mStatus = UnknownError;
|
||||
return 0;
|
||||
}
|
||||
|
||||
mStatus = Open;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
U32 Win32File::read(void* dst, U32 size)
|
||||
{
|
||||
if (mStatus != Open && mStatus != EndOfFile)
|
||||
return 0;
|
||||
|
||||
DWORD bytesRead;
|
||||
if (!::ReadFile((HANDLE)mHandle,dst,size,&bytesRead,0))
|
||||
_updateStatus();
|
||||
else if (bytesRead != size)
|
||||
mStatus = EndOfFile;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
U32 Win32File::write(const void* src, U32 size)
|
||||
{
|
||||
if ((mStatus != Open && mStatus != EndOfFile) || !size)
|
||||
return 0;
|
||||
|
||||
DWORD bytesWritten;
|
||||
if (!::WriteFile((HANDLE)mHandle,src,size,&bytesWritten,0))
|
||||
_updateStatus();
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
void Win32File::_updateStatus()
|
||||
{
|
||||
switch (::GetLastError())
|
||||
{
|
||||
case ERROR_INVALID_ACCESS: mStatus = AccessDenied; break;
|
||||
case ERROR_TOO_MANY_OPEN_FILES: mStatus = UnknownError; break;
|
||||
case ERROR_PATH_NOT_FOUND: mStatus = NoSuchFile; break;
|
||||
case ERROR_FILE_NOT_FOUND: mStatus = NoSuchFile; break;
|
||||
case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break;
|
||||
case ERROR_HANDLE_DISK_FULL: mStatus = FileSystemFull; break;
|
||||
case ERROR_ACCESS_DENIED: mStatus = AccessDenied; break;
|
||||
default: mStatus = UnknownError; break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Win32Directory::Win32Directory(const Path& path,String name)
|
||||
{
|
||||
mPath = path;
|
||||
mName = name;
|
||||
mStatus = Closed;
|
||||
mHandle = 0;
|
||||
}
|
||||
|
||||
Win32Directory::~Win32Directory()
|
||||
{
|
||||
if (mHandle)
|
||||
close();
|
||||
}
|
||||
|
||||
Path Win32Directory::getName() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
bool Win32Directory::open()
|
||||
{
|
||||
if (!_IsDirectory(mName))
|
||||
{
|
||||
mStatus = NoSuchFile;
|
||||
return false;
|
||||
}
|
||||
mStatus = Open;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Win32Directory::close()
|
||||
{
|
||||
if (mHandle)
|
||||
{
|
||||
::FindClose((HANDLE)mHandle);
|
||||
mHandle = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Win32Directory::read(Attributes* entry)
|
||||
{
|
||||
if (mStatus != Open)
|
||||
return false;
|
||||
|
||||
WIN32_FIND_DATA info;
|
||||
if (!mHandle)
|
||||
{
|
||||
mHandle = ::FindFirstFileW((PathToOS(mName) + "\\*").utf16(), &info);
|
||||
|
||||
if (mHandle == NULL)
|
||||
{
|
||||
_updateStatus();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!::FindNextFileW((HANDLE)mHandle, &info))
|
||||
{
|
||||
_updateStatus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip "." and ".." entries
|
||||
if (info.cFileName[0] == '.' && (info.cFileName[1] == '\0' ||
|
||||
(info.cFileName[1] == '.' && info.cFileName[2] == '\0')))
|
||||
return read(entry);
|
||||
|
||||
_CopyStatAttributes(info,entry);
|
||||
entry->name = info.cFileName;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
U32 Win32Directory::calculateChecksum()
|
||||
{
|
||||
// Return checksum of current entry
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Win32Directory::getAttributes(Attributes* attr)
|
||||
{
|
||||
WIN32_FIND_DATA info;
|
||||
HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);
|
||||
::FindClose(handle);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
_updateStatus();
|
||||
return false;
|
||||
}
|
||||
|
||||
_CopyStatAttributes(info,attr);
|
||||
attr->name = mPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
FileNode::Status Win32Directory::getStatus() const
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
void Win32Directory::_updateStatus()
|
||||
{
|
||||
switch (::GetLastError())
|
||||
{
|
||||
case ERROR_NO_MORE_FILES: mStatus = EndOfFile; break;
|
||||
case ERROR_INVALID_ACCESS: mStatus = AccessDenied; break;
|
||||
case ERROR_PATH_NOT_FOUND: mStatus = NoSuchFile; break;
|
||||
case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break;
|
||||
case ERROR_ACCESS_DENIED: mStatus = AccessDenied; break;
|
||||
default: mStatus = UnknownError; break;
|
||||
}
|
||||
}
|
||||
|
||||
} // Namespace Win32
|
||||
|
||||
bool FS::VerifyWriteAccess(const Path &path)
|
||||
{
|
||||
// due to UAC's habit of creating "virtual stores" when permission isn't actually available
|
||||
// actually create, write, read, verify, and delete a file to the folder being tested
|
||||
|
||||
String temp = path.getFullPath();
|
||||
temp += "\\torque_writetest.tmp";
|
||||
|
||||
// first, (try and) delete the file if it exists
|
||||
::DeleteFileW(temp.utf16());
|
||||
|
||||
// now, create the file
|
||||
|
||||
HANDLE hFile = ::CreateFileW(PathToOS(temp).utf16(),
|
||||
GENERIC_WRITE, 0,
|
||||
NULL, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
|
||||
NULL);
|
||||
|
||||
if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )
|
||||
return false;
|
||||
|
||||
U32 t = Platform::getTime();
|
||||
|
||||
DWORD bytesWritten;
|
||||
if (!::WriteFile(hFile,&t,sizeof(t),&bytesWritten,0))
|
||||
{
|
||||
::CloseHandle(hFile);
|
||||
::DeleteFileW(temp.utf16());
|
||||
return false;
|
||||
}
|
||||
|
||||
// close the file
|
||||
::CloseHandle(hFile);
|
||||
|
||||
// open for read
|
||||
|
||||
hFile = ::CreateFileW(PathToOS(temp).utf16(),
|
||||
GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
|
||||
NULL);
|
||||
|
||||
if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )
|
||||
return false;
|
||||
|
||||
U32 t2 = 0;
|
||||
|
||||
DWORD bytesRead;
|
||||
if (!::ReadFile(hFile,&t2,sizeof(t2),&bytesRead,0))
|
||||
{
|
||||
::CloseHandle(hFile);
|
||||
::DeleteFileW(temp.utf16());
|
||||
return false;
|
||||
}
|
||||
|
||||
::CloseHandle(hFile);
|
||||
::DeleteFileW(temp.utf16());
|
||||
|
||||
return t == t2;
|
||||
}
|
||||
|
||||
|
||||
} // Namespace Torque
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume )
|
||||
{
|
||||
return new Win32::Win32FileSystem( volume );
|
||||
}
|
||||
|
||||
String Platform::FS::getAssetDir()
|
||||
{
|
||||
char cen_buf[2048];
|
||||
#ifdef TORQUE_UNICODE
|
||||
if (!Platform::getWebDeployment())
|
||||
{
|
||||
TCHAR buf[ 2048 ];
|
||||
::GetModuleFileNameW( NULL, buf, sizeof( buf ) );
|
||||
convertUTF16toUTF8( buf, cen_buf, sizeof( cen_buf ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
TCHAR buf[ 2048 ];
|
||||
GetCurrentDirectoryW( sizeof( buf ) / sizeof( buf[ 0 ] ), buf );
|
||||
convertUTF16toUTF8( buf, cen_buf, sizeof( cen_buf ) );
|
||||
return Path::CleanSeparators(cen_buf);
|
||||
}
|
||||
#else
|
||||
::GetModuleFileNameA( NULL, cen_buf, 2047);
|
||||
#endif
|
||||
|
||||
char *delimiter = dStrrchr( cen_buf, '\\' );
|
||||
|
||||
if( delimiter != NULL )
|
||||
*delimiter = '\0';
|
||||
|
||||
return Path::CleanSeparators(cen_buf);
|
||||
}
|
||||
|
||||
/// Function invoked by the kernel layer to install OS specific
|
||||
/// file systems.
|
||||
bool Platform::FS::InstallFileSystems()
|
||||
{
|
||||
WCHAR buffer[1024];
|
||||
|
||||
// [8/24/2009 tomb] This stops Windows from complaining about drives that have no disks in
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
|
||||
// Load all the Win32 logical drives.
|
||||
DWORD mask = ::GetLogicalDrives();
|
||||
char drive[] = "A";
|
||||
char volume[] = "A:/";
|
||||
while (mask)
|
||||
{
|
||||
if (mask & 1)
|
||||
{
|
||||
volume[0] = drive[0];
|
||||
Platform::FS::Mount(drive, Platform::FS::createNativeFS(volume));
|
||||
}
|
||||
mask >>= 1;
|
||||
drive[0]++;
|
||||
}
|
||||
|
||||
// Set the current working dir. Windows normally returns
|
||||
// upper case driver letters, but the cygwin bash shell
|
||||
// seems to make it return lower case drives. Force upper
|
||||
// to be consistent with the mounts.
|
||||
::GetCurrentDirectory(sizeof(buffer), buffer);
|
||||
|
||||
if (buffer[1] == ':')
|
||||
buffer[0] = dToupper(buffer[0]);
|
||||
|
||||
String wd = buffer;
|
||||
|
||||
wd += '/';
|
||||
|
||||
Platform::FS::SetCwd(wd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
130
Engine/source/platformWin32/winVolume.h
Normal file
130
Engine/source/platformWin32/winVolume.h
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 _WINVOLUME_H_
|
||||
#define _WINVOLUME_H_
|
||||
|
||||
#ifndef _VOLUME_H_
|
||||
#include "core/volume.h"
|
||||
#endif
|
||||
|
||||
namespace Torque
|
||||
{
|
||||
using namespace FS;
|
||||
|
||||
namespace Win32
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class Win32FileSystem: public FileSystem
|
||||
{
|
||||
public:
|
||||
Win32FileSystem(String volume);
|
||||
~Win32FileSystem();
|
||||
|
||||
String getTypeStr() const { return "Win32"; }
|
||||
|
||||
FileNodeRef resolve(const Path& path);
|
||||
FileNodeRef create(const Path& path,FileNode::Mode);
|
||||
bool remove(const Path& path);
|
||||
bool rename(const Path& from,const Path& to);
|
||||
Path mapTo(const Path& path);
|
||||
Path mapFrom(const Path& path);
|
||||
|
||||
private:
|
||||
String mVolume;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Win32 stdio file access.
|
||||
/// This class makes use the fopen, fread and fwrite for buffered io.
|
||||
class Win32File: public File
|
||||
{
|
||||
public:
|
||||
~Win32File();
|
||||
|
||||
Path getName() const;
|
||||
Status getStatus() const;
|
||||
bool getAttributes(Attributes*);
|
||||
U64 getSize();
|
||||
|
||||
U32 getPosition();
|
||||
U32 setPosition(U32,SeekMode);
|
||||
|
||||
bool open(AccessMode);
|
||||
bool close();
|
||||
|
||||
U32 read(void* dst, U32 size);
|
||||
U32 write(const void* src, U32 size);
|
||||
|
||||
private:
|
||||
friend class Win32FileSystem;
|
||||
|
||||
U32 calculateChecksum();
|
||||
|
||||
Path mPath;
|
||||
String mName;
|
||||
void *mHandle;
|
||||
Status mStatus;
|
||||
|
||||
Win32File(const Path &path, String name);
|
||||
|
||||
bool _updateInfo();
|
||||
void _updateStatus();
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class Win32Directory: public Directory
|
||||
{
|
||||
public:
|
||||
~Win32Directory();
|
||||
|
||||
Path getName() const;
|
||||
Status getStatus() const;
|
||||
bool getAttributes(Attributes*);
|
||||
|
||||
bool open();
|
||||
bool close();
|
||||
bool read(Attributes*);
|
||||
|
||||
private:
|
||||
friend class Win32FileSystem;
|
||||
|
||||
U32 calculateChecksum();
|
||||
|
||||
Path mPath;
|
||||
String mName;
|
||||
void *mHandle;
|
||||
Status mStatus;
|
||||
|
||||
Win32Directory(const Path &path,String name);
|
||||
|
||||
void _updateStatus();
|
||||
};
|
||||
|
||||
} // Namespace
|
||||
} // Namespace
|
||||
#endif
|
||||
631
Engine/source/platformWin32/winWindow.cpp
Normal file
631
Engine/source/platformWin32/winWindow.cpp
Normal file
|
|
@ -0,0 +1,631 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "platformWin32/platformWin32.h"
|
||||
#include "platformWin32/winConsole.h"
|
||||
#include "platformWin32/winDirectInput.h"
|
||||
#include "windowManager/win32/win32Window.h"
|
||||
#include "console/console.h"
|
||||
#include "math/mRandom.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "T3D/resource.h"
|
||||
#include <d3d9.h>
|
||||
#include "gfx/gfxInit.h"
|
||||
#include "gfx/gfxDevice.h"
|
||||
#include "core/strings/unicode.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
|
||||
|
||||
extern void createFontInit();
|
||||
extern void createFontShutdown();
|
||||
extern void installRedBookDevices();
|
||||
extern void handleRedBookCallback(U32, U32);
|
||||
|
||||
static MRandomLCG sgPlatRandom;
|
||||
static bool sgQueueEvents;
|
||||
|
||||
// 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 );
|
||||
|
||||
// static helper variables
|
||||
static HANDLE gMutexHandle = NULL;
|
||||
static bool sgDoubleByteEnabled = false;
|
||||
|
||||
// track window states
|
||||
Win32PlatState winState;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Microsoft Layer for Unicode
|
||||
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/compiling_your_application_with_the_microsoft_layer_for_unicode.asp
|
||||
//
|
||||
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
#ifdef UNICODE
|
||||
|
||||
HMODULE LoadUnicowsProc(void)
|
||||
{
|
||||
return(LoadLibraryA("unicows.dll"));
|
||||
}
|
||||
|
||||
#ifdef _cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern FARPROC _PfnLoadUnicows = (FARPROC) &LoadUnicowsProc;
|
||||
#ifdef _cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------
|
||||
Win32PlatState::Win32PlatState()
|
||||
{
|
||||
log_fp = NULL;
|
||||
hinstOpenGL = NULL;
|
||||
hinstGLU = NULL;
|
||||
hinstOpenAL = NULL;
|
||||
appDC = NULL;
|
||||
appInstance = NULL;
|
||||
currentTime = 0;
|
||||
processId = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool Platform::excludeOtherInstances(const char *mutexName)
|
||||
{
|
||||
#ifdef UNICODE
|
||||
UTF16 b[512];
|
||||
convertUTF8toUTF16((UTF8 *)mutexName, b, sizeof(b));
|
||||
gMutexHandle = CreateMutex(NULL, true, b);
|
||||
#else
|
||||
gMutexHandle = CreateMutex(NULL, true, mutexName);
|
||||
#endif
|
||||
if(!gMutexHandle)
|
||||
return false;
|
||||
|
||||
if(GetLastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
CloseHandle(gMutexHandle);
|
||||
gMutexHandle = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Platform::restartInstance()
|
||||
{
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
ZeroMemory( &si, sizeof(si) );
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory( &pi, sizeof(pi) );
|
||||
|
||||
TCHAR cen_buf[2048];
|
||||
GetModuleFileName( NULL, cen_buf, 2047);
|
||||
|
||||
// Start the child process.
|
||||
if( CreateProcess( cen_buf,
|
||||
NULL, // Command line
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&si, // Pointer to STARTUPINFO structure
|
||||
&pi ) // Pointer to PROCESS_INFORMATION structure
|
||||
!= false )
|
||||
{
|
||||
WaitForInputIdle( pi.hProcess, 5000 );
|
||||
CloseHandle( pi.hProcess );
|
||||
CloseHandle( pi.hThread );
|
||||
}
|
||||
}
|
||||
|
||||
///just check if the app's global mutex exists, and if so,
|
||||
///return true - otherwise, false. Should be called before ExcludeOther
|
||||
/// at very start of app execution.
|
||||
bool Platform::checkOtherInstances(const char *mutexName)
|
||||
{
|
||||
#ifdef TORQUE_MULTITHREAD
|
||||
|
||||
HANDLE pMutex = NULL;
|
||||
|
||||
#ifdef UNICODE
|
||||
UTF16 b[512];
|
||||
convertUTF8toUTF16((UTF8 *)mutexName, b, sizeof(b));
|
||||
pMutex = CreateMutex(NULL, true, b);
|
||||
#else
|
||||
pMutex = CreateMutex(NULL, true, mutexName);
|
||||
#endif
|
||||
|
||||
if(!pMutex)
|
||||
return false;
|
||||
|
||||
if(GetLastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
//another mutex of the same name exists
|
||||
//close ours
|
||||
CloseHandle(pMutex);
|
||||
pMutex = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
CloseHandle(pMutex);
|
||||
pMutex = NULL;
|
||||
#endif
|
||||
|
||||
//we don;t care, always false
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void Platform::AlertOK(const char *windowTitle, const char *message)
|
||||
{
|
||||
ShowCursor(true);
|
||||
#ifdef UNICODE
|
||||
UTF16 m[1024], t[512];
|
||||
convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
|
||||
convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
|
||||
MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK);
|
||||
#else
|
||||
MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK);
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool Platform::AlertOKCancel(const char *windowTitle, const char *message)
|
||||
{
|
||||
ShowCursor(true);
|
||||
#ifdef UNICODE
|
||||
UTF16 m[1024], t[512];
|
||||
convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
|
||||
convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
|
||||
return MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK;
|
||||
#else
|
||||
return MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK;
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool Platform::AlertRetry(const char *windowTitle, const char *message)
|
||||
{
|
||||
ShowCursor(true);
|
||||
#ifdef UNICODE
|
||||
UTF16 m[1024], t[512];
|
||||
convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
|
||||
convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
|
||||
return (MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY);
|
||||
#else
|
||||
return (MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY);
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
HIMC gIMEContext;
|
||||
|
||||
static void InitInput()
|
||||
{
|
||||
#ifndef TORQUE_LIB
|
||||
#ifdef UNICODE
|
||||
//gIMEContext = ImmGetContext(getWin32WindowHandle());
|
||||
//ImmReleaseContext( getWin32WindowHandle(), gIMEContext );
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void Platform::init()
|
||||
{
|
||||
Con::printf("Initializing platform...");
|
||||
|
||||
// Set the platform variable for the scripts
|
||||
Con::setVariable( "$platform", "windows" );
|
||||
|
||||
WinConsole::create();
|
||||
|
||||
if ( !WinConsole::isEnabled() )
|
||||
Input::init();
|
||||
|
||||
InitInput(); // in case DirectInput falls through
|
||||
|
||||
installRedBookDevices();
|
||||
|
||||
sgDoubleByteEnabled = GetSystemMetrics( SM_DBCSENABLED );
|
||||
sgQueueEvents = true;
|
||||
Con::printf("Done");
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void Platform::shutdown()
|
||||
{
|
||||
sgQueueEvents = false;
|
||||
|
||||
if(gMutexHandle)
|
||||
CloseHandle(gMutexHandle);
|
||||
|
||||
Input::destroy();
|
||||
|
||||
GFXDevice::destroy();
|
||||
|
||||
WinConsole::destroy();
|
||||
}
|
||||
|
||||
extern bool LinkConsoleFunctions;
|
||||
|
||||
#ifndef TORQUE_SHARED
|
||||
|
||||
extern S32 TorqueMain(S32 argc, const char **argv);
|
||||
|
||||
//--------------------------------------
|
||||
static S32 run(S32 argc, const char **argv)
|
||||
{
|
||||
// Console hack to ensure consolefunctions get linked in
|
||||
LinkConsoleFunctions=true;
|
||||
|
||||
createFontInit();
|
||||
|
||||
S32 ret = TorqueMain(argc, argv);
|
||||
|
||||
createFontShutdown();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
S32 main(S32 argc, const char **argv)
|
||||
{
|
||||
winState.appInstance = GetModuleHandle(NULL);
|
||||
return run(argc, argv);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
#include "unit/test.h"
|
||||
#include "app/mainLoop.h"
|
||||
|
||||
S32 PASCAL WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32)
|
||||
{
|
||||
#if 0
|
||||
// Run a unit test.
|
||||
StandardMainLoop::initCore();
|
||||
UnitTesting::TestRun tr;
|
||||
tr.test("Platform", true);
|
||||
#else
|
||||
|
||||
Vector<char *> argv( __FILE__, __LINE__ );
|
||||
|
||||
char moduleName[256];
|
||||
#ifdef TORQUE_UNICODE
|
||||
{
|
||||
TCHAR buf[ 256 ];
|
||||
GetModuleFileNameW( NULL, buf, sizeof( buf ) );
|
||||
convertUTF16toUTF8( buf, moduleName, sizeof( moduleName ) );
|
||||
}
|
||||
#else
|
||||
GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
|
||||
#endif
|
||||
argv.push_back(moduleName);
|
||||
|
||||
for (const char* word,*ptr = lpszCmdLine; *ptr; )
|
||||
{
|
||||
// Eat white space
|
||||
for (; dIsspace(*ptr) && *ptr; ptr++)
|
||||
;
|
||||
|
||||
// Pick out the next word
|
||||
for (word = ptr; !dIsspace(*ptr) && *ptr; ptr++)
|
||||
;
|
||||
|
||||
// Add the word to the argument list.
|
||||
if (*word)
|
||||
{
|
||||
int len = ptr - word;
|
||||
char *arg = (char *) dMalloc(len + 1);
|
||||
dStrncpy(arg, word, len);
|
||||
arg[len] = 0;
|
||||
argv.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
winState.appInstance = hInstance;
|
||||
|
||||
S32 retVal = run(argv.size(), (const char **) argv.address());
|
||||
|
||||
for(U32 j = 1; j < argv.size(); j++)
|
||||
dFree(argv[j]);
|
||||
|
||||
return retVal;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else //TORQUE_SHARED
|
||||
|
||||
extern "C"
|
||||
{
|
||||
bool torque_engineinit(int argc, const char **argv);
|
||||
int torque_enginetick();
|
||||
bool torque_engineshutdown();
|
||||
};
|
||||
|
||||
int TorqueMain(int argc, const char **argv)
|
||||
{
|
||||
if (!torque_engineinit(argc, argv))
|
||||
return 1;
|
||||
|
||||
while(torque_enginetick())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
torque_engineshutdown();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
S32 torque_winmain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32)
|
||||
{
|
||||
Vector<char *> argv( __FILE__, __LINE__ );
|
||||
|
||||
char moduleName[256];
|
||||
#ifdef TORQUE_UNICODE
|
||||
{
|
||||
TCHAR buf[ 256 ];
|
||||
GetModuleFileNameW( NULL, buf, sizeof( buf ) );
|
||||
convertUTF16toUTF8( buf, moduleName, sizeof( moduleName ) );
|
||||
}
|
||||
#else
|
||||
GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
|
||||
#endif
|
||||
argv.push_back(moduleName);
|
||||
|
||||
for (const char* word,*ptr = lpszCmdLine; *ptr; )
|
||||
{
|
||||
// Eat white space
|
||||
for (; dIsspace(*ptr) && *ptr; ptr++)
|
||||
;
|
||||
|
||||
// Test for quotes
|
||||
bool withinQuotes = dIsquote(*ptr);
|
||||
|
||||
if (!withinQuotes)
|
||||
{
|
||||
// Pick out the next word
|
||||
for (word = ptr; !dIsspace(*ptr) && *ptr; ptr++)
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Advance past the first quote. We don't want to include it.
|
||||
ptr++;
|
||||
|
||||
// Pick out the next quote
|
||||
for (word = ptr; !dIsquote(*ptr) && *ptr; ptr++)
|
||||
;
|
||||
}
|
||||
|
||||
// Add the word to the argument list.
|
||||
if (*word)
|
||||
{
|
||||
int len = ptr - word;
|
||||
char *arg = (char *) dMalloc(len + 1);
|
||||
dStrncpy(arg, word, len);
|
||||
arg[len] = 0;
|
||||
argv.push_back(arg);
|
||||
}
|
||||
|
||||
// If we had a quote, skip past it for the next arg
|
||||
if (withinQuotes && *ptr)
|
||||
{
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
winState.appInstance = hInstance;
|
||||
|
||||
S32 retVal = TorqueMain(argv.size(), (const char **) argv.address());
|
||||
|
||||
for(U32 j = 1; j < argv.size(); j++)
|
||||
dFree(argv[j]);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
F32 Platform::getRandom()
|
||||
{
|
||||
return sgPlatRandom.randF();
|
||||
}
|
||||
|
||||
////--------------------------------------
|
||||
/// Spawn the default Operating System web browser with a URL
|
||||
/// @param webAddress URL to pass to browser
|
||||
/// @return true if browser successfully spawned
|
||||
bool Platform::openWebBrowser( const char* webAddress )
|
||||
{
|
||||
static bool sHaveKey = false;
|
||||
static wchar_t sWebKey[512];
|
||||
char utf8WebKey[512];
|
||||
|
||||
{
|
||||
HKEY regKey;
|
||||
DWORD size = sizeof( sWebKey );
|
||||
|
||||
if ( RegOpenKeyEx( HKEY_CLASSES_ROOT, dT("\\http\\shell\\open\\command"), 0, KEY_QUERY_VALUE, ®Key ) != ERROR_SUCCESS )
|
||||
{
|
||||
Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to open the HKCR\\http registry key!!!");
|
||||
return( false );
|
||||
}
|
||||
|
||||
if ( RegQueryValueEx( regKey, dT(""), NULL, NULL, (unsigned char *)sWebKey, &size ) != ERROR_SUCCESS )
|
||||
{
|
||||
Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to query the open command registry key!!!" );
|
||||
return( false );
|
||||
}
|
||||
|
||||
RegCloseKey( regKey );
|
||||
sHaveKey = true;
|
||||
|
||||
convertUTF16toUTF8(sWebKey,utf8WebKey,512);
|
||||
|
||||
#ifdef UNICODE
|
||||
char *p = dStrstr((const char *)utf8WebKey, "%1");
|
||||
#else
|
||||
char *p = strstr( (const char *) sWebKey , "%1");
|
||||
#endif
|
||||
if (p) *p = 0;
|
||||
|
||||
}
|
||||
|
||||
STARTUPINFO si;
|
||||
dMemset( &si, 0, sizeof( si ) );
|
||||
si.cb = sizeof( si );
|
||||
|
||||
char buf[1024];
|
||||
#ifdef UNICODE
|
||||
dSprintf( buf, sizeof( buf ), "%s %s", utf8WebKey, webAddress );
|
||||
UTF16 b[1024];
|
||||
convertUTF8toUTF16((UTF8 *)buf, b, sizeof(b));
|
||||
#else
|
||||
dSprintf( buf, sizeof( buf ), "%s %s", sWebKey, webAddress );
|
||||
#endif
|
||||
|
||||
//Con::errorf( ConsoleLogEntry::General, "** Web browser command = %s **", buf );
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
dMemset( &pi, 0, sizeof( pi ) );
|
||||
CreateProcess( NULL,
|
||||
#ifdef UNICODE
|
||||
b,
|
||||
#else
|
||||
buf,
|
||||
#endif
|
||||
NULL,
|
||||
NULL,
|
||||
false,
|
||||
CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
|
||||
NULL,
|
||||
NULL,
|
||||
&si,
|
||||
&pi );
|
||||
|
||||
return( true );
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Login password routines:
|
||||
//--------------------------------------
|
||||
#ifdef UNICODE
|
||||
static const UTF16* TorqueRegKey = dT("SOFTWARE\\GarageGames\\Torque");
|
||||
#else
|
||||
static const char* TorqueRegKey = "SOFTWARE\\GarageGames\\Torque";
|
||||
#endif
|
||||
|
||||
const char* Platform::getLoginPassword()
|
||||
{
|
||||
HKEY regKey;
|
||||
char* returnString = NULL;
|
||||
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_QUERY_VALUE, ®Key ) == ERROR_SUCCESS )
|
||||
{
|
||||
U8 buf[32];
|
||||
DWORD size = sizeof( buf );
|
||||
if ( RegQueryValueEx( regKey, dT("LoginPassword"), NULL, NULL, buf, &size ) == ERROR_SUCCESS )
|
||||
{
|
||||
returnString = Con::getReturnBuffer( size + 1 );
|
||||
dStrcpy( returnString, (const char*) buf );
|
||||
}
|
||||
|
||||
RegCloseKey( regKey );
|
||||
}
|
||||
|
||||
if ( returnString )
|
||||
return( returnString );
|
||||
else
|
||||
return( "" );
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
bool Platform::setLoginPassword( const char* password )
|
||||
{
|
||||
HKEY regKey;
|
||||
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_WRITE, ®Key ) == ERROR_SUCCESS )
|
||||
{
|
||||
if ( RegSetValueEx( regKey, dT("LoginPassword"), 0, REG_SZ, (const U8*) password, dStrlen( password ) + 1 ) != ERROR_SUCCESS )
|
||||
Con::errorf( ConsoleLogEntry::General, "setLoginPassword - Failed to set the subkey value!" );
|
||||
|
||||
RegCloseKey( regKey );
|
||||
return( true );
|
||||
}
|
||||
else
|
||||
Con::errorf( ConsoleLogEntry::General, "setLoginPassword - Failed to open the Torque registry key!" );
|
||||
|
||||
return( false );
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Silly Korean registry key checker:
|
||||
//
|
||||
// NOTE: "Silly" refers to the nature of this hack, and is not intended
|
||||
// as commentary on Koreans as a nationality. Thank you for your
|
||||
// attention.
|
||||
//--------------------------------------
|
||||
ConsoleFunction( isKoreanBuild, bool, 1, 1, "isKoreanBuild()" )
|
||||
{
|
||||
argc; argv;
|
||||
HKEY regKey;
|
||||
bool result = false;
|
||||
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_QUERY_VALUE, ®Key ) == ERROR_SUCCESS )
|
||||
{
|
||||
DWORD val;
|
||||
DWORD size = sizeof( val );
|
||||
if ( RegQueryValueEx( regKey, dT("Korean"), NULL, NULL, (U8*) &val, &size ) == ERROR_SUCCESS )
|
||||
result = ( val > 0 );
|
||||
|
||||
RegCloseKey( regKey );
|
||||
}
|
||||
|
||||
return( result );
|
||||
}
|
||||
54
Engine/source/platformWin32/win_common_prefix.h
Normal file
54
Engine/source/platformWin32/win_common_prefix.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//------------------------------
|
||||
//win_common_prefix.h
|
||||
//------------------------------
|
||||
|
||||
//------------------------------
|
||||
// basic build defines
|
||||
|
||||
// define our platform
|
||||
//#define TARG_WIN32 1
|
||||
//#define WIN32 1
|
||||
|
||||
// normally, this should be on for a PC build
|
||||
//#define USEASSEMBLYTERRBLEND 1
|
||||
|
||||
|
||||
//------------------------------
|
||||
// setup compiler-specific flags
|
||||
|
||||
// METROWERKS CODEWARRIOR
|
||||
//#if __MWERKS__
|
||||
//#pragma once
|
||||
// we turn this on to use inlined CW6-safe ASM in CWProject builds.
|
||||
// ... and thus not require NASM for CWProject building ...
|
||||
//#define TARG_INLINED_ASM 1
|
||||
//#endif
|
||||
|
||||
|
||||
// these are general build flags Torque uses.
|
||||
//#define PNG_NO_READ_tIME 1
|
||||
//#define PNG_NO_WRITE_TIME 1
|
||||
|
||||
//#define NO_MILES_OPENAL 1
|
||||
41
Engine/source/platformWin32/win_debug_prefix.h
Normal file
41
Engine/source/platformWin32/win_debug_prefix.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//win_debug_prefix.h
|
||||
|
||||
#include "win_common_prefix.h"
|
||||
|
||||
// our defines
|
||||
//#define BUILD_SUFFIX "_DEBUG"
|
||||
//#define DEBUG 1
|
||||
//#define ENABLE_ASSERTS 1
|
||||
|
||||
// the PC doesn't need this. just the mac...
|
||||
//#define ITFDUMP_NOASM 1
|
||||
|
||||
|
||||
//#define USEASSEMBLYTERRBLEND 1
|
||||
|
||||
//#define PNG_NO_READ_tIME 1
|
||||
//#define PNG_NO_WRITE_TIME 1
|
||||
|
||||
//#define NO_MILES_OPENAL 1
|
||||
45
Engine/source/platformWin32/win_release_prefix.h
Normal file
45
Engine/source/platformWin32/win_release_prefix.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//win_debug_prefix.h
|
||||
|
||||
#include "win_common_prefix.h"
|
||||
|
||||
// our defines
|
||||
//#define TGE_RELEASE 1
|
||||
//#define TGE_NO_ASSERTS 1
|
||||
//#define BUILD_SUFFIX ""
|
||||
|
||||
// these should be off in a release build
|
||||
//#define DEBUG 1
|
||||
//#define ENABLE_ASSERTS 1
|
||||
|
||||
// the PC doesn't need this. just the mac...
|
||||
//#define ITFDUMP_NOASM 1
|
||||
|
||||
|
||||
//#define USEASSEMBLYTERRBLEND 1
|
||||
|
||||
//#define PNG_NO_READ_tIME 1
|
||||
//#define PNG_NO_WRITE_TIME 1
|
||||
|
||||
//#define NO_MILES_OPENAL 1
|
||||
Loading…
Add table
Add a link
Reference in a new issue