Evaluate an #HotIf expression or callback function. This is called by MainWindowProc when it receives an AHK_HOT_IF_EVAL message.
| 10657 | // Evaluate an #HotIf expression or callback function. |
| 10658 | // This is called by MainWindowProc when it receives an AHK_HOT_IF_EVAL message. |
| 10659 | ResultType HotkeyCriterion::Eval(LPTSTR aHotkeyName) |
| 10660 | { |
| 10661 | // Initialize a new quasi-thread to evaluate the expression. This may not be necessary for simple |
| 10662 | // expressions, but expressions which call user-defined functions may otherwise interfere with |
| 10663 | // whatever quasi-thread is running when the hook thread requests that this expression be evaluated. |
| 10664 | |
| 10665 | // Based on parts of MsgMonitor(). See there for comments. |
| 10666 | |
| 10667 | if (g_nThreads >= g_MaxThreadsTotal) |
| 10668 | return CONDITION_FALSE; |
| 10669 | |
| 10670 | bool prev_defer_messages = g_DeferMessagesForUnderlyingPump; |
| 10671 | // Force the use of PeekMessage() within MsgSleep() since GetMessage() is known to stall while |
| 10672 | // the system is waiting for our keyboard hook to return (last confirmed on Windows 10.0.18356). |
| 10673 | // This might relate to WM_TIMER being lower priority than the input hardware processing |
| 10674 | // performed by GetMessage(). MsgSleep() relies on WM_TIMER acting as a timeout for GetMessage(). |
| 10675 | g_DeferMessagesForUnderlyingPump = true; |
| 10676 | |
| 10677 | // See MsgSleep() for comments about the following section. |
| 10678 | // Critical seems to improve reliability, either because the thread completes faster (i.e. before the timeout) or because we check for messages less often. |
| 10679 | InitNewThread(0, false, true, true); |
| 10680 | ResultType result; |
| 10681 | |
| 10682 | // Let HotIf default to the criterion currently being evaluated, in case Hotkey() is called. |
| 10683 | g->HotCriterion = this; |
| 10684 | |
| 10685 | // Update A_ThisHotkey, useful if #HotIf calls a function to do its dirty work. |
| 10686 | LPTSTR prior_hotkey_name[] = { g_script.mThisHotkeyName, g_script.mPriorHotkeyName }; |
| 10687 | DWORD prior_hotkey_time[] = { g_script.mThisHotkeyStartTime, g_script.mPriorHotkeyStartTime }; |
| 10688 | g_script.mPriorHotkeyName = g_script.mThisHotkeyName; // For consistency |
| 10689 | g_script.mPriorHotkeyStartTime = g_script.mThisHotkeyStartTime; // |
| 10690 | g_script.mThisHotkeyName = aHotkeyName; |
| 10691 | g_script.mThisHotkeyStartTime = // Updated for consistency. |
| 10692 | g_script.mLastPeekTime = GetTickCount(); |
| 10693 | |
| 10694 | // CALL THE CALLBACK |
| 10695 | ExprTokenType param = aHotkeyName; |
| 10696 | __int64 retval; |
| 10697 | result = IObjectPtr(Callback)->ExecuteInNewThread(_T("#HotIf"), ¶m, 1, &retval); |
| 10698 | if (result != FAIL) |
| 10699 | result = retval ? CONDITION_TRUE : CONDITION_FALSE; |
| 10700 | |
| 10701 | |
| 10702 | // The following allows the expression to set the Last Found Window for the |
| 10703 | // hotkey function. |
| 10704 | // There may be some rare cases where the wrong hotkey gets this HWND (perhaps |
| 10705 | // if there are multiple hotkey messages in the queue), but there doesn't seem |
| 10706 | // to be any easy way around that. |
| 10707 | g_HotExprLFW = g->hWndLastUsed; // Even if above failed, for simplicity. |
| 10708 | |
| 10709 | // A_ThisHotkey must be restored else A_PriorHotkey will get an incorrect value later. |
| 10710 | g_script.mThisHotkeyName = prior_hotkey_name[0]; |
| 10711 | g_script.mThisHotkeyStartTime = prior_hotkey_time[0]; |
| 10712 | g_script.mPriorHotkeyName = prior_hotkey_name[1]; |
| 10713 | g_script.mPriorHotkeyStartTime = prior_hotkey_time[1]; |
| 10714 | |
| 10715 | ResumeUnderlyingThread(); |
| 10716 |
no test coverage detected