Repository: microsoft/terminal
File: src/host/CursorBlinker.cpp (C++)

Code before:
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "precomp.h"
#include "../host/scrolling.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#pragma hdrstop

using namespace Microsoft::Console;
using namespace Microsoft::Console::Interactivity;

static void CALLBACK CursorTimerRoutineWrapper(_Inout_ PTP_CALLBACK_INSTANCE /*Instance*/, _Inout_opt_ PVOID /*Context*/, _Inout_ PTP_TIMER /*Timer*/)
{
    CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();

    // There's a slight race condition here.
    // CreateThreadpoolTimer callbacks may be scheduled even after they were canceled.
    // But I'm not too concerned that this will lead to issues at the time of writing,
    // as CursorBlinker is allocated as a static variable through the Globals class.
    // It'd be nice to fix this, but realistically it'll likely not lead to issues.
    gci.LockConsole();
    // Cursor& cursor = gci.GetActiveOutputBuffer().GetTextBuffer().GetCursor();
    gci.GetCursorBlinker().TimerRoutine(gci.GetActiveOutputBuffer());
    gci.UnlockConsole();
}

CursorBlinker::CursorBlinker() :
    _timer(THROW_LAST_ERROR_IF_NULL(CreateThreadpoolTimer(&CursorTimerRoutineWrapper, nullptr, nullptr))),
    _uCaretBlinkTime(INFINITE) // default to no blink
{
}

CursorBlinker::~CursorBlinker()
{
    KillCaretTimer();
}

void CursorBlinker::UpdateSystemMetrics() noexcept
{
    // This can be -1 in a TS session
    _uCaretBlinkTime = ServiceLocator::LocateSystemConfigurationProvider()->GetCaretBlinkTime();

    // If animations are disabled, or the blink rate is infinite, blinking is not allowed.
    BOOL animationsEnabled = TRUE;
    SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0);
    auto& blinkingState = ServiceLocator::LocateGlobals().getConsoleInformation().GetBlinkingState();

Review comment (suggestion):
There's no TryLockConsole() anymore, so I replaced it with a worse solution. 😅
Using the new thread pool API we can simply tell the OS to not block us on exit. It's simpler so that's a win at least.

Code after:
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "precomp.h"
#include "../host/scrolling.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#pragma hdrstop

using namespace Microsoft::Console;
using namespace Microsoft::Console::Interactivity;

static void CALLBACK CursorTimerRoutineWrapper(_Inout_ PTP_CALLBACK_INSTANCE /*Instance*/, _Inout_opt_ PVOID /*Context*/, _Inout_ PTP_TIMER /*Timer*/)
{
    CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();

    // There's a slight race condition here.
    // CreateThreadpoolTimer callbacks may be scheduled even after they were canceled.
    // But I'm not too concerned that this will lead to issues at the time of writing,
    // as CursorBlinker is allocated as a static variable through the Globals class.
    // It'd be nice to fix this, but realistically it'll likely not lead to issues.
    gci.LockConsole();
    gci.GetCursorBlinker().TimerRoutine(gci.GetActiveOutputBuffer());
    gci.UnlockConsole();
}

CursorBlinker::CursorBlinker() :
    _timer(THROW_LAST_ERROR_IF_NULL(CreateThreadpoolTimer(&CursorTimerRoutineWrapper, nullptr, nullptr))),
    _uCaretBlinkTime(INFINITE) // default to no blink
{
}

CursorBlinker::~CursorBlinker()
{
    KillCaretTimer();
}

void CursorBlinker::UpdateSystemMetrics() noexcept
{
    // This can be -1 in a TS session
    _uCaretBlinkTime = ServiceLocator::LocateSystemConfigurationProvider()->GetCaretBlinkTime();

    // If animations are disabled, or the blink rate is infinite, blinking is not allowed.
    BOOL animationsEnabled = TRUE;
    SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0);
    auto& blinkingState = ServiceLocator::LocateGlobals().getConsoleInformation().GetBlinkingState();
    blinkingState.SetBlinkingAllowed(animationsEnabled && _uCaretBlinkTime != INFINITE);