smsvn -> ssc-hg glue: rearrange directory structure

This commit is contained in:
Devin J. Pohly
2013-06-10 15:38:43 -04:00
parent 51576d5942
commit 80057f53cd
3362 changed files with 0 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
#include "global.h"
#include "ArchHooks.h"
#include "RageLog.h"
#include "RageThreads.h"
#include "arch/arch_default.h"
bool ArchHooks::g_bQuitting = false;
bool ArchHooks::g_bToggleWindowed = false;
// Keep from pulling RageThreads.h into ArchHooks.h
static RageMutex g_Mutex( "ArchHooks" );
ArchHooks *HOOKS = NULL;
bool ArchHooks::GetAndClearToggleWindowed()
{
LockMut( g_Mutex );
bool bToggle = g_bToggleWindowed;
g_bToggleWindowed = false;
return bToggle;
}
void ArchHooks::SetToggleWindowed()
{
LockMut( g_Mutex );
g_bToggleWindowed = true;
}
void ArchHooks::SetHasFocus( bool bHasFocus )
{
if( bHasFocus == m_bHasFocus )
return;
m_bHasFocus = bHasFocus;
LOG->Trace( "App %s focus", bHasFocus? "has":"doesn't have" );
LockMut( g_Mutex );
m_bFocusChanged = true;
}
bool ArchHooks::AppFocusChanged()
{
LockMut( g_Mutex );
bool bFocusChanged = m_bFocusChanged;
m_bFocusChanged = false;
return bFocusChanged;
}
bool ArchHooks::GoToURL( RString sUrl )
{
return false;
}
ArchHooks *ArchHooks::Create()
{
return new ARCH_HOOKS;
}
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+162
View File
@@ -0,0 +1,162 @@
#ifndef ARCH_HOOKS_H
#define ARCH_HOOKS_H
class ArchHooks
{
public:
static ArchHooks *Create();
ArchHooks(): m_bHasFocus(true), m_bFocusChanged(false) { }
virtual ~ArchHooks() { }
virtual void Init() { }
/*
* Return the general name of the architecture, eg. "Windows", "OS X", "Unix".
*/
virtual RString GetArchName() const { return "generic"; }
/* This is called as soon as the loading window is shown, and we can
* safely log. */
virtual void DumpDebugInfo() { }
/* Re-exec the game. If this is implemented, it doesn't return. */
virtual void RestartProgram() { }
/*
* Get the 2-letter RFC-639 code of the user's preferred language
* for localized messages, in lowercase.
*/
static RString GetPreferredLanguage();
/*
* A string that uniquely identifies the machine in some way
*/
virtual RString GetMachineId() const { return RString(); }
/*
* If this is a second instance, return true. Optionally, give focus to the existing
* window.
*/
virtual bool CheckForMultipleInstances() { return false; }
virtual void SetTime( tm newtime ) { }
virtual void BoostPriority() { }
virtual void UnBoostPriority() { }
/*
* The priority of the concurrent rendering thread may need to be boosted
* on some schedulers.
*/
virtual void SetupConcurrentRenderingThread() { }
/*
* Returns true if the user wants to quit (eg. ^C, or clicked a "close window" button).
*/
static bool UserQuit() { return g_bQuitting; }
static void SetUserQuit() { g_bQuitting = true; }
/*
* Returns true if the user wants to toggle windowed mode and atomically clears
* the boolean.
*/
static bool GetAndClearToggleWindowed();
static void SetToggleWindowed();
/*
* Return the amount of time since the program started. (This may actually be
* since the initialization of HOOKS.
*
* Full microsecond accuracy may not be available.
*
* bAccurate is a hint: it specifies whether to prefer short-term precision
* or long-term accuracy. If false, the implementation may give higher resolution
* results, but not be as stable over long periods (eg. may drift depending on
* clock speed shifts on laptops). If true, lower precision results (usually with
* no less than a 1ms granularity) are returned, but the results should be stable
* over long periods of time.
*
* Note that bAccurate may change the result significantly; it may use a different
* timer, and may have a different concept of when the program "started".
*
* This is a static function, implemented in whichever ArchHooks source is used,
* so it can be used at any time (such as in global constructors), before HOOKS
* is initialized.
*
* RageTimer layers on top of this, and attempts to correct wrapping, as the
* underlying timers may be 32-bit, but implementations should try to avoid
* wrapping if possible.
*/
static int64_t GetMicrosecondsSinceStart( bool bAccurate );
/*
* Add file search paths, higher priority first.
*/
static void MountInitialFilesystems( const RString &sDirOfExecutable );
/*
* Add file search paths for user-writable directories.
*/
static void MountUserFilesystems( const RString &sDirOfExecutable );
/*
* Platform-specific code calls this to indicate focus changes.
*/
void SetHasFocus( bool bAppHasFocus );
/*
* Return true if the application has input focus.
*/
bool AppHasFocus() const { return m_bHasFocus; }
/*
* Returns true if the application's focus has changed since last called.
*/
bool AppFocusChanged();
/*
* Open a URL in the default web browser
*/
virtual bool GoToURL( RString sUrl );
virtual float GetDisplayAspectRatio() = 0;
private:
/* This are helpers for GetMicrosecondsSinceStart on systems with a timer
* that may loop or move backwards. */
static int64_t FixupTimeIfLooped( int64_t usecs );
static int64_t FixupTimeIfBackwards( int64_t usecs );
static bool g_bQuitting;
static bool g_bToggleWindowed;
bool m_bHasFocus;
bool m_bFocusChanged;
};
#endif
extern ArchHooks *HOOKS; // global and accessable from anywhere in our program
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+84
View File
@@ -0,0 +1,84 @@
#include "global.h"
#include "ArchHooks.h"
/*
* This is a helper for GetMicrosecondsSinceStart on systems with a system
* timer that may loop or move backwards.
*
* The time may decrease last for at least two reasons:
*
* 1. The underlying timer may be 32-bit and use millisecs internally, in which case
* the timer will loop every 2^32 ms.
*
* 2. The underlying clock may have moved backwards (eg. system clock and ntpd).
*
* If the system clock moves backwards, we can't just clamp the time; if it moved back
* an hour, we'd sit around for an hour until it catches up.
*
* Keep track of an offset: the amount of time to add to the result. If we move back
* by 100ms, the offset will be increased by 100ms. If we loop, the offset will be
* increased by the duration 2^32 ms.
*
* This helper only needs to be used if one or both of the above conditions can occur.
* If the underlying timer is reliable, this doesn't need to be used (for a small
* efficiency bonus). Also, you may omit this for GetMicrosecondsSinceStart() when
* bAccurate == false.
*/
int64_t ArchHooks::FixupTimeIfLooped( int64_t usecs )
{
static int64_t last = 0;
static int64_t offset_us = 0;
/* The time has wrapped if the last time was very high and the current time is very low. */
const int64_t i32BitMaxMs = uint64_t(1) << 32;
const int64_t i32BitMaxUs = i32BitMaxMs*1000;
const int64_t one_day = uint64_t(24*60*60)*1000000;
if( last > (i32BitMaxUs-one_day) && usecs < one_day )
offset_us += i32BitMaxUs;
last = usecs;
return usecs + offset_us;
}
int64_t ArchHooks::FixupTimeIfBackwards( int64_t usecs )
{
static int64_t last = 0;
static int64_t offset_us = 0;
if( usecs < last )
{
/* The time has moved backwards. Increase the offset by the amount we moved. */
offset_us += last - usecs;
}
last = usecs;
return usecs + offset_us;
}
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+484
View File
@@ -0,0 +1,484 @@
#include "global.h"
#include "ArchHooks_MacOSX.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "archutils/Unix/CrashHandler.h"
#include "archutils/Unix/SignalHandler.h"
#include "SpecialFiles.h"
#include "ProductInfo.h"
#include <CoreServices/CoreServices.h>
#include <ApplicationServices/ApplicationServices.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/mach.h>
extern "C" {
#include <mach/mach_time.h>
#include <IOKit/graphics/IOGraphicsLib.h>
}
#include <IOKit/IOKitLib.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>
static bool IsFatalSignal( int signal )
{
switch( signal )
{
case SIGINT:
case SIGTERM:
case SIGHUP:
return false;
default:
return true;
}
}
static bool DoCleanShutdown( int signal, siginfo_t *si, const ucontext_t *uc )
{
if( IsFatalSignal(signal) )
return false;
/* ^C. */
ArchHooks::SetUserQuit();
return true;
}
static bool DoCrashSignalHandler( int signal, siginfo_t *si, const ucontext_t *uc )
{
/* Don't dump a debug file if the user just hit ^C. */
if( !IsFatalSignal(signal) )
return true;
CrashHandler::CrashSignalHandler( signal, si, uc );
return true; // Unreached
}
static bool DoEmergencyShutdown( int signal, siginfo_t *si, const ucontext_t *us )
{
if( IsFatalSignal(signal) )
_exit( 1 ); // We ran the crash handler already
return false;
}
void ArchHooks_MacOSX::Init()
{
/* First, handle non-fatal termination signals. */
SignalHandler::OnClose( DoCleanShutdown );
CrashHandler::CrashHandlerHandleArgs( g_argc, g_argv );
CrashHandler::InitializeCrashHandler();
SignalHandler::OnClose( DoCrashSignalHandler );
SignalHandler::OnClose( DoEmergencyShutdown );
/* Now that the crash handler is set up, disable crash reporter. */
// Breaks gdb
// task_set_exception_ports( mach_task_self(), EXC_MASK_ALL, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0 );
// CF*Copy* functions' return values need to be released, CF*Get* functions' do not.
CFStringRef key = CFSTR( "ApplicationBundlePath" );
CFBundleRef bundle = CFBundleGetMainBundle();
CFStringRef appID = CFBundleGetIdentifier( bundle );
if( appID == NULL )
{
// We were probably launched through a symlink. Don't bother hunting down the real path.
return;
}
CFStringRef version = CFStringRef( CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey) );
CFPropertyListRef old = CFPreferencesCopyAppValue( key, appID );
CFURLRef path = CFBundleCopyBundleURL( bundle );
CFPropertyListRef value = CFURLCopyFileSystemPath( path, kCFURLPOSIXPathStyle );
CFMutableDictionaryRef newDict = NULL;
if( old && CFGetTypeID(old) != CFDictionaryGetTypeID() )
{
CFRelease( old );
old = NULL;
}
if( !old )
{
newDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
CFDictionaryAddValue( newDict, version, value );
}
else
{
CFTypeRef oldValue;
CFDictionaryRef dict = CFDictionaryRef( old );
if( !CFDictionaryGetValueIfPresent(dict, version, &oldValue) || !CFEqual(oldValue, value) )
{
// The value is either not present or it is but it is different
newDict = CFDictionaryCreateMutableCopy( kCFAllocatorDefault, 0, dict );
CFDictionarySetValue( newDict, version, value );
}
CFRelease( old );
}
if( newDict )
{
CFPreferencesSetAppValue( key, newDict, appID );
if( !CFPreferencesAppSynchronize(appID) )
LOG->Warn( "Failed to record the run path." );
CFRelease( newDict );
}
CFRelease( value );
CFRelease( path );
}
RString ArchHooks_MacOSX::GetArchName() const
{
#if defined(__ppc__)
return "Mac OS X (ppc)";
#elif defined(__i386__)
return "Mac OS X (i386)";
#else
#error What arch?
#endif
}
RString ArchHooks_MacOSX::GetMachineId() const
{
RString ret;
CFMutableDictionaryRef dict = IOServiceMatching( "IOPlatformExpertDevice" );
CFMutableDictionaryRef property;
io_service_t service;
if( dict )
{
// This consumes the reference.
service = IOServiceGetMatchingService( kIOMasterPortDefault, dict );
if( service )
{
CFTypeRef serial;
CFStringRef key = CFSTR( "IOPlatformSerialNumber" ); // kIOPlatformSerialNumberKey
serial = IORegistryEntryCreateCFProperty( service, key, kCFAllocatorDefault, 0 );
if( serial )
{
const char *str = CFStringGetCStringPtr( (CFStringRef)serial, CFStringGetSystemEncoding() );
ret = str? str:"";
CFRelease( serial );
}
IOObjectRelease( service );
}
}
dict = IOServiceMatching( kIOEthernetInterfaceClass );
if( !dict )
return ret;
property = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
if( !property )
{
CFRelease( dict );
return ret;
}
CFDictionarySetValue( property, CFSTR(kIOPrimaryInterface), kCFBooleanTrue );
CFDictionarySetValue( dict, CFSTR(kIOPropertyMatchKey), property );
CFRelease( property );
io_iterator_t iter;
if( IOServiceGetMatchingServices(kIOMasterPortDefault, dict, &iter) != KERN_SUCCESS )
return ret;
while( (service = IOIteratorNext(iter)) )
{
CFTypeRef data;
io_object_t controller;
if( IORegistryEntryGetParentEntry(service, kIOServicePlane, &controller) != KERN_SUCCESS )
{
IOObjectRelease( service );
continue;
}
data = IORegistryEntryCreateCFProperty( controller, CFSTR(kIOMACAddress),
kCFAllocatorDefault, 0 );
if( data )
{
const uint8_t *p = CFDataGetBytePtr( (CFDataRef)data );
ret += ssprintf( "-%02x:%02x:%02x:%02x:%02x:%02x",
p[0], p[1], p[2], p[3], p[4], p[5] );
CFRelease( data );
}
IOObjectRelease( controller );
IOObjectRelease( service );
}
IOObjectRelease( iter );
return ret;
}
void ArchHooks_MacOSX::DumpDebugInfo()
{
/* Get system version */
RString sSystemVersion;
{
long major = 0, minor = 0, bugFix = 0;
Gestalt( gestaltSystemVersionMajor, &major );
Gestalt( gestaltSystemVersionMinor, &minor );
Gestalt( gestaltSystemVersionBugFix, &bugFix );
if( bugFix )
sSystemVersion = ssprintf( "Mac OS X %ld.%ld.%ld", major, minor, bugFix );
else
sSystemVersion = ssprintf( "Mac OS X %ld.%ld", major, minor );
}
size_t size;
#define GET_PARAM( name, var ) (size = sizeof(var), sysctlbyname(name, &var, &size, NULL, 0) )
/* Get memory */
float fRam;
char ramPower;
{
uint64_t iRam = 0;
GET_PARAM( "hw.memsize", iRam );
if( iRam >= 1073741824 )
{
fRam = float( double(iRam) / 1073741824.0 );
ramPower = 'G';
}
else
{
fRam = float( double(iRam) / 1048576.0 );
ramPower = 'M';
}
}
/* Get processor information */
int iMaxCPUs = 0;
int iCPUs = 0;
float fFreq;
char freqPower;
RString sModel;
do {
char szModel[128];
uint64_t iFreq;
GET_PARAM( "hw.logicalcpu_max", iMaxCPUs );
GET_PARAM( "hw.logicalcpu", iCPUs );
GET_PARAM( "hw.cpufrequency", iFreq );
if( iFreq >= 1000000000 )
{
fFreq = float( double(iFreq) / 1000000000.0 );
freqPower = 'G';
}
else
{
fFreq = float( double(iFreq) / 1000000.0 );
freqPower = 'M';
}
if( GET_PARAM("hw.model", szModel) )
{
sModel = "Unknown";
break;
}
sModel = szModel;
CFURLRef urlRef = CFBundleCopyResourceURL( CFBundleGetMainBundle(), CFSTR("Hardware.plist"), NULL, NULL );
if( urlRef == NULL )
break;
CFDataRef dataRef = NULL;
SInt32 error;
CFURLCreateDataAndPropertiesFromResource( NULL, urlRef, &dataRef, NULL, NULL, &error );
CFRelease( urlRef );
if( dataRef == NULL )
break;
// This also works with binary property lists for some reason.
CFPropertyListRef plRef = CFPropertyListCreateFromXMLData( NULL, dataRef, kCFPropertyListImmutable, NULL );
CFRelease( dataRef );
if( plRef == NULL )
break;
if( CFGetTypeID(plRef) != CFDictionaryGetTypeID() )
{
CFRelease( plRef );
break;
}
CFStringRef keyRef = CFStringCreateWithCStringNoCopy( NULL, szModel, kCFStringEncodingMacRoman, kCFAllocatorNull );
CFStringRef modelRef = (CFStringRef)CFDictionaryGetValue( (CFDictionaryRef)plRef, keyRef );
if( modelRef )
sModel = CFStringGetCStringPtr( modelRef, kCFStringEncodingMacRoman );
CFRelease( keyRef );
CFRelease( plRef );
} while( false );
#undef GET_PARAM
/* Send all of the information to the log */
LOG->Info( "Model: %s (%d/%d)", sModel.c_str(), iCPUs, iMaxCPUs );
LOG->Info( "Clock speed %.2f %cHz", fFreq, freqPower );
LOG->Info( "%s", sSystemVersion.c_str());
LOG->Info( "Memory: %.2f %cB", fRam, ramPower );
}
RString ArchHooks::GetPreferredLanguage()
{
CFStringRef app = kCFPreferencesCurrentApplication;
CFTypeRef t = CFPreferencesCopyAppValue( CFSTR("AppleLanguages"), app );
RString ret = "en";
if( t == NULL )
return ret;
if( CFGetTypeID(t) != CFArrayGetTypeID() )
{
CFRelease( t );
return ret;
}
CFArrayRef languages = CFArrayRef( t );
CFStringRef lang;
if( CFArrayGetCount(languages) > 0 &&
(lang = (CFStringRef)CFArrayGetValueAtIndex(languages, 0)) != NULL )
{
// MacRoman agrees with ASCII in the low-order 7 bits.
const char *str = CFStringGetCStringPtr( lang, kCFStringEncodingMacRoman );
ASSERT( str );
ret = RString( str, 2 );
}
CFRelease( languages );
return ret;
}
bool ArchHooks_MacOSX::GoToURL( RString sUrl )
{
CFURLRef url = CFURLCreateWithBytes( kCFAllocatorDefault, (const UInt8*)sUrl.data(),
sUrl.length(), kCFStringEncodingUTF8, NULL );
OSStatus result = LSOpenCFURLRef( url, NULL );
CFRelease( url );
return result == 0;
}
int64_t ArchHooks::GetMicrosecondsSinceStart( bool bAccurate )
{
// http://developer.apple.com/qa/qa2004/qa1398.html
static double factor = 0.0;
if( unlikely(factor == 0.0) )
{
mach_timebase_info_data_t timeBase;
mach_timebase_info( &timeBase );
factor = timeBase.numer / ( 1000.0 * timeBase.denom );
}
return int64_t( mach_absolute_time() * factor );
}
#include "RageFileManager.h"
static void PathForFolderType( char dir[PATH_MAX], OSType folderType )
{
FSRef fs;
if( FSFindFolder(kUserDomain, folderType, kDontCreateFolder, &fs) )
FAIL_M( ssprintf("FSFindFolder(%lu) failed.", folderType) );
if( FSRefMakePath(&fs, (UInt8 *)dir, PATH_MAX) )
FAIL_M( "FSRefMakePath() failed." );
}
void ArchHooks::MountInitialFilesystems( const RString &sDirOfExecutable )
{
char dir[PATH_MAX];
CFURLRef dataUrl = CFBundleCopyResourceURL( CFBundleGetMainBundle(), CFSTR("StepMania"), CFSTR("smzip"), NULL );
FILEMAN->Mount( "dir", sDirOfExecutable, "/" );
if( dataUrl )
{
CFStringRef dataPath = CFURLCopyFileSystemPath( dataUrl, kCFURLPOSIXPathStyle );
CFStringGetCString( dataPath, dir, PATH_MAX, kCFStringEncodingUTF8 );
if( strncmp(sDirOfExecutable, dir, sDirOfExecutable.length()) == 0 )
FILEMAN->Mount( "zip", dir + sDirOfExecutable.length(), "/" );
CFRelease( dataPath );
CFRelease( dataUrl );
}
}
void ArchHooks::MountUserFilesystems( const RString &sDirOfExecutable )
{
char dir[PATH_MAX];
// /Save -> ~/Library/Preferences/PRODUCT_ID
PathForFolderType( dir, kPreferencesFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID, dir), "/Save" );
// /UserPackages -> ~/Library/Application Support/PRODUCT_ID/Packages
PathForFolderType( dir, kApplicationSupportFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID "/Packages", dir), "/" + SpecialFiles::USER_PACKAGES_DIR );
// /Screenshots -> ~/Pictures/PRODUCT_ID Screenshots
PathForFolderType( dir, kPictureDocumentsFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID " Screenshots", dir), "/Screenshots" );
// /Cache -> ~/Library/Caches/PRODUCT_ID
PathForFolderType( dir, kCachedDataFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/" PRODUCT_ID, dir), "/Cache" );
// /Logs -> ~/Library/Logs/PRODUCT_ID
PathForFolderType( dir, kDomainLibraryFolderType );
FILEMAN->Mount( "dir", ssprintf("%s/Logs/" PRODUCT_ID, dir), "/Logs" );
}
static inline int GetIntValue( CFTypeRef r )
{
int ret;
if( !r || CFGetTypeID(r) != CFNumberGetTypeID() || !CFNumberGetValue(CFNumberRef(r), kCFNumberIntType, &ret) )
return 0;
return ret;
}
float ArchHooks_MacOSX::GetDisplayAspectRatio()
{
io_connect_t displayPort = CGDisplayIOServicePort( CGMainDisplayID() );
CFDictionaryRef dict = IODisplayCreateInfoDictionary( displayPort, 0 );
int width = GetIntValue( CFDictionaryGetValue(dict, CFSTR(kDisplayHorizontalImageSize)) );
int height = GetIntValue( CFDictionaryGetValue(dict, CFSTR(kDisplayVerticalImageSize)) );
CFRelease( dict );
if( width && height )
return float(width)/height;
return 4/3.f;
}
/*
* (c) 2003-2006 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+48
View File
@@ -0,0 +1,48 @@
#ifndef ARCH_HOOKS_MACOSX_H
#define ARCH_HOOKS_MACOSX_H
#include "ArchHooks.h"
class ArchHooks_MacOSX : public ArchHooks
{
public:
void Init();
RString GetArchName() const;
RString GetMachineId() const;
void DumpDebugInfo();
RString GetPreferredLanguage();
bool GoToURL( RString sUrl );
float GetDisplayAspectRatio();
};
#ifdef ARCH_HOOKS
#error "More than one ArchHooks selected!"
#endif
#define ARCH_HOOKS ArchHooks_MacOSX
#endif /* ARCH_HOOKS_MACOSX_H */
/*
* (c) 2003-2005 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+312
View File
@@ -0,0 +1,312 @@
#include "global.h"
#include "ArchHooks_Unix.h"
#include "ProductInfo.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageThreads.h"
#include "LocalizedString.h"
#include "archutils/Unix/SignalHandler.h"
#include "archutils/Unix/GetSysInfo.h"
#include "archutils/Unix/LinuxThreadHelpers.h"
#include "archutils/Unix/EmergencyShutdown.h"
#include "archutils/Unix/AssertionHandler.h"
#include <unistd.h>
#include <sys/time.h>
#if defined(CRASH_HANDLER)
#include "archutils/Unix/CrashHandler.h"
#endif
#if defined(HAVE_FFMPEG)
#include <ffmpeg/avcodec.h>
#endif
static bool IsFatalSignal( int signal )
{
switch( signal )
{
case SIGINT:
case SIGTERM:
case SIGHUP:
return false;
default:
return true;
}
}
static bool DoCleanShutdown( int signal, siginfo_t *si, const ucontext_t *uc )
{
if( IsFatalSignal(signal) )
return false;
/* ^C. */
ArchHooks::SetUserQuit();
return true;
}
#if defined(CRASH_HANDLER)
static bool DoCrashSignalHandler( int signal, siginfo_t *si, const ucontext_t *uc )
{
/* Don't dump a debug file if the user just hit ^C. */
if( !IsFatalSignal(signal) )
return true;
CrashHandler::CrashSignalHandler( signal, si, uc );
return false;
}
#endif
static bool EmergencyShutdown( int signal, siginfo_t *si, const ucontext_t *uc )
{
if( !IsFatalSignal(signal) )
return false;
DoEmergencyShutdown();
#if defined(CRASH_HANDLER)
/* If we ran the crash handler, then die. */
kill( getpid(), SIGKILL );
#endif
/* We didn't run the crash handler. Run the default handler, so we can dump core. */
return false;
}
#if defined(HAVE_TLS)
static thread_local int g_iTestTLS = 0;
static int TestTLSThread( void *p )
{
g_iTestTLS = 2;
return 0;
}
static void TestTLS()
{
#if defined(LINUX)
/* TLS won't work on older threads libraries, and may crash. */
if( !UsingNPTL() )
return;
#endif
/* TLS won't work on older Linux kernels. Do a simple check. */
g_iTestTLS = 1;
RageThread TestThread;
TestThread.SetName( "TestTLS" );
TestThread.Create( TestTLSThread, NULL );
TestThread.Wait();
if( g_iTestTLS == 1 )
RageThread::SetSupportsTLS( true );
}
#endif
#if 1
/* If librt is available, use CLOCK_MONOTONIC to implement GetMicrosecondsSinceStart,
* if supported, so changes to the system clock don't cause problems. */
namespace
{
clockid_t g_Clock = CLOCK_REALTIME;
void OpenGetTime()
{
static bool bInitialized = false;
if( bInitialized )
return;
bInitialized = true;
/* Check whether the clock is actually supported. */
timespec ts;
if( clock_getres(CLOCK_MONOTONIC, &ts) == -1 )
return;
/* If the resolution is worse than a millisecond, fall back on CLOCK_REALTIME. */
if( ts.tv_sec > 0 || ts.tv_nsec > 1000000 )
return;
g_Clock = CLOCK_MONOTONIC;
}
};
clockid_t ArchHooks_Unix::GetClock()
{
OpenGetTime();
return g_Clock;
}
int64_t ArchHooks::GetMicrosecondsSinceStart( bool bAccurate )
{
OpenGetTime();
timespec ts;
clock_gettime( g_Clock, &ts );
int64_t iRet = int64_t(ts.tv_sec) * 1000000 + int64_t(ts.tv_nsec)/1000;
if( g_Clock != CLOCK_MONOTONIC )
iRet = ArchHooks::FixupTimeIfBackwards( iRet );
return iRet;
}
#else
int64_t ArchHooks::GetMicrosecondsSinceStart( bool bAccurate )
{
struct timeval tv;
gettimeofday( &tv, NULL );
int64_t iRet = int64_t(tv.tv_sec) * 1000000 + int64_t(tv.tv_usec);
ret = FixupTimeIfBackwards( ret );
return iRet;
}
#endif
RString ArchHooks::GetPreferredLanguage()
{
return "en";
}
void ArchHooks_Unix::Init()
{
/* First, handle non-fatal termination signals. */
SignalHandler::OnClose( DoCleanShutdown );
#if defined(CRASH_HANDLER)
CrashHandler::CrashHandlerHandleArgs( g_argc, g_argv );
CrashHandler::InitializeCrashHandler();
SignalHandler::OnClose( DoCrashSignalHandler );
#endif
/* Set up EmergencyShutdown, to try to shut down the window if we crash.
* This might blow up, so be sure to do it after the crash handler. */
SignalHandler::OnClose( EmergencyShutdown );
InstallExceptionHandler();
#if defined(HAVE_TLS) && !defined(BSD)
TestTLS();
#endif
}
#ifndef _CS_GNU_LIBC_VERSION
#define _CS_GNU_LIBC_VERSION 2
#endif
static RString LibcVersion()
{
char buf[1024] = "(error)";
int ret = confstr( _CS_GNU_LIBC_VERSION, buf, sizeof(buf) );
if( ret == -1 )
return "(unknown)";
return buf;
}
void ArchHooks_Unix::DumpDebugInfo()
{
RString sys;
int vers;
GetKernel( sys, vers );
LOG->Info( "OS: %s ver %06i", sys.c_str(), vers );
#if defined(CRASH_HANDLER)
LOG->Info( "Crash backtrace component: %s", BACKTRACE_METHOD_TEXT );
LOG->Info( "Crash lookup component: %s", BACKTRACE_LOOKUP_METHOD_TEXT );
#if defined(BACKTRACE_DEMANGLE_METHOD_TEXT)
LOG->Info( "Crash demangle component: %s", BACKTRACE_DEMANGLE_METHOD_TEXT );
#endif
#endif
LOG->Info( "Runtime library: %s", LibcVersion().c_str() );
LOG->Info( "Threads library: %s", ThreadsVersion().c_str() );
#if defined(HAVE_FFMPEG)
LOG->Info( "libavcodec: %#x (%u)", avcodec_version(), avcodec_build() );
#endif
}
void ArchHooks_Unix::SetTime( tm newtime )
{
RString sCommand = ssprintf( "date %02d%02d%02d%02d%04d.%02d",
newtime.tm_mon+1,
newtime.tm_mday,
newtime.tm_hour,
newtime.tm_min,
newtime.tm_year+1900,
newtime.tm_sec );
LOG->Trace( "executing '%s'", sCommand.c_str() );
int ret = system( sCommand );
if( ret == -1 || ret == 127 || !WIFEXITED(ret) || WEXITSTATUS(ret) )
LOG->Trace( "'%s' failed", sCommand.c_str() );
ret = system( "hwclock --systohc" );
if( ret == -1 || ret == 127 || !WIFEXITED(ret) || WEXITSTATUS(ret) )
LOG->Trace( "'hwclock --systohc' failed" );
}
#include "RageFileManager.h"
#include <sys/stat.h>
static LocalizedString COULDNT_FIND_SONGS( "ArchHooks_Unix", "Couldn't find 'Songs'" );
void ArchHooks::MountInitialFilesystems( const RString &sDirOfExecutable )
{
#if defined(UNIX)
/* Mount the root filesystem, so we can read files in /proc, /etc, and so on.
* This is /rootfs, not /root, to avoid confusion with root's home directory. */
FILEMAN->Mount( "dir", "/", "/rootfs" );
/* Mount /proc, so Alsa9Buf::GetSoundCardDebugInfo() and others can access it.
* (Deprecated; use rootfs.) */
FILEMAN->Mount( "dir", "/proc", "/proc" );
#endif
RString Root;
struct stat st;
if( !stat(sDirOfExecutable + "/Packages", &st) && st.st_mode&S_IFDIR )
Root = sDirOfExecutable;
else if( !stat(sDirOfExecutable + "/Songs", &st) && st.st_mode&S_IFDIR )
Root = sDirOfExecutable;
else if( !stat(RageFileManagerUtil::sInitialWorkingDirectory + "/Songs", &st) && st.st_mode&S_IFDIR )
Root = RageFileManagerUtil::sInitialWorkingDirectory;
else
RageException::Throw( "%s", COULDNT_FIND_SONGS.GetValue().c_str() );
FILEMAN->Mount( "dir", Root, "/" );
}
void ArchHooks::MountUserFilesystems( const RString &sDirOfExecutable )
{
/* Path to write general mutable user data.
* Lowercase the PRODUCT_ID; dotfiles and directories are almost always lowercase. */
const char *szHome = getenv( "HOME" );
RString sProductId = PRODUCT_ID;
sProductId.MakeLower();
RString sUserDataPath = ssprintf( "%s/.%s", szHome? szHome:".", sProductId.c_str() );
FILEMAN->Mount( "dir", sUserDataPath + "/Cache", "/Cache" );
FILEMAN->Mount( "dir", sUserDataPath + "/Logs", "/Logs" );
FILEMAN->Mount( "dir", sUserDataPath + "/Save", "/Save" );
FILEMAN->Mount( "dir", sUserDataPath + "/Screenshots", "/Screenshots" );
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+50
View File
@@ -0,0 +1,50 @@
#ifndef ARCH_HOOKS_UNIX_H
#define ARCH_HOOKS_UNIX_H
#include "ArchHooks.h"
class ArchHooks_Unix: public ArchHooks
{
public:
void Init();
RString GetArchName() const { return "Unix"; }
void DumpDebugInfo();
void SetTime( tm newtime );
int64_t GetMicrosecondsSinceStart();
void MountInitialFilesystems( const RString &sDirOfExecutable );
float GetDisplayAspectRatio() { return 4.0f/3; }
static clockid_t GetClock();
};
#ifdef ARCH_HOOKS
#error "More than one ArchHooks selected!"
#endif
#define ARCH_HOOKS ArchHooks_Unix
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+219
View File
@@ -0,0 +1,219 @@
#include "global.h"
#include "ArchHooks_Win32.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "RageThreads.h"
#include "ProductInfo.h"
#include "archutils/win32/AppInstance.h"
#include "archutils/win32/crash.h"
#include "archutils/win32/DebugInfoHunt.h"
#include "archutils/win32/ErrorStrings.h"
#include "archutils/win32/RestartProgram.h"
#include "archutils/win32/GotoURL.h"
#include "archutils/Win32/RegistryAccess.h"
static HANDLE g_hInstanceMutex;
static bool g_bIsMultipleInstance = false;
#if _MSC_VER >= 1400 // VC8
void InvalidParameterHandler( const wchar_t *szExpression, const wchar_t *szFunction, const wchar_t *szFile,
unsigned int iLine, uintptr_t pReserved )
{
FAIL_M( "Invalid parameter" );
}
#endif
ArchHooks_Win32::ArchHooks_Win32()
{
HOOKS = this;
/* Disable critical errors, and handle them internally. We never want the
* "drive not ready", etc. dialogs to pop up. */
SetErrorMode( SetErrorMode(0) | SEM_FAILCRITICALERRORS );
CrashHandler::CrashHandlerHandleArgs( g_argc, g_argv );
SetUnhandledExceptionFilter( CrashHandler::ExceptionHandler );
#if _MSC_VER >= 1400 // VC8
_set_invalid_parameter_handler( InvalidParameterHandler );
#endif
/* Windows boosts priority on keyboard input, among other things. Disable that for
* the main thread. */
SetThreadPriorityBoost( GetCurrentThread(), TRUE );
g_hInstanceMutex = CreateMutex( NULL, TRUE, PRODUCT_ID );
g_bIsMultipleInstance = false;
if( GetLastError() == ERROR_ALREADY_EXISTS )
g_bIsMultipleInstance = true;
}
ArchHooks_Win32::~ArchHooks_Win32()
{
CloseHandle( g_hInstanceMutex );
}
void ArchHooks_Win32::DumpDebugInfo()
{
/* This is a good time to do the debug search: before we actually
* start OpenGL (in case something goes wrong). */
SearchForDebugInfo();
}
struct CallbackData
{
HWND hParent;
HWND hResult;
};
// Like GW_ENABLEDPOPUP:
static BOOL CALLBACK GetEnabledPopup( HWND hWnd, LPARAM lParam )
{
CallbackData *pData = (CallbackData *) lParam;
if( GetParent(hWnd) != pData->hParent )
return TRUE;
if( (GetWindowLong(hWnd, GWL_STYLE) & WS_POPUP) != WS_POPUP )
return TRUE;
if( !IsWindowEnabled(hWnd) )
return TRUE;
pData->hResult = hWnd;
return FALSE;
}
static const RString CURRENT_VERSION_KEY = "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion";
RString ArchHooks_Win32::GetMachineId() const
{
RString s;
if( RegistryAccess::GetRegValue( CURRENT_VERSION_KEY, "ProductID", s ) )
return s;
return RString();
}
bool ArchHooks_Win32::CheckForMultipleInstances()
{
if( !g_bIsMultipleInstance )
return false;
/* Search for the existing window. Prefer to use the class name, which is less likely to
* have a false match, and will match the gameplay window. If that fails, try the window
* name, which should match the loading window. */
HWND hWnd = FindWindow( PRODUCT_ID, NULL );
if( hWnd == NULL )
hWnd = FindWindow( NULL, PRODUCT_ID );
if( hWnd != NULL )
{
/* If the application has a model dialog box open, we want to be sure to give focus to it,
* not the main window. */
CallbackData data;
data.hParent = hWnd;
data.hResult = NULL;
EnumWindows( GetEnabledPopup, (LPARAM) &data );
if( data.hResult != NULL )
SetForegroundWindow( data.hResult );
else
SetForegroundWindow( hWnd );
}
return true;
}
void ArchHooks_Win32::RestartProgram()
{
Win32RestartProgram();
}
void ArchHooks_Win32::SetTime( tm newtime )
{
SYSTEMTIME st;
ZERO( st );
st.wYear = (WORD)newtime.tm_year+1900;
st.wMonth = (WORD)newtime.tm_mon+1;
st.wDay = (WORD)newtime.tm_mday;
st.wHour = (WORD)newtime.tm_hour;
st.wMinute = (WORD)newtime.tm_min;
st.wSecond = (WORD)newtime.tm_sec;
st.wMilliseconds = 0;
SetLocalTime( &st );
}
void ArchHooks_Win32::BoostPriority()
{
/* We just want a slight boost, so we don't skip needlessly if something happens
* in the background. We don't really want to be high-priority--above normal should
* be enough. However, ABOVE_NORMAL_PRIORITY_CLASS is only supported in Win2000
* and later. */
OSVERSIONINFO version;
version.dwOSVersionInfoSize=sizeof(version);
if( !GetVersionEx(&version) )
{
LOG->Warn( werr_ssprintf(GetLastError(), "GetVersionEx failed") );
return;
}
#ifndef ABOVE_NORMAL_PRIORITY_CLASS
#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
#endif
DWORD pri = HIGH_PRIORITY_CLASS;
if( version.dwMajorVersion >= 5 )
pri = ABOVE_NORMAL_PRIORITY_CLASS;
/* Be sure to boost the app, not the thread, to make sure the
* sound thread stays higher priority than the main thread. */
SetPriorityClass( GetCurrentProcess(), pri );
}
void ArchHooks_Win32::UnBoostPriority()
{
SetPriorityClass( GetCurrentProcess(), NORMAL_PRIORITY_CLASS );
}
void ArchHooks_Win32::SetupConcurrentRenderingThread()
{
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL );
}
bool ArchHooks_Win32::GoToURL( RString sUrl )
{
return ::GotoURL( sUrl );
}
float ArchHooks_Win32::GetDisplayAspectRatio()
{
DEVMODE dm;
ZERO( dm );
dm.dmSize = sizeof(dm);
BOOL bResult = EnumDisplaySettings( NULL, ENUM_REGISTRY_SETTINGS, &dm );
ASSERT( bResult );
return dm.dmPelsWidth / (float)dm.dmPelsHeight;
}
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+57
View File
@@ -0,0 +1,57 @@
#ifndef ARCH_HOOKS_WIN32_H
#define ARCH_HOOKS_WIN32_H
#include "ArchHooks.h"
class RageMutex;
class ArchHooks_Win32: public ArchHooks
{
public:
ArchHooks_Win32();
~ArchHooks_Win32();
RString GetArchName() const { return "Windows"; }
void DumpDebugInfo();
void RestartProgram();
RString GetMachineId() const;
bool CheckForMultipleInstances();
void SetTime( tm newtime );
void BoostPriority();
void UnBoostPriority();
void SetupConcurrentRenderingThread();
bool GoToURL( RString sUrl );
virtual float GetDisplayAspectRatio();
};
#ifdef ARCH_HOOKS
#error "More than one ArchHooks selected!"
#endif
#define ARCH_HOOKS ArchHooks_Win32
#endif
/*
* (c) 2002-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,187 @@
#include "global.h"
#include "ArchHooks.h"
#include "RageUtil.h"
#include "archutils/Win32/SpecialDirs.h"
#include "ProductInfo.h"
#include "RageFileManager.h"
#include "SpecialFiles.h"
// for timeGetTime
#include <windows.h>
#include <mmsystem.h>
#if defined(_MSC_VER)
#pragma comment(lib, "winmm.lib")
#endif
static bool g_bTimerInitialized;
static void InitTimer()
{
if( g_bTimerInitialized )
return;
g_bTimerInitialized = true;
timeBeginPeriod( 1 );
}
int64_t ArchHooks::GetMicrosecondsSinceStart( bool bAccurate )
{
if( !g_bTimerInitialized )
InitTimer();
int64_t ret = timeGetTime() * int64_t(1000);
if( bAccurate )
{
ret = FixupTimeIfLooped( ret );
ret = FixupTimeIfBackwards( ret );
}
return ret;
}
static RString GetMountDir( const RString &sDirOfExecutable )
{
/* All Windows data goes in the directory one level above the executable. */
CHECKPOINT_M( ssprintf( "DOE \"%s\"", sDirOfExecutable.c_str()) );
vector<RString> asParts;
split( sDirOfExecutable, "/", asParts );
CHECKPOINT_M( ssprintf( "... %i asParts", asParts.size()) );
ASSERT_M( asParts.size() > 1, ssprintf("Strange sDirOfExecutable: %s", sDirOfExecutable.c_str()) );
RString sDir = join( "/", asParts.begin(), asParts.end()-1 );
return sDir;
}
void ArchHooks::MountInitialFilesystems( const RString &sDirOfExecutable )
{
RString sDir = GetMountDir( sDirOfExecutable );
FILEMAN->Mount( "dir", sDir, "/" );
}
void ArchHooks::MountUserFilesystems( const RString &sDirOfExecutable )
{
RString sAppDataDir = SpecialDirs::GetAppDataDir() + PRODUCT_ID;
FILEMAN->Mount( "dir", sAppDataDir + "/Cache", "/Cache" );
FILEMAN->Mount( "dir", sAppDataDir + "/Logs", "/Logs" );
FILEMAN->Mount( "dir", sAppDataDir + "/Save", "/Save" );
FILEMAN->Mount( "dir", sAppDataDir + "/Screenshots", "/Screenshots" );
FILEMAN->Mount( "dir", sAppDataDir + "/Packages", "/" + SpecialFiles::USER_PACKAGES_DIR );
}
static RString LangIdToString( LANGID l )
{
switch( PRIMARYLANGID(l) )
{
case LANG_ARABIC: return "ar";
case LANG_BULGARIAN: return "bg";
case LANG_CATALAN: return "ca";
case LANG_CHINESE: return "zh";
case LANG_CZECH: return "cs";
case LANG_DANISH: return "da";
case LANG_GERMAN: return "de";
case LANG_GREEK: return "el";
case LANG_SPANISH: return "es";
case LANG_FINNISH: return "fi";
case LANG_FRENCH: return "fr";
case LANG_HEBREW: return "iw";
case LANG_HUNGARIAN: return "hu";
case LANG_ICELANDIC: return "is";
case LANG_ITALIAN: return "it";
case LANG_JAPANESE: return "ja";
case LANG_KOREAN: return "ko";
case LANG_DUTCH: return "nl";
case LANG_NORWEGIAN: return "no";
case LANG_POLISH: return "pl";
case LANG_PORTUGUESE: return "pt";
case LANG_ROMANIAN: return "ro";
case LANG_RUSSIAN: return "ru";
case LANG_CROATIAN: return "hr";
// case LANG_SERBIAN: return "sr"; // same as LANG_CROATIAN?
case LANG_SLOVAK: return "sk";
case LANG_ALBANIAN: return "sq";
case LANG_SWEDISH: return "sv";
case LANG_THAI: return "th";
case LANG_TURKISH: return "tr";
case LANG_URDU: return "ur";
case LANG_INDONESIAN: return "in";
case LANG_UKRAINIAN: return "uk";
case LANG_SLOVENIAN: return "sl";
case LANG_ESTONIAN: return "et";
case LANG_LATVIAN: return "lv";
case LANG_LITHUANIAN: return "lt";
case LANG_VIETNAMESE: return "vi";
case LANG_ARMENIAN: return "hy";
case LANG_BASQUE: return "eu";
case LANG_MACEDONIAN: return "mk";
case LANG_AFRIKAANS: return "af";
case LANG_GEORGIAN: return "ka";
case LANG_FAEROESE: return "fo";
case LANG_HINDI: return "hi";
case LANG_MALAY: return "ms";
case LANG_KAZAK: return "kk";
case LANG_SWAHILI: return "sw";
case LANG_UZBEK: return "uz";
case LANG_TATAR: return "tt";
case LANG_PUNJABI: return "pa";
case LANG_GUJARATI: return "gu";
case LANG_TAMIL: return "ta";
case LANG_KANNADA: return "kn";
case LANG_MARATHI: return "mr";
case LANG_SANSKRIT: return "sa";
// These aren't present in the VC6 headers. We'll never have translations to these languages anyway. -C
//case LANG_MONGOLIAN: return "mn";
//case LANG_GALICIAN: return "gl";
default:
case LANG_ENGLISH: return "en";
}
}
static LANGID GetLanguageID()
{
HINSTANCE hDLL = LoadLibrary( "kernel32.dll" );
if( hDLL )
{
typedef LANGID(GET_USER_DEFAULT_UI_LANGUAGE)(void);
GET_USER_DEFAULT_UI_LANGUAGE *pGetUserDefaultUILanguage = (GET_USER_DEFAULT_UI_LANGUAGE*) GetProcAddress( hDLL, "GetUserDefaultUILanguage" );
if( pGetUserDefaultUILanguage )
{
LANGID ret = pGetUserDefaultUILanguage();
FreeLibrary( hDLL );
return ret;
}
FreeLibrary( hDLL );
}
return GetUserDefaultLangID();
}
RString ArchHooks::GetPreferredLanguage()
{
return LangIdToString( GetLanguageID() );
}
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+226
View File
@@ -0,0 +1,226 @@
#include "global.h"
#include "ArchHooks_Xbox.h"
#include "dsound.h" // for timeGetTime
#include "archutils/Xbox/custom_launch_params.h" // for XGetCustomLaunchData
#include "archutils/Xbox/VirtualMemory.h"
#include <xtl.h> // for XNetStartup
#include <new.h> // for _set_new_handler and _set_new_mode
typedef struct _UNICODE_STRING {unsigned short Length; unsigned short MaximumLength; PSTR Buffer;} UNICODE_STRING,*PUNICODE_STRING;
extern "C" XBOXAPI DWORD WINAPI IoCreateSymbolicLink(IN PUNICODE_STRING SymbolicLinkName,IN PUNICODE_STRING DeviceName);
static bool g_bTimerInitialized;
static DWORD g_iStartTime;
static void InitTimer()
{
if( g_bTimerInitialized )
return;
g_bTimerInitialized = true;
g_iStartTime = timeGetTime();
}
int64_t ArchHooks::GetMicrosecondsSinceStart( bool bAccurate )
{
if( !g_bTimerInitialized )
InitTimer();
int64_t ret = (timeGetTime() - g_iStartTime) * int64_t(1000);
if( bAccurate )
{
ret = FixupTimeIfLooped( ret );
ret = FixupTimeIfBackwards( ret );
}
return ret;
}
void MountDriveLetter(char drive, char* szDevice, char* szDir)
{
char szSourceDevice[256];
char szDestinationDrive[16];
sprintf(szDestinationDrive, "\\??\\%c:", drive);
sprintf(szSourceDevice,"\\Device\\%s",szDevice);
if (*szDir != 0x00 && *szDir != '\\')
{
strcat(szSourceDevice, "\\");
strcat(szSourceDevice, szDir);
}
UNICODE_STRING LinkName =
{
strlen(szDestinationDrive),
strlen(szDestinationDrive) + 1,
szDestinationDrive
};
UNICODE_STRING DeviceName =
{
strlen(szSourceDevice),
strlen(szSourceDevice) + 1,
szSourceDevice
};
IoCreateSymbolicLink(&LinkName, &DeviceName);
}
void MountDrives()
{
MountDriveLetter('A', "Cdrom0", "\\");
MountDriveLetter('E', "Harddisk0\\Partition1", "\\");
MountDriveLetter('C', "Harddisk0\\Partition2", "\\");
MountDriveLetter('X', "Harddisk0\\Partition3", "\\");
MountDriveLetter('Y', "Harddisk0\\Partition4", "\\");
MountDriveLetter('F', "Harddisk0\\Partition6", "\\");
MountDriveLetter('G', "Harddisk0\\Partition7", "\\");
}
bool SetupNetwork()
{
#if !defined(WITHOUT_NETWORKING)
XNetStartupParams xnsp;
memset(&xnsp, 0, sizeof(xnsp));
xnsp.cfgSizeOfStruct = sizeof(XNetStartupParams);
xnsp.cfgFlags = XNET_STARTUP_BYPASS_SECURITY;
INT err = XNetStartup(&xnsp);
return err == 0;
#else
return true;
#endif
}
// if Xbox has 128 meg RAM make sure its used
void EnableExtraRAM()
{
LARGE_INTEGER regVal;
// Verify that we have 128 megs available
MEMORYSTATUS memStatus;
GlobalMemoryStatus( &memStatus );
if( memStatus.dwTotalPhys < (100 * 1024 * 1024) )
return;
// Grab the existing default type (0x02FF)
READMSRREG( 0x02FF, &regVal );
// Set the default to WriteBack (0x06)
regVal.LowPart = (regVal.LowPart & ~0xFF) | 0x06;
WRITEMSRREG( 0x02FF, regVal );
}
void InitDevices()
{
XDEVICE_PREALLOC_TYPE xdpt[] = {{XDEVICE_TYPE_GAMEPAD, 4}, {XDEVICE_TYPE_MEMORY_UNIT, 2}};
XInitDevices( sizeof(xdpt) / sizeof(XDEVICE_PREALLOC_TYPE), xdpt );
}
ArchHooks_Xbox::ArchHooks_Xbox()
{
_set_new_handler(NoMemory);
_set_new_mode(1);
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) CheckPageFault);
XGetCustomLaunchData();
// mount A to DVD, C, E, F, G, X, and Y to the harddisk
MountDrives();
SetupNetwork();
EnableExtraRAM();
InitDevices();
}
static RString XLangID( DWORD Lang )
{
switch(Lang)
{
case XC_LANGUAGE_JAPANESE:return "JA";
case XC_LANGUAGE_GERMAN:return "DE";
case XC_LANGUAGE_FRENCH:return "FR";
case XC_LANGUAGE_SPANISH:return "ES";
case XC_LANGUAGE_ITALIAN:return "IT";
case XC_LANGUAGE_KOREAN:return "KO";
case XC_LANGUAGE_TCHINESE:return "ZH";
case XC_LANGUAGE_PORTUGUESE:return "PT";
default:
case XC_LANGUAGE_ENGLISH: return "EN";
}
}
RString ArchHooks::GetPreferredLanguage()
{
return XLangID( XGetLanguage() );
}
ArchHooks_Xbox::~ArchHooks_Xbox()
{
// We only want to reboot the Xbox in a software manner.
XLaunchNewImage( NULL, NULL );
}
void ArchHooks_Xbox::DumpDebugInfo()
{
}
float ArchHooks_Xbox::GetDisplayAspectRatio()
{
float fDisplayAspectRatio = 4.0 / 3.0;
IDirect3DSurface8 *pBackBuffer = NULL;
if( D3D__pDevice->GetBackBuffer( -1, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer) == D3D_OK )
{
D3DSURFACE_DESC Desc;
if( pBackBuffer->GetDesc(&Desc) == D3D_OK )
{
fDisplayAspectRatio = (float)Desc.Width / (float)Desc.Height;
}
pBackBuffer->Release();
}
return fDisplayAspectRatio;
}
#include "RageFileManager.h"
void ArchHooks::MountInitialFilesystems( const RString &sDirOfExecutable )
{
FILEMAN->Mount( "dir", "D:\\", "/" );
}
void ArchHooks::MountUserFilesystems( const RString &sDirOfExecutable )
{
// Mount everything game-writable (not counting the editor) to the game title persistent data region ( /E/TDATA/33342530/ )
FILEMAN->Mount( "dir", "T:/Cache", "/Cache" );
FILEMAN->Mount( "dir", "T:/Logs", "/Logs" );
FILEMAN->Mount( "dir", "T:/Save", "/Save" );
FILEMAN->Mount( "dir", "T:/Screenshots", "/Screenshots" );
}
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+78
View File
@@ -0,0 +1,78 @@
#ifndef ARCH_HOOKS_XBOX_H
#define ARCH_HOOKS_XBOX_H
#include "ArchHooks.h"
class RageMutex;
class ArchHooks_Xbox: public ArchHooks
{
public:
ArchHooks_Xbox();
~ArchHooks_Xbox();
RString GetArchName() { return "Xbox"; }
void DumpDebugInfo();
float GetDisplayAspectRatio();
//void MountInitialFilesystems( const RString &sDirOfExecutable );
};
// XXX: This stuff doesn't belong here. Hide it in ArchHooks.
// Read a 64 bit MSR register
inline void READMSRREG( UINT32 reg, LARGE_INTEGER *val )
{
UINT32 lowPart, highPart;
__asm
{
mov ecx, reg
rdmsr
mov lowPart, eax
mov highPart, edx
};
val->LowPart = lowPart;
val->HighPart = highPart;
}
// Write a 64 bit MSR register
inline void WRITEMSRREG( UINT32 reg, LARGE_INTEGER val )
{
__asm
{
mov ecx, reg
mov eax, val.LowPart
mov edx, val.HighPart
wrmsr
};
}
#ifdef ARCH_HOOKS
#error "More than one ArchHooks selected!"
#endif
#define ARCH_HOOKS ArchHooks_Xbox
#endif
/*
* (c) 2002-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+192
View File
@@ -0,0 +1,192 @@
#include "global.h"
#include "Dialog.h"
#include "DialogDriver.h"
#if !defined(SMPACKAGE)
#include "PrefsManager.h"
#endif
#include "RageUtil.h"
#include "RageLog.h"
#include "RageThreads.h"
#if !defined(SMPACKAGE)
static Preference<RString> g_sIgnoredDialogs( "IgnoredDialogs", "" );
#endif
static DialogDriver *g_pImpl = NULL;
static DialogDriver_Null g_NullDriver;
static bool g_bWindowed = true; // Start out true so that we'll show errors before DISPLAY is init'd.
static bool DialogsEnabled()
{
return g_bWindowed;
}
void Dialog::Init()
{
if( g_pImpl != NULL )
return;
g_pImpl = DialogDriver::Create();
/* DialogDriver_Null should have worked, at least. */
ASSERT( g_pImpl != NULL );
}
void Dialog::Shutdown()
{
delete g_pImpl;
g_pImpl = NULL;
}
static bool MessageIsIgnored( RString sID )
{
#if !defined(SMPACKAGE)
vector<RString> asList;
split( g_sIgnoredDialogs, ",", asList );
for( unsigned i = 0; i < asList.size(); ++i )
if( !sID.CompareNoCase(asList[i]) )
return true;
#endif
return false;
}
void Dialog::IgnoreMessage( RString sID )
{
/* We can't ignore messages before PREFSMAN is around. */
#if !defined(SMPACKAGE)
if( PREFSMAN == NULL )
{
if( sID != "" && LOG )
LOG->Warn( "Dialog: message \"%s\" set ID too early for ignorable messages", sID.c_str() );
return;
}
if( sID == "" )
return;
if( MessageIsIgnored(sID) )
return;
vector<RString> asList;
split( g_sIgnoredDialogs, ",", asList );
asList.push_back( sID );
g_sIgnoredDialogs.Set( join(",",asList) );
PREFSMAN->SavePrefsToDisk();
#endif
}
void Dialog::Error( RString sMessage, RString sID )
{
Dialog::Init();
if( LOG )
LOG->Trace( "Dialog: \"%s\" [%s]", sMessage.c_str(), sID.c_str() );
if( sID != "" && MessageIsIgnored(sID) )
return;
RageThread::SetIsShowingDialog( true );
g_pImpl->Error( sMessage, sID );
RageThread::SetIsShowingDialog( false );
}
void Dialog::SetWindowed( bool bWindowed )
{
g_bWindowed = bWindowed;
}
void Dialog::OK( RString sMessage, RString sID )
{
Dialog::Init();
if( LOG )
LOG->Trace( "Dialog: \"%s\" [%s]", sMessage.c_str(), sID.c_str() );
if( sID != "" && MessageIsIgnored(sID) )
return;
RageThread::SetIsShowingDialog( true );
// only show Dialog if windowed
if( DialogsEnabled() )
g_pImpl->OK( sMessage, sID ); // call derived version
else
g_NullDriver.OK( sMessage, sID );
RageThread::SetIsShowingDialog( false );
}
Dialog::Result Dialog::AbortRetryIgnore( RString sMessage, RString sID )
{
Dialog::Init();
if( LOG )
LOG->Trace( "Dialog: \"%s\" [%s]", sMessage.c_str(), sID.c_str() );
if( sID != "" && MessageIsIgnored(sID) )
return g_NullDriver.AbortRetryIgnore( sMessage, sID );
RageThread::SetIsShowingDialog( true );
// only show Dialog if windowed
Dialog::Result ret;
if( DialogsEnabled() )
ret = g_pImpl->AbortRetryIgnore( sMessage, sID ); // call derived version
else
ret = g_NullDriver.AbortRetryIgnore( sMessage, sID );
RageThread::SetIsShowingDialog( false );
return ret;
}
Dialog::Result Dialog::AbortRetry( RString sMessage, RString sID )
{
Dialog::Init();
if( LOG )
LOG->Trace( "Dialog: \"%s\" [%s]", sMessage.c_str(), sID.c_str() );
if( sID != "" && MessageIsIgnored(sID) )
return g_NullDriver.AbortRetry( sMessage, sID );
RageThread::SetIsShowingDialog( true );
// only show Dialog if windowed
Dialog::Result ret;
if( DialogsEnabled() )
ret = g_pImpl->AbortRetry( sMessage, sID ); // call derived version
else
ret = g_NullDriver.AbortRetry( sMessage, sID );
RageThread::SetIsShowingDialog( false );
return ret;
}
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+48
View File
@@ -0,0 +1,48 @@
#ifndef DIALOG_BOX_H
#define DIALOG_BOX_H
namespace Dialog
{
/* ID can be used to identify a class of messages, for "don't display this
* dialog"-type prompts. */
void Init();
void Shutdown();
void SetWindowed( bool bWindowed );
enum Result { abort, retry, ignore };
void Error( RString sError, RString sID = "" );
void OK( RString sMessage, RString sID = "" );
Result AbortRetryIgnore( RString sMessage, RString sID = "" );
Result AbortRetry( RString sMessage, RString sID = "" );
/* for DialogDrivers */
void IgnoreMessage( RString sID );
}
#endif
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+70
View File
@@ -0,0 +1,70 @@
#include "global.h"
#include "DialogDriver.h"
#include "Foreach.h"
#include "RageLog.h"
map<istring, CreateDialogDriverFn> *RegisterDialogDriver::g_pRegistrees;
RegisterDialogDriver::RegisterDialogDriver( const istring &sName, CreateDialogDriverFn pfn )
{
if( g_pRegistrees == NULL )
g_pRegistrees = new map<istring, CreateDialogDriverFn>;
ASSERT( g_pRegistrees->find(sName) == g_pRegistrees->end() );
(*g_pRegistrees)[sName] = pfn;
}
REGISTER_DIALOG_DRIVER_CLASS( Null );
DialogDriver *DialogDriver::Create()
{
RString sDrivers = "win32,macosx,null";
vector<RString> asDriversToTry;
split( sDrivers, ",", asDriversToTry, true );
ASSERT( asDriversToTry.size() != 0 );
FOREACH_CONST( RString, asDriversToTry, Driver )
{
map<istring, CreateDialogDriverFn>::const_iterator iter = RegisterDialogDriver::g_pRegistrees->find( istring(*Driver) );
if( iter == RegisterDialogDriver::g_pRegistrees->end() )
continue;
DialogDriver *pRet = (iter->second)();
DEBUG_ASSERT( pRet );
const RString sError = pRet->Init();
if( sError.empty() )
return pRet;
if( LOG )
LOG->Info( "Couldn't load driver %s: %s", Driver->c_str(), sError.c_str() );
SAFE_DELETE( pRet );
}
return NULL;
}
/*
* (c) 2002-2006 Glenn Maynard, Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+57
View File
@@ -0,0 +1,57 @@
#ifndef DIALOG_BOX_DRIVER_H
#define DIALOG_BOX_DRIVER_H
#include "Dialog.h"
#include "RageUtil.h"
class DialogDriver
{
public:
static DialogDriver *Create();
virtual void Error( RString sMessage, RString sID ) { printf("Error: %s\n", sMessage.c_str()); }
virtual void OK( RString sMessage, RString sID ) {}
virtual Dialog::Result AbortRetryIgnore( RString sMessage, RString sID ) { return Dialog::ignore; }
virtual Dialog::Result AbortRetry( RString sMessage, RString sID ) { return Dialog::abort; }
virtual RString Init() { return RString(); }
virtual ~DialogDriver() { }
};
class DialogDriver_Null : public DialogDriver { };
typedef DialogDriver *(*CreateDialogDriverFn)();
struct RegisterDialogDriver
{
static map<istring, CreateDialogDriverFn> *g_pRegistrees;
RegisterDialogDriver( const istring &sName, CreateDialogDriverFn pfn );
};
#define REGISTER_DIALOG_DRIVER_CLASS( name ) \
static RegisterDialogDriver register_##name( #name, CreateClass<DialogDriver_##name, DialogDriver> )
#endif
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+127
View File
@@ -0,0 +1,127 @@
#include "global.h"
#include "RageUtil.h"
#include "DialogDriver_MacOSX.h"
#include "RageThreads.h"
#include "ProductInfo.h"
#include "InputFilter.h"
#include <CoreFoundation/CoreFoundation.h>
REGISTER_DIALOG_DRIVER_CLASS( MacOSX );
static CFOptionFlags ShowAlert( CFOptionFlags flags, const RString& sMessage, CFStringRef OK,
CFStringRef alt = NULL, CFStringRef other = NULL)
{
CFOptionFlags result;
CFStringRef text = CFStringCreateWithCString( NULL, sMessage, kCFStringEncodingUTF8 );
if( text == NULL )
{
RString error = ssprintf( "CFString for dialog string \"%s\" could not be created.", sMessage.c_str() );
WARN( error );
DEBUG_ASSERT_M( false, error );
return kCFUserNotificationDefaultResponse; // Is this better than displaying an "unknown error" message?
}
CFUserNotificationDisplayAlert( 0.0, flags, NULL, NULL, NULL, CFSTR(PRODUCT_FAMILY),
text, OK, alt, other, &result );
CFRelease( text );
// Flush all input that's accumulated while the dialog box was up.
if( INPUTFILTER )
{
vector<InputEvent> dummy;
INPUTFILTER->Reset();
INPUTFILTER->GetInputEvents( dummy );
}
return result;
}
#define LSTRING(b,x) CFBundleCopyLocalizedString( (b), CFSTR(x), NULL, CFSTR("Localizable") )
void DialogDriver_MacOSX::OK( RString sMessage, RString sID )
{
CFBundleRef bundle = CFBundleGetMainBundle();
CFStringRef sDSA = LSTRING( bundle, "Don't show again" );
CFOptionFlags result = ShowAlert( kCFUserNotificationNoteAlertLevel, sMessage, CFSTR("OK"), sDSA );
CFRelease( sDSA );
if( result == kCFUserNotificationAlternateResponse )
Dialog::IgnoreMessage( sID );
}
void DialogDriver_MacOSX::Error( RString sError, RString sID )
{
ShowAlert( kCFUserNotificationStopAlertLevel, sError, CFSTR("OK") );
}
Dialog::Result DialogDriver_MacOSX::AbortRetryIgnore( RString sMessage, RString sID )
{
CFBundleRef bundle = CFBundleGetMainBundle();
CFStringRef sIgnore = LSTRING( bundle, "Ignore" );
CFStringRef sRetry = LSTRING( bundle, "Retry" );
CFStringRef sAbort = LSTRING( bundle, "Abort" );
CFOptionFlags result = ShowAlert( kCFUserNotificationNoteAlertLevel, sMessage, sIgnore, sRetry, sAbort );
CFRelease( sIgnore );
CFRelease( sRetry );
CFRelease( sAbort );
switch( result )
{
case kCFUserNotificationDefaultResponse:
Dialog::IgnoreMessage( sID );
return Dialog::ignore;
case kCFUserNotificationAlternateResponse:
return Dialog::retry;
case kCFUserNotificationOtherResponse:
case kCFUserNotificationCancelResponse:
return Dialog::abort;
default:
FAIL_M( ssprintf("Invalid response: %d.", int(result)) );
}
}
Dialog::Result DialogDriver_MacOSX::AbortRetry( RString sMessage, RString sID )
{
CFBundleRef bundle = CFBundleGetMainBundle();
CFStringRef sRetry = LSTRING( bundle, "Retry" );
CFStringRef sAbort = LSTRING( bundle, "Abort" );
CFOptionFlags result = ShowAlert( kCFUserNotificationNoteAlertLevel, sMessage, sRetry, sAbort );
CFRelease( sRetry );
CFRelease( sAbort );
switch( result )
{
case kCFUserNotificationDefaultResponse:
case kCFUserNotificationCancelResponse:
return Dialog::abort;
case kCFUserNotificationAlternateResponse:
return Dialog::retry;
default:
FAIL_M( ssprintf("Invalid response: %d.", int(result)) );
}
}
/*
* (c) 2003-2006 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+40
View File
@@ -0,0 +1,40 @@
#ifndef DIALOG_BOX_DRIVER_MACOSX_H
#define DIALOG_BOX_DRIVER_MACOSX_H
#include "DialogDriver.h"
class DialogDriver_MacOSX: public DialogDriver
{
public:
void Error( RString sError, RString sID );
void OK( RString sMessage, RString sID );
Dialog::Result AbortRetryIgnore( RString sMessage, RString sID );
Dialog::Result AbortRetry( RString sMessage, RString sID );
};
#endif
/*
* (c) 2003-2004 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+265
View File
@@ -0,0 +1,265 @@
#include "global.h"
#include "DialogDriver_Win32.h"
#include "RageUtil.h"
#if !defined(SMPACKAGE)
#include "LocalizedString.h"
#endif
#include "ThemeManager.h"
#include "ProductInfo.h"
#include "archutils/win32/AppInstance.h"
#include "archutils/win32/ErrorStrings.h"
#include "archutils/win32/GotoURL.h"
#include "archutils/win32/RestartProgram.h"
#include "archutils/Win32/SpecialDirs.h"
#if !defined(SMPACKAGE)
#include "archutils/win32/WindowsResources.h"
#include "archutils/win32/GraphicsWindow.h"
#endif
#include "archutils/win32/DialogUtil.h"
#if defined(SMPACKAGE)
int __stdcall AfxMessageBox(LPCTSTR lpszText, UINT nType, UINT nIDHelp);
#endif
REGISTER_DIALOG_DRIVER_CLASS( Win32 );
static bool g_bHush;
static RString g_sMessage;
static bool g_bAllowHush;
#if !defined(SMPACKAGE)
static BOOL CALLBACK OKWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_INITDIALOG:
{
// Disable the parent window, like a modal MessageBox does.
EnableWindow( GetParent(hWnd), FALSE );
DialogUtil::LocalizeDialogAndContents( hWnd );
// Hide or display "Don't show this message."
g_bHush = false;
HWND hHushButton = GetDlgItem( hWnd, IDC_HUSH );
int iStyle = GetWindowLong( hHushButton, GWL_STYLE );
if( g_bAllowHush )
iStyle |= WS_VISIBLE;
else
iStyle &= ~WS_VISIBLE;
SetWindowLong( hHushButton, GWL_STYLE, iStyle );
// Set static text.
RString sMessage = g_sMessage;
sMessage.Replace( "\n", "\r\n" );
SetWindowText( GetDlgItem(hWnd, IDC_MESSAGE), sMessage );
// Focus is on any of the controls in the dialog by default.
// I'm not sure why. Set focus to the button manually. -Chris
SetFocus( GetDlgItem(hWnd, IDOK) );
}
break;
case WM_DESTROY:
// Re-enable the parent window.
EnableWindow( GetParent(hWnd), TRUE );
break;
case WM_COMMAND:
switch( LOWORD(wParam) )
{
case IDOK:
g_bHush = !!IsDlgButtonChecked( hWnd, IDC_HUSH );
/* fall through */
case IDCANCEL:
EndDialog( hWnd, 0 );
break;
}
}
return FALSE;
}
#endif
#if !defined(SMPACKAGE)
static HWND GetHwnd()
{
return GraphicsWindow::GetHwnd();
}
#endif
#if !defined(SMPACKAGE)
static LocalizedString ERROR_WINDOW_TITLE("Dialog-Prompt", "Error");
static RString GetWindowTitle()
{
RString s = ERROR_WINDOW_TITLE.GetValue();
return s;
}
#endif
void DialogDriver_Win32::OK( RString sMessage, RString sID )
{
g_bAllowHush = sID != "";
g_sMessage = sMessage;
AppInstance handle;
#if !defined(SMPACKAGE)
DialogBox( handle.Get(), MAKEINTRESOURCE(IDD_OK), ::GetHwnd(), OKWndProc );
#else
::AfxMessageBox( ConvertUTF8ToACP(sMessage).c_str(), MB_OK, 0 );
#endif
if( g_bAllowHush && g_bHush )
Dialog::IgnoreMessage( sID );
}
#if !defined(SMPACKAGE)
static RString g_sErrorString;
static BOOL CALLBACK ErrorWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_INITDIALOG:
{
DialogUtil::SetHeaderFont( hWnd, IDC_STATIC_HEADER_TEXT );
// Set static text
RString sMessage = g_sErrorString;
sMessage.Replace( "\n", "\r\n" );
SetWindowText( GetDlgItem(hWnd, IDC_EDIT_ERROR), sMessage );
}
break;
case WM_COMMAND:
switch( LOWORD(wParam) )
{
case IDC_BUTTON_VIEW_LOG:
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory( &si, sizeof(si) );
RString sAppDataDir = SpecialDirs::GetAppDataDir();
RString sCommand = "notepad \"" + sAppDataDir + PRODUCT_ID + "/Logs/log.txt\"";
CreateProcess(
NULL, // pointer to name of executable module
sCommand.GetBuffer(), // pointer to command line string
NULL, // process security attributes
NULL, // thread security attributes
false, // handle inheritance flag
0, // creation flags
NULL, // pointer to new environment block
NULL, // pointer to current directory name
&si, // pointer to STARTUPINFO
&pi // pointer to PROCESS_INFORMATION
);
}
break;
case IDC_BUTTON_REPORT:
GotoURL( REPORT_BUG_URL );
break;
case IDC_BUTTON_RESTART:
Win32RestartProgram();
/* not reached */
ASSERT( 0 );
EndDialog( hWnd, 0 );
break;
case IDOK:
EndDialog( hWnd, 0 );
break;
}
break;
case WM_CTLCOLORSTATIC:
{
HDC hdc = (HDC)wParam;
HWND hwndStatic = (HWND)lParam;
HBRUSH hbr = NULL;
// TODO: Change any attributes of the DC here
switch( GetDlgCtrlID(hwndStatic) )
{
case IDC_STATIC_HEADER_TEXT:
case IDC_STATIC_ICON:
hbr = (HBRUSH)::GetStockObject(WHITE_BRUSH);
SetBkMode( hdc, OPAQUE );
SetBkColor( hdc, RGB(255,255,255) );
break;
}
// TODO: Return a different brush if the default is not desired
return (BOOL)hbr;
}
}
return FALSE;
}
#endif
void DialogDriver_Win32::Error( RString sError, RString sID )
{
#if !defined(SMPACKAGE)
g_sErrorString = sError;
// throw up a pretty error dialog
AppInstance handle;
DialogBox( handle.Get(), MAKEINTRESOURCE(IDD_ERROR_DIALOG), NULL, ErrorWndProc );
#else
::AfxMessageBox( ConvertUTF8ToACP(sError).c_str(), MB_OK, 0 );
#endif
}
Dialog::Result DialogDriver_Win32::AbortRetryIgnore( RString sMessage, RString ID )
{
int iRet = 0;
#if !defined(SMPACKAGE)
iRet = ::MessageBox(::GetHwnd(), ConvertUTF8ToACP(sMessage).c_str(), ConvertUTF8ToACP(::GetWindowTitle()).c_str(), MB_ABORTRETRYIGNORE|MB_DEFBUTTON3 );
#else
iRet = ::AfxMessageBox( ConvertUTF8ToACP(sMessage).c_str(), MB_ABORTRETRYIGNORE|MB_DEFBUTTON3, 0 );
#endif
switch( iRet )
{
case IDABORT: return Dialog::abort;
case IDRETRY: return Dialog::retry;
default: ASSERT(0);
case IDIGNORE: return Dialog::ignore;
}
}
Dialog::Result DialogDriver_Win32::AbortRetry( RString sMessage, RString sID )
{
int iRet = 0;
#if !defined(SMPACKAGE)
iRet = ::MessageBox(::GetHwnd(), ConvertUTF8ToACP(sMessage).c_str(), ConvertUTF8ToACP(::GetWindowTitle()).c_str(), MB_RETRYCANCEL);
#else
iRet = ::AfxMessageBox( ConvertUTF8ToACP(sMessage).c_str(), MB_RETRYCANCEL, 0 );
#endif
switch( iRet )
{
case IDRETRY: return Dialog::retry;
default: ASSERT(0);
case IDCANCEL: return Dialog::abort;
}
}
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+40
View File
@@ -0,0 +1,40 @@
#ifndef DIALOG_BOX_DRIVER_WIN32_H
#define DIALOG_BOX_DRIVER_WIN32_H
#include "DialogDriver.h"
class DialogDriver_Win32: public DialogDriver
{
public:
void Error( RString sMessage, RString sID );
void OK( RString sMessage, RString sID );
Dialog::Result AbortRetryIgnore( RString sMessage, RString sID );
Dialog::Result AbortRetry( RString sMessage, RString sID );
};
#endif
/*
* (c) 2003-2004 Glenn Maynard, Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+219
View File
@@ -0,0 +1,219 @@
#include "global.h"
#include "InputFilter.h"
#include "RageUtil.h"
#include "InputHandler.h"
#include "RageLog.h"
#include "LocalizedString.h"
#include "arch/arch_default.h"
#include "InputHandler_MonkeyKeyboard.h"
#include "Foreach.h"
void InputHandler::UpdateTimer()
{
m_LastUpdate.Touch();
m_iInputsSinceUpdate = 0;
}
void InputHandler::ButtonPressed( DeviceInput di )
{
if( di.ts.IsZero() )
{
di.ts = m_LastUpdate.Half();
++m_iInputsSinceUpdate;
}
INPUTFILTER->ButtonPressed( di );
if( m_iInputsSinceUpdate >= 1000 )
{
/*
* We haven't received an update in a long time, so warn about it. We expect to receive
* input events before the first UpdateTimer call only on the first update. Leave
* m_iInputsSinceUpdate where it is, so we only warn once. Only updates that didn't provide
* a timestamp are counted; if the driver provides its own timestamps, UpdateTimer is
* optional.
*/
LOG->Warn( "InputHandler::ButtonPressed: Driver sent many updates without calling UpdateTimer" );
FAIL_M("x");
}
}
wchar_t InputHandler::DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers )
{
wchar_t c = L'\0';
switch( button )
{
default:
if( button < 127 )
c = (wchar_t) button;
else if( button >= KEY_KP_C0 && button <= KEY_KP_C9 )
c =(wchar_t) (button - KEY_KP_C0) + '0';
break;
case KEY_KP_SLASH: c = L'/'; break;
case KEY_KP_ASTERISK: c = L'*'; break;
case KEY_KP_HYPHEN: c = L'-'; break;
case KEY_KP_PLUS: c = L'+'; break;
case KEY_KP_PERIOD: c = L'.'; break;
case KEY_KP_EQUAL: c = L'='; break;
}
// Handle some default US keyboard modifiers for derived InputHandlers that
// don't implement DeviceButtonToChar.
if( bUseCurrentKeyModifiers )
{
bool bHoldingShift =
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT)) ||
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT));
bool bHoldingCtrl =
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL)) ||
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL));
if( bHoldingShift && !bHoldingCtrl )
{
MakeUpper( &c, 1 );
switch( c )
{
case L'`': c = L'~'; break;
case L'1': c = L'!'; break;
case L'2': c = L'@'; break;
case L'3': c = L'#'; break;
case L'4': c = L'$'; break;
case L'5': c = L'%'; break;
case L'6': c = L'^'; break;
case L'7': c = L'&'; break;
case L'8': c = L'*'; break;
case L'9': c = L'('; break;
case L'0': c = L')'; break;
case L'-': c = L'_'; break;
case L'=': c = L'+'; break;
case L'[': c = L'{'; break;
case L']': c = L'}'; break;
case L'\'': c = L'"'; break;
case L'\\': c = L'|'; break;
case L';': c = L':'; break;
case L',': c = L'<'; break;
case L'.': c = L'>'; break;
case L'/': c = L'?'; break;
}
}
}
return c;
}
static LocalizedString HOME ( "DeviceButton", "Home" );
static LocalizedString END ( "DeviceButton", "End" );
static LocalizedString UP ( "DeviceButton", "Up" );
static LocalizedString DOWN ( "DeviceButton", "Down" );
static LocalizedString SPACE ( "DeviceButton", "Space" );
static LocalizedString SHIFT ( "DeviceButton", "Shift" );
static LocalizedString CTRL ( "DeviceButton", "Ctrl" );
static LocalizedString ALT ( "DeviceButton", "Alt" );
static LocalizedString INSERT ( "DeviceButton", "Insert" );
static LocalizedString DEL ( "DeviceButton", "Delete" );
static LocalizedString PGUP ( "DeviceButton", "PgUp" );
static LocalizedString PGDN ( "DeviceButton", "PgDn" );
static LocalizedString BACKSLASH ( "DeviceButton", "Backslash" );
RString InputHandler::GetDeviceSpecificInputString( const DeviceInput &di )
{
if( di.device == InputDevice_Invalid )
return RString();
if( di.device == DEVICE_KEYBOARD )
{
wchar_t c = DeviceButtonToChar( di.button, false );
if( c && c != L' ' ) // Don't show "Key " for space.
return InputDeviceToString( di.device ) + " " + Capitalize( WStringToRString(wstring()+c) );
}
RString s = DeviceButtonToString( di.button );
if( di.device != DEVICE_KEYBOARD )
s = InputDeviceToString( di.device ) + " " + s;
return s;
}
RString InputHandler::GetLocalizedInputString( const DeviceInput &di )
{
switch( di.button )
{
case KEY_HOME: return HOME.GetValue();
case KEY_END: return END.GetValue();
case KEY_UP: return UP.GetValue();
case KEY_DOWN: return DOWN.GetValue();
case KEY_SPACE: return SPACE.GetValue();
case KEY_LSHIFT: case KEY_RSHIFT: return SHIFT.GetValue();
case KEY_LCTRL: case KEY_RCTRL: return CTRL.GetValue();
case KEY_LALT: case KEY_RALT: return ALT.GetValue();
case KEY_INSERT: return INSERT.GetValue();
case KEY_DEL: return DEL.GetValue();
case KEY_PGUP: return PGUP.GetValue();
case KEY_PGDN: return PGDN.GetValue();
case KEY_BACKSLASH: return BACKSLASH.GetValue();
default:
wchar_t c = DeviceButtonToChar( di.button, false );
if( c && c != L' ' ) // Don't show "Key " for space.
return Capitalize( WStringToRString(wstring()+c) );
return DeviceButtonToString( di.button );
}
}
DriverList InputHandler::m_pDriverList;
static LocalizedString INPUT_HANDLERS_EMPTY( "Arch", "Input Handlers cannot be empty." );
void InputHandler::Create( const RString &drivers_, vector<InputHandler *> &Add )
{
const RString drivers = drivers_.empty()? RString(DEFAULT_INPUT_DRIVER_LIST):drivers_;
vector<RString> DriversToTry;
split( drivers, ",", DriversToTry, true );
if( DriversToTry.empty() )
RageException::Throw( "%s", INPUT_HANDLERS_EMPTY.GetValue().c_str() );
FOREACH_CONST( RString, DriversToTry, s )
{
RageDriver *pDriver = InputHandler::m_pDriverList.Create( *s );
if( pDriver == NULL )
{
LOG->Trace( "Unknown Input Handler name: %s", s->c_str() );
continue;
}
InputHandler *ret = dynamic_cast<InputHandler *>( pDriver );
DEBUG_ASSERT( ret );
Add.push_back( ret );
}
// Always add
Add.push_back( new InputHandler_MonkeyKeyboard );
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+105
View File
@@ -0,0 +1,105 @@
#ifndef INPUT_HANDLER_H
#define INPUT_HANDLER_H
/*
* This is a simple class to handle special input devices. Update()
* will be called during the input update; the derived class should
* send appropriate events to InputHandler.
*
* Note that, if the underlying device is capable of it, you're free to
* start a blocking thread; just store inputs in your class and send them
* off in a batch on the next Update. This gets much more accurate timestamps;
* we get events more quickly and timestamp them, instead of having a rough timing
* granularity due to the framerate.
*
* Send input events for a specific type of device. Only one driver
* for a given set of InputDevice types should be loaded for a given
* arch. For example, any number of drivers may produce DEVICE_PUMPn
* events, but only one may be loaded at a time. (This will be inconvenient
* if, for example, we have two completely distinct methods of getting
* input for the same device; we have no method to allocate device numbers.
* We don't need this now; I'll write it if it becomes needed.)
*/
#include "RageInputDevice.h" // for InputDevice
#include "arch/RageDriver.h"
class InputHandler: public RageDriver
{
public:
static void Create( const RString &sDrivers, vector<InputHandler *> &apAdd );
static DriverList m_pDriverList;
InputHandler() { m_iInputsSinceUpdate = 0; }
virtual ~InputHandler() { }
virtual void Update() { }
virtual bool DevicesChanged() { return false; }
virtual void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut ) = 0;
// Override to return a pretty string that's specific to the controller type.
virtual RString GetDeviceSpecificInputString( const DeviceInput &di );
virtual RString GetLocalizedInputString( const DeviceInput &di );
virtual wchar_t DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers );
// Override to find out whether the controller is currently plugged in.
// Not all InputHandlers will support this. Not applicable to all InputHandlers.
virtual InputDeviceState GetInputDeviceState( InputDevice id ) { return InputDeviceState_Connected; }
/* In Windows, some devices need to be recreated if we recreate our main window.
* Override this if you need to do that. */
virtual void WindowReset() { }
protected:
/* Convenience function: Call this to queue a received event. This may be called
* in a thread.
*
* Important detail: If the timestamp, di.ts, is zero, then it is assumed that
* this is not a threaded event handler. In that case, input is being polled,
* and the actual time the button was pressed may be any time since the last
* poll. In this case, ButtonPressed will pretend the button was pressed at
* the midpoint since the last update, which will smooth out the error.
*
* Note that timestamps are set to the current time by default, so for this to
* happen, you need to explicitly call di.ts.SetZero().
*
* If the timestamp is set, it'll be left alone. */
void ButtonPressed( DeviceInput di );
/* Call this at the end of polling input. */
void UpdateTimer();
private:
RageTimer m_LastUpdate;
int m_iInputsSinceUpdate;
};
#define REGISTER_INPUT_HANDLER_CLASS2( name, x ) \
static RegisterRageDriver register_##name( &InputHandler::m_pDriverList, #name, CreateClass<InputHandler_##x, RageDriver> )
#define REGISTER_INPUT_HANDLER_CLASS( name ) REGISTER_INPUT_HANDLER_CLASS2( name, name )
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,752 @@
#include "global.h"
#include "InputHandler_DirectInput.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "archutils/Win32/AppInstance.h"
#include "archutils/Win32/DirectXHelpers.h"
#include "archutils/Win32/ErrorStrings.h"
#include "archutils/Win32/GraphicsWindow.h"
#include "archutils/Win32/RegistryAccess.h"
#include "InputFilter.h"
#include "PrefsManager.h"
#include "Foreach.h"
#include "InputHandler_DirectInputHelper.h"
REGISTER_INPUT_HANDLER_CLASS2( DirectInput, DInput );
static vector<DIDevice> Devices;
/* Number of joysticks found: */
static int g_iNumJoysticks;
static BOOL CALLBACK EnumDevicesCallback( const DIDEVICEINSTANCE *pdidInstance, void *pContext )
{
DIDevice device;
switch( pdidInstance->dwDevType & 0xFF )
{
case DIDEVTYPE_KEYBOARD: device.type = device.KEYBOARD; break;
case DIDEVTYPE_JOYSTICK: device.type = device.JOYSTICK; break;
default: return DIENUM_CONTINUE;
}
device.JoystickInst = *pdidInstance;
switch( device.type )
{
case device.JOYSTICK:
if( g_iNumJoysticks == NUM_JOYSTICKS )
return DIENUM_CONTINUE;
device.dev = enum_add2( DEVICE_JOY1, g_iNumJoysticks );
g_iNumJoysticks++;
break;
case device.KEYBOARD:
device.dev = DEVICE_KEYBOARD;
break;
}
Devices.push_back(device);
return DIENUM_CONTINUE;
}
static void CheckForDirectInputDebugMode()
{
int iVal;
if( RegistryAccess::GetRegValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\DirectInput", "emulation", iVal) )
{
if( iVal & 0x8 )
LOG->Warn("DirectInput keyboard debug mode appears to be enabled. This reduces\n"
"input timing accuracy significantly. Disabling this is strongly recommended." );
}
}
static BOOL CALLBACK CountDevicesCallback( const DIDEVICEINSTANCE *pdidInstance, void *pContext )
{
(*(int*)pContext)++;
return DIENUM_CONTINUE;
}
static int GetNumHidDevices()
{
int i = 0;
RegistryAccess::GetRegValue( "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\HidUsb\\Enum", "Count", i, false ); // don't warn on error
return i;
}
static int GetNumJoysticksSlow()
{
int iCount = 0;
HRESULT hr = g_dinput->EnumDevices( DIDEVTYPE_JOYSTICK, CountDevicesCallback, &iCount, DIEDFL_ATTACHEDONLY );
if( hr != DI_OK )
{
LOG->Warn( hr_ssprintf(hr, "g_dinput->EnumDevices") );
}
return iCount;
}
InputHandler_DInput::InputHandler_DInput()
{
LOG->Trace( "InputHandler_DInput::InputHandler_DInput()" );
CheckForDirectInputDebugMode();
m_bShutdown = false;
g_iNumJoysticks = 0;
AppInstance inst;
HRESULT hr = DirectInputCreate(inst.Get(), DIRECTINPUT_VERSION, &g_dinput, NULL);
if( hr != DI_OK )
RageException::Throw( hr_ssprintf(hr, "InputHandler_DInput: DirectInputCreate") );
LOG->Trace( "InputHandler_DInput: IDirectInput::EnumDevices(DIDEVTYPE_KEYBOARD)" );
hr = g_dinput->EnumDevices( DIDEVTYPE_KEYBOARD, EnumDevicesCallback, NULL, DIEDFL_ATTACHEDONLY );
if( hr != DI_OK )
RageException::Throw( hr_ssprintf(hr, "InputHandler_DInput: IDirectInput::EnumDevices") );
LOG->Trace( "InputHandler_DInput: IDirectInput::EnumDevices(DIDEVTYPE_JOYSTICK)" );
hr = g_dinput->EnumDevices( DIDEVTYPE_JOYSTICK, EnumDevicesCallback, NULL, DIEDFL_ATTACHEDONLY );
if( hr != DI_OK )
RageException::Throw( hr_ssprintf(hr, "InputHandler_DInput: IDirectInput::EnumDevices") );
for( unsigned i = 0; i < Devices.size(); ++i )
{
if( Devices[i].Open() )
continue;
Devices.erase( Devices.begin() + i );
i--;
continue;
}
LOG->Info( "Found %u DirectInput devices:", Devices.size() );
for( unsigned i = 0; i < Devices.size(); ++i )
{
LOG->Info( " %d: '%s' axes: %d, hats: %d, buttons: %d (%s)",
i,
Devices[i].m_sName.c_str(),
Devices[i].axes,
Devices[i].hats,
Devices[i].buttons,
Devices[i].buffered? "buffered": "unbuffered" );
}
m_iLastSeenNumHidDevices = GetNumHidDevices();
m_iNumTimesLeftToPollForJoysticksChanged = 0;
m_iLastSeenNumJoysticks = GetNumJoysticksSlow();
StartThread();
}
void InputHandler_DInput::StartThread()
{
ASSERT( !m_InputThread.IsCreated() );
if( PREFSMAN->m_bThreadedInput )
{
m_InputThread.SetName( "DirectInput thread" );
m_InputThread.Create( InputThread_Start, this );
}
}
void InputHandler_DInput::ShutdownThread()
{
m_bShutdown = true;
if( m_InputThread.IsCreated() )
{
LOG->Trace( "Shutting down DirectInput thread ..." );
m_InputThread.Wait();
LOG->Trace( "DirectInput thread shut down." );
}
m_bShutdown = false;
}
InputHandler_DInput::~InputHandler_DInput()
{
ShutdownThread();
for( unsigned i = 0; i < Devices.size(); ++i )
Devices[i].Close();
Devices.clear();
g_dinput->Release();
g_dinput = NULL;
}
void InputHandler_DInput::WindowReset()
{
/* We need to reopen keyboards. */
ShutdownThread();
for( unsigned i = 0; i < Devices.size(); ++i )
{
if( Devices[i].type != Devices[i].KEYBOARD )
continue;
Devices[i].Close();
/* We lose buffered inputs here, so we need to clear all pressed keys. */
INPUTFILTER->ResetDevice( Devices[i].dev );
bool ret = Devices[i].Open();
/* Reopening it should succeed. */
ASSERT( ret );
}
StartThread();
}
#define HAT_UP_MASK 1
#define HAT_DOWN_MASK 2
#define HAT_LEFT_MASK 4
#define HAT_RIGHT_MASK 8
static int TranslatePOV(DWORD value)
{
const int HAT_VALS[] =
{
HAT_UP_MASK,
HAT_UP_MASK | HAT_RIGHT_MASK,
HAT_RIGHT_MASK,
HAT_DOWN_MASK | HAT_RIGHT_MASK,
HAT_DOWN_MASK,
HAT_DOWN_MASK | HAT_LEFT_MASK,
HAT_LEFT_MASK,
HAT_UP_MASK | HAT_LEFT_MASK
};
if( LOWORD(value) == 0xFFFF )
return 0;
/* Round the value up: */
value += 4500 / 2;
value %= 36000;
value /= 4500;
if( value >= 8 )
return 0; /* shouldn't happen */
return HAT_VALS[value];
}
static HRESULT GetDeviceState( LPDIRECTINPUTDEVICE2 dev, int size, void *ptr )
{
HRESULT hr = dev->GetDeviceState( size, ptr );
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
{
hr = dev->Acquire();
if( hr != DI_OK )
{
LOG->Trace( hr_ssprintf(hr, "?") );
return hr;
}
hr = dev->GetDeviceState( size, ptr );
}
return hr;
}
/* This doesn't take a timestamp; instead, we let InputHandler::ButtonPressed figure
* it out. Be sure to call InputHandler::Update() between each poll. */
void InputHandler_DInput::UpdatePolled( DIDevice &device, const RageTimer &tm )
{
switch( device.type )
{
default:
ASSERT(0);
case device.KEYBOARD:
{
unsigned char keys[256];
HRESULT hr = GetDeviceState( device.Device, 256, keys );
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
return;
if( hr != DI_OK )
{
LOG->MapLog( "UpdatePolled", hr_ssprintf(hr, "Failures on polled keyboard update") );
return;
}
for( int k = 0; k < 256; ++k )
{
const DeviceButton key = (DeviceButton) device.Inputs[k].num;
ButtonPressed( DeviceInput(device.dev, key, !!(keys[k] & 0x80) ) );
}
}
break;
case device.JOYSTICK:
{
DIJOYSTATE state;
HRESULT hr = GetDeviceState(device.Device, sizeof(state), &state);
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
return;
/* Set each known axis, button and POV. */
for( unsigned i = 0; i < device.Inputs.size(); ++i )
{
const input_t &in = device.Inputs[i];
const InputDevice dev = device.dev;
switch(in.type)
{
case in.BUTTON:
{
DeviceInput di( dev, enum_add2(JOY_BUTTON_1, in.num), !!state.rgbButtons[in.ofs - DIJOFS_BUTTON0], tm );
ButtonPressed( di );
break;
}
case in.AXIS:
{
DeviceButton neg = DeviceButton_Invalid, pos = DeviceButton_Invalid;
int val = 0;
switch( in.ofs )
{
case DIJOFS_X: neg = JOY_LEFT; pos = JOY_RIGHT;
val = state.lX;
break;
case DIJOFS_Y: neg = JOY_UP; pos = JOY_DOWN;
val = state.lY;
break;
case DIJOFS_Z: neg = JOY_Z_UP; pos = JOY_Z_DOWN;
val = state.lZ;
break;
case DIJOFS_RX: neg = JOY_ROT_LEFT; pos = JOY_ROT_RIGHT;
val = state.lRx;
break;
case DIJOFS_RY: neg = JOY_ROT_UP; pos = JOY_ROT_DOWN;
val = state.lRy;
break;
case DIJOFS_RZ: neg = JOY_ROT_Z_UP; pos = JOY_ROT_Z_DOWN;
val = state.lRz;
break;
case DIJOFS_SLIDER(0):
neg = JOY_AUX_1; pos = JOY_AUX_2;
val = state.rglSlider[0];
break;
case DIJOFS_SLIDER(1):
neg = JOY_AUX_3; pos = JOY_AUX_4;
val = state.rglSlider[1];
break;
default: LOG->MapLog( "unknown input",
"Controller '%s' is returning an unknown joystick offset, %i",
device.m_sName.c_str(), in.ofs );
continue;
}
if( neg != DeviceButton_Invalid )
{
float l = SCALE( int(val), 0.0f, 100.0f, 0.0f, 1.0f );
ButtonPressed( DeviceInput(dev, neg, max(-l,0), tm) );
ButtonPressed( DeviceInput(dev, pos, max(+l,0), tm) );
}
break;
}
case in.HAT:
if( in.num == 0 )
{
const int pos = TranslatePOV( state.rgdwPOV[in.ofs - DIJOFS_POV(0)] );
ButtonPressed( DeviceInput(dev, JOY_HAT_UP, !!(pos & HAT_UP_MASK), tm) );
ButtonPressed( DeviceInput(dev, JOY_HAT_DOWN, !!(pos & HAT_DOWN_MASK), tm) );
ButtonPressed( DeviceInput(dev, JOY_HAT_LEFT, !!(pos & HAT_LEFT_MASK), tm) );
ButtonPressed( DeviceInput(dev, JOY_HAT_RIGHT, !!(pos & HAT_RIGHT_MASK), tm) );
}
break;
}
}
}
break;
}
}
void InputHandler_DInput::UpdateBuffered( DIDevice &device, const RageTimer &tm )
{
DWORD numevents;
DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
numevents = INPUT_QSIZE;
HRESULT hr = device.Device->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), evtbuf, &numevents, 0 );
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
{
INPUTFILTER->ResetDevice( device.dev );
return;
}
if( hr != DI_OK )
{
LOG->Trace( hr_ssprintf(hr, "UpdateBuffered: IDirectInputDevice2_GetDeviceData") );
return;
}
if( GetForegroundWindow() != GraphicsWindow::GetHwnd() )
{
/* Discard input when not focused, and release all keys. */
INPUTFILTER->ResetDevice( device.dev );
return;
}
for( int i = 0; i < (int) numevents; ++i )
{
for(unsigned j = 0; j < device.Inputs.size(); ++j)
{
const input_t &in = device.Inputs[j];
const InputDevice dev = device.dev;
if( evtbuf[i].dwOfs != in.ofs )
continue;
switch( in.type )
{
case in.KEY:
/*
switch( in.num )
{
// "Joystick with Keyboard" hack
case 115: //s
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_UP, !!(evtbuf[i].dwData & 0x80), tm) );
break;
case 120: //x
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_DOWN, !!(evtbuf[i].dwData & 0x80), tm) );
break;
case 122: //z
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_LEFT, !!(evtbuf[i].dwData & 0x80), tm) );
break;
case 99: //c
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_RIGHT, !!(evtbuf[i].dwData & 0x80), tm) );
break;
case 100: //d
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_BUTTON_1, !!(evtbuf[i].dwData & 0x80), tm) );
break;
case 101: //e
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_BUTTON_2, !!(evtbuf[i].dwData & 0x80), tm) );
break;
default:
*/
ButtonPressed( DeviceInput(dev, (DeviceButton) in.num, !!(evtbuf[i].dwData & 0x80), tm) );
/*
break;
}
*/
break;
case in.BUTTON:
ButtonPressed( DeviceInput(dev, enum_add2(JOY_BUTTON_1, in.num), !!evtbuf[i].dwData, tm) );
break;
case in.AXIS:
{
DeviceButton up = DeviceButton_Invalid, down = DeviceButton_Invalid;
switch(in.ofs)
{
case DIJOFS_X: up = JOY_LEFT; down = JOY_RIGHT; break;
case DIJOFS_Y: up = JOY_UP; down = JOY_DOWN; break;
case DIJOFS_Z: up = JOY_Z_UP; down = JOY_Z_DOWN; break;
case DIJOFS_RX: up = JOY_ROT_UP; down = JOY_ROT_DOWN; break;
case DIJOFS_RY: up = JOY_ROT_LEFT; down = JOY_ROT_RIGHT; break;
case DIJOFS_RZ: up = JOY_ROT_Z_UP; down = JOY_ROT_Z_DOWN; break;
case DIJOFS_SLIDER(0): up = JOY_AUX_1; down = JOY_AUX_2; break;
case DIJOFS_SLIDER(1): up = JOY_AUX_3; down = JOY_AUX_4; break;
default: LOG->MapLog( "unknown input",
"Controller '%s' is returning an unknown joystick offset, %i",
device.m_sName.c_str(), in.ofs );
continue;
}
float l = SCALE( int(evtbuf[i].dwData), 0.0f, 100.0f, 0.0f, 1.0f );
ButtonPressed( DeviceInput(dev, up, max(-l,0), tm) );
ButtonPressed( DeviceInput(dev, down, max(+l,0), tm) );
break;
}
case in.HAT:
{
const int pos = TranslatePOV( evtbuf[i].dwData );
ButtonPressed( DeviceInput(dev, JOY_HAT_UP, !!(pos & HAT_UP_MASK), tm) );
ButtonPressed( DeviceInput(dev, JOY_HAT_DOWN, !!(pos & HAT_DOWN_MASK), tm) );
ButtonPressed( DeviceInput(dev, JOY_HAT_LEFT, !!(pos & HAT_LEFT_MASK), tm) );
ButtonPressed( DeviceInput(dev, JOY_HAT_RIGHT, !!(pos & HAT_RIGHT_MASK), tm) );
}
}
}
}
}
void InputHandler_DInput::PollAndAcquireDevices( bool bBuffered )
{
for( unsigned i = 0; i < Devices.size(); ++i )
{
if( Devices[i].buffered != bBuffered )
continue;
HRESULT hr = Devices[i].Device->Poll();
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
{
INPUTFILTER->ResetDevice( Devices[i].dev );
/* This will fail with "access denied" on the keyboard if we don't
* have focus. */
hr = Devices[i].Device->Acquire();
if( hr != DI_OK )
continue;
Devices[i].Device->Poll();
}
}
}
void InputHandler_DInput::Update()
{
/* Handle polled devices. Handle buffered, too, if there's no input thread to do it. */
PollAndAcquireDevices( false );
if( !m_InputThread.IsCreated() )
PollAndAcquireDevices( true );
for( unsigned i = 0; i < Devices.size(); ++i )
{
if( !Devices[i].buffered )
{
UpdatePolled( Devices[i], RageZeroTimer );
}
else if( !m_InputThread.IsCreated() )
{
/* If we have an input thread, it'll handle buffered devices. */
UpdateBuffered( Devices[i], RageZeroTimer );
}
}
InputHandler::UpdateTimer();
}
const float POLL_FOR_JOYSTICK_CHANGES_LENGTH_SECONDS = 15.0f;
const float POLL_FOR_JOYSTICK_CHANGES_EVERY_SECONDS = 0.25f;
bool InputHandler_DInput::DevicesChanged()
{
//
// GetNumJoysticksSlow() blocks DirectInput for a while even if called from a
// different thread, so we can't poll with it.
// GetNumHidDevices() is fast, but sometimes the DirectInput joysticks haven't updated by
// the time the HID registry value changes.
// So, poll using GetNumHidDevices(). When that changes, poll using GetNumJoysticksSlow()
// for a little while to give DirectInput time to catch up. On this XP machine, it takes
// 2-10 DirectInput polls (0.5-2.5 seconds) to catch a newly installed device after the
// registry value changes, and catches non-new plugged/unplugged devices on the first
// DirectInputPoll.
// Note that this "poll for N seconds" method will not work if the Add New Hardware wizard
// halts device installation to wait for a driver. Most of the joysticks people would
// want to use don't prompt for a driver though and the wizard adds them pretty quickly.
//
int iOldNumHidDevices = m_iLastSeenNumHidDevices;
m_iLastSeenNumHidDevices = GetNumHidDevices();
if( iOldNumHidDevices != m_iLastSeenNumHidDevices )
{
LOG->Warn( "HID devices changes" );
m_iNumTimesLeftToPollForJoysticksChanged = (int)(POLL_FOR_JOYSTICK_CHANGES_LENGTH_SECONDS / POLL_FOR_JOYSTICK_CHANGES_EVERY_SECONDS);
}
if( m_iNumTimesLeftToPollForJoysticksChanged > 0 )
{
static RageTimer timerPollJoysticks;
if( timerPollJoysticks.Ago() >= POLL_FOR_JOYSTICK_CHANGES_EVERY_SECONDS )
{
m_iNumTimesLeftToPollForJoysticksChanged--;
timerPollJoysticks.Touch();
LOG->Warn( "polling for joystick changes" );
int iOldNumJoysticks = m_iLastSeenNumJoysticks;
m_iLastSeenNumJoysticks = GetNumJoysticksSlow();
if( iOldNumJoysticks != m_iLastSeenNumJoysticks )
{
LOG->Warn( "joysticks changed" );
m_iNumTimesLeftToPollForJoysticksChanged = 0;
return true;
}
}
}
return false;
}
void InputHandler_DInput::InputThreadMain()
{
if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST))
LOG->Warn(werr_ssprintf(GetLastError(), "Failed to set DirectInput thread priority"));
/* Enable priority boosting. */
SetThreadPriorityBoost( GetCurrentThread(), FALSE );
vector<DIDevice*> BufferedDevices;
HANDLE Handle = CreateEvent( NULL, FALSE, FALSE, NULL );
for( unsigned i = 0; i < Devices.size(); ++i )
{
if( !Devices[i].buffered )
continue;
BufferedDevices.push_back( &Devices[i] );
Devices[i].Device->Unacquire();
HRESULT hr = Devices[i].Device->SetEventNotification( Handle );
if( FAILED(hr) )
LOG->Warn( "IDirectInputDevice2_SetEventNotification failed on %i", i );
Devices[i].Device->Acquire();
}
while( !m_bShutdown )
{
CHECKPOINT;
if( BufferedDevices.size() )
{
/* Update buffered devices. */
PollAndAcquireDevices( true );
int ret = WaitForSingleObjectEx( Handle, 50, true );
if( ret == -1 )
{
LOG->Trace( werr_ssprintf(GetLastError(), "WaitForSingleObjectEx failed") );
continue;
}
/* Update devices even if no event was triggered, since this also checks for focus
* loss. */
RageTimer now;
for( unsigned i = 0; i < BufferedDevices.size(); ++i )
UpdateBuffered( *BufferedDevices[i], now );
}
CHECKPOINT;
/* If we have no buffered devices, we didn't delay at WaitForMultipleObjectsEx. */
if( BufferedDevices.size() == 0 )
usleep( 50000 );
CHECKPOINT;
}
CHECKPOINT;
for( unsigned i = 0; i < Devices.size(); ++i )
{
if( !Devices[i].buffered )
continue;
Devices[i].Device->Unacquire();
Devices[i].Device->SetEventNotification( NULL );
}
CloseHandle(Handle);
}
void InputHandler_DInput::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
{
for( unsigned i=0; i < Devices.size(); ++i )
vDevicesOut.push_back( InputDeviceInfo(Devices[i].dev, Devices[i].m_sName) );
}
static wchar_t ScancodeAndKeysToChar( DWORD scancode, unsigned char keys[256] )
{
static HKL layout = GetKeyboardLayout(0); // 0 == current thread
UINT vk = MapVirtualKeyEx( scancode, 1, layout );
static bool bInitialized = false;
typedef int (WINAPI TOUNICODEEX)( IN UINT wVirtKey, IN UINT wScanCode, IN CONST BYTE *lpKeyState, OUT LPWSTR pwszBuff, IN int cchBuff, IN UINT wFlags, IN HKL dwhkl );
static TOUNICODEEX *pToUnicodeEx;
if( !bInitialized )
{
bInitialized = true;
HMODULE hModule = GetModuleHandle( "user32.dll" );
pToUnicodeEx = (TOUNICODEEX *) GetProcAddress( hModule, "ToUnicodeEx" );
}
unsigned short result[2]; // ToAscii writes a max of 2 chars
ZERO( result );
if( pToUnicodeEx != NULL )
{
int iNum = pToUnicodeEx( vk, scancode, keys, (LPWSTR)result, 2, 0, layout );
if( iNum == 1 )
return result[0];
}
else
{
int iNum = ToAsciiEx( vk, scancode, keys, result, 0, layout );
// iNum == 2 will happen only for dead keys. See MSDN for ToAsciiEx.
if( iNum == 1 )
{
RString s = RString()+(char)result[0];
return ConvertCodepageToWString( s, CP_ACP )[0];
}
}
return '\0';
}
wchar_t InputHandler_DInput::DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers )
{
// ToAsciiEx maps these keys to a character. They shouldn't be mapped to any character.
switch( button )
{
case KEY_ESC:
case KEY_TAB:
case KEY_ENTER:
case KEY_BACK:
return '\0';
}
FOREACH_CONST( DIDevice, Devices, d )
{
if( d->type != DIDevice::KEYBOARD )
continue;
FOREACH_CONST( input_t, d->Inputs, i )
{
if( button != i->num )
continue;
unsigned char keys[256];
ZERO( keys );
if( bUseCurrentKeyModifiers )
GetKeyboardState(keys);
wchar_t c = ScancodeAndKeysToChar( i->ofs, keys );
if( c )
return c;
}
}
return InputHandler::DeviceButtonToChar( button, bUseCurrentKeyModifiers );
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,63 @@
#ifndef INPUTHANDLER_DIRECTINPUT_H
#define INPUTHANDLER_DIRECTINPUT_H
#include "InputHandler.h"
#include "RageThreads.h"
struct DIDevice;
class InputHandler_DInput: public InputHandler
{
public:
InputHandler_DInput();
~InputHandler_DInput();
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
wchar_t DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers );
void Update();
bool DevicesChanged();
void WindowReset();
private:
RageThread m_InputThread;
bool m_bShutdown;
int m_iLastSeenNumHidDevices; // This changes first on plug/unplug
int m_iNumTimesLeftToPollForJoysticksChanged;
int m_iLastSeenNumJoysticks; // This changes sometime after m_iLastSeenNumHidDevices
void UpdatePolled( DIDevice &device, const RageTimer &tm );
void UpdateBuffered( DIDevice &device, const RageTimer &tm );
void PollAndAcquireDevices( bool bBuffered );
static int InputThread_Start( void *p ) { ((InputHandler_DInput *) p)->InputThreadMain(); return 0; }
void InputThreadMain();
void StartThread();
void ShutdownThread();
};
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,329 @@
#include "global.h"
#include "InputHandler_DirectInputHelper.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "archutils/Win32/DirectXHelpers.h"
#include "archutils/Win32/ErrorStrings.h"
#include "archutils/Win32/GraphicsWindow.h"
#if defined(_MSC_VER)
#pragma comment(lib, "dinput.lib")
#if defined(_WINDOWS)
#pragma comment(lib, "dxguid.lib")
#endif
#endif
LPDIRECTINPUT g_dinput = NULL;
static int ConvertScancodeToKey( int scancode );
static BOOL CALLBACK DIJoystick_EnumDevObjectsProc(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data);
DIDevice::DIDevice()
{
buttons = axes = hats = 0;
dev = InputDevice_Invalid;
buffered = true;
memset(&JoystickInst, 0, sizeof(JoystickInst));
Device = NULL;
}
bool DIDevice::Open()
{
m_sName = ConvertACPToUTF8( JoystickInst.tszProductName );
LOG->Trace( "Opening device '%s'", m_sName.c_str() );
buffered = true;
LPDIRECTINPUTDEVICE tmpdevice;
HRESULT hr = g_dinput->CreateDevice( JoystickInst.guidInstance, &tmpdevice, NULL );
if ( hr != DI_OK )
{
LOG->Info( hr_ssprintf(hr, "OpenDevice: IDirectInput_CreateDevice") );
return false;
}
hr = tmpdevice->QueryInterface( IID_IDirectInputDevice2, (LPVOID *) &Device );
tmpdevice->Release();
if ( hr != DI_OK )
{
LOG->Info( hr_ssprintf(hr, "OpenDevice(%s): IDirectInputDevice::QueryInterface", m_sName.c_str()) );
return false;
}
int coop = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
if( type == KEYBOARD )
coop = DISCL_NONEXCLUSIVE | DISCL_FOREGROUND;
hr = Device->SetCooperativeLevel( GraphicsWindow::GetHwnd(), coop );
if ( hr != DI_OK )
{
LOG->Info( hr_ssprintf(hr, "OpenDevice(%s): IDirectInputDevice2::SetCooperativeLevel", m_sName.c_str()) );
return false;
}
hr = Device->SetDataFormat( type == JOYSTICK? &c_dfDIJoystick: &c_dfDIKeyboard );
if ( hr != DI_OK )
{
LOG->Info( hr_ssprintf(hr, "OpenDevice(%s): IDirectInputDevice2::SetDataFormat", m_sName.c_str()) );
return false;
}
switch( type )
{
case JOYSTICK:
Device->EnumObjects( DIJoystick_EnumDevObjectsProc, this, DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
break;
case KEYBOARD:
/* Always 256-button. */
for( int b = 0; b < 256; ++b )
{
input_t in;
in.type = in.KEY;
in.num = ConvertScancodeToKey(b);
in.ofs = b;
buttons++;
Inputs.push_back(in);
}
break;
}
{
DIPROPDWORD dipdw;
memset(&dipdw, 0, sizeof(dipdw));
dipdw.diph.dwSize = sizeof(dipdw);
dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = INPUT_QSIZE;
hr = Device->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph );
if ( hr == DI_POLLEDDEVICE )
{
/* This device doesn't support buffering, so we're forced
* to use less reliable polling. */
buffered = false;
}
else if ( hr != DI_OK )
{
LOG->Info( hr_ssprintf(hr, "OpenDevice(%s): IDirectInputDevice2::SetProperty", m_sName.c_str()) );
return false;
}
}
return true;
}
void DIDevice::Close()
{
/* Don't try to close a device that isn't open. */
ASSERT( Device != NULL );
Device->Unacquire();
Device->Release();
Device = NULL;
buttons = axes = hats = NULL;
Inputs.clear();
}
static BOOL CALLBACK DIJoystick_EnumDevObjectsProc(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data)
{
DIDevice *device = (DIDevice *) data;
HRESULT hr;
input_t in;
const int SupportedMask = DIDFT_BUTTON | DIDFT_POV | DIDFT_AXIS;
if(!(dev->dwType & SupportedMask))
return DIENUM_CONTINUE; /* unsupported */
in.ofs = dev->dwOfs;
if(dev->dwType & DIDFT_BUTTON) {
if( device->buttons == 24 )
return DIENUM_CONTINUE; /* too many buttons */
in.type = in.BUTTON;
in.num = device->buttons;
device->buttons++;
} else if(dev->dwType & DIDFT_POV) {
in.type = in.HAT;
in.num = device->hats;
device->hats++;
} else { /* dev->dwType & DIDFT_AXIS */
DIPROPRANGE diprg;
DIPROPDWORD dilong;
in.type = in.AXIS;
in.num = device->axes;
diprg.diph.dwSize = sizeof(diprg);
diprg.diph.dwHeaderSize = sizeof(diprg.diph);
diprg.diph.dwObj = dev->dwOfs;
diprg.diph.dwHow = DIPH_BYOFFSET;
diprg.lMin = -100;
diprg.lMax = 100;
hr = device->Device->SetProperty( DIPROP_RANGE, &diprg.diph );
if ( hr != DI_OK )
return DIENUM_CONTINUE; /* don't use this axis */
/* Set dead zone to 0. */
dilong.diph.dwSize = sizeof(dilong);
dilong.diph.dwHeaderSize = sizeof(dilong.diph);
dilong.diph.dwObj = dev->dwOfs;
dilong.diph.dwHow = DIPH_BYOFFSET;
dilong.dwData = 0;
hr = device->Device->SetProperty( DIPROP_DEADZONE, &dilong.diph );
if ( hr != DI_OK )
return DIENUM_CONTINUE; /* don't use this axis */
device->axes++;
}
device->Inputs.push_back(in);
return DIENUM_CONTINUE;
}
static int ConvertScancodeToKey( int scancode )
{
switch(scancode)
{
case DIK_ESCAPE: return KEY_ESC;
case DIK_1: return KEY_C1;
case DIK_2: return KEY_C2;
case DIK_3: return KEY_C3;
case DIK_4: return KEY_C4;
case DIK_5: return KEY_C5;
case DIK_6: return KEY_C6;
case DIK_7: return KEY_C7;
case DIK_8: return KEY_C8;
case DIK_9: return KEY_C9;
case DIK_0: return KEY_C0;
case DIK_MINUS: return KEY_HYPHEN;
case DIK_EQUALS: return KEY_EQUAL;
case DIK_BACK: return KEY_BACK;
case DIK_TAB: return KEY_TAB;
case DIK_Q: return KEY_Cq;
case DIK_W: return KEY_Cw;
case DIK_E: return KEY_Ce;
case DIK_R: return KEY_Cr;
case DIK_T: return KEY_Ct;
case DIK_Y: return KEY_Cy;
case DIK_U: return KEY_Cu;
case DIK_I: return KEY_Ci;
case DIK_O: return KEY_Co;
case DIK_P: return KEY_Cp;
case DIK_LBRACKET: return KEY_LBRACKET;
case DIK_RBRACKET: return KEY_RBRACKET;
case DIK_RETURN: return KEY_ENTER;
case DIK_LCONTROL: return KEY_LCTRL;
case DIK_A: return KEY_Ca;
case DIK_S: return KEY_Cs;
case DIK_D: return KEY_Cd;
case DIK_F: return KEY_Cf;
case DIK_G: return KEY_Cg;
case DIK_H: return KEY_Ch;
case DIK_J: return KEY_Cj;
case DIK_K: return KEY_Ck;
case DIK_L: return KEY_Cl;
case DIK_SEMICOLON: return KEY_SEMICOLON;
case DIK_APOSTROPHE: return KEY_SQUOTE;
case DIK_GRAVE: return KEY_ACCENT;
case DIK_LSHIFT: return KEY_LSHIFT;
case DIK_BACKSLASH: return KEY_BACKSLASH;
case DIK_OEM_102: return KEY_BACKSLASH;
case DIK_Z: return KEY_Cz;
case DIK_X: return KEY_Cx;
case DIK_C: return KEY_Cc;
case DIK_V: return KEY_Cv;
case DIK_B: return KEY_Cb;
case DIK_N: return KEY_Cn;
case DIK_M: return KEY_Cm;
case DIK_COMMA: return KEY_COMMA;
case DIK_PERIOD: return KEY_PERIOD;
case DIK_SLASH: return KEY_SLASH;
case DIK_RSHIFT: return KEY_RSHIFT;
case DIK_MULTIPLY: return KEY_KP_ASTERISK;
case DIK_LMENU: return KEY_LALT;
case DIK_SPACE: return KEY_SPACE;
case DIK_CAPITAL: return KEY_CAPSLOCK;
case DIK_F1: return KEY_F1;
case DIK_F2: return KEY_F2;
case DIK_F3: return KEY_F3;
case DIK_F4: return KEY_F4;
case DIK_F5: return KEY_F5;
case DIK_F6: return KEY_F6;
case DIK_F7: return KEY_F7;
case DIK_F8: return KEY_F8;
case DIK_F9: return KEY_F9;
case DIK_F10: return KEY_F10;
case DIK_NUMLOCK: return KEY_NUMLOCK;
case DIK_SCROLL: return KEY_SCRLLOCK;
case DIK_NUMPAD7: return KEY_KP_C7;
case DIK_NUMPAD8: return KEY_KP_C8;
case DIK_NUMPAD9: return KEY_KP_C9;
case DIK_SUBTRACT: return KEY_KP_HYPHEN;
case DIK_NUMPAD4: return KEY_KP_C4;
case DIK_NUMPAD5: return KEY_KP_C5;
case DIK_NUMPAD6: return KEY_KP_C6;
case DIK_ADD: return KEY_KP_PLUS;
case DIK_NUMPAD1: return KEY_KP_C1;
case DIK_NUMPAD2: return KEY_KP_C2;
case DIK_NUMPAD3: return KEY_KP_C3;
case DIK_NUMPAD0: return KEY_KP_C0;
case DIK_DECIMAL: return KEY_KP_PERIOD;
case DIK_F11: return KEY_F11;
case DIK_F12: return KEY_F12;
case DIK_F13: return KEY_F13;
case DIK_F14: return KEY_F14;
case DIK_F15: return KEY_F15;
case DIK_NUMPADEQUALS: return KEY_KP_EQUAL;
case DIK_NUMPADENTER: return KEY_KP_ENTER;
case DIK_RCONTROL: return KEY_RCTRL;
case DIK_DIVIDE: return KEY_KP_SLASH;
case DIK_SYSRQ: return KEY_PRTSC;
case DIK_RMENU: return KEY_RALT;
case DIK_PAUSE: return KEY_PAUSE;
case DIK_HOME: return KEY_HOME;
case DIK_UP: return KEY_UP;
case DIK_PRIOR: return KEY_PGUP;
case DIK_LEFT: return KEY_LEFT;
case DIK_RIGHT: return KEY_RIGHT;
case DIK_END: return KEY_END;
case DIK_DOWN: return KEY_DOWN;
case DIK_NEXT: return KEY_PGDN;
case DIK_INSERT: return KEY_INSERT;
case DIK_DELETE: return KEY_DEL;
case DIK_LWIN: return KEY_LMETA;
case DIK_RWIN: return KEY_RMETA;
case DIK_APPS: return KEY_MENU;
default: return '?';
};
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,67 @@
#ifndef INPUTHANDLER_DIRECTINPUT_HELPER_H
#define INPUTHANDLER_DIRECTINPUT_HELPER_H
#include "InputFilter.h"
#define DIRECTINPUT_VERSION 0x0500
#include <dinput.h>
extern LPDIRECTINPUT g_dinput;
#define INPUT_QSIZE 32
typedef struct input_t
{
/* DirectInput offset for this input type: */
DWORD ofs;
/* Button, axis or hat: */
enum Type { KEY, BUTTON, AXIS, HAT } type;
int num;
} input_t;
struct DIDevice
{
DIDEVICEINSTANCE JoystickInst;
LPDIRECTINPUTDEVICE2 Device;
RString m_sName;
enum { KEYBOARD, JOYSTICK } type;
bool buffered;
int buttons, axes, hats;
vector<input_t> Inputs;
InputDevice dev;
DIDevice();
bool Open();
void Close();
};
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,446 @@
#include "global.h"
#include "InputHandler_Linux_Event.h"
#include "RageLog.h"
#include "RageUtil.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <linux/input.h>
REGISTER_INPUT_HANDLER_CLASS2( Event, Linux_Event );
bool InputHandler_Linux_Event::m_bFoundAnyJoysticks;
static RString BustypeToString( int iBus )
{
switch( iBus )
{
// case BUS_ADB:
// case BUS_AMIGA: return "amiga input";
case BUS_BLUETOOTH: return "Bluetooth";
case BUS_GAMEPORT: return "gameport";
// case BUS_HIL:
// case BUS_HOST:
// case BUS_I2C:
case BUS_I8042: return "keyboard";
case BUS_ISA: return "ISA";
case BUS_ISAPNP: return "ISAPNP";
case BUS_PARPORT: return "parallel port";
case BUS_PCI: return "PCI";
case BUS_RS232: return "serial port";
case BUS_USB: return "USB";
case BUS_XTKBD: return "XT keyboard";
default: return ssprintf("unknown bus %x", iBus);
}
}
struct EventDevice
{
EventDevice();
~EventDevice();
bool Open( RString sFile, InputDevice dev );
bool IsOpen() const { return m_iFD != -1; }
void Close()
{
if( m_iFD != -1 )
close( m_iFD );
m_iFD = -1;
}
int m_iFD;
RString m_sPath;
RString m_sName;
InputDevice m_Dev;
int aiAbsMin[ABS_MAX];
int aiAbsMax[ABS_MAX];
DeviceButton aiAbsMappingHigh[ABS_MAX];
DeviceButton aiAbsMappingLow[ABS_MAX];
};
static vector<EventDevice *> g_apEventDevices;
/* Return true if the numbered event device exists. sysfs may not always be
* there; return false if we don't know. */
static bool EventDeviceExists( int iNum )
{
RString sDir = ssprintf( "/sys/class" );
struct stat st;
if( stat(sDir, &st) == -1 )
return true;
RString sFile = ssprintf( "/sys/class/input/event%i", iNum );
return stat(sFile, &st) == 0;
}
static bool BitIsSet( const uint8_t *pArray, uint32_t iBit )
{
return !!(pArray[iBit/8] & (1<<(iBit%8)));
}
EventDevice::EventDevice()
{
m_iFD = -1;
}
bool EventDevice::Open( RString sFile, InputDevice dev )
{
m_sPath = sFile;
m_Dev = dev;
m_iFD = open( sFile, O_RDWR );
if( m_iFD == -1 )
{
if( errno == ENODEV )
return false;
if( !EventDeviceExists(m_iFD) )
return false;
LOG->Warn( "Error opening %s: %s", sFile.c_str(), strerror(errno) );
return false;
}
static bool bLogged = false;
if( !bLogged )
{
bLogged = true;
int iVersion;
if( ioctl(m_iFD, EVIOCGVERSION, &iVersion) == -1 )
LOG->Warn( "ioctl(EVIOCGVERSION): %s", strerror(errno) );
else
LOG->Info( "Event driver: v%i.%i.%i", (iVersion >> 16) & 0xFF, (iVersion >> 8) & 0xFF, iVersion & 0xFF );
}
char szName[1024];
if( ioctl(m_iFD, EVIOCGNAME(sizeof(szName)), szName) == -1 )
{
LOG->Warn( "ioctl(EVIOCGNAME): %s", strerror(errno) );
m_sName = "(unknown)";
}
else
{
m_sName = szName;
}
input_id DevInfo;
if( ioctl(m_iFD, EVIOCGID, &DevInfo) == -1 )
{
LOG->Warn( "ioctl(EVIOCGID): %s", strerror(errno) );
}
else
{
LOG->Info( "Input device: %s: %s device, ID %04x:%04x, version %x: %s", sFile.c_str(),
BustypeToString(DevInfo.bustype).c_str(), DevInfo.vendor, DevInfo.product,
DevInfo.version, m_sName.c_str() );
}
uint8_t iABSMask[ABS_MAX/8 + 1];
memset( iABSMask, 0, sizeof(iABSMask) );
if( ioctl(m_iFD, EVIOCGBIT(EV_ABS, sizeof(iABSMask)), iABSMask) < 0 )
LOG->Warn( "ioctl(EVIOCGBIT(EV_ABS)): %s", strerror(errno) );
if( !BitIsSet(iABSMask, ABS_X) && !BitIsSet(iABSMask, ABS_THROTTLE) && !BitIsSet(iABSMask, ABS_WHEEL) )
{
LOG->Info( " Not a joystick; ignored" );
Close();
return false;
}
uint8_t iKeyMask[KEY_MAX/8 + 1];
memset( iKeyMask, 0, sizeof(iKeyMask) );
if( ioctl(m_iFD, EVIOCGBIT(EV_KEY, sizeof(iKeyMask)), iKeyMask) < 0 )
LOG->Warn( "ioctl(EVIOCGBIT(EV_KEY)): %s", strerror(errno) );
uint8_t iEventTypes[EV_MAX/8];
memset( iEventTypes, 0, sizeof(iEventTypes) );
if( ioctl(m_iFD, EVIOCGBIT(0, EV_MAX), iEventTypes) == -1 )
LOG->Warn( "ioctl(EV_MAX): %s", strerror(errno) );
{
vector<RString> setEventTypes;
if( BitIsSet(iEventTypes, EV_SYN) ) setEventTypes.push_back( "syn" );
if( BitIsSet(iEventTypes, EV_KEY) ) setEventTypes.push_back( "key" );
if( BitIsSet(iEventTypes, EV_REL) ) setEventTypes.push_back( "rel" );
if( BitIsSet(iEventTypes, EV_ABS) ) setEventTypes.push_back( "abs" );
if( BitIsSet(iEventTypes, EV_MSC) ) setEventTypes.push_back( "misc" );
if( BitIsSet(iEventTypes, EV_SW) ) setEventTypes.push_back( "sw" );
if( BitIsSet(iEventTypes, EV_LED) ) setEventTypes.push_back( "led" );
if( BitIsSet(iEventTypes, EV_SND) ) setEventTypes.push_back( "snd" );
if( BitIsSet(iEventTypes, EV_REP) ) setEventTypes.push_back( "rep" );
if( BitIsSet(iEventTypes, EV_FF) ) setEventTypes.push_back( "ff" );
if( BitIsSet(iEventTypes, EV_PWR) ) setEventTypes.push_back( "pwr" );
if( BitIsSet(iEventTypes, EV_FF_STATUS) ) setEventTypes.push_back( "ff_status" );
LOG->Info( " Event types: %s", join(", ", setEventTypes).c_str() );
}
int iTotalKeys = 0;
for( int i = 0; i < KEY_MAX; ++i )
{
if( !BitIsSet(iKeyMask, i) )
continue;
++iTotalKeys;
}
int iTotalAxes = 0;
const DeviceButton iExtraAxes[] = { JOY_LEFT_2, JOY_UP_2, JOY_AUX_1, JOY_AUX_3 };
int iNextExtraAxis = 0;
for( int i = 0; i < ABS_MAX; ++i )
{
if( !BitIsSet(iABSMask, i) )
continue;
struct input_absinfo absinfo;
if( ioctl(m_iFD, EVIOCGABS(i), &absinfo) < 0 )
{
LOG->Warn( "ioctl(EVIOCGABS): %s", strerror(errno) );
continue;
}
//LOG->Info( " Axis %i: min: %i; max: %i; fuzz: %i; flat: %i",
// i, absinfo.minimum, absinfo.maximum, absinfo.fuzz, absinfo.flat );
aiAbsMin[i] = absinfo.minimum;
aiAbsMax[i] = absinfo.maximum;
aiAbsMappingHigh[i] = enum_add2(JOY_RIGHT, 2*i);
aiAbsMappingLow[i] = enum_add2(JOY_LEFT, 2*i);
if( i == ABS_X )
{
aiAbsMappingHigh[i] = JOY_RIGHT;
aiAbsMappingLow[i] = JOY_LEFT;
}
else if( i == ABS_Y )
{
aiAbsMappingHigh[i] = JOY_DOWN;
aiAbsMappingLow[i] = JOY_UP;
}
else if( i == ABS_Z )
{
aiAbsMappingHigh[i] = JOY_Z_DOWN;
aiAbsMappingLow[i] = JOY_Z_UP;
}
else if( i == ABS_RX )
{
aiAbsMappingHigh[i] = JOY_ROT_RIGHT;
aiAbsMappingLow[i] = JOY_ROT_LEFT;
}
else if( i == ABS_RY )
{
aiAbsMappingHigh[i] = JOY_ROT_DOWN;
aiAbsMappingLow[i] = JOY_ROT_UP;
}
else if( i == ABS_RZ )
{
aiAbsMappingHigh[i] = JOY_ROT_Z_DOWN;
aiAbsMappingLow[i] = JOY_ROT_Z_UP;
}
else if( i == ABS_HAT0X )
{
aiAbsMappingHigh[i] = JOY_HAT_RIGHT;
aiAbsMappingLow[i] = JOY_HAT_LEFT;
}
else if( i == ABS_HAT0Y )
{
aiAbsMappingHigh[i] = JOY_HAT_UP;
aiAbsMappingLow[i] = JOY_HAT_DOWN;
}
else
{
if( iNextExtraAxis < (int) ARRAYLEN(iExtraAxes) )
{
aiAbsMappingLow[i] = iExtraAxes[iNextExtraAxis];
aiAbsMappingHigh[i] = enum_add2( aiAbsMappingLow[i], 1 );
++iNextExtraAxis;
}
}
++iTotalAxes;
}
LOG->Info( " Total keys: %i; total axes: %i", iTotalKeys, iTotalAxes );
return true;
}
EventDevice::~EventDevice()
{
Close();
}
InputHandler_Linux_Event::InputHandler_Linux_Event()
{
if( InputHandler_Linux_Event::m_bFoundAnyJoysticks )
{
LOG->Trace( "InputHandler_Linux_Event disabled (joystick driver already loaded)" );
return;
}
/* Permission problems are likely. We want to warn about them only if there's actually
* an underlying device, but if we can't open the device, the only way we can tell if
* there'd be anything there is sysfs. That won't always be there. */
m_bFoundAnyJoysticks = false;
InputDevice NextDevice = DEVICE_JOY1;
for( int i = 0; i < 64; ++i )
{
RString sFile = ssprintf( "/dev/input/event%i", i );
g_apEventDevices.push_back( new EventDevice );
EventDevice *pDev = g_apEventDevices.back();
if( !pDev->Open(sFile, NextDevice) )
{
delete pDev;
g_apEventDevices.pop_back();
continue;
}
NextDevice = enum_add2(NextDevice, 1);
m_bFoundAnyJoysticks = true;
}
m_bShutdown = false;
if( m_bFoundAnyJoysticks )
{
m_InputThread.SetName( "Event input thread" );
m_InputThread.Create( InputThread_Start, this );
/* We loaded joysticks, so disable joydev. */
}
}
InputHandler_Linux_Event::~InputHandler_Linux_Event()
{
if( m_InputThread.IsCreated() )
{
m_bShutdown = true;
LOG->Trace( "Shutting down joystick thread ..." );
m_InputThread.Wait();
LOG->Trace( "Joystick thread shut down." );
}
for( int i = 0; i < (int) g_apEventDevices.size(); ++i )
delete g_apEventDevices[i];
g_apEventDevices.clear();
}
int InputHandler_Linux_Event::InputThread_Start( void *p )
{
((InputHandler_Linux_Event *) p)->InputThread();
return 0;
}
void InputHandler_Linux_Event::InputThread()
{
while( !m_bShutdown )
{
fd_set fdset;
FD_ZERO( &fdset );
int iMaxFD = -1;
for( int i = 0; i < (int) g_apEventDevices.size(); ++i )
{
int iFD = g_apEventDevices[i]->m_iFD;
if( !g_apEventDevices[i]->IsOpen() )
continue;
FD_SET( iFD, &fdset );
iMaxFD = max( iMaxFD, iFD );
}
if( iMaxFD == -1 )
break;
struct timeval zero = {0,100000};
if( select(iMaxFD+1, &fdset, NULL, NULL, &zero) <= 0 )
continue;
RageTimer now;
for( int i = 0; i < (int) g_apEventDevices.size(); ++i )
{
if( !g_apEventDevices[i]->IsOpen() )
continue;
if( !FD_ISSET(g_apEventDevices[i]->m_iFD, &fdset) )
continue;
input_event event;
int ret = read( g_apEventDevices[i]->m_iFD, &event, sizeof(event) );
if( ret == -1 )
{
LOG->Warn( "Error reading from %s: %s; disabled", g_apEventDevices[i]->m_sPath.c_str(), strerror(errno) );
g_apEventDevices[i]->Close();
continue;
}
if( ret != sizeof(event) )
{
LOG->Warn("Unexpected packet (size %i != %i) from joystick %i; disabled", ret, (int)sizeof(event), i);
g_apEventDevices[i]->Close();
continue;
}
switch (event.type) {
case EV_KEY: {
int iNum = event.code;
// In 2.6.11 using an EMS USB2, the event number for P1 Tri (the first button)
// is being reported as 32 instead of 0. Correct for this.
wrap( iNum, 32 ); // max number of joystick buttons. Make this a constant?
ButtonPressed( DeviceInput(g_apEventDevices[i]->m_Dev, enum_add2(JOY_BUTTON_1, iNum), event.value != 0, now) );
break;
}
case EV_ABS: {
ASSERT_M( event.code < ABS_MAX, ssprintf("%i", event.code) );
DeviceButton neg = g_apEventDevices[i]->aiAbsMappingLow[event.code];
DeviceButton pos = g_apEventDevices[i]->aiAbsMappingHigh[event.code];
float l = SCALE( int(event.value), (float) g_apEventDevices[i]->aiAbsMin[i], (float) g_apEventDevices[i]->aiAbsMax[i], -1.0f, 1.0f );
ButtonPressed( DeviceInput(g_apEventDevices[i]->m_Dev, neg, max(-l,0), now) );
ButtonPressed( DeviceInput(g_apEventDevices[i]->m_Dev, pos, max(+l,0), now) );
break;
}
}
}
}
InputHandler::UpdateTimer();
}
void InputHandler_Linux_Event::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
{
for( unsigned i = 0; i < g_apEventDevices.size(); ++i )
{
EventDevice *pDev = g_apEventDevices[i];
vDevicesOut.push_back( InputDeviceInfo(pDev->m_Dev, pDev->m_sName) );
}
}
/*
* (c) 2003-2008 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,55 @@
/* InputHandler_Linux_Event - evdev-based input driver */
#ifndef INPUT_HANDLER_LINUX_EVENT_H
#define INPUT_HANDLER_LINUX_EVENT_H
#include "InputHandler.h"
#include "RageThreads.h"
class InputHandler_Linux_Event: public InputHandler
{
public:
enum { NUM_JOYSTICKS = 4 };
InputHandler_Linux_Event();
~InputHandler_Linux_Event();
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
/* Shared with InputHandler_Linux_Joystick.cpp: */
static bool m_bFoundAnyJoysticks;
private:
static int InputThread_Start( void *p );
void InputThread();
RageThread m_InputThread;
bool m_bShutdown;
};
#define USE_INPUT_HANDLER_LINUX_JOYSTICK
#endif
/*
* (c) 2003-2008 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,226 @@
#include "global.h"
#include "InputHandler_Linux_Joystick.h"
#include "InputHandler_Linux_Event.h" // for m_bFoundAnyJoysticks
#include "RageLog.h"
#include "RageUtil.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <linux/joystick.h>
#include <set>
REGISTER_INPUT_HANDLER_CLASS2( Joystick, Linux_Joystick );
static const char *Paths[InputHandler_Linux_Joystick::NUM_JOYSTICKS] =
{
"/dev/js0",
"/dev/js1",
"/dev/input/js0",
"/dev/input/js1",
};
InputHandler_Linux_Joystick::InputHandler_Linux_Joystick()
{
LOG->Trace( "InputHandler_Linux_Joystick::InputHandler_Linux_Joystick" );
for(int i = 0; i < NUM_JOYSTICKS; ++i)
fds[i] = -1;
if( InputHandler_Linux_Event::m_bFoundAnyJoysticks )
{
LOG->Trace( "InputHandler_Linux_Joystick disabled (joystick driver already loaded)" );
return;
}
/* We check both eg. /dev/js0 and /dev/input/js0. If both exist, they're probably
* the same device; keep track of device IDs so we don't open the same joystick
* twice. */
set< pair<int,int> > devices;
bool bFoundAnyJoysticks = false;
for(int i = 0; i < NUM_JOYSTICKS; ++i)
{
struct stat st;
if( stat( Paths[i], &st ) == -1 )
{
if( errno != ENOENT )
LOG->Warn( "Couldn't stat %s: %s", Paths[i], strerror(errno) );
continue;
}
if( !S_ISCHR( st.st_mode ) )
{
LOG->Warn( "Ignoring %s: not a character device", Paths[i] );
continue;
}
pair<int,int> dev( major(st.st_rdev), minor(st.st_rdev) );
if( devices.find(dev) != devices.end() )
continue; /* dupe */
devices.insert( dev );
fds[i] = open( Paths[i], O_RDONLY );
if(fds[i] != -1)
{
char szName[1024];
ZERO( szName );
if( ioctl(fds[i], JSIOCGNAME(sizeof(szName)), szName) < 0 )
m_sDescription[i] = ssprintf( "Unknown joystick at %s", Paths[i] );
else
m_sDescription[i] = szName;
LOG->Info("Opened %s", Paths[i]);
bFoundAnyJoysticks = true;
}
}
m_bShutdown = false;
if( bFoundAnyJoysticks )
{
m_InputThread.SetName( "Joystick thread" );
m_InputThread.Create( InputThread_Start, this );
InputHandler_Linux_Event::m_bFoundAnyJoysticks = true;
}
}
InputHandler_Linux_Joystick::~InputHandler_Linux_Joystick()
{
if( m_InputThread.IsCreated() )
{
m_bShutdown = true;
LOG->Trace( "Shutting down joystick thread ..." );
m_InputThread.Wait();
LOG->Trace( "Joystick thread shut down." );
}
for(int i = 0; i < NUM_JOYSTICKS; ++i)
if(fds[i] != -1) close(fds[i]);
}
int InputHandler_Linux_Joystick::InputThread_Start( void *p )
{
((InputHandler_Linux_Joystick *) p)->InputThread();
return 0;
}
void InputHandler_Linux_Joystick::InputThread()
{
while( !m_bShutdown )
{
fd_set fdset;
FD_ZERO(&fdset);
int max_fd = -1;
for(int i = 0; i < NUM_JOYSTICKS; ++i)
{
if (fds[i] < 0)
continue;
FD_SET(fds[i], &fdset);
max_fd = max(max_fd, fds[i]);
}
if(max_fd == -1)
break;
struct timeval zero = {0,100000};
if( select(max_fd+1, &fdset, NULL, NULL, &zero) <= 0 )
continue;
RageTimer now;
printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n");
for(int i = 0; i < NUM_JOYSTICKS; ++i)
{
if( fds[i] == -1 )
continue;
if(!FD_ISSET(fds[i], &fdset))
continue;
js_event event;
int ret = read(fds[i], &event, sizeof(event));
if(ret != sizeof(event))
{
LOG->Warn("Unexpected packet (size %i != %i) from joystick %i; disabled", ret, (int)sizeof(event), i);
close(fds[i]);
fds[i] = -1;
continue;
}
InputDevice id = InputDevice(DEVICE_JOY1 + i);
event.type &= ~JS_EVENT_INIT;
switch (event.type) {
case JS_EVENT_BUTTON: {
int iNum = event.number;
// In 2.6.11 using an EMS USB2, the event number for P1 Tri (the first button)
// is being reported as 32 instead of 0. Correct for this.
wrap( iNum, 32 ); // max number of joystick buttons. Make this a constant?
ButtonPressed( DeviceInput(id, enum_add2(JOY_BUTTON_1, iNum), event.value, now) );
break;
}
case JS_EVENT_AXIS: {
DeviceButton neg = enum_add2(JOY_LEFT, 2*event.number);
DeviceButton pos = enum_add2(JOY_RIGHT, 2*event.number);
float l = SCALE( int(event.value), 0.0f, 32767, 0.0f, 1.0f );
ButtonPressed( DeviceInput(id, neg, max(-l,0), now) );
ButtonPressed( DeviceInput(id, pos, max(+l,0), now) );
break;
}
default:
LOG->Warn("Unexpected packet (type %i) from joystick %i; disabled", event.type, i);
close(fds[i]);
fds[i] = -1;
continue;
}
}
}
InputHandler::UpdateTimer();
}
void InputHandler_Linux_Joystick::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
{
for(int i = 0; i < NUM_JOYSTICKS; ++i)
{
if (fds[i] < 0)
continue;
vDevicesOut.push_back( InputDeviceInfo(InputDevice(DEVICE_JOY1+i), m_sDescription[i]) );
}
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,51 @@
#ifndef INPUT_HANDLER_LINUX_JOYSTICK_H
#define INPUT_HANDLER_LINUX_JOYSTICK_H 1
#include "InputHandler.h"
#include "RageThreads.h"
class InputHandler_Linux_Joystick: public InputHandler
{
public:
enum { NUM_JOYSTICKS = 4 };
InputHandler_Linux_Joystick();
~InputHandler_Linux_Joystick();
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
private:
static int InputThread_Start( void *p );
void InputThread();
int fds[NUM_JOYSTICKS];
RString m_sDescription[NUM_JOYSTICKS];
RageThread m_InputThread;
bool m_bShutdown;
};
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,221 @@
#include "global.h"
/* This handler is used for odd cases where we don't use SDL for input. */
#include "InputHandler_Linux_tty.h"
#include "InputHandler_Linux_tty_keys.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "RageException.h"
#include "archutils/Unix/SignalHandler.h"
#include "SDL_utils.h"
#include <errno.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/kd.h>
#include <linux/keyboard.h>
#include <termios.h>
REGISTER_INPUT_HANDLER_CLASS2( tty, Linux_tty );
/* Map from keys (ignoring shifts) to SDLK values. */
static int keys[NR_KEYS];
static termios saved_kbd_termios;
static int saved_kbd_mode;
/* This is normally a singleton. Keep track of it, so we can access it
* from our signal handler. */
static InputHandler_Linux_tty *handler = NULL;
void InputHandler_Linux_tty::OnCrash(int signo)
{
/* Make sure we delete the input handler if we crash, so we don't leave
* the terminal in raw mode. */
delete handler;
handler = NULL;
}
InputHandler_Linux_tty::InputHandler_Linux_tty()
{
fd = open("/dev/tty", O_RDWR);
if(fd == -1)
RageException::Throw("open(\"/dev/tty\"): %s", strerror(errno));
if (tcgetattr(fd, &saved_kbd_termios) == -1)
RageException::Throw("tcgetattr(%i) failed: %s", fd, strerror(errno));
termios keyboard_termios = saved_kbd_termios;
keyboard_termios.c_lflag &= ~(ICANON | ECHO | ISIG);
keyboard_termios.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
keyboard_termios.c_cc[VMIN] = 0;
keyboard_termios.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSAFLUSH, &keyboard_termios) == -1)
RageException::Throw("tcsetattr(%i, TCSAFLUSH) failed: %s", fd, strerror(errno));
if (ioctl(fd, KDGKBMODE, &saved_kbd_mode) == -1)
RageException::Throw("ioctl(%i, KDGKBMODE) failed: %s", fd, strerror(errno));
if (ioctl(fd, KDSKBMODE, K_MEDIUMRAW) == -1)
RageException::Throw("ioctl(%i, KDSKBMODE, K_MEDIUMRAW) failed: %s", fd, strerror(errno));
if (ioctl(fd, KDSETMODE, KD_GRAPHICS) == -1 )
RageException::Throw("ioctl(%i, KDSETMODE, KD_GRAPHICS) failed: %s", fd, strerror(errno));
memset(keys, 0, sizeof(keys));
for (int i = 0; i < NR_KEYS; ++i)
{
switch(i)
{
case SCANCODE_PRINTSCREEN: keys[i] = SDLK_PRINT; continue;
case SCANCODE_BREAK: keys[i] = SDLK_BREAK; continue;
case SCANCODE_BREAK_ALTERNATIVE: keys[i] = SDLK_PAUSE; continue;
case SCANCODE_LEFTSHIFT: keys[i] = SDLK_LSHIFT; continue;
case SCANCODE_RIGHTSHIFT: keys[i] = SDLK_RSHIFT; continue;
case SCANCODE_LEFTCONTROL: keys[i] = SDLK_LCTRL; continue;
case SCANCODE_RIGHTCONTROL: keys[i] = SDLK_RCTRL; continue;
case SCANCODE_RIGHTWIN: keys[i] = SDLK_RSUPER; continue;
case SCANCODE_LEFTWIN: keys[i] = SDLK_LSUPER; continue;
case 127: keys[i] = SDLK_MENU; continue;
}
kbentry entry;
entry.kb_table = 0;
entry.kb_index = i;
if (ioctl(fd, KDGKBENT, &entry))
continue; /* error */
const int kern_map = entry.kb_value;
switch(kern_map)
{
case K_ENTER: keys[i] = SDLK_RETURN; break;
case K_F1: keys[i] = SDLK_F1; break;
case K_F2: keys[i] = SDLK_F2; break;
case K_F3: keys[i] = SDLK_F3; break;
case K_F4: keys[i] = SDLK_F4; break;
case K_F5: keys[i] = SDLK_F5; break;
case K_F6: keys[i] = SDLK_F6; break;
case K_F7: keys[i] = SDLK_F7; break;
case K_F8: keys[i] = SDLK_F8; break;
case K_F9: keys[i] = SDLK_F9; break;
case K_F10: keys[i] = SDLK_F10; break;
case K_F11: keys[i] = SDLK_F11; break;
case K_F12: keys[i] = SDLK_F12; break;
case K_UP: keys[i] = SDLK_UP; break;
case K_DOWN: keys[i] = SDLK_DOWN; break;
case K_LEFT: keys[i] = SDLK_LEFT; break;
case K_RIGHT: keys[i] = SDLK_RIGHT; break;
case K_P0: keys[i] = SDLK_KP0; break;
case K_P1: keys[i] = SDLK_KP1; break;
case K_P2: keys[i] = SDLK_KP2; break;
case K_P3: keys[i] = SDLK_KP3; break;
case K_P4: keys[i] = SDLK_KP4; break;
case K_P5: keys[i] = SDLK_KP5; break;
case K_P6: keys[i] = SDLK_KP6; break;
case K_P7: keys[i] = SDLK_KP7; break;
case K_P8: keys[i] = SDLK_KP8; break;
case K_P9: keys[i] = SDLK_KP9; break;
case K_PPLUS: keys[i] = SDLK_KP_PLUS; break;
case K_PMINUS: keys[i] = SDLK_KP_MINUS; break;
case K_PSTAR: keys[i] = SDLK_KP_MULTIPLY; break;
case K_PSLASH: keys[i] = SDLK_KP_DIVIDE; break;
case K_PENTER: keys[i] = SDLK_KP_ENTER; break;
case K_PDOT: keys[i] = SDLK_KP_PERIOD; break;
case K_ALT: keys[i] = SDLK_LALT; break;
case K_ALTGR: keys[i] = SDLK_RALT; break;
case K_INSERT: keys[i] = SDLK_INSERT; break;
case K_REMOVE: keys[i] = SDLK_DELETE; break;
case K_PGUP: keys[i] = SDLK_PAGEUP; break;
case K_PGDN: keys[i] = SDLK_PAGEDOWN; break;
case K_FIND: keys[i] = SDLK_HOME; break;
case K_SELECT: keys[i] = SDLK_END; break;
case K_NUM: keys[i] = SDLK_NUMLOCK; break;
case K_CAPS: keys[i] = SDLK_CAPSLOCK; break;
case K_F13: keys[i] = SDLK_PRINT; break;
case K_HOLD: keys[i] = SDLK_SCROLLOCK; break;
case K_PAUSE: keys[i] = SDLK_PAUSE; break;
case 127: keys[i] = SDLK_BACKSPACE; break;
default: keys[i] = KVAL(kern_map);
}
}
handler = this;
SignalHandler::OnClose(OnCrash);
}
InputHandler_Linux_tty::~InputHandler_Linux_tty()
{
LOG->Trace("~InputHandler_Linux_tty");
ioctl(fd, KDSETMODE, KD_TEXT);
ioctl(fd, KDSKBMODE, saved_kbd_mode);
tcsetattr(fd, TCSAFLUSH, &saved_kbd_termios);
close(fd);
handler = NULL;
}
void InputHandler_Linux_tty::Update()
{
while (1)
{
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
struct timeval zero = {0,0};
if ( select(fd+1, &fdset, NULL, NULL, &zero) <= 0 )
return;
unsigned char keybuf[BUFSIZ];
SDL_keysym keysym;
int ret = read(fd, keybuf, BUFSIZ);
for ( int i=0; i < ret; ++i ) {
const int key = keybuf[i] & 0x7F;
const int butno = keys[key];
const bool pressed = !(keybuf[i] & 0x80);
ButtonPressed( DeviceInput(DEVICE_KEYBOARD, butno, pressed) );
}
}
InputHandler::UpdateTimer();
}
void InputHandler_Linux_tty::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
{
vDevicesOut.push_back( InputDeviceInfo(DEVICE_KEYBOARD,"Keyboard") );
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,43 @@
#ifndef INPUT_HANDLER_LINUX_TTY_H
#define INPUT_HANDLER_LINUX_TTY_H 1
#include "InputHandler.h"
class InputHandler_Linux_tty: public InputHandler
{
int fd;
static void OnCrash(int);
public:
void Update();
InputHandler_Linux_tty();
~InputHandler_Linux_tty();
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
};
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,142 @@
/* Keyboard interface for svgalib. */
/* Can be used independently. */
#ifndef VGAKEYBOARD_H
#define VGAKEYBOARD_H
#define SCANCODE_ESCAPE 1
#define SCANCODE_1 2
#define SCANCODE_2 3
#define SCANCODE_3 4
#define SCANCODE_4 5
#define SCANCODE_5 6
#define SCANCODE_6 7
#define SCANCODE_7 8
#define SCANCODE_8 9
#define SCANCODE_9 10
#define SCANCODE_0 11
#define SCANCODE_MINUS 12
#define SCANCODE_EQUAL 13
#define SCANCODE_BACKSPACE 14
#define SCANCODE_TAB 15
#define SCANCODE_Q 16
#define SCANCODE_W 17
#define SCANCODE_E 18
#define SCANCODE_R 19
#define SCANCODE_T 20
#define SCANCODE_Y 21
#define SCANCODE_U 22
#define SCANCODE_I 23
#define SCANCODE_O 24
#define SCANCODE_P 25
#define SCANCODE_BRACKET_LEFT 26
#define SCANCODE_BRACKET_RIGHT 27
#define SCANCODE_ENTER 28
#define SCANCODE_LEFTCONTROL 29
#define SCANCODE_A 30
#define SCANCODE_S 31
#define SCANCODE_D 32
#define SCANCODE_F 33
#define SCANCODE_G 34
#define SCANCODE_H 35
#define SCANCODE_J 36
#define SCANCODE_K 37
#define SCANCODE_L 38
#define SCANCODE_SEMICOLON 39
#define SCANCODE_APOSTROPHE 40
#define SCANCODE_GRAVE 41
#define SCANCODE_LEFTSHIFT 42
#define SCANCODE_BACKSLASH 43
#define SCANCODE_Z 44
#define SCANCODE_X 45
#define SCANCODE_C 46
#define SCANCODE_V 47
#define SCANCODE_B 48
#define SCANCODE_N 49
#define SCANCODE_M 50
#define SCANCODE_COMMA 51
#define SCANCODE_PERIOD 52
#define SCANCODE_SLASH 53
#define SCANCODE_RIGHTSHIFT 54
#define SCANCODE_KEYPADMULTIPLY 55
#define SCANCODE_LEFTALT 56
#define SCANCODE_SPACE 57
#define SCANCODE_CAPSLOCK 58
#define SCANCODE_F1 59
#define SCANCODE_F2 60
#define SCANCODE_F3 61
#define SCANCODE_F4 62
#define SCANCODE_F5 63
#define SCANCODE_F6 64
#define SCANCODE_F7 65
#define SCANCODE_F8 66
#define SCANCODE_F9 67
#define SCANCODE_F10 68
#define SCANCODE_NUMLOCK 69
#define SCANCODE_SCROLLLOCK 70
#define SCANCODE_KEYPAD7 71
#define SCANCODE_CURSORUPLEFT 71
#define SCANCODE_KEYPAD8 72
#define SCANCODE_CURSORUP 72
#define SCANCODE_KEYPAD9 73
#define SCANCODE_CURSORUPRIGHT 73
#define SCANCODE_KEYPADMINUS 74
#define SCANCODE_KEYPAD4 75
#define SCANCODE_CURSORLEFT 75
#define SCANCODE_KEYPAD5 76
#define SCANCODE_KEYPAD6 77
#define SCANCODE_CURSORRIGHT 77
#define SCANCODE_KEYPADPLUS 78
#define SCANCODE_KEYPAD1 79
#define SCANCODE_CURSORDOWNLEFT 79
#define SCANCODE_KEYPAD2 80
#define SCANCODE_CURSORDOWN 80
#define SCANCODE_KEYPAD3 81
#define SCANCODE_CURSORDOWNRIGHT 81
#define SCANCODE_KEYPAD0 82
#define SCANCODE_KEYPADPERIOD 83
#define SCANCODE_LESS 86
#define SCANCODE_F11 87
#define SCANCODE_F12 88
#define SCANCODE_KEYPADENTER 96
#define SCANCODE_RIGHTCONTROL 97
#define SCANCODE_CONTROL 97
#define SCANCODE_KEYPADDIVIDE 98
#define SCANCODE_PRINTSCREEN 99
#define SCANCODE_RIGHTALT 100
#define SCANCODE_BREAK 101 /* Beware: is 119 */
#define SCANCODE_BREAK_ALTERNATIVE 119 /* on some keyboards! */
#define SCANCODE_HOME 102
#define SCANCODE_CURSORBLOCKUP 103 /* Cursor key block */
#define SCANCODE_PAGEUP 104
#define SCANCODE_CURSORBLOCKLEFT 105 /* Cursor key block */
#define SCANCODE_CURSORBLOCKRIGHT 106 /* Cursor key block */
#define SCANCODE_END 107
#define SCANCODE_CURSORBLOCKDOWN 108 /* Cursor key block */
#define SCANCODE_PAGEDOWN 109
#define SCANCODE_INSERT 110
#define SCANCODE_REMOVE 111
#define SCANCODE_RIGHTWIN 126
#define SCANCODE_LEFTWIN 125
#endif
@@ -0,0 +1,482 @@
#include "global.h"
#include "RageLog.h"
#include "InputHandler_MacOSX_HID.h"
#include "Foreach.h"
#include "PrefsManager.h"
#include "InputFilter.h"
#include "archutils/Darwin/DarwinThreadHelpers.h"
#include "archutils/Darwin/KeyboardDevice.h"
#include "archutils/Darwin/JoystickDevice.h"
#include "archutils/Darwin/PumpDevice.h"
#include <IOKit/IOMessage.h>
#include <Carbon/Carbon.h>
REGISTER_INPUT_HANDLER_CLASS2( HID, MacOSX_HID );
void InputHandler_MacOSX_HID::QueueCallback( void *target, int result, void *refcon, void *sender )
{
// The result seems useless as you can't actually return anything...
// refcon is the Device number
RageTimer now;
InputHandler_MacOSX_HID *This = (InputHandler_MacOSX_HID *)target;
IOHIDQueueInterface **queue = (IOHIDQueueInterface **)sender;
IOHIDEventStruct event;
AbsoluteTime zeroTime = { 0, 0 };
HIDDevice *dev = This->m_vDevices[int( refcon )];
vector<DeviceInput> vPresses;
while( (result = CALL(queue, getNextEvent, &event, zeroTime, 0)) == kIOReturnSuccess )
{
if( event.longValueSize != 0 && event.longValue != NULL )
{
free( event.longValue );
continue;
}
//LOG->Trace( "Got event with cookie %p, value %d", event.elementCookie, int(event.value) );
dev->GetButtonPresses( vPresses, event.elementCookie, event.value, now );
}
FOREACH_CONST( DeviceInput, vPresses, i )
INPUTFILTER->ButtonPressed( *i );
}
static void RunLoopStarted( CFRunLoopObserverRef o, CFRunLoopActivity a, void *sem )
{
CFRunLoopObserverInvalidate( o );
CFRelease( o ); // we don't need this any longer
((RageSemaphore *)sem)->Post();
}
int InputHandler_MacOSX_HID::Run( void *data )
{
InputHandler_MacOSX_HID *This = (InputHandler_MacOSX_HID *)data;
This->m_LoopRef = CFRunLoopGetCurrent();
CFRetain( This->m_LoopRef );
This->StartDevices();
{
const RString sError = SetThreadPrecedence( 1.0f );
if( !sError.empty() )
LOG->Warn( "Could not set precedence of the input thread: %s", sError.c_str() );
}
// Add an observer for the start of the run loop
{
/* The function copies the information out of the structure, so the memory pointed
* to by context does not need to persist beyond the function call. */
CFRunLoopObserverContext context = { 0, &This->m_Sem, NULL, NULL, NULL };
CFRunLoopObserverRef o = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopEntry,
false, 0, RunLoopStarted, &context);
CFRunLoopAddObserver( This->m_LoopRef, o, kCFRunLoopDefaultMode );
}
/* Add a source for ending the run loop. This serves two purposes:
* 1. it provides a way to terminate the run loop when IH_MacOSX_HID exists, and
* 2. it ensures that CFRunLoopRun() doesn't return immediately if there are no other sources. */
{
/* Being a little tricky here, the perform callback takes a void* and returns nothing.
* CFRunLoopStop takes a CFRunLoopRef (a pointer) so cast the function and pass the loop ref. */
void *info = This->m_LoopRef;
void (*perform)(void *) = (void (*)(void *))CFRunLoopStop;
// { version, info, retain, release, copyDescription, equal, hash, schedule, cancel, perform }
CFRunLoopSourceContext context = { 0, info, NULL, NULL, NULL, NULL, NULL, NULL, NULL, perform };
// Pass 1 so that it is called after all inputs have been handled (they will have order = 0)
This->m_SourceRef = CFRunLoopSourceCreate( kCFAllocatorDefault, 1, &context );
CFRunLoopAddSource( This->m_LoopRef, This->m_SourceRef, kCFRunLoopDefaultMode );
}
CFRunLoopRun();
LOG->Trace( "Shutting down input handler thread..." );
return 0;
}
void InputHandler_MacOSX_HID::DeviceAdded( void *refCon, io_iterator_t )
{
InputHandler_MacOSX_HID *This = (InputHandler_MacOSX_HID *)refCon;
LockMut( This->m_ChangeLock );
This->m_bChanged = true;
}
void InputHandler_MacOSX_HID::DeviceChanged( void *refCon, io_service_t service, natural_t messageType, void *arg )
{
if( messageType == kIOMessageServiceIsTerminated )
{
InputHandler_MacOSX_HID *This = (InputHandler_MacOSX_HID *)refCon;
LockMut( This->m_ChangeLock );
This->m_bChanged = true;
}
}
// m_LoopRef needs to be set before this is called
void InputHandler_MacOSX_HID::StartDevices()
{
int n = 0;
ASSERT( m_LoopRef );
FOREACH( HIDDevice *, m_vDevices, i )
(*i)->StartQueue( m_LoopRef, InputHandler_MacOSX_HID::QueueCallback, this, n++ );
CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource( m_NotifyPort );
CFRunLoopAddSource( m_LoopRef, runLoopSource, kCFRunLoopDefaultMode );
}
InputHandler_MacOSX_HID::~InputHandler_MacOSX_HID()
{
FOREACH( HIDDevice *, m_vDevices, i )
delete *i;
if( PREFSMAN->m_bThreadedInput )
{
CFRunLoopSourceSignal( m_SourceRef );
CFRunLoopWakeUp( m_LoopRef );
m_InputThread.Wait();
CFRelease( m_SourceRef );
CFRelease( m_LoopRef );
LOG->Trace( "Input handler thread shut down." );
}
FOREACH( io_iterator_t, m_vIters, i )
IOObjectRelease( *i );
IONotificationPortDestroy( m_NotifyPort );
}
static CFDictionaryRef GetMatchingDictionary( int usagePage, int usage )
{
// Build the matching dictionary.
CFMutableDictionaryRef dict;
if( (dict = IOServiceMatching(kIOHIDDeviceKey)) == NULL )
FAIL_M( "Couldn't create a matching dictionary." );
// Refine the search by only looking for joysticks
CFNumberRef usagePageRef = CFInt( usagePage );
CFNumberRef usageRef = CFInt( usage );
CFDictionarySetValue( dict, CFSTR(kIOHIDPrimaryUsagePageKey), usagePageRef );
CFDictionarySetValue( dict, CFSTR(kIOHIDPrimaryUsageKey), usageRef );
// Cleanup after ourselves
CFRelease( usagePageRef );
CFRelease( usageRef );
return dict;
}
// Factor this out because nothing else in IH_C::AddDevices() needs to know the type of the device.
static HIDDevice *MakeDevice( InputDevice id )
{
if( id == DEVICE_KEYBOARD )
return new KeyboardDevice;
if( IsJoystick(id) )
return new JoystickDevice;
if( IsPump(id) )
return new PumpDevice;
return NULL;
}
void InputHandler_MacOSX_HID::AddDevices( int usagePage, int usage, InputDevice &id )
{
io_iterator_t iter;
CFDictionaryRef dict = GetMatchingDictionary( usagePage, usage );
kern_return_t ret = IOServiceAddMatchingNotification( m_NotifyPort, kIOFirstMatchNotification, dict,
InputHandler_MacOSX_HID::DeviceAdded, this, &iter );
io_object_t device;
if( ret != KERN_SUCCESS )
return;
m_vIters.push_back( iter );
// Iterate over the devices and add them
while( (device = IOIteratorNext(iter)) )
{
LOG->Trace( "\tFound device %d", id );
HIDDevice *dev = MakeDevice( id );
int num;
if( !dev )
{
LOG->Trace( "\t\tInvalid id, deleting device" );
IOObjectRelease( device );
continue;
}
if( !dev->Open(device) || (num = dev->AssignIDs(id)) == -1 )
{
LOG->Trace( "\tFailed top open or assign id, deleting device" );
delete dev;
IOObjectRelease( device );
continue;
}
io_iterator_t i;
enum_add( id, num );
m_vDevices.push_back( dev );
ret = IOServiceAddInterestNotification( m_NotifyPort, device, kIOGeneralInterest,
InputHandler_MacOSX_HID::DeviceChanged, this, &i );
if( ret == KERN_SUCCESS )
m_vIters.push_back( i );
else
LOG->Trace( "\t\tFailed to add device changed notification, deleting device" );
IOObjectRelease( device );
}
}
InputHandler_MacOSX_HID::InputHandler_MacOSX_HID() : m_Sem( "Input thread started" ), m_ChangeLock( "Input handler change lock" )
{
InputDevice id = DEVICE_KEYBOARD;
// Set up the notify ports.
m_NotifyPort = IONotificationPortCreate( kIOMasterPortDefault );
// Add devices.
LOG->Trace( "Finding keyboards" );
AddDevices( kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard, id );
LOG->Trace( "Finding joysticks" );
id = DEVICE_JOY1;
AddDevices( kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, id );
AddDevices( kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, id );
LOG->Trace( "Finding pump" );
id = DEVICE_PUMP1;
AddDevices( kHIDPage_VendorDefinedStart, 0x0001, id ); // Pump pads use the first vendor specific usage page.
m_bChanged = false;
if( PREFSMAN->m_bThreadedInput )
{
m_InputThread.SetName( "Input thread" );
m_InputThread.Create( InputHandler_MacOSX_HID::Run, this );
// Wait for the run loop to start before returning.
m_Sem.Wait();
}
else
{
m_LoopRef = CFRunLoopRef( GetCFRunLoopFromEventLoop(GetMainEventLoop()) );
CFRetain( m_LoopRef );
StartDevices();
}
}
void InputHandler_MacOSX_HID::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevices )
{
FOREACH_CONST( HIDDevice *, m_vDevices, i )
(*i)->GetDevicesAndDescriptions( vDevices );
}
RString InputHandler_MacOSX_HID::GetDeviceSpecificInputString( const DeviceInput &di )
{
if( di.device == DEVICE_KEYBOARD )
{
#define OTHER(n) (KEY_OTHER_0 + (n))
switch( di.button )
{
case KEY_DEL: return "del";
case KEY_BACK: return "delete";
case KEY_ENTER: return "return";
case KEY_LALT: return "left option";
case KEY_RALT: return "right option";
case KEY_LMETA: return "left cmd";
case KEY_RMETA: return "right cmd";
case KEY_INSERT: return "help";
case OTHER(0): return "F17";
case OTHER(1): return "F18";
case OTHER(2): return "F19";
case OTHER(3): return "F20";
case OTHER(4): return "F21";
case OTHER(5): return "F22";
case OTHER(6): return "F23";
case OTHER(7): return "F25";
case OTHER(8): return "execute";
case OTHER(9): return "select";
case OTHER(10): return "stop";
case OTHER(11): return "again";
case OTHER(12): return "undo";
case OTHER(13): return "cut";
case OTHER(14): return "copy";
case OTHER(15): return "paste";
case OTHER(16): return "find";
case OTHER(17): return "mute";
case OTHER(18): return "volume up";
case OTHER(19): return "volume down";
case OTHER(20): return "AS/400 equal";
case OTHER(21): return "international 1";
case OTHER(22): return "international 2";
case OTHER(23): return "international 3";
case OTHER(24): return "international 4";
case OTHER(25): return "international 5";
case OTHER(26): return "international 6";
case OTHER(27): return "international 7";
case OTHER(28): return "international 8";
case OTHER(29): return "international 9";
case OTHER(30): return "lang 1";
case OTHER(31): return "lang 2";
case OTHER(32): return "lang 3";
case OTHER(33): return "lang 4";
case OTHER(34): return "lang 5";
case OTHER(35): return "lang 6";
case OTHER(36): return "lang 7";
case OTHER(37): return "lang 8";
case OTHER(38): return "lang 9";
case OTHER(39): return "alt erase";
case OTHER(40): return "sys req";
case OTHER(41): return "cancel";
case OTHER(42): return "separator";
case OTHER(43): return "out";
case OTHER(44): return "oper";
case OTHER(45): return "clear/again"; // XXX huh?
case OTHER(46): return "cr sel/props"; // XXX
case OTHER(47): return "ex sel";
case OTHER(48): return "non US backslash";
case OTHER(49): return "application";
case OTHER(50): return "prior";
}
#undef OTHER
}
if( di.device == DEVICE_PUMP1 || di.device == DEVICE_PUMP2 )
{
switch( di.button )
{
case JOY_BUTTON_1: return "UL";
case JOY_BUTTON_2: return "UR";
case JOY_BUTTON_3: return "MID";
case JOY_BUTTON_4: return "DL";
case JOY_BUTTON_5: return "DR";
case JOY_BUTTON_6: return "Esc";
case JOY_BUTTON_7: return "P2 UL";
case JOY_BUTTON_8: return "P2 UR";
case JOY_BUTTON_9: return "P2 MID";
case JOY_BUTTON_10: return "P2 DL";
case JOY_BUTTON_11: return "P2 DR";
}
}
return InputHandler::GetDeviceSpecificInputString( di );
}
wchar_t InputHandler_MacOSX_HID::DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers )
{
// KeyTranslate maps these keys to a character. They shouldn't be mapped to any character.
switch( button )
{
default:
if( (button >= KEY_F1 && button <= KEY_F16) )
return L'\0';
break;
case KEY_UP:
case KEY_DOWN:
case KEY_LEFT:
case KEY_RIGHT:
case KEY_ESC:
case KEY_TAB:
case KEY_ENTER:
case KEY_PRTSC:
case KEY_SCRLLOCK:
case KEY_PAUSE:
case KEY_DEL:
case KEY_HOME:
case KEY_END:
case KEY_PGUP:
case KEY_PGDN:
case KEY_NUMLOCK:
case KEY_KP_ENTER:
return L'\0';
}
// Find the USB key code for this DeviceButton
UInt8 iMacVirtualKey;
if( KeyboardDevice::DeviceButtonToMacVirtualKey( button, iMacVirtualKey ) )
{
UInt32 modifiers = 0;
if( bUseCurrentKeyModifiers )
modifiers = GetCurrentKeyModifiers();
SInt16 iCurrentKeyScript = GetScriptManagerVariable( smKeyScript );
SInt16 iCurrentKeyLayoutID = GetScriptVariable( iCurrentKeyScript, smScriptKeys );
static SInt16 iLastKeyLayoutID = !iCurrentKeyLayoutID; // Just be different.
static UInt32 iDeadKeyState;
static UCKeyboardLayout **KeyLayout;
if( iCurrentKeyLayoutID != iLastKeyLayoutID )
{
iDeadKeyState = 0;
KeyLayout = (UCKeyboardLayout **)GetResource( 'uchr', iCurrentKeyLayoutID );
iLastKeyLayoutID = iCurrentKeyLayoutID;
}
if( KeyLayout )
{
UInt32 keyboardType = LMGetKbdType();
UInt32 modifiers = bUseCurrentKeyModifiers ? GetCurrentKeyModifiers() : 0;
UniChar unicodeInputString[4];
UniCharCount length;
OSStatus status = UCKeyTranslate( *KeyLayout, iMacVirtualKey, kUCKeyActionDown, modifiers,
keyboardType, 0, &iDeadKeyState, ARRAYLEN(unicodeInputString),
&length, unicodeInputString );
if( status )
return L'\0';
CFStringRef inputString = CFStringCreateWithCharacters( NULL, unicodeInputString, length );
char utf8InputString[7]; // Max size is 6 (although really only 4 are used) + null.
if( !CFStringGetCString(inputString, utf8InputString, 7, kCFStringEncodingUTF8) )
{
CFRelease( inputString );
return L'\0';
}
wchar_t ch = utf8_get_char( utf8InputString );
CFRelease( inputString );
return ch == INVALID_CHAR ? L'\0' : ch;
}
else
{
// Fall back on the 'KCHR' resource.
static unsigned long state = 0;
static Ptr keymap = NULL;
Ptr new_keymap;
new_keymap = (Ptr)GetScriptManagerVariable(smKCHRCache);
if( new_keymap != keymap )
{
keymap = new_keymap;
state = 0;
}
// XXX: Only returns ascii. é will be returned as e.
return KeyTranslate( keymap, UInt16(iMacVirtualKey)|modifiers, &state ) & 0xFF;
}
}
return InputHandler::DeviceButtonToChar( button, bUseCurrentKeyModifiers );
}
/*
* (c) 2005, 2006 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,69 @@
#ifndef INPUT_HANDLER_MACOSX_HID_H
#define INPUT_HANDLER_MACOSX_HID_H
#include <vector>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include "InputHandler.h"
#include "RageThreads.h"
class HIDDevice;
class InputHandler_MacOSX_HID : public InputHandler
{
private:
vector<HIDDevice *> m_vDevices;
RageThread m_InputThread;
RageSemaphore m_Sem;
CFRunLoopRef m_LoopRef;
CFRunLoopSourceRef m_SourceRef;
vector<io_iterator_t> m_vIters; // We don't really care about these but they need to stick around
IONotificationPortRef m_NotifyPort;
RageMutex m_ChangeLock;
bool m_bChanged;
static int Run( void *data );
static void DeviceAdded( void *refCon, io_iterator_t iter );
static void DeviceChanged( void *refCon, io_service_t service, natural_t messageType, void *arg );
void StartDevices();
void AddDevices( int usagePage, int usage, InputDevice &id );
public:
InputHandler_MacOSX_HID();
~InputHandler_MacOSX_HID();
bool DevicesChanged() { LockMut( m_ChangeLock ); return m_bChanged; }
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
RString GetDeviceSpecificInputString( const DeviceInput &di );
wchar_t DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers );
static void QueueCallback( void *target, int result, void *refcon, void *sender );
};
#endif
/*
* (c) 2005-2006 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,119 @@
#include "global.h"
#include "InputHandler_MonkeyKeyboard.h"
#include "RageUtil.h"
#include "PrefsManager.h"
InputHandler_MonkeyKeyboard::InputHandler_MonkeyKeyboard()
{
m_dbLast = DeviceButton_Invalid;
}
InputHandler_MonkeyKeyboard::~InputHandler_MonkeyKeyboard()
{
}
void InputHandler_MonkeyKeyboard::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
{
vDevicesOut.push_back( InputDeviceInfo(DEVICE_KEYBOARD, "MonkeyKeyboard") );
}
static const DeviceButton g_keys[] =
{
// Some of the default keys for the dance game type
KEY_LEFT, // DANCE_BUTTON_LEFT,
KEY_RIGHT, // DANCE_BUTTON_RIGHT,
KEY_UP, // DANCE_BUTTON_UP,
KEY_DOWN, // DANCE_BUTTON_DOWN,
KEY_ENTER, // DANCE_BUTTON_START,
KEY_ENTER, // DANCE_BUTTON_START,
KEY_ENTER, // DANCE_BUTTON_START,
KEY_DEL, // DANCE_BUTTON_MENULEFT
KEY_PGDN, // DANCE_BUTTON_MENURIGHT
KEY_HOME, // DANCE_BUTTON_MENUUP
KEY_END, // DANCE_BUTTON_MENUDOWN
KEY_F1, // DANCE_BUTTON_COIN
KEY_F1, // DANCE_BUTTON_COIN
KEY_KP_C4, // DANCE_BUTTON_LEFT,
KEY_KP_C6, // DANCE_BUTTON_RIGHT,
KEY_KP_C8, // DANCE_BUTTON_UP,
KEY_KP_C2, // DANCE_BUTTON_DOWN,
KEY_KP_C7, // DANCE_BUTTON_UPLEFT,
KEY_KP_C9, // DANCE_BUTTON_UPRIGHT,
KEY_KP_ENTER, // DANCE_BUTTON_START,
KEY_KP_ENTER, // DANCE_BUTTON_START,
KEY_KP_ENTER, // DANCE_BUTTON_START,
KEY_KP_SLASH, // DANCE_BUTTON_MENULEFT
KEY_KP_ASTERISK, // DANCE_BUTTON_MENURIGHT
KEY_KP_HYPHEN, // DANCE_BUTTON_MENUUP
KEY_KP_PLUS, // DANCE_BUTTON_MENUDOWN
};
static DeviceButton GetRandomKeyboardKey()
{
int index = RandomInt( ARRAYLEN(g_keys) );
return g_keys[index];
}
void InputHandler_MonkeyKeyboard::Update()
{
if( !PREFSMAN->m_bMonkeyInput )
{
if( m_dbLast != DeviceButton_Invalid )
{
// End the previous key
DeviceInput di = DeviceInput( DEVICE_KEYBOARD, m_dbLast, 0 );
ButtonPressed( di );
m_dbLast = DeviceButton_Invalid;
}
InputHandler::UpdateTimer();
return;
}
float fSecsAgo = m_timerPressButton.Ago();
if( fSecsAgo > 0.5 )
{
if( m_dbLast != DeviceButton_Invalid )
{
// End the previous key
DeviceInput di = DeviceInput( DEVICE_KEYBOARD, m_dbLast, 0 );
ButtonPressed( di );
}
// Choose a new key and send it.
m_dbLast = GetRandomKeyboardKey();
DeviceInput di = DeviceInput( DEVICE_KEYBOARD, m_dbLast, 1 );
ButtonPressed( di );
m_timerPressButton.Touch();
}
InputHandler::UpdateTimer();
}
/*
* (c) 2002-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,47 @@
#ifndef INPUT_HANDLER_MONKEY_SCRIPT
#define INPUT_HANDLER_MONKEY_SCRIPT
#include "InputHandler.h"
#include "RageTimer.h"
#include "RageInputDevice.h"
class InputHandler_MonkeyKeyboard: public InputHandler
{
public:
void Update();
InputHandler_MonkeyKeyboard();
~InputHandler_MonkeyKeyboard();
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
private:
RageTimer m_timerPressButton;
DeviceButton m_dbLast; // Last input that we sent
};
#endif
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,122 @@
#include "global.h"
#include "RageUtil.h"
#include "InputHandler_Win32_MIDI.h"
#include "RageLog.h"
#include <windows.h>
#include <mmsystem.h>
#pragma comment (lib,"winmm.lib")
REGISTER_INPUT_HANDLER_CLASS2( MIDI, Win32_MIDI );
static HMIDIIN g_device;
static void CALLBACK midiCallback(HMIDIIN g_device, UINT status, DWORD instancePtr, DWORD data, DWORD timestamp);
static RString GetMidiError( MMRESULT result )
{
char szError[256];
midiOutGetErrorText( result, szError, 256 );
return szError;
}
InputHandler_Win32_MIDI::InputHandler_Win32_MIDI()
{
int device_id = 0;
g_device = NULL;
if( device_id >= (int) midiInGetNumDevs() )
{
m_bFoundDevice = false;
return;
}
m_bFoundDevice = true;
MMRESULT result = midiInOpen( &g_device, device_id, (DWORD) &midiCallback, (DWORD) this, CALLBACK_FUNCTION );
if( result != MMSYSERR_NOERROR )
{
LOG->Warn( "Error opening MIDI device: %s", GetMidiError(result).c_str() );
return;
}
result = midiInStart(g_device);
if( result != MMSYSERR_NOERROR )
{
LOG->Warn( "Error starting MIDI device: %s", GetMidiError(result).c_str() );
return;
}
}
InputHandler_Win32_MIDI::~InputHandler_Win32_MIDI()
{
MMRESULT result;
result = midiInReset( g_device );
if( result != MMSYSERR_NOERROR )
{
LOG->Warn( "Error resetting MIDI device: %s", GetMidiError(result).c_str() );
return;
}
result = midiInClose( g_device );
if( result != MMSYSERR_NOERROR )
{
LOG->Warn( "Error closing MIDI device: %s", GetMidiError(result).c_str() );
return;
}
}
void InputHandler_Win32_MIDI::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
{
if( m_bFoundDevice )
{
vDevicesOut.push_back( InputDeviceInfo(DEVICE_MIDI,"Win32_MIDI") );
}
}
static void CALLBACK midiCallback( HMIDIIN device, UINT status, DWORD instancePtr, DWORD data, DWORD timestamp )
{
if( status == MIM_DATA )
{
int iType = data & 0xff;
int iChannel = (data & 0xff00) >> 8;
int iValue = (data & 0xff0000) >> 16;
// Channel 0 in midi is a special channel that generally will get triggered when too many channels are pressed.
if( iChannel == 0 )
return;
if( iType == 144 )
{
DeviceInput di = DeviceInput( DEVICE_MIDI, enum_add2(MIDI_FIRST, iChannel), iValue > 0 );
di.ts.Touch();
((InputHandler_Win32_MIDI *)instancePtr)->SetDev( di );
}
}
}
/*
* Copyright (c) 2005 Charles Lohr
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,48 @@
#ifndef INPUT_HANDLER_WIN32_MIDI
#define INPUT_HANDLER_WIN32_MIDI
#include "InputHandler.h"
#include "RageInputDevice.h"
class InputHandler_Win32_MIDI: public InputHandler
{
public:
InputHandler_Win32_MIDI();
~InputHandler_Win32_MIDI();
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
void SetDev( DeviceInput key ) { ButtonPressed( key ); }
private:
bool m_bFoundDevice;
};
#endif
/*
* (c) 2002-2005 Charles Lohr, Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,70 @@
#include "global.h"
#include "InputHandler_Win32_Para.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageInputDevice.h"
#include "archutils/Win32/USB.h"
// TODO: Abstract this windows-specific stuff into USBDevice.
extern "C" {
#include "archutils/Win32/ddk/setupapi.h"
/* Quiet header warning: */
#include "archutils/Win32/ddk/hidsdi.h"
}
REGISTER_INPUT_HANDLER_CLASS2( Para, Win32_Para );
static void InitHack( HANDLE h )
{
UCHAR hack[] = {0, 1};
if( HidD_SetFeature(h, (PVOID) hack, 2) == TRUE )
LOG->Info( "Para controller powered on successfully" );
else
LOG->Warn( "Para controller power-on failed" );
}
InputHandler_Win32_Para::InputHandler_Win32_Para()
{
const int para_usb_vid = 0x0507;
const int para_usb_pid = 0x0011;
USBDevice *dev = new USBDevice;
if( dev->Open(para_usb_vid, para_usb_pid, sizeof(long), 0, InitHack) )
{
LOG->Info("Para controller initialized");
}
SAFE_DELETE( dev );
}
void InputHandler_Win32_Para::GetDevicesAndDescriptions(vector<InputDeviceInfo>& vDevicesOut )
{
// The device appears as a HID joystick
}
/*
* (c) 2002-2004 Chris Danford, Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,40 @@
/* Initializes a USB Para controller so that it will function as a regular HID joystick. */
#ifndef INPUT_HANDLER_WIN32_PARA_H
#define INPUT_HANDLER_WIN32_PARA_H
#include "InputHandler.h"
class InputHandler_Win32_Para: public InputHandler
{
public:
InputHandler_Win32_Para();
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
};
#endif
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,182 @@
#include "global.h"
#include "InputHandler_Win32_Pump.h"
#include "PrefsManager.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageInputDevice.h"
#include "archutils/Win32/ErrorStrings.h"
#include "archutils/Win32/USB.h"
REGISTER_INPUT_HANDLER_CLASS2( Pump, Win32_Pump );
InputHandler_Win32_Pump::InputHandler_Win32_Pump()
{
m_bShutdown = false;
const int pump_usb_vid = 0x0d2f, pump_usb_pid = 0x0001;
m_pDevice = new USBDevice[NUM_PUMPS];
bool bFoundOnePad = false;
for( int i = 0; i < NUM_PUMPS; ++i )
{
if( m_pDevice[i].Open(pump_usb_vid, pump_usb_pid, sizeof(long), i, NULL) )
{
bFoundOnePad = true;
LOG->Info( "Found Pump pad %i", i );
}
}
/* Don't start a thread if we have no pads. */
if( bFoundOnePad && PREFSMAN->m_bThreadedInput )
{
InputThread.SetName( "Pump thread" );
InputThread.Create( InputThread_Start, this );
}
}
InputHandler_Win32_Pump::~InputHandler_Win32_Pump()
{
if( InputThread.IsCreated() )
{
m_bShutdown = true;
LOG->Trace( "Shutting down Pump thread ..." );
InputThread.Wait();
LOG->Trace( "Pump thread shut down." );
}
delete[] m_pDevice;
}
void InputHandler_Win32_Pump::HandleInput( int iDevice, int iEvent )
{
static const int bits[] = {
/* P1 */ (1<<9), (1<<12), (1<<13), (1<<11), (1<<10),
/* ESC */ (1<<16),
/* P1 */ (1<<17), (1<<20), (1<<21), (1<<19), (1<<18),
};
InputDevice id = InputDevice( DEVICE_PUMP1 + iDevice );
for( int iButton = 0; iButton < ARRAYLEN(bits); ++iButton )
{
DeviceInput di( id, enum_add2(JOY_BUTTON_1, iButton), !(iEvent & bits[iButton]) );
/* If we're in a thread, our timestamp is accurate. */
if( InputThread.IsCreated() )
di.ts.Touch();
ButtonPressed( di );
}
}
RString InputHandler_Win32_Pump::GetDeviceSpecificInputString( const DeviceInput &di )
{
switch( di.button )
{
case JOY_BUTTON_1: return "UL";
case JOY_BUTTON_2: return "UR";
case JOY_BUTTON_3: return "MID";
case JOY_BUTTON_4: return "DL";
case JOY_BUTTON_5: return "DR";
case JOY_BUTTON_6: return "Esc";
case JOY_BUTTON_7: return "P2 UL";
case JOY_BUTTON_8: return "P2 UR";
case JOY_BUTTON_9: return "P2 MID";
case JOY_BUTTON_10: return "P2 DL";
case JOY_BUTTON_11: return "P2 DR";
}
return InputHandler::GetDeviceSpecificInputString( di );
}
void InputHandler_Win32_Pump::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
{
for(int i = 0; i < NUM_PUMPS; ++i)
{
if( m_pDevice[i].IsOpen() )
{
vDevicesOut.push_back( InputDeviceInfo(InputDevice(DEVICE_PUMP1+i),"Pump USB") );
}
}
}
int InputHandler_Win32_Pump::InputThread_Start( void *p )
{
((InputHandler_Win32_Pump *) p)->InputThreadMain();
return 0;
}
void InputHandler_Win32_Pump::InputThreadMain()
{
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) )
LOG->Warn( werr_ssprintf(GetLastError(), "Failed to set Pump thread priority") );
/* Enable priority boosting. */
SetThreadPriorityBoost( GetCurrentThread(), FALSE );
vector<WindowsFileIO *> apSources;
for( int i = 0; i < NUM_PUMPS; ++i )
{
if( m_pDevice[i].m_IO.IsOpen() )
apSources.push_back( &m_pDevice[i].m_IO );
}
while( !m_bShutdown )
{
CHECKPOINT;
int iActual = 0, iVal = 0;
int iRet = WindowsFileIO::read_several( apSources, &iVal, iActual, 0.100f );
CHECKPOINT;
if( iRet <= 0 )
continue; /* no event */
HandleInput( iActual, iVal );
InputHandler::UpdateTimer();
}
CHECKPOINT;
}
void InputHandler_Win32_Pump::Update()
{
if( !InputThread.IsCreated() )
{
for( int i = 0; i < NUM_PUMPS; ++i )
{
int iRet = m_pDevice[i].GetPadEvent();
if( iRet == -1 )
continue; /* no event */
HandleInput( i, iRet );
}
InputHandler::UpdateTimer();
}
}
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,53 @@
#ifndef INPUT_HANDLER_WIN32_PUMP_H
#define INPUT_HANDLER_WIN32_PUMP_H
#include "InputHandler.h"
#include "RageThreads.h"
class USBDevice;
class InputHandler_Win32_Pump: public InputHandler
{
public:
void Update();
InputHandler_Win32_Pump();
~InputHandler_Win32_Pump();
RString GetDeviceSpecificInputString( const DeviceInput &di );
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
private:
USBDevice *m_pDevice;
RageThread InputThread;
bool m_bShutdown;
static int InputThread_Start( void *p );
void InputThreadMain();
void HandleInput( int devno, int event );
};
#endif
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+222
View File
@@ -0,0 +1,222 @@
#include "global.h"
#include "InputHandler_X11.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "RageDisplay.h"
#include "InputFilter.h"
#include "archutils/Unix/X11Helper.h"
#include <X11/Xlib.h>
#include <X11/keysym.h>
using namespace X11Helper;
REGISTER_INPUT_HANDLER_CLASS( X11 );
static DeviceButton XSymToDeviceButton( int key )
{
#define KEY_INV DeviceButton_Invalid
static const DeviceButton ASCIIKeySyms[] =
{
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 0 - 4 */
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 5 - 9 */
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 10 - 14 */
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 15 - 19 */
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 20 - 24 */
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 25 - 29 */
KEY_INV , KEY_INV , KEY_SPACE , KEY_EXCL , KEY_QUOTE , /* 30 - 34 */
KEY_HASH , KEY_DOLLAR , KEY_PERCENT , KEY_AMPER , KEY_SQUOTE , /* 35 - 39 */
KEY_LPAREN , KEY_RPAREN , KEY_ASTERISK , KEY_PLUS , KEY_COMMA , /* 40 - 44 */
KEY_HYPHEN , KEY_PERIOD , KEY_SLASH , KEY_C0 , KEY_C1 , /* 45 - 49 */
KEY_C2 , KEY_C3 , KEY_C4 , KEY_C5 , KEY_C6 , /* 50 - 54 */
KEY_C7 , KEY_C8 , KEY_C9 , KEY_COLON , KEY_SEMICOLON, /* 55 - 59 */
KEY_LANGLE , KEY_EQUAL , KEY_RANGLE , KEY_QUESTION, KEY_AT , /* 60 - 64 */
KEY_CA , KEY_CB , KEY_CC , KEY_CD , KEY_CE , /* 65 - 69 */
KEY_CF , KEY_CG , KEY_CH , KEY_CI , KEY_CJ , /* 70 - 74 */
KEY_CK , KEY_CL , KEY_CM , KEY_CN , KEY_CO , /* 75 - 79 */
KEY_CP , KEY_CQ , KEY_CR , KEY_CS , KEY_CT , /* 80 - 84 */
KEY_CU , KEY_CV , KEY_CW , KEY_CX , KEY_CY , /* 85 - 89 */
KEY_CZ , KEY_LBRACKET, KEY_BACKSLASH, KEY_RBRACKET, KEY_CARAT , /* 90 - 94 */
KEY_UNDERSCORE, KEY_ACCENT , KEY_Ca , KEY_Cb , KEY_Cc , /* 95 - 99 */
KEY_Cd , KEY_Ce , KEY_Cf , KEY_Cg , KEY_Ch , /* 100 - 104 */
KEY_Ci , KEY_Cj , KEY_Ck , KEY_Cl , KEY_Cm , /* 105 - 109 */
KEY_Cn , KEY_Co , KEY_Cp , KEY_Cq , KEY_Cr , /* 110 - 114 */
KEY_Cs , KEY_Ct , KEY_Cu , KEY_Cv , KEY_Cw , /* 115 - 119 */
KEY_Cx , KEY_Cy , KEY_Cz , KEY_LBRACE , KEY_PIPE , /* 120 - 124 */
KEY_RBRACE , KEY_INV , KEY_DEL /* 125 - 127 */
};
/* 32...127: */
if( key < int(ARRAYLEN(ASCIIKeySyms)))
return ASCIIKeySyms[key];
/* XK_KP_0 ... XK_KP_9 to KEY_KP_C0 ... KEY_KP_C9 */
if( key >= XK_KP_0 && key <= XK_KP_9 )
return enum_add2(KEY_KP_C0, key - XK_KP_0);
switch( key )
{
/* These are needed because of the way X registers the keypad. */
case XK_BackSpace: return KEY_BACK;
case XK_Tab: return KEY_TAB;
case XK_Pause: return KEY_PAUSE;
case XK_Escape: return KEY_ESC;
case XK_KP_Insert: return KEY_KP_C0;
case XK_KP_End: return KEY_KP_C1;
case XK_KP_Down: return KEY_KP_C2;
case XK_KP_Page_Down: return KEY_KP_C3;
case XK_KP_Left: return KEY_KP_C4;
case XK_KP_Begin: return KEY_KP_C5;
case XK_KP_Right: return KEY_KP_C6;
case XK_KP_Home: return KEY_KP_C7;
case XK_KP_Up: return KEY_KP_C8;
case XK_KP_Page_Up: return KEY_KP_C9;
case XK_KP_Decimal: return KEY_KP_PERIOD;
case XK_KP_Divide: return KEY_KP_SLASH;
case XK_KP_Multiply: return KEY_KP_ASTERISK;
case XK_KP_Subtract: return KEY_KP_HYPHEN;
case XK_KP_Add: return KEY_KP_PLUS;
case XK_KP_Equal: return KEY_KP_EQUAL;
case XK_KP_Enter: return KEY_KP_ENTER;
case XK_Up: return KEY_UP;
case XK_Down: return KEY_DOWN;
case XK_Right: return KEY_RIGHT;
case XK_Left: return KEY_LEFT;
case XK_Insert: return KEY_INSERT;
case XK_Home: return KEY_HOME;
case XK_Delete: return KEY_DEL;
case XK_End: return KEY_END;
case XK_Page_Up: return KEY_PGUP;
case XK_Page_Down: return KEY_PGDN;
case XK_F1: return KEY_F1;
case XK_F2: return KEY_F2;
case XK_F3: return KEY_F3;
case XK_F4: return KEY_F4;
case XK_F5: return KEY_F5;
case XK_F6: return KEY_F6;
case XK_F7: return KEY_F7;
case XK_F8: return KEY_F8;
case XK_F9: return KEY_F9;
case XK_F10: return KEY_F10;
case XK_F11: return KEY_F11;
case XK_F12: return KEY_F12;
case XK_F13: return KEY_F13;
case XK_F14: return KEY_F14;
case XK_F15: return KEY_F15;
case XK_Num_Lock: return KEY_NUMLOCK;
case XK_Caps_Lock: return KEY_CAPSLOCK;
case XK_Scroll_Lock: return KEY_SCRLLOCK;
case XK_Return: return KEY_ENTER;
case XK_Sys_Req: return KEY_PRTSC;
case XK_Print: return KEY_PRTSC;
case XK_Shift_R: return KEY_RSHIFT;
case XK_Shift_L: return KEY_LSHIFT;
case XK_Control_R: return KEY_RCTRL;
case XK_Control_L: return KEY_LCTRL;
case XK_Alt_R: return KEY_RALT;
case XK_Alt_L: return KEY_LALT;
case XK_Meta_R: return KEY_RMETA;
case XK_Meta_L: return KEY_LMETA;
case XK_Super_L: return KEY_LSUPER;
case XK_Super_R: return KEY_RSUPER;
case XK_Menu: return KEY_MENU;
}
return DeviceButton_Invalid;
}
InputHandler_X11::InputHandler_X11()
{
if( Dpy == NULL || Win == None )
return;
XWindowAttributes winAttrib;
XGetWindowAttributes( Dpy, Win, &winAttrib );
XSelectInput( Dpy, Win, winAttrib.your_event_mask | KeyPressMask | KeyReleaseMask );
}
InputHandler_X11::~InputHandler_X11()
{
if( Dpy == NULL || Win == None )
return;
XWindowAttributes winAttrib;
XGetWindowAttributes( Dpy, Win, &winAttrib );
XSelectInput( Dpy, Win, winAttrib.your_event_mask & ~(KeyPressMask|KeyReleaseMask) );
}
void InputHandler_X11::Update()
{
if( Dpy == NULL || Win == None )
{
InputHandler::UpdateTimer();
return;
}
XEvent event, lastEvent;
DeviceButton lastDB = DeviceButton_Invalid;
lastEvent.type = 0;
while( XCheckWindowEvent(Dpy, Win, KeyPressMask | KeyReleaseMask, &event) )
{
const bool bPress = event.type == KeyPress;
if( lastEvent.type != 0 )
{
if( bPress && event.xkey.time == lastEvent.xkey.time &&
event.xkey.keycode == lastEvent.xkey.keycode )
{
// This is a repeat event so ignore it.
lastEvent.type = 0;
continue;
}
// This is a new event so the last release was not a repeat.
ButtonPressed( DeviceInput(DEVICE_KEYBOARD, lastDB, 0) );
lastEvent.type = 0;
}
// Why only the zero index?
lastDB = XSymToDeviceButton( XLookupKeysym(&event.xkey, 0) );
if( lastDB == DeviceButton_Invalid )
continue;
if( bPress )
ButtonPressed( DeviceInput(DEVICE_KEYBOARD, lastDB, 1) );
else
lastEvent = event;
}
// Handle any last releases.
if( lastEvent.type != 0 )
ButtonPressed( DeviceInput(DEVICE_KEYBOARD, lastDB, 0) );
InputHandler::UpdateTimer();
}
void InputHandler_X11::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
{
if( Dpy && Win )
vDevicesOut.push_back( InputDeviceInfo(DEVICE_KEYBOARD,"Keyboard") );
}
/*
* (c) 2005, 2006 Sean Burke, Ben Anderson, Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+42
View File
@@ -0,0 +1,42 @@
/* InputHandler_X11 - X-based keyboard input handler. */
#ifndef INPUT_HANDLER_X11_H
#define INPUT_HANDLER_X11_H
#include "InputHandler.h"
class InputHandler_X11: public InputHandler
{
public:
InputHandler_X11();
~InputHandler_X11();
void Update();
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
};
#endif
/*
* (c) 2005 Sean Burke, Ben Anderson
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+232
View File
@@ -0,0 +1,232 @@
#include "global.h"
#include "InputHandler_Xbox.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "RageDisplay.h"
#include <xtl.h>
struct DEVICE_STATE {
XPP_DEVICE_TYPE *pxdt;
DWORD dwState;
};
byte buttonMasks[] = { XINPUT_GAMEPAD_DPAD_LEFT,
XINPUT_GAMEPAD_DPAD_RIGHT,
XINPUT_GAMEPAD_DPAD_UP,
XINPUT_GAMEPAD_DPAD_DOWN,
XINPUT_GAMEPAD_START,
XINPUT_GAMEPAD_BACK,
XINPUT_GAMEPAD_LEFT_THUMB,
XINPUT_GAMEPAD_RIGHT_THUMB};
/**
* XBOX controller maps to the following RageInputDevice constants:
* DPAD -> JOY_HAT_...
* START -> JOY_AUX_1
* BACK -> JOY_AUX_2
* Left thumb button -> JOY_AUX_3
* Right thumb button -> JOY_AUX_4
* Following buttons are JOY_(index):
* A, B, X, Y, BLACK, WHITE, Left trigger, right trigger
*/
InputHandler_Xbox::InputHandler_Xbox()
{
//
// Init joysticks
//
ZeroMemory( joysticks, sizeof(joysticks) );
getHandles();
}
InputHandler_Xbox::~InputHandler_Xbox()
{
for(unsigned i = 0; i < NUM_JOYSTICKS; i++)
{
if(joysticks[i] != 0)
XInputClose(joysticks[i]);
}
}
void InputHandler_Xbox::Update()
{
// check insertions and removals
DWORD dwInsert, dwRemove;
DEVICE_STATE devices = {XDEVICE_TYPE_GAMEPAD, 0};
bool changes = false;
// Check each device type to see if any changes have occurred.
if( XGetDeviceChanges( devices.pxdt, &dwInsert, &dwRemove ) )
{
for(int j = 0; j < 4; j++)
{
if(1 << j & dwRemove)
{
changes = true;
LOG->Trace("A joystick was removed");
}
if(1 << j & dwInsert)
{
changes = true;
LOG->Trace("A joystick was inserted");
}
}
}
if(changes)
{
getHandles();
return;
}
for(unsigned i = 0; i < NUM_JOYSTICKS; i++)
{
if(joysticks[i] == 0)
continue;
InputDevice inputDevice = InputDevice(DEVICE_JOY1 + i);
XINPUT_STATE xis;
// Query latest state.
XInputGetState( joysticks[i], &xis );
// check buttons
for(int j = 0; j < ARRAYLEN(buttonMasks); j++)
{
DWORD nowPressed = xis.Gamepad.wButtons & buttonMasks[j];
DWORD wasPressed = lastState[i].wButtons & buttonMasks[j];
if(nowPressed != wasPressed)
{
DeviceButton Button = DeviceButton(JOY_HAT_LEFT + j);
if(Button >= JOY_BUTTON_32)
{
LOG->Warn("Ignored joystick event (button too high)");
continue;
}
DeviceInput di(inputDevice, Button, nowPressed != 0);
ButtonPressed(di);
continue;
}
}
// check analog buttons
for(int j = 0; j < ARRAYLEN(xis.Gamepad.bAnalogButtons); j++)
{
bool nowPressed = xis.Gamepad.bAnalogButtons[j] > XINPUT_GAMEPAD_MAX_CROSSTALK;
bool wasPressed = lastState[i].bAnalogButtons[j] > XINPUT_GAMEPAD_MAX_CROSSTALK;
if(nowPressed != wasPressed)
{
DeviceButton Button = DeviceButton(JOY_BUTTON_1 + j);
if(Button >= JOY_BUTTON_32)
{
LOG->Warn("Ignored joystick event (button too high)");
continue;
}
DeviceInput di(inputDevice, Button, nowPressed);
ButtonPressed(di);
continue;
}
}
// check thumbsticks
SHORT axes[] = { xis.Gamepad.sThumbLX, xis.Gamepad.sThumbLY, xis.Gamepad.sThumbRX, xis.Gamepad.sThumbRY};
for(int j = 0; j < ARRAYLEN(axes); j++)
{
if(axes[j] != 0)
{
// Reverse y axis (negative values are down, not up)
if(j == 1 || j == 3)
axes[j] = -axes[j];
DeviceButton neg = (DeviceButton)(JOY_LEFT + (2 * j));
DeviceButton pos = (DeviceButton)(JOY_RIGHT + (2 * j));
float l = SCALE( axes[j], 0.0f, 32768.0f, 0.0f, 1.0f );
ButtonPressed(DeviceInput(inputDevice, neg,max(-l,0),RageZeroTimer));
ButtonPressed(DeviceInput(inputDevice, pos,max(+l,0),RageZeroTimer));
continue;
}
}
memcpy(&lastState[i], &xis.Gamepad, sizeof(XINPUT_GAMEPAD));
}
InputHandler::UpdateTimer();
}
void InputHandler_Xbox::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
{
for( int i=0; i<NUM_JOYSTICKS; i++ )
{
if( joysticks[i] != 0 )
{
vDevicesOut.push_back( InputDeviceInfo(InputDevice(DEVICE_JOY1+i),"XboxGameHardware") );
}
}
}
void InputHandler_Xbox::getHandles()
{
for(unsigned i = 0; i < NUM_JOYSTICKS; i++)
{
if(joysticks[i] != 0)
XInputClose(joysticks[i]);
}
ZeroMemory( joysticks, sizeof(joysticks) );
ZeroMemory( lastState, sizeof(lastState) );
// Work out joystick handles
DEVICE_STATE devices = {XDEVICE_TYPE_GAMEPAD, 0};
devices.dwState = XGetDevices( devices.pxdt );
// Check the global gamepad state for a connected device.
unsigned playersAllocated = 0;
unsigned joysFound = 0;
for( unsigned i = 0; i < NUM_PORTS; i++ )
{
if( devices.dwState & 1 << i)
{
if(playersAllocated < NUM_JOYSTICKS)
{
XINPUT_POLLING_PARAMETERS pollingParameters = {TRUE, TRUE, 0, 8, 8, 0,};
joysticks[playersAllocated] = XInputOpen(XDEVICE_TYPE_GAMEPAD, (DWORD)i, XDEVICE_NO_SLOT, &pollingParameters);
playersAllocated++;
}
joysFound++;
}
}
LOG->Info( "Found %d connected joysticks for %d players", joysFound, playersAllocated );
}
/*
* (c) 2004 Ryan Dortmans
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+52
View File
@@ -0,0 +1,52 @@
#ifndef INPUT_HANDLER_XBOX_H
#define INPUT_HANDLER_XBOX_H
#include "InputHandler.h"
#include <xtl.h>
#define NUM_PORTS 4
class InputHandler_Xbox: public InputHandler
{
HANDLE joysticks[NUM_JOYSTICKS];
XINPUT_GAMEPAD lastState[NUM_JOYSTICKS];
public:
void Update();
InputHandler_Xbox();
~InputHandler_Xbox();
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
private:
void getHandles();
};
#define USE_INPUT_HANDLER_XBOX
REGISTER_INPUT_HANDLER_CLASS( Xbox );
#endif
/*
* (c) 2004 Ryan Dortmans
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+56
View File
@@ -0,0 +1,56 @@
#include "global.h"
#include "LightsDriver.h"
#include "RageLog.h"
#include "Foreach.h"
#include "arch/arch_default.h"
DriverList LightsDriver::m_pDriverList;
void LightsDriver::Create( const RString &sDrivers, vector<LightsDriver *> &Add )
{
LOG->Trace( "Initializing lights drivers: %s", sDrivers.c_str() );
vector<RString> asDriversToTry;
split( sDrivers, ",", asDriversToTry, true );
FOREACH_CONST( RString, asDriversToTry, Driver )
{
RageDriver *pRet = m_pDriverList.Create( *Driver );
if( pRet == NULL )
{
LOG->Trace( "Unknown lights driver: %s", Driver->c_str() );
continue;
}
LightsDriver *pDriver = dynamic_cast<LightsDriver *>( pRet );
ASSERT( pDriver != NULL );
LOG->Info( "Lights driver: %s", Driver->c_str() );
Add.push_back( pDriver );
}
}
/*
* (c) 2002-2005 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+52
View File
@@ -0,0 +1,52 @@
/* LightsDriver - Controls lights */
#ifndef LightsDriver_H
#define LightsDriver_H
#include "LightsManager.h"
#include "arch/RageDriver.h"
struct LightsState;
class LightsDriver: public RageDriver
{
public:
static void Create( const RString &sDriver, vector<LightsDriver *> &apAdd );
static DriverList m_pDriverList;
LightsDriver() {};
virtual ~LightsDriver() {};
virtual void Set( const LightsState *ls ) = 0;
};
#define REGISTER_SOUND_DRIVER_CLASS2( name, x ) \
static RegisterRageDriver register_##x( &LightsDriver::m_pDriverList, #name, CreateClass<LightsDriver_##x, RageDriver> )
#define REGISTER_SOUND_DRIVER_CLASS( name ) REGISTER_SOUND_DRIVER_CLASS2( name, name )
#endif
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+52
View File
@@ -0,0 +1,52 @@
#include "global.h"
#include "LightsDriver_Export.h"
REGISTER_SOUND_DRIVER_CLASS(Export);
RageMutex LightsDriver_Export::m_Lock( "LightsDriver_Export");
LightsState LightsDriver_Export::m_State;
LightsDriver_Export::LightsDriver_Export()
{
memset( &m_State, 0, sizeof(m_State) );
}
void LightsDriver_Export::Set( const LightsState *ls )
{
m_Lock.Lock();
m_State = *ls;
m_Lock.Unlock();
}
LightsState LightsDriver_Export::GetState()
{
m_Lock.Lock();
LightsState ret( m_State );
m_Lock.Unlock();
return ret;
}
/*
* (c) 2006 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+48
View File
@@ -0,0 +1,48 @@
/* LightsDriver_Export - Export lights data for other devices that supply lights as a secondary service. */
#ifndef LIGHTS_DRIVER_EXPORT_H
#define LIGHTS_DRIVER_EXPORT_H
#include "LightsDriver.h"
#include "RageThreads.h"
class LightsDriver_Export: public LightsDriver
{
public:
LightsDriver_Export();
virtual void Set( const LightsState *ls );
/* Get the current lights state. This can be called from a thread. */
static LightsState GetState();
private:
static RageMutex m_Lock;
static LightsState m_State;
};
#endif
/*
* (c) 2006 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,100 @@
//
// LightsDriver_LinuxParallel - Parallel Port Based Lights Driver for Linux
//
// This requires root permissions to work! (run as root or suid)
// This code was written using SystemMessage Driver as template.
//
#include "global.h"
#include <sys/io.h>
#include "LightsDriver_LinuxParallel.h"
#include "ScreenManager.h"
#include "InputMapper.h"
#include "Game.h"
static const int PORT_ADDRESS = 0x378;
static const bool SCREEN_DEBUG = false;
REGISTER_SOUND_DRIVER_CLASS(LinuxParallel);
LightsDriver_LinuxParallel::LightsDriver_LinuxParallel()
{
// Give port's permissions and reset all bits to zero
ioperm( PORT_ADDRESS, 1, 1 );
outb( 0, PORT_ADDRESS );
}
LightsDriver_LinuxParallel::~LightsDriver_LinuxParallel()
{
// Reset all bits to zero and free the port's permissions
outb( 0, PORT_ADDRESS );
ioperm( PORT_ADDRESS, 1, 0 );
}
void LightsDriver_LinuxParallel::Set( const LightsState *ls )
{
// Set LightState to port
RString s;
// Prepare Screen Output too for debugging
s += "LinuxParallel Lights Driver Debug\n";
s += "Lights Mode: " + LightsModeToString(LIGHTSMAN->GetLightsMode()) + "\n";
// Cabinet Lights
int i = 0;
unsigned char output = 0;
s += "Cabinet Bits: ";
FOREACH_CabinetLight( cl )
{
s += ls->m_bCabinetLights[cl] ? '1' : '0';
if ( ls->m_bCabinetLights[cl] )
output += (unsigned char)pow((double)2,i);
i++;
}
s += "\n";
int iNumGameButtonsToShow = INPUTMAPPER->GetInputScheme()->ButtonNameToIndex( "Start" );
if( iNumGameButtonsToShow == GameButton_Invalid )
iNumGameButtonsToShow = INPUTMAPPER->GetInputScheme()->m_iButtonsPerController;
FOREACH_ENUM( GameController, gc )
{
s += ssprintf("Controller%d Bits: ",gc+1);
for( int gb=0; gb<iNumGameButtonsToShow; gb++ )
s += ls->m_bGameButtonLights[gc][gb] ? '1' : '0';
s += "\n";
}
s += ssprintf("Output Port: 0x%x\n", PORT_ADDRESS);
s += ssprintf("Output Byte: %i\n", output);
if( SCREEN_DEBUG )
SCREENMAN->SystemMessageNoAnimate( s );
// Send byte to port
outb( output, PORT_ADDRESS );
}
/*
* (c) 2004 Hugo Hromic M. <[email protected]>
*
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,42 @@
/* LightsDriver_LinuxParallel - Parallel port-based lights for Linux */
#ifndef LightsDriver_LinuxParallel_H
#define LightsDriver_LinuxParallel_H
#include "LightsDriver.h"
class LightsDriver_LinuxParallel : public LightsDriver
{
public:
LightsDriver_LinuxParallel();
virtual ~LightsDriver_LinuxParallel();
virtual void Set( const LightsState *ls );
};
#endif
/*
* (c) 2004 Hugo Hromic M. <[email protected]>
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,234 @@
#include "global.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include "LightsDriver_LinuxWeedTech.h"
#include "RageLog.h"
REGISTER_SOUND_DRIVER_CLASS(LinuxWeedTech);
// Begin serial driver //
static int fd = -1;
static LightsState CurLights;
static inline void SerialClose()
{
if( fd != 1 )
close( fd );
fd = -1;
}
static inline void SerialOut( const char *str, size_t len )
{
if( fd ==-1 )
return;
while( len )
{
ssize_t result = write( fd, str, len );
if( result == -1 )
{
LOG->Trace( "Failed to write to lights driver: %s", strerror(errno) );
SerialClose();
return;
}
len -= result;
str += result;
}
usleep( 2000 );
}
static inline void SerialOpen()
{
// Make sure we've not already opened the port
SerialClose();
// Open a fresh instance..
fd = open( "/dev/ttyS0", O_WRONLY | O_NOCTTY | O_NDELAY );
if( fd < 0 )
{
LOG->Warn( "Error opening serial port for lights. Error:: %d %s", errno, strerror(errno) );
}
else
{
struct termios my_termios;
tcgetattr( fd, &my_termios );
tcflush( fd, TCIFLUSH );
my_termios.c_cflag = B9600 | CS8 | CLOCAL | HUPCL;
cfsetospeed( &my_termios, B9600 );
tcsetattr( fd, TCSANOW, &my_termios );
}
}
// End serial driver //
/* Module maps
MODULE #A
Channel A: Marquee (Up-Left)
Channel B: Marquee (Up-Right)
Channel C: Marquee (Down-Left)
Channel D: Marquee (Down-Right)
Channel E: MenuButtons (P1)
Channel F: MenuButtons (P2)
Channel G: Bass (Left)
Channel H: Bass (Right)
Channel I: DancePad P1-Up
Channel J: DancePad P1-Down
Channel K: DancePad P1-Left
Channel L: DancePad P1-Right
Channel M: DancePad P2-Up
Channel N: DancePad P2-Down
MODULE #B
Channel A: DancePad P2-Left
Channel B: DancePad P2-Right
Channel C: <not used>
Channel D: <not used>
Channel E: <not used>
Channel F: <not used>
Channel G: <not used>
Channel H: <not used>
Channel I: <not used>
Channel J: <not used>
Channel K: <not used>
Channel L: <not used>
Channel M: <not used>
Channel N: <not used>
*/
LightsDriver_LinuxWeedTech::LightsDriver_LinuxWeedTech()
{
// Open port
SerialOpen();
// Disable device echoing
char strinit[5] = { 'A', 'X', '0', 0x0d, 0x00 };
SerialOut( strinit, 5 );
strinit[0] = 'B';
SerialOut( strinit, 5 );
}
LightsDriver_LinuxWeedTech::~LightsDriver_LinuxWeedTech()
{
// Turn off all lights
char strkill[5] = { 'A', 'W', '0', 0x0d, 0x00 };
SerialOut (strkill, 5 );
strkill[0] = 'B';
SerialOut( strkill, 5 );
// Close port
SerialClose();
}
void LightsDriver_LinuxWeedTech::Set( const LightsState *ls )
{
// Re-used var's
char str[6] = { 0x00, 0x00, 0x00, '1', 0x0d, 0x00 };
bool bOn = false;
{
LightsMode lm = LIGHTSMAN->GetLightsMode();
if( lm == LIGHTSMODE_GAMEPLAY )
{
// Since all cabinet lights flash together during gameplay.. If 1 light is on, all are on.
// However, the player's menu buttons do NOT flash. This section allows us to turn
// on multiple lights without bogging down the system with delays. ((2ms between commands req.))
FOREACH_CabinetLight( cl )
{
bOn |= ls->m_bCabinetLights[cl];
CurLights.m_bCabinetLights[cl] = ls->m_bCabinetLights[cl];
}
str[0]='A';
str[1]='W';
if( bOn )
{
str[2]='C';
str[3]='F';
}
else
{
str[2]='0';
str[3]='0';
}
// Send command
puts( str );
SerialOut( str, 6 );
return;
}
FOREACH_CabinetLight( cl )
{
// Only send the command if the light has changed states (on/off)
bOn = ls->m_bCabinetLights[cl];
if( bOn != CurLights.m_bCabinetLights[cl] )
{
if(cl == LIGHT_MARQUEE_UP_LEFT) {str[0] = 'A'; str[2] = 'A';}
else if(cl == LIGHT_MARQUEE_UP_RIGHT) {str[0] = 'A'; str[2] = 'B';}
else if(cl == LIGHT_MARQUEE_LR_LEFT) {str[0] = 'A'; str[2] = 'C';}
else if(cl == LIGHT_MARQUEE_LR_RIGHT) {str[0] = 'A'; str[2] = 'D';}
else if(cl == LIGHT_BASS_LEFT) {str[0] = 'A'; str[2] = 'G';}
else if(cl == LIGHT_BASS_RIGHT) {str[0] = 'A'; str[2] = 'H';}
if( bOn )
str[1]='L';
else
str[1]='H';
if( str[0] != 0x00 )
{
SerialOut( str, 6 );
str[0]=0x00;
}
CurLights.m_bCabinetLights[cl] = bOn;
}
}
}
FOREACH_ENUM( GameController, gc )
{
FOREACH_ENUM( GameButton, gb )
{
// Only send the command if the light has changed states (on/off)
bool bOn = ls->m_bGameButtonLights[gc][gb];
if(bOn != CurLights.m_bGameButtonLights[gc][gb]) {
if(gc == GameController_1) {
if(gb == DANCE_BUTTON_LEFT) {str[0] = 'A'; str[2] = 'I';}
if(gb == DANCE_BUTTON_RIGHT) {str[0] = 'A'; str[2] = 'J';}
if(gb == DANCE_BUTTON_UP) {str[0] = 'A'; str[2] = 'K';}
if(gb == DANCE_BUTTON_DOWN) {str[0] = 'A'; str[2] = 'L';}
if(gb == GAME_BUTTON_START) {str[0] = 'A'; str[2] = 'E';}
}
else if(gc == GameController_2) {
if(gb == DANCE_BUTTON_LEFT) {str[0] = 'A'; str[2] = 'M';}
if(gb == DANCE_BUTTON_RIGHT) {str[0] = 'A'; str[2] = 'N';}
if(gb == DANCE_BUTTON_UP) {str[0] = 'B'; str[2] = 'A';}
if(gb == DANCE_BUTTON_DOWN) {str[0] = 'B'; str[2] = 'B';}
if(gb == GAME_BUTTON_START) {str[0] = 'A'; str[2] = 'F';}
}
if( bOn )
str[1]='L';
else
str[1]='H';
if( str[0] != 0x00 )
{
//SerialOut(str, 6);
str[0]=0x00;
}
CurLights.m_bGameButtonLights[gc][gb] = bOn;
}
}
}
}
@@ -0,0 +1,45 @@
/*
* LightsDriver_LinuxWeedTech: Control lights with WTDIO-M from Weeder Technologies
* http://www.weedtech.com
*/
#ifndef LightsDriver_LinuxWeedTech_H
#define LightsDriver_LinuxWeedTech_H
#include "arch/Lights/LightsDriver.h"
class LightsDriver_LinuxWeedTech : public LightsDriver
{
public:
LightsDriver_LinuxWeedTech();
virtual ~LightsDriver_LinuxWeedTech();
virtual void Set( const LightsState *ls );
};
#endif
/*
* (c) 2003-2004 Kevin Slaughter
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,69 @@
#include "global.h"
#include "LightsDriver_SystemMessage.h"
#include "ScreenManager.h"
#include "InputMapper.h"
#include "PrefsManager.h"
REGISTER_SOUND_DRIVER_CLASS(SystemMessage);
LightsDriver_SystemMessage::LightsDriver_SystemMessage()
{
}
LightsDriver_SystemMessage::~LightsDriver_SystemMessage()
{
}
void LightsDriver_SystemMessage::Set( const LightsState *ls )
{
if( !PREFSMAN->m_bDebugLights )
return;
RString s;
s += LightsModeToString(LIGHTSMAN->GetLightsMode()) + "\n";
s += "Cabinet: ";
FOREACH_CabinetLight( cl )
{
s += ls->m_bCabinetLights[cl] ? '1' : '0';
}
s += "\n";
FOREACH_ENUM( GameController, gc )
{
s += ssprintf("Controller%d: ",gc+1);
FOREACH_ENUM( GameButton, gb )
{
s += ls->m_bGameButtonLights[gc][gb] ? '1' : '0';
}
s += "\n";
}
SCREENMAN->SystemMessageNoAnimate( s );
}
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,40 @@
#ifndef LightsDriver_SystemMessage_H
#define LightsDriver_SystemMessage_H
#include "LightsDriver.h"
class LightsDriver_SystemMessage : public LightsDriver
{
public:
LightsDriver_SystemMessage();
virtual ~LightsDriver_SystemMessage();
virtual void Set( const LightsState *ls );
};
#endif
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,136 @@
#include "global.h"
#include "LightsDriver_Win32Parallel.h"
#include "windows.h"
#include "RageUtil.h"
REGISTER_SOUND_DRIVER_CLASS(Win32Parallel);
HINSTANCE hDLL = NULL;
typedef void (WINAPI PORTOUT)(short int Port, char Data);
PORTOUT* PortOut = NULL;
typedef short int (WINAPI ISDRIVERINSTALLED)();
ISDRIVERINSTALLED* IsDriverInstalled = NULL;
const int LIGHTS_PER_PARALLEL_PORT = 8;
const int MAX_PARALLEL_PORTS = 3;
short LPT_ADDRESS[MAX_PARALLEL_PORTS] =
{
0x378, // LPT1
0x278, // LPT2
0x3bc, // LPT3
};
int CabinetLightToIndex( CabinetLight cl )
{
return cl;
}
int GameControllerAndGameButtonToIndex( GameController gc, GameButton gb )
{
CLAMP( (int&)gb, 0, 4 );
return NUM_CabinetLight + gc*4 + gb;
}
void IndexToLptAndPin( int index, int &lpt_out, int &pin_out )
{
lpt_out = index / LIGHTS_PER_PARALLEL_PORT;
ASSERT( lpt_out >= 0 && lpt_out < MAX_PARALLEL_PORTS );
pin_out = index % LIGHTS_PER_PARALLEL_PORT;
}
LightsDriver_Win32Parallel::LightsDriver_Win32Parallel()
{
// init io.dll
hDLL = LoadLibrary("parallel_lights_io.dll");
if(hDLL == NULL)
{
MessageBox(NULL, "Could not LoadLibrary( parallel_lights_io.dll ).", "ERROR", MB_OK );
return;
}
//Get the function pointers
PortOut = (PORTOUT*) GetProcAddress(hDLL, "PortOut");
IsDriverInstalled = (ISDRIVERINSTALLED*) GetProcAddress(hDLL, "IsDriverInstalled");
}
LightsDriver_Win32Parallel::~LightsDriver_Win32Parallel()
{
FreeLibrary( hDLL );
}
void LightsDriver_Win32Parallel::Set( const LightsState *ls )
{
BYTE data[MAX_PARALLEL_PORTS] =
{
0x00,
0x00,
0x00
};
{
FOREACH_CabinetLight( cl )
{
bool bOn = ls->m_bCabinetLights[cl];
int index = CabinetLightToIndex( cl );
int lpt;
int pin;
IndexToLptAndPin( index, lpt, pin );
BYTE mask = (BYTE) (0x01 << pin);
if( bOn )
data[lpt] |= mask;
else
data[lpt] &= ~mask;
}
}
FOREACH_ENUM( GameController, gc )
{
FOREACH_ENUM( GameButton, gb )
{
bool bOn = ls->m_bGameButtonLights[gc][gb];
int index = GameControllerAndGameButtonToIndex( gc, gb );
int lpt;
int pin;
IndexToLptAndPin( index, lpt, pin );
BYTE mask = (BYTE) (0x01 << pin);
if( bOn )
data[lpt] |= mask;
else
data[lpt] &= ~mask;
}
}
{
for( int i=0; i<MAX_PARALLEL_PORTS; i++ )
{
short address = LPT_ADDRESS[i];
PortOut( address, data[i] );
}
}
}
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,45 @@
/*
* LightsDriver_Win32Parallel - Control lights with Kit 74:
* http://www.google.com/search?hl=en&lr=&ie=UTF-8&oe=UTF-8&q=kit+74+relay
*/
#ifndef LightsDriver_Win32Parallel_H
#define LightsDriver_Win32Parallel_H
#include "arch/Lights/LightsDriver.h"
class LightsDriver_Win32Parallel : public LightsDriver
{
public:
LightsDriver_Win32Parallel();
virtual ~LightsDriver_Win32Parallel();
virtual void Set( const LightsState *ls );
};
#endif
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+83
View File
@@ -0,0 +1,83 @@
#include "global.h"
#include "LoadingWindow.h"
#include "PrefsManager.h"
#include "RageLog.h"
#include "arch/arch_default.h"
LoadingWindow *LoadingWindow::Create()
{
if( !PREFSMAN->m_bShowLoadingWindow )
return new LoadingWindow_Null;
#if defined(UNIX) && !defined(HAVE_GTK)
return new LoadingWindow_Null;
#endif
/* Don't load NULL by default. */
const RString drivers = "xbox,win32,macosx,gtk";
vector<RString> DriversToTry;
split( drivers, ",", DriversToTry, true );
ASSERT( DriversToTry.size() != 0 );
RString Driver;
LoadingWindow *ret = NULL;
for( unsigned i = 0; ret == NULL && i < DriversToTry.size(); ++i )
{
Driver = DriversToTry[i];
#ifdef USE_LOADING_WINDOW_MACOSX
if( !DriversToTry[i].CompareNoCase("MacOSX") ) ret = new LoadingWindow_MacOSX;
#endif
#ifdef USE_LOADING_WINDOW_GTK
if( !DriversToTry[i].CompareNoCase("Gtk") ) ret = new LoadingWindow_Gtk;
#endif
#ifdef USE_LOADING_WINDOW_WIN32
if( !DriversToTry[i].CompareNoCase("Win32") ) ret = new LoadingWindow_Win32;
#endif
#ifdef USE_LOADING_WINDOW_XBOX
if( !DriversToTry[i].CompareNoCase("Xbox") ) ret = new LoadingWindow_Xbox;
#endif
if( !DriversToTry[i].CompareNoCase("Null") ) ret = new LoadingWindow_Null;
if( ret == NULL )
continue;
RString sError = ret->Init();
if( sError != "" )
{
LOG->Info( "Couldn't load driver %s: %s", DriversToTry[i].c_str(), sError.c_str() );
SAFE_DELETE( ret );
}
}
if( ret )
LOG->Info( "Loading window: %s", Driver.c_str() );
return ret;
}
/*
* (c) 2002-2005 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+45
View File
@@ -0,0 +1,45 @@
/* LoadingWindow - opens and displays the loading banner. */
#ifndef LOADING_WINDOW_H
#define LOADING_WINDOW_H
struct RageSurface;
class LoadingWindow
{
public:
static LoadingWindow *Create();
virtual RString Init() { return RString(); }
virtual ~LoadingWindow() { }
virtual void Paint() { }
virtual void SetText( RString str ) = 0;
virtual void SetIcon( const RageSurface *pIcon ) { }
};
#endif
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,82 @@
#include "global.h"
#include "RageLog.h"
#include "RageFileManager.h"
#include "RageUtil.h"
#include "LoadingWindow_Gtk.h"
#include "LoadingWindow_GtkModule.h"
#include <dlfcn.h>
static void *Handle = NULL;
static INIT Module_Init;
static SHUTDOWN Module_Shutdown;
static SETTEXT Module_SetText;
LoadingWindow_Gtk::LoadingWindow_Gtk()
{
}
RString LoadingWindow_Gtk::Init()
{
ASSERT( Handle == NULL );
Handle = dlopen( RageFileManagerUtil::sDirOfExecutable + "/" + "GtkModule.so", RTLD_NOW );
if( Handle == NULL )
return ssprintf( "dlopen(): %s", dlerror() );
Module_Init = (INIT) dlsym(Handle, "Init");
if( !Module_Init )
return "Couldn't load symbol Module_Init";
Module_Shutdown = (SHUTDOWN) dlsym(Handle, "Shutdown");
if( !Module_Shutdown )
return "Couldn't load symbol Module_Shutdown";
Module_SetText = (SETTEXT) dlsym(Handle, "SetText");
if( !Module_SetText )
return "Couldn't load symbol Module_SetText";
const char *ret = Module_Init( &g_argc, &g_argv );
if( ret != NULL )
return ret;
return "";
}
LoadingWindow_Gtk::~LoadingWindow_Gtk()
{
if( Module_Shutdown != NULL )
Module_Shutdown();
Module_Shutdown = NULL;
if( Handle )
dlclose( Handle );
Handle = NULL;
}
void LoadingWindow_Gtk::SetText( RString s )
{
Module_SetText( s );
}
/*
* (c) 2003-2004 Glenn Maynard, Sean Burke
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,44 @@
/* LoadingWindow_Gtk - Loading window for GTK (usually X) */
#ifndef LOADING_WINDOW_GTK
#define LOADING_WINDOW_GTK
#include "LoadingWindow.h"
class LoadingWindow_Gtk: public LoadingWindow
{
public:
LoadingWindow_Gtk();
RString Init();
~LoadingWindow_Gtk();
void SetText( RString str );
};
#define USE_LOADING_WINDOW_GTK
#endif
/*
* (c) 2003-2004 Glenn Maynard, Sean Burke
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,73 @@
#include "global.h"
#include "LoadingWindow_GtkModule.h"
#include <gtk/gtk.h>
static GtkWidget *label;
static GtkWidget *window;
extern "C" const char *Init( int *argc, char ***argv )
{
const gchar *splash_image_path = "Data/splash.png";
GtkWidget *vbox;
GtkWidget *loadimage;
gtk_disable_setlocale();
if( !gtk_init_check(argc,argv) )
return "Couldn't initialize gtk (cannot open display)";
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position( GTK_WINDOW(window), GTK_WIN_POS_CENTER );
gtk_widget_realize(window);
loadimage = gtk_image_new_from_file(splash_image_path);
label = gtk_label_new(NULL);
gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_CENTER);
vbox = gtk_vbox_new(FALSE,5);
gtk_container_add(GTK_CONTAINER(window),vbox);
gtk_box_pack_start(GTK_BOX(vbox),loadimage,FALSE,FALSE,0);
gtk_box_pack_end(GTK_BOX(vbox),label,TRUE,TRUE,0);
gtk_widget_show_all(window);
gtk_main_iteration_do(FALSE);
return NULL;
}
extern "C" void Shutdown()
{
gtk_widget_hide_all(window);
g_signal_emit_by_name (G_OBJECT (window), "destroy");
while( gtk_events_pending() )
gtk_main_iteration_do(FALSE);
}
extern "C" void SetText( const char *s )
{
gtk_label_set_text(GTK_LABEL(label), s);
gtk_widget_show(label);
gtk_main_iteration_do(FALSE);
}
/*
* (c) 2003-2004 Glenn Maynard, Sean Burke
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,33 @@
#ifndef LOADING_WINDOW_MODULE_GTK
#define LOADING_WINDOW_MODULE_GTK
typedef const char *(*INIT)(int *argc, char ***argv);
typedef void (*SHUTDOWN)();
typedef void (*SETTEXT)( const char *s );
#endif
/*
* (c) 2003-2004 Glenn Maynard, Sean Burke
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,42 @@
/* LoadingWindow_MacOSX - Loading window for OSX */
#ifndef LOADING_WINDOW_MACOSX_H
#define LOADING_WINDOW_MACOSX_H
#include "LoadingWindow.h"
class LoadingWindow_MacOSX : public LoadingWindow
{
public:
LoadingWindow_MacOSX();
~LoadingWindow_MacOSX();
void SetText( RString str );
};
#define USE_LOADING_WINDOW_MACOSX
#endif
/*
* (c) 2003-2005, 2008 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,150 @@
#import <Cocoa/Cocoa.h>
#import "ProductInfo.h"
#import "LoadingWindow_MacOSX.h"
#import "RageUtil.h"
#import "RageFile.h"
@interface LoadingWindowHelper : NSObject
{
@public
NSWindow *m_Window;
NSTextView *m_Text;
NSAutoreleasePool *m_Pool;
}
- (void) setupWindow:(NSImage *)image;
@end
@implementation LoadingWindowHelper
- (void) setupWindow:(NSImage *)image
{
NSSize size = [image size];
NSRect viewRect, windowRect;
float height = 0.0f;
NSFont *font = [NSFont systemFontOfSize:0.0f];
NSRect textRect;
// Just give it a size until it is created.
textRect = NSMakeRect( 0, 0, size.width, size.height );
m_Text = [[NSTextView alloc] initWithFrame:textRect];
[m_Text setFont:font];
height = [[m_Text layoutManager] defaultLineHeightForFont:font]*3 + 4;
textRect = NSMakeRect( 0, 0, size.width, height );
[m_Text setFrame:textRect];
[m_Text setEditable:NO];
[m_Text setSelectable:NO];
[m_Text setDrawsBackground:YES];
[m_Text setBackgroundColor:[NSColor lightGrayColor]];
[m_Text setAlignment:NSCenterTextAlignment];
[m_Text setHorizontallyResizable:NO];
[m_Text setVerticallyResizable:NO];
[m_Text setString:@"Initializing Hardware..."];
viewRect = NSMakeRect( 0, height, size.width, size.height );
NSImageView *iView = [[NSImageView alloc] initWithFrame:viewRect];
[iView setImage:image];
[iView setImageFrameStyle:NSImageFrameNone];
windowRect = NSMakeRect( 0, 0, size.width, size.height + height );
m_Window = [[NSWindow alloc] initWithContentRect:windowRect
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:YES];
NSView *view = [m_Window contentView];
// Set some properties.
[m_Window setOneShot:YES];
[m_Window setReleasedWhenClosed:YES];
[m_Window setExcludedFromWindowsMenu:YES];
[m_Window useOptimizedDrawing:YES];
[m_Window setTitle:@PRODUCT_FAMILY];
[m_Window center];
// Set subviews.
[view addSubview:m_Text];
[view addSubview:iView];
[m_Text release];
[iView release];
// Display the window.
[m_Window makeKeyAndOrderFront:nil];
}
@end
static LoadingWindowHelper *g_Helper = nil;
LoadingWindow_MacOSX::LoadingWindow_MacOSX()
{
RageFile f;
RString data;
vector<RString> vs;
GetDirListing( "Data/splash*.png", vs, false, true );
if( vs.empty() || !f.Open(vs[0]) )
return;
f.Read( data );
if( data.empty() )
return;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSImage *image = nil;
NSData *d = [[NSData alloc] initWithBytes:data.data() length:data.length()];
image = [[NSImage alloc] initWithData:d];
[d release];
if( !image )
{
[pool release];
return;
}
g_Helper = [[LoadingWindowHelper alloc] init];
g_Helper->m_Pool = pool;
[g_Helper performSelectorOnMainThread:@selector(setupWindow:) withObject:image waitUntilDone:YES];
[image release];
}
LoadingWindow_MacOSX::~LoadingWindow_MacOSX()
{
if( !g_Helper )
return;
NSAutoreleasePool *pool = g_Helper->m_Pool;
[g_Helper->m_Window performSelectorOnMainThread:@selector(close) withObject:nil waitUntilDone:YES];
[g_Helper release];
g_Helper = nil;
[pool release];
}
void LoadingWindow_MacOSX::SetText( RString str )
{
if( !g_Helper )
return;
NSString *s = [[NSString alloc] initWithUTF8String:str];
[g_Helper->m_Text performSelectorOnMainThread:@selector(setString:) withObject:(s ? s : @"") waitUntilDone:NO];
[s release];
}
/*
* (c) 2003-2006, 2008 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,39 @@
#ifndef LOADING_WINDOW_NULL_H
#define LOADING_WINDOW_NULL_H
#include "LoadingWindow.h"
class LoadingWindow_Null: public LoadingWindow
{
public:
void SetText( RString str ) { }
};
#define USE_LOADING_WINDOW_NULL
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,194 @@
#include "global.h"
#include "RageUtil.h"
#include "LoadingWindow_Win32.h"
#include "RageFileManager.h"
#include "archutils/win32/WindowsResources.h"
#include "archutils/win32/WindowIcon.h"
#include "archutils/win32/ErrorStrings.h"
#include <windows.h>
#include "RageSurface_Load.h"
#include "RageSurface.h"
#include "RageSurfaceUtils.h"
#include "RageLog.h"
#include "ProductInfo.h"
#include "LocalizedString.h"
#include "RageSurfaceUtils_Zoom.h"
static HBITMAP g_hBitmap = NULL;
/* Load a RageSurface into a GDI surface. */
static HBITMAP LoadWin32Surface( RageSurface *&s )
{
RageSurfaceUtils::ConvertSurface( s, s->w, s->h, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0 );
HDC hScreen = GetDC(NULL);
ASSERT_M( hScreen, werr_ssprintf(GetLastError(), "hScreen") );
HBITMAP bitmap = CreateCompatibleBitmap( hScreen, s->w, s->h );
ASSERT_M( bitmap, werr_ssprintf(GetLastError(), "CreateCompatibleBitmap") );
HDC BitmapDC = CreateCompatibleDC( hScreen );
SelectObject( BitmapDC, bitmap );
/* This is silly, but simple. We only do this once, on a small image. */
for( int y = 0; y < s->h; ++y )
{
unsigned const char *line = ((unsigned char *) s->pixels) + (y * s->pitch);
for( int x = 0; x < s->w; ++x )
{
unsigned const char *data = line + (x*s->format->BytesPerPixel);
SetPixelV( BitmapDC, x, y, RGB( data[3], data[2], data[1] ) );
}
}
SelectObject( BitmapDC, NULL );
DeleteObject( BitmapDC );
ReleaseDC( NULL, hScreen );
return bitmap;
}
static HBITMAP LoadWin32Surface( RString sFile, HWND hWnd )
{
RString error;
RageSurface *pSurface = RageSurfaceUtils::LoadFile( sFile, error );
if( pSurface == NULL )
return NULL;
/* Resize the splash image to fit the dialog. Stretch to fit horizontally,
* maintaining aspect ratio. */
{
RECT r;
GetClientRect( hWnd, &r );
int iWidth = r.right;
float fRatio = (float) iWidth / pSurface->w;
int iHeight = lrintf( pSurface->h * fRatio );
RageSurfaceUtils::Zoom( pSurface, iWidth, iHeight );
}
HBITMAP ret = LoadWin32Surface( pSurface );
delete pSurface;
return ret;
}
BOOL CALLBACK LoadingWindow_Win32::WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_INITDIALOG:
{
vector<RString> vs;
GetDirListing( "Data/splash*.png", vs, false, true );
if( !vs.empty() )
g_hBitmap = LoadWin32Surface( vs[0], hWnd );
}
if( g_hBitmap == NULL )
g_hBitmap = LoadWin32Surface( "Data/splash.bmp", hWnd );
SendMessage(
GetDlgItem(hWnd,IDC_SPLASH),
STM_SETIMAGE,
(WPARAM) IMAGE_BITMAP,
(LPARAM) (HANDLE) g_hBitmap );
SetWindowTextA( hWnd, PRODUCT_ID );
break;
case WM_DESTROY:
DeleteObject( g_hBitmap );
g_hBitmap = NULL;
break;
}
return FALSE;
}
void LoadingWindow_Win32::SetIcon( const RageSurface *pIcon )
{
if( m_hIcon != NULL )
DestroyIcon( m_hIcon );
m_hIcon = IconFromSurface( pIcon );
if( m_hIcon != NULL )
SetClassLong( hwnd, GCL_HICON, (LONG) m_hIcon );
}
LoadingWindow_Win32::LoadingWindow_Win32()
{
m_hIcon = NULL;
hwnd = CreateDialog( handle.Get(), MAKEINTRESOURCE(IDD_LOADING_DIALOG), NULL, WndProc );
for( unsigned i = 0; i < 3; ++i )
text[i] = "ABC"; /* always set on first call */
SetText( "" );
Paint();
}
LoadingWindow_Win32::~LoadingWindow_Win32()
{
if( hwnd )
DestroyWindow( hwnd );
if( m_hIcon != NULL )
DestroyIcon( m_hIcon );
}
void LoadingWindow_Win32::Paint()
{
SendMessage( hwnd, WM_PAINT, 0, 0 );
/* Process all queued messages since the last paint. This allows the window to
* come back if it loses focus during load. */
MSG msg;
while( PeekMessage( &msg, hwnd, 0, 0, PM_NOREMOVE ) )
{
GetMessage(&msg, hwnd, 0, 0 );
DispatchMessage( &msg );
}
}
void LoadingWindow_Win32::SetText( RString sText )
{
vector<RString> asMessageLines;
split( sText, "\n", asMessageLines, false );
while( asMessageLines.size() < 3 )
asMessageLines.push_back( "" );
const int msgid[] = { IDC_STATIC_MESSAGE1, IDC_STATIC_MESSAGE2, IDC_STATIC_MESSAGE3 };
for( unsigned i = 0; i < 3; ++i )
{
if( text[i] == asMessageLines[i] )
continue;
text[i] = asMessageLines[i];
HWND hwndItem = ::GetDlgItem( hwnd, msgid[i] );
::SetWindowText( hwndItem, ConvertUTF8ToACP(asMessageLines[i]).c_str() );
}
}
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,56 @@
/* LoadingWindow_Win32 - Loading window using a Windows dialog box. */
#ifndef LOADING_WINDOW_WIN32_H
#define LOADING_WINDOW_WIN32_H
#include "LoadingWindow.h"
#include <windows.h>
#include "archutils/Win32/AppInstance.h"
class LoadingWindow_Win32: public LoadingWindow
{
public:
LoadingWindow_Win32();
~LoadingWindow_Win32();
void SetText( RString sText );
void Paint();
void SetIcon( const RageSurface *pIcon );
private:
AppInstance handle;
HWND hwnd;
RString text[3];
HICON m_hIcon;
static BOOL CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
};
#define USE_LOADING_WINDOW_WIN32
#endif
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,179 @@
#include "global.h"
#include "LoadingWindow_Xbox.h"
#include "RageLog.h"
#include "ProductInfo.h"
LPDIRECT3D8 g_pD3D = NULL; // DirectX Object
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL; // Screen Object
LPDIRECT3DTEXTURE8 splash = NULL; // splash texture
LPD3DXSPRITE g_sprite = NULL; // sprite object
LoadingWindow_Xbox::LoadingWindow_Xbox()
{
// Initialise Direct3D
g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);
// Create a structure to hold the settings for our device
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
// Fill the structure.
// Set fullscreen 640x480x32 mode
d3dpp.BackBufferWidth = 640;
d3dpp.BackBufferHeight = 480;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
// Create one backbuffer and a zbuffer
d3dpp.BackBufferCount = 1;
// Set up how the backbuffer is "presented" to the frontbuffer each time
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
//Create a Direct3D device.
g_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp, &g_pD3DDevice);
g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
// Create the sprite object (for painting the image)
D3DXCreateSprite(g_pD3DDevice, &g_sprite);
// Load the default font
XFONT_OpenDefaultFont(&font);
font->SetTextColor(D3DCOLOR_XRGB(255,255,255));
font->SetTextAlignment(XFONT_CENTER);
// Load the splash.png
HRESULT result = D3DXCreateTextureFromFileA(g_pD3DDevice, "D:\\Data\\splash.png", &splash);
useImage = (result == D3D_OK);
if(!useImage)
LOG->Trace("Error loading splash.png - %i", result);
SetText(RString("Loading songs"));
}
LoadingWindow_Xbox::~LoadingWindow_Xbox()
{
g_pD3DDevice->Release();
g_pD3D->Release();
font->Release();
}
void LoadingWindow_Xbox::Paint()
{
LPDIRECT3DSURFACE8 g_pFrontBuffer;
if(text == "")
return;
g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
g_pD3DDevice->BeginScene();
g_pD3DDevice->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&g_pFrontBuffer);
if(useImage)
{
// Draw the splash image
// Only draw if the splash texture is successfully loaded
D3DXVECTOR2 pos;
pos.x = 70.0f;
pos.y = 30.0f;
g_sprite->Begin();
g_sprite->Draw(splash, NULL, NULL, NULL, NULL, &pos, 0xFFFFFFFF);
g_sprite->End();
}
else
{
// Lo-fi version: print the product name and version at the top of the screen
font->SetTextColor(D3DCOLOR_XRGB(255, 0, 0));
RString title = "Version ";
title = title + PRODUCT_VER;
WCHAR wc_title[200] = {0};
swprintf(wc_title, L"%S", title.c_str());
font->TextOut(g_pFrontBuffer, L"StepMania", -1, 320, 30);
font->SetTextColor(D3DCOLOR_XRGB(255, 255, 0));
font->TextOut(g_pFrontBuffer, wc_title, -1, 320, 40 + font->GetTextHeight());
}
// Draw the text on the screen
font->SetTextColor(D3DCOLOR_XRGB(255, 255, 255));
basic_string <char>::size_type newLineIndex = text.find("\n", 0);
int y = 240;
if(newLineIndex == RString.npos)
{
WCHAR wc_text[200] = {0};
swprintf(wc_text, L"%S", text.c_str());
font->TextOut(g_pFrontBuffer, wc_text, wcslen(wc_text), 320, y);
}
else
{
int start = 0;
while(start != RString.npos)
{
RString toPrint;
if(newLineIndex != RString.npos)
toPrint = text.substr(start, newLineIndex - start);
else
toPrint = text.substr(start);
if(toPrint != "")
{
WCHAR wc_text[200] = {0};
swprintf(wc_text, L"%S", toPrint.c_str());
font->TextOut(g_pFrontBuffer, wc_text, wcslen(wc_text), 320, y);
}
y = y + font->GetTextHeight() + 10;
if(newLineIndex != RString.npos)
start = newLineIndex + 1;
else
start = RString.npos;
newLineIndex = text.find("\n", start);
}
}
g_pFrontBuffer->Release();
g_pD3DDevice->EndScene();
g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
}
void LoadingWindow_Xbox::SetText(RString str)
{
text = str ;
}
/*
* (c) 2004 Ryan Dortmans
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,51 @@
#ifndef LOADING_WINDOW_XBOX_H
#define LOADING_WINDOW_XBOX_H
#include "LoadingWindow.h"
#include <xtl.h>
#include <xfont.h>
#define XFONT_TRUETYPE
class LoadingWindow_Xbox: public LoadingWindow
{
public:
LoadingWindow_Xbox();
~LoadingWindow_Xbox();
void Paint();
void SetText(RString str);
protected:
RString text ;
XFONT* font;
bool useImage;
};
#define USE_LOADING_WINDOW_XBOX
#endif
/*
* (c) 2004 Ryan Dortmans
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,33 @@
#include "global.h"
#include "LowLevelWindow.h"
#include "arch/arch_default.h"
LowLevelWindow *LowLevelWindow::Create()
{
return new ARCH_LOW_LEVEL_WINDOW;
}
/*
* (c) 2002-2005 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+72
View File
@@ -0,0 +1,72 @@
#ifndef LOW_LEVEL_WINDOW_H
#define LOW_LEVEL_WINDOW_H
/* This handles low-level operations that OGL 1.x doesn't give us. */
#include <set>
class DisplayResolution;
typedef set<DisplayResolution> DisplayResolutions;
class VideoModeParams;
class RenderTarget;
struct RenderTargetParam;
class LowLevelWindow
{
public:
static LowLevelWindow *Create();
virtual ~LowLevelWindow() { }
virtual void *GetProcAddress( RString s ) = 0;
// Return "" if mode change was successful, otherwise an error message.
// bNewDeviceOut is set true if a new device was created and textures
// need to be reloaded.
virtual RString TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut ) = 0;
virtual void GetDisplayResolutions( DisplayResolutions &out ) const = 0;
virtual void LogDebugInformation() const { }
virtual bool IsSoftwareRenderer( RString &sError ) { return false; }
virtual void SwapBuffers() = 0;
virtual void Update() { }
virtual const VideoModeParams &GetActualVideoModeParams() const = 0;
virtual bool SupportsRenderToTexture() const { return false; }
virtual RenderTarget *CreateRenderTarget() { return NULL; }
virtual bool SupportsThreadedRendering() { return false; }
virtual void BeginConcurrentRenderingMainThread() { }
virtual void EndConcurrentRenderingMainThread() { }
virtual void BeginConcurrentRendering() { }
virtual void EndConcurrentRendering() { }
};
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,76 @@
#ifndef LOW_LEVEL_WINDOW_MACOSX_H
#define LOW_LEVEL_WINDOW_MACOSX_H
#include "LowLevelWindow.h"
#include "RageDisplay.h"
#include <objc/objc.h>
typedef const struct __CFDictionary *CFDictionaryRef;
/* XXX: This was changed to a uint32_t later and its header file cannot be included
* since Style conflicts. Ugh. */
typedef struct _CGDirectDisplayID *CGDirectDisplayID;
class LowLevelWindow_MacOSX : public LowLevelWindow
{
VideoModeParams m_CurrentParams;
id m_WindowDelegate;
id m_Context;
id m_BGContext;
CFDictionaryRef m_CurrentDisplayMode;
CGDirectDisplayID m_DisplayID;
public:
LowLevelWindow_MacOSX();
~LowLevelWindow_MacOSX();
void *GetProcAddress( RString s );
RString TryVideoMode( const VideoModeParams& p, bool& newDeviceOut );
void GetDisplayResolutions( DisplayResolutions &dr ) const;
void SwapBuffers();
void Update();
const VideoModeParams &GetActualVideoModeParams() const { return m_CurrentParams; }
bool SupportsRenderToTexture() const { return true; }
RenderTarget *CreateRenderTarget();
bool SupportsThreadedRendering() { return m_BGContext; }
void BeginConcurrentRendering();
private:
void ShutDownFullScreen();
int ChangeDisplayMode( const VideoModeParams& p );
void SetActualParamsFromMode( CFDictionaryRef mode );
};
#ifdef ARCH_LOW_LEVEL_WINDOW
#error "More than one LowLevelWindow selected!"
#endif
#define ARCH_LOW_LEVEL_WINDOW LowLevelWindow_MacOSX
#endif
/*
* (c) 2005-2006, 2008 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,630 @@
#import "global.h"
#import "LowLevelWindow_MacOSX.h"
#import "DisplayResolutions.h"
#import "RageUtil.h"
#import "RageThreads.h"
#import "RageDisplay_OGL_Helpers.h"
#import "arch/ArchHooks/ArchHooks.h"
#import <Cocoa/Cocoa.h>
#import <OpenGl/OpenGl.h>
#import <OpenGL/gl.h>
#import <mach-o/dyld.h>
// Bad header!
extern "C" {
#include <IOKit/graphics/IOGraphicsLib.h>
}
static const unsigned int g_iStyleMask = NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask | NSResizableWindowMask;
static bool g_bResized;
static int g_iWidth;
static int g_iHeight;
static RageMutex g_ResizeLock( "Window resize lock." );
// Simple helper class
class AutoreleasePool
{
AutoreleasePool( const AutoreleasePool& );
AutoreleasePool &operator=( const AutoreleasePool& );
NSAutoreleasePool *m_Pool;
public:
AutoreleasePool() { m_Pool = [[NSAutoreleasePool alloc] init]; }
~AutoreleasePool() { [m_Pool release]; }
};
#define POOL AutoreleasePool UNIQUE_NAME(pool)
// Window delegate class
@interface SMWindowDelegate : NSObject
{
@public
NSWindow *m_Window;
}
- (void) windowDidBecomeKey:(NSNotification *)aNotification;
- (void) windowDidResignKey:(NSNotification *)aNotification;
- (void) windowWillClose:(NSNotification *)aNotification;
- (void) windowDidResize:(NSNotification *)aNotification;
// XXX maybe use whichever screen contains the window? Hard for me to test though.
//- (void) windowDidChangeScreen:(NSNotification *)aNotification;
// Helper methods to perform actions on the main thread.
- (void) setupWindow;
- (void) closeWindow;
- (void) setParams:(NSValue *)params;
@end
@implementation SMWindowDelegate
- (void) windowDidBecomeKey:(NSNotification *)aNotification
{
HOOKS->SetHasFocus( true );
}
- (void) windowDidResignKey:(NSNotification *)aNotification
{
HOOKS->SetHasFocus( false );
}
- (void) windowWillClose:(NSNotification *)aNotification
{
ArchHooks::SetUserQuit();
}
- (void) windowDidResize:(NSNotification *)aNotification
{
id window = [aNotification object];
NSSize size = [NSWindow contentRectForFrameRect:[window frame] styleMask:g_iStyleMask].size;
LockMut( g_ResizeLock );
g_bResized = true;
g_iWidth = int( size.width );
g_iHeight = int( size.height );
}
- (void) setupWindow
{
NSRect rect = NSMakeRect( 0, 0, 0, 0 );
m_Window = [[NSWindow alloc] initWithContentRect:rect
styleMask:g_iStyleMask
backing:NSBackingStoreBuffered
defer:YES];
[m_Window setExcludedFromWindowsMenu:YES];
[m_Window useOptimizedDrawing:YES];
[m_Window setReleasedWhenClosed:NO];
[m_Window setDelegate:self];
}
- (void) closeWindow
{
[m_Window setDelegate:nil];
[m_Window close];
[m_Window release];
}
- (void) setParams:(NSValue *)params
{
const VideoModeParams &p = *(const VideoModeParams *)[params pointerValue];
NSRect contentRect = { { 0, 0 }, { p.width, p.height } };
[m_Window setContentSize:contentRect.size];
[m_Window setTitle:[NSString stringWithUTF8String:p.sWindowTitle.c_str()]];
[m_Window center];
[m_Window makeKeyAndOrderFront:nil];
}
@end
enum GLContextType
{
WINDOWED,
FULL_SCREEN,
PIXEL_BUFFER
};
static NSOpenGLContext *CreateOGLContext( GLContextType type, int iColorSize, int iAlphaSize, int iDepthSize, NSOpenGLContext *share, bool &bShared )
{
NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFANoRecovery, // so we can share with the full screen context
NSOpenGLPFAAccelerated,
NSOpenGLPFAMinimumPolicy,
NSOpenGLPFAColorSize, NSOpenGLPixelFormatAttribute(iColorSize),
NSOpenGLPFAAlphaSize, NSOpenGLPixelFormatAttribute(iAlphaSize),
NSOpenGLPFADepthSize, NSOpenGLPixelFormatAttribute(iDepthSize),
NSOpenGLPixelFormatAttribute(0), // 9
NSOpenGLPixelFormatAttribute(0), // 10
NSOpenGLPixelFormatAttribute(0), // 11
NSOpenGLPixelFormatAttribute(0), // 12
NSOpenGLPixelFormatAttribute(0) // Must be at the end.
};
const int n = 9; // The first noncommon index.
switch( type )
{
case WINDOWED:
attrs[n+0] = NSOpenGLPFAWindow;
attrs[n+1] = NSOpenGLPFADoubleBuffer;
break;
case FULL_SCREEN:
attrs[n+0] = NSOpenGLPFAFullScreen;
attrs[n+1] = NSOpenGLPFADoubleBuffer;
attrs[n+2] = NSOpenGLPFAScreenMask;
attrs[n+3] = NSOpenGLPixelFormatAttribute( CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay) );
break;
case PIXEL_BUFFER:
attrs[n+0] = NSOpenGLPFAOffScreen;
attrs[n+1] = NSOpenGLPFAPixelBuffer;
break;
}
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
if( !pixelFormat )
return nil;
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:share];
bShared = share && context;
if( !context )
context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
[pixelFormat release];
return context;
}
class RenderTarget_MacOSX : public RenderTarget
{
public:
RenderTarget_MacOSX( id shareContext );
~RenderTarget_MacOSX();
void Create( const RenderTargetParam &param, int &iTextureWidthOut, int &iTextureHeightOut );
unsigned GetTexture() const { return m_iTexHandle; }
void StartRenderingTo();
void FinishRenderingTo();
private:
NSOpenGLContext *m_ShareContext, *m_OldContext, *m_PBufferContext;
GLuint m_iTexHandle;
int m_iWidth, m_iHeight;
};
RenderTarget_MacOSX::RenderTarget_MacOSX( id shareContext )
{
m_ShareContext = shareContext;
m_OldContext = nil;
m_PBufferContext = nil;
m_iTexHandle = 0;
m_iWidth = 0;
m_iHeight = 0;
}
RenderTarget_MacOSX::~RenderTarget_MacOSX()
{
POOL;
[m_PBufferContext release];
if( m_iTexHandle )
glDeleteTextures( 1, &m_iTexHandle );
}
void RenderTarget_MacOSX::Create( const RenderTargetParam &param, int &iTextureWidthOut, int &iTextureHeightOut )
{
POOL;
m_iWidth = param.iWidth;
m_iHeight = param.iHeight;
// PBuffer needs to be a power of 2.
int iTextureWidth = power_of_two( param.iWidth );
int iTextureHeight = power_of_two( param.iHeight );
// Create the PBuffer.
unsigned long format = param.bWithAlpha? GL_RGBA:GL_RGB;
NSOpenGLPixelBuffer *PBuffer = [[NSOpenGLPixelBuffer alloc] initWithTextureTarget:GL_TEXTURE_2D
textureInternalFormat:format
textureMaxMipMapLevel:0 // No idea.
pixelsWide:iTextureWidth
pixelsHigh:iTextureHeight];
DEBUG_ASSERT( PBuffer );
// Create an OGL context.
bool bShared = false;
m_PBufferContext = CreateOGLContext( PIXEL_BUFFER, 24, param.bWithAlpha? 8:0, param.bWithDepthBuffer? 16:0, m_ShareContext, bShared );
DEBUG_ASSERT( m_PBufferContext );
DEBUG_ASSERT( bShared );
[m_PBufferContext setPixelBuffer:PBuffer cubeMapFace:0 mipMapLevel:0
currentVirtualScreen:[m_ShareContext currentVirtualScreen]];
[PBuffer release]; // XXX: Hopefully this is retained by the PBufferContext.
glGenTextures( 1, &m_iTexHandle );
glBindTexture( GL_TEXTURE_2D, m_iTexHandle );
while( glGetError() != GL_NO_ERROR )
;
iTextureWidthOut = iTextureWidth;
iTextureHeightOut = iTextureHeight;
glTexImage2D( GL_TEXTURE_2D, 0, param.bWithAlpha? GL_RGBA8:GL_RGB8,
iTextureWidth, iTextureHeight, 0, param.bWithAlpha? GL_RGBA:GL_RGB,
GL_UNSIGNED_BYTE, NULL );
GLenum error = glGetError();
ASSERT_M( error == GL_NO_ERROR, RageDisplay_OGL_Helpers::GLToString(error) );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
}
void RenderTarget_MacOSX::StartRenderingTo()
{
DEBUG_ASSERT( !m_OldContext );
m_OldContext = [NSOpenGLContext currentContext];
[m_PBufferContext makeCurrentContext];
glViewport( 0, 0, m_iWidth, m_iHeight );
}
void RenderTarget_MacOSX::FinishRenderingTo()
{
DEBUG_ASSERT( m_OldContext );
glBindTexture( GL_TEXTURE_2D, m_iTexHandle );
while( glGetError() != GL_NO_ERROR )
;
glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_iWidth, m_iHeight );
GLenum error = glGetError();
ASSERT_M( error == GL_NO_ERROR, RageDisplay_OGL_Helpers::GLToString(error) );
glBindTexture( GL_TEXTURE_2D, 0 );
[m_OldContext makeCurrentContext];
m_OldContext = nil;
}
LowLevelWindow_MacOSX::LowLevelWindow_MacOSX() : m_Context(nil), m_BGContext(nil), m_CurrentDisplayMode(NULL), m_DisplayID(0)
{
POOL;
m_WindowDelegate = [[SMWindowDelegate alloc] init];
[m_WindowDelegate performSelectorOnMainThread:@selector(setupWindow) withObject:nil waitUntilDone:YES];
m_CurrentParams.windowed = true; // We are essentially windowed to begin with.
SetActualParamsFromMode( CGDisplayCurrentMode(kCGDirectMainDisplay) );
HOOKS->SetHasFocus( [NSApp isActive] );
}
LowLevelWindow_MacOSX::~LowLevelWindow_MacOSX()
{
POOL;
ShutDownFullScreen();
[m_Context clearDrawable];
[m_Context release];
[m_BGContext clearDrawable];
[m_BGContext release];
[m_WindowDelegate performSelectorOnMainThread:@selector(closeWindow) withObject:nil waitUntilDone:YES];
[m_WindowDelegate release];
}
void *LowLevelWindow_MacOSX::GetProcAddress( RString s )
{
// http://developer.apple.com/qa/qa2001/qa1188.html
// Both functions mentioned in there are deprecated in 10.4.
const RString& symbolName( '_' + s );
const uint32_t count = _dyld_image_count();
NSSymbol symbol = NULL;
const uint32_t options = NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR;
for( uint32_t i = 0; i < count && !symbol; ++i )
symbol = NSLookupSymbolInImage( _dyld_get_image_header(i), symbolName, options );
return symbol ? NSAddressOfSymbol( symbol ) : NULL;
}
RString LowLevelWindow_MacOSX::TryVideoMode( const VideoModeParams& p, bool& newDeviceOut )
{
// Always set these params.
m_CurrentParams.bSmoothLines = p.bSmoothLines;
m_CurrentParams.bTrilinearFiltering = p.bTrilinearFiltering;
m_CurrentParams.bAnisotropicFiltering = p.bAnisotropicFiltering;
m_CurrentParams.interlaced = p.interlaced;
m_CurrentParams.PAL = p.PAL;
m_CurrentParams.fDisplayAspectRatio = p.fDisplayAspectRatio;
#define X(x) p.x != m_CurrentParams.x
const bool bRebuildContext = X(bpp) || X(windowed) || !m_Context;
const bool bChangeMode = X(width) || X(height) || X(rate) || bRebuildContext;
const bool bChangeVsync = X(vsync) || bRebuildContext;
#undef X
if( !bChangeMode && !bChangeVsync )
return RString();
POOL;
newDeviceOut = false;
ASSERT( p.bpp == 16 || p.bpp == 32 );
// If we don't have focus, we cannot be full screen.
if( p.windowed || !HOOKS->AppHasFocus() )
{
if( bRebuildContext )
{
bool bShared;
NSOpenGLContext *newContext = CreateOGLContext( WINDOWED, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared );
if( !newContext )
return "Failed to create OGL context.";
ShutDownFullScreen();
[m_Context release];
m_Context = newContext;
newDeviceOut = !bShared;
m_CurrentParams.bpp = p.bpp;
[m_BGContext release];
m_BGContext = nil;
m_BGContext = CreateOGLContext( WINDOWED, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared );
if( m_BGContext && !bShared )
{
[m_BGContext release];
m_BGContext = nil;
}
}
[m_WindowDelegate performSelectorOnMainThread:@selector(setParams:) withObject:[NSValue valueWithPointer:&p] waitUntilDone:YES];
[m_Context setView:[((SMWindowDelegate *)m_WindowDelegate)->m_Window contentView]];
[m_Context update];
[m_Context makeCurrentContext];
m_CurrentParams.windowed = true;
SetActualParamsFromMode( CGDisplayCurrentMode(kCGDirectMainDisplay) );
m_CurrentParams.vsync = p.vsync; // hack
return RString();
}
if( bChangeMode )
{
int result = ChangeDisplayMode( p );
if( result )
return ssprintf( "Failed to switch to full screen:%d x %d @ %d. Error %d.",
p.width, p.height, p.rate, result );
}
if( bRebuildContext )
{
bool bShared;
NSOpenGLContext *newContext = CreateOGLContext( FULL_SCREEN, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared );
if( !newContext )
return "Failed to create full screen OGL context.";
[m_Context clearDrawable];
[m_Context release];
m_Context = newContext;
newDeviceOut = !bShared;
m_CurrentParams.bpp = p.bpp;
[m_BGContext release];
m_BGContext = CreateOGLContext( FULL_SCREEN, p.bpp == 16? 16:24, p.bpp == 16? 1:8, 16, m_Context, bShared );
if( m_BGContext && !bShared )
{
[m_BGContext release];
m_BGContext = nil;
}
}
[m_Context setFullScreen];
[m_Context update];
[m_Context makeCurrentContext];
if( bChangeVsync )
{
long swap = p.vsync ? 1 : 0;
[m_Context setValues:&swap forParameter:NSOpenGLCPSwapInterval];
m_CurrentParams.vsync = p.vsync;
}
return RString();
}
void LowLevelWindow_MacOSX::ShutDownFullScreen()
{
if( m_CurrentParams.windowed )
return;
ASSERT( m_CurrentDisplayMode );
// Clear the front and back framebuffers before switching out of FullScreen mode.
// (This is not strictly necessary, but avoids an untidy flash of garbage.)
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClear( GL_COLOR_BUFFER_BIT );
[m_Context flushBuffer];
glClear( GL_COLOR_BUFFER_BIT );
[m_Context flushBuffer];
[NSOpenGLContext clearCurrentContext];
[m_Context clearDrawable];
[m_BGContext clearDrawable];
CGDisplayErr err = CGDisplaySwitchToMode( kCGDirectMainDisplay, m_CurrentDisplayMode );
ASSERT( err == kCGErrorSuccess );
CGDisplayShowCursor( kCGDirectMainDisplay );
err = CGDisplayRelease( m_DisplayID );
ASSERT( err == kCGErrorSuccess );
SetActualParamsFromMode( m_CurrentDisplayMode );
// We don't own this so we cannot release it.
m_CurrentDisplayMode = NULL;
m_CurrentParams.windowed = true;
}
int LowLevelWindow_MacOSX::ChangeDisplayMode( const VideoModeParams& p )
{
CFDictionaryRef mode = NULL;
CFDictionaryRef newMode;
CGDisplayErr err;
if( !m_CurrentDisplayMode )
{
m_DisplayID = CGMainDisplayID();
if( (err = CGDisplayCapture(m_DisplayID)) != kCGErrorSuccess )
return err;
// Only hide the first time we go to full screen.
CGDisplayHideCursor( kCGDirectMainDisplay );
mode = CGDisplayCurrentMode( kCGDirectMainDisplay );
}
if( p.rate == REFRESH_DEFAULT )
newMode = CGDisplayBestModeForParameters( kCGDirectMainDisplay, p.bpp, p.width, p.height, NULL );
else
newMode = CGDisplayBestModeForParametersAndRefreshRate( kCGDirectMainDisplay, p.bpp,
p.width, p.height, p.rate, NULL );
err = CGDisplaySwitchToMode( kCGDirectMainDisplay, newMode );
if( err != kCGErrorSuccess )
return err; // We don't own mode, don't release it.
if( !m_CurrentDisplayMode )
m_CurrentDisplayMode = mode;
m_CurrentParams.windowed = false;
SetActualParamsFromMode( newMode );
return 0;
}
void LowLevelWindow_MacOSX::SetActualParamsFromMode( CFDictionaryRef mode )
{
SInt32 rate;
bool ret = CFNumberGetValue( (CFNumberRef)CFDictionaryGetValue(mode, CFSTR("RefreshRate")),
kCFNumberSInt32Type, &rate );
if( !ret || rate == 0)
rate = 60;
m_CurrentParams.rate = rate;
if( !m_CurrentParams.windowed )
{
long swap;
m_CurrentParams.width = CGDisplayPixelsWide( kCGDirectMainDisplay );
m_CurrentParams.height = CGDisplayPixelsHigh( kCGDirectMainDisplay );
CGLGetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &swap );
m_CurrentParams.vsync = swap != 0;
}
else
{
NSSize size = [[((SMWindowDelegate *)m_WindowDelegate)->m_Window contentView] frame].size;
m_CurrentParams.width = int(size.width);
m_CurrentParams.height = int(size.height);
}
m_CurrentParams.bpp = CGDisplayBitsPerPixel( kCGDirectMainDisplay );
}
static int GetIntValue( CFTypeRef r )
{
int ret;
if( !r || CFGetTypeID(r) != CFNumberGetTypeID() || !CFNumberGetValue(CFNumberRef(r), kCFNumberIntType, &ret) )
return 0;
return ret;
}
static bool GetBoolValue( CFTypeRef r )
{
return r && CFGetTypeID( r ) == CFBooleanGetTypeID() && CFBooleanGetValue( CFBooleanRef(r) );
}
void LowLevelWindow_MacOSX::GetDisplayResolutions( DisplayResolutions &dr ) const
{
CFArrayRef modes = CGDisplayAvailableModes( kCGDirectMainDisplay );
ASSERT( modes );
const CFIndex count = CFArrayGetCount( modes );
for( CFIndex i = 0; i < count; ++i )
{
CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex( modes, i );
int width = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayWidth) );
int height = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayHeight) );
CFTypeRef safe = CFDictionaryGetValue( dict, kCGDisplayModeIsSafeForHardware );
bool stretched = GetBoolValue( CFDictionaryGetValue(dict, kCGDisplayModeIsStretched) );
if( !width || !height )
continue;
if( safe && !GetBoolValue( safe ) )
continue;
DisplayResolution res = { width, height, stretched };
dr.insert( res );
}
// Do not release modes! We don't own them here.
}
void LowLevelWindow_MacOSX::SwapBuffers()
{
CGLFlushDrawable( CGLGetCurrentContext() );
}
void LowLevelWindow_MacOSX::Update()
{
// Keep the system from sleeping or the screen saver from activating.
UpdateSystemActivity( IdleActivity );
LockMutex lock( g_ResizeLock );
if( likely(!g_bResized) )
return;
g_bResized = false;
if( m_CurrentParams.width == g_iWidth && m_CurrentParams.height == g_iHeight )
return;
m_CurrentParams.width = g_iWidth;
m_CurrentParams.height = g_iHeight;
lock.Unlock(); // Unlock before calling ResolutionChanged().
[m_Context update];
DISPLAY->ResolutionChanged();
}
RenderTarget *LowLevelWindow_MacOSX::CreateRenderTarget()
{
return new RenderTarget_MacOSX( m_Context );
}
void LowLevelWindow_MacOSX::BeginConcurrentRendering()
{
if( m_CurrentParams.windowed )
[m_BGContext setView:[((SMWindowDelegate *)m_WindowDelegate)->m_Window contentView]];
else
[m_BGContext setFullScreen];
[m_BGContext makeCurrentContext];
}
/*
* (c) 2005-2006, 2008 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,432 @@
#include "global.h"
#include "LowLevelWindow_Win32.h"
#include "archutils/Win32/DirectXHelpers.h"
#include "archutils/Win32/ErrorStrings.h"
#include "archutils/Win32/GraphicsWindow.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "RageDisplay.h"
#include "LocalizedString.h"
#include "RageDisplay_OGL_Helpers.h"
#include "RageDisplay_OGL.h"
#include <GL/gl.h>
static PIXELFORMATDESCRIPTOR g_CurrentPixelFormat;
static HGLRC g_HGLRC = NULL;
static HGLRC g_HGLRC_Background = NULL;
static void DestroyGraphicsWindowAndOpenGLContext()
{
if( g_HGLRC != NULL )
{
wglMakeCurrent( NULL, NULL );
wglDeleteContext( g_HGLRC );
g_HGLRC = NULL;
}
if( g_HGLRC_Background != NULL )
{
wglDeleteContext( g_HGLRC_Background );
g_HGLRC_Background = NULL;
}
ZERO( g_CurrentPixelFormat );
GraphicsWindow::DestroyGraphicsWindow();
}
void *LowLevelWindow_Win32::GetProcAddress( RString s )
{
void *pRet = wglGetProcAddress( s );
if( pRet != NULL )
return pRet;
return ::GetProcAddress( GetModuleHandle(NULL), s );
}
LowLevelWindow_Win32::LowLevelWindow_Win32()
{
ASSERT( g_HGLRC == NULL );
ASSERT( g_HGLRC_Background == NULL );
GraphicsWindow::Initialize( false );
}
LowLevelWindow_Win32::~LowLevelWindow_Win32()
{
DestroyGraphicsWindowAndOpenGLContext();
GraphicsWindow::Shutdown();
}
void LowLevelWindow_Win32::GetDisplayResolutions( DisplayResolutions &out ) const
{
GraphicsWindow::GetDisplayResolutions( out );
}
int ChooseWindowPixelFormat( const VideoModeParams &p, PIXELFORMATDESCRIPTOR *PixelFormat )
{
ASSERT( GraphicsWindow::GetHwnd() != NULL );
ASSERT( GraphicsWindow::GetHDC() != NULL );
ZERO( *PixelFormat );
PixelFormat->nSize = sizeof(PIXELFORMATDESCRIPTOR);
PixelFormat->nVersion = 1;
PixelFormat->dwFlags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL;
PixelFormat->iPixelType = PFD_TYPE_RGBA;
PixelFormat->cColorBits = p.bpp == 16? 16:24;
PixelFormat->cDepthBits = 16;
return ChoosePixelFormat( GraphicsWindow::GetHDC(), PixelFormat );
}
void DumpPixelFormat( const PIXELFORMATDESCRIPTOR &pfd )
{
RString str = ssprintf( "Mode: " );
bool bInvalidFormat = false;
if( pfd.dwFlags & PFD_GENERIC_FORMAT )
{
if( pfd.dwFlags & PFD_GENERIC_ACCELERATED ) str += "MCD ";
else { str += "software "; bInvalidFormat = true; }
}
else
{
str += "ICD ";
}
if( pfd.iPixelType != PFD_TYPE_RGBA ) { str += "indexed "; bInvalidFormat = true; }
if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ) { str += "!OPENGL "; bInvalidFormat = true; }
if( !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ) { str += "!window "; bInvalidFormat = true; }
if( !(pfd.dwFlags & PFD_DOUBLEBUFFER) ) { str += "!dbuff "; bInvalidFormat = true; }
str += ssprintf( "%i (%i%i%i) ", pfd.cColorBits, pfd.cRedBits, pfd.cGreenBits, pfd.cBlueBits );
if( pfd.cAlphaBits ) str += ssprintf( "%i alpha ", pfd.cAlphaBits );
if( pfd.cDepthBits ) str += ssprintf( "%i depth ", pfd.cDepthBits );
if( pfd.cStencilBits ) str += ssprintf( "%i stencil ", pfd.cStencilBits );
if( pfd.cAccumBits ) str += ssprintf( "%i accum ", pfd.cAccumBits );
if( bInvalidFormat )
LOG->Warn( "Invalid format: %s", str.c_str() );
else
LOG->Info( "%s", str.c_str() );
}
/* This function does not reset the video mode if it fails, because we might be trying
* yet another video mode, so we'd just thrash the display. On fatal error,
* LowLevelWindow_Win32::~LowLevelWindow_Win32 will call GraphicsWindow::Shutdown(). */
RString LowLevelWindow_Win32::TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut )
{
//LOG->Warn( "LowLevelWindow_Win32::TryVideoMode" );
ASSERT_M( p.bpp == 16 || p.bpp == 32, ssprintf("%i", p.bpp) );
bNewDeviceOut = false;
/* We're only allowed to change the pixel format of a window exactly once. */
bool bCanSetPixelFormat = true;
/* Do we have an old window? */
if( GraphicsWindow::GetHwnd() == NULL )
{
/* No. Always create and show the window before changing the video mode.
* Otherwise, some other window may have focus, and changing the video mode will
* cause that window to be resized. */
bNewDeviceOut = true;
GraphicsWindow::CreateGraphicsWindow( p );
} else {
/* We already have a window. Assume that it's pixel format has already been
* set. */
bCanSetPixelFormat = false;
}
ASSERT( GraphicsWindow::GetHwnd() );
/* Set the display mode: switch to a fullscreen mode or revert to windowed mode. */
LOG->Trace("SetScreenMode ...");
RString sErr = GraphicsWindow::SetScreenMode( p );
if( !sErr.empty() )
return sErr;
PIXELFORMATDESCRIPTOR PixelFormat;
int iPixelFormat = ChooseWindowPixelFormat( p, &PixelFormat );
if( iPixelFormat == 0 )
{
/* Destroy the window. */
DestroyGraphicsWindowAndOpenGLContext();
return "Pixel format not found";
}
bool bNeedToSetPixelFormat = false;
{
/* We'll need to recreate it if the pixel format is going to change. We
* aren't allowed to change the pixel format twice. */
PIXELFORMATDESCRIPTOR DestPixelFormat;
ZERO( DestPixelFormat );
DescribePixelFormat( GraphicsWindow::GetHDC(), iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &DestPixelFormat );
if( memcmp( &DestPixelFormat, &g_CurrentPixelFormat, sizeof(PIXELFORMATDESCRIPTOR) ) )
{
LOG->Trace("Reset: pixel format changing" );
bNeedToSetPixelFormat = true;
}
}
if( bNeedToSetPixelFormat && !bCanSetPixelFormat )
{
/*
* The screen mode has changed, so we need to set the pixel format. If we're
* not allowed to do so, destroy the window and make a new one.
*
* For some reason, if we destroy the old window before creating the new one,
* the "maximized apps go under the taskbar" glitch will happen when we quit.
* We have to create the new window first.
*/
LOG->Trace( "Mode requires new pixel format, and we've already set one; resetting OpenGL context" );
if( g_HGLRC != NULL )
{
wglMakeCurrent( NULL, NULL );
wglDeleteContext( g_HGLRC );
g_HGLRC = NULL;
wglDeleteContext( g_HGLRC_Background );
g_HGLRC_Background = NULL;
}
bNewDeviceOut = true;
}
/* If we deleted the OpenGL context above, also recreate the window. Otherwise, just
* reconfigure it. */
GraphicsWindow::CreateGraphicsWindow( p, bNewDeviceOut );
if( bNeedToSetPixelFormat )
{
/* Set the pixel format. */
if( !SetPixelFormat(GraphicsWindow::GetHDC(), iPixelFormat, &PixelFormat) )
{
/* Destroy the window. */
DestroyGraphicsWindowAndOpenGLContext();
return werr_ssprintf( GetLastError(), "Pixel format failed" );
}
DescribePixelFormat( GraphicsWindow::GetHDC(), iPixelFormat, sizeof(g_CurrentPixelFormat), &g_CurrentPixelFormat );
DumpPixelFormat( g_CurrentPixelFormat );
}
if( g_HGLRC == NULL )
{
g_HGLRC = wglCreateContext( GraphicsWindow::GetHDC() );
if ( g_HGLRC == NULL )
{
DestroyGraphicsWindowAndOpenGLContext();
return hr_ssprintf( GetLastError(), "wglCreateContext" );
}
g_HGLRC_Background = wglCreateContext( GraphicsWindow::GetHDC() );
if( g_HGLRC_Background == NULL )
{
DestroyGraphicsWindowAndOpenGLContext();
return hr_ssprintf( GetLastError(), "wglCreateContext" );
}
if( !wglShareLists(g_HGLRC, g_HGLRC_Background) )
{
LOG->Warn( werr_ssprintf(GetLastError(), "wglShareLists failed") );
wglDeleteContext( g_HGLRC_Background );
g_HGLRC_Background = NULL;
}
if( !wglMakeCurrent( GraphicsWindow::GetHDC(), g_HGLRC ) )
{
DestroyGraphicsWindowAndOpenGLContext();
return hr_ssprintf( GetLastError(), "wglCreateContext" );
}
}
return RString(); // we set the video mode successfully
}
bool LowLevelWindow_Win32::SupportsThreadedRendering()
{
return g_HGLRC_Background != NULL;
}
void LowLevelWindow_Win32::BeginConcurrentRendering()
{
if( !wglMakeCurrent( GraphicsWindow::GetHDC(), g_HGLRC_Background ) )
{
LOG->Warn( hr_ssprintf(GetLastError(), "wglMakeCurrent") );
FAIL_M( hr_ssprintf(GetLastError(), "wglMakeCurrent") );
}
}
void LowLevelWindow_Win32::EndConcurrentRendering()
{
wglMakeCurrent( NULL, NULL );
}
static LocalizedString OPENGL_NOT_AVAILABLE( "LowLevelWindow_Win32", "OpenGL hardware acceleration is not available." );
bool LowLevelWindow_Win32::IsSoftwareRenderer( RString &sError )
{
RString sVendor = (const char*)glGetString(GL_VENDOR);
RString sRenderer = (const char*)glGetString(GL_RENDERER);
LOG->Trace( "LowLevelWindow_Win32::IsSoftwareRenderer '%s', '%s'", sVendor.c_str(), sRenderer.c_str() );
if( sVendor == "Microsoft Corporation" && sRenderer == "GDI Generic" )
{
sError = OPENGL_NOT_AVAILABLE;
return true;
}
return false;
}
void LowLevelWindow_Win32::SwapBuffers()
{
::SwapBuffers( GraphicsWindow::GetHDC() );
}
void LowLevelWindow_Win32::Update()
{
GraphicsWindow::Update();
}
const VideoModeParams &LowLevelWindow_Win32::GetActualVideoModeParams() const
{
return GraphicsWindow::GetParams();
}
class RenderTarget_Win32 : public RenderTarget
{
public:
RenderTarget_Win32( LowLevelWindow_Win32 *pWind );
virtual ~RenderTarget_Win32();
void Create( const RenderTargetParam &param, int &iTextureWidthOut, int &iTextureHeightOut );
unsigned int GetTexture() const { return m_texHandle; }
void StartRenderingTo();
void FinishRenderingTo();
virtual bool InvertY() const { return true; }
private:
LowLevelWindow_Win32 *m_pWind;
int m_width;
int m_height;
GLuint m_texHandle;
HDC m_hOldDeviceContext;
HGLRC m_hOldRenderContext;
};
RenderTarget_Win32::RenderTarget_Win32(LowLevelWindow_Win32 *pWind)
{
m_pWind = pWind;
m_texHandle = 0;
m_hOldDeviceContext = NULL;
m_hOldRenderContext = NULL;
}
RenderTarget_Win32::~RenderTarget_Win32()
{
glDeleteTextures( 1, &m_texHandle ); // deleting a 0 texture is safe and ignored
}
void RenderTarget_Win32::Create(const RenderTargetParam &param, int &iTextureWidthOut, int &iTextureHeightOut)
{
m_Param = param;
m_width = param.iWidth;
m_height = param.iHeight;
FlushGLErrors();
glGenTextures( 1, &m_texHandle );
ASSERT(m_texHandle > 0);
glBindTexture( GL_TEXTURE_2D, m_texHandle );
int iTextureWidth = power_of_two( param.iWidth );
int iTextureHeight = power_of_two( param.iHeight );
iTextureWidthOut = iTextureWidth;
iTextureHeightOut = iTextureHeight;
GLenum internalformat;
GLenum type = param.bWithAlpha? GL_RGBA:GL_RGB;
if( param.bFloat && GLExt.m_bGL_ARB_texture_float )
internalformat = param.bWithAlpha? GL_RGBA16F_ARB:GL_RGB16F_ARB;
else
internalformat = param.bWithAlpha? GL_RGBA8:GL_RGB8;
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, iTextureWidth,
iTextureHeight, 0, type, GL_UNSIGNED_BYTE, NULL);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
AssertNoGLError();
}
void RenderTarget_Win32::StartRenderingTo()
{
m_hOldDeviceContext = wglGetCurrentDC();
m_hOldRenderContext = wglGetCurrentContext();
BOOL successful = wglMakeCurrent(GraphicsWindow::GetHDC(), g_HGLRC);
ASSERT_M( successful == TRUE, "wglMakeCurrent failed in RenderTarget_Win32::StartRenderingTo()" );
FlushGLErrors();
glBindTexture( GL_TEXTURE_2D, m_texHandle );
AssertNoGLError();
}
void RenderTarget_Win32::FinishRenderingTo()
{
FlushGLErrors();
glBindTexture( GL_TEXTURE_2D, m_texHandle );
AssertNoGLError();
glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height );
glBindTexture( GL_TEXTURE_2D, 0 );
AssertNoGLError();
BOOL successful = wglMakeCurrent(m_hOldDeviceContext, m_hOldRenderContext);
ASSERT_M( successful == TRUE, "wglMakeCurrent failed in RenderTarget_Win32::FinishRenderingTo()" );
m_hOldDeviceContext = 0;
m_hOldRenderContext = 0;
}
RenderTarget* LowLevelWindow_Win32::CreateRenderTarget()
{
return new RenderTarget_Win32( this );
}
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,56 @@
#ifndef LOW_LEVEL_WINDOW_WIN32_H
#define LOW_LEVEL_WINDOW_WIN32_H
#include "LowLevelWindow.h"
class LowLevelWindow_Win32: public LowLevelWindow
{
public:
LowLevelWindow_Win32();
~LowLevelWindow_Win32();
void *GetProcAddress( RString s );
RString TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut );
void GetDisplayResolutions( DisplayResolutions &out ) const;
bool IsSoftwareRenderer( RString &sError );
void SwapBuffers();
void Update();
bool SupportsThreadedRendering();
void BeginConcurrentRendering();
void EndConcurrentRendering();
virtual bool SupportsRenderToTexture() const { return true; }
virtual RenderTarget *CreateRenderTarget();
const VideoModeParams &GetActualVideoModeParams() const;
};
#ifdef ARCH_LOW_LEVEL_WINDOW
#error "More than one LowLevelWindow selected!"
#endif
#define ARCH_LOW_LEVEL_WINDOW LowLevelWindow_Win32
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,540 @@
#include "global.h"
#include "LowLevelWindow_X11.h"
#include "RageLog.h"
#include "RageException.h"
#include "archutils/Unix/X11Helper.h"
#include "PrefsManager.h" // XXX
#include "RageDisplay.h" // VideoModeParams
#include "DisplayResolutions.h"
#include "LocalizedString.h"
#include "RageDisplay_OGL_Helpers.h"
using namespace RageDisplay_OGL_Helpers;
using namespace X11Helper;
#include <stack>
#include <math.h> // ceil()
#define GLX_GLXEXT_PROTOTYPES
#include <GL/glx.h> // All sorts of stuff...
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrandr.h>
#if defined(HAVE_LIBXTST)
#include <X11/extensions/XTest.h>
#endif
static GLXContext g_pContext = NULL;
static GLXContext g_pBackgroundContext = NULL;
static Window g_AltWindow = None;
static Rotation g_OldRotation;
static int g_iOldSize;
XRRScreenConfiguration *g_pScreenConfig = NULL;
static LocalizedString FAILED_CONNECTION_XSERVER( "LowLevelWindow_X11", "Failed to establish a connection with the X server" );
LowLevelWindow_X11::LowLevelWindow_X11()
{
if( !OpenXConnection() )
RageException::Throw( "%s", FAILED_CONNECTION_XSERVER.GetValue().c_str() );
const int iScreen = DefaultScreen( Dpy );
int iXServerVersion = XVendorRelease( Dpy ); /* eg. 40201001 */
int iMajor = iXServerVersion / 10000000; iXServerVersion %= 10000000;
int iMinor = iXServerVersion / 100000; iXServerVersion %= 100000;
int iRevision = iXServerVersion / 1000; iXServerVersion %= 1000;
int iPatch = iXServerVersion;
LOG->Info( "Display: %s (screen %i)", DisplayString(Dpy), iScreen );
LOG->Info( "X server vendor: %s [%i.%i.%i.%i]", XServerVendor( Dpy ), iMajor, iMinor, iRevision, iPatch );
LOG->Info( "Server GLX vendor: %s [%s]", glXQueryServerString( Dpy, iScreen, GLX_VENDOR ), glXQueryServerString( Dpy, iScreen, GLX_VERSION ) );
LOG->Info( "Client GLX vendor: %s [%s]", glXGetClientString( Dpy, GLX_VENDOR ), glXGetClientString( Dpy, GLX_VERSION ) );
m_bWasWindowed = true;
g_pScreenConfig = XRRGetScreenInfo( Dpy, RootWindow(Dpy, DefaultScreen(Dpy)) );
}
LowLevelWindow_X11::~LowLevelWindow_X11()
{
// Reset the display
if( !m_bWasWindowed )
{
XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), g_iOldSize, g_OldRotation, CurrentTime );
XUngrabKeyboard( Dpy, CurrentTime );
}
if( g_pContext )
{
glXDestroyContext( Dpy, g_pContext );
g_pContext = NULL;
}
if( g_pBackgroundContext )
{
glXDestroyContext( Dpy, g_pBackgroundContext );
g_pBackgroundContext = NULL;
}
XRRFreeScreenConfigInfo( g_pScreenConfig );
g_pScreenConfig = NULL;
XDestroyWindow( Dpy, Win );
Win = None;
XDestroyWindow( Dpy, g_AltWindow );
g_AltWindow = None;
CloseXConnection();
}
void *LowLevelWindow_X11::GetProcAddress( RString s )
{
// XXX: We should check whether glXGetProcAddress or
// glXGetProcAddressARB is available, and go by that, instead of
// assuming like this.
return (void*) glXGetProcAddressARB( (const GLubyte*) s.c_str() );
}
RString LowLevelWindow_X11::TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut )
{
#if defined(UNIX)
/*
* nVidia cards:
*
* This only works the first time we set up a window; after that, the
* drivers appear to cache the value, so you have to actually restart
* the program to change it again.
*/
static char buf[128];
strcpy( buf, "__GL_SYNC_TO_VBLANK=" );
strcat( buf, p.vsync?"1":"0" );
putenv( buf );
#endif
if( g_pContext == NULL || p.bpp != CurrentParams.bpp || m_bWasWindowed != p.windowed )
{
// Different depth, or we didn't make a window before. New context.
bNewDeviceOut = true;
int visAttribs[32];
int i = 0;
ASSERT( p.bpp == 16 || p.bpp == 32 );
if( p.bpp == 32 )
{
visAttribs[i++] = GLX_RED_SIZE; visAttribs[i++] = 8;
visAttribs[i++] = GLX_GREEN_SIZE; visAttribs[i++] = 8;
visAttribs[i++] = GLX_BLUE_SIZE; visAttribs[i++] = 8;
}
else
{
visAttribs[i++] = GLX_RED_SIZE; visAttribs[i++] = 5;
visAttribs[i++] = GLX_GREEN_SIZE; visAttribs[i++] = 6;
visAttribs[i++] = GLX_BLUE_SIZE; visAttribs[i++] = 5;
}
visAttribs[i++] = GLX_DEPTH_SIZE; visAttribs[i++] = 16;
visAttribs[i++] = GLX_RGBA;
visAttribs[i++] = GLX_DOUBLEBUFFER;
visAttribs[i++] = None;
XVisualInfo *xvi = glXChooseVisual( Dpy, DefaultScreen(Dpy), visAttribs );
if( xvi == NULL )
return "No visual available for that depth.";
// I get strange behavior if I add override redirect after creating the window.
// So, let's recreate the window when changing that state.
if( !MakeWindow(Win, xvi->screen, xvi->depth, xvi->visual, p.width, p.height, !p.windowed) )
return "Failed to create the window.";
if( !MakeWindow(g_AltWindow, xvi->screen, xvi->depth, xvi->visual, p.width, p.height, !p.windowed) )
FAIL_M( "Failed to create the alt window." ); // Should this be fatal?
char *szWindowTitle = const_cast<char *>( p.sWindowTitle.c_str() );
XChangeProperty( Dpy, Win, XA_WM_NAME, XA_STRING, 8, PropModeReplace,
reinterpret_cast<unsigned char*>(szWindowTitle), strlen(szWindowTitle) );
if( g_pContext )
glXDestroyContext( Dpy, g_pContext );
if( g_pBackgroundContext )
glXDestroyContext( Dpy, g_pBackgroundContext );
g_pContext = glXCreateContext( Dpy, xvi, NULL, True );
g_pBackgroundContext = glXCreateContext( Dpy, xvi, g_pContext, True );
glXMakeCurrent( Dpy, Win, g_pContext );
XWindowAttributes winAttrib;
XGetWindowAttributes( Dpy, Win, &winAttrib );
XSelectInput( Dpy, Win, winAttrib.your_event_mask | StructureNotifyMask );
XMapWindow( Dpy, Win );
// XXX: Why do we need to wait for the MapNotify event?
while( true )
{
XEvent event;
XMaskEvent( Dpy, StructureNotifyMask, &event );
if( event.type == MapNotify )
break;
}
XSelectInput( Dpy, Win, winAttrib.your_event_mask );
}
else
{
// We're remodeling the existing window, and not touching the
// context.
bNewDeviceOut = false;
}
g_iOldSize = XRRConfigCurrentConfiguration( g_pScreenConfig, &g_OldRotation );
if( !p.windowed )
{
// Find a matching mode.
int iSizesXct;
XRRScreenSize *pSizesX = XRRSizes( Dpy, DefaultScreen(Dpy), &iSizesXct );
ASSERT_M( iSizesXct != 0, "Couldn't get resolution list from X server" );
int iSizeMatch = -1;
for( int i = 0; i < iSizesXct; ++i )
{
if( pSizesX[i].width == p.width && pSizesX[i].height == p.height )
{
iSizeMatch = i;
break;
}
}
// Set this mode.
// XXX: This doesn't handle if the config has changed since we queried it (see man Xrandr)
XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), iSizeMatch, 1, CurrentTime );
// Move the window to the corner that the screen focuses in on.
XMoveWindow( Dpy, Win, 0, 0 );
XRaiseWindow( Dpy, Win );
if( m_bWasWindowed )
{
// We want to prevent the WM from catching anything that comes from the keyboard.
XGrabKeyboard( Dpy, Win, True, GrabModeAsync, GrabModeAsync, CurrentTime );
m_bWasWindowed = false;
}
}
else
{
if( !m_bWasWindowed )
{
XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), g_iOldSize, g_OldRotation, CurrentTime );
// In windowed mode, we actually want the WM to function normally.
// Release any previous grab.
XUngrabKeyboard( Dpy, CurrentTime );
m_bWasWindowed = true;
}
}
int rate = XRRConfigCurrentRate( g_pScreenConfig );
// Do this before resizing the window so that pane-style WMs (Ion,
// ratpoison) don't resize us back inappropriately.
{
XSizeHints hints;
hints.flags = PBaseSize;
hints.base_width = p.width;
hints.base_height = p.height;
XSetWMNormalHints( Dpy, Win, &hints );
}
// Do this even if we just created the window -- works around Ion2 not
// catching WM normal hints changes in mapped windows.
XResizeWindow( Dpy, Win, p.width, p.height );
CurrentParams = p;
CurrentParams.rate = rate;
return ""; // Success
}
void LowLevelWindow_X11::LogDebugInformation() const
{
LOG->Info( "Direct rendering: %s", glXIsDirect( Dpy, glXGetCurrentContext() )? "yes":"no" );
}
bool LowLevelWindow_X11::IsSoftwareRenderer( RString &sError )
{
if( glXIsDirect( Dpy, glXGetCurrentContext() ) )
return false;
sError = "Direct rendering is not available.";
return true;
}
void LowLevelWindow_X11::SwapBuffers()
{
glXSwapBuffers( Dpy, Win );
if( PREFSMAN->m_bDisableScreenSaver )
{
/* Disable the screensaver. */
#if defined(HAVE_LIBXTST)
/* This causes flicker. */
// XForceScreenSaver( Dpy, ScreenSaverReset );
/*
* Instead, send a null relative mouse motion, to trick X into thinking there has been
* user activity.
*
* This also handles XScreenSaver; XForceScreenSaver only handles the internal X11
* screen blanker.
*
* This will delay the X blanker, DPMS and XScreenSaver from activating, and will
* disable the blanker and XScreenSaver if they're already active (unless XSS is
* locked). For some reason, it doesn't un-blank DPMS if it's already active.
*/
XLockDisplay( Dpy );
int event_base, error_base, major, minor;
if( XTestQueryExtension( Dpy, &event_base, &error_base, &major, &minor ) )
{
XTestFakeRelativeMotionEvent( Dpy, 0, 0, 0 );
XSync( Dpy, False );
}
XUnlockDisplay( Dpy );
#endif
}
}
void LowLevelWindow_X11::GetDisplayResolutions( DisplayResolutions &out ) const
{
int iSizesXct;
XRRScreenSize *pSizesX = XRRSizes( Dpy, DefaultScreen( Dpy ), &iSizesXct );
ASSERT_M( iSizesXct != 0, "Couldn't get resolution list from X server" );
for( int i = 0; i < iSizesXct; ++i )
{
DisplayResolution res = { pSizesX[i].width, pSizesX[i].height, true };
out.insert( res );
}
}
bool LowLevelWindow_X11::SupportsThreadedRendering()
{
return g_pBackgroundContext != NULL;
}
class RenderTarget_X11: public RenderTarget
{
public:
RenderTarget_X11( LowLevelWindow_X11 *pWind );
~RenderTarget_X11();
void Create( const RenderTargetParam &param, int &iTextureWidthOut, int &iTextureHeightOut );
unsigned GetTexture() const { return m_iTexHandle; }
void StartRenderingTo();
void FinishRenderingTo();
/* Copying from the Pbuffer to the texture flips Y. */
virtual bool InvertY() const { return true; }
private:
int m_iWidth, m_iHeight;
LowLevelWindow_X11 *m_pWind;
GLXPbuffer m_iPbuffer;
GLXContext m_pPbufferContext;
unsigned int m_iTexHandle;
GLXContext m_pOldContext;
GLXDrawable m_pOldDrawable;
};
RenderTarget_X11::RenderTarget_X11( LowLevelWindow_X11 *pWind )
{
m_pWind = pWind;
m_iPbuffer = 0;
m_pPbufferContext = NULL;
m_iTexHandle = 0;
m_pOldContext = NULL;
m_pOldDrawable = 0;
}
RenderTarget_X11::~RenderTarget_X11()
{
if( m_pPbufferContext )
glXDestroyContext( Dpy, m_pPbufferContext );
if( m_iPbuffer )
glXDestroyPbuffer( Dpy, m_iPbuffer );
if( m_iTexHandle )
glDeleteTextures( 1, reinterpret_cast<GLuint*>(&m_iTexHandle) );
}
/* Note that although the texture size may need to be a power of 2, the Pbuffer
* does not. */
void RenderTarget_X11::Create( const RenderTargetParam &param, int &iTextureWidthOut, int &iTextureHeightOut )
{
//ASSERT( param.iWidth == power_of_two(param.iWidth) && param.iHeight == power_of_two(param.iHeight) );
m_iWidth = param.iWidth;
m_iHeight = param.iHeight;
int pConfigAttribs[] =
{
GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, param.bWithAlpha? 8:GLX_DONT_CARE,
GLX_DOUBLEBUFFER, False,
GLX_DEPTH_SIZE, param.bWithDepthBuffer? 16:GLX_DONT_CARE,
None
};
int iConfigs;
GLXFBConfig *pConfigs = glXChooseFBConfig( Dpy, DefaultScreen(Dpy), pConfigAttribs, &iConfigs );
ASSERT( pConfigs );
const int pPbufferAttribs[] =
{
GLX_PBUFFER_WIDTH, param.iWidth,
GLX_PBUFFER_HEIGHT, param.iHeight,
None
};
for( int i = 0; i < iConfigs; ++i )
{
m_iPbuffer = glXCreatePbuffer( Dpy, pConfigs[i], pPbufferAttribs );
if( m_iPbuffer == 0 )
continue;
XVisualInfo *pVisual = glXGetVisualFromFBConfig( Dpy, pConfigs[i] );
m_pPbufferContext = glXCreateContext( Dpy, pVisual, g_pContext, True );
ASSERT( m_pPbufferContext );
XFree( pVisual );
break;
}
ASSERT( m_iPbuffer );
// allocate OpenGL texture resource
glGenTextures( 1, reinterpret_cast<GLuint*>(&m_iTexHandle) );
glBindTexture( GL_TEXTURE_2D, m_iTexHandle );
LOG->Trace( "n %i, %ix%i", m_iTexHandle, param.iWidth, param.iHeight );
while( glGetError() != GL_NO_ERROR )
;
int iTextureWidth = power_of_two( param.iWidth );
int iTextureHeight = power_of_two( param.iHeight );
iTextureWidthOut = iTextureWidth;
iTextureHeightOut = iTextureHeight;
glTexImage2D( GL_TEXTURE_2D, 0, param.bWithAlpha? GL_RGBA8:GL_RGB8,
iTextureWidth, iTextureHeight, 0, param.bWithAlpha? GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, NULL );
GLenum error = glGetError();
ASSERT_M( error == GL_NO_ERROR, GLToString(error) );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
}
void RenderTarget_X11::StartRenderingTo()
{
m_pOldContext = glXGetCurrentContext();
m_pOldDrawable = glXGetCurrentDrawable();
glXMakeCurrent( Dpy, m_iPbuffer, m_pPbufferContext );
glViewport( 0, 0, m_iWidth, m_iHeight );
}
void RenderTarget_X11::FinishRenderingTo()
{
glFlush();
glBindTexture( GL_TEXTURE_2D, m_iTexHandle );
while( glGetError() != GL_NO_ERROR )
;
glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_iWidth, m_iHeight );
GLenum error = glGetError();
ASSERT_M( error == GL_NO_ERROR, GLToString(error) );
glBindTexture( GL_TEXTURE_2D, 0 );
glXMakeCurrent( Dpy, m_pOldDrawable, m_pOldContext );
m_pOldContext = NULL;
m_pOldDrawable = 0;
}
bool LowLevelWindow_X11::SupportsRenderToTexture() const
{
/* Server must support pbuffers: */
const int iScreen = DefaultScreen( Dpy );
float fVersion = strtof( glXQueryServerString(Dpy, iScreen, GLX_VERSION), NULL );
if( fVersion < 1.3f )
return false;
return true;
}
RenderTarget *LowLevelWindow_X11::CreateRenderTarget()
{
return new RenderTarget_X11( this );
}
void LowLevelWindow_X11::BeginConcurrentRenderingMainThread()
{
/* Move the main thread, which is going to be loading textures, etc. but
* not rendering, to an undisplayed window. This results in smoother
* rendering. */
bool b = glXMakeCurrent( Dpy, g_AltWindow, g_pContext );
ASSERT(b);
}
void LowLevelWindow_X11::EndConcurrentRenderingMainThread()
{
bool b = glXMakeCurrent( Dpy, Win, g_pContext );
ASSERT(b);
}
void LowLevelWindow_X11::BeginConcurrentRendering()
{
bool b = glXMakeCurrent( Dpy, Win, g_pBackgroundContext );
ASSERT(b);
}
void LowLevelWindow_X11::EndConcurrentRendering()
{
bool b = glXMakeCurrent( Dpy, None, NULL );
ASSERT(b);
}
/*
* (c) 2005 Ben Anderson
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,70 @@
/* LowLevelWindow_X11 - OpenGL GLX window driver. */
#ifndef LOW_LEVEL_WINDOW_X11_H
#define LOW_LEVEL_WINDOW_X11_H
#include "RageDisplay.h" // VideoModeParams
#include "LowLevelWindow.h"
class LowLevelWindow_X11 : public LowLevelWindow
{
public:
LowLevelWindow_X11();
~LowLevelWindow_X11();
void *GetProcAddress(RString s);
RString TryVideoMode(const VideoModeParams &p, bool &bNewDeviceOut);
void LogDebugInformation() const;
bool IsSoftwareRenderer( RString &sError );
void SwapBuffers();
const VideoModeParams &GetActualVideoModeParams() const { return CurrentParams; }
void GetDisplayResolutions( DisplayResolutions &out ) const;
bool SupportsRenderToTexture() const;
RenderTarget *CreateRenderTarget();
bool SupportsThreadedRendering();
void BeginConcurrentRenderingMainThread();
void EndConcurrentRenderingMainThread();
void BeginConcurrentRendering();
void EndConcurrentRendering();
private:
bool m_bWasWindowed;
VideoModeParams CurrentParams;
};
#ifdef ARCH_LOW_LEVEL_WINDOW
#error "More than one LowLevelWindow selected!"
#endif
#define ARCH_LOW_LEVEL_WINDOW LowLevelWindow_X11
#endif
/*
* (c) 2005 Ben Anderson
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+162
View File
@@ -0,0 +1,162 @@
#include "global.h"
#include "MemoryCardDriver.h"
#include "RageFileManager.h"
#include "RageLog.h"
#include "Foreach.h"
#include "ProfileManager.h"
static const RString TEMP_MOUNT_POINT = "/@mctemptimeout/";
bool UsbStorageDevice::operator==(const UsbStorageDevice& other) const
{
// LOG->Trace( "Comparing %d %d %d %s %s to %d %d %d %s %s",
// iBus, iPort, iLevel, sName.c_str(), sOsMountDir.c_str(),
// other.iBus, other.iPort, other.iLevel, other.sName.c_str(), other.sOsMountDir.c_str() );
#define COMPARE(x) if( x != other.x ) return false
COMPARE( iBus );
COMPARE( iPort );
COMPARE( iLevel );
COMPARE( sOsMountDir );
return true;
#undef COMPARE
}
void UsbStorageDevice::SetOsMountDir( const RString &s )
{
sOsMountDir = s;
}
bool MemoryCardDriver::NeedUpdate( bool bMount )
{
if( bMount )
{
/* Check if any devices need a write test. */
for( unsigned i=0; i<m_vDevicesLastSeen.size(); i++ )
{
const UsbStorageDevice &d = m_vDevicesLastSeen[i];
if( d.m_State == UsbStorageDevice::STATE_CHECKING )
return true;
}
}
return USBStorageDevicesChanged();
}
bool MemoryCardDriver::DoOneUpdate( bool bMount, vector<UsbStorageDevice>& vStorageDevicesOut )
{
if( !NeedUpdate(bMount) )
return false;
vector<UsbStorageDevice> vOld = m_vDevicesLastSeen; // copy
GetUSBStorageDevices( vStorageDevicesOut );
// log connects
FOREACH( UsbStorageDevice, vStorageDevicesOut, newd )
{
vector<UsbStorageDevice>::iterator iter = find( vOld.begin(), vOld.end(), *newd );
if( iter == vOld.end() ) // didn't find
LOG->Trace( "New device connected: %s", newd->sDevice.c_str() );
}
/* When we first see a device, regardless of bMount, just return it as CHECKING,
* so the main thread knows about the device. On the next call where bMount is
* true, check it. */
for( unsigned i=0; i<vStorageDevicesOut.size(); i++ )
{
UsbStorageDevice &d = vStorageDevicesOut[i];
/* If this device was just connected (it wasn't here last time), set it to
* CHECKING and return it, to let the main thread know about the device before
* we start checking. */
vector<UsbStorageDevice>::iterator iter = find( vOld.begin(), vOld.end(), d );
if( iter == vOld.end() ) // didn't find
{
LOG->Trace( "New device entering CHECKING: %s", d.sDevice.c_str() );
d.m_State = UsbStorageDevice::STATE_CHECKING;
continue;
}
/* Preserve the state of the device, and any data loaded from previous checks. */
d.m_State = iter->m_State;
d.bIsNameAvailable = iter->bIsNameAvailable;
d.sName = iter->sName;
/* The device was here last time. If CHECKING, check the device now, if
* we're allowed to. */
if( d.m_State == UsbStorageDevice::STATE_CHECKING )
{
if( !bMount )
{
/* We can't check it now. Keep STATE_CHECKING, and check it when we can. */
d.m_State = UsbStorageDevice::STATE_CHECKING;
continue;
}
if( !this->Mount(&d) )
{
d.SetError( "MountFailed" );
continue;
}
if( TestWrite(&d) )
{
/* We've successfully mounted and tested the device. Read the
* profile name (by mounting a temporary, private mountpoint),
* and then unmount it until Mount() is called. */
d.m_State = UsbStorageDevice::STATE_READY;
FILEMAN->Mount( "dir", d.sOsMountDir, TEMP_MOUNT_POINT );
d.bIsNameAvailable = PROFILEMAN->FastLoadProfileNameFromMemoryCard( TEMP_MOUNT_POINT, d.sName );
FILEMAN->Unmount( "dir", d.sOsMountDir, TEMP_MOUNT_POINT );
}
this->Unmount( &d );
LOG->Trace( "WriteTest: %s, Name: %s", d.m_State == UsbStorageDevice::STATE_ERROR? "failed":"succeeded", d.sName.c_str() );
}
}
m_vDevicesLastSeen = vStorageDevicesOut;
return true;
}
#include "arch/arch_default.h"
MemoryCardDriver *MemoryCardDriver::Create()
{
MemoryCardDriver *ret = NULL;
#ifdef ARCH_MEMORY_CARD_DRIVER
ret = new ARCH_MEMORY_CARD_DRIVER;
#endif
if( !ret )
ret = new MemoryCardDriver_Null;
return ret;
}
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+132
View File
@@ -0,0 +1,132 @@
#ifndef MEMORY_CARD_DRIVER_H
#define MEMORY_CARD_DRIVER_H
struct UsbStorageDevice
{
UsbStorageDevice() { MakeBlank(); }
void MakeBlank()
{
// -1 means "don't know"
iBus = -1;
iPort = -1;
iLevel = -1;
sDevice = "";
sSerial = "<none>"; // be different than a card with no serial
sOsMountDir = "";
m_State = STATE_NONE;
bIsNameAvailable = false;
sName = "";
idVendor = 0;
idProduct = 0;
sVendor = "";
sProduct = "";
sVolumeLabel = "";
iVolumeSizeMB = 0;
};
int iBus;
int iPort;
int iLevel;
RString sSerial;
RString sDevice;
RString sOsMountDir; // WITHOUT trailing slash
RString sSysPath; // Linux: /sys/block name
enum State
{
/* Empty device. This is used only by MemoryCardManager. */
STATE_NONE,
/* The card has been detected, but we haven't finished write tests, loading
* the quick profile information, etc. yet. We can display something on
* screen, in order to appear responsive, show that something's happening and
* aid diagnostics, though. */
STATE_CHECKING,
/* We can't write to the device; it may be write-protected, use a filesystem
* that we don't understand, unformatted, etc. */
STATE_ERROR,
/* The device is ready and usable. sName is filled in, if available. */
STATE_READY,
NUM_State,
State_INVALID
};
State m_State;
RString m_sError;
void SetError( const RString &sError ) { m_State = STATE_ERROR; m_sError = sError; }
bool bIsNameAvailable; // Name in the profile on the memory card.
RString sName; // Name in the profile on the memory card.
int idVendor;
int idProduct;
RString sVendor;
RString sProduct;
RString sVolumeLabel;
int iVolumeSizeMB;
bool IsBlank() const { return m_State == STATE_NONE; }
void SetOsMountDir( const RString &s );
bool operator==(const UsbStorageDevice& other) const;
};
class MemoryCardDriver
{
public:
static MemoryCardDriver *Create();
MemoryCardDriver() {}
virtual ~MemoryCardDriver() {}
/* Make a device accessible via its pDevice->sOsMountDir. This will be called
* before any access to the device, and before TestWrite. */
virtual bool Mount( UsbStorageDevice* pDevice ) = 0;
virtual void Unmount( UsbStorageDevice* pDevice ) = 0;
/* Poll for memory card changes. If anything has changed, fill in vStorageDevicesOut
* and return true. */
bool DoOneUpdate( bool bMount, vector<UsbStorageDevice>& vStorageDevicesOut );
protected:
/* This may be called before GetUSBStorageDevices; return false if the results of
* GetUSBStorageDevices have not changed. (This is an optimization.) */
virtual bool USBStorageDevicesChanged() { return true; }
virtual void GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut ) { }
/* Test the device. On failure, call pDevice->SetError() appropriately, and return false. */
virtual bool TestWrite( UsbStorageDevice* pDevice ) { return true; }
private:
vector<UsbStorageDevice> m_vDevicesLastSeen;
bool NeedUpdate( bool bMount );
};
#endif
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,348 @@
#include "global.h"
#include "MemoryCardDriverThreaded_Linux.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageFile.h"
#include <cerrno>
#include <fcntl.h>
#include <dirent.h>
bool MemoryCardDriverThreaded_Linux::TestWrite( UsbStorageDevice* pDevice )
{
if( access(pDevice->sOsMountDir, W_OK) == -1 )
{
pDevice->SetError( "TestFailed" );
return false;
}
return true;
}
static bool ExecuteCommand( const RString &sCommand )
{
LOG->Trace( "executing '%s'", sCommand.c_str() );
int ret = system(sCommand);
LOG->Trace( "done executing '%s'", sCommand.c_str() );
if( ret != 0 )
{
RString sError = ssprintf("failed to execute '%s' with error %d", sCommand.c_str(), ret);
if( ret == -1 )
sError += ssprintf(": %s", sCommand.c_str());
LOG->Warn( "%s", sError.c_str() );
}
return ret == 0;
}
static bool ReadFile( const RString &sPath, RString &sBuf )
{
sBuf.clear();
int fd = open( sPath, O_RDONLY );
if( fd == -1 )
{
LOG->Warn( "Error opening \"%s\": %s", sPath.c_str(), strerror(errno) );
return false;
}
while(1)
{
char buf[1024];
int iGot = read( fd, buf, sizeof(buf) );
if( iGot == -1 )
{
close(fd);
LOG->Warn( "Error reading \"%s\": %s", sPath.c_str(), strerror(errno) );
return false;
}
sBuf.append( buf, iGot );
if( iGot < (int) sizeof(buf) )
break;
}
close(fd);
return true;
}
static void GetFileList( const RString &sPath, vector<RString> &out )
{
out.clear();
DIR *dp = opendir( sPath );
if( dp == NULL )
return; // false; // XXX warn
while( const struct dirent *ent = readdir(dp) )
out.push_back( ent->d_name );
closedir( dp );
}
bool MemoryCardDriverThreaded_Linux::USBStorageDevicesChanged()
{
RString sThisDevices;
/* If a device is removed and reinserted, the inode of the /sys/block entry
* will change. */
RString sDevicePath = "/sys/block/";
vector<RString> asDevices;
GetFileList( sDevicePath, asDevices );
for( unsigned i = 0; i < asDevices.size(); ++i )
{
struct stat buf;
if( stat( sDevicePath + asDevices[i], &buf ) == -1 )
continue; // XXX warn
sThisDevices += ssprintf( "%i,", (int) buf.st_ino );
}
bool bChanged = sThisDevices != m_sLastDevices;
m_sLastDevices = sThisDevices;
if( bChanged )
LOG->Trace( "Change in USB storage devices detected." );
return bChanged;
}
void MemoryCardDriverThreaded_Linux::GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut )
{
LOG->Trace( "GetUSBStorageDevices" );
vDevicesOut.clear();
{
vector<RString> asDevices;
RString sBlockDevicePath = "/sys/block/";
GetFileList( sBlockDevicePath, asDevices );
for( unsigned i = 0; i < asDevices.size(); ++i )
{
const RString &sDevice = asDevices[i];
if( sDevice == "." || sDevice == ".." )
continue;
UsbStorageDevice usbd;
RString sPath = sBlockDevicePath + sDevice + "/";
usbd.sSysPath = sPath;
/* Ignore non-removable devices. */
RString sBuf;
if( !ReadFile( sPath + "removable", sBuf ) )
continue; // already warned
if( atoi(sBuf) != 1 )
continue;
/* HACK: The kernel isn't exposing all of /sys atomically, so we end up
* missing the partition due to it not being shown yet. The kernel should
* be exposing all of this atomically. */
usleep(50000);
/* If the first partition device exists, eg. /sys/block/uba/uba1, use it. */
if( access(usbd.sSysPath + sDevice + "1", F_OK) != -1 )
usbd.sDevice = "/dev/" + sDevice + "1";
else
usbd.sDevice = "/dev/" + sDevice;
/*
* sPath/device should be a symlink to the actual device. For USB
* devices, it looks like this:
*
* device -> ../../devices/pci0000:00/0000:00:02.1/usb2/2-1/2-1:1.0
*
* "2-1" is "bus-port".
*/
char szLink[256];
int iRet = readlink( sPath + "device", szLink, sizeof(szLink) );
if( iRet == -1 )
{
LOG->Warn( "readlink(\"%s\"): %s", (sPath + "device").c_str(), strerror(errno) );
}
else
{
/*
* The full path looks like
*
* ../../devices/pci0000:00/0000:00:02.1/usb2/2-2/2-2.1/2-2.1:1.0
*
* Each path element refers to a new hop in the chain.
* "usb2" = second USB host
* 2- second USB host,
* -2 port 1 on the host,
* .1 port 1 on an attached hub
* .2 ... port 2 on the next hub ...
*
* We want the bus number and the port of the last hop. The level is
* the number of hops.
*/
szLink[iRet] = 0;
vector<RString> asBits;
split( szLink, "/", asBits );
if( strstr( szLink, "usb" ) != NULL )
{
RString sHostPort = asBits[asBits.size()-2];
sHostPort.Replace( "-", "." );
asBits.clear();
split( sHostPort, ".", asBits );
if( asBits.size() > 1 )
{
usbd.iBus = atoi( asBits[0] );
usbd.iPort = atoi( asBits[asBits.size()-1] );
usbd.iLevel = asBits.size() - 1;
}
}
}
if( ReadFile( sPath + "device/../idVendor", sBuf ) )
sscanf( sBuf, "%x", &usbd.idVendor );
if( ReadFile( sPath + "device/../idProduct", sBuf ) )
sscanf( sBuf, "%x", &usbd.idProduct );
if( ReadFile( sPath + "device/../serial", sBuf ) )
{
usbd.sSerial = sBuf;
TrimRight( usbd.sSerial );
}
if( ReadFile( sPath + "device/../product", sBuf ) )
{
usbd.sProduct = sBuf;
TrimRight( usbd.sProduct );
}
if( ReadFile( sPath + "device/../manufacturer", sBuf ) )
{
usbd.sVendor = sBuf;
TrimRight( usbd.sVendor );
}
vDevicesOut.push_back( usbd );
}
}
{
// Find where each device is mounted. Output looks like:
// /dev/sda1 /mnt/flash1 auto noauto,owner 0 0
// /dev/sdb1 /mnt/flash2 auto noauto,owner 0 0
// /dev/sdc1 /mnt/flash3 auto noauto,owner 0 0
RString fn = "/rootfs/etc/fstab";
RageFile f;
if( !f.Open(fn) )
{
LOG->Warn( "can't open '%s': %s", fn.c_str(), f.GetError().c_str() );
return;
}
RString sLine;
while( !f.AtEOF() )
{
switch( f.GetLine(sLine) )
{
case 0: continue; /* eof */
case -1:
LOG->Warn( "error reading '%s': %s", fn.c_str(), f.GetError().c_str() );
return;
}
char szScsiDevice[1024];
char szMountPoint[1024];
int iRet = sscanf( sLine, "%s %s", szScsiDevice, szMountPoint );
if( iRet != 2 )
continue; // don't process this line
RString sMountPoint = szMountPoint;
TrimLeft( sMountPoint );
TrimRight( sMountPoint );
// search for the mountpoint corresponding to the device
for( unsigned i=0; i<vDevicesOut.size(); i++ )
{
UsbStorageDevice& usbd = vDevicesOut[i];
if( usbd.sDevice == szScsiDevice ) // found our match
{
usbd.sOsMountDir = sMountPoint;
break; // stop looking for a match
}
}
}
}
for( unsigned i=0; i<vDevicesOut.size(); i++ )
{
UsbStorageDevice& usbd = vDevicesOut[i];
LOG->Trace( " sDevice: %s, iBus: %d, iLevel: %d, iPort: %d, id: %04X:%04X, Vendor: '%s', Product: '%s', sSerial: \"%s\", sOsMountDir: %s",
usbd.sDevice.c_str(), usbd.iBus, usbd.iLevel, usbd.iPort, usbd.idVendor, usbd.idProduct, usbd.sVendor.c_str(),
usbd.sProduct.c_str(), usbd.sSerial.c_str(), usbd.sOsMountDir.c_str() );
}
/* Remove any devices that we couldn't find a mountpoint for. */
for( unsigned i=0; i<vDevicesOut.size(); i++ )
{
UsbStorageDevice& usbd = vDevicesOut[i];
if( usbd.sOsMountDir.empty() )
{
LOG->Trace( "Ignoring %s (couldn't find in /etc/fstab)", usbd.sDevice.c_str() );
vDevicesOut.erase( vDevicesOut.begin()+i );
--i;
}
}
LOG->Trace( "Done with GetUSBStorageDevices" );
}
bool MemoryCardDriverThreaded_Linux::Mount( UsbStorageDevice* pDevice )
{
ASSERT( !pDevice->sDevice.empty() );
RString sCommand = "mount " + pDevice->sDevice;
bool bMountedSuccessfully = ExecuteCommand( sCommand );
return bMountedSuccessfully;
}
void MemoryCardDriverThreaded_Linux::Unmount( UsbStorageDevice* pDevice )
{
if( pDevice->sDevice.empty() )
return;
/* Use umount -l, so we unmount the device even if it's in use. Open
* files remain usable, and the device (eg. /dev/sda) won't be reused
* by new devices until those are closed. Without this, if something
* causes the device to not unmount here, we'll never unmount it; that
* causes a device name leak, eventually running us out of mountpoints. */
RString sCommand = "sync; umount -l \"" + pDevice->sDevice + "\"";
ExecuteCommand( sCommand );
}
/*
* (c) 2003-2005 Chris Danford, Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,50 @@
#ifndef MemoryCardDriverThreaded_Linux_H
#define MemoryCardDriverThreaded_Linux_H 1
#include "MemoryCardDriver.h"
class MemoryCardDriverThreaded_Linux : public MemoryCardDriver
{
public:
virtual bool Mount( UsbStorageDevice* pDevice );
virtual void Unmount( UsbStorageDevice* pDevice );
protected:
void GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut );
bool USBStorageDevicesChanged();
bool TestWrite( UsbStorageDevice* pDevice );
RString m_sLastDevices;
};
#ifdef ARCH_MEMORY_CARD_DRIVER
#error "More than one MemoryCardDriver selected!"
#endif
#define ARCH_MEMORY_CARD_DRIVER MemoryCardDriverThreaded_Linux
#endif
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,249 @@
#include "global.h"
#include "MemoryCardDriverThreaded_MacOSX.h"
#include "Foreach.h"
#include "RageUtil.h"
#include "RageLog.h"
#include <Carbon/Carbon.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/usb/USBSpec.h>
#include <IOKit/usb/IOUSBLib.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <paths.h>
#include <unistd.h>
class MemoryCardDriverThreaded_MacOSX::Helper
{
public:
Helper( MemoryCardDriverThreaded_MacOSX *driver )
{
m_HandlerUPP = NewEventHandlerUPP( VolumesChanged );
EventTypeSpec types[] = { { kEventClassVolume, kEventVolumeMounted },
{ kEventClassVolume, kEventVolumeUnmounted } };
UInt32 numTypes = sizeof(types)/sizeof(types[0]);
OSStatus ret = InstallApplicationEventHandler( m_HandlerUPP, numTypes, types, driver, &m_Handler );
ASSERT( ret == noErr );
}
~Helper()
{
RemoveEventHandler( m_Handler );
DisposeEventHandlerUPP( m_HandlerUPP );
}
private:
static OSStatus VolumesChanged( EventHandlerCallRef ref, EventRef event, void *p )
{
MemoryCardDriverThreaded_MacOSX *driver = (MemoryCardDriverThreaded_MacOSX *)p;
LockMut( driver->m_ChangedLock );
driver->m_bChanged = true;
return eventNotHandledErr; // let others do something
}
EventHandlerUPP m_HandlerUPP;
EventHandlerRef m_Handler;
};
MemoryCardDriverThreaded_MacOSX::MemoryCardDriverThreaded_MacOSX() : m_ChangedLock( "MC changed lock" )
{
m_bChanged = true;
m_pHelper = new Helper( this );
}
MemoryCardDriverThreaded_MacOSX::~MemoryCardDriverThreaded_MacOSX()
{
delete m_pHelper;
}
void MemoryCardDriverThreaded_MacOSX::Unmount( UsbStorageDevice *pDevice )
{
ParamBlockRec pb;
Str255 name; // A pascal string.
const RString& base = Basename( pDevice->sOsMountDir );
memset( &pb, 0, sizeof(pb) );
name[0] = min( base.length(), size_t(255) );
strncpy( (char *)&name[1], base, name[0] );
pb.volumeParam.ioNamePtr = name;
pb.volumeParam.ioVolIndex = -1; // Use ioNamePtr to find the volume.
if( PBFlushVolSync(&pb) != noErr )
LOG->Warn( "Failed to flush the memory card." );
}
bool MemoryCardDriverThreaded_MacOSX::USBStorageDevicesChanged()
{
LockMut( m_ChangedLock );
return m_bChanged;
}
static int GetIntProperty( io_registry_entry_t entry, CFStringRef key )
{
CFTypeRef t = IORegistryEntryCreateCFProperty( entry, key, NULL, 0 );
if( !t )
return -1;
if( CFGetTypeID( t ) != CFNumberGetTypeID() )
{
CFRelease( t );
return -1;
}
int num;
if( !CFNumberGetValue(CFNumberRef(t), kCFNumberIntType, &num) )
num = -1;
CFRelease( t );
return num;
}
static RString GetStringProperty( io_registry_entry_t entry, CFStringRef key )
{
CFTypeRef t = IORegistryEntryCreateCFProperty( entry, key, NULL, 0 );
if( !t )
return RString();
if( CFGetTypeID( t ) != CFStringGetTypeID() )
{
CFRelease( t );
return RString();
}
CFStringRef s = CFStringRef( t );
RString ret;
const size_t len = CFStringGetMaximumSizeForEncoding( CFStringGetLength(s), kCFStringEncodingUTF8 );
char *buf = new char[len + 1];
if( CFStringGetCString( s, buf, len + 1, kCFStringEncodingUTF8 ) )
ret = buf;
delete[] buf;
CFRelease( t );
return ret;
}
void MemoryCardDriverThreaded_MacOSX::GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut )
{
LockMut( m_ChangedLock );
// First, get all device paths
struct statfs *fs;
int num = getfsstat( NULL, 0, MNT_NOWAIT );
fs = new struct statfs[num];
num = getfsstat( fs, num * sizeof(struct statfs), MNT_NOWAIT );
ASSERT( num != -1 );
for( int i = 0; i < num; ++i )
{
if( strncmp(fs[i].f_mntfromname, _PATH_DEV, strlen(_PATH_DEV)) )
continue;
const RString& sDevicePath = fs[i].f_mntfromname;
const RString& sDisk = Basename( sDevicePath ); // disk#[[s#] ...]
// Now that we have the disk name, look up the IOServices associated with it.
CFMutableDictionaryRef dict;
if( !(dict = IOBSDNameMatching(kIOMasterPortDefault, 0, sDisk)) )
continue;
// Look for certain properties: Leaf, Ejectable, Writable.
CFDictionarySetValue( dict, CFSTR(kIOMediaLeafKey), kCFBooleanTrue );
CFDictionarySetValue( dict, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue );
CFDictionarySetValue( dict, CFSTR(kIOMediaWritableKey), kCFBooleanTrue );
// Get the matching iterator. As always, this consumes a reference to dict.
io_iterator_t iter;
kern_return_t ret = IOServiceGetMatchingServices( kIOMasterPortDefault, dict, &iter );
if( ret != KERN_SUCCESS || iter == 0 )
continue;
// I'm not quite sure what it means to have two services with this device.
// Iterate over them all. If one contains what we want, stop.
io_registry_entry_t device; // This is the same as an io_object_t.
while( (device = IOIteratorNext(iter)) )
{
// Look at the parent of the device until we see an IOUSBMassStorageClass
while( device != MACH_PORT_NULL && !IOObjectConformsTo(device, "IOUSBMassStorageClass") )
{
io_registry_entry_t entry;
ret = IORegistryEntryGetParentEntry( device, kIOServicePlane, &entry );
IOObjectRelease( device );
device = ret == KERN_SUCCESS? entry:MACH_PORT_NULL;
}
// Now look for the corresponding IOUSBDevice, it's likely 2 up the tree
while( device != MACH_PORT_NULL && !IOObjectConformsTo(device, "IOUSBDevice") )
{
io_registry_entry_t entry;
ret = IORegistryEntryGetParentEntry( device, kIOServicePlane, &entry );
IOObjectRelease( device );
device = ret == KERN_SUCCESS? entry:MACH_PORT_NULL;
}
if( device == MACH_PORT_NULL )
continue;
// At this point, it is pretty safe to say that we've found a USB device.
vDevicesOut.push_back( UsbStorageDevice() );
UsbStorageDevice& usbd = vDevicesOut.back();
LOG->Trace( "Found memory card at path: %s.", fs[i].f_mntonname );
usbd.SetOsMountDir( fs[i].f_mntonname );
usbd.iVolumeSizeMB = int( (uint64_t(fs[i].f_blocks) * fs[i].f_bsize) >> 20 );
// Now we can get some more information from the registry tree.
usbd.iBus = GetIntProperty( device, CFSTR("USB Address") );
usbd.iPort = GetIntProperty( device, CFSTR("PortNum") );
// usbd.iLevel ?
usbd.sSerial = GetStringProperty( device, CFSTR("USB Serial Number") );
usbd.sDevice = fs[i].f_mntfromname;
usbd.idVendor = GetIntProperty( device, CFSTR(kUSBVendorID) );
usbd.idProduct = GetIntProperty( device, CFSTR(kUSBProductID) );
usbd.sVendor = GetStringProperty( device, CFSTR("USB Vendor Name") );
usbd.sProduct = GetStringProperty( device, CFSTR("USB Product Name") );
IOObjectRelease( device );
break; // We found what we wanted
}
IOObjectRelease( iter );
}
m_bChanged = false;
delete[] fs;
}
bool MemoryCardDriverThreaded_MacOSX::TestWrite( UsbStorageDevice *pDevice )
{
if( access(pDevice->sOsMountDir, W_OK) )
{
pDevice->SetError( "TestFailed" );
return false;
}
return true;
}
/*
* (c) 2005-2006, 2008 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,61 @@
#ifndef MEMORY_CARD_DRIVER_THREADED_MACOSX_H
#define MEMORY_CARD_DRIVER_THREADED_MACOSX_H
#include "MemoryCardDriver.h"
#include "RageThreads.h"
class MemoryCardDriverThreaded_MacOSX : public MemoryCardDriver
{
public:
MemoryCardDriverThreaded_MacOSX();
~MemoryCardDriverThreaded_MacOSX();
bool Mount( UsbStorageDevice *pDevice ) { return true; }
void Unmount( UsbStorageDevice *pDevice );
protected:
bool USBStorageDevicesChanged();
void GetUSBStorageDevices( vector<UsbStorageDevice>& vStorageDevicesOut );
bool TestWrite( UsbStorageDevice *pDevice );
private:
MemoryCardDriverThreaded_MacOSX( const MemoryCardDriverThreaded_MacOSX &m );
MemoryCardDriverThreaded_MacOSX &operator=( const MemoryCardDriverThreaded_MacOSX &m );
bool m_bChanged;
RageMutex m_ChangedLock;
class Helper;
friend class Helper;
Helper *m_pHelper;
};
#ifdef ARCH_MEMORY_CARD_DRIVER
#error "More than one MemoryCardDriver selected."
#endif
#define ARCH_MEMORY_CARD_DRIVER MemoryCardDriverThreaded_MacOSX
#endif
/*
* (c) 2005-2006, 2008 Steve Checkoway
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,232 @@
#include "global.h"
#include "MemoryCardDriverThreaded_Windows.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "archutils/Win32/ErrorStrings.h"
#include "PlayerNumber.h"
#include "MemoryCardManager.h"
MemoryCardDriverThreaded_Windows::MemoryCardDriverThreaded_Windows()
{
m_dwLastLogicalDrives = 0;
}
MemoryCardDriverThreaded_Windows::~MemoryCardDriverThreaded_Windows()
{
}
static bool TestReady( const RString &sDrive, RString &sVolumeLabelOut )
{
TCHAR szVolumeNameBuffer[MAX_PATH];
DWORD dwVolumeSerialNumber;
DWORD dwMaximumComponentLength;
DWORD lpFileSystemFlags;
TCHAR szFileSystemNameBuffer[MAX_PATH];
if( !GetVolumeInformation(
sDrive,
szVolumeNameBuffer,
sizeof(szVolumeNameBuffer),
&dwVolumeSerialNumber,
&dwMaximumComponentLength,
&lpFileSystemFlags,
szFileSystemNameBuffer,
sizeof(szFileSystemNameBuffer)) )
return false;
sVolumeLabelOut = szVolumeNameBuffer;
return true;
}
bool MemoryCardDriverThreaded_Windows::TestWrite( UsbStorageDevice* pDevice )
{
/* Try to write a file, to check if the device is writable and that we have write permission.
* Use FILE_ATTRIBUTE_TEMPORARY to try to avoid actually writing to the device. This reduces
* the chance of corruption if the user removes the device immediately, without doing anything. */
for( int i = 0; i < 10; ++i )
{
HANDLE hFile = CreateFile( ssprintf( "%stmp%i", pDevice->sOsMountDir.c_str(), RandomInt(100000)),
GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL );
if( hFile == INVALID_HANDLE_VALUE )
{
DWORD iError = GetLastError();
LOG->Warn( werr_ssprintf(iError, "Couldn't write to %s", pDevice->sOsMountDir.c_str()) );
if( iError == ERROR_FILE_EXISTS )
continue;
break;
}
CloseHandle( hFile );
return true;
}
pDevice->SetError( "TestFailed" );
return false;
}
static bool IsFloppyDrive( const RString &sDrive )
{
char szBuf[1024];
int iRet = QueryDosDevice( sDrive, szBuf, 1024 );
if( iRet == 0 )
{
LOG->Warn( werr_ssprintf(GetLastError(), "QueryDosDevice(%s)", sDrive.c_str()) );
return false;
}
// Make sure szBuf is terminated with two nulls. This only may be needed if the buffer filled.
szBuf[iRet-2] = 0;
szBuf[iRet-1] = 0;
const char *p = szBuf;
while( *p )
{
if( BeginsWith(p, "\\Device\\Floppy") )
return true;
p += strlen(p)+1;
}
return false;
}
void MemoryCardDriverThreaded_Windows::GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut )
{
LOG->Trace( "MemoryCardDriverThreaded_Windows::GetUSBStorageDevices" );
DWORD dwLogicalDrives = ::GetLogicalDrives();
m_dwLastLogicalDrives = dwLogicalDrives;
const int MAX_DRIVES = 26;
for( int i=0; i<MAX_DRIVES; ++i )
{
DWORD mask = (1 << i);
if( !(m_dwLastLogicalDrives & mask) )
continue; // drive letter is invalid
RString sDrive = ssprintf( "%c:", 'A'+i%26 );
LOG->Trace( sDrive );
if( IsFloppyDrive(sDrive) )
{
LOG->Trace( "IsFloppyDrive" );
continue;
}
// Testing hack: Allow non-removable drive letters to be used if that
// driver letter is specified as a m_sMemoryCardOsMountPoint.
bool bIsSpecifiedMountPoint = false;
FOREACH_ENUM( PlayerNumber, p )
bIsSpecifiedMountPoint |= MEMCARDMAN->m_sMemoryCardOsMountPoint[p].Get().EqualsNoCase(sDrive);
RString sDrivePath = sDrive + "\\";
if( bIsSpecifiedMountPoint )
{
LOG->Trace( "'%s' is a specified mount point. Allowing...", sDrive.c_str() );
}
else
{
if( GetDriveType(sDrivePath) != DRIVE_REMOVABLE )
{
LOG->Trace( "not DRIVE_REMOVABLE" );
continue;
}
}
RString sVolumeLabel;
if( !TestReady(sDrivePath, sVolumeLabel) )
{
LOG->Trace( "not TestReady" );
continue;
}
vDevicesOut.push_back( UsbStorageDevice() );
UsbStorageDevice &usbd = vDevicesOut.back();
usbd.SetOsMountDir( sDrive );
usbd.sDevice = "\\\\.\\" + sDrive;
usbd.sVolumeLabel = sVolumeLabel;
}
for( size_t i = 0; i < vDevicesOut.size(); ++i )
{
UsbStorageDevice &usbd = vDevicesOut[i];
// TODO: fill in bus/level/port with this:
// http://www.codeproject.com/system/EnumDeviceProperties.asp
// find volume size
DWORD dwSectorsPerCluster;
DWORD dwBytesPerSector;
DWORD dwNumberOfFreeClusters;
DWORD dwTotalNumberOfClusters;
if( GetDiskFreeSpace(
usbd.sOsMountDir,
&dwSectorsPerCluster,
&dwBytesPerSector,
&dwNumberOfFreeClusters,
&dwTotalNumberOfClusters ) )
{
usbd.iVolumeSizeMB = (int)roundf( dwTotalNumberOfClusters * (float)dwSectorsPerCluster * dwBytesPerSector / (1024*1024) );
}
}
}
bool MemoryCardDriverThreaded_Windows::USBStorageDevicesChanged()
{
return ::GetLogicalDrives() != m_dwLastLogicalDrives;
}
bool MemoryCardDriverThreaded_Windows::Mount( UsbStorageDevice* pDevice )
{
// nothing to do here...
return true;
}
void MemoryCardDriverThreaded_Windows::Unmount( UsbStorageDevice* pDevice )
{
/* Try to flush the device before returning. This requires administrator priviliges. */
HANDLE hDevice = CreateFile( pDevice->sDevice, GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hDevice == INVALID_HANDLE_VALUE )
{
LOG->Warn( werr_ssprintf(GetLastError(), "Couldn't open memory card device to flush (%s): CreateFile", pDevice->sDevice.c_str()) );
return;
}
if( !FlushFileBuffers(hDevice) )
LOG->Warn( werr_ssprintf(GetLastError(), "Couldn't flush memory card device (%s): FlushFileBuffers", pDevice->sDevice.c_str()) );
CloseHandle( hDevice );
}
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,54 @@
#ifndef MemoryCardDriverThreaded_Windows_H
#define MemoryCardDriverThreaded_Windows_H
#include "MemoryCardDriver.h"
#include <windows.h>
class MemoryCardDriverThreaded_Windows: public MemoryCardDriver
{
public:
MemoryCardDriverThreaded_Windows();
virtual ~MemoryCardDriverThreaded_Windows();
virtual bool Mount( UsbStorageDevice* pDevice );
virtual void Unmount( UsbStorageDevice* pDevice );
private:
void GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut );
bool USBStorageDevicesChanged();
bool TestWrite( UsbStorageDevice* pDevice );
DWORD m_dwLastLogicalDrives;
};
#ifdef ARCH_MEMORY_CARD_DRIVER
#error "More than one MemoryCardDriver included!"
#endif
#define ARCH_MEMORY_CARD_DRIVER MemoryCardDriverThreaded_Windows
#endif
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,150 @@
#include "global.h"
#include "MemoryCardDriverThreaded_Xbox.h"
#include "RageUtil.h"
#include "RageLog.h"
MemoryCardDriverThreaded_Xbox::MemoryCardDriverThreaded_Xbox()
{
}
MemoryCardDriverThreaded_Xbox::~MemoryCardDriverThreaded_Xbox()
{
}
static bool TestReady( const RString &sDrive, RString &sVolumeLabelOut )
{
TCHAR szVolumeNameBuffer[MAX_PATH];
DWORD dwVolumeSerialNumber;
DWORD dwMaximumComponentLength;
DWORD lpFileSystemFlags;
TCHAR szFileSystemNameBuffer[MAX_PATH];
if( !GetVolumeInformation(
sDrive,
szVolumeNameBuffer,
sizeof(szVolumeNameBuffer),
&dwVolumeSerialNumber,
&dwMaximumComponentLength,
&lpFileSystemFlags,
szFileSystemNameBuffer,
sizeof(szFileSystemNameBuffer)) ){
LOG->Trace("GetVolumeInformation failed %u", GetLastError());
return false;
}
sVolumeLabelOut = szVolumeNameBuffer;
return true;
}
bool MemoryCardDriverThreaded_Xbox::TestWrite( UsbStorageDevice* pDevice )
{
/* Try to write a file, to check if the device is writable and that we have write permission.*/
for( int i = 0; i < 10; ++i )
{
HANDLE hFile = CreateFile(
ssprintf( "%s\\tmp%i", pDevice->sOsMountDir.c_str(), RandomInt(100000)),
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_NEW,
FILE_FLAG_DELETE_ON_CLOSE,
NULL );
if( hFile == INVALID_HANDLE_VALUE )
{
DWORD iError = GetLastError();
LOG->Warn( "Couldn't write to %s (%u)", pDevice->sOsMountDir.c_str(), iError);
if( iError == ERROR_FILE_EXISTS )
continue;
break;
}
CloseHandle( hFile );
return true;
}
pDevice->SetError( "TestFailed" );
return false;
}
void MemoryCardDriverThreaded_Xbox::GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut )
{
DWORD devices=XGetDevices(XDEVICE_TYPE_MEMORY_UNIT);
for(int port=0;port<4;port++){
//top slot
if(devices&(1<<port)){
vDevicesOut.push_back( UsbStorageDevice() );
UsbStorageDevice &usbd = vDevicesOut.back();
usbd.iPort=port;
usbd.iLevel=0;
usbd.sDevice=ssprintf("Memory card on port %u on slot 0", port);
}
//bottom slot
if(devices&(1<<(port+16))){
vDevicesOut.push_back( UsbStorageDevice() );
UsbStorageDevice &usbd = vDevicesOut.back();
usbd.iPort=port;
usbd.iLevel=1;
usbd.sDevice=ssprintf("Memory card on port %u on slot 1", port);
}
}
}
bool MemoryCardDriverThreaded_Xbox::USBStorageDevicesChanged()
{
DWORD ins, rem;
return XGetDeviceChanges(XDEVICE_TYPE_MEMORY_UNIT, &ins, &rem)==TRUE;
}
bool MemoryCardDriverThreaded_Xbox::Mount( UsbStorageDevice* pDevice )
{
LOG->Trace( "%s", __FUNCTION__);
CHAR drive;
DWORD MountRetval=XMountMU(pDevice->iPort, pDevice->iLevel, &drive);
if(MountRetval==ERROR_SUCCESS){
LOG->Trace("Mounted memory card from port %u slot %u to %c:", pDevice->iPort, pDevice->iLevel, drive);
pDevice->SetOsMountDir(ssprintf("%c:", drive));
RString sVolumeLabel;
if( !TestReady(pDevice->sOsMountDir + "\\", sVolumeLabel) )
{
LOG->Trace( "not TestReady" );
}
pDevice->sVolumeLabel = sVolumeLabel;
return true;
}else{
LOG->Trace("Could not mount memory card %u", MountRetval);
return false;
}
}
void MemoryCardDriverThreaded_Xbox::Unmount( UsbStorageDevice* pDevice )
{
XUnmountMU(pDevice->iPort, pDevice->iLevel);
}
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,51 @@
#ifndef MemoryCardDriverThreaded_Xbox_H
#define MemoryCardDriverThreaded_Xbox_H
#include "MemoryCardDriver.h"
class MemoryCardDriverThreaded_Xbox: public MemoryCardDriver
{
public:
MemoryCardDriverThreaded_Xbox();
virtual ~MemoryCardDriverThreaded_Xbox();
virtual bool Mount( UsbStorageDevice* pDevice );
virtual void Unmount( UsbStorageDevice* pDevice );
private:
void GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut );
bool USBStorageDevicesChanged();
bool TestWrite( UsbStorageDevice* pDevice );
};
#ifdef ARCH_MEMORY_CARD_DRIVER
#error "More than one MemoryCardDriver included!"
#endif
#define ARCH_MEMORY_CARD_DRIVER MemoryCardDriverThreaded_Xbox
#endif
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,42 @@
#ifndef MEMORY_CARD_ENUMERATOR_NULL_H
#define MEMORY_CARD_ENUMERATOR_NULL_H
#include "MemoryCardDriver.h"
class MemoryCardDriver_Null : public MemoryCardDriver
{
public:
MemoryCardDriver_Null() {}
virtual bool USBStorageDevicesChanged() { return false; }
virtual void GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut ) { }
virtual bool Mount( UsbStorageDevice* pDevice ) { return false; }
virtual void Unmount( UsbStorageDevice* pDevice ) {}
virtual void Flush( UsbStorageDevice* pDevice ) {}
};
#endif
/*
* (c) 2003-2004 Chris Danford
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+150
View File
@@ -0,0 +1,150 @@
#include "global.h"
#include "MovieTexture.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "MovieTexture_Null.h"
#include "PrefsManager.h"
#include "RageFile.h"
#include "LocalizedString.h"
#include "Foreach.h"
#include "arch/arch_default.h"
void ForceToAscii( RString &str )
{
for( unsigned i=0; i<str.size(); ++i )
if( str[i] < 0x20 || str[i] > 0x7E )
str[i] = '?';
}
bool RageMovieTexture::GetFourCC( RString fn, RString &handler, RString &type )
{
RString ignore, ext;
splitpath( fn, ignore, ignore, ext);
if( !ext.CompareNoCase(".mpg") ||
!ext.CompareNoCase(".mpeg") ||
!ext.CompareNoCase(".mpv") ||
!ext.CompareNoCase(".mpe") )
{
handler = type = "MPEG";
return true;
}
//Not very pretty but should do all the same error checking without iostream
#define HANDLE_ERROR(x) { \
LOG->Warn( "Error reading %s: %s", fn.c_str(), x ); \
handler = type = ""; \
return false; \
}
RageFile file;
if( !file.Open(fn) )
HANDLE_ERROR("Could not open file.");
if( !file.Seek(0x70) )
HANDLE_ERROR("Could not seek.");
type = " ";
if( file.Read((char *)type.c_str(), 4) != 4 )
HANDLE_ERROR("Could not read.");
ForceToAscii( type );
if( file.Seek(0xBC) != 0xBC )
HANDLE_ERROR("Could not seek.");
handler = " ";
if( file.Read((char *)handler.c_str(), 4) != 4 )
HANDLE_ERROR("Could not read.");
ForceToAscii( handler );
return true;
#undef HANDLE_ERROR
}
DriverList RageMovieTextureDriver::m_pDriverList;
// Helper for MakeRageMovieTexture()
static void DumpAVIDebugInfo( const RString& fn )
{
RString type, handler;
if( !RageMovieTexture::GetFourCC( fn, handler, type ) )
return;
LOG->Trace( "Movie %s has handler '%s', type '%s'", fn.c_str(), handler.c_str(), type.c_str() );
}
static Preference<RString> g_sMovieDrivers( "MovieDrivers", "" ); // "" == default
/* Try drivers in order of preference until we find one that works. */
static LocalizedString MOVIE_DRIVERS_EMPTY ( "Arch", "Movie Drivers cannot be empty." );
static LocalizedString COULDNT_CREATE_MOVIE_DRIVER ( "Arch", "Couldn't create a movie driver." );
RageMovieTexture *RageMovieTexture::Create( RageTextureID ID )
{
DumpAVIDebugInfo( ID.filename );
RString sDrivers = g_sMovieDrivers;
if( sDrivers.empty() )
sDrivers = DEFAULT_MOVIE_DRIVER_LIST;
vector<RString> DriversToTry;
split( sDrivers, ",", DriversToTry, true );
if( DriversToTry.empty() )
RageException::Throw( "%s", MOVIE_DRIVERS_EMPTY.GetValue().c_str() );
RageMovieTexture *ret = NULL;
FOREACH_CONST( RString, DriversToTry, Driver )
{
LOG->Trace( "Initializing driver: %s", Driver->c_str() );
RageDriver *pDriverBase = RageMovieTextureDriver::m_pDriverList.Create( *Driver );
if( pDriverBase == NULL )
{
LOG->Trace( "Unknown movie driver name: %s", Driver->c_str() );
continue;
}
RageMovieTextureDriver *pDriver = dynamic_cast<RageMovieTextureDriver *>( pDriverBase );
ASSERT( pDriver );
RString sError;
ret = pDriver->Create( ID, sError );
delete pDriver;
if( ret == NULL )
{
LOG->Trace( "Couldn't load driver %s: %s", Driver->c_str(), sError.c_str() );
SAFE_DELETE( ret );
continue;
}
LOG->Trace( "Created movie texture \"%s\" with driver \"%s\"",
ID.filename.c_str(), Driver->c_str() );
break;
}
if ( !ret )
RageException::Throw( "%s", COULDNT_CREATE_MOVIE_DRIVER.GetValue().c_str() );
return ret;
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+64
View File
@@ -0,0 +1,64 @@
#ifndef MOVIE_TEXTURE_H
#define MOVIE_TEXTURE_H
#include "RageTexture.h"
#include "arch/RageDriver.h"
#include <map>
class RageMovieTexture : public RageTexture
{
public:
static RageMovieTexture *Create( RageTextureID ID );
RageMovieTexture( RageTextureID ID ): RageTexture(ID) { }
virtual ~RageMovieTexture() { }
virtual void Update( float fDeltaTime ) { }
virtual void Reload() = 0;
virtual void SetPosition( float fSeconds ) = 0;
virtual void SetPlaybackRate( float fRate ) = 0;
virtual void SetLooping( bool looping=true ) { }
bool IsAMovie() const { return true; }
static bool GetFourCC( RString fn, RString &handler, RString &type );
};
class RageMovieTextureDriver: public RageDriver
{
public:
virtual ~RageMovieTextureDriver() { }
virtual RageMovieTexture *Create( RageTextureID ID, RString &sError ) = 0;
static DriverList m_pDriverList;
};
#define REGISTER_MOVIE_TEXTURE_CLASS( name ) \
static RegisterRageDriver register_##name( &RageMovieTextureDriver::m_pDriverList, #name, CreateClass<RageMovieTextureDriver_##name, RageDriver> )
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,579 @@
#include "global.h"
#if defined(_MSC_VER)
/* XXX register thread */
#pragma comment(lib, "winmm.lib")
// Link with the DirectShow base class libraries
#if defined(DEBUG)
#pragma comment(lib, "baseclasses/debug/strmbasd.lib")
#else
#pragma comment(lib, "baseclasses/release/strmbase.lib")
#endif
#endif
#include "MovieTexture_DShowHelper.h"
#include "MovieTexture_DShow.h"
/* for TEXTUREMAN->GetTextureColorDepth() */
#include "RageTextureManager.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "RageException.h"
#include "RageSurface.h"
#include "arch/Dialog/Dialog.h"
#include "archutils/Win32/DirectXHelpers.h"
#include <vfw.h> /* for GetVideoCodecDebugInfo */
#if defined(_MSC_VER)
#pragma comment(lib, "vfw32.lib")
#endif
RageMovieTexture *RageMovieTextureDriver_DShow::Create( RageTextureID ID, RString &sError )
{
MovieTexture_DShow *pRet = new MovieTexture_DShow( ID );
sError = pRet->Init();
if( !sError.empty() )
SAFE_DELETE( pRet );
return pRet;
}
REGISTER_MOVIE_TEXTURE_CLASS( DShow );
static RString FourCCToString( int fcc )
{
char c[4];
c[0] = char((fcc >> 0) & 0xFF);
c[1] = char((fcc >> 8) & 0xFF);
c[2] = char((fcc >> 16) & 0xFF);
c[3] = char((fcc >> 24) & 0xFF);
RString s;
for( int i = 0; i < 4; ++i )
s += clamp( c[i], '\x20', '\x7e' );
return s;
}
static void CheckCodecVersion( RString codec, RString desc )
{
if( !codec.CompareNoCase("DIVX") )
{
/* "DivX 5.0.5 Codec" */
Regex GetDivXVersion;
int major, minor, rev;
if( sscanf( desc, "DivX %i.%i.%i", &major, &minor, &rev ) != 3 &&
sscanf( desc, "DivX Pro %i.%i.%i", &major, &minor, &rev ) != 3 )
{
LOG->Warn( "Couldn't parse DivX version \"%s\"", desc.c_str() );
return;
}
/* 5.0.0 through 5.0.4 are old and cause crashes. Warn. */
if( major == 5 && minor == 0 && rev < 5 )
{
Dialog::OK(
ssprintf("The version of DivX installed, %i.%i.%i, is out of date and may\n"
"cause instability. Please upgrade to DivX 5.0.5 or newer, available at:\n"
"\n"
"http://www.divx.com/", major, minor, rev),
desc );
return;
}
}
}
static void GetVideoCodecDebugInfo()
{
ICINFO info = { sizeof(ICINFO) };
LOG->Info( "Video codecs:" );
CHECKPOINT;
int i;
for( i=0; ICInfo(ICTYPE_VIDEO, i, &info); ++i )
{
CHECKPOINT;
if( FourCCToString(info.fccHandler) == "ASV1" )
{
/* Broken. */
LOG->Info("%i: %s: skipped", i, FourCCToString(info.fccHandler).c_str());
continue;
}
LOG->Trace( "Scanning codec %s", FourCCToString(info.fccHandler).c_str() );
CHECKPOINT;
HIC hic = ICOpen( info.fccType, info.fccHandler, ICMODE_DECOMPRESS );
if( !hic )
{
LOG->Info("Couldn't open video codec %s",
FourCCToString(info.fccHandler).c_str());
continue;
}
CHECKPOINT;
if( ICGetInfo(hic, &info, sizeof(ICINFO)) )
{
CheckCodecVersion( FourCCToString(info.fccHandler), WStringToRString(info.szDescription) );
CHECKPOINT;
LOG->Info( " %s: %ls (%ls)",
FourCCToString(info.fccHandler).c_str(), info.szName, info.szDescription );
}
else
LOG->Info( "ICGetInfo(%s) failed", FourCCToString(info.fccHandler).c_str() );
CHECKPOINT;
ICClose(hic);
}
if( i == 0 )
LOG->Info( " None found" );
}
MovieTexture_DShow::MovieTexture_DShow( RageTextureID ID ) :
RageMovieTexture( ID ),
buffer_lock( "buffer_lock", 1 ),
buffer_finished( "buffer_finished", 0 )
{
LOG->Trace( "MovieTexture_DShow::MovieTexture_DShow()" );
static bool bFirst = true;
if( bFirst )
{
bFirst = false;
GetVideoCodecDebugInfo();
}
m_bLoop = true;
m_bPlaying = false;
m_uTexHandle = 0;
buffer = NULL;
}
RString MovieTexture_DShow::Init()
{
RString sError = Create();
if( sError != "" )
return sError;
CreateFrameRects();
// flip all frame rects because movies are upside down
for( unsigned i=0; i<m_TextureCoordRects.size(); i++ )
swap(m_TextureCoordRects[i].top, m_TextureCoordRects[i].bottom);
return RString();
}
/* Hold buffer_lock. If it's held, then the decoding thread is waiting
* for us to process a frame; do so. */
void MovieTexture_DShow::SkipUpdates()
{
while( buffer_lock.TryWait() )
CheckFrame();
}
void MovieTexture_DShow::StopSkippingUpdates()
{
buffer_lock.Post();
}
MovieTexture_DShow::~MovieTexture_DShow()
{
LOG->Trace( "MovieTexture_DShow::~MovieTexture_DShow" );
LOG->Flush();
SkipUpdates();
/* Shut down the graph. We can't call Stop() here, since that will
* call SkipUpdates again, which will deadlock if we call it twice
* in a row. */
if( m_pGB )
{
LOG->Trace( "MovieTexture_DShow: shutdown" );
LOG->Flush();
CComPtr<IMediaControl> pMC;
m_pGB.QueryInterface(&pMC);
HRESULT hr;
if( FAILED( hr = pMC->Stop() ) )
RageException::Throw( hr_ssprintf(hr, "Could not stop the DirectShow graph.") );
// Stop();
m_pGB.Release();
}
LOG->Trace( "MovieTexture_DShow: shutdown ok" );
LOG->Flush();
if( m_uTexHandle )
DISPLAY->DeleteTexture( m_uTexHandle );
}
void MovieTexture_DShow::Reload()
{
// do nothing
}
/* If there's a frame waiting in the buffer, then the decoding thread put it there
* and is waiting for us to do something with it. */
void MovieTexture_DShow::CheckFrame()
{
if(buffer == NULL)
return;
CHECKPOINT;
/* Just in case we were invalidated: */
CreateTexture();
// DirectShow feeds us in BGR8
RageSurface *pFromDShow = CreateSurfaceFrom(
m_iSourceWidth, m_iSourceHeight,
24,
0xFF0000,
0x00FF00,
0x0000FF,
0x000000,
(uint8_t *) buffer, m_iSourceWidth*3 );
/*
* Optimization notes:
*
* With D3D, this surface can be anything; it'll convert it on the fly. If
* it happens to exactly match the texture, it'll copy a little faster.
*
* With OpenGL, it's best that this be a real, supported texture format, though
* it doesn't need to be that of the actual texture. If it isn't, it'll have
* to do a very slow conversion. Both RGB8 and BGR8 are both (usually) valid
* formats.
*/
CHECKPOINT;
DISPLAY->UpdateTexture(
m_uTexHandle,
pFromDShow,
0, 0,
m_iImageWidth, m_iImageHeight );
CHECKPOINT;
delete pFromDShow;
buffer = NULL;
CHECKPOINT;
/* Start the decoding thread again. */
buffer_finished.Post();
CHECKPOINT;
}
void MovieTexture_DShow::Update(float fDeltaTime)
{
CHECKPOINT;
// restart the movie if we reach the end
if( m_bLoop )
{
// Check for completion events
CComPtr<IMediaEvent> pME;
m_pGB.QueryInterface(&pME);
long lEventCode, lParam1, lParam2;
pME->GetEvent( &lEventCode, &lParam1, &lParam2, 0 );
if( lEventCode == EC_COMPLETE )
SetPosition(0);
}
CHECKPOINT;
CheckFrame();
}
RString PrintCodecError( HRESULT hr, RString s )
{
/* Actually, we might need XviD; we might want to look
* at the file and try to figure out if it's something
* common: DIV3, DIV4, DIV5, XVID, or maybe even MPEG2. */
RString err = hr_ssprintf(hr, "%s", s.c_str());
return
ssprintf(
"There was an error initializing a movie: %s.\n"
"Could not locate the DivX video codec.\n"
"DivX is required to movie textures and must\n"
"be installed before running the application.\n\n"
"Please visit http://www.divx.com to download the latest version.",
err.c_str() );
}
RString MovieTexture_DShow::GetActiveFilterList()
{
RString ret;
IEnumFilters *pEnum = NULL;
HRESULT hr = m_pGB->EnumFilters(&pEnum);
if (FAILED(hr))
return hr_ssprintf(hr, "EnumFilters");
IBaseFilter *pF = NULL;
while( S_OK == pEnum->Next(1, &pF, 0) )
{
FILTER_INFO FilterInfo;
pF->QueryFilterInfo( &FilterInfo );
if( ret != "" )
ret += ", ";
ret += WStringToRString(FilterInfo.achName);
if( FilterInfo.pGraph )
FilterInfo.pGraph->Release();
pF->Release();
}
pEnum->Release();
return ret;
}
RString MovieTexture_DShow::Create()
{
RageTextureID actualID = GetID();
HRESULT hr;
actualID.iAlphaBits = 0;
if( FAILED( hr=CoInitialize(NULL) ) )
RageException::Throw( hr_ssprintf(hr, "Could not CoInitialize") );
// Create the filter graph
if( FAILED( hr=m_pGB.CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC) ) )
RageException::Throw( hr_ssprintf(hr, "Could not create CLSID_FilterGraph!") );
// Create the Texture Renderer object
CTextureRenderer *pCTR = new CTextureRenderer;
/* Get a pointer to the IBaseFilter on the TextureRenderer, and add it to the
* graph. When m_pGB is released, it will free pFTR. */
CComPtr<IBaseFilter> pFTR = pCTR;
if( FAILED( hr = m_pGB->AddFilter(pFTR, L"TEXTURERENDERER" ) ) )
RageException::Throw( hr_ssprintf(hr, "Could not add renderer filter to graph!") );
// Add the source filter
CComPtr<IBaseFilter> pFSrc; // Source Filter
wstring wFileName = RStringToWstring(actualID.filename);
// if this fails, it's probably because the user doesn't have DivX installed
/* No, it also happens if the movie can't be opened for some reason; for example,
* if another program has it open and locked. Missing codecs probably won't
* show up until Connect(). */
if( FAILED( hr = m_pGB->AddSourceFilter( wFileName.c_str(), wFileName.c_str(), &pFSrc ) ) )
return PrintCodecError( hr, "Could not create source filter to graph!" );
// Find the source's output and the renderer's input
CComPtr<IPin> pFTRPinIn; // Texture Renderer Input Pin
if( FAILED( hr = pFTR->FindPin( L"In", &pFTRPinIn ) ) )
return hr_ssprintf(hr, "Could not find input pin" );
CComPtr<IPin> pFSrcPinOut; // Source Filter Output Pin
if( FAILED( hr = pFSrc->FindPin( L"Output", &pFSrcPinOut ) ) )
return hr_ssprintf( hr, "Could not find output pin" );
// Connect these two filters
if( FAILED( hr = m_pGB->Connect( pFSrcPinOut, pFTRPinIn ) ) )
return PrintCodecError( hr, "Could not connect pins" );
LOG->Trace( "Filters: %s", GetActiveFilterList().c_str() );
// Pass us to our TextureRenderer.
pCTR->SetRenderTarget(this);
/* Cap the max texture size to the hardware max. */
actualID.iMaxSize = min( actualID.iMaxSize, DISPLAY->GetMaxTextureSize() );
// The graph is built, now get the set the output video width and height.
// The source and image width will always be the same since we can't scale the video
m_iSourceWidth = pCTR->GetVidWidth();
m_iSourceHeight = pCTR->GetVidHeight();
/* image size cannot exceed max size */
m_iImageWidth = min( m_iSourceWidth, actualID.iMaxSize );
m_iImageHeight = min( m_iSourceHeight, actualID.iMaxSize );
/* Texture dimensions need to be a power of two; jump to the next. */
m_iTextureWidth = power_of_two(m_iImageWidth);
m_iTextureHeight = power_of_two(m_iImageHeight);
/* We've set up the movie, so we know the dimensions we need. Set
* up the texture. */
CreateTexture();
/* Pausing the graph will cause only one frame to be rendered. Do that, then
* wait for the frame to be rendered, to guarantee that the texture is set
* when this function returns. */
Pause();
CHECKPOINT;
pCTR->m_OneFrameDecoded.Wait();
CHECKPOINT;
CheckFrame();
CHECKPOINT;
// Start the graph running
Play();
return RString();
}
void MovieTexture_DShow::NewData(const char *data)
{
ASSERT(data);
/* Try to lock. */
if( buffer_lock.TryWait() )
{
/* The main thread is doing something uncommon, such as pausing.
* Drop this frame. */
return;
}
buffer = data;
buffer_finished.Wait();
ASSERT( buffer == NULL );
buffer_lock.Post();
}
void MovieTexture_DShow::CreateTexture()
{
if( m_uTexHandle )
return;
PixelFormat pixfmt;
switch( TEXTUREMAN->GetPrefs().m_iMovieColorDepth )
{
default:
ASSERT(0);
case 16:
if( DISPLAY->SupportsTextureFormat(PixelFormat_RGB5) )
pixfmt = PixelFormat_RGB5;
else
pixfmt = PixelFormat_RGBA4; // everything supports RGBA4
break;
case 32:
if( DISPLAY->SupportsTextureFormat(PixelFormat_RGB8) )
pixfmt = PixelFormat_RGB8;
else if( DISPLAY->SupportsTextureFormat(PixelFormat_RGBA8) )
pixfmt = PixelFormat_RGBA8;
else if( DISPLAY->SupportsTextureFormat(PixelFormat_RGB5) )
pixfmt = PixelFormat_RGB5;
else
pixfmt = PixelFormat_RGBA4; // everything supports RGBA4
break;
}
const RageDisplay::PixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc(pixfmt);
RageSurface *img = CreateSurface( m_iTextureWidth, m_iTextureHeight,
pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
m_uTexHandle = DISPLAY->CreateTexture( pixfmt, img, false );
delete img;
}
void MovieTexture_DShow::Play()
{
SkipUpdates();
LOG->Trace("MovieTexture_DShow::Play()");
CComPtr<IMediaControl> pMC;
m_pGB.QueryInterface(&pMC);
// Start the graph running;
HRESULT hr;
if( FAILED(hr = pMC->Run()) )
RageException::Throw( hr_ssprintf(hr, "Could not run the DirectShow graph.") );
m_bPlaying = true;
StopSkippingUpdates();
}
void MovieTexture_DShow::Pause()
{
SkipUpdates();
CComPtr<IMediaControl> pMC;
m_pGB.QueryInterface(&pMC);
HRESULT hr;
/* Use Pause(), so we'll get a still frame in CTextureRenderer::OnReceiveFirstSample. */
if( FAILED(hr = pMC->Pause()) )
RageException::Throw( hr_ssprintf(hr, "Could not pause the DirectShow graph.") );
StopSkippingUpdates();
}
void MovieTexture_DShow::SetPosition( float fSeconds )
{
SkipUpdates();
CComPtr<IMediaPosition> pMP;
m_pGB.QueryInterface(&pMP);
pMP->put_CurrentPosition(0);
StopSkippingUpdates();
}
void MovieTexture_DShow::SetPlaybackRate( float fRate )
{
if( fRate == 0 )
{
this->Pause();
return;
}
SkipUpdates();
CComPtr<IMediaPosition> pMP;
m_pGB.QueryInterface(&pMP);
HRESULT hr = pMP->put_Rate(fRate); // fails on many codecs
StopSkippingUpdates();
if( FAILED(hr) )
{
this->Pause();
return;
}
}
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+105
View File
@@ -0,0 +1,105 @@
/* MovieTexture_DShow - DirectShow movie renderer. */
#ifndef RAGE_MOVIE_TEXTURE_DSHOW_H
#define RAGE_MOVIE_TEXTURE_DSHOW_H
#include "MovieTexture.h"
/* Don't know why we need this for the headers ... */
typedef char TCHAR, *PTCHAR;
/* Prevent these from using Dbg stuff, which we don't link in. */
#ifdef DEBUG
#undef DEBUG
#undef _DEBUG
#define GIVE_BACK_DEBUG
#endif
#include <atlbase.h>
#ifdef GIVE_BACK_DEBUG
#undef GIVE_BACK_DEBUG
#define _DEBUG
#define DEBUG
#endif
#include "baseclasses/streams.h"
#include "RageDisplay.h"
#include "RageTexture.h"
#include "RageThreads.h"
class MovieTexture_DShow : public RageMovieTexture
{
public:
MovieTexture_DShow( RageTextureID ID );
virtual ~MovieTexture_DShow();
RString Init();
/* only called by RageTextureManager::InvalidateTextures */
void Invalidate() { m_uTexHandle = 0; }
void Update( float fDeltaTime );
virtual void Reload();
virtual void Play();
virtual void Pause();
virtual void SetPosition( float fSeconds );
virtual void SetPlaybackRate( float fRate );
void SetLooping( bool bLooping=true ) { m_bLoop = bLooping; }
void NewData( const char *pBuffer );
private:
const char *buffer;
RageSemaphore buffer_lock, buffer_finished;
RString Create();
void CreateTexture();
void SkipUpdates();
void StopSkippingUpdates();
void CheckFrame();
RString GetActiveFilterList();
unsigned GetTexHandle() const { return m_uTexHandle; }
unsigned m_uTexHandle;
CComPtr<IGraphBuilder> m_pGB; // GraphBuilder
bool m_bLoop;
bool m_bPlaying;
};
class RageMovieTextureDriver_DShow: public RageMovieTextureDriver
{
public:
virtual RageMovieTexture *Create( RageTextureID ID, RString &sError );
};
#endif
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,121 @@
#include "global.h"
#include "MovieTexture_DShowHelper.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "archutils/Win32/DirectXHelpers.h"
//-----------------------------------------------------------------------------
// Define GUID for Texture Renderer
// {71771540-2017-11cf-AE26-0020AFD79767}
//-----------------------------------------------------------------------------
struct __declspec(uuid("{71771540-2017-11cf-ae26-0020afd79767}")) CLSID_TextureRenderer;
static HRESULT CBV_ret;
CTextureRenderer::CTextureRenderer():
CBaseVideoRenderer(__uuidof(CLSID_TextureRenderer),
NAME("Texture Renderer"), NULL, &CBV_ret),
m_OneFrameDecoded( "m_OneFrameDecoded", 0 )
{
if( FAILED(CBV_ret) )
RageException::Throw( hr_ssprintf(CBV_ret, "Could not create texture renderer object!") );
m_pTexture = NULL;
}
CTextureRenderer::~CTextureRenderer()
{
}
HRESULT CTextureRenderer::CheckMediaType(const CMediaType *pmt)
{
VIDEOINFO *pvi;
// Reject the connection if this is not a video type
if( *pmt->FormatType() != FORMAT_VideoInfo )
return E_INVALIDARG;
/* Force the graph to R8G8B8. DirectShow won't generate a FMT_RGB5 that OpenGL
* can handle. It's faster to generate FMT8 and let OpenGL convert on the fly
* than to generate FMT_RGB5 and convert it ourself. */
pvi = (VIDEOINFO *)pmt->Format();
if( IsEqualGUID( *pmt->Type(), MEDIATYPE_Video) &&
IsEqualGUID( *pmt->Subtype(), MEDIASUBTYPE_RGB24) )
return S_OK;
return E_FAIL;
}
// SetMediaType: Graph connection has been made.
HRESULT CTextureRenderer::SetMediaType(const CMediaType *pmt)
{
// Retrive the size of this media type
VIDEOINFO *pviBmp; // Bitmap info header
pviBmp = (VIDEOINFO *)pmt->Format();
m_lVidWidth = pviBmp->bmiHeader.biWidth;
m_lVidHeight = abs(pviBmp->bmiHeader.biHeight);
m_lVidPitch = (m_lVidWidth * 3 + 3) + ~3; // We are forcing RGB24
return S_OK;
}
void CTextureRenderer::SetRenderTarget( MovieTexture_DShow* pTexture )
{
m_pTexture = pTexture;
}
// DoRenderSample: A sample has been delivered. Copy it.
HRESULT CTextureRenderer::DoRenderSample( IMediaSample * pSample )
{
if( m_pTexture == NULL )
{
LOG->Warn( "DoRenderSample called while m_pTexture was NULL!" );
return S_OK;
}
BYTE *pBmpBuffer; // Bitmap buffer
// Get the video bitmap buffer
pSample->GetPointer( &pBmpBuffer );
// Copy the bits
m_pTexture->NewData((char *) pBmpBuffer);
return S_OK;
}
void CTextureRenderer::OnReceiveFirstSample( IMediaSample * pSample )
{
/* If the main thread is in MovieTexture_DShow::Create, kick: */
if( m_OneFrameDecoded.GetValue() == 0 )
m_OneFrameDecoded.Post();
DoRenderSample( pSample );
}
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,66 @@
#ifndef RAGE_MOVIE_TEXTURE_DSHOW_HELPER_H
#define RAGE_MOVIE_TEXTURE_DSHOW_HELPER_H
#include "MovieTexture_DShow.h"
//-----------------------------------------------------------------------------
// CTextureRenderer Class Declarations
//
// Usage: 1) CheckMediaType is called by the graph
// 2) SetMediaType is called by the graph
// 3) call GetVidWidth and GetVidHeight to get texture information
// 4) call SetRenderTarget
// 5) Do RenderSample is called by the graph
//-----------------------------------------------------------------------------
class CTextureRenderer : public CBaseVideoRenderer
{
public:
CTextureRenderer();
~CTextureRenderer();
HRESULT CheckMediaType( const CMediaType *pmt ); // Format acceptable?
HRESULT SetMediaType( const CMediaType *pmt ); // Video format notification
HRESULT DoRenderSample( IMediaSample *pMediaSample ); // New video sample
void OnReceiveFirstSample( IMediaSample * pSample );
long GetVidWidth() const { return m_lVidWidth; }
long GetVidHeight() const { return m_lVidHeight; }
void SetRenderTarget( MovieTexture_DShow* pTexture );
RageSemaphore m_OneFrameDecoded;
protected:
// Video width, height, and pitch.
long m_lVidWidth, m_lVidHeight, m_lVidPitch;
char *output;
MovieTexture_DShow* m_pTexture; // the video surface we will copy new frames to
};
#endif
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,768 @@
#include "global.h"
#include "MovieTexture_FFMpeg.h"
#include "RageDisplay.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageFile.h"
#include "RageSurface.h"
#include <cerrno>
namespace avcodec
{
#include <ffmpeg/avformat.h>
};
#if defined(_MSC_VER) && !defined(XBOX)
#pragma comment(lib, "ffmpeg/lib/avcodec.lib")
#pragma comment(lib, "ffmpeg/lib/avformat.lib")
#endif
#if defined(XBOX)
/* NOTES: ffmpeg static libraries arent included in cvs - you have to build them yourself
* or remove this file to produce xbox build
*
* build ffmpeg with mingw32 ( howto http://arrozcru.no-ip.org/ffmpeg_wiki/tiki-index.php )
* ./configure --enable-memalign-hack --enable-static --disable-mmx --target-os=mingw32 --arch=x86
* you can use various switches to enable/disable codecs/muxers/etc.
*
* libgcc.a and libmingwex.a comes from mingw installation
* msys\mingw\lib\gcc\mingw32\3.4.5\libgcc.a */
#pragma comment(lib, "ffmpeg/lib/libavcodec.a")
#pragma comment(lib, "ffmpeg/lib/libavformat.a")
#pragma comment(lib, "ffmpeg/lib/libavutil.a")
#pragma comment(lib, "ffmpeg/lib/libgcc.a")
#endif
static struct AVPixelFormat_t
{
int bpp;
uint32_t masks[4];
avcodec::PixelFormat pf;
bool bHighColor;
bool bByteSwapOnLittleEndian;
MovieDecoderPixelFormatYCbCr YUV;
} AVPixelFormats[] = {
{
32,
{ 0xFF000000,
0x00FF0000,
0x0000FF00,
0x000000FF },
avcodec::PIX_FMT_YUYV422,
false, /* N/A */
true,
PixelFormatYCbCr_YUYV422,
},
{
32,
{ 0x0000FF00,
0x00FF0000,
0xFF000000,
0x000000FF },
avcodec::PIX_FMT_BGRA,
true,
true,
PixelFormatYCbCr_Invalid,
},
{
32,
{ 0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000 },
avcodec::PIX_FMT_ARGB,
true,
true,
PixelFormatYCbCr_Invalid,
},
/*
{
32,
{ 0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000 },
avcodec::PIX_FMT_ABGR,
true,
true,
PixelFormatYCbCr_Invalid,
},
{
32,
{ 0xFF000000,
0x00FF0000,
0x0000FF00,
0x000000FF },
avcodec::PIX_FMT_RGBA,
true,
true,
PixelFormatYCbCr_Invalid,
}, */
{
24,
{ 0xFF0000,
0x00FF00,
0x0000FF,
0x000000 },
avcodec::PIX_FMT_RGB24,
true,
true,
PixelFormatYCbCr_Invalid,
},
{
24,
{ 0x0000FF,
0x00FF00,
0xFF0000,
0x000000 },
avcodec::PIX_FMT_BGR24,
true,
true,
PixelFormatYCbCr_Invalid,
},
{
16,
{ 0x7C00,
0x03E0,
0x001F,
0x0000 },
avcodec::PIX_FMT_RGB555,
false,
false,
PixelFormatYCbCr_Invalid,
},
{ 0, { 0,0,0,0 }, avcodec::PIX_FMT_NB, true, false, PixelFormatYCbCr_Invalid }
};
static void FixLilEndian()
{
#if defined(ENDIAN_LITTLE)
static bool Initialized = false;
if( Initialized )
return;
Initialized = true;
for( int i = 0; i < AVPixelFormats[i].bpp; ++i )
{
AVPixelFormat_t &pf = AVPixelFormats[i];
if( !pf.bByteSwapOnLittleEndian )
continue;
for( int mask = 0; mask < 4; ++mask)
{
int m = pf.masks[mask];
switch( pf.bpp )
{
case 24: m = Swap24(m); break;
case 32: m = Swap32(m); break;
default: ASSERT(0);
}
pf.masks[mask] = m;
}
}
#endif
}
static int FindCompatibleAVFormat( bool bHighColor )
{
for( int i = 0; AVPixelFormats[i].bpp; ++i )
{
AVPixelFormat_t &fmt = AVPixelFormats[i];
if( fmt.YUV != PixelFormatYCbCr_Invalid )
{
EffectMode em = MovieTexture_Generic::GetEffectMode( fmt.YUV );
if( !DISPLAY->IsEffectModeSupported(em) )
continue;
}
else if( fmt.bHighColor != bHighColor )
{
continue;
}
PixelFormat pixfmt = DISPLAY->FindPixelFormat( fmt.bpp,
fmt.masks[0],
fmt.masks[1],
fmt.masks[2],
fmt.masks[3],
true /* realtime */
);
if( pixfmt == PixelFormat_Invalid )
continue;
return i;
}
return -1;
}
class MovieTexture_FFMpeg: public MovieTexture_Generic
{
public:
MovieTexture_FFMpeg( RageTextureID ID );
static void RegisterProtocols();
static RageSurface *AVCodecCreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, int &iAVTexfmt, MovieDecoderPixelFormatYCbCr &fmtout );
};
RageSurface *RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, int &iAVTexfmt, MovieDecoderPixelFormatYCbCr &fmtout )
{
FixLilEndian();
int iAVTexfmtIndex = FindCompatibleAVFormat( bPreferHighColor );
if( iAVTexfmtIndex == -1 )
iAVTexfmtIndex = FindCompatibleAVFormat( !bPreferHighColor );
if( iAVTexfmtIndex == -1 )
{
/* No dice. Use the first avcodec format of the preferred bit depth,
* and let the display system convert. */
for( iAVTexfmtIndex = 0; AVPixelFormats[iAVTexfmtIndex].bpp; ++iAVTexfmtIndex )
if( AVPixelFormats[iAVTexfmtIndex].bHighColor == bPreferHighColor )
break;
ASSERT( AVPixelFormats[iAVTexfmtIndex].bpp );
}
const AVPixelFormat_t *pfd = &AVPixelFormats[iAVTexfmtIndex];
iAVTexfmt = pfd->pf;
fmtout = pfd->YUV;
LOG->Trace( "Texture pixel format: %i %i (%ibpp, %08x %08x %08x %08x)", iAVTexfmt, fmtout,
pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
if( pfd->YUV == PixelFormatYCbCr_YUYV422 )
iTextureWidth /= 2;
return CreateSurface( iTextureWidth, iTextureHeight, pfd->bpp,
pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
}
class MovieDecoder_FFMpeg: public MovieDecoder
{
public:
MovieDecoder_FFMpeg();
~MovieDecoder_FFMpeg();
RString Open( RString sFile );
void Close();
void Rewind();
void GetFrame( RageSurface *pOut );
int DecodeFrame( float fTargetTime );
int GetWidth() const { return m_pStream->codec->width; }
int GetHeight() const { return m_pStream->codec->height; }
RageSurface *CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout );
float GetTimestamp() const;
float GetFrameDuration() const;
private:
void Init();
RString OpenCodec();
int ReadPacket();
int DecodePacket( float fTargetTime );
avcodec::AVStream *m_pStream;
avcodec::AVFrame m_Frame;
avcodec::PixelFormat m_AVTexfmt; /* PixelFormat of output surface */
float m_fPTS;
avcodec::AVFormatContext *m_fctx;
bool m_bGetNextTimestamp;
float m_fTimestamp;
float m_fTimestampOffset;
float m_fLastFrameDelay;
int m_iFrameNumber;
bool m_bHadBframes;
avcodec::AVPacket m_Packet;
int m_iCurrentPacketOffset;
/* 0 = no EOF
* 1 = EOF from ReadPacket
* 2 = EOF from ReadPacket and DecodePacket */
int m_iEOF;
};
MovieDecoder_FFMpeg::MovieDecoder_FFMpeg()
{
FixLilEndian();
m_fctx = NULL;
m_pStream = NULL;
m_iCurrentPacketOffset = -1;
/* Until we play the whole movie once without hitting a B-frame, assume
* they exist. */
m_bHadBframes = true;
Init();
}
MovieDecoder_FFMpeg::~MovieDecoder_FFMpeg()
{
if( m_iCurrentPacketOffset != -1 )
{
avcodec::av_free_packet( &m_Packet );
m_iCurrentPacketOffset = -1;
}
}
void MovieDecoder_FFMpeg::Init()
{
m_iEOF = 0;
m_bGetNextTimestamp = true;
m_fTimestamp = 0;
m_fLastFrameDelay = 0;
m_fPTS = -1;
m_iFrameNumber = -1; /* decode one frame and you're on the 0th */
m_fTimestampOffset = 0;
if( m_iCurrentPacketOffset != -1 )
{
avcodec::av_free_packet( &m_Packet );
m_iCurrentPacketOffset = -1;
}
}
/* Read until we get a frame, EOF or error. Return -1 on error, 0 on EOF, 1 if we have a frame. */
int MovieDecoder_FFMpeg::DecodeFrame( float fTargetTime )
{
while( 1 )
{
int ret = DecodePacket( fTargetTime );
if( ret == 1 )
return 1;
if( ret == -1 )
return -1;
if( ret == 0 && m_iEOF > 0 )
return 0; /* eof */
ASSERT( ret == 0 );
ret = ReadPacket();
if( ret < 0 )
return ret; /* error */
}
}
float MovieDecoder_FFMpeg::GetTimestamp() const
{
return m_fTimestamp - m_fTimestampOffset;
}
float MovieDecoder_FFMpeg::GetFrameDuration() const
{
return m_fLastFrameDelay;
}
/* Read a packet. Return -1 on error, 0 on EOF, 1 on OK. */
int MovieDecoder_FFMpeg::ReadPacket()
{
if( m_iEOF > 0 )
return 0;
while( 1 )
{
CHECKPOINT;
if( m_iCurrentPacketOffset != -1 )
{
m_iCurrentPacketOffset = -1;
avcodec::av_free_packet( &m_Packet );
}
int ret = avcodec::av_read_frame( m_fctx, &m_Packet );
/* XXX: why is avformat returning AVERROR_NOMEM on EOF? */
if( ret < 0 )
{
/* EOF. */
m_iEOF = 1;
m_Packet.size = 0;
return 0;
}
if( m_Packet.stream_index == m_pStream->index )
{
m_iCurrentPacketOffset = 0;
return 1;
}
/* It's not for the video stream; ignore it. */
avcodec::av_free_packet( &m_Packet );
}
}
/* Decode data from the current packet. Return -1 on error, 0 if the packet is finished,
* and 1 if we have a frame (we may have more data in the packet). */
int MovieDecoder_FFMpeg::DecodePacket( float fTargetTime )
{
if( m_iEOF == 0 && m_iCurrentPacketOffset == -1 )
return 0; /* no packet */
while( m_iEOF == 1 || (m_iEOF == 0 && m_iCurrentPacketOffset < m_Packet.size) )
{
if( m_bGetNextTimestamp )
{
if (m_Packet.dts != int64_t(AV_NOPTS_VALUE))
{
m_fPTS = float( m_Packet.dts * av_q2d(m_pStream->time_base) );
/* dts is the timestamp of the first frame in this packet. Only use it once;
* if we get more than one frame from the same packet (eg. f;lushing the last
* frame), extrapolate. */
m_Packet.dts = int64_t(AV_NOPTS_VALUE);
}
else
m_fPTS = -1;
m_bGetNextTimestamp = false;
}
/* If we have no data on the first frame, just return EOF; passing an empty packet
* to avcodec_decode_video in this case is crashing it. However, passing an empty
* packet is normal with B-frames, to flush. This may be unnecessary in newer
* versions of avcodec, but I'm waiting until a new stable release to upgrade. */
if( m_Packet.size == 0 && m_iFrameNumber == -1 )
return 0; /* eof */
bool bSkipThisFrame =
fTargetTime != -1 &&
GetTimestamp() + GetFrameDuration() <= fTargetTime &&
(m_pStream->codec->frame_number % 2) == 0;
int iGotFrame;
CHECKPOINT;
/* Hack: we need to send size = 0 to flush frames at the end, but we have
* to give it a buffer to read from since it tries to read anyway. */
static uint8_t dummy[FF_INPUT_BUFFER_PADDING_SIZE] = { 0 };
int len = avcodec::avcodec_decode_video(
m_pStream->codec,
&m_Frame, &iGotFrame,
m_Packet.size? m_Packet.data:dummy, m_Packet.size );
CHECKPOINT;
if( len < 0 )
{
LOG->Warn("avcodec_decode_video: %i", len);
return -1; // XXX
}
m_iCurrentPacketOffset += len;
if( !iGotFrame )
{
if( m_iEOF == 1 )
m_iEOF = 2;
continue;
}
m_bGetNextTimestamp = true;
if( m_fPTS != -1 )
{
m_fTimestamp = m_fPTS;
}
else
{
/* If the timestamp is zero, this frame is to be played at the
* time of the last frame plus the length of the last frame. */
m_fTimestamp += m_fLastFrameDelay;
}
/* Length of this frame: */
m_fLastFrameDelay = (float)m_pStream->codec->time_base.num / m_pStream->codec->time_base.den;
m_fLastFrameDelay += m_Frame.repeat_pict * (m_fLastFrameDelay * 0.5f);
++m_iFrameNumber;
if( m_Frame.pict_type == FF_B_TYPE )
m_bHadBframes = true;
if( m_iFrameNumber == 0 )
{
/* Some videos start with a timestamp other than 0. I think this is used
* when audio starts before the video. We don't want to honor that, since
* the DShow renderer doesn't and we don't want to break sync compatibility. */
const float expect = 0;
const float actual = m_fTimestamp;
if( actual - expect > 0 )
{
LOG->Trace("Expect %f, got %f -> %f", expect, actual, actual - expect );
m_fTimestampOffset = actual - expect;
}
}
if( bSkipThisFrame )
continue;
return 1;
}
return 0; /* packet done */
}
void MovieDecoder_FFMpeg::GetFrame( RageSurface *pSurface )
{
avcodec::AVPicture pict;
pict.data[0] = (unsigned char *) pSurface->pixels;
pict.linesize[0] = pSurface->pitch;
avcodec::img_convert( &pict, m_AVTexfmt,
(avcodec::AVPicture *) &m_Frame, m_pStream->codec->pix_fmt,
m_pStream->codec->width, m_pStream->codec->height );
}
static avcodec::AVStream *FindVideoStream( avcodec::AVFormatContext *m_fctx )
{
ASSERT_M( m_fctx->nb_streams <= MAX_STREAMS, ssprintf( "m_fctx->nb_streams = %d", m_fctx->nb_streams) );
for( unsigned stream = 0; stream < m_fctx->nb_streams; ++stream )
{
avcodec::AVStream *enc = m_fctx->streams[stream];
if( enc->codec->codec_type == avcodec::CODEC_TYPE_VIDEO )
return enc;
}
return NULL;
}
static RString averr_ssprintf( int err, const char *fmt, ... )
{
ASSERT( err < 0 );
va_list va;
va_start(va, fmt);
RString s = vssprintf( fmt, va );
va_end(va);
RString Error;
switch( err )
{
case AVERROR_IO: Error = "I/O error"; break;
case AVERROR_NUMEXPECTED: Error = "number syntax expected in filename"; break;
case AVERROR_INVALIDDATA: Error = "invalid data found"; break;
case AVERROR_NOMEM: Error = "not enough memory"; break;
case AVERROR_NOFMT: Error = "unknown format"; break;
default: Error = ssprintf( "unknown error %i", err ); break;
}
return s + " (" + Error + ")";
}
int URLRageFile_open( avcodec::URLContext *h, const char *filename, int flags )
{
if( strncmp( filename, "rage://", 7 ) )
{
LOG->Warn("URLRageFile_open: Unexpected path \"%s\"", filename );
return -EIO;
}
filename += 7;
int mode = 0;
switch( flags )
{
case URL_RDONLY: mode = RageFile::READ; break;
case URL_WRONLY: mode = RageFile::WRITE | RageFile::STREAMED; break;
case URL_RDWR: FAIL_M( "O_RDWR unsupported" );
}
RageFile *f = new RageFile;
if( !f->Open(filename, mode) )
{
LOG->Trace("Error opening \"%s\": %s", filename, f->GetError().c_str() );
delete f;
return -EIO;
}
h->is_streamed = false;
h->priv_data = f;
return 0;
}
int URLRageFile_read( avcodec::URLContext *h, unsigned char *buf, int size )
{
RageFile *f = (RageFile *) h->priv_data;
return f->Read( buf, size );
}
int URLRageFile_write( avcodec::URLContext *h, unsigned char *buf, int size )
{
RageFile *f = (RageFile *) h->priv_data;
return f->Write( buf, size );
}
avcodec::offset_t URLRageFile_seek( avcodec::URLContext *h, avcodec::offset_t pos, int whence )
{
RageFile *f = (RageFile *) h->priv_data;
if( whence == AVSEEK_SIZE )
return f->Tell();
if( whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END )
return -1;
return f->Seek( (int) pos, whence );
}
int URLRageFile_close( avcodec::URLContext *h )
{
RageFile *f = (RageFile *) h->priv_data;
delete f;
return 0;
}
static avcodec::URLProtocol RageProtocol =
{
"rage",
URLRageFile_open,
URLRageFile_read,
URLRageFile_write,
URLRageFile_seek,
URLRageFile_close,
NULL
};
void MovieTexture_FFMpeg::RegisterProtocols()
{
static bool Done = false;
if( Done )
return;
Done = true;
avcodec::av_register_all();
avcodec::register_protocol( &RageProtocol );
}
RString MovieDecoder_FFMpeg::Open( RString sFile )
{
MovieTexture_FFMpeg::RegisterProtocols();
int ret = avcodec::av_open_input_file( &m_fctx, "rage://" + sFile, NULL, 0, NULL );
if( ret < 0 )
return RString( averr_ssprintf(ret, "AVCodec: Couldn't open \"%s\"", sFile.c_str()) );
ret = avcodec::av_find_stream_info( m_fctx );
if( ret < 0 )
return RString( averr_ssprintf(ret, "AVCodec (%s): Couldn't find codec parameters", sFile.c_str()) );
avcodec::AVStream *pStream = FindVideoStream( m_fctx );
if( pStream == NULL )
return "Couldn't find any video streams";
m_pStream = pStream;
if( m_pStream->codec->codec_id == avcodec::CODEC_ID_NONE )
return ssprintf( "Unsupported codec %08x", m_pStream->codec->codec_tag );
RString sError = OpenCodec();
if( !sError.empty() )
return ssprintf( "AVCodec (%s): %s", sFile.c_str(), sError.c_str() );
LOG->Trace( "Bitrate: %i", m_pStream->codec->bit_rate );
LOG->Trace( "Codec pixel format: %s", avcodec::avcodec_get_pix_fmt_name(m_pStream->codec->pix_fmt) );
return RString();
}
RString MovieDecoder_FFMpeg::OpenCodec()
{
Init();
ASSERT( m_pStream );
if( m_pStream->codec->codec )
avcodec::avcodec_close( m_pStream->codec );
avcodec::AVCodec *pCodec = avcodec::avcodec_find_decoder( m_pStream->codec->codec_id );
if( pCodec == NULL )
return ssprintf( "Couldn't find decoder %i", m_pStream->codec->codec_id );
LOG->Trace("Opening codec %s", pCodec->name );
if( !m_bHadBframes )
{
LOG->Trace("Setting CODEC_FLAG_LOW_DELAY" );
m_pStream->codec->flags |= CODEC_FLAG_LOW_DELAY;
}
int ret = avcodec::avcodec_open( m_pStream->codec, pCodec );
if( ret < 0 )
return RString( averr_ssprintf(ret, "Couldn't open codec \"%s\"", pCodec->name) );
ASSERT( m_pStream->codec->codec );
/* This is set to true when we find a B-frame, to use on the next loop. */
m_bHadBframes = false;
return RString();
}
void MovieDecoder_FFMpeg::Close()
{
if( m_pStream && m_pStream->codec->codec )
{
avcodec::avcodec_close( m_pStream->codec );
m_pStream = NULL;
}
if( m_fctx )
{
avcodec::av_close_input_file( m_fctx );
m_fctx = NULL;
}
Init();
}
void MovieDecoder_FFMpeg::Rewind()
{
avcodec::av_seek_frame( m_fctx, -1, 0, 0 );
OpenCodec();
}
RageSurface *MovieDecoder_FFMpeg::CreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, MovieDecoderPixelFormatYCbCr &fmtout )
{
return RageMovieTextureDriver_FFMpeg::AVCodecCreateCompatibleSurface( iTextureWidth, iTextureHeight, bPreferHighColor, *ConvertValue<int>(&m_AVTexfmt), fmtout );
}
MovieTexture_FFMpeg::MovieTexture_FFMpeg( RageTextureID ID ):
MovieTexture_Generic( ID, new MovieDecoder_FFMpeg )
{
}
RageMovieTexture *RageMovieTextureDriver_FFMpeg::Create( RageTextureID ID, RString &sError )
{
MovieTexture_FFMpeg *pRet = new MovieTexture_FFMpeg( ID );
sError = pRet->Init();
if( !sError.empty() )
SAFE_DELETE( pRet );
return pRet;
}
REGISTER_MOVIE_TEXTURE_CLASS( FFMpeg );
/*
* (c) 2003-2005 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,41 @@
/* MovieTexture_FFMpeg - FFMpeg movie renderer. */
#ifndef RAGE_MOVIE_TEXTURE_FFMPEG_H
#define RAGE_MOVIE_TEXTURE_FFMPEG_H
#include "MovieTexture_Generic.h"
struct RageSurface;
class RageMovieTextureDriver_FFMpeg: public RageMovieTextureDriver
{
public:
virtual RageMovieTexture *Create( RageTextureID ID, RString &sError );
static RageSurface *AVCodecCreateCompatibleSurface( int iTextureWidth, int iTextureHeight, bool bPreferHighColor, int &iAVTexfmt, MovieDecoderPixelFormatYCbCr &fmtout );
};
#endif
/*
* (c) 2003-2005 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -0,0 +1,556 @@
#include "global.h"
#include "MovieTexture_Generic.h"
#include "PrefsManager.h"
#include "RageDisplay.h"
#include "RageLog.h"
#include "RageSurface.h"
#include "RageTextureManager.h"
#include "RageTextureRenderTarget.h"
#include "RageUtil.h"
#include "Sprite.h"
#if defined(WIN32) && !defined(XBOX)
#include "archutils/Win32/ErrorStrings.h"
#include <windows.h>
#endif
static Preference<bool> g_bMovieTextureDirectUpdates( "MovieTextureDirectUpdates", true );
MovieTexture_Generic::MovieTexture_Generic( RageTextureID ID, MovieDecoder *pDecoder ):
RageMovieTexture( ID )
{
LOG->Trace( "MovieTexture_Generic::MovieTexture_Generic(%s)", ID.filename.c_str() );
m_pDecoder = pDecoder;
m_uTexHandle = 0;
m_pRenderTarget = NULL;
m_pTextureIntermediate = NULL;
m_bLoop = true;
m_pSurface = NULL;
m_pTextureLock = NULL;
m_ImageWaiting = FRAME_NONE;
m_fRate = 1;
m_bWantRewind = false;
m_fClock = 0;
m_bFrameSkipMode = false;
m_pSprite = new Sprite;
}
RString MovieTexture_Generic::Init()
{
RString sError = m_pDecoder->Open( GetID().filename );
if( sError != "" )
return sError;
CreateTexture();
CreateFrameRects();
/* Decode one frame, to guarantee that the texture is drawn when this function returns. */
int ret = m_pDecoder->DecodeFrame( -1 );
if( ret == -1 )
return ssprintf( "%s: error getting first frame", GetID().filename.c_str() );
if( ret == 0 )
{
/* There's nothing there. */
return ssprintf( "%s: EOF getting first frame", GetID().filename.c_str() );
}
m_ImageWaiting = FRAME_DECODED;
LOG->Trace( "Resolution: %ix%i (%ix%i, %ix%i)",
m_iSourceWidth, m_iSourceHeight,
m_iImageWidth, m_iImageHeight, m_iTextureWidth, m_iTextureHeight );
UpdateFrame();
CHECKPOINT;
return RString();
}
MovieTexture_Generic::~MovieTexture_Generic()
{
if( m_pDecoder )
m_pDecoder->Close();
/* m_pSprite may reference the texture; delete it before DestroyTexture. */
delete m_pSprite;
DestroyTexture();
delete m_pDecoder;
}
/* Delete the surface and texture. The decoding thread must be stopped, and this
* is normally done after destroying the decoder. */
void MovieTexture_Generic::DestroyTexture()
{
delete m_pSurface;
m_pSurface = NULL;
delete m_pTextureLock;
m_pTextureLock = NULL;
if( m_uTexHandle )
{
DISPLAY->DeleteTexture( m_uTexHandle );
m_uTexHandle = 0;
}
delete m_pRenderTarget;
m_pRenderTarget = NULL;
delete m_pTextureIntermediate;
m_pTextureIntermediate = NULL;
}
class RageMovieTexture_Generic_Intermediate : public RageTexture
{
public:
RageMovieTexture_Generic_Intermediate( RageTextureID ID, int iWidth, int iHeight,
int iImageWidth, int iImageHeight, int iTextureWidth, int iTextureHeight,
RageSurfaceFormat SurfaceFormat, PixelFormat pixfmt ):
RageTexture(ID),
m_SurfaceFormat( SurfaceFormat )
{
m_PixFmt = pixfmt;
m_iSourceWidth = iWidth;
m_iSourceHeight = iHeight;
/* int iMaxSize = min( GetID().iMaxSize, DISPLAY->GetMaxTextureSize() );
m_iImageWidth = min( m_iSourceWidth, iMaxSize );
m_iImageHeight = min( m_iSourceHeight, iMaxSize );
m_iTextureWidth = power_of_two( m_iImageWidth );
m_iTextureHeight = power_of_two( m_iImageHeight );
*/
m_iImageWidth = iImageWidth;
m_iImageHeight = iImageHeight;
m_iTextureWidth = iTextureWidth;
m_iTextureHeight = iTextureHeight;
CreateFrameRects();
m_uTexHandle = 0;
CreateTexture();
}
virtual ~RageMovieTexture_Generic_Intermediate()
{
if( m_uTexHandle )
{
DISPLAY->DeleteTexture( m_uTexHandle );
m_uTexHandle = 0;
}
}
virtual void Invalidate() { m_uTexHandle = 0; }
virtual void Reload() { }
virtual unsigned GetTexHandle() const
{
return m_uTexHandle;
}
bool IsAMovie() const { return true; }
private:
void CreateTexture()
{
if( m_uTexHandle )
return;
RageSurface *pSurface = CreateSurfaceFrom( m_iImageWidth, m_iImageHeight,
m_SurfaceFormat.BitsPerPixel,
m_SurfaceFormat.Mask[0],
m_SurfaceFormat.Mask[1],
m_SurfaceFormat.Mask[2],
m_SurfaceFormat.Mask[3], NULL, 1 );
m_uTexHandle = DISPLAY->CreateTexture( m_PixFmt, pSurface, false );
delete pSurface;
}
unsigned m_uTexHandle;
RageSurfaceFormat m_SurfaceFormat;
PixelFormat m_PixFmt;
};
void MovieTexture_Generic::Invalidate()
{
m_uTexHandle = 0;
if( m_pTextureIntermediate != NULL )
m_pTextureIntermediate->Invalidate();
}
void MovieTexture_Generic::CreateTexture()
{
if( m_uTexHandle || m_pRenderTarget != NULL )
return;
CHECKPOINT;
m_iSourceWidth = m_pDecoder->GetWidth();
m_iSourceHeight = m_pDecoder->GetHeight();
/* Adjust m_iSourceWidth to support different source aspect ratios. */
float fSourceAspectRatio = m_pDecoder->GetSourceAspectRatio();
if( fSourceAspectRatio < 1 )
m_iSourceHeight = lrintf( m_iSourceHeight / fSourceAspectRatio );
else if( fSourceAspectRatio > 1 )
m_iSourceWidth = lrintf( m_iSourceWidth * fSourceAspectRatio );
/* Cap the max texture size to the hardware max. */
int iMaxSize = min( GetID().iMaxSize, DISPLAY->GetMaxTextureSize() );
m_iImageWidth = min( m_iSourceWidth, iMaxSize );
m_iImageHeight = min( m_iSourceHeight, iMaxSize );
/* Texture dimensions need to be a power of two; jump to the next. */
m_iTextureWidth = power_of_two( m_iImageWidth );
m_iTextureHeight = power_of_two( m_iImageHeight );
MovieDecoderPixelFormatYCbCr fmt = PixelFormatYCbCr_Invalid;
if( m_pSurface == NULL )
{
ASSERT( m_pTextureLock == NULL );
if( g_bMovieTextureDirectUpdates )
m_pTextureLock = DISPLAY->CreateTextureLock();
m_pSurface = m_pDecoder->CreateCompatibleSurface( m_iImageWidth, m_iImageHeight,
TEXTUREMAN->GetPrefs().m_iMovieColorDepth == 32, fmt );
if( m_pTextureLock != NULL )
{
delete [] m_pSurface->pixels;
m_pSurface->pixels = NULL;
}
}
PixelFormat pixfmt = DISPLAY->FindPixelFormat( m_pSurface->format->BitsPerPixel,
m_pSurface->format->Mask[0],
m_pSurface->format->Mask[1],
m_pSurface->format->Mask[2],
m_pSurface->format->Mask[3] );
if( pixfmt == PixelFormat_Invalid )
{
/* We weren't given a natively-supported pixel format. Pick a supported
* one. This is a fallback case, and implies a second conversion. */
switch( TEXTUREMAN->GetPrefs().m_iMovieColorDepth )
{
default:
ASSERT(0);
case 16:
if( DISPLAY->SupportsTextureFormat(PixelFormat_RGB5) )
pixfmt = PixelFormat_RGB5;
else
pixfmt = PixelFormat_RGBA4;
break;
case 32:
if( DISPLAY->SupportsTextureFormat(PixelFormat_RGB8) )
pixfmt = PixelFormat_RGB8;
else if( DISPLAY->SupportsTextureFormat(PixelFormat_RGBA8) )
pixfmt = PixelFormat_RGBA8;
else if( DISPLAY->SupportsTextureFormat(PixelFormat_RGB5) )
pixfmt = PixelFormat_RGB5;
else
pixfmt = PixelFormat_RGBA4;
break;
}
}
if( fmt != PixelFormatYCbCr_Invalid )
{
SAFE_DELETE( m_pTextureIntermediate );
m_pSprite->UnloadTexture();
/* Create the render target. This will receive the final, converted texture. */
RenderTargetParam param;
param.iWidth = m_iImageWidth;
param.iHeight = m_iImageHeight;
RageTextureID TargetID( GetID() );
TargetID.filename += " target";
m_pRenderTarget = new RageTextureRenderTarget( TargetID, param );
/* Create the intermediate texture. This receives the YUV image. */
RageTextureID IntermedID( GetID() );
IntermedID.filename += " intermediate";
m_pTextureIntermediate = new RageMovieTexture_Generic_Intermediate( IntermedID,
m_pDecoder->GetWidth(), m_pDecoder->GetHeight(),
m_pSurface->w, m_pSurface->h,
power_of_two(m_pSurface->w), power_of_two(m_pSurface->h),
*m_pSurface->format, pixfmt );
/* Configure the sprite. This blits the intermediate onto the ifnal render target. */
m_pSprite->SetHorizAlign( align_left );
m_pSprite->SetVertAlign( align_top );
/* Hack: Sprite wants to take ownership of the texture, and will decrement the refcount
* when it unloads the texture. Normally we'd make a "copy", but we can't access
* RageTextureManager from here. Just increment the refcount. */
++m_pTextureIntermediate->m_iRefCount;
m_pSprite->SetTexture( m_pTextureIntermediate );
m_pSprite->SetEffectMode( GetEffectMode(fmt) );
return;
}
m_uTexHandle = DISPLAY->CreateTexture( pixfmt, m_pSurface, false );
}
/* Handle decoding for a frame. Return true if a frame was decoded, false if not
* (due to quit, error, EOF, etc). If true is returned, we'll be in FRAME_DECODED. */
bool MovieTexture_Generic::DecodeFrame()
{
bool bTriedRewind = false;
do
{
if( m_bWantRewind )
{
if( bTriedRewind )
{
LOG->Trace( "File \"%s\" looped more than once in one frame", GetID().filename.c_str() );
return false;
}
m_bWantRewind = false;
bTriedRewind = true;
/* When resetting the clock, set it back by the length of the last frame,
* so it has a proper delay. */
float fDelay = m_pDecoder->GetFrameDuration();
/* Restart. */
m_pDecoder->Rewind();
m_fClock = -fDelay;
}
CHECKPOINT;
/* Read a frame. */
float fTargetTime = -1;
if( m_bFrameSkipMode && m_fClock > m_pDecoder->GetTimestamp() )
fTargetTime = m_fClock;
int ret = m_pDecoder->DecodeFrame( fTargetTime );
if( ret == -1 )
return false;
if( m_bWantRewind && m_pDecoder->GetTimestamp() == 0 )
m_bWantRewind = false; /* ignore */
if( ret == 0 )
{
/* EOF. */
if( !m_bLoop )
return false;
LOG->Trace( "File \"%s\" looping", GetID().filename.c_str() );
m_bWantRewind = true;
continue;
}
/* We got a frame. */
} while( m_bWantRewind );
return true;
}
/*
* Returns:
* == 0 if the currently decoded frame is ready to be displayed
* > 0 (seconds) if it's not yet time to display;
*/
float MovieTexture_Generic::CheckFrameTime()
{
if( m_fRate == 0 )
return 1; // "a long time until the next frame"
const float fOffset = (m_pDecoder->GetTimestamp() - m_fClock) / m_fRate;
/* If we're ahead, we're decoding too fast; delay. */
if( fOffset > 0.00001f )
{
if( m_bFrameSkipMode )
{
/* We're caught up; stop skipping frames. */
LOG->Trace( "stopped skipping frames" );
m_bFrameSkipMode = false;
}
return fOffset;
}
/*
* We're behind by -Offset seconds.
*
* If we're just slightly behind, don't worry about it; we'll simply
* not sleep, so we'll move as fast as we can to catch up.
*
* If we're far behind, we're short on CPU. Skip texture updates; this
* is a big bottleneck on many systems.
*
* If we hit a threshold, start skipping frames via #1. If we do that,
* don't stop once we hit the threshold; keep doing it until we're fully
* caught up.
*
* We should try to notice if we simply don't have enough CPU for the video;
* it's better to just stay in frame skip mode than to enter and exit it
* constantly, but we don't want to do that due to a single timing glitch.
*/
const float FrameSkipThreshold = 0.5f;
if( -fOffset >= FrameSkipThreshold && !m_bFrameSkipMode )
{
LOG->Trace( "(%s) Time is %f, and the movie is at %f. Entering frame skip mode.",
GetID().filename.c_str(), m_fClock, m_pDecoder->GetTimestamp() );
m_bFrameSkipMode = true;
}
return 0;
}
/* Decode data. */
void MovieTexture_Generic::DecodeSeconds( float fSeconds )
{
m_fClock += fSeconds * m_fRate;
/* We might need to decode more than one frame per update. However, there
* have been bugs in ffmpeg that cause it to not handle EOF properly, which
* could make this never return, so let's play it safe. */
int iMax = 4;
while( --iMax )
{
/* If we don't have a frame decoded, decode one. */
if( m_ImageWaiting == FRAME_NONE )
{
if( !DecodeFrame() )
break;
m_ImageWaiting = FRAME_DECODED;
}
/* If we have a frame decoded, see if it's time to display it. */
float fTime = CheckFrameTime();
if( fTime > 0 )
return;
CHECKPOINT;
UpdateFrame();
m_ImageWaiting = FRAME_NONE;
return;
}
LOG->MapLog( "movie_looping", "MovieTexture_Generic::Update looping" );
}
void MovieTexture_Generic::UpdateFrame()
{
/* Just in case we were invalidated: */
CreateTexture();
if( m_pTextureLock != NULL )
{
int iHandle = m_pTextureIntermediate != NULL? m_pTextureIntermediate->GetTexHandle(): this->GetTexHandle();
m_pTextureLock->Lock( iHandle, m_pSurface );
}
m_pDecoder->GetFrame( m_pSurface );
if( m_pTextureLock != NULL )
m_pTextureLock->Unlock( m_pSurface, true );
if( m_pRenderTarget != NULL )
{
CHECKPOINT;
/* If we have no m_pTextureLock, we still have to upload the texture. */
if( m_pTextureLock == NULL )
DISPLAY->UpdateTexture(
m_pTextureIntermediate->GetTexHandle(),
m_pSurface,
0, 0,
m_pSurface->w, m_pSurface->h );
CHECKPOINT;
m_pRenderTarget->BeginRenderingTo( false );
m_pSprite->Draw();
m_pRenderTarget->FinishRenderingTo();
}
else
{
CHECKPOINT;
if( m_pTextureLock == NULL )
DISPLAY->UpdateTexture(
m_uTexHandle,
m_pSurface,
0, 0,
m_iImageWidth, m_iImageHeight );
CHECKPOINT;
}
}
static EffectMode EffectModes[] =
{
EffectMode_YUYV422,
};
COMPILE_ASSERT( ARRAYLEN(EffectModes) == NUM_PixelFormatYCbCr );
EffectMode MovieTexture_Generic::GetEffectMode( MovieDecoderPixelFormatYCbCr fmt )
{
ASSERT( fmt != PixelFormatYCbCr_Invalid );
return EffectModes[fmt];
}
void MovieTexture_Generic::Reload()
{
}
void MovieTexture_Generic::SetPosition( float fSeconds )
{
/* We can reset to 0, but I don't think this API supports fast seeking
* yet. I don't think we ever actually seek except to 0 right now,
* anyway. XXX */
if( fSeconds != 0 )
{
LOG->Warn( "MovieTexture_Generic::SetPosition(%f): non-0 seeking unsupported; ignored", fSeconds );
return;
}
LOG->Trace( "Seek to %f", fSeconds );
m_bWantRewind = true;
}
unsigned MovieTexture_Generic::GetTexHandle() const
{
if( m_pRenderTarget != NULL )
return m_pRenderTarget->GetTexHandle();
return m_uTexHandle;
}
/*
* (c) 2003-2005 Glenn Maynard
* All rights reserved.
*
* 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, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* 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 OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/

Some files were not shown because too many files have changed in this diff Show More