diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index ce487eb7..d4bbe01c 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -168,6 +168,10 @@ static plDispatchBase* gDisp = nil; static plTimerCallbackManager* gTimerMgr = nil; static plAudioSystem* gAudio = nil; +#ifdef HS_BUILD_FOR_WIN32 +extern ITaskbarList3* gTaskbarList; +#endif + hsBool plClient::fDelayMS = false; plClient* plClient::fInstance=nil; @@ -768,6 +772,11 @@ hsBool plClient::MsgReceive(plMessage* msg) } break; + case plClientMsg::kFlashWindow: + { + FlashWindow(); + } + break; } return true; } @@ -864,8 +873,8 @@ hsBool plClient::MsgReceive(plMessage* msg) // plResPatcherMsg //============================================================================ if (plResPatcherMsg * resMsg = plResPatcherMsg::ConvertNoRef(msg)) { - plgDispatch::Dispatch()->UnRegisterForExactType(plResPatcherMsg::Index(), GetKey()); - IOnAsyncInitComplete(); + IHandlePatcherMsg(resMsg); + return true; } return hsKeyedObject::MsgReceive(msg); @@ -1291,6 +1300,24 @@ void plClient::IProgressMgrCallbackProc(plOperationProgress * progress) if(!fInstance) return; + // Increments the taskbar progress [Windows 7+] +#ifdef HS_BUILD_FOR_WIN32 + if (gTaskbarList) + { + HWND hwnd = fInstance->GetWindowHandle(); // lazy + if (progress->IsAborting()) + // We'll assume this is fatal + gTaskbarList->SetProgressState(hwnd, TBPF_ERROR); + else if (progress->IsLastUpdate()) + gTaskbarList->SetProgressState(hwnd, TBPF_NOPROGRESS); + else if (progress->GetMax() == 0.f) + gTaskbarList->SetProgressState(hwnd, TBPF_INDETERMINATE); + else + // This will set TBPF_NORMAL for us + gTaskbarList->SetProgressValue(hwnd, (ULONGLONG)progress->GetProgress(), (ULONGLONG)progress->GetMax()); + } +#endif + fInstance->fMessagePumpProc(); // HACK HACK HACK HACK! @@ -2415,6 +2442,17 @@ void plClient::WindowActivate(bool active) fWindowActive = active; } +void plClient::FlashWindow() +{ +#ifdef HS_BUILD_FOR_WIN32 + FLASHWINFO info; + info.cbSize = sizeof(info); + info.dwFlags = FLASHW_TIMERNOFG | FLASHW_ALL; + info.hwnd = fWindowHndl; + info.uCount = -1; + FlashWindowEx(&info); +#endif +} //============================================================================ void plClient::IOnAsyncInitComplete () { @@ -2531,6 +2569,18 @@ void plClient::IHandlePreloaderMsg (plPreloaderMsg * msg) { IPatchGlobalAgeFiles(); } +//============================================================================ +void plClient::IHandlePatcherMsg (plResPatcherMsg * msg) { + plgDispatch::Dispatch()->UnRegisterForExactType(plResPatcherMsg::Index(), GetKey()); + + if (!msg->Success()) { + plNetClientApp::GetInstance()->QueueDisableNet(true, msg->GetError()); + return; + } + + IOnAsyncInitComplete(); +} + //============================================================================ void plClient::IHandleNetCommAuthMsg (plNetCommAuthMsg * msg) { diff --git a/Sources/Plasma/Apps/plClient/plClient.h b/Sources/Plasma/Apps/plClient/plClient.h index 459ae84f..b529f5bf 100644 --- a/Sources/Plasma/Apps/plClient/plClient.h +++ b/Sources/Plasma/Apps/plClient/plClient.h @@ -82,7 +82,7 @@ class plBinkPlayer; class plPreloaderMsg; class plNetCommAuthMsg; class plAgeLoaded2Msg; - +class plResPatcherMsg; typedef void (*plMessagePumpProc)( void ); @@ -179,6 +179,7 @@ protected: void ICompleteInit (); void IOnAsyncInitComplete (); + void IHandlePatcherMsg (plResPatcherMsg * msg); void IHandlePreloaderMsg (plPreloaderMsg * msg); void IHandleNetCommAuthMsg (plNetCommAuthMsg * msg); bool IHandleAgeLoaded2Msg (plAgeLoaded2Msg * msg); @@ -289,6 +290,7 @@ public: virtual void WindowActivate(bool active); virtual hsBool WindowActive() const { return fWindowActive; } + void FlashWindow(); void SetMessagePumpProc( plMessagePumpProc proc ) { fMessagePumpProc = proc; } void ResetDisplayDevice(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync = false); void ResizeDisplayDevice(int Width, int Height, hsBool Windowed); diff --git a/Sources/Plasma/Apps/plClient/winmain.cpp b/Sources/Plasma/Apps/plClient/winmain.cpp index 29cd7730..b762daf2 100644 --- a/Sources/Plasma/Apps/plClient/winmain.cpp +++ b/Sources/Plasma/Apps/plClient/winmain.cpp @@ -92,6 +92,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com // Globals // hsBool gHasMouse = false; +ITaskbarList3* gTaskbarList = nil; // NT 6.1+ taskbar stuff extern hsBool gDataServerLocal; @@ -121,6 +122,7 @@ bool gPendingActivateFlag = false; static bool s_loginDlgRunning = false; static CEvent s_statusEvent(kEventManualReset); +static UINT s_WmTaskbarList = RegisterWindowMessage("TaskbarButtonCreated"); FILE *errFP = nil; HINSTANCE gHInst = NULL; // Instance of this app @@ -362,7 +364,7 @@ void DebugMsgF(const char* format, ...); // Handles all the windows messages we might receive LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ +{ static bool gDragging = false; static uint32_t keyState=0; @@ -402,12 +404,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) gClient->GetInputManager()->HandleWin32ControlEvent(message, wParam, lParam, hWnd); } } - break; + return TRUE; #if 0 case WM_KILLFOCUS: SetForegroundWindow(hWnd); - break; + return TRUE; #endif case WM_SYSKEYUP: @@ -419,7 +421,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } //DefWindowProc(hWnd, message, wParam, lParam); } - break; + return TRUE; case WM_SYSCOMMAND: switch (wParam) { @@ -435,9 +437,9 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (plNetClientMgr * mgr = plNetClientMgr::GetInstance()) mgr->QueueDisableNet(false, nil); DestroyWindow(gClient->GetWindowHandle()); - break; + return TRUE; } - break; + return TRUE; case WM_ACTIVATE: { @@ -478,7 +480,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) gPendingActivateFlag = active; } } - break; + return TRUE; // Let go of the mouse if the window is being moved. case WM_ENTERSIZEMOVE: @@ -486,7 +488,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) gDragging = true; if( gClient ) gClient->WindowActivate(false); - break; + return TRUE; // Redo the mouse capture if the window gets moved case WM_EXITSIZEMOVE: @@ -494,7 +496,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) gDragging = false; if( gClient ) gClient->WindowActivate(true); - break; + return TRUE; // Redo the mouse capture if the window gets moved (special case for Colin // and his cool program that bumps windows out from under the taskbar) @@ -506,7 +508,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } else DebugMsgF("Got WM_MOVE, but ignoring"); - break; + return TRUE; /// Resize the window // (we do WM_SIZING here instead of WM_SIZE because, for some reason, WM_SIZE is @@ -521,7 +523,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) ::GetClientRect(hWnd, &r); gClient->GetPipeline()->Resize(r.right - r.left, r.bottom - r.top); } - break; + return TRUE; case WM_SIZE: // Let go of the mouse if the window is being minimized @@ -538,7 +540,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (gClient) gClient->WindowActivate(true); } - break; + return TRUE; case WM_CLOSE: gClient->SetDone(TRUE); @@ -554,9 +556,22 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return TRUE; case WM_CREATE: // Create renderer - break; + return TRUE; } - return DefWindowProc(hWnd, message, wParam, lParam); + + // Messages we registered for manually (no const value) + if (message == s_WmTaskbarList) + { + // Grab the Windows 7 taskbar list stuff + if (gTaskbarList) + gTaskbarList->Release(); + HRESULT result = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_ALL, IID_ITaskbarList3, (void**)&gTaskbarList); + if (FAILED(result)) + gTaskbarList = nil; + return TRUE; + } + else + return DefWindowProc(hWnd, message, wParam, lParam); } void PumpMessageQueueProc( void ) diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp index 2acd2a7b..c8760d2a 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp @@ -721,6 +721,13 @@ void cyMisc::ExcludeRegionSetNow(pyKey& sender, pyKey& exKey, uint16_t state) plgDispatch::MsgSend( msg ); // whoosh... off it goes } +void cyMisc::FlashWindow() +{ + plKey clientKey = hsgResMgr::ResMgr()->FindKey(kClient_KEY); + plClientMsg* pMsg = new plClientMsg(plClientMsg::kFlashWindow); + pMsg->Send(clientKey); // whoosh... off it goes +} + #include "hsTimer.h" ///////////////////////////////////////////////////////////////////////////// // diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMisc.h b/Sources/Plasma/FeatureLib/pfPython/cyMisc.h index 2d1c25f6..4cc5385d 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMisc.h +++ b/Sources/Plasma/FeatureLib/pfPython/cyMisc.h @@ -244,6 +244,15 @@ public: // static hsBool WasLocallyNotified(pyKey &selfkey); + ///////////////////////////////////////////////////////////////////////////// + // + // Function : FlashWindow + // PARAMETERS : + // + // PURPOSE : Flashes the client window if it is not focused + // + static void FlashWindow(); + ///////////////////////////////////////////////////////////////////////////// // // Function : GetClientName diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp index 57740e98..9f7ff0c8 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp @@ -48,6 +48,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtFlashWindow, cyMisc::FlashWindow, "Flashes the client window if it is not focused"); + PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetAgeName, "DEPRECIATED - use ptDniInfoSource instead") { return PyString_FromString(cyMisc::GetAgeName()); @@ -514,6 +516,8 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtDumpLogs, args, "Params: folder\nDumps all cur void cyMisc::AddPlasmaMethods(std::vector &methods) { + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtFlashWindow); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAgeName); PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAgeInfo); PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAgeTime); diff --git a/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp b/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp index f242afe4..9ae2fea9 100644 --- a/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp +++ b/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp @@ -300,6 +300,7 @@ void pfSecurePreloader::Start() void pfSecurePreloader::Terminate() { FATAL("pfSecurePreloader failure"); + fProgress->SetAborting(); plPreloaderMsg* msg = new plPreloaderMsg; msg->fSuccess = false; diff --git a/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.h b/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.h index b4932694..196a09cc 100644 --- a/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.h +++ b/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.h @@ -94,6 +94,8 @@ public: kEnableRenderScene, kResetGraphicsDevice, kSetGraphicsDefaults, + + kFlashWindow, }; // graphics settings fields diff --git a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp index 718ca27f..4dd8c424 100644 --- a/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp +++ b/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp @@ -278,7 +278,10 @@ void plResPatcher::Finish(bool success) if (success) PatcherLog(kHeader, "--- Patch Completed Successfully ---"); else + { PatcherLog(kHeader, "--- Patch Killed by Error ---"); + fProgress->SetAborting(); + } plResPatcherMsg* pMsg = new plResPatcherMsg(success, sLastError); pMsg->Send(); // whoosh... off it goes diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp index 03112b49..2a243ac3 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp @@ -165,11 +165,9 @@ void plNetLinkingMgr::NCAgeJoinerCallback ( // Tell the user we failed to link. // In the future, we might want to try graceful recovery (link back to Relto?) if (!params->success) { - plNetClientMgr::StaticErrorMsg(params->msg); - hsMessageBox(params->msg, "Linking Error", hsMessageBoxNormal, hsMessageBoxIconError); + plNetClientApp::GetInstance()->ErrorMsg(params->msg); #ifdef PLASMA_EXTERNAL_RELEASE - plClientMsg* clientMsg = new plClientMsg(plClientMsg::kQuit); - clientMsg->Send(hsgResMgr::ResMgr()->FindKey(kClient_KEY)); + plNetClientApp::GetInstance()->QueueDisableNet(true, params->msg); #endif return; } diff --git a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp index d874a55c..d98d813b 100644 --- a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp @@ -389,7 +389,6 @@ void plOperationProgress::SetAborting() hsSetBits(fFlags, kAborting); plProgressMgr::GetInstance()->IUpdateCallbackProc(this); fMax = fValue = 0.f; - hsClearBits(fFlags, kAborting); } void plOperationProgress::SetRetry() diff --git a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h index 061d4a77..b3dc11f1 100644 --- a/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h +++ b/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h @@ -153,6 +153,7 @@ class plOperationProgress // progress bars above this one know to adjust their totals to not include any amount // that wasn't completed, and will set this progress bar to zero void SetAborting(); + bool IsAborting() { return hsCheckBits(fFlags, kAborting); } // If you're reusing an existing progress bar to retry a failed operation, call this. // It will set the retry flag, and reset the progress bar so the next update will // count as the first. If you set retry in RegisterOperation, don't use this too.