//////////////////////////////////////////////////////////////////////////////////////// // // Nestopia - NES/Famicom emulator written in C++ // // Copyright (C) 2003-2008 Martin Freij // // This file is part of Nestopia. // // Nestopia is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Nestopia is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Nestopia; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //////////////////////////////////////////////////////////////////////////////////////// #include "language/resource.h" #include "NstResourceCursor.hpp" #include "NstResourceVersion.hpp" #include "NstApplicationException.hpp" #include "NstApplicationLanguage.hpp" #include #include #include #include #if NST_MSVC #pragma comment(lib,"comctl32") #endif namespace Nestopia { namespace Application { const wchar_t Instance::appClassName[] = L"Nestopia"; struct Instance::Global { Global(); struct Paths { explicit Paths(HINSTANCE); Path exePath; Path wrkPath; Path configPath;//bg configuration files path }; struct Hooks { explicit Hooks(HINSTANCE); ~Hooks(); void OnCreate(const CREATESTRUCT&,HWND); void OnDestroy(HWND); static LRESULT CALLBACK CBTProc(int,WPARAM,LPARAM); enum { CHILDREN_RESERVE = 8 }; typedef Collection::Vector Children; HHOOK const handle; HWND window; Children children; }; const Paths paths; const Resource::Version version; Language language; Hooks hooks; IconStyle iconStyle; }; Instance::Global Instance::global; Instance::Events::Callbacks Instance::Events::callbacks; Instance::Global::Paths::Paths(HINSTANCE const hInstance) { uint length; do { exePath.Reserve( exePath.Capacity() + 255 ); length = ::GetModuleFileName( hInstance, exePath.Ptr(), exePath.Capacity() ); } while (length == exePath.Capacity()); exePath.ShrinkTo( length ); exePath.MakePretty( false ); exePath.Defrag(); wrkPath = "."; wrkPath.MakeAbsolute( true ); wrkPath.Defrag(); configPath.Reserve(configPath.Capacity() + 255);//bg //bg bool isLua = false; //bg check -lua between command line options int nArgs; LPWSTR *szArglist = ::CommandLineToArgvW(::GetCommandLineW(), &nArgs); if( szArglist != NULL) { for(int i=0; i name; name.ShrinkTo( ::GetClassName( hWnd, name.Ptr(), MAX_LENGTH+1 ) ); if (name.Equal( menuClassName ) || name.Equal( STATUSCLASSNAME ) || name.Equal( L"IME" )) return; if (window) { children.PushBack( hWnd ); Events::Signal( EVENT_SYSTEM_BUSY ); } else if (name.Equal( appClassName )) { window = hWnd; } else { return; } } const Events::WindowCreateParam param = { hWnd, createStruct.cx > 0 ? createStruct.cx : 0, createStruct.cy > 0 ? createStruct.cy : 0 }; Events::Signal( EVENT_WINDOW_CREATE, ¶m ); } } void Instance::Global::Hooks::OnDestroy(HWND const hWnd) { NST_ASSERT( hWnd ); Children::Iterator const child = children.Find( hWnd ); if (child) { Events::Signal( EVENT_SYSTEM_BUSY ); } else if (window != hWnd) { return; } { const Events::WindowDestroyParam param = { hWnd }; Events::Signal( EVENT_WINDOW_DESTROY, ¶m ); } if (child) children.Erase( child ); else window = NULL; } LRESULT CALLBACK Instance::Global::Hooks::CBTProc(const int nCode,const WPARAM wParam,const LPARAM lParam) { NST_COMPILE_ASSERT( HCBT_CREATEWND >= 0 && HCBT_DESTROYWND >= 0 ); switch (nCode) { case HCBT_CREATEWND: NST_ASSERT( lParam ); if (const CREATESTRUCT* const createStruct = reinterpret_cast(lParam)->lpcs) { NST_VERIFY( wParam ); global.hooks.OnCreate( *createStruct, reinterpret_cast(wParam) ); } return 0; case HCBT_DESTROYWND: NST_VERIFY( wParam ); if (wParam) global.hooks.OnDestroy( reinterpret_cast(wParam) ); return 0; } return nCode < 0 ? ::CallNextHookEx( global.hooks.handle, nCode, wParam, lParam ) : 0; } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("", on) #endif Instance::Global::Global() : paths ( ::GetModuleHandle(NULL) ), version ( paths.exePath.Ptr(), Resource::Version::PRODUCT ), hooks ( ::GetModuleHandle(NULL) ), iconStyle ( ICONSTYLE_NES ) { } Instance::Language& Instance::GetLanguage() { return global.language; } const String::Heap& Instance::GetVersion() { return global.version; } const Path& Instance::GetExePath() { return global.paths.exePath; } const Path Instance::GetExePath(const GenericString file) { return Path( global.paths.exePath.Directory(), file ); } const Path Instance::GetConfigPath(const GenericString file)//bg { return Path( global.paths.configPath, file ); } const Path Instance::GetFullPath(const GenericString relPath) { Path curPath( "." ); curPath.MakeAbsolute( true ); const BOOL succeeded = ::SetCurrentDirectory( global.paths.wrkPath.Ptr() ); Path wrkPath( relPath ); wrkPath.MakeAbsolute(); if (succeeded) ::SetCurrentDirectory( curPath.Ptr() ); return wrkPath; } const Path Instance::GetLongPath(wcstring const shortPath) { Path longPath; longPath.Reserve( 255 ); uint length = ::GetLongPathName( shortPath, longPath.Ptr(), longPath.Capacity() + 1 ); if (length > longPath.Capacity() + 1) { longPath.Reserve( length - 1 ); length = ::GetLongPathName( shortPath, longPath.Ptr(), longPath.Capacity() + 1 ); } if (!length) return shortPath; const wchar_t slashed = GenericString(shortPath).Back(); longPath.ShrinkTo( length ); longPath.MakePretty( slashed == '\\' || slashed == '/' ); return longPath; } const Path Instance::GetTmpPath(GenericString file) { Path path; path.Reserve( 255 ); if (uint length = ::GetTempPath( path.Capacity() + 1, path.Ptr() )) { if (length > path.Capacity() + 1) { path.Reserve( length - 1 ); length = ::GetTempPath( path.Capacity() + 1, path.Ptr() ); } if (length) path = GetLongPath( path.Ptr() ); } if (path.Length()) path.MakePretty( true ); else path << ".\\"; if (file.Empty()) file = L"nestopia.tmp"; path << file; return path; } Instance::Events::Callbacks::~Callbacks() { NST_VERIFY( Empty() ); } void Instance::Events::Add(const Callback& callback) { NST_ASSERT( bool(callback) && !callbacks.Find( callback ) ); callbacks.PushBack( callback ); } void Instance::Events::Signal(const Event event,const void* param) { for (Callbacks::ConstIterator it(callbacks.Begin()), end(callbacks.End()); it != end; ++it) (*it)( event, param ); } void Instance::Events::Remove(const void* const instance) { for (Callbacks::Iterator it(callbacks.Begin()); it != callbacks.End(); ++it) { if (it->VoidPtr() == instance) { callbacks.Erase( it ); break; } } } Window::Generic Instance::GetMainWindow() { return global.hooks.window; } uint Instance::NumChildWindows() { return global.hooks.children.Size(); } Window::Generic Instance::GetChildWindow(uint i) { return global.hooks.children[i]; } Window::Generic Instance::GetActiveWindow() { HWND hWnd = ::GetActiveWindow(); return hWnd ? hWnd : global.hooks.window; } Instance::IconStyle Instance::GetIconStyle() { return global.iconStyle; } void Instance::SetIconStyle(IconStyle style) { global.iconStyle = style; } void Instance::ShowChildWindows(bool show) { const uint state = (show ? SW_SHOWNA : SW_HIDE); for (Global::Hooks::Children::ConstIterator it(global.hooks.children.Begin()), end(global.hooks.children.End()); it != end; ++it) ::ShowWindow( *it, state ); } Instance::Waiter::Waiter() : hCursor(::SetCursor(Resource::Cursor::GetWait())) {} Instance::Locker::Locker() : hWnd(GetMainWindow()), enabled(::IsWindowEnabled(hWnd)) { ::EnableWindow( hWnd, false ); ::LockWindowUpdate( hWnd ); } bool Instance::Locker::CheckInput(int vKey) const { if (::GetAsyncKeyState( vKey ) & 0x8000U) { while (::GetAsyncKeyState( vKey ) & 0x8000U) ::Sleep( 10 ); return true; } return false; } Instance::Locker::~Locker() { ::LockWindowUpdate( NULL ); ::EnableWindow( hWnd, enabled ); for (MSG msg;::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE|PM_QS_INPUT );); } Instance::Instance() { if (!static_cast(cfg)["preferences"]["application"]["allow-multiple-instances"].Yes()) { ::CreateMutex( NULL, true, L"Nestopia Mutex" ); if (::GetLastError() == ERROR_ALREADY_EXISTS) { if (Window::Generic window = Window::Generic::Find( appClassName )) { window.Activate(); if (const uint length = cfg.GetStartupFile().Length()) { COPYDATASTRUCT cds; cds.dwData = COPYDATA_OPENFILE_ID; cds.cbData = (length + 1) * sizeof(wchar_t); cds.lpData = const_cast(cfg.GetStartupFile().Ptr()); window.Send( WM_COPYDATA, 0, &cds ); } } throw Exception(); } } if (global.paths.exePath.Empty()) throw Exception( L"unicows.dll file is missing!" ); global.language.Load( cfg ); if (global.hooks.handle == NULL) throw Exception( IDS_ERR_FAILED, L"SetWindowsHookEx()" ); if (FAILED(::CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ))) throw Exception( IDS_ERR_FAILED, L"CoInitializeEx()" ); Object::Pod initCtrlEx; initCtrlEx.dwSize = sizeof(initCtrlEx); initCtrlEx.dwICC = ICC_WIN95_CLASSES; ::InitCommonControlsEx( &initCtrlEx ); } Instance::~Instance() { ::CoUninitialize(); } void Instance::Save() { global.language.Save( cfg ); } } }