From b68d9a91b8c904ed13b448fb9a13d78c5aa7deee Mon Sep 17 00:00:00 2001 From: ugetab Date: Wed, 19 May 2010 21:51:25 +0000 Subject: [PATCH] Added Single Instantiation mode, which makes starting a second copy of FCEUX with a path to a game make it load the file into the first, then exit. To enable, set SingleInstanceOnly 1 in the config file. If someone else wants to edit things to make this easy on the user, go for it. If started with nothing in the command line, it will simply start a second copy. --- changelog.txt | 1 + src/drivers/win/config.cpp | 2 + src/drivers/win/main.cpp | 77 ++++++++++++++++++++++++++++++++++++-- src/drivers/win/main.h | 7 ++++ src/drivers/win/window.cpp | 20 ++++++++-- 5 files changed, 101 insertions(+), 6 deletions(-) diff --git a/changelog.txt b/changelog.txt index 94ed4174..ce342f93 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,4 @@ +19-may-2010 - ugetab - Win32 - Added single-instance mode, which makes starting a second copy of FCEUX load the file into the first, then exit. 18-may-2010 - adelikat - Movie + loadstate errors are handled more gracefully now, more informative error messages and the movie doesn't have to stop 18-may-2010 - adelikat - Implemented a "full savestate-movie load" mode similar to the implementation in VBA-rr & SNES9x-rr. In this mode loading a savestate in read+write doesn't truncate the movie to its frame count immediately. Instead it waits until input is recording into the movie (next frame). For win32 this feature is togglable in movie options and the context menu. For SDL this is off by default and a toggle will need to be added. 17-may-2010 - adelikat - Made gamepad 2 off by default. diff --git a/src/drivers/win/config.cpp b/src/drivers/win/config.cpp index 84852566..be5c74a6 100644 --- a/src/drivers/win/config.cpp +++ b/src/drivers/win/config.cpp @@ -63,6 +63,7 @@ extern int CurrentState; extern bool pauseWhileActive; //adelikat: Cheats dialog extern bool AVIdisableMovieMessages; extern bool replaceP2StartWithMicrophone; +extern bool SingleInstanceOnly; extern bool oldInputDisplay; extern bool fullSaveStateLoads; @@ -313,6 +314,7 @@ static CFGSTRUCT fceuconfig[] = { AC(pauseWhileActive), AC(AVIdisableMovieMessages), AC(replaceP2StartWithMicrophone), + AC(SingleInstanceOnly), ENDCFGSTRUCT }; diff --git a/src/drivers/win/main.cpp b/src/drivers/win/main.cpp index 11472449..7ac39ac6 100644 --- a/src/drivers/win/main.cpp +++ b/src/drivers/win/main.cpp @@ -185,11 +185,15 @@ int erendlinep = 239; //mbg 6/30/06 - indicates that the main loop should close the game as soon as it can bool closeGame = false; - // Counts the number of frames that have not been displayed. // Used for the bot, to skip frames (makes things faster). int BotFramesSkipped = 0; +// Instantiated FCEUX stuff: +bool SingleInstanceOnly=false; // Enable/disable option +bool DoInstantiatedExit=false; +HWND DoInstantiatedExitWindow; + // Internal functions void SetDirs() { @@ -580,6 +584,48 @@ void initDirectories() } } */ + +static BOOL CALLBACK EnumCallbackFCEUXInstantiated(HWND hWnd, LPARAM lParam) +{ + //LPSTR lpClassName = '\0'; + std::string TempString; + char buf[512]; + bool PassedTest=true; + + GetClassName(hWnd, buf, 511); + //Console.WriteLine(lpClassName.ToString()); + + TempString = buf; + + if (TempString != "FCEUXWindowClass") + return true; + + //memset(buf, 0, 512 * sizeof(char)); + GetWindowText(hWnd, buf, 512 * sizeof(char)); + + if (hWnd != hAppWnd) { + PassedTest = (PassedTest & (buf[0] == 'F')); + PassedTest = (PassedTest & (buf[1] == 'C')); + PassedTest = (PassedTest & (buf[2] == 'E')); + PassedTest = (PassedTest & (buf[3] == 'U')); + PassedTest = (PassedTest & (buf[4] == 'X')); + PassedTest = (PassedTest & (buf[5] == ' ')); + PassedTest = (PassedTest & ((buf[6] >= '2') & (buf[6] <= '9'))); + PassedTest = (PassedTest & (buf[7] == '.')); + PassedTest = (PassedTest & ((buf[8] >= '1') & (buf[8] <= '9'))); + PassedTest = (PassedTest & (buf[9] == '.')); + PassedTest = (PassedTest & ((buf[10] >= '4') & (buf[10] <= '9'))); + + if (PassedTest) { + DoInstantiatedExit=true; + DoInstantiatedExitWindow = hWnd; + } + } + + //printf("[%03i] Found '%s'\n", ++WinCount, buf); + return true; +} + #include "x6502.h" int main(int argc,char *argv[]) { @@ -624,8 +670,6 @@ int main(int argc,char *argv[]) sprintf(TempArray,"%s\\%s",BaseDirectory.c_str(),cfgFile.c_str()); LoadConfig(TempArray); - - //Bleh, need to find a better place for this. { pal_emulation = !!pal_emulation; @@ -675,6 +719,33 @@ int main(int argc,char *argv[]) CreateMainWindow(); + // Do single instance coding, since we now know if the user wants it, + // and we have a source window to send from + // http://wiki.github.com/ffi/ffi/windows-examples + if (SingleInstanceOnly) { + // Checks window names / hWnds, decides if there's going to be a conflict. + EnumDesktopWindows(NULL, EnumCallbackFCEUXInstantiated, (LPARAM)0); + + if (DoInstantiatedExit) { + + if(t) + { + COPYDATASTRUCT cData; + DATA tData; + + sprintf(tData.strFilePath,"%s",t); + + cData.dwData = 1; + cData.cbData = sizeof ( tData ); + cData.lpData = &tData; + + SendMessage(DoInstantiatedExitWindow,WM_COPYDATA,(WPARAM)(HWND)hAppWnd, (LPARAM)(LPVOID) &cData); + do_exit(); + return 0; + } + } + } + if(!InitDInput()) { do_exit(); diff --git a/src/drivers/win/main.h b/src/drivers/win/main.h index fcd616e9..e4d4e21a 100644 --- a/src/drivers/win/main.h +++ b/src/drivers/win/main.h @@ -23,6 +23,13 @@ #define GOO_CONFIRMEXIT 2 /* Confirmation before exiting. */ #define GOO_POWERRESET 4 /* Confirm on power/reset. */ +//For single instance mode, transfers with WM_COPYDATA +//http://www.go4expert.com/forums/showthread.php?t=19730 +typedef struct WMCopyStruct +{ + char strFilePath[2048]; +} DATA; + extern int maxconbskip; extern int ffbskip; extern void LoadNewGamey(HWND hParent, const char *initialdir); diff --git a/src/drivers/win/window.cpp b/src/drivers/win/window.cpp index c19206d8..41549741 100644 --- a/src/drivers/win/window.cpp +++ b/src/drivers/win/window.cpp @@ -1221,11 +1221,25 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) RECT file_rect; RECT help_rect; int x = 0; - + char TempArray[2048]; + PCOPYDATASTRUCT pcData; switch(msg) { + case WM_COPYDATA: + + pcData = (PCOPYDATASTRUCT) lParam; + + switch ( pcData->dwData ) + { + case 1: //cData.dwData = 1; (can use for other types as well) + if (!ALoad((LPSTR) ( (DATA *) (pcData->lpData) )-> strFilePath)) + MessageBox(hWnd,"File from second instance failed to open", "Failed to open file", MB_OK); + break; + } + break; + case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: @@ -2360,7 +2374,7 @@ int CreateMainWindow() winclass.hIconSm = LoadIcon(fceu_hInstance, "ICON_1"); winclass.hCursor = LoadCursor(NULL, IDC_ARROW); winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //mbg merge 7/17/06 added cast - winclass.lpszClassName = "FCEULTRA"; + winclass.lpszClassName = "FCEUXWindowClass"; if(!RegisterClassEx(&winclass)) { @@ -2386,7 +2400,7 @@ int CreateMainWindow() if (MainWindow_wndy==-32000) MainWindow_wndy=0; hAppWnd = CreateWindowEx( 0, - "FCEULTRA", + "FCEUXWindowClass", FCEU_NAME_AND_VERSION, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS, /* Style */ MainWindow_wndx,