home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 242 / Issue 242 - April 2008 - DPCS0408DVD.ISO / Open Source / AutoHotKey / Source / AutoHotkey104705_source.exe / source / hotkey.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-08-06  |  129.1 KB  |  2,495 lines

  1. /*
  2. AutoHotkey
  3.  
  4. Copyright 2003-2007 Chris Mallett (support@autohotkey.com)
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15. */
  16.  
  17. #include "stdafx.h" // pre-compiled headers
  18. #include "hotkey.h"
  19. #include "globaldata.h"  // For g_os and other global vars.
  20. #include "window.h" // For MsgBox()
  21. //#include "application.h" // For ExitApp()
  22.  
  23. // Initialize static members:
  24. HookType Hotkey::sWhichHookNeeded = 0;
  25. HookType Hotkey::sWhichHookAlways = 0;
  26. DWORD Hotkey::sTimePrev = {0};
  27. DWORD Hotkey::sTimeNow = {0};
  28. Hotkey *Hotkey::shk[MAX_HOTKEYS] = {NULL};
  29. HotkeyIDType Hotkey::sNextID = 0;
  30. const HotkeyIDType &Hotkey::sHotkeyCount = Hotkey::sNextID;
  31. bool Hotkey::sJoystickHasHotkeys[MAX_JOYSTICKS] = {false};
  32. DWORD Hotkey::sJoyHotkeyCount = 0;
  33.  
  34.  
  35.  
  36. HWND HotCriterionAllowsFiring(HotCriterionType aHotCriterion, char *aWinTitle, char *aWinText)
  37. // This is a global function because it's used by both hotkeys and hotstrings.
  38. // In addition to being called by the hook thread, this can now be called by the main thread.
  39. // That happens when a WM_HOTKEY message arrives that is non-hook (such as for Win9x).
  40. // Returns a non-NULL HWND if firing is allowed.  However, if it's a global criterion or
  41. // a "not-criterion" such as #IfWinNotActive, (HWND)1 is returned rather than a genuine HWND.
  42. {
  43.     HWND found_hwnd;
  44.     switch(aHotCriterion)
  45.     {
  46.     case HOT_IF_ACTIVE:
  47.     case HOT_IF_NOT_ACTIVE:
  48.         found_hwnd = WinActive(g_default, aWinTitle, aWinText, "", "", false); // Thread-safe.
  49.         break;
  50.     case HOT_IF_EXIST:
  51.     case HOT_IF_NOT_EXIST:
  52.         found_hwnd = WinExist(g_default, aWinTitle, aWinText, "", "", false, false); // Thread-safe.
  53.         break;
  54.     default: // HOT_NO_CRITERION (listed last because most callers avoids calling here by checking this value first).
  55.         return (HWND)1; // Always allow hotkey to fire.
  56.     }
  57.     return (aHotCriterion == HOT_IF_ACTIVE || aHotCriterion == HOT_IF_EXIST) ? found_hwnd : (HWND)!found_hwnd;
  58. }
  59.  
  60.  
  61.  
  62. ResultType SetGlobalHotTitleText(char *aWinTitle, char *aWinText)
  63. // Allocate memory for aWinTitle/Text (if necessary) and update g_HotWinTitle/Text to point to it.
  64. // Returns FAIL if memory couldn't be allocated, or OK otherwise.
  65. // This is a global function because it's used by both hotkeys and hotstrings.
  66. {
  67.     // Caller relies on this check:
  68.     if (!(*aWinTitle || *aWinText)) // In case of something weird but legit like: #IfWinActive, ,
  69.     {
  70.         g_HotCriterion = HOT_NO_CRITERION; // Don't allow blank title+text to avoid having it interpreted as the last-found-window.
  71.         g_HotWinTitle = ""; // Helps maintainability and some things might rely on it.
  72.         g_HotWinText = "";  //
  73.         return OK;
  74.     }
  75.  
  76.     // Storing combinations of WinTitle+WinText doesn't have as good a best-case memory savings as
  77.     // have a separate linked list for Title vs. Text.  But it does save code size, and in the vast
  78.     // majority of scripts, the memory savings would be insignficant.
  79.     HotkeyCriterion *cp;
  80.     for (cp = g_FirstHotCriterion; cp; cp = cp->mNextCriterion)
  81.         if (!strcmp(cp->mHotWinTitle, aWinTitle) && !strcmp(cp->mHotWinText, aWinText)) // Case insensitive.
  82.         {
  83.             // Match found, so point to the existing memory.
  84.             g_HotWinTitle = cp->mHotWinTitle;
  85.             g_HotWinText = cp->mHotWinText;
  86.             return OK;
  87.         }
  88.  
  89.     // Since above didn't return, there is no existing entry in the linked list that matches this exact
  90.     // combination of Title and Text.  So create a new item and allocate memory for it.
  91.     if (   !(cp = (HotkeyCriterion *)SimpleHeap::Malloc(sizeof(HotkeyCriterion)))   )
  92.         return FAIL;
  93.     cp->mNextCriterion = NULL;
  94.     if (*aWinTitle)
  95.     {
  96.         if (   !(cp->mHotWinTitle = SimpleHeap::Malloc(aWinTitle))   )
  97.             return FAIL;
  98.     }
  99.     else
  100.         cp->mHotWinTitle = "";
  101.     if (*aWinText)
  102.     {
  103.         if (   !(cp->mHotWinText = SimpleHeap::Malloc(aWinText))   )
  104.             return FAIL;
  105.     }
  106.     else
  107.         cp->mHotWinText = "";
  108.  
  109.     g_HotWinTitle = cp->mHotWinTitle;
  110.     g_HotWinText = cp->mHotWinText;
  111.  
  112.     // Update the linked list:
  113.     if (!g_FirstHotCriterion)
  114.         g_FirstHotCriterion = g_LastHotCriterion = cp;
  115.     else
  116.     {
  117.         g_LastHotCriterion->mNextCriterion = cp;
  118.         // This must be done after the above:
  119.         g_LastHotCriterion = cp;
  120.     }
  121.  
  122.     return OK;
  123. }
  124.  
  125.  
  126.  
  127. void Hotkey::ManifestAllHotkeysHotstringsHooks()
  128. // This function examines all hotkeys and hotstrings to determine:
  129. // - Which hotkeys to register/unregister, or activate/deactivate in the hook.
  130. // - Which hotkeys to be changed from HK_NORMAL to HK_KEYBD_HOOK (or vice versa).
  131. // - In pursuit of the above, also assess the interdependencies between hotkeys: the presence or
  132. //   absence of a given hotkey can sometimes impact whether other hotkeys need to be converted from
  133. //   HK_NORMAL to HK_KEYBD_HOOK.  For example, a newly added/enabled global hotkey variant can
  134. //   cause a HK_KEYBD_HOOK hotkey to become HK_NORMAL, and the converse is also true.
  135. // - Based on the above, decide whether the keyboard and/or mouse hooks need to be (de)activated.
  136. {
  137.     // v1.0.37.05: A prefix key such as "a" in "a & b" should cause any use of "a" as a suffix
  138.     // (such as ^!a) also to be a hook hotkey.  Otherwise, the ^!a hotkey won't fire because the
  139.     // hook prevents the OS's hotkey monitor from seeing that the hotkey was pressed.  NOTE:
  140.     // This is done only for vitual keys because prefix keys that are done by scan code (mModifierSC)
  141.     // should already be hook hotkeys when used as suffix keys (there may be a few unusual exceptions,
  142.     // but they seem too rare to justify the extra code size).
  143.     // Update for v1.0.40: This first pass through the hotkeys now also checks things for hotkeys
  144.     // that can affect other hotkeys. If this weren't done in the first pass, it might be possible
  145.     // for a hotkey to make some other hotkey into a hook hotkey, but then the hook might not be
  146.     // installed if that hotkey had already been processed earlier in the second pass.  Similarly,
  147.     // a hotkey processed earlier in the second pass might have been registered when in fact it
  148.     // should have become a hook hotkey due to something learned only later in the second pass.
  149.     // Doing these types of things in the first pass resolves such situations.
  150.     bool vk_is_prefix[VK_ARRAY_COUNT] = {false};
  151.     bool hk_is_inactive[MAX_HOTKEYS]; // No init needed.
  152.     bool is_win9x = g_os.IsWin9x(); // Might help performance a little by avoiding calls in loops.
  153.     HotkeyVariant *vp;
  154.     int i, j;
  155.  
  156.     // FIRST PASS THROUGH THE HOTKEYS:
  157.     for (i = 0; i < sHotkeyCount; ++i)
  158.     {
  159.         Hotkey &hot = *shk[i]; // For performance and convenience.
  160.         if (   hk_is_inactive[i] = ((g_IsSuspended && !hot.IsExemptFromSuspend())
  161.             || hot.IsCompletelyDisabled())   ) // Listed last for short-circuit performance.
  162.         {
  163.             // In the cases above, nothing later below can change the fact that this hotkey should
  164.             // now be in an unregistered state.
  165.             if (hot.mIsRegistered)
  166.             {
  167.                 hot.Unregister();
  168.                 // In case the hotkey's thread is already running, it seems best to cancel any repeat-run
  169.                 // that has already been scheduled.  Older comment: CT_SUSPEND, at least, relies on us to do this.
  170.                 for (vp = hot.mFirstVariant; vp; vp = vp->mNextVariant)
  171.                     vp->mRunAgainAfterFinished = false; // Applies to all hotkey types, not just registered ones.
  172.             }
  173.             continue;
  174.         }
  175.         // Otherwise, this hotkey will be in effect, so check its attributes.
  176.  
  177.         if (hot.mKeybdHookMandatory)
  178.         {
  179.             // v1.0.44: The following Relies upon by some things like the Hotkey constructor and the tilde prefix
  180.             // (the latter can set mKeybdHookMandatory for a hotkey sometime after the first variant is added [such
  181.             // as for a subsequent variant]).  This practice also improves maintainability.
  182.             if (HK_TYPE_CAN_BECOME_KEYBD_HOOK(hot.mType)) // To ensure it hasn't since become a joystick/mouse/mouse-and-keyboard hotkey.
  183.                 hot.mType = HK_KEYBD_HOOK;
  184.         }
  185.         else // Hook isn't mandatory, so set any non-mouse/joystick/both hotkey to normal for possibly overriding later below.
  186.         {
  187.             // v1.0.42: The following is done to support situations in which a hotkey can be a hook hotkey sometimes,
  188.             // but after a (de)suspend or after a change to other hotkeys via the Hotkey command, might no longer
  189.             // require the hook.  Examples include:
  190.             // 1) A hotkey can't be registered because some other app is using it, but later
  191.             //    that condition changes.
  192.             // 2) Suspend or the Hotkey command changes wildcard hotkeys so that non-wildcard
  193.             //    hotkeys that have the same suffix are no longer eclipsed, and thus don't need
  194.             //    the hook.  The same type of thing can happen if a key-up hotkey is disabled,
  195.             //    which would allow it's key-down hotkey to become non-hook.  Similarly, if a
  196.             //    all of a prefix key's hotkeys become disabled, and that prefix is also a suffix,
  197.             //    those suffixes no longer need to be hook hotkeys.
  198.             // 3) There may be other ways, especially in the future involving #IfWin keys whose
  199.             //    criteria change.
  200.             if (hot.mType == HK_KEYBD_HOOK)
  201.                 hot.mType = HK_NORMAL; // To possibly be overridden back to HK_KEYBD_HOOK later below; but if not, it will be registered later below.
  202.         }
  203.  
  204.         if (hot.mModifierVK)
  205.             vk_is_prefix[hot.mModifierVK] = true;
  206.  
  207.         if (hot.mKeyUp && hot.mVK) // No need to do the below for mSC hotkeys since their down hotkeys would already be handled by the hook.
  208.         {
  209.             // The following is done even for Win9x because "no functionality" might be preferable to
  210.             // partial functionality.  This might be relied upon by the remapping feature to prevent a
  211.             // remapping such as a::b from partially become active and having unintended side-effects
  212.             // (could use more review).
  213.             // For each key-up hotkey, search for any its counterpart that's a down-hotkey (if any).
  214.             // Such a hotkey should also be handled by the hook because if registered, such as
  215.             // "#5" (reg) and "#5 up" (hook), the hook would suppress the down event because it
  216.             // is unaware that down-hotkey exists (it's suppressed to prevent the key from being
  217.             // stuck in a logically down state).
  218.             for (j = 0; j < sHotkeyCount; ++j)
  219.             {
  220.                 // No need to check the following because they are already hook hotkeys
  221.                 // (except on Win9x, for which the repercussions of changing or fixing
  222.                 // it to take these into effect have not been evaluated yet):
  223.                 // mModifierVK/SC
  224.                 // mAllowExtraModifiers
  225.                 // mNoSuppress
  226.                 // In addition, there is no need to check shk[j]->mKeyUp because that can't be
  227.                 // true if it's mType is HK_NORMAL:
  228.                 // Also, g_IsSuspended and IsCompletelyDisabled() aren't checked
  229.                 // because it's harmless to operate on disabled hotkeys in this way.
  230.                 if (shk[j]->mVK == hot.mVK && HK_TYPE_CAN_BECOME_KEYBD_HOOK(shk[j]->mType) // Ordered for short-circuit performance.
  231.                     && shk[j]->mModifiersConsolidatedLR == hot.mModifiersConsolidatedLR)
  232.                     shk[j]->mType = HK_KEYBD_HOOK; // Done even for Win9x (see comments above).
  233.                     // And if it's currently registered, it will be unregistered later below.
  234.             }
  235.         }
  236.  
  237.         // v1.0.42: The following is now not done for Win9x because it seems inappropriate for
  238.         // a wilcard hotkey (which will be attempt-registered due to Win9x's lack of hook) to also
  239.         // disable hotkeys that share the same suffix as the wildcard.
  240.         // v1.0.40: If this is a wildcard hotkey, any hotkeys it eclipses (i.e. includes as subsets)
  241.         // should be made into hook hotkeys also, because otherwise they would be overridden by hook.
  242.         // The following criteria are checked:
  243.         // 1) Exclude those that have a ModifierSC/VK because in those cases, mAllowExtraModifiers is
  244.         //    ignored.
  245.         // 2) Exclude those that lack an mVK because those with mSC can't eclipse registered hotkeys
  246.         //   (since any would-be eclipsed mSC hotkey is already a hook hotkey due to is SC nature).
  247.         // 3) It must not have any mModifiersLR because such hotkeys can't completely eclipse
  248.         //    registered hotkeys since they always have neutral vs. left/right-specific modifiers.
  249.         //    For example, if *<^a is a hotkey, ^a can still be a registered hotkey because it could
  250.         //    still be activated by pressing RControl+a.
  251.         // 4) For maintainability, it doesn't check mNoSuppress because the hook is needed anyway,
  252.         //    so might as well handle eclipsed hotkeys with it too.
  253.         if (hot.mAllowExtraModifiers && !is_win9x && hot.mVK && !hot.mModifiersLR && !(hot.mModifierSC || hot.mModifierVK))
  254.         {
  255.             for (j = 0; j < sHotkeyCount; ++j)
  256.             {
  257.                 // If it's not of type HK_NORMAL, there's no need to change its type regardless
  258.                 // of the values of its other members.  Also, if the wildcard hotkey (hot) has
  259.                 // any neutral modifiers, this hotkey must have at least those neutral modifiers
  260.                 // too or else it's not eclipsed (and thus registering it is okay).  In other words,
  261.                 // the wildcard hotkey's neutral modifiers must be a perfect subset of this hotkey's
  262.                 // modifiers for this one to be eclipsed by it. Note: Neither mModifiersLR nor
  263.                 // mModifiersConsolidated is checked for simplicity and also because it seems to add
  264.                 // flexibility.  For example, *<^>^a would require both left AND right ctrl to be down,
  265.                 // not EITHER. In other words, mModifiersLR can never in effect contain a neutral modifier.
  266.                 if (shk[j]->mVK == hot.mVK && HK_TYPE_CAN_BECOME_KEYBD_HOOK(shk[j]->mType) // Ordered for short-circuit performance.
  267.                     && (hot.mModifiers & shk[j]->mModifiers) == hot.mModifiers)
  268.                     // Note: No need to check mModifiersLR because it would already be a hook hotkey in that case;
  269.                     // that is, the check of shk[j]->mType precludes it.  It also precludes the possibility
  270.                     // of shk[j] being a key-up hotkey, wildcard hotkey, etc.
  271.                     shk[j]->mType = HK_KEYBD_HOOK;
  272.                     // And if it's currently registered, it will be unregistered later below.
  273.             }
  274.         }
  275.     } // End of first pass loop.
  276.  
  277.     // SECOND PASS THROUGH THE HOTKEYS:
  278.     // v1.0.42: Reset sWhichHookNeeded because it's now possible that the hook was on before but no longer
  279.     // needed due to changing of a hotkey from hook to registered (for various reasons described above):
  280.     for (sWhichHookNeeded = 0, i = 0; i < sHotkeyCount; ++i)
  281.     {
  282.         if (hk_is_inactive[i])
  283.             continue; // v1.0.40: Treat disabled hotkeys as though they're not even present.
  284.         Hotkey &hot = *shk[i]; // For performance and convenience.
  285.  
  286.         // HK_MOUSE_HOOK hotkeys, and most HK_KEYBD_HOOK hotkeys, are handled by the hotkey constructor.
  287.         // What we do here upgrade any NORMAL/registered hotkey to HK_KEYBD_HOOK if there are other
  288.         // hotkeys that interact or overlap with it in such a way that the hook is preferred.
  289.         // This evaluation is done here because only now that hotkeys are about to be activated do
  290.         // we know which ones are disabled or suspended, and thus don't need to be taken into account.
  291.         if (HK_TYPE_CAN_BECOME_KEYBD_HOOK(hot.mType) && !is_win9x)
  292.         {
  293.             if (vk_is_prefix[hot.mVK])
  294.                 // If it's a suffix that is also used as a prefix, use hook (this allows ^!a to work without $ when "a & b" is a hotkey).
  295.                 // v1.0.42: This was fixed so that mVK_WasSpecifiedByNumber dosn't affect it.  That is, a suffix that's
  296.                 // also used as a prefix should become a hook hotkey even if the suffix is specified as "vkNNN::".
  297.                 hot.mType = HK_KEYBD_HOOK;
  298.                 // And if it's currently registered, it will be unregistered later below.
  299.             else
  300.             {
  301.                 // v1.0.42: Since the above has ascertained that the OS isn't Win9x, any #IfWin
  302.                 // keyboard hotkey must use the hook if it lacks an enabled, non-suspended, global variant.
  303.                 // Under those conditions, the hotkey is either:
  304.                 // 1) Single-variant hotkey that has critiera (non-global).
  305.                 // 2) Multi-variant hotkey but all variants have criteria (non-global).
  306.                 // 3) A hotkey with a non-suppressed (~) variant (always, for code simplicity): already handled by AddVariant().
  307.                 // In both cases above, the hook must handle the hotkey because there can be
  308.                 // situations in which the hook should let the hotkey's keystroke pass through
  309.                 // to the active window (i.e. the hook is needed to dynamically disable the hotkey).
  310.                 // mHookAction isn't checked here since those hotkeys shouldn't reach this stage (since they're always hook hotkeys).
  311.                 for (hot.mType = HK_KEYBD_HOOK, vp = hot.mFirstVariant; vp; vp = vp->mNextVariant)
  312.                 {
  313.                     if (   !vp->mHotCriterion && vp->mEnabled // It's a global variant (no criteria) and it's enabled...
  314.                         && (!g_IsSuspended || vp->mJumpToLabel->IsExemptFromSuspend())   )
  315.                         // ... and this variant isn't suspended (we already know IsCompletelyDisabled()==false from an earlier check).
  316.                     {
  317.                         hot.mType = HK_NORMAL; // Override the default.  Hook not needed.
  318.                         break;
  319.                     }
  320.                 }
  321.                 // If the above promoted it from NORMAL to HOOK but the hotkey is currently registered,
  322.                 // it will be unregistered later below.
  323.             }
  324.         }
  325.  
  326.         // Check if this mouse hotkey also requires the keyboard hook (e.g. #LButton).
  327.         // Some mouse hotkeys, such as those with normal modifiers, don't require it
  328.         // since the mouse hook has logic to handle that situation.  But those that
  329.         // are composite hotkeys such as "RButton & Space" or "Space & RButton" need
  330.         // the keyboard hook:
  331.         if (hot.mType == HK_MOUSE_HOOK && (
  332.             hot.mModifierSC || hot.mSC // i.e. since it's an SC, the modifying key isn't a mouse button.
  333.             || hot.mHookAction // v1.0.25.05: At least some alt-tab actions require the keyboard hook. For example, a script consisting only of "MButton::AltTabAndMenu" would not work properly otherwise.
  334.             // v1.0.25.05: The line below was added to prevent the Start Menu from appearing, which
  335.             // requires the keyboard hook. ALT hotkeys don't need it because the mouse hook sends
  336.             // a CTRL keystroke to disguise them, a trick that is unfortunately not reliable for
  337.             // when it happens while the while key is down (though it does disguise a Win-up).
  338.             || ((hot.mModifiersConsolidatedLR & (MOD_LWIN|MOD_RWIN)) && !(hot.mModifiersConsolidatedLR & (MOD_LALT|MOD_RALT)))
  339.             // For v1.0.30, above has been expanded to include Win+Shift and Win+Control modifiers.
  340.             || (hot.mVK && !IsMouseVK(hot.mVK)) // e.g. "RButton & Space"
  341.             || (hot.mModifierVK && !IsMouseVK(hot.mModifierVK)))   ) // e.g. "Space & RButton"
  342.             hot.mType = HK_BOTH_HOOKS;  // Needed by ChangeHookState().
  343.             // For the above, the following types of mouse hotkeys do not need the keyboard hook:
  344.             // 1) mAllowExtraModifiers: Already handled since the mouse hook fetches the modifier state
  345.             //    manually when the keyboard hook isn't installed.
  346.             // 2) mModifiersConsolidatedLR (i.e. the mouse button is modified by a normal modifier
  347.             //    such as CTRL): Same reason as #1.
  348.             // 3) As a subset of #2, mouse hotkeys that use WIN as a modifier will not have the
  349.             //    Start Menu suppressed unless the keyboard hook is installed.  It's debatable,
  350.             //    but that seems a small price to pay (esp. given how rare it is just to have
  351.             //    the mouse hook with no keyboard hook) to avoid the overhead of the keyboard hook.
  352.         
  353.         // If the hotkey is normal, try to register it.  If the register fails, use the hook to try
  354.         // to override any other script or program that might have it registered (as documented):
  355.         if (hot.mType == HK_NORMAL)
  356.         {
  357.             if (!hot.Register()) // Can't register it, usually due to some other application or the OS using it.
  358.                 if (!is_win9x)   // If the OS supports the hook, it can be used to override the other application.
  359.                     hot.mType = HK_KEYBD_HOOK;
  360.                 //else it's Win9x (v1.0.42), so don't force type to hook in case this hotkey is temporarily
  361.                 // in use by some other application.  That way, the registration will be retried next time
  362.                 // the Suspend or Hotkey command calls this function.
  363.                 // The old Win9x warning dialog was removed to reduce code size (since usage of
  364.                 // Win9x is becoming very rare).
  365.         }
  366.         else // mType isn't NORMAL (possibly due to something above changing it), so ensure it isn't registered.
  367.             if (hot.mIsRegistered) // Improves typical performance since this hotkey could be mouse, joystick, etc.
  368.                 // Although the hook effectively overrides registered hotkeys, they should be unregistered anyway
  369.                 // to prevent the Send command from triggering the hotkey, and perhaps other side-effects.
  370.                 hot.Unregister();
  371.  
  372.         // If this is a hook hotkey and the OS is Win9x, the old warning has been removed to
  373.         // reduce code size (since usage of Win9x is becoming very rare).  Older comment:
  374.         // Since it's flagged as a hook in spite of the fact that the OS is Win9x, it means
  375.         // that some previous logic determined that it's not even worth trying to register
  376.         // it because it's just plain not supported.
  377.         switch (hot.mType) // It doesn't matter if the OS is Win9x because in that case, other sections just ignore hook hotkeys.
  378.         {
  379.         case HK_KEYBD_HOOK: sWhichHookNeeded |= HOOK_KEYBD; break;
  380.         case HK_MOUSE_HOOK: sWhichHookNeeded |= HOOK_MOUSE; break;
  381.         case HK_BOTH_HOOKS: sWhichHookNeeded |= HOOK_KEYBD|HOOK_MOUSE; break;
  382.         }
  383.     } // for()
  384.  
  385.     // Check if anything else requires the hook.
  386.     // But do this part outside of the above block because these values may have changed since
  387.     // this function was first called.  The Win9x warning message was removed so that scripts can be
  388.     // run on multiple OSes without a continual warning message just because it happens to be running
  389.     // on Win9x.  By design, the Num/Scroll/CapsLock AlwaysOn/Off setting stays in effect even when
  390.     // Suspend in ON.
  391.     if (   Hotstring::mAtLeastOneEnabled
  392.         || !(g_ForceNumLock == NEUTRAL && g_ForceCapsLock == NEUTRAL && g_ForceScrollLock == NEUTRAL)   )
  393.         sWhichHookNeeded |= HOOK_KEYBD;
  394.     if (g_BlockMouseMove || (g_HSResetUponMouseClick && Hotstring::mAtLeastOneEnabled))
  395.         sWhichHookNeeded |= HOOK_MOUSE;
  396.  
  397.     // Install or deinstall either or both hooks, if necessary, based on these param values.
  398.     ChangeHookState(shk, sHotkeyCount, sWhichHookNeeded, sWhichHookAlways);
  399.  
  400.     // Fix for v1.0.34: If the auto-execute section uses the Hotkey command but returns before doing
  401.     // something that calls MsgSleep, the main timer won't have been turned on.  For example:
  402.     // Hotkey, Joy1, MySubroutine
  403.     // ;Sleep 1  ; This was a workaround before this fix.
  404.     // return
  405.     // By putting the following check here rather than in AutoHotkey.cpp, that problem is resolved.
  406.     // In addition...
  407.     if (sJoyHotkeyCount)  // Joystick hotkeys require the timer to be always on.
  408.         SET_MAIN_TIMER
  409. }
  410.  
  411.  
  412.  
  413. void Hotkey::AllDestructAndExit(int aExitCode)
  414. {
  415.     // PostQuitMessage() might be needed to prevent hang-on-exit.  Once this is done, no message boxes or
  416.     // other dialogs can be displayed.  MSDN: "The exit value returned to the system must be the wParam
  417.     // parameterof the WM_QUIT message."  In our case, PostQuitMessage() should announce the same exit code
  418.     // that we will eventually call exit() with:
  419.     PostQuitMessage(aExitCode);
  420.  
  421.     AddRemoveHooks(0); // Remove all hooks. By contrast, registered hotkeys are unregistered below.
  422.     if (g_PlaybackHook) // Would be unusual for this to be installed during exit, but should be checked for completeness.
  423.         UnhookWindowsHookEx(g_PlaybackHook);
  424.     for (int i = 0; i < sHotkeyCount; ++i)
  425.         delete shk[i]; // Unregisters before destroying.
  426.  
  427.     // Do this only at the last possible moment prior to exit() because otherwise
  428.     // it may free memory that is still in use by objects that depend on it.
  429.     // This is actually kinda wrong because when exit() is called, the destructors
  430.     // of static, global, and main-scope objects will be called.  If any of these
  431.     // destructors try to reference memory freed() by DeleteAll(), there could
  432.     // be trouble.
  433.     // It's here mostly for traditional reasons.  I'm 99.99999 percent sure that there would be no
  434.     // penalty whatsoever to omitting this, since any modern OS will reclaim all
  435.     // memory dynamically allocated upon program termination.  Indeed, omitting
  436.     // deletes and free()'s for simple objects will often improve the reliability
  437.     // and performance since the OS is far more efficient at reclaiming the memory
  438.     // than us doing it manually (which involves a potentially large number of deletes
  439.     // due to all the objects and sub-objects to be destructed in a typical C++ program).
  440.     // UPDATE: In light of the first paragraph above, it seems best not to do this at all,
  441.     // instead letting all implicitly-called destructors run prior to program termination,
  442.     // at which time the OS will reclaim all remaining memory:
  443.     //SimpleHeap::DeleteAll();
  444.  
  445.     // In light of the comments below, and due to the fact that anyone using this app
  446.     // is likely to want the anti-focus-stealing measure to always be disabled, I
  447.     // think it's best not to bother with this ever, since its results are
  448.     // unpredictable:
  449. /*    if (g_os.IsWin98orLater() || g_os.IsWin2000orLater())
  450.         // Restore the original timeout value that was set by WinMain().
  451.         // Also disables the compiler warning for the PVOID cast.
  452.         // Note: In many cases, this call will fail to set the value (perhaps even if
  453.         // SystemParametersInfo() reports success), probably because apps aren't
  454.         // supposed to change this value unless they currently have the input
  455.         // focus or something similar (and this app probably doesn't meet the criteria
  456.         // at this stage).  So I think what will happen is: the value set
  457.         // in WinMain() will stay in effect until the user reboots, at which time
  458.         // the default value store in the registry will once again be in effect.
  459.         // This limitation seems harmless.  Indeed, it's probably a good thing not to
  460.         // set it back afterward so that windows behave more consistently for the user
  461.         // regardless of whether this program is currently running.
  462. #ifdef _MSC_VER
  463.     #pragma warning( disable : 4312 )
  464. #endif
  465.         SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (PVOID)g_OriginalTimeout, SPIF_SENDCHANGE);
  466. #ifdef _MSC_VER
  467.     #pragma warning( default : 4312 ) 
  468. #endif
  469. */
  470.     // I know this isn't the preferred way to exit the program.  However, due to unusual
  471.     // conditions such as the script having MsgBoxes or other dialogs displayed on the screen
  472.     // at the time the user exits (in which case our main event loop would be "buried" underneath
  473.     // the event loops of the dialogs themselves), this is the only reliable way I've found to exit
  474.     // so far.  The caller has already called PostQuitMessage(), which might not help but it doesn't hurt:
  475.     exit(aExitCode); // exit() is insignificant in code size.  It does more than ExitProcess(), but perhaps nothing more that this application actually requires.
  476.     // By contrast to _exit(), exit() flushes all file buffers before terminating the process. It also
  477.     // calls any functions registered via atexit or _onexit.
  478. }
  479.  
  480.  
  481.  
  482. bool Hotkey::PrefixHasNoEnabledSuffixes(int aVKorSC, bool aIsSC)
  483. // aVKorSC contains the virtual key or scan code of the specified prefix key (it's a scan code if aIsSC is true).
  484. // Returns true if this prefix key has no suffixes that can possibly.  Each such suffix is prevented from
  485. // firing by one or more of the following:
  486. // 1) Hotkey is completely disabled via IsCompletelyDisabled().
  487. // 2) Hotkey has criterion and those criterion do not allow the hotkey to fire.
  488. {
  489.     // v1.0.44: Added aAsModifier so that a pair of hotkeys such as:
  490.     //   LControl::tooltip LControl
  491.     //   <^c::tooltip ^c
  492.     // ...works as it did in versions prior to 1.0.41, namely that LControl fires on key-up rather than
  493.     // down because it is considered a prefix key for the <^c hotkey .
  494.     modLR_type aAsModifier = KeyToModifiersLR(aIsSC ? 0 : aVKorSC, aIsSC ? aVKorSC : 0, NULL);
  495.  
  496.     for (int i = 0; i < sHotkeyCount; ++i)
  497.     {
  498.         Hotkey &hk = *shk[i];
  499.         if (aVKorSC != (aIsSC ? hk.mModifierSC : hk.mModifierVK) && !(aAsModifier & hk.mModifiersLR)
  500.             || hk.IsCompletelyDisabled())
  501.             continue; // This hotkey isn't enabled or it doesn't use the specified key as a prefix.  No further checking for it.
  502.         if (hk.mHookAction)
  503.         {
  504.             if (g_IsSuspended)
  505.                 // An alt-tab hotkey (non-NULL mHookAction) is always suspended when g_IsSuspended==true because
  506.                 // alt-tab hotkeys have no subroutine capable of making them exempt.  So g_IsSuspended is checked
  507.                 // for alt-tab hotkeys here; and for other types of hotkeys, it's checked further below.
  508.                 continue;
  509.             else // This alt-tab hotkey is currently active.
  510.                 return false; // Since any stored mHotCriterion are ignored for alt-tab hotkeys, no further checking is needed.
  511.         }
  512.         // Otherwise, find out if any of its variants is eligible to fire.  If so, immediately return
  513.         // false because even one eligible hotkey means this prefix is enabled.
  514.         for (HotkeyVariant *vp = hk.mFirstVariant; vp; vp = vp->mNextVariant)
  515.             // v1.0.42: Fixed to take into account whether the hotkey is suspended (previously it only checked
  516.             // whether the hotkey was enabled (above), which isn't enough):
  517.             if (   vp->mEnabled // This particular variant within its parent hotkey is enabled.
  518.                 && (!g_IsSuspended || vp->mJumpToLabel->IsExemptFromSuspend()) // This variant isn't suspended...
  519.                 && (!vp->mHotCriterion || HotCriterionAllowsFiring(vp->mHotCriterion
  520.                     , vp->mHotWinTitle, vp->mHotWinText))   ) // ... and its critieria allow it to fire.
  521.                 return false; // At least one of this prefix's suffixes is eligible for firing.
  522.     }
  523.     // Since above didn't return, no hotkeys were found for this prefix that are capable of firing.
  524.     return true;
  525. }
  526.  
  527.  
  528.  
  529. HotkeyVariant *Hotkey::CriterionAllowsFiring(HWND *aFoundHWND)
  530. // Caller must not call this for AltTab hotkeys IDs because this will always return NULL in such cases.
  531. // Returns the address of the first matching non-global hotkey variant that is allowed to fire.
  532. // If there is no non-global one eligible, the global one is returned (or NULL if none).
  533. // If non-NULL, aFoundHWND is an output variable for the caller, but it is only set if a
  534. // non-global/criterion variant is found; that is, it isn't changed when no match is found or
  535. // when the match is a global variant.  Even when set, aFoundHWND will be (HWND)1 for
  536. // "not-criteria" such as #IfWinNotActive.
  537. {
  538.     // Check mParentEnabled in case the hotkey became disabled between the time the message was posted
  539.     // and the time it arrived.  A similar check is done for "suspend" later below (since "suspend"
  540.     // is a per-variant attribute).
  541.     if (!mParentEnabled) // IsCompletelyDisabled() isn't called because the loop below checks all the mEnabled flags, no need to do it twice.
  542.         return NULL;
  543.  
  544.     HWND unused;
  545.     HWND &found_hwnd = aFoundHWND ? *aFoundHWND : unused;  // To simplify other things.
  546.     found_hwnd = NULL;  // Set default output parameter for caller (in case loop never sets it).
  547.     HotkeyVariant *vp, *vp_to_fire;
  548.  
  549.     // aHookAction isn't checked because this should never be called for alt-tab hotkeys (see other comments above).
  550.     for (vp_to_fire = NULL, vp = mFirstVariant; vp; vp = vp->mNextVariant)
  551.     {
  552.         // Technically, g_IsSuspended needs to be checked only if our caller is TriggerJoyHotkeys()
  553.         // because other callers would never have received the hotkey message in the first place.
  554.         // However, since it's possible for a hotkey to become suspended between the time its hotkey
  555.         // message is posted and the time it is fetched and processed, aborting the firing seems
  556.         // like the best choice for the widest variety of circumstances (even though it's a departure
  557.         // from the behavior in previous versions).  Another reason to check g_IsSuspended unconditionally
  558.         // is for maintainability and code size reduction.  Finally, it's unlikely to significantly
  559.         // impact performance since the vast majority of hotkeys have either one or just a few variants.
  560.         if (   vp->mEnabled // This particular variant within its parent hotkey is enabled.
  561.             && (!g_IsSuspended || vp->mJumpToLabel->IsExemptFromSuspend()) // This variant isn't suspended...
  562.             && (!vp->mHotCriterion || (found_hwnd = HotCriterionAllowsFiring(vp->mHotCriterion
  563.                 , vp->mHotWinTitle, vp->mHotWinText)))   ) // ... and its critieria allow it to fire.
  564.         {
  565.             if (vp->mHotCriterion) // Since this is the first criteria hotkey, it takes precedence.
  566.                 return vp;
  567.             //else since this is variant has no critieria, let the first criteria variant in the list
  568.             // take precedence over it (if there is one).  If none is found, the vp_to_fire will stay
  569.             // set as the non-critierion variant.
  570.             vp_to_fire = vp;
  571.         }
  572.     }
  573.     return vp_to_fire; // Either NULL or the variant found by the loop.
  574. }
  575.  
  576.  
  577.  
  578. bool Hotkey::CriterionFiringIsCertain(HotkeyIDType &aHotkeyIDwithFlags, bool aKeyUp, UCHAR &aNoSuppress
  579.     , bool &aFireWithNoSuppress, char *aSingleChar)
  580. // v1.0.44: Caller has ensured that aFireWithNoSuppress is true if has already been decided and false if undecided.
  581. // Upon return, caller can assume that the value in it is now decided rather than undecided.
  582. // v1.0.42: Caller must not call this for AltTab hotkeys IDs, but this will always return NULL in such cases.
  583. // aHotkeyToFireUponRelease is sometimes modified for the caller here, as is *aSingleChar (if aSingleChar isn't NULL).
  584. // Caller has ensured that aHotkeyIDwithFlags contains a valid/existing hotkey ID.
  585. // Technically, aHotkeyIDwithMask can be with or without the flags in the high bits.
  586. // If present, they're removed.
  587. {
  588.     // aHookAction isn't checked because this should never be called for alt-tab hotkeys (see other comments above).
  589.     HotkeyIDType hotkey_id = aHotkeyIDwithFlags & HOTKEY_ID_MASK;
  590.     // The following check is for maintainability, since caller should have already checked and
  591.     // handled HOTKEY_ID_ALT_TAB and similar.  Less-than-zero check not necessary because it's unsigned.
  592.     if (hotkey_id >= sHotkeyCount)
  593.         return false; // Special alt-tab hotkey quasi-ID used by the hook.
  594.     Hotkey &hk = *shk[hotkey_id]; // For convenience and performance.
  595.  
  596.     if (aFireWithNoSuppress // Caller has already determined its value with certainty...
  597.         || (hk.mNoSuppress & NO_SUPPRESS_SUFFIX_VARIES) != NO_SUPPRESS_SUFFIX_VARIES) // ...or its value is easy to determine, so do it now (compare to itself since it's a bitwise union).
  598.     {
  599.         // Since aFireWithNoSuppress can now be easily determined for the caller (or was already determined by the caller
  600.         // itself), it's possible to take advantage of the following optimization, which is especially important in cases
  601.         // where TitleMatchMode is "slow":
  602.         // For performance, the following returns without having called WinExist/Active if it sees that one of this
  603.         // hotkey's variant's will certainly fire due to the fact that it has a non-suspended global variant.
  604.         // This reduces the number of situations in which double the number of WinExist/Active() calls are made
  605.         // (once in the hook to determine whether the hotkey keystroke should be passed through to the active window,
  606.         // and again upon receipt of the message for reasons explained there).
  607.         for (HotkeyVariant *vp = hk.mFirstVariant; vp; vp = vp->mNextVariant)
  608.             if (!vp->mHotCriterion && vp->mEnabled && (!g_IsSuspended || vp->mJumpToLabel->IsExemptFromSuspend()))
  609.             {
  610.                 // Fix for v1.0.47.02: The following section (above "return") was moved into this block
  611.                 // from above the for() because only when this for() returns is it certain that this
  612.                 // hk/hotkey_id is actually the right one, and thus its attributes can be used to determine
  613.                 // aFireWithNoSuppress for the caller.
  614.                 // Since this hotkey has variants only of one type (tilde or non-tilde), this variant must be of that type.
  615.                 if (!aFireWithNoSuppress) // Caller hasn't yet determined its value with certainty (currently, this statement might always be true).
  616.                     aFireWithNoSuppress = (hk.mNoSuppress & AT_LEAST_ONE_VARIANT_HAS_TILDE); // Due to other checks, this means all variants are tilde.
  617.                 return true;
  618.             }
  619.     }
  620.  
  621.     // Since above didn't return, a slower method is needed to find out which variant of this hotkey (if any)
  622.     // should fire.
  623.     HotkeyVariant *vp;
  624.     if (vp = hk.CriterionAllowsFiring())
  625.     {
  626.         if (!aFireWithNoSuppress) // Caller hasn't yet determined its value with certainty (currently, this statement might always be true).
  627.             aFireWithNoSuppress = vp->mNoSuppress;
  628.         return true; // It found an eligible variant to fire.
  629.     }
  630.  
  631.     // Since above didn't find any variant of the hotkey than can fire, check for other eligible hotkeys.
  632.     if (!(hk.mModifierVK || hk.mModifierSC || hk.mHookAction)) // Rule out those that aren't susceptible to the bug due to their lack of support for wildcards.
  633.     {
  634.         // Fix for v1.0.46.13: Although the section higher above found no variant to fire for the
  635.         // caller-specified hotkey ID, it's possible that some other hotkey (one with a wildcard) is
  636.         // eligible to fire due to the eclipsing behavior of wildcard hotkeys.  For example:
  637.         //    #IfWinNotActive Untitled
  638.         //    q::tooltip %A_ThisHotkey% Non-notepad
  639.         //    #IfWinActive Untitled
  640.         //    *q::tooltip %A_ThisHotkey% Notepad
  641.         // However, the logic here might not be a perfect solution because it fires the first available
  642.         // hotkey that has a variant whose criteria are met (which might not be exactly the desired rules
  643.         // of precedence).  However, I think it's extremely rare that there would be more than one hotkey
  644.         // that matches the original hotkey (VK, SC, has-wildcard) etc.  Even in the rare cases that there
  645.         // is more than one, the rarity is compounded by the rarity of the bug even occurring, which probably
  646.         // makes the odds vanishingly small.  That's why the following simple, high-performance loop is used
  647.         // rather than more a more complex one that "locates the smallest (most specific) eclipsed wildcard
  648.         // hotkey", or "the uppermost variant among all eclipsed wildcards that is eligible to fire".
  649.         for (int i = 0; i < sHotkeyCount; ++i) // This loop is undesirable; but its performance impact probably isn't measurable in the majority of cases.
  650.         {
  651.             Hotkey &hk2 = *shk[i]; // For performance and convenience.
  652.             if (   hk2.mVK == hk.mVK // VK and SC (one of which is typically zero) must both match for
  653.                 && hk2.mSC == hk.mSC // this bug to have wrongly eclipsed a qualified variant of some other hotkey.
  654.                 && hk2.mAllowExtraModifiers // To be eclipsable by the original hotkey, a candidate must have a wildcard.
  655.                 && hk2.mKeyUp == aKeyUp // Seems necessary that up/down nature is the same in both.
  656.                 && !hk2.mModifierVK // Avoid accidental matching of normal hotkeys with custom-combo "&"
  657.                 && !hk2.mModifierSC // hotkeys that happen to have the same mVK/SC.
  658.                 && !hk2.mHookAction // Might be unnecessary to check this; but just in case.
  659.                 && hk2.mID != hotkey_id // Don't consider the original hotkey because it's was already found ineligible.
  660.                 && (hk.mAllowExtraModifiers // Either the original hotkey must allow extra modifiers or the candidate must not have any modifiers present on the original (otherwise the user probably isn't holding down the right keys to trigger this hotkey).
  661.                     || !((hk.mModifiersConsolidatedLR ^ hk2.mModifiersConsolidatedLR) & hk2.mModifiersConsolidatedLR)) // "The modifiers that are different intersected with those present on the candidate", which are those present on the candidate that are absent from this original.
  662.                 //&& hk2.mType != HK_JOYSTICK // Seems unnecessary since joystick hotkeys don't call us and even if they did, probably should be included.
  663.                 //&& hk2.mParentEnabled   ) // CriterionAllowsFiring() will check this for us.
  664.                 )
  665.             {
  666.                 // The following section is similar to one higher above, so maintain them together:
  667.                 if (vp = hk2.CriterionAllowsFiring())
  668.                 {
  669.                     if (!aFireWithNoSuppress) // Caller hasn't yet determined its value with certainty (currently, this statement might always be true).
  670.                         aFireWithNoSuppress = vp->mNoSuppress;
  671.                     aHotkeyIDwithFlags = hk2.mID; // Caller currently doesn't need the flags put onto it, so they're omitted.
  672.                     return true; // It found an eligible variant to fire.
  673.                 }
  674.             }
  675.         }
  676.     }
  677.  
  678.     // Otherwise, this hotkey has no variants that can fire.  Caller wants a few things updated in that case.
  679.     if (!aFireWithNoSuppress) // Caller hasn't yet determined its value with certainty.
  680.         aFireWithNoSuppress = true; // Fix for v1.0.47.04: Added this line and the one above to fix the fact that a context-sensitive hotkey like "a UP::" would block the down-event of that key even when the right window/criteria aren't met.
  681.     // If this is a key-down hotkey:
  682.     // Leave aHotkeyToFireUponRelease set to whatever it was so that the critieria are
  683.     // evaluated later, at the time of release.  It seems more correct that way, though the actual
  684.     // change (hopefully improvement) in usability is unknown.
  685.     // Since the down-event of this key won't be suppressed, it seems best never to suppress the
  686.     // key-up hotkey (if it has one), if nothing else than to be sure the logical key state of that
  687.     // key as shown by GetAsyncKeyState() returns the correct value (for modifiers, this is even more
  688.     // important since them getting stuck down causes undesirable behavior).  If it doesn't have a
  689.     // key-up hotkey, the up-keystroke should wind up being non-suppressed anyway due to default
  690.     // processing).
  691.     if (!aKeyUp)
  692.         aNoSuppress |= NO_SUPPRESS_NEXT_UP_EVENT;  // Update output parameter for the caller.
  693.     if (aSingleChar)
  694.         *aSingleChar = '#'; // '#' in KeyHistory to indicate this hotkey is disabled due to #IfWin criterion.
  695.     return false;
  696. }
  697.  
  698.  
  699.  
  700. void Hotkey::TriggerJoyHotkeys(int aJoystickID, DWORD aButtonsNewlyDown)
  701. {
  702.     for (int i = 0; i < sHotkeyCount; ++i)
  703.     {
  704.         Hotkey &hk = *shk[i]; // For performance and convenience.
  705.         // Fix for v1.0.34: If hotkey isn't enabled, or hotkeys are suspended and this one isn't
  706.         // exempt, don't fire it.  These checks are necessary only for joystick hotkeys because 
  707.         // normal hotkeys are completely deactivated when turned off or suspended, but the joystick
  708.         // is still polled even when some joystick hotkeys are disabled.  UPDATE: In v1.0.42, Suspend
  709.         // is checked upon receipt of the message, not here upon sending.
  710.         if (hk.mType == HK_JOYSTICK && hk.mVK == aJoystickID
  711.             && (aButtonsNewlyDown & ((DWORD)0x01 << (hk.mSC - JOYCTRL_1)))) // This hotkey's button is among those newly pressed.
  712.         {
  713.             // Criteria are checked, and variant determined, upon arrival of message rather than when sending
  714.             // ("suspend" is also checked then).  This is because joystick button presses are never hidden
  715.             // from the active window (the concept really doesn't apply), so not checking here avoids the
  716.             // performance loss of a second check (the loss can be significant in the case of
  717.             // "SetTitleMatchMode Slow").
  718.             //
  719.             // Post it to the thread because the message pump itself (not the WindowProc) will handle it.
  720.             // UPDATE: Posting to NULL would have a risk of discarding the message if a MsgBox pump or
  721.             // pump other than MsgSleep() were running.  The only reason it doesn't is that this function
  722.             // is only ever called from MsgSleep(), which is careful to process all messages (at least
  723.             // those that aren't kept queued due to the message filter) prior to returning to its caller.
  724.             // But for maintainability, it seems best to change this to g_hWnd vs. NULL to make joystick
  725.             // hotkeys behave more like standard hotkeys.
  726.             PostMessage(g_hWnd, WM_HOTKEY, (WPARAM)i, 0);
  727.         }
  728.         //else continue the loop in case the user has newly pressed more than one joystick button.
  729.     }
  730. }
  731.  
  732.  
  733.  
  734. void Hotkey::Perform(HotkeyVariant &aVariant)
  735. // Caller is reponsible for having called PerformIsAllowed() before calling us.
  736. {
  737.     static bool sDialogIsDisplayed = false;  // Prevents double-display caused by key buffering.
  738.     if (sDialogIsDisplayed) // Another recursion layer is already displaying the warning dialog below.
  739.         return; // Don't allow new hotkeys to fire during that time.
  740.  
  741.     // Help prevent runaway hotkeys (infinite loops due to recursion in bad script files):
  742.     static UINT throttled_key_count = 0;  // This var doesn't belong in struct since it's used only here.
  743.     UINT time_until_now;
  744.     int display_warning;
  745.     if (!sTimePrev)
  746.         sTimePrev = GetTickCount();
  747.  
  748.     ++throttled_key_count;
  749.     sTimeNow = GetTickCount();
  750.     // Calculate the amount of time since the last reset of the sliding interval.
  751.     // Note: A tickcount in the past can be subtracted from one in the future to find
  752.     // the true difference between them, even if the system's uptime is greater than
  753.     // 49 days and the future one has wrapped but the past one hasn't.  This is
  754.     // due to the nature of DWORD math.  The only time this calculation will be
  755.     // unreliable is when the true difference between the past and future
  756.     // tickcounts itself is greater than about 49 days:
  757.     time_until_now = (sTimeNow - sTimePrev);
  758.     if (display_warning = (throttled_key_count > (DWORD)g_MaxHotkeysPerInterval
  759.         && time_until_now < (DWORD)g_HotkeyThrottleInterval))
  760.     {
  761.         // The moment any dialog is displayed, hotkey processing is halted since this
  762.         // app currently has only one thread.
  763.         char error_text[2048];
  764.         // Using %f with wsprintf() yields a floating point runtime error dialog.
  765.         // UPDATE: That happens if you don't cast to float, or don't have a float var
  766.         // involved somewhere.  Avoiding floats altogether may reduce EXE size
  767.         // and maybe other benefits (due to it not being "loaded")?
  768.         snprintf(error_text, sizeof(error_text), "%u hotkeys have been received in the last %ums.\n\n"
  769.             "Do you want to continue?\n(see #MaxHotkeysPerInterval in the help file)"  // In case its stuck in a loop.
  770.             , throttled_key_count, time_until_now);
  771.  
  772.         // Turn off any RunAgain flags that may be on, which in essense is the same as de-buffering
  773.         // any pending hotkey keystrokes that haven't yet been fired:
  774.         ResetRunAgainAfterFinished();
  775.  
  776.         // This is now needed since hotkeys can still fire while a messagebox is displayed.
  777.         // Seems safest to do this even if it isn't always necessary:
  778.         sDialogIsDisplayed = true;
  779.         g_AllowInterruption = false;
  780.         if (MsgBox(error_text, MB_YESNO) == IDNO)
  781.             g_script.ExitApp(EXIT_CRITICAL); // Might not actually Exit if there's an OnExit subroutine.
  782.         g_AllowInterruption = true;
  783.         sDialogIsDisplayed = false;
  784.     }
  785.     // The display_warning var is needed due to the fact that there's an OR in this condition:
  786.     if (display_warning || time_until_now > (DWORD)g_HotkeyThrottleInterval)
  787.     {
  788.         // Reset the sliding interval whenever it expires.  Doing it this way makes the
  789.         // sliding interval more sensitive than alternate methods might be.
  790.         // Also reset it if a warning was displayed, since in that case it didn't expire.
  791.         throttled_key_count = 0;
  792.         sTimePrev = sTimeNow;
  793.     }
  794.     if (display_warning)
  795.         // At this point, even though the user chose to continue, it seems safest
  796.         // to ignore this particular hotkey event since it might be WinClose or some
  797.         // other command that would have unpredictable results due to the displaying
  798.         // of the dialog itself.
  799.         return;
  800.  
  801.  
  802.     // This is stored as an attribute of the script (semi-globally) rather than passed
  803.     // as a parameter to ExecUntil (and from their on to any calls to SendKeys() that it
  804.     // makes) because it's possible for SendKeys to be called asynchronously, namely
  805.     // by a timed subroutine, while #HotkeyModifierTimeout is still in effect,
  806.     // in which case we would want SendKeys() to take note of these modifiers even
  807.     // if it was called from an ExecUntil() other than ours here:
  808.     g_script.mThisHotkeyModifiersLR = mModifiersConsolidatedLR;
  809.     bool unregistered_during_thread = mUnregisterDuringThread && mIsRegistered;
  810.  
  811.     // LAUNCH HOTKEY SUBROUTINE:
  812.     // For v1.0.23, the below allows the $ hotkey prefix to unregister the hotkey on
  813.     // Windows 9x, which allows the send command to send the hotkey itself without
  814.     // causing an infinite loop of keystrokes.  For simplicity, the hotkey is kept
  815.     // unregistered during the entire duration of the thread, rather than trying to
  816.     // selectively do it before and after each Send command of the thread:
  817.     if (unregistered_during_thread) // Do it every time through the loop in case the hotkey is re-registered by its own subroutine.
  818.         Unregister(); // This takes care of other details for us.
  819.     ++aVariant.mExistingThreads;  // This is the thread count for this particular hotkey only.
  820.     ResultType result = aVariant.mJumpToLabel->Execute();
  821.     --aVariant.mExistingThreads;
  822.     if (unregistered_during_thread)
  823.         Register();
  824.  
  825.     if (result == FAIL)
  826.         aVariant.mRunAgainAfterFinished = false;  // Ensure this is reset due to the error.
  827.     else if (aVariant.mRunAgainAfterFinished)
  828.     {
  829.         // But MsgSleep() can change it back to true again, when called by the above call
  830.         // to ExecUntil(), to keep it auto-repeating:
  831.         aVariant.mRunAgainAfterFinished = false;  // i.e. this "run again" ticket has now been used up.
  832.         if (GetTickCount() - aVariant.mRunAgainTime <= 1000)
  833.         {
  834.             // v1.0.44.14: Post a message rather than directly running the above ExecUntil again.
  835.             // This fixes unreported bugs in previous versions where the thread isn't reinitialized before
  836.             // the launch of one of these buffered hotkeys, which caused settings such as SetKeyDelay
  837.             // not to start off at their defaults.  Also, there are quite a few other things that the main
  838.             // event loop does to prep for the launch of a hotkey.  Rather than copying them here or
  839.             // trying to put them into a shared function (which would be difficult due to their nature),
  840.             // it's much more maintainable to post a message, and in most cases, it shouldn't measurably
  841.             // affect response time (this feature is rarely used anyway).
  842.             PostMessage(g_hWnd, WM_HOTKEY, (WPARAM)mID, 0);
  843.         }
  844.         //else it was posted too long ago, so don't do it.  This is because most users wouldn't
  845.         // want a buffered hotkey to stay pending for a long time after it was pressed, because
  846.         // that might lead to unexpected behavior.
  847.     }
  848. }
  849.  
  850.  
  851.  
  852. ResultType Hotkey::Dynamic(char *aHotkeyName, char *aLabelName, char *aOptions, Label *aJumpToLabel)
  853. // Creates, updates, enables, or disables a hotkey dynamically (while the script is running).
  854. // Returns OK or FAIL.
  855. {
  856.     if (!strnicmp(aHotkeyName, "IfWin", 5)) // Seems reasonable to assume that anything starting with "IfWin" can't be the name of a hotkey.
  857.     {
  858.         HotCriterionType hot_criterion;
  859.         bool invert = !strnicmp(aHotkeyName + 5, "Not", 3);
  860.         if (!strnicmp(aHotkeyName + (invert ? 8 : 5), "Active", 6)) // It matches #IfWin[Not]Active.
  861.             hot_criterion = invert ? HOT_IF_NOT_ACTIVE : HOT_IF_ACTIVE;
  862.         else if (!strnicmp(aHotkeyName + (invert ? 8 : 5), "Exist", 5))
  863.             hot_criterion = invert ? HOT_IF_NOT_EXIST : HOT_IF_EXIST;
  864.         else // It starts with IfWin but isn't Active or Exist: Don't alter the current criterion.
  865.             return g_ErrorLevel->Assign(ERRORLEVEL_ERROR);
  866.         if (!(*aLabelName || *aOptions)) // This check is done only after detecting bad spelling of IfWin above.
  867.             g_HotCriterion = HOT_NO_CRITERION;
  868.         else if (SetGlobalHotTitleText(aLabelName, aOptions)) // Currently, it only fails upon out-of-memory.
  869.             g_HotCriterion = hot_criterion; // Only set at the last minute so that previous criteria will stay in effect if it fails.
  870.         else
  871.             return g_ErrorLevel->Assign(ERRORLEVEL_ERROR);
  872.         return g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Indicate success.
  873.     }
  874.  
  875.     // For maintainability (and script readability), don't support "U" as a substitute for "UseErrorLevel",
  876.     // since future options might contain the letter U as a "parameter" that immediately follows an option-letter.
  877.     bool use_errorlevel = strcasestr(aOptions, "UseErrorLevel");
  878.     #define RETURN_HOTKEY_ERROR(level, msg, info) return use_errorlevel ? g_ErrorLevel->Assign(level) \
  879.         : g_script.ScriptError(msg ERR_ABORT, info)
  880.  
  881.     HookActionType hook_action = 0; // Set default.
  882.     if (!aJumpToLabel) // It wasn't provided by caller (resolved at load-time).
  883.         if (   !(hook_action = ConvertAltTab(aLabelName, true))   )
  884.             if (   *aLabelName && !(aJumpToLabel = g_script.FindLabel(aLabelName))   )
  885.                 RETURN_HOTKEY_ERROR(HOTKEY_EL_BADLABEL, ERR_NO_LABEL, aLabelName);
  886.     // Above has ensured that aJumpToLabel and hook_action can't both be non-zero.  Furthermore,
  887.     // both can be zero/NULL only when the caller is updating an existing hotkey to have new options
  888.     // (i.e. it's retaining its current label).
  889.  
  890.     bool suffix_has_tilde;
  891.     Hotkey *hk = FindHotkeyByTrueNature(aHotkeyName, suffix_has_tilde); // NULL if not found.
  892.     HotkeyVariant *variant = hk ? hk->FindVariant() : NULL;
  893.     bool update_all_hotkeys = false;  // This method avoids multiple calls to ManifestAllHotkeysHotstringsHooks() (which is high-overhead).
  894.     bool variant_was_just_created = false;
  895.  
  896.     switch (hook_action)
  897.     {
  898.     case HOTKEY_ID_ON:
  899.     case HOTKEY_ID_OFF:
  900.     case HOTKEY_ID_TOGGLE:
  901.         if (!hk)
  902.             RETURN_HOTKEY_ERROR(HOTKEY_EL_NOTEXIST, ERR_NONEXISTENT_HOTKEY, aHotkeyName);
  903.         if (!(variant || hk->mHookAction)) // mHookAction (alt-tab) hotkeys don't need a variant that matches the current criteria.
  904.             // To avoid ambiguity and also allow the script to use ErrorLevel to detect whether a variant
  905.             // already exists, it seems best to strictly require a matching variant rather than falling back
  906.             // onto some "default variant" such as the global variant (if any).
  907.             RETURN_HOTKEY_ERROR(HOTKEY_EL_NOTEXISTVARIANT, ERR_NONEXISTENT_VARIANT, aHotkeyName);
  908.         if (hook_action == HOTKEY_ID_TOGGLE)
  909.             hook_action = hk->mHookAction
  910.                 ? (hk->mParentEnabled ? HOTKEY_ID_OFF : HOTKEY_ID_ON) // Enable/disable parent hotkey (due to alt-tab being a global hotkey).
  911.                 : (variant->mEnabled ? HOTKEY_ID_OFF : HOTKEY_ID_ON); // Enable/disable individual variant.
  912.         if (hook_action == HOTKEY_ID_ON)
  913.         {
  914.             if (hk->mHookAction ? hk->EnableParent() : hk->Enable(*variant))
  915.                 update_all_hotkeys = true; // Do it this way so that any previous "true" value isn't lost.
  916.         }
  917.         else
  918.             if (hk->mHookAction ? hk->DisableParent() : hk->Disable(*variant))
  919.                 update_all_hotkeys = true; // Do it this way so that any previous "true" value isn't lost.
  920.         break;
  921.  
  922.     default: // hook_action is 0 or an AltTab action.  COMMAND: Hotkey, Name, Label|AltTabAction
  923.         if (!hk) // No existing hotkey of this name, so create a new hotkey.
  924.         {
  925.             if (hook_action) // COMMAND (create hotkey): Hotkey, Name, AltTabAction
  926.                 hk = AddHotkey(NULL, hook_action, aHotkeyName, suffix_has_tilde, use_errorlevel);
  927.             else // COMMAND (create hotkey): Hotkey, Name, LabelName [, Options]
  928.             {
  929.                 if (!aJumpToLabel) // Caller is trying to set new aOptions for a nonexistent hotkey.
  930.                     RETURN_HOTKEY_ERROR(HOTKEY_EL_NOTEXIST, ERR_NONEXISTENT_HOTKEY, aHotkeyName);
  931.                 hk = AddHotkey(aJumpToLabel, 0, aHotkeyName, suffix_has_tilde, use_errorlevel);
  932.             }
  933.             if (!hk)
  934.                 return use_errorlevel ? OK : FAIL; // AddHotkey() already displayed the error (or set ErrorLevel).
  935.             variant = hk->mLastVariant; // Update for use with the options-parsing section further below.
  936.             update_all_hotkeys = true;
  937.             variant_was_just_created = true;
  938.         }
  939.         else // Hotkey already exists (though possibly not the required variant).  Update the hotkey if appropriate.
  940.         {
  941.             if (hk->mHookAction != hook_action) // COMMAND: Change to/from alt-tab hotkey.
  942.             {
  943.                 // LoadIncludedFile() contains logic and comments similar to this, so maintain them together.
  944.                 // If hook_action isn't zero, the caller is converting this hotkey into a global alt-tab
  945.                 // hotkey (alt-tab hotkeys are never subject to #IfWin, as documented).  Thus, variant can
  946.                 // be NULL because making a hotkey become alt-tab doesn't require the creation or existence
  947.                 // of a variant matching the current #IfWin criteria.  However, continue on to process the
  948.                 // Options parameter in case it contains "On" or some other keyword applicable to alt-tab.
  949.                 hk->mHookAction = hook_action;
  950.                 if (!hook_action)
  951.                     // Since this hotkey is going from alt-tab to non-alt-tab, make sure it's not disabled
  952.                     // because currently, mParentEnabled is only actually used by alt-tab hotkeys (though it
  953.                     // may have other uses in the future, which is why it's implemented and named the way it is).
  954.                     hk->mParentEnabled = true;
  955.                 else // This hotkey is becoming alt-tab.
  956.                 {
  957.                     // Make the hook mandatory for this hotkey. Known limitation: For maintainability and code size,
  958.                     // this is never undone (even if the hotkey is changed back to non-alt-tab) because there are
  959.                     // many other reasons a hotkey's mKeybdHookMandatory could be true, so can't change it back to
  960.                     // false without checking all those other things.
  961.                     if (HK_TYPE_CAN_BECOME_KEYBD_HOOK(hk->mType))
  962.                         hk->mKeybdHookMandatory = true; // Causes mType to be set to HK_KEYBD_HOOK by ManifestAllHotkeysHotstringsHooks().
  963.                 }
  964.                 // Even if it's still an alt-tab action (just a different one), hook's data structures still
  965.                 // need to be updated.  Otherwise, this is a change from an alt-tab type to a non-alt-tab type,
  966.                 // or vice versa: Due to the complexity of registered vs. hook hotkeys, for now just start from
  967.                 // scratch so that there is high confidence that the hook and all registered hotkeys, including
  968.                 // their interdependencies, will be re-initialized correctly.
  969.                 update_all_hotkeys = true;
  970.             }
  971.             // If the above changed the action from an Alt-tab type to non-alt-tab, there may be a label present
  972.             // to be applied to the existing variant (or created as a new variant).
  973.             if (aJumpToLabel) // COMMAND (update hotkey): Hotkey, Name, LabelName [, Options]
  974.             {
  975.                 // If there's a matching variant, update it's label. Otherwise, create a new variant.
  976.                 if (variant) // There's an existing variant...
  977.                 {
  978.                     if (aJumpToLabel != variant->mJumpToLabel) // ...and it's label is being changed.
  979.                     {
  980.                         // If the new label's exempt status is different than the old's, re-manifest all hotkeys
  981.                         // in case the new presence/absence of this variant impacts other hotkeys.
  982.                         // v1.0.42: However, this only needs to be done if Suspend is currently turned on,
  983.                         // since otherwise the change in exempt status can't change whether this variant is
  984.                         // currently in effect.
  985.                         if (variant->mEnabled && g_IsSuspended && aJumpToLabel->IsExemptFromSuspend() != variant->mJumpToLabel->IsExemptFromSuspend())
  986.                             update_all_hotkeys = true;
  987.                         variant->mJumpToLabel = aJumpToLabel; // Must be done only after the above has finished using the old mJumpToLabel.
  988.                         // Older comment:
  989.                         // If this hotkey is currently a static hotkey (one not created by the Hotkey command):
  990.                         // Even though it's about to be transformed into a dynamic hotkey via the Hotkey command,
  991.                         // mName can be left pointing to the original Label::mName memory because that should
  992.                         // never change; it will always contain the true name of this hotkey, namely its
  993.                         // keystroke+modifiers (e.g. ^!c).
  994.                     }
  995.                 }
  996.                 else // No existing variant matching current #IfWin critieria, so create a new variant.
  997.                 {
  998.                     if (   !(variant = hk->AddVariant(aJumpToLabel, suffix_has_tilde))   ) // Out of memory.
  999.                         RETURN_HOTKEY_ERROR(HOTKEY_EL_MEM, ERR_OUTOFMEM, aHotkeyName);
  1000.                     variant_was_just_created = true;
  1001.                     update_all_hotkeys = true;
  1002.                 }
  1003.             }
  1004.             //else NULL label, so either it just became an alt-tab hotkey above, or it's "Hotkey, Name,, Options".
  1005.             // Either way, continue on and let the error-catch below report it if it qualifies as an error.
  1006.         } // Hotkey already existed.
  1007.         break;
  1008.     } // switch(hook_action)
  1009.  
  1010.     // Above has ensured that hk is not NULL.
  1011.  
  1012.     // The following check catches:
  1013.     // Hotkey, Name,, Options  ; Where name exists as a hotkey, but the right variant doesn't yet exist.
  1014.     // If it catches anything else, that could be a bug, so this error message will help spot it.
  1015.     if (!(variant || hk->mHookAction)) // mHookAction (alt-tab) hotkeys don't need a variant that matches the current criteria.
  1016.         RETURN_HOTKEY_ERROR(HOTKEY_EL_NOTEXISTVARIANT, ERR_NONEXISTENT_VARIANT, aHotkeyName);
  1017.     // Below relies on the fact that either variant or hk->mHookAction (or both) is now non-zero.
  1018.     // Specifically, when an existing hotkey was changed to become an alt-tab hotkey, above, there will sometimes
  1019.     // be a NULL variant (depending on whether there happens to be a variant in the hotkey that matches the current criteria).
  1020.  
  1021.     // If aOptions is blank, any new hotkey or variant created above will have used the current values of
  1022.     // g_MaxThreadsBuffer, etc.
  1023.     if (*aOptions)
  1024.     {
  1025.         for (char *cp = aOptions; *cp; ++cp)
  1026.         {
  1027.             switch(toupper(*cp))
  1028.             {
  1029.             case 'O': // v1.0.38.02.
  1030.                 if (toupper(cp[1]) == 'N') // Full validation for maintainability.
  1031.                 {
  1032.                     ++cp; // Omit the 'N' from further consideration in case it ever becomes a valid option letter.
  1033.                     if (hk->mHookAction ? hk->EnableParent() : hk->Enable(*variant)) // Under these conditions, earlier logic has ensured variant is non-NULL.
  1034.                         update_all_hotkeys = true; // Do it this way so that any previous "true" value isn't lost.
  1035.                 }
  1036.                 else if (!strnicmp(cp, "Off", 3))
  1037.                 {
  1038.                     cp += 2; // Omit the letters of the word from further consideration in case "f" ever becomes a valid option letter.
  1039.                     if (hk->mHookAction ? hk->DisableParent() : hk->Disable(*variant)) // Under these conditions, earlier logic has ensured variant is non-NULL.
  1040.                         update_all_hotkeys = true; // Do it this way so that any previous "true" value isn't lost.
  1041.                     if (variant_was_just_created) // This variant (and possibly its parent hotkey) was just created above.
  1042.                         update_all_hotkeys = false; // Override the "true" that was set (either right above *or* anywhere earlier) because this new hotkey/variant won't affect other hotkeys.
  1043.                 }
  1044.                 break;
  1045.             case 'B':
  1046.                 if (variant)
  1047.                     variant->mMaxThreadsBuffer = (cp[1] != '0');  // i.e. if the char is NULL or something other than '0'.
  1048.                 break;
  1049.             // For options such as P & T: Use atoi() vs. ATOI() to avoid interpreting something like 0x01B
  1050.             // as hex when in fact the B was meant to be an option letter:
  1051.             case 'P':
  1052.                 if (variant)
  1053.                     variant->mPriority = atoi(cp + 1);
  1054.                 break;
  1055.             case 'T':
  1056.                 if (variant)
  1057.                 {
  1058.                     variant->mMaxThreads = atoi(cp + 1);
  1059.                     if (variant->mMaxThreads > MAX_THREADS_LIMIT)
  1060.                         // For now, keep this limited to prevent stack overflow due to too many pseudo-threads.
  1061.                         variant->mMaxThreads = MAX_THREADS_LIMIT;
  1062.                     else if (variant->mMaxThreads < 1)
  1063.                         variant->mMaxThreads = 1;
  1064.                 }
  1065.                 break;
  1066.             case 'U':
  1067.                 // The actual presence of "UseErrorLevel" was already acted upon earlier, so nothing
  1068.                 // is done here other than skip over the string so that the letters inside it aren't
  1069.                 // misinterpreted as other option letters.
  1070.                 if (!stricmp(cp, "UseErrorLevel"))
  1071.                     cp += 12; // Omit the rest of the letters in the string from further consideration.
  1072.                 break;
  1073.             // Otherwise: Ignore other characters, such as the digits that comprise the number after the T option.
  1074.             }
  1075.         } // for()
  1076.     } // if (*aOptions)
  1077.  
  1078.     if (update_all_hotkeys)
  1079.         ManifestAllHotkeysHotstringsHooks(); // See its comments for why it's done in so many of the above situations.
  1080.  
  1081.     // Somewhat debatable, but the following special ErrorLevels are set even if the above didn't
  1082.     // need to re-manifest the hotkeys.
  1083.     if (use_errorlevel)
  1084.     {
  1085.         g_ErrorLevel->Assign(ERRORLEVEL_NONE); // Set default, possibly to be overridden below.
  1086.         if (HK_TYPE_IS_HOOK(hk->mType))
  1087.         {
  1088.             if (g_os.IsWin9x())
  1089.                 g_ErrorLevel->Assign(HOTKEY_EL_WIN9X); // Reported even if the hotkey is disabled or suspended.
  1090.         }
  1091.         else // HK_NORMAL (registered hotkey).  v1.0.44.07
  1092.             if (hk->mType != HK_JOYSTICK && !hk->mIsRegistered && (!g_IsSuspended || hk->IsExemptFromSuspend()) && !hk->IsCompletelyDisabled())
  1093.                 g_ErrorLevel->Assign(HOTKEY_EL_NOREG); // Generally affects only Win9x because other OSes should have reverted to hook when register failed.
  1094.     }
  1095.     return OK;
  1096. }
  1097.  
  1098.  
  1099.  
  1100. Hotkey *Hotkey::AddHotkey(Label *aJumpToLabel, HookActionType aHookAction, char *aName, bool aSuffixHasTilde, bool aUseErrorLevel)
  1101. // Caller provides aJumpToLabel rather than a Line* because at the time a hotkey or hotstring
  1102. // is created, the label's destination line is not yet known.  So the label is used a placeholder.
  1103. // Caller must ensure that either aJumpToLabel or aName is not NULL.
  1104. // aName is NULL whenever the caller is creating a static hotkey, at loadtime (i.e. one that
  1105. // points to a hotkey label rather than a normal label).  The only time aJumpToLabel should
  1106. // be NULL is when the caller is creating a dynamic hotkey that has an aHookAction.
  1107. // Returns the address of the new hotkey on success, or NULL otherwise.
  1108. // The caller is responsible for calling ManifestAllHotkeysHotstringsHooks(), if appropriate.
  1109. {
  1110.     if (   !(shk[sNextID] = new Hotkey(sNextID, aJumpToLabel, aHookAction, aName, aSuffixHasTilde, aUseErrorLevel))   )
  1111.     {
  1112.         if (aUseErrorLevel)
  1113.             g_ErrorLevel->Assign(HOTKEY_EL_MEM);
  1114.         //else currently a silent failure due to rarity.
  1115.         return NULL;
  1116.     }
  1117.     if (!shk[sNextID]->mConstructedOK)
  1118.     {
  1119.         delete shk[sNextID];  // SimpleHeap allows deletion of most recently added item.
  1120.         return NULL;  // The constructor already displayed the error (or updated ErrorLevelevel).
  1121.     }
  1122.     ++sNextID;
  1123.     return shk[sNextID - 1]; // Indicate success by returning the new hotkey.
  1124. }
  1125.  
  1126.  
  1127.  
  1128. Hotkey::Hotkey(HotkeyIDType aID, Label *aJumpToLabel, HookActionType aHookAction, char *aName
  1129.     , bool aSuffixHasTilde, bool aUseErrorLevel)
  1130. // Constructor.
  1131. // Caller provides aJumpToLabel rather than a Line* because at the time a hotkey or hotstring
  1132. // is created, the label's destination line is not yet known.  So the label is used a placeholder.
  1133. // Even if the caller-provided aJumpToLabel is NULL, a non-NULL mJumpToLabel will be stored in
  1134. // each hotkey/variant so that NULL doesn't have to be constantly checked during script runtime.
  1135.     : mID(HOTKEY_ID_INVALID)  // Default until overridden.
  1136.     // Caller must ensure that either aName or aJumpToLabel isn't NULL.
  1137.     , mVK(0)
  1138.     , mSC(0)
  1139.     , mModifiers(0)
  1140.     , mModifiersLR(0)
  1141.     , mKeybdHookMandatory(false)
  1142.     , mAllowExtraModifiers(false)
  1143.     , mKeyUp(false)
  1144.     , mNoSuppress(0)  // Default is to suppress both prefixes and suffixes.
  1145.     , mModifierVK(0)
  1146.     , mModifierSC(0)
  1147.     , mModifiersConsolidatedLR(0)
  1148.     , mType(HK_NORMAL) // Default unless overridden to become mouse, joystick, hook, etc.
  1149.     , mVK_WasSpecifiedByNumber(false)
  1150.     , mUnregisterDuringThread(false)
  1151.     , mIsRegistered(false)
  1152.     , mParentEnabled(true)
  1153.     , mHookAction(aHookAction)   // Alt-tab and possibly other uses.
  1154.     , mFirstVariant(NULL), mLastVariant(NULL)  // Init linked list early for maintainability.
  1155.     , mConstructedOK(false)
  1156.  
  1157. // It's better to receive the hotkey_id as a param, since only the caller has better knowledge and
  1158. // verification of the fact that this hotkey's id is always set equal to it's index in the array
  1159. // (for performance reasons).
  1160. {
  1161.     if (sHotkeyCount >= MAX_HOTKEYS || sNextID > HOTKEY_ID_MAX)  // The latter currently probably can't happen.
  1162.     {
  1163.         // This will actually cause the script to terminate if this hotkey is a static (load-time)
  1164.         // hotkey.  In the future, some other behavior is probably better:
  1165.         if (aUseErrorLevel)
  1166.             g_ErrorLevel->Assign(HOTKEY_EL_MAXCOUNT);
  1167.         else
  1168.             MsgBox("Max hotkeys.");  // Brief msg since so rare.
  1169.         return;
  1170.     }
  1171.  
  1172.     char *hotkey_name = aName ? aName : aJumpToLabel->mName;
  1173.     if (!TextInterpret(hotkey_name, this, aUseErrorLevel)) // The called function already displayed the error.
  1174.         return;
  1175.  
  1176.     if (mType != HK_JOYSTICK) // Perform modifier adjustment and other activities that don't apply to joysticks.
  1177.     {
  1178.         // Remove any modifiers that are obviously redundant from keys (even NORMAL/registered ones
  1179.         // due to cases where RegisterHotkey() fails and the key is then auto-enabled via the hook).
  1180.         // No attempt is currently made to correct a silly hotkey such as "lwin & lwin".  In addition,
  1181.         // weird hotkeyssuch as <^Control and ^LControl are not currently validated and might yield
  1182.         // unpredictable results.
  1183.         bool is_neutral;
  1184.         modLR_type modifiers_lr;
  1185.         if (modifiers_lr = KeyToModifiersLR(mVK, mSC, &is_neutral))
  1186.         {
  1187.             // This hotkey's action-key is itself a modifier, so ensure that it's not defined
  1188.             // to modify itself.  Other sections might rely on us doing this:
  1189.             if (is_neutral)
  1190.                 // Since the action-key is a neutral modifier (not left or right specific),
  1191.                 // turn off any neutral modifiers that may be on:
  1192.                 mModifiers &= ~ConvertModifiersLR(modifiers_lr);
  1193.             else
  1194.                 mModifiersLR &= ~modifiers_lr;
  1195.         }
  1196.  
  1197.         if (   (mHookAction == HOTKEY_ID_ALT_TAB || mHookAction == HOTKEY_ID_ALT_TAB_SHIFT)
  1198.             && !mModifierVK && !mModifierSC   ) // For simplicity, this section is also done for Win9x even though alt-tab hotkeys will not be activated.
  1199.         {
  1200.             char error_text[512];
  1201.             if (mModifiers)
  1202.             {
  1203.                 if (aUseErrorLevel)
  1204.                     g_ErrorLevel->Assign(HOTKEY_EL_ALTTAB);
  1205.                 else
  1206.                 {
  1207.                     // Neutral modifier has been specified.  Future enhancement: improve this
  1208.                     // to try to guess which key, left or right, should be used based on the
  1209.                     // location of the suffix key on the keyboard.
  1210.                     snprintf(error_text, sizeof(error_text), "The AltTab hotkey \"%s\" must specify which key (L or R).", hotkey_name);
  1211.                     if (g_script.mIsReadyToExecute) // Dynamically registered via the Hotkey command.
  1212.                     {
  1213.                         snprintfcat(error_text, sizeof(error_text), "\n\n%s", ERR_ABORT_NO_SPACES);
  1214.                         g_script.ScriptError(error_text);
  1215.                     }
  1216.                     else
  1217.                         MsgBox(error_text);
  1218.                 }
  1219.                 return;  // Key is invalid so don't give it an ID.
  1220.             }
  1221.             if (mModifiersLR)
  1222.             {
  1223.                 // If mModifiersLR contains only a single modifier key, that is valid
  1224.                 // so we convert it here to its corresponding mModifierVK for use by
  1225.                 // the hook.  For simplicity, this section is also done for Win9x even
  1226.                 // though alt-tab hotkeys will not be activated.
  1227.                 switch (mModifiersLR)
  1228.                 {
  1229.                 case MOD_LCONTROL: mModifierVK = VK_LCONTROL; break;
  1230.                 case MOD_RCONTROL: mModifierVK = VK_RCONTROL; break;
  1231.                 case MOD_LSHIFT: mModifierVK = VK_LSHIFT; break;
  1232.                 case MOD_RSHIFT: mModifierVK = VK_RSHIFT; break;
  1233.                 case MOD_LALT: mModifierVK = VK_LMENU; break;
  1234.                 case MOD_RALT: mModifierVK = VK_RMENU; break;
  1235.                 case MOD_LWIN: mModifierVK = VK_LWIN; break;
  1236.                 case MOD_RWIN: mModifierVK = VK_RWIN; break;
  1237.                 default:
  1238.                     if (aUseErrorLevel)
  1239.                         g_ErrorLevel->Assign(HOTKEY_EL_ALTTAB);
  1240.                     else
  1241.                     {
  1242.                         snprintf(error_text, sizeof(error_text), "The AltTab hotkey \"%s\" must have exactly "
  1243.                             "one modifier/prefix.", hotkey_name);
  1244.                         if (g_script.mIsReadyToExecute) // Dynamically registered via the Hotkey command.
  1245.                         {
  1246.                             snprintfcat(error_text, sizeof(error_text), ERR_ABORT);
  1247.                             g_script.ScriptError(error_text);
  1248.                         }
  1249.                         else
  1250.                             MsgBox(error_text);
  1251.                     }
  1252.                     return;  // Key is invalid so don't give it an ID.
  1253.                 }
  1254.                 // Since above didn't return:
  1255.                 mModifiersLR = 0;  // Since ModifierVK/SC is now its substitute.
  1256.             }
  1257.             // Update: This is no longer needed because the hook attempts to compensate.
  1258.             // However, leaving it enabled may improve performance and reliability.
  1259.             // Update#2: No, it needs to be disabled, otherwise alt-tab won't work right
  1260.             // in the rare case where an ALT key itself is defined as "AltTabMenu":
  1261.             //else
  1262.                 // It has no ModifierVK/SC and no modifiers, so it's a hotkey that is defined
  1263.                 // to fire only when the Alt-Tab menu is visible.  Since the ALT key must be
  1264.                 // down for that menu to be visible (on all OSes?), add the ALT key to this
  1265.                 // keys modifiers so that it will be detected as a hotkey whenever the
  1266.                 // Alt-Tab menu is visible:
  1267.             //    modifiers |= MOD_ALT;
  1268.         }
  1269.  
  1270.         if (g_os.IsWin9x())
  1271.         {
  1272.             // Fix for v1.0.25: If no VK could be found, try to find one so that the attempt
  1273.             // to register the hotkey will have a chance of success.  This change is known to
  1274.             // permit all the keys normally handled by scan code to work as hotkeys on Win9x.
  1275.             // Namely: Del, Ins, Home, End, PgUp/PgDn, and the arrow keys.
  1276.             // One of the main reasons for handling these keys by scan code is that
  1277.             // they each have a counterpart on the Numpad, and in many cases a user would
  1278.             // not want both to become hotkeys.  It could be argued that this should be changed
  1279.             // For Win NT/2k/XP too, but then it would have to be documented that the $ prefix
  1280.             // would have to be used to prevent the "both keys of the pair" behavior.  This
  1281.             // confusion plus the chance of breaking existing scripts doesn't seem worth the
  1282.             // small benefit of being able to avoid the keyboard hook in cases where these
  1283.             // would be the only hotkeys using it.  If there is ever a need, a new hotkey
  1284.             // prefix or option can be added someday to handle this, perhaps #LinkPairedKeys
  1285.             // to avoid having yet another reserved hotkey prefix/symbol.
  1286.             if (mSC)
  1287.                 if (mVK = sc_to_vk(mSC)) // Might always succeed in finding a non-zero VK.
  1288.                     mSC = 0;  // For maintainability, to force it to be one type or the other.
  1289.  
  1290.             // v1.0.42 (see comments at "mModifiersLR" further below):
  1291.             if (mModifiersLR)
  1292.             {
  1293.                 mModifiers |= ConvertModifiersLR(mModifiersLR); // Convert L/R modifiers into neutral ones for use with RegisterHotkey().
  1294.                 mModifiersLR = 0;
  1295.             }
  1296.         }
  1297.         else // Not Win9x.
  1298.         {
  1299.             if (HK_TYPE_CAN_BECOME_KEYBD_HOOK(mType)) // Added in v1.0.39 to make a hotkey such as "LButton & LCtrl" install the mouse hook.
  1300.             {
  1301.                 switch (mVK)
  1302.                 {
  1303.                 // I had trouble finding this before, so this comment serves as a means of finding the place
  1304.                 // where a hotkey with a non-zero mSC is set to type HK_KEYBD_HOOK (Win9x converts mSC to mVK higher above).
  1305.                 case 0: // Scan codes having no available virtual key must always be handled by the hook.
  1306.                 // In addition, to support preventing the toggleable keys from toggling, handle those
  1307.                 // with the hook also. But for Win9x(), attempt to register such hotkeys by not requiring the hook
  1308.                 // (due to checks above, this section doesn't run for Win9x).  This provides partial functionality
  1309.                 // on Win9x, the limitation being that the key will probably be toggled to its opposite state when
  1310.                 // it's used as a hotkey. But the user may be able to concoct a script workaround for that.
  1311.                 case VK_NUMLOCK:
  1312.                 case VK_CAPITAL:
  1313.                 case VK_SCROLL:
  1314.                 // When the AppsKey used as a suffix, always use the hook to handle it because registering
  1315.                 // such keys with RegisterHotkey() will fail to suppress(hide) the key-up events from the system,
  1316.                 // and the key-up for Apps key, at least in apps like Explorer, is a special event that results in
  1317.                 // the context menu appearing (though most other apps seem to use the key-down event rather than
  1318.                 // the key-up, so they would probably work okay).  Note: Of possible future use is the fact that
  1319.                 // if the Alt key is held down before pressing Appskey, it's native function does
  1320.                 // not occur.  This may be similar to the fact that LWIN and RWIN don't cause the
  1321.                 // start menu to appear if a shift key is held down.  For Win9x, leave it as a registered
  1322.                 // hotkey (as set higher above) it in case its limited capability is useful to someone.
  1323.                 case VK_APPS:
  1324.                 // Finally, the non-neutral (left-right) modifier keys (except LWin and RWin) must also
  1325.                 // be done with the hook because even if RegisterHotkey() claims to succeed on them,
  1326.                 // I'm 99% sure I tried it and the hotkeys don't actually work with that method:
  1327.                 case VK_LCONTROL: // But for Win9x(), attempt to register such hotkeys by not requiring the
  1328.                 case VK_RCONTROL: // hook here. RegisterHotkey() probably doesn't fail on Win9x, these
  1329.                 case VK_LSHIFT:   // L/RControl/Shift/Alt hotkeys don't actually function as hotkeys.
  1330.                 case VK_RSHIFT:   // However, it doesn't seem worth adding code to convert to neutral since
  1331.                 case VK_LMENU:    // that might break existing scripts.
  1332.                 case VK_RMENU:
  1333.                     mKeybdHookMandatory = true;
  1334.                     break;
  1335.  
  1336.                 // To prevent the Start Menu from appearing for a naked LWIN or RWIN, must
  1337.                 // handle this key with the hook (the presence of a normal modifier makes
  1338.                 // this unnecessary, at least under WinXP, because the Start Menu is
  1339.                 // never invoked when a modifier key is held down with lwin/rwin).
  1340.                 // But keep it NORMAL on Win9x (as set higher above) since there's a
  1341.                 // chance some people might find it useful, perhaps by doing their own
  1342.                 // workaround for the Start Menu appearing (via "Send {Ctrl}").
  1343.                 case VK_LWIN:
  1344.                 case VK_RWIN:
  1345.                 // If this hotkey is an unmodified modifier (e.g. Control::) and there
  1346.                 // are any other hotkeys that rely specifically on this modifier,
  1347.                 // have the hook handle this hotkey so that it will only fire on key-up
  1348.                 // rather than key-down.  Note: cases where this key's modifiersLR or
  1349.                 // ModifierVK/SC are non-zero -- as well as hotkeys that use sc vs. vk
  1350.                 // -- have already been set to use the keybd hook, so don't need to be
  1351.                 // handled here.  UPDATE: All the following cases have been already set
  1352.                 // to be HK_KEYBD_HOOK:
  1353.                 // - left/right ctrl/alt/shift (since RegisterHotkey() doesn't support them).
  1354.                 // - Any key with a ModifierVK/SC
  1355.                 // - The naked lwin or rwin key (due to the check above)
  1356.                 // Therefore, the only case left to be detected by this next line is the
  1357.                 // one in which the user configures the naked neutral key VK_SHIFT,
  1358.                 // VK_MENU, or VK_CONTROL.  As a safety precaution, always handle those
  1359.                 // neutral keys with the hook so that their action will only fire
  1360.                 // when the key is released (thus allowing each key to be used for its
  1361.                 // normal modifying function):
  1362.                 // If it's Win9x, this section is never reached so the hotkey is not set to be
  1363.                 // HK_KEYBD_HOOK.  This allows it to be registered as a normal hotkey in case
  1364.                 // it's of use to anyone.
  1365.                 case VK_CONTROL:
  1366.                 case VK_MENU:
  1367.                 case VK_SHIFT:
  1368.                     if (!mModifiers && !mModifiersLR) // Modifier key as suffix and has no modifiers (or only a ModifierVK/SC).
  1369.                         mKeybdHookMandatory = true;
  1370.                     //else keys modified by CTRL/SHIFT/ALT/WIN can always be registered normally because these
  1371.                     // modifiers are never used (are overridden) when that key is used as a ModifierVK
  1372.                     // for another key.  Example: if key1 is a ModifierVK for another key, ^key1
  1373.                     // (or any other modified versions of key1) can be registered as a hotkey because
  1374.                     // that doesn't affect the hook's ability to use key1 as a prefix:
  1375.                     break;
  1376.                 }
  1377.             } // if HK_TYPE_CAN_BECOME_KEYBD_HOOK(mType)
  1378.         } // Not Win9x.
  1379.  
  1380.         // Below section avoids requiring the hook on Win9x in some cases because that would effectively prevent any
  1381.         // attempt to register such hotkeys, which might work, at least for the following cases:
  1382.         // g_ForceKeybdHook (#UseHook): Seems best to ignore it on Win9x (which is probably the documented behavior).
  1383.         // mNoSuppress: They're supported as normal (non-tilde) hotkeys as long as RegisterHotkey succeeds.
  1384.         // mAllowExtraModifiers: v1.0.42: The hotkey is registered along with any explicit modifiers it might have
  1385.         //     (the wildcard part is ignored).  In prior versions, wildcard hotkeys were disabled, and so were
  1386.         //     any non-wildcard suffixes they "eclipsed" (e.g. *^a would eclipse/disable ^!a).  Both cases are
  1387.         //     now enabled in v1.0.42.
  1388.         // mModifiersLR: v1.0.42: In older versions, such hotkeys were wrongly registered without any modifiers
  1389.         //     at all.  For example, <#x would be registered simply as "x".  To correct this, it seemed best
  1390.         //     to convert left/right modifiers (include lwin/rwin since RegisterHotkey requires the neutral "Win")
  1391.         //     to their neutral counterparts so that an attempt can be made to register them.  This step is done
  1392.         //     in the code section above.
  1393.         // mModifierVK or SC: Always set to use the hook so that they're disabled on Win9x, which seems best because
  1394.         //     there's no way to even get close to full functionality on Win9x.
  1395.         // mVK && vk_to_sc(mVK, true): Its mVK corresponds to two scan codes, which would requires the hook
  1396.         //     (except Win9x). The hook is required because when the hook receives a NumpadEnter keystroke
  1397.         //     (regardless of whether NumpadEnter itself is also a hotkey), it will look up that scan code
  1398.         //     and see that it always and unconditionally takes precedence over its VK, and thus the VK is ignored.
  1399.         //     If that scan code has no hotkey, nothing happens (i.e. pressing NumpadEnter doesn't fire an Enter::
  1400.         //     hotkey; it does nothing at all unless NumpadEnter:: is also a hotkey).  By contrast, if Enter
  1401.         //     became a registered hotkey vs. hook, it would fire for both Enter and NumpadEnter unless the script
  1402.         //     happened to have a NumpadEnter hotkey also (which we don't check due to rarity).
  1403.         // aHookAction: In versions older than 1.0.42, most of these were disabled just because they happened
  1404.         //     to be mModifierVK/SC hotkeys, which are always flagged as hook and thus disabled in Win9x.
  1405.         //     However, ones that were implemented as <#x rather than "LWin & x" were inappropriately enabled.
  1406.         //     as a naked/unmodified "x" hotkey (due to mModifiersLR paragraph above, and the fact that
  1407.         //     aHookAction hotkeys really should be explicitly disabled since they can't function on Win9x.
  1408.         //     This disabling-by-setting-to-type-hook is now ALSO done in ManifestAllHotkeysHotstringsHooks() because
  1409.         //     the hotkey command is capable of changing a hotkey to/from the alt-tab type.
  1410.         // mKeyUp: Disabled as an unintended/benevolent side-effect of older loop processing in
  1411.         //     ManifestAllHotkeysHotstringsHooks().  This is retained, though now it's made more explicit via below,
  1412.         //     for maintainability.
  1413.         // Older Note: Do this for both AT_LEAST_ONE_VARIANT_HAS_TILDE and NO_SUPPRESS_PREFIX.  In the case of
  1414.         // NO_SUPPRESS_PREFIX, the hook is needed anyway since the only way to get NO_SUPPRESS_PREFIX in
  1415.         // effect is with a hotkey that has a ModifierVK/SC.
  1416.         if (HK_TYPE_CAN_BECOME_KEYBD_HOOK(mType))
  1417.             if (   (mModifiersLR || aHookAction || mKeyUp || mModifierVK || mModifierSC) // mSC is handled higher above.
  1418.                 || !g_os.IsWin9x() && (g_ForceKeybdHook || mAllowExtraModifiers // mNoSuppress&NO_SUPPRESS_PREFIX has already been handled elsewhere. Other bits in mNoSuppress must be checked later because they can change by any variants added after *this* one.
  1419.                     || (mVK && !mVK_WasSpecifiedByNumber && vk_to_sc(mVK, true)))   ) // Its mVK corresponds to two scan codes (such as "ENTER").
  1420.                 mKeybdHookMandatory = true;
  1421.             // v1.0.38.02: The check of mVK_WasSpecifiedByNumber above was added so that an explicit VK hotkey such
  1422.             // as "VK24::" (which is VK_HOME) can be handled via RegisterHotkey() vs. the hook.  Someone asked for
  1423.             // this ability, but even if it weren't for that it seems more correct to recognize an explicitly-specified
  1424.             // VK as a "neutral VK" (i.e. one that fires for both scan codes if the VK has two scan codes). The user
  1425.             // can always specify "SCnnn::" as a hotkey to avoid this fire-on-both-scan-codes behavior.
  1426.  
  1427.         // Currently, these take precedence over each other in the following order, so don't
  1428.         // just bitwise-or them together in case there's any ineffectual stuff stored in
  1429.         // the fields that have no effect (e.g. modifiers have no effect if there's a mModifierVK):
  1430.         if (mModifierVK)
  1431.             mModifiersConsolidatedLR = KeyToModifiersLR(mModifierVK);
  1432.         else if (mModifierSC)
  1433.             mModifiersConsolidatedLR = KeyToModifiersLR(0, mModifierSC);
  1434.         else
  1435.         {
  1436.             mModifiersConsolidatedLR = mModifiersLR;
  1437.             if (mModifiers)
  1438.                 mModifiersConsolidatedLR |= ConvertModifiers(mModifiers);
  1439.         }
  1440.     } // if (mType != HK_JOYSTICK)
  1441.  
  1442.     // If mKeybdHookMandatory==true, ManifestAllHotkeysHotstringsHooks() will set mType to HK_KEYBD_HOOK for us.
  1443.  
  1444.     // To avoid memory leak, this is done only when it is certain the hotkey will be created:
  1445.     if (   !(mName = aName ? SimpleHeap::Malloc(aName) : hotkey_name)
  1446.         || !(AddVariant(aJumpToLabel, aSuffixHasTilde))   ) // Too rare to worry about freeing the other if only one fails.
  1447.     {
  1448.         if (aUseErrorLevel)
  1449.             g_ErrorLevel->Assign(HOTKEY_EL_MEM);
  1450.         else
  1451.             g_script.ScriptError(ERR_OUTOFMEM);
  1452.         return;
  1453.     }
  1454.     // Above has ensured that both mFirstVariant and mLastVariant are non-NULL, so callers can rely on that.
  1455.  
  1456.     // Always assign the ID last, right before a successful return, so that the caller is notified
  1457.     // that the constructor succeeded:
  1458.     mConstructedOK = true;
  1459.     mID = aID;
  1460.     // Don't do this because the caller still needs the old/unincremented value:
  1461.     //++sHotkeyCount;  // Hmm, seems best to do this here, but revisit this sometime.
  1462. }
  1463.  
  1464.  
  1465.  
  1466. HotkeyVariant *Hotkey::FindVariant()
  1467. // Returns he address of the variant in this hotkey whose criterion matches the current #IfWin criterion.
  1468. // If no match, it returns NULL.
  1469. {
  1470.     for (HotkeyVariant *vp = mFirstVariant; vp; vp = vp->mNextVariant)
  1471.         if (vp->mHotCriterion == g_HotCriterion && (g_HotCriterion == HOT_NO_CRITERION
  1472.             || (!strcmp(vp->mHotWinTitle, g_HotWinTitle) && !strcmp(vp->mHotWinText, g_HotWinText)))) // Case insensitive.
  1473.             return vp;
  1474.     return NULL;
  1475. }
  1476.  
  1477.  
  1478.  
  1479. HotkeyVariant *Hotkey::AddVariant(Label *aJumpToLabel, bool aSuffixHasTilde)
  1480. // Returns NULL upon out-of-memory; otherwise, the address of the new variant.
  1481. // Even if aJumpToLabel is NULL, a non-NULL mJumpToLabel will be stored in each variant so that
  1482. // NULL doesn't have to be constantly checked during script runtime.
  1483. // The caller is responsible for calling ManifestAllHotkeysHotstringsHooks(), if appropriate.
  1484. {
  1485.     HotkeyVariant *vp;
  1486.     if (   !(vp = (HotkeyVariant *)SimpleHeap::Malloc(sizeof(HotkeyVariant)))   )
  1487.         return NULL;
  1488.     ZeroMemory(vp, sizeof(HotkeyVariant));
  1489.     // The following members are left at 0/NULL by the above:
  1490.     // mNextVariant
  1491.     // mExistingThreads
  1492.     // mRunAgainAfterFinished
  1493.     // mRunAgainTime
  1494.     // mPriority (default priority is always 0)
  1495.     HotkeyVariant &v = *vp;
  1496.     // aJumpToLabel can be NULL for dynamic hotkeys that are hook actions such as Alt-Tab.
  1497.     // So for maintainability and to help avg-case performance in loops, provide a non-NULL placeholder:
  1498.     v.mJumpToLabel = aJumpToLabel ? aJumpToLabel : g_script.mPlaceholderLabel;
  1499.     v.mMaxThreads = g_MaxThreadsPerHotkey;    // The values of these can vary during load-time.
  1500.     v.mMaxThreadsBuffer = g_MaxThreadsBuffer; //
  1501.     v.mHotCriterion = g_HotCriterion; // If this hotkey is an alt-tab one (mHookAction), this is stored but ignored until/unless the Hotkey command converts it into a non-alt-tab hotkey.
  1502.     v.mHotWinTitle = g_HotWinTitle;
  1503.     v.mHotWinText = g_HotWinText;  // The value of this and other globals used above can vary during load-time.
  1504.     v.mEnabled = true;
  1505.     if (aSuffixHasTilde)
  1506.     {
  1507.         v.mNoSuppress = true; // Override the false value set by ZeroMemory above.
  1508.         mNoSuppress |= AT_LEAST_ONE_VARIANT_HAS_TILDE;
  1509.         // For simplicity, make the hook mandatory for any hotkey that has at least one non-suppressed variant.
  1510.         // Otherwise, ManifestAllHotkeysHotstringsHooks() would have to do a loop to check if any
  1511.         // non-suppressed variants are actually enabled & non-suspended to decide if the hook is actually needed
  1512.         // for a hotkey that has a global variant.  Due to rarity and code size, it doesn't seem worth it.
  1513.         mKeybdHookMandatory = true;
  1514.     }
  1515.     else
  1516.         mNoSuppress |= AT_LEAST_ONE_VARIANT_LACKS_TILDE;
  1517.  
  1518.     // Update the linked list:
  1519.     if (!mFirstVariant)
  1520.         mFirstVariant = mLastVariant = vp;
  1521.     else
  1522.     {
  1523.         mLastVariant->mNextVariant = vp;
  1524.         // This must be done after the above:
  1525.         mLastVariant = vp;
  1526.     }
  1527.  
  1528.     return vp;  // Indicate success by returning the new object.
  1529. }
  1530.  
  1531.  
  1532.  
  1533. ResultType Hotkey::TextInterpret(char *aName, Hotkey *aThisHotkey, bool aUseErrorLevel)
  1534. // Returns OK or FAIL.  This function is static and aThisHotkey is passed in as a parameter
  1535. // so that aThisHotkey can be NULL. NULL signals that aName should be checked as a valid
  1536. // hotkey only rather than populating the members of the new hotkey aThisHotkey. This function
  1537. // and those it calls should avoid showing any error dialogs in validation mode.  Instead,
  1538. // it should simply return OK if aName is a valid hotkey and FAIL otherwise.
  1539. {
  1540.     // Make a copy that can be modified:
  1541.     char hotkey_name[256];
  1542.     strlcpy(hotkey_name, aName, sizeof(hotkey_name));
  1543.     char *term1 = hotkey_name;
  1544.     char *term2 = strstr(term1, COMPOSITE_DELIMITER);
  1545.     if (!term2)
  1546.         return TextToKey(TextToModifiers(term1, aThisHotkey), aName, false, aThisHotkey, aUseErrorLevel);
  1547.     if (*term1 == '~')
  1548.     {
  1549.         if (aThisHotkey)
  1550.         {
  1551.             aThisHotkey->mNoSuppress |= NO_SUPPRESS_PREFIX;
  1552.             aThisHotkey->mKeybdHookMandatory = true;
  1553.         }
  1554.         term1 = omit_leading_whitespace(term1 + 1);
  1555.     }
  1556.     char *end_of_term1 = omit_trailing_whitespace(term1, term2) + 1;
  1557.     // Temporarily terminate the string so that the 2nd term is hidden:
  1558.     char ctemp = *end_of_term1;
  1559.     *end_of_term1 = '\0';
  1560.     ResultType result = TextToKey(term1, aName, true, aThisHotkey, aUseErrorLevel);
  1561.     *end_of_term1 = ctemp;  // Undo the termination.
  1562.     if (result == FAIL)
  1563.         return FAIL;
  1564.     term2 += COMPOSITE_DELIMITER_LENGTH;
  1565.     term2 = omit_leading_whitespace(term2);
  1566.     // Even though modifiers on keys already modified by a mModifierVK are not supported, call
  1567.     // TextToModifiers() anyway to use its output (for consistency).  The modifiers it sets
  1568.     // are currently ignored because the mModifierVK takes precedence.
  1569.     return TextToKey(TextToModifiers(term2, aThisHotkey), aName, false, aThisHotkey, aUseErrorLevel);
  1570. }
  1571.  
  1572.  
  1573.  
  1574. char *Hotkey::TextToModifiers(char *aText, Hotkey *aThisHotkey, HotkeyProperties *aProperties)
  1575. // This function and those it calls should avoid showing any error dialogs when caller passes NULL for aThisHotkey.
  1576. // Takes input param <text> to support receiving only a subset of object.text.
  1577. // Returns the location in <text> of the first non-modifier key.
  1578. // Checks only the first char(s) for modifiers in case these characters appear elsewhere (e.g. +{+}).
  1579. // But come to think of it, +{+} isn't valid because + itself is already shift-equals.  So += would be
  1580. // used instead, e.g. +==action.  Similarly, all the others, except =, would be invalid as hotkeys also.
  1581. // UPDATE: On some keyboard layouts, the + key (and various others) don't require the shift key to be
  1582. // manifest.  Thus, on these systems a hotkey such as ^+:: is now supported as meaning Ctrl-Plus.
  1583. {
  1584.     // Init output parameter for caller if it gave one:
  1585.     if (aProperties)
  1586.         ZeroMemory(aProperties, sizeof(HotkeyProperties));
  1587.  
  1588.     if (!*aText)
  1589.         return aText; // Below relies on this having ensured that aText isn't blank.
  1590.  
  1591.     // Explicitly avoids initializing modifiers to 0 because the caller may have already included
  1592.     // some set some modifiers in there.
  1593.     char *marker;
  1594.     bool key_left, key_right;
  1595.  
  1596.     // Simplifies and reduces code size below:
  1597.     mod_type temp_modifiers;
  1598.     mod_type &modifiers = aProperties ? aProperties->modifiers : (aThisHotkey ? aThisHotkey->mModifiers : temp_modifiers);
  1599.     modLR_type temp_modifiersLR;
  1600.     modLR_type &modifiersLR = aProperties ? aProperties->modifiersLR: (aThisHotkey ? aThisHotkey->mModifiersLR : temp_modifiersLR);
  1601.  
  1602.     // Improved for v1.0.37.03: The loop's condition is now marker[1] vs. marker[0] so that
  1603.     // the last character is never considered a modifier.  This allows a modifier symbol
  1604.     // to double as the name of a suffix key.  It also fixes issues on layouts where the
  1605.     // symbols +^#! do not require the shift key to be held down, such as the German layout.
  1606.     //
  1607.     // Improved for v1.0.40.01: The loop's condition now stops when it reaches a single space followed
  1608.     // by the word "Up" so that hotkeys like "< up" and "+ up" are supported by seeing their '<' or '+' as
  1609.     // a key name rather than a modifier symbol.
  1610.     for (marker = aText, key_left = false, key_right = false; marker[1] && stricmp(marker + 1, " Up"); ++marker)
  1611.     {
  1612.         switch (*marker)
  1613.         {
  1614.         case '>':
  1615.             key_right = true;
  1616.             break;
  1617.         case '<':
  1618.             key_left = true;
  1619.             break;
  1620.         case '*': // On Win9x, an attempt will be made to register such hotkeys (ignoring the wildcard).
  1621.             if (aThisHotkey)
  1622.                 aThisHotkey->mAllowExtraModifiers = true;
  1623.             if (aProperties)
  1624.                 aProperties->has_asterisk = true;
  1625.             break;
  1626.         case '~':
  1627.             if (aProperties)
  1628.                 aProperties->suffix_has_tilde = true; // If this is the prefix's tilde rather than the suffix, it will be overridden later below.
  1629.             break;
  1630.         case '$':
  1631.             if (g_os.IsWin9x())
  1632.             {
  1633.                 if (aThisHotkey)
  1634.                     aThisHotkey->mUnregisterDuringThread = true;
  1635.             }
  1636.             else
  1637.                 if (aThisHotkey)
  1638.                     aThisHotkey->mKeybdHookMandatory = true; // This flag will be ignored if TextToKey() decides this is a JOYSTICK or MOUSE hotkey.
  1639.                 // else ignore the flag and try to register normally, which in most cases seems better
  1640.                 // than disabling the hotkey.
  1641.             break;
  1642.         case '!':
  1643.             if ((!key_right && !key_left))
  1644.             {
  1645.                 modifiers |= MOD_ALT;
  1646.                 break;
  1647.             }
  1648.             // Both left and right may be specified, e.g. ><+a means both shift keys must be held down:
  1649.             if (key_left)
  1650.             {
  1651.                 modifiersLR |= MOD_LALT;
  1652.                 key_left = false;
  1653.             }
  1654.             if (key_right)
  1655.             {
  1656.                 modifiersLR |= MOD_RALT;
  1657.                 key_right = false;
  1658.             }
  1659.             break;
  1660.         case '^':
  1661.             if ((!key_right && !key_left))
  1662.             {
  1663.                 modifiers |= MOD_CONTROL;
  1664.                 break;
  1665.             }
  1666.             if (key_left)
  1667.             {
  1668.                 modifiersLR |= MOD_LCONTROL;
  1669.                 key_left = false;
  1670.             }
  1671.             if (key_right)
  1672.             {
  1673.                 modifiersLR |= MOD_RCONTROL;
  1674.                 key_right = false;
  1675.             }
  1676.             break;
  1677.         case '+':
  1678.             if ((!key_right && !key_left))
  1679.             {
  1680.                 modifiers |= MOD_SHIFT;
  1681.                 break;
  1682.             }
  1683.             if (key_left)
  1684.             {
  1685.                 modifiersLR |= MOD_LSHIFT;
  1686.                 key_left = false;
  1687.             }
  1688.             if (key_right)
  1689.             {
  1690.                 modifiersLR |= MOD_RSHIFT;
  1691.                 key_right = false;
  1692.             }
  1693.             break;
  1694.         case '#':
  1695.             if ((!key_right && !key_left))
  1696.             {
  1697.                 modifiers |= MOD_WIN;
  1698.                 break;
  1699.             }
  1700.             if (key_left)
  1701.             {
  1702.                 modifiersLR |= MOD_LWIN;
  1703.                 key_left = false;
  1704.             }
  1705.             if (key_right)
  1706.             {
  1707.                 modifiersLR |= MOD_RWIN;
  1708.                 key_right = false;
  1709.             }
  1710.             break;
  1711.         default:
  1712.             goto break_loop; // Stop immediately whenever a non-modifying char is found.
  1713.         } // switch (*marker)
  1714.     } // for()
  1715. break_loop:
  1716.  
  1717.     // Now *marker is the start of the key's name.  In addition, one of the following is now true:
  1718.     // 1) marker[0] is a non-modifier symbol; that is, the loop stopped because it found the first non-modifier symbol.
  1719.     // 2) marker[1] is '\0'; that is, the loop stopped because it reached the next-to-last char (the last char itself is never a modifier; e.g. ^+ is Ctrl+Plus on some keyboard layouts).
  1720.     // 3) marker[1] is the start of the string " Up", in which case marker[0] is considered the suffix key even if it happens to be a modifier symbol (see comments at for-loop's control stmt).
  1721.     if (aProperties)
  1722.     {
  1723.         // When caller passes non-NULL aProperties, it didn't omit the prefix portion of a composite hotkey
  1724.         // (e.g. the "a & " part of "a & b" is present).  So parse these and all other types of hotkeys when in this mode.
  1725.         char *composite, *temp;
  1726.         if (composite = strstr(marker, COMPOSITE_DELIMITER))
  1727.         {
  1728.             strlcpy(aProperties->prefix_text, marker, sizeof(aProperties->prefix_text)); // Protect against overflow case script ultra-long (and thus invalid) key name.
  1729.             if (temp = strstr(aProperties->prefix_text, COMPOSITE_DELIMITER)) // Check again in case it tried to overflow.
  1730.                 omit_trailing_whitespace(aProperties->prefix_text, temp)[1] = '\0'; // Truncate prefix_text so that the suffix text is omitted.
  1731.             composite = omit_leading_whitespace(composite + COMPOSITE_DELIMITER_LENGTH);
  1732.             if (aProperties->suffix_has_tilde = (*composite == '~')) // Override any value of suffix_has_tilde set higher above.
  1733.                 ++composite; // For simplicity, no skipping of leading whitespace between tilde and the suffix key name.
  1734.             strlcpy(aProperties->suffix_text, composite, sizeof(aProperties->suffix_text)); // Protect against overflow case script ultra-long (and thus invalid) key name.
  1735.         }
  1736.         else // A normal (non-composite) hotkey, so suffix_has_tilde was already set properly (higher above).
  1737.             strlcpy(aProperties->suffix_text, omit_leading_whitespace(marker), sizeof(aProperties->suffix_text)); // Protect against overflow case script ultra-long (and thus invalid) key name.
  1738.         if (temp = strcasestr(aProperties->suffix_text, " Up")) // Should be reliable detection method because leading spaces have been omitted and it's unlikely a legitmate key name will ever contain a space followed by "Up".
  1739.         {
  1740.             omit_trailing_whitespace(aProperties->suffix_text, temp)[1] = '\0'; // Omit " Up" from suffix_text since caller wants that.
  1741.             aProperties->is_key_up = true; // Override the default set earlier.
  1742.         }
  1743.     }
  1744.     return marker;
  1745. }
  1746.  
  1747.  
  1748.  
  1749. ResultType Hotkey::TextToKey(char *aText, char *aHotkeyName, bool aIsModifier, Hotkey *aThisHotkey, bool aUseErrorLevel)
  1750. // This function and those it calls should avoid showing any error dialogs when caller passes aUseErrorLevel==true or
  1751. // NULL for aThisHotkey (however, there is at least one exception explained in comments below where it occurs).
  1752. // Caller must ensure that aText is a modifiable string.
  1753. // Takes input param aText to support receiving only a subset of mName.
  1754. // In private members, sets the values of vk/sc or ModifierVK/ModifierSC depending on aIsModifier.
  1755. // It may also merge new modifiers into the existing value of modifiers, so the caller
  1756. // should never reset modifiers after calling this.
  1757. // Returns OK or FAIL.
  1758. {
  1759.     char error_text[512];
  1760.     vk_type temp_vk; // No need to initialize this one.
  1761.     sc_type temp_sc = 0;
  1762.     modLR_type modifiersLR = 0;
  1763.     bool is_mouse = false;
  1764.     int joystick_id;
  1765.  
  1766.     HotkeyTypeType hotkey_type_temp;
  1767.     HotkeyTypeType &hotkey_type = aThisHotkey ? aThisHotkey->mType : hotkey_type_temp; // Simplifies and reduces code size below.
  1768.  
  1769.     if (!aIsModifier)
  1770.     {
  1771.         // Previous steps should make it unnecessary to call omit_leading_whitespace(aText).
  1772.         char *cp = StrChrAny(aText, " \t"); // Find first space or tab.
  1773.         if (cp && !stricmp(omit_leading_whitespace(cp), "Up"))
  1774.         {
  1775.             // This is a key-up hotkey, such as "Ctrl Up::".
  1776.             if (aThisHotkey)
  1777.                 aThisHotkey->mKeyUp = true;
  1778.             *cp = '\0'; // Terminate at the first space so that the word "up" is removed from further consideration by us and callers.
  1779.         }
  1780.     }
  1781.  
  1782.     if (temp_vk = TextToVK(aText, &modifiersLR, true)) // Assign.
  1783.     {
  1784.         if (aIsModifier)
  1785.         {
  1786.             if (temp_vk == VK_WHEEL_DOWN || temp_vk == VK_WHEEL_UP)
  1787.             {
  1788.                 if (aUseErrorLevel)
  1789.                     g_ErrorLevel->Assign(HOTKEY_EL_UNSUPPORTED_PREFIX);
  1790.                 else
  1791.                 {
  1792.                     // In this case, aThisHotkey is NOT checked because it seems better to yield a double
  1793.                     // syntax error at load-time (once for Hotkey failure and again for "unrecognized action"
  1794.                     // than to show only the generic error message.  Note that the Hotkey command (at runtime)
  1795.                     // also uses the below to show a single error dialog.
  1796.                     snprintf(error_text, sizeof(error_text), "\"%s\" is not allowed as a prefix key.", aText);
  1797.                     if (g_script.mIsReadyToExecute) // Dynamically registered via the Hotkey command.
  1798.                     {
  1799.                         snprintfcat(error_text, sizeof(error_text), ERR_ABORT);
  1800.                         g_script.ScriptError(error_text);
  1801.                     }
  1802.                     else
  1803.                         MsgBox(error_text);
  1804.                 }
  1805.                 return FAIL;
  1806.             }
  1807.         }
  1808.         else
  1809.             // This is done here rather than at some later stage because we have access to the raw
  1810.             // name of the suffix key (with any leading modifiers such as ^ omitted from the beginning):
  1811.             if (aThisHotkey)
  1812.                 aThisHotkey->mVK_WasSpecifiedByNumber = !strnicmp(aText, "VK", 2);
  1813.         is_mouse = IsMouseVK(temp_vk);
  1814.         if (modifiersLR & (MOD_LSHIFT | MOD_RSHIFT))
  1815.             if (temp_vk >= 'A' && temp_vk <= 'Z')  // VK of an alpha char is the same as the ASCII code of its uppercase version.
  1816.                 modifiersLR &= ~(MOD_LSHIFT | MOD_RSHIFT);
  1817.                 // Above: Making alpha chars case insensitive seems much more friendly.  In other words,
  1818.                 // if the user defines ^Z as a hotkey, it will really be ^z, not ^+z.  By removing SHIFT
  1819.                 // from the modifiers here, we're only removing it from our modifiers, not the global
  1820.                 // modifiers that have already been set elsewhere for this key (e.g. +Z will still be +z).
  1821.     }
  1822.     else // No virtual key was found.  Is there a scan code?
  1823.         if (   !(temp_sc = TextToSC(aText))   )
  1824.             if (   !(temp_sc = (sc_type)ConvertJoy(aText, &joystick_id, true))   )  // Is there a joystick control/button?
  1825.             {
  1826.                 if (aUseErrorLevel)
  1827.                     // Tempting to store the name of the invalid key in ErrorLevel, but because it might
  1828.                     // be really long, it seems best not to.  Another reason is that the keyname could
  1829.                     // conceivably be the same as one of the other/reserved ErrorLevel numbers.
  1830.                     g_ErrorLevel->Assign(HOTKEY_EL_INVALID_KEYNAME);
  1831.                 else if (aThisHotkey)
  1832.                 {
  1833.                     // If it fails while aThisHotkey!=NULL, that should mean that this was called as
  1834.                     // a result of the Hotkey command rather than at loadtime.  This is because at 
  1835.                     // loadtime, the first call here (for validation, i.e. aThisHotkey==NULL) should have
  1836.                     // caught the error and converted the line into a non-hotkey (command), which in turn
  1837.                     // would make loadtime's second call to create the hotkey always succeed. Also, it's
  1838.                     // more appropriate to say "key name" than "hotkey" in this message because it's only
  1839.                     // showing the one bad key name when it's a composite hotkey such as "Capslock & y".
  1840.                     snprintf(error_text, sizeof(error_text), "\"%s\" is not a valid key name." ERR_ABORT, aText);
  1841.                     g_script.ScriptError(error_text);
  1842.                 }
  1843.                 //else do not show an error in this case because the loader will attempt to interpret
  1844.                 // this line as a commmand.  If that too fails, it will show an "unrecognized action"
  1845.                 // dialog.
  1846.                 return FAIL;
  1847.             }
  1848.             else
  1849.             {
  1850.                 ++sJoyHotkeyCount;
  1851.                 hotkey_type = HK_JOYSTICK;
  1852.                 temp_vk = (vk_type)joystick_id;  // 0 is the 1st joystick, 1 the 2nd, etc.
  1853.                 sJoystickHasHotkeys[joystick_id] = true;
  1854.             }
  1855.  
  1856.  
  1857. /*
  1858. If ever do this, be sure to avoid doing it for keys that must be tracked by scan code (e.g. those in the
  1859. scan code array).
  1860.     if (!temp_vk && !is_mouse)  // sc must be non-zero or else it would have already returned above.
  1861.         if (temp_vk = sc_to_vk(temp_sc))
  1862.         {
  1863.             snprintf(error_text, sizeof(error_text), "DEBUG: \"%s\" (scan code %X) was successfully mapped to virtual key %X", text, temp_sc, temp_vk);
  1864.             MsgBox(error_text);
  1865.             temp_sc = 0; // Maybe set this just for safety, even though a non-zero vk should always take precedence over it.
  1866.         }
  1867. */
  1868.     if (is_mouse)
  1869.         hotkey_type = HK_MOUSE_HOOK;
  1870.  
  1871.     if (aIsModifier)
  1872.     {
  1873.         if (aThisHotkey)
  1874.         {
  1875.             aThisHotkey->mModifierVK = temp_vk;
  1876.             aThisHotkey->mModifierSC = temp_sc;
  1877.         }
  1878.     }
  1879.     else
  1880.     {
  1881.         if (aThisHotkey)
  1882.         {
  1883.             aThisHotkey->mVK = temp_vk;
  1884.             aThisHotkey->mSC = temp_sc;
  1885.             // Turn on any additional modifiers.  e.g. SHIFT to realize '#'.
  1886.             // Fix for v1.0.37.03: To avoid using the keyboard hook for something like "+::", which in
  1887.             // turn would allow the hotkey fire only for LShift+Equals rather than RShift+Equals, convert
  1888.             // modifiers from left-right to neutral.  But exclude right-side modifiers (except RWin) so that
  1889.             // things like AltGr are more precisely handled (the implications of this policy could use
  1890.             // further review).  Currently, right-Alt (via AltGr) is the only possible right-side key.
  1891.             aThisHotkey->mModifiers |= ConvertModifiersLR(modifiersLR & (MOD_RWIN|MOD_LWIN|MOD_LCONTROL|MOD_LALT|MOD_LSHIFT));
  1892.             aThisHotkey->mModifiersLR |= (modifiersLR & (MOD_RSHIFT|MOD_RALT|MOD_RCONTROL)); // Not MOD_RWIN since it belongs above.
  1893.         }
  1894.     }
  1895.     return OK;
  1896. }
  1897.  
  1898.  
  1899.  
  1900. ResultType Hotkey::Register()
  1901. // Returns OK or FAIL.  Caller is responsible for having checked whether the hotkey is suspended or disabled.
  1902. {
  1903.     if (mIsRegistered)
  1904.         return OK;  // It's normal for a disabled hotkey to return OK.
  1905.  
  1906.     if (mType != HK_NORMAL) // Caller normally checks this for performance, but it's checked again for maintainability.
  1907.         return FAIL; // Don't attempt to register joystick or hook hotkeys, since their VK/modifiers aren't intended for that.
  1908.  
  1909.     // Indicate that the key modifies itself because RegisterHotkey() requires that +SHIFT,
  1910.     // for example, be used to register the naked SHIFT key.  So what we do here saves the
  1911.     // user from having to specify +SHIFT in the script:
  1912.     mod_type modifiers_to_register = mModifiers;
  1913.     switch (mVK)
  1914.     {
  1915.     case VK_LWIN:
  1916.     case VK_RWIN: modifiers_to_register |= MOD_WIN; break;
  1917.     case VK_CONTROL: modifiers_to_register |= MOD_CONTROL; break;
  1918.     case VK_SHIFT: modifiers_to_register |= MOD_SHIFT; break;
  1919.     case VK_MENU: modifiers_to_register |= MOD_ALT; break;
  1920.     }
  1921.  
  1922.     // Must register them to our main window (i.e. don't use NULL to indicate our thread),
  1923.     // otherwise any modal dialogs, such as MessageBox(), that call DispatchMessage()
  1924.     // internally wouldn't be able to find anyone to send hotkey messages to, so they
  1925.     // would probably be lost:
  1926.     if (mIsRegistered = RegisterHotKey(g_hWnd, mID, modifiers_to_register, mVK))
  1927.         return OK;
  1928.     return FAIL;
  1929.     // Above: On failure, reset the modifiers in case this function changed them.  This is
  1930.     // done in case this hotkey will now be handled by the hook, which doesn't want any
  1931.     // extra modifiers that were added above.
  1932.     // UPDATE: In v1.0.42, the mModifiers value is never changed here because a hotkey that
  1933.     // gets registered here one time might fail to be registered someother time (perhaps due
  1934.     // to Suspend, followed by some other app taking ownership that hotkey, followed by
  1935.     // de-suspend, which would then have a hotkey with the wrong modifiers in it for the hook.
  1936.  
  1937.     // Really old method:
  1938.     //if (   !(mIsRegistered = RegisterHotKey(NULL, id, modifiers, vk))   )
  1939.     //    // If another app really is already using this hotkey, there's no point in trying to have the keyboard
  1940.     //    // hook try to handle it instead because registered hotkeys take precedence over keyboard hook.
  1941.     //    // UPDATE: For WinNT/2k/XP, this warning can be disabled because registered hotkeys can be
  1942.     //    // overridden by the hook.  But something like this is probably needed for Win9x:
  1943.     //    char error_text[MAX_EXEC_STRING];
  1944.     //    snprintf(error_text, sizeof(error_text), "RegisterHotKey() of hotkey \"%s\" (id=%d, virtual key=%d, modifiers=%d) failed,"
  1945.     //        " perhaps because another application (or Windows itself) is already using it."
  1946.     //        "  You could try adding the line \"%s, On\" prior to its line in the script."
  1947.     //        , text, id, vk, modifiers, g_cmd[CMD_FORCE_KEYBD_HOOK]);
  1948.     //    MsgBox(error_text);
  1949.     //    return FAIL;
  1950.     //return 1;
  1951. }
  1952.  
  1953.  
  1954.  
  1955. ResultType Hotkey::Unregister()
  1956. // Returns OK or FAIL.
  1957. {
  1958.     if (!mIsRegistered)
  1959.         return OK;
  1960.     // Don't report any errors in here, at least not when we were called in conjunction
  1961.     // with cleanup and exit.  Such reporting might cause an infinite loop, leading to
  1962.     // a stack overflow if the reporting itself encounters an error and tries to exit,
  1963.     // which in turn would call us again:
  1964.     if (mIsRegistered = !UnregisterHotKey(g_hWnd, mID))  // I've see it fail in one rare case.
  1965.         return FAIL;
  1966.     return OK;
  1967. }
  1968.  
  1969.  
  1970.  
  1971. void Hotkey::InstallKeybdHook()
  1972. // Caller must ensure that sWhichHookNeeded and sWhichHookAlways contain HOOK_MOUSE, if appropriate.
  1973. // Generally, this is achieved by having ensured that ManifestAllHotkeysHotstringsHooks() was called at least
  1974. // once since the last time any hotstrings or hotkeys were disabled/enabled/changed, but in any case at least
  1975. // once since the program started.
  1976. {
  1977.     sWhichHookNeeded |= HOOK_KEYBD;
  1978.     if (!g_KeybdHook)
  1979.         ChangeHookState(shk, sHotkeyCount, sWhichHookNeeded, sWhichHookAlways);
  1980. }
  1981.  
  1982.  
  1983.  
  1984. void Hotkey::InstallMouseHook()
  1985. // Same comment as for InstallKeybdHook() above.
  1986. {
  1987.     sWhichHookNeeded |= HOOK_MOUSE;
  1988.     if (!g_MouseHook)
  1989.         ChangeHookState(shk, sHotkeyCount, sWhichHookNeeded, sWhichHookAlways);
  1990. }
  1991.  
  1992.  
  1993.  
  1994. Hotkey *Hotkey::FindHotkeyByTrueNature(char *aName, bool &aSuffixHasTilde)
  1995. // Returns the address of the hotkey if found, NULL otherwise.
  1996. // In v1.0.42, it tries harder to find a match so that the order of modifier symbols doesn't affect the true nature of a hotkey.
  1997. // For example, ^!c should be the same as !^c, primarily because RegisterHotkey() and the hook would consider them the same.
  1998. // Although this may break a few existing scripts that rely on the old behavior for obscure uses such as dynamically enabling
  1999. // a duplicate hotkey via the Hotkey command, the #IfWin directives should make up for that in most cases.
  2000. // Primary benefits to the above:
  2001. // 1) Catches script bugs, such as unintended duplicates.
  2002. // 2) Allows a script to use the Hotkey command more precisely and with greater functionality.
  2003. // 3) Allows hotkey variants to work correctly even when the order of modifiers varies.  For example, if ^!c is a hotkey that fires
  2004. //    only when window type 1 is active and !^c (reversed modifiers) is a hotkey that firesly only when window type 2 is active,
  2005. //    one of them would never fire because the hook isn't capable or storing two hotkey IDs for the same combination of
  2006. //    modifiers+VK/SC.
  2007. {
  2008.     HotkeyProperties prop_candidate, prop_existing;
  2009.     TextToModifiers(aName, NULL, &prop_candidate);
  2010.     aSuffixHasTilde = prop_candidate.suffix_has_tilde; // Set for caller.
  2011.     // Both suffix_has_tilde and a hypothetical prefix_has_tilde are ignored during dupe-checking below.
  2012.     // See comments inside the loop for details.
  2013.  
  2014.     for (int i = 0; i < sHotkeyCount; ++i)
  2015.     {
  2016.         TextToModifiers(shk[i]->mName, NULL, &prop_existing);
  2017.         if (   prop_existing.modifiers == prop_candidate.modifiers
  2018.             && prop_existing.modifiersLR == prop_candidate.modifiersLR
  2019.             && prop_existing.is_key_up == prop_candidate.is_key_up
  2020.             // Treat wildcard (*) as an entirely separate hotkey from one without a wildcard.  This is because
  2021.             // the hook has special handling for wildcards that allow non-wildcard hotkeys that overlap them to
  2022.             // take precedence, sort of like "clip children".  The logic that builds the eclipsing array would
  2023.             // need to be redesigned, which might not even be possible given the complexity of interactions
  2024.             // between variant-precedence and hotkey/wildcard-precedence.
  2025.             // By contrast, in v1.0.44 pass-through (~) is considered an attribute of each variant of a
  2026.             // particular hotkey, not something that makes an entirely new hotkey.
  2027.             // This was done because the old method of having them distinct appears to have only one advantage:
  2028.             // the ability to dynamically enable/disable ~x separately from x (since if both were in effect
  2029.             // simultaneously, one would override the other due to two different hotkey IDs competing for the same
  2030.             // ID slot within the VK/SC hook arrays).  The advantages of allowing tilde to be a per-variant attribute
  2031.             // seem substantial, namely to have some variant/siblings pass-through while others do not.
  2032.             && prop_existing.has_asterisk == prop_candidate.has_asterisk
  2033.             // v1.0.43.05: Use stricmp not lstrcmpi so that the higher ANSI letters because an uppercase
  2034.             // high ANSI letter isn't necessarily produced by holding down the shift key and pressing the
  2035.             // lowercase letter.  In addition, it preserves backward compatibility and may improve flexibility.
  2036.             && !stricmp(prop_existing.prefix_text, prop_candidate.prefix_text)
  2037.             && !stricmp(prop_existing.suffix_text, prop_candidate.suffix_text)   )
  2038.             return shk[i]; // Match found.
  2039.     }
  2040.  
  2041.     return NULL;  // No match found.
  2042. }
  2043.  
  2044.  
  2045.  
  2046. Hotkey *Hotkey::FindHotkeyContainingModLR(modLR_type aModifiersLR) // , HotkeyIDType hotkey_id_to_omit)
  2047. // Returns the address of the hotkey if found, NULL otherwise.
  2048. // Find the first hotkey whose modifiersLR contains *any* of the modifiers shows in the parameter value.
  2049. // Obsolete: The caller tells us the ID of the hotkey to omit from the search because that one
  2050. // would always be found (since something like "lcontrol=calc.exe" in the script
  2051. // would really be defines as  "<^control=calc.exe".
  2052. // Note: By intent, this function does not find hotkeys whose normal/neutral modifiers
  2053. // contain <modifiersLR>.
  2054. {
  2055.     if (!aModifiersLR)
  2056.         return NULL;
  2057.     for (int i = 0; i < sHotkeyCount; ++i)
  2058.         // Bitwise set-intersection: indicates if anything in common:
  2059.         if (shk[i]->mModifiersLR & aModifiersLR)
  2060.         //if (i != hotkey_id_to_omit && shk[i]->mModifiersLR & modifiersLR)
  2061.             return shk[i];
  2062.     return NULL;  // No match found.
  2063. }
  2064.  
  2065.  
  2066. //Hotkey *Hotkey::FindHotkeyWithThisModifier(vk_type aVK, sc_type aSC)
  2067. //// Returns the address of the hotkey if found, NULL otherwise.
  2068. //// Answers the question: What is the first hotkey with mModifierVK or mModifierSC equal to those given?
  2069. //// A non-zero vk param will take precendence over any non-zero value for sc.
  2070. //{
  2071. //    if (!aVK & !aSC)
  2072. //        return NULL;
  2073. //    for (int i = 0; i < sHotkeyCount; ++i)
  2074. //        if (   (aVK && aVK == shk[i]->mModifierVK) || (aSC && aSC == shk[i]->mModifierSC)   )
  2075. //            return shk[i];
  2076. //    return NULL;  // No match found.
  2077. //}
  2078. //
  2079. //
  2080. //
  2081. //Hotkey *Hotkey::FindHotkeyBySC(sc2_type aSC2, mod_type aModifiers, modLR_type aModifiersLR)
  2082. //// Returns the address of the hotkey if found, NULL otherwise.
  2083. //// Answers the question: What is the first hotkey with the given sc & modifiers *regardless* of
  2084. //// any non-zero mModifierVK or mModifierSC it may have?  The mModifierSC/vk is ignored because
  2085. //// the caller wants to know whether this key would be blocked if its counterpart were registered.
  2086. //// For example, the hook wouldn't see "MEDIA_STOP & NumpadENTER" at all if NumPadENTER was
  2087. //// already registered via RegisterHotkey(), since RegisterHotkey() doesn't honor any modifiers
  2088. //// other than the standard ones.
  2089. //{
  2090. //    for (int i = 0; i < sHotkeyCount; ++i)
  2091. //        if (!shk[i]->mVK && (shk[i]->mSC == aSC2.a || shk[i]->mSC == aSC2.b))
  2092. //            if (shk[i]->mModifiers == aModifiers && shk[i]->mModifiersLR == aModifiersLR)  // Ensures an exact match.
  2093. //                return shk[i];
  2094. //    return NULL;  // No match found.
  2095. //}
  2096.  
  2097.  
  2098.  
  2099. char *Hotkey::ListHotkeys(char *aBuf, int aBufSize)
  2100. // Translates this script's list of variables into text equivalent, putting the result
  2101. // into aBuf and returning the position in aBuf of its new string terminator.
  2102. {
  2103.     char *aBuf_orig = aBuf;
  2104.     // Save vertical space by limiting newlines here:
  2105.     aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, "Type\tOff?\tRunning\tName\r\n"
  2106.                              "-------------------------------------------------------------------\r\n");
  2107.     // Start at the oldest and continue up through the newest:
  2108.     for (int i = 0; i < sHotkeyCount; ++i)
  2109.         aBuf = shk[i]->ToText(aBuf, BUF_SPACE_REMAINING, true);
  2110.     return aBuf;
  2111. }
  2112.  
  2113.  
  2114.  
  2115. char *Hotkey::ToText(char *aBuf, int aBufSize, bool aAppendNewline)
  2116. // aBufSize is an int so that any negative values passed in from caller are not lost.
  2117. // Caller has ensured that aBuf isn't NULL.
  2118. // Translates this var into its text equivalent, putting the result into aBuf and
  2119. // returning the position in aBuf of its new string terminator.
  2120. {
  2121.     char *aBuf_orig = aBuf;
  2122.  
  2123.     HotkeyVariant *vp;
  2124.     int existing_threads;
  2125.     for (existing_threads = 0, vp = mFirstVariant; vp; vp = vp->mNextVariant)
  2126.         existing_threads += vp->mExistingThreads;
  2127.  
  2128.     char existing_threads_str[128];
  2129.     if (existing_threads)
  2130.         _itoa(existing_threads, existing_threads_str, 10);
  2131.     else
  2132.         *existing_threads_str = '\0'; // Make it blank to avoid clutter in the hotkey display.
  2133.  
  2134.     char htype[32];
  2135.     switch (mType)
  2136.     {
  2137.     case HK_NORMAL: strcpy(htype, "reg"); break;
  2138.     case HK_KEYBD_HOOK: strcpy(htype, "k-hook"); break;
  2139.     case HK_MOUSE_HOOK: strcpy(htype, "m-hook"); break;
  2140.     case HK_BOTH_HOOKS: strcpy(htype, "2-hooks"); break;
  2141.     case HK_JOYSTICK: strcpy(htype, "joypoll"); break;
  2142.     default: *htype = '\0'; // For maintainability; should never happen.
  2143.     }
  2144.  
  2145.     char *enabled_str;
  2146.     if (IsCompletelyDisabled()) // Takes into account alt-tab vs. non-alt-tab, etc.
  2147.         enabled_str = "OFF";
  2148.     else if (mHookAction && mParentEnabled) // It's completely "on" in this case.
  2149.         enabled_str = "";
  2150.     else // It's on or if all individual variants are on, otherwise it's partial.
  2151.     {
  2152.         // Set default: Empty string means "ON" because it reduces clutter in the displayed list.
  2153.         for (enabled_str = "", vp = mFirstVariant; vp; vp = vp->mNextVariant)
  2154.             if (!vp->mEnabled)
  2155.             {
  2156.                 enabled_str = "PART";
  2157.                 break;
  2158.             }
  2159.     }
  2160.  
  2161.     aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, "%s%s\t%s\t%s\t%s"
  2162.         , htype, (mType == HK_NORMAL && !mIsRegistered) ? "(no)" : ""
  2163.         , enabled_str
  2164.         , existing_threads_str
  2165.         , mName);
  2166.     if (aAppendNewline && BUF_SPACE_REMAINING >= 2)
  2167.     {
  2168.         *aBuf++ = '\r';
  2169.         *aBuf++ = '\n';
  2170.         *aBuf = '\0';
  2171.     }
  2172.     return aBuf;
  2173. }
  2174.  
  2175.  
  2176. ///////////////
  2177. // Hot Strings
  2178. ///////////////
  2179.  
  2180. // Init static variables:
  2181. Hotstring **Hotstring::shs = NULL;
  2182. HotstringIDType Hotstring::sHotstringCount = 0;
  2183. HotstringIDType Hotstring::sHotstringCountMax = 0;
  2184. bool Hotstring::mAtLeastOneEnabled = false;
  2185.  
  2186.  
  2187. void Hotstring::SuspendAll(bool aSuspend)
  2188. {
  2189.     if (sHotstringCount < 1) // At least one part below relies on this check.
  2190.         return;
  2191.  
  2192.     UINT u;
  2193.     if (aSuspend) // Suspend all those that aren't exempt.
  2194.     {
  2195.         for (mAtLeastOneEnabled = false, u = 0; u < sHotstringCount; ++u)
  2196.             if (shs[u]->mJumpToLabel->IsExemptFromSuspend())
  2197.                 mAtLeastOneEnabled = true;
  2198.             else
  2199.                 shs[u]->mSuspended = true;
  2200.     }
  2201.     else // Unsuspend all.
  2202.     {
  2203.         for (u = 0; u < sHotstringCount; ++u)
  2204.             shs[u]->mSuspended = false;
  2205.         // v1.0.44.08: Added the following section.  Also, the HS buffer is reset, but only when hotstrings
  2206.         // are newly enabled after having been entirely disabled.  This is because CollectInput() would not
  2207.         // have been called in a long time, making the contents of g_HSBuf obsolete, which in turn might
  2208.         // otherwise cause accidental firings based on old keystrokes coupled with new ones.
  2209.         if (!mAtLeastOneEnabled)
  2210.         {
  2211.             mAtLeastOneEnabled = true; // sHotstringCount was already checked higher above.
  2212.             *g_HSBuf = '\0';
  2213.             g_HSBufLength = 0;
  2214.         }
  2215.     }
  2216. }
  2217.  
  2218.  
  2219.  
  2220. ResultType Hotstring::Perform()
  2221. // Returns OK or FAIL.  Caller has already ensured that the backspacing (if specified by mDoBackspace)
  2222. // has been done.
  2223. {
  2224.     if (mExistingThreads >= mMaxThreads && !ACT_IS_ALWAYS_ALLOWED(mJumpToLabel->mJumpToLine->mActionType))
  2225.         return FAIL;
  2226.     // See Hotkey::Perform() for details about this.  For hot strings -- which also use the
  2227.     // g_script.mThisHotkeyStartTime value to determine whether g_script.mThisHotkeyModifiersLR
  2228.     // is still timely/accurate -- it seems best to set to "no modifiers":
  2229.     g_script.mThisHotkeyModifiersLR = 0;
  2230.     ++mExistingThreads;  // This is the thread count for this particular hotstring only.
  2231.     ResultType result = mJumpToLabel->Execute();
  2232.     --mExistingThreads;
  2233.     return result ? OK : FAIL;    // Return OK on all non-failure results.
  2234. }
  2235.  
  2236.  
  2237.  
  2238. void Hotstring::DoReplace(LPARAM alParam)
  2239. // LOWORD(alParam) is the char from the set of EndChars that the user had to press to trigger the hotkey.
  2240. // This is not applicable if mEndCharRequired is false, in which case caller should have passed zero.
  2241. {
  2242.     // The below buffer allows room for the longest replacement text plus MAX_HOTSTRING_LENGTH for the
  2243.     // optional backspaces, +10 for the possible presence of {Raw} and a safety margin.
  2244.     char SendBuf[LINE_SIZE + MAX_HOTSTRING_LENGTH + 10] = "";
  2245.     char *start_of_replacement = SendBuf;  // Set default.
  2246.  
  2247.     if (mDoBackspace)
  2248.     {
  2249.         // Subtract 1 from backspaces because the final key pressed by the user to make a
  2250.         // match was already suppressed by the hook (it wasn't sent through to the active
  2251.         // window).  So what we do is backspace over all the other keys prior to that one,
  2252.         // put in the replacement text (if applicable), then send the EndChar through
  2253.         // (if applicable) to complete the sequence.
  2254.         int backspace_count = mStringLength - 1;
  2255.         if (mEndCharRequired)
  2256.             ++backspace_count;
  2257.         if (!mEndCharRequired && LOWORD(alParam)) // Added for v1.0.44.09 as a dead key fix (see CollectInput()).
  2258.             --backspace_count;
  2259.         for (int i = 0; i < backspace_count; ++i)
  2260.             *start_of_replacement++ = '\b';  // Use raw backspaces, not {BS n}, in case the send will be raw.
  2261.         *start_of_replacement = '\0'; // Terminate the string created above.
  2262.     }
  2263.  
  2264.     if (*mReplacement)
  2265.     {
  2266.         strcpy(start_of_replacement, mReplacement);
  2267.         CaseConformModes case_conform_mode = (CaseConformModes)HIWORD(alParam);
  2268.         if (case_conform_mode == CASE_CONFORM_ALL_CAPS)
  2269.             CharUpper(start_of_replacement);
  2270.         else if (case_conform_mode == CASE_CONFORM_FIRST_CAP)
  2271.             *start_of_replacement = (char)ltoupper(*start_of_replacement);
  2272.     }
  2273.  
  2274.     if (*mReplacement && !mOmitEndChar) // The ending character (if present) needs to be sent too.
  2275.     {
  2276.         // Send the final character in raw mode so that chars such as !{} are sent properly.
  2277.         // v1.0.43: Avoid two separate calls to SendKeys because:
  2278.         // 1) It defeats the uninterruptibility of the hotstring's replacement by allowing the user's
  2279.         //    buffered keystrokes to take effect in between the two calls to SendKeys.
  2280.         // 2) Performance: Avoids having to install the playback hook twice, etc.
  2281.         char end_char;
  2282.         if (mEndCharRequired && (end_char = (char)LOWORD(alParam))) // Must now check mEndCharRequired because LOWORD has been overloaded with context-sensitive meanings.
  2283.         {
  2284.             start_of_replacement += strlen(start_of_replacement);
  2285.             sprintf(start_of_replacement, "%s%c", mSendRaw ? "" : "{Raw}", (char)end_char); // v1.0.43.02: Don't send "{Raw}" if already in raw mode!
  2286.         }
  2287.     }
  2288.  
  2289.     if (!*SendBuf) // No keys to send.
  2290.         return;
  2291.  
  2292.     // For the following, mSendMode isn't checked because the backup/restore is needed to varying extents
  2293.     // by every mode.
  2294.     int old_delay = g.KeyDelay;
  2295.     int old_press_duration = g.PressDuration;
  2296.     int old_delay_play = g.KeyDelayPlay;
  2297.     int old_press_duration_play = g.PressDurationPlay;
  2298.     g.KeyDelay = mKeyDelay; // This is relatively safe since SendKeys() normally can't be interrupted by a new thread.
  2299.     g.PressDuration = -1;   // Always -1, since Send command can be used in body of hotstring to have a custom press duration.
  2300.     g.KeyDelayPlay = -1;
  2301.     g.PressDurationPlay = mKeyDelay; // Seems likely to be more useful (such as in games) to apply mKeyDelay to press duration rather than above.
  2302.  
  2303.     // v1.0.43: The following section gives time for the hook to pass the final keystroke of the hotstring to the
  2304.     // system.  This is necessary only for modes other than the original/SendEvent mode because that one takes
  2305.     // advantage of the serialized nature of the keyboard hook to ensure the user's final character always appears
  2306.     // on screen before the replacement text can appear.
  2307.     // By contrast, when the mode is SendPlay (and less frequently, SendInput), the system and/or hook needs
  2308.     // another timeslice to ensure that AllowKeyToGoToSystem() actually takes effect on screen (SuppressThisKey()
  2309.     // doesn't seem to have this problem).
  2310.     if (!(mDoBackspace || mOmitEndChar) && mSendMode != SM_EVENT) // The final character of the abbreviation (or its EndChar) was not suppressed by the hook.
  2311.         Sleep(0);
  2312.  
  2313.     SendKeys(SendBuf, mSendRaw, mSendMode); // Send the backspaces and/or replacement.
  2314.  
  2315.     g.KeyDelay = old_delay;                        // Restore original values.
  2316.     g.PressDuration = old_press_duration;          //
  2317.     g.KeyDelayPlay = old_delay_play;               //
  2318.     g.PressDurationPlay = old_press_duration_play; //
  2319. }
  2320.  
  2321.  
  2322.  
  2323. ResultType Hotstring::AddHotstring(Label *aJumpToLabel, char *aOptions, char *aHotstring, char *aReplacement
  2324.     , bool aHasContinuationSection)
  2325. // Caller provides aJumpToLabel rather than a Line* because at the time a hotkey or hotstring
  2326. // is created, the label's destination line is not yet known.  So the label is used a placeholder.
  2327. // Returns OK or FAIL.
  2328. // Caller has ensured that aHotstringOptions is blank if there are no options.  Otherwise, aHotstringOptions
  2329. // should end in a colon, which marks the end of the options list.  aHotstring is the hotstring itself
  2330. // (e.g. "ahk"), which does not have to be unique, unlike the label name, which was made unique by also
  2331. // including any options in with the label name (e.g. ::ahk:: is a different label than :c:ahk::).
  2332. // Caller has also ensured that aHotstring is not blank.
  2333. {
  2334.     // The length is limited for performance reasons, notably so that the hook does not have to move
  2335.     // memory around in the buffer it uses to watch for hotstrings:
  2336.     if (strlen(aHotstring) > MAX_HOTSTRING_LENGTH)
  2337.         return g_script.ScriptError("Hotstring max abbreviation length is " MAX_HOTSTRING_LENGTH_STR ".", aHotstring);
  2338.  
  2339.     if (!shs)
  2340.     {
  2341.         if (   !(shs = (Hotstring **)malloc(HOTSTRING_BLOCK_SIZE * sizeof(Hotstring *)))   )
  2342.             return g_script.ScriptError(ERR_OUTOFMEM); // Short msg. since so rare.
  2343.         sHotstringCountMax = HOTSTRING_BLOCK_SIZE;
  2344.     }
  2345.     else if (sHotstringCount >= sHotstringCountMax) // Realloc to preserve contents and keep contiguous array.
  2346.     {
  2347.         // Expand the array by one block.  Use a temp var. because realloc() returns NULL on failure
  2348.         // but leaves original block allocated.
  2349.         void *realloc_temp = realloc(shs, (sHotstringCountMax + HOTSTRING_BLOCK_SIZE) * sizeof(Hotstring *));
  2350.         if (!realloc_temp)
  2351.             return g_script.ScriptError(ERR_OUTOFMEM);  // Short msg. since so rare.
  2352.         shs = (Hotstring **)realloc_temp;
  2353.         sHotstringCountMax += HOTSTRING_BLOCK_SIZE;
  2354.     }
  2355.  
  2356.     if (   !(shs[sHotstringCount] = new Hotstring(aJumpToLabel, aOptions, aHotstring, aReplacement, aHasContinuationSection))   )
  2357.         return g_script.ScriptError(ERR_OUTOFMEM); // Short msg. since so rare.
  2358.     if (!shs[sHotstringCount]->mConstructedOK)
  2359.     {
  2360.         delete shs[sHotstringCount];  // SimpleHeap allows deletion of most recently added item.
  2361.         return FAIL;  // The constructor already displayed the error.
  2362.     }
  2363.  
  2364.     ++sHotstringCount;
  2365.     mAtLeastOneEnabled = true; // Added in v1.0.44.  This method works because the script can't be suspended while hotstrings are being created (upon startup).
  2366.     return OK;
  2367. }
  2368.  
  2369.  
  2370.  
  2371. Hotstring::Hotstring(Label *aJumpToLabel, char *aOptions, char *aHotstring, char *aReplacement, bool aHasContinuationSection)
  2372.     : mJumpToLabel(aJumpToLabel)  // Any NULL value will cause failure further below.
  2373.     , mString(NULL), mReplacement(""), mStringLength(0)
  2374.     , mSuspended(false)
  2375.     , mExistingThreads(0)
  2376.     , mMaxThreads(g_MaxThreadsPerHotkey)  // The value of g_MaxThreadsPerHotkey can vary during load-time.
  2377.     , mPriority(g_HSPriority), mKeyDelay(g_HSKeyDelay), mSendMode(g_HSSendMode)  // And all these can vary too.
  2378.     , mCaseSensitive(g_HSCaseSensitive), mConformToCase(g_HSConformToCase), mDoBackspace(g_HSDoBackspace)
  2379.     , mOmitEndChar(g_HSOmitEndChar), mSendRaw(aHasContinuationSection ? true : g_HSSendRaw)
  2380.     , mEndCharRequired(g_HSEndCharRequired), mDetectWhenInsideWord(g_HSDetectWhenInsideWord), mDoReset(g_HSDoReset)
  2381.     , mHotCriterion(g_HotCriterion)
  2382.     , mHotWinTitle(g_HotWinTitle), mHotWinText(g_HotWinText)
  2383.     , mConstructedOK(false)
  2384. {
  2385.     // Insist on certain qualities so that they never need to be checked other than here:
  2386.     if (!mJumpToLabel) // Caller has already ensured that aHotstring is not blank.
  2387.         return;
  2388.  
  2389.     ParseOptions(aOptions, mPriority, mKeyDelay, mSendMode, mCaseSensitive, mConformToCase, mDoBackspace
  2390.         , mOmitEndChar, mSendRaw, mEndCharRequired, mDetectWhenInsideWord, mDoReset);
  2391.  
  2392.     // To avoid memory leak, this is done only when it is certain the hotkey will be created:
  2393.     if (   !(mString = SimpleHeap::Malloc(aHotstring))   )
  2394.     {
  2395.         g_script.ScriptError(ERR_OUTOFMEM); // Short msg since very rare.
  2396.         return;
  2397.     }
  2398.     mStringLength = (UCHAR)strlen(mString);
  2399.     if (*aReplacement)
  2400.     {
  2401.         // To avoid wasting memory due to SimpleHeap's block-granularity, only allocate there if the replacement
  2402.         // string is short (note that replacement strings can be over 16,000 characters long).  Since
  2403.         // hotstrings can be disabled but never entirely deleted, it's not a memory leak in either case
  2404.         // since memory allocated by either method will be freed when the program exits.
  2405.         size_t size = strlen(aReplacement) + 1;
  2406.         if (   !(mReplacement = (size > MAX_ALLOC_SIMPLE) ? (char *)malloc(size) : SimpleHeap::Malloc(size))   )
  2407.         {
  2408.             g_script.ScriptError(ERR_OUTOFMEM); // Short msg since very rare.
  2409.             return;
  2410.         }
  2411.         strcpy(mReplacement, aReplacement);
  2412.     }
  2413.     else // Leave mReplacement blank, but make this false so that the hook doesn't do extra work.
  2414.         mConformToCase = false;
  2415.  
  2416.     mConstructedOK = true; // Done at the very end.
  2417. }
  2418.  
  2419.  
  2420.  
  2421.  
  2422. void Hotstring::ParseOptions(char *aOptions, int &aPriority, int &aKeyDelay, SendModes &aSendMode
  2423.     , bool &aCaseSensitive, bool &aConformToCase, bool &aDoBackspace, bool &aOmitEndChar, bool &aSendRaw
  2424.     , bool &aEndCharRequired, bool &aDetectWhenInsideWord, bool &aDoReset)
  2425. {
  2426.     // In this case, colon rather than zero marks the end of the string.  However, the string
  2427.     // might be empty so check for that too.  In addition, this is now called from
  2428.     // IsDirective(), so that's another reason to check for normal string termination.
  2429.     char *cp1;
  2430.     for (char *cp = aOptions; *cp && *cp != ':'; ++cp)
  2431.     {
  2432.         cp1 = cp + 1;
  2433.         switch(toupper(*cp))
  2434.         {
  2435.         case '*':
  2436.             aEndCharRequired = (*cp1 == '0');
  2437.             break;
  2438.         case '?':
  2439.             aDetectWhenInsideWord = (*cp1 != '0');
  2440.             break;
  2441.         case 'B': // Do backspacing.
  2442.             aDoBackspace = (*cp1 != '0');
  2443.             break;
  2444.         case 'C':
  2445.             if (*cp1 == '0') // restore both settings to default.
  2446.             {
  2447.                 aConformToCase = true;
  2448.                 aCaseSensitive = false;
  2449.             }
  2450.             else if (*cp1 == '1')
  2451.             {
  2452.                 aConformToCase = false;
  2453.                 aCaseSensitive = false;
  2454.             }
  2455.             else // treat as plain "C"
  2456.             {
  2457.                 aConformToCase = false;  // No point in conforming if its case sensitive.
  2458.                 aCaseSensitive = true;
  2459.             }
  2460.             break;
  2461.         case 'O':
  2462.             aOmitEndChar = (*cp1 != '0');
  2463.             break;
  2464.         // For options such as K & P: Use atoi() vs. ATOI() to avoid interpreting something like 0x01C
  2465.         // as hex when in fact the C was meant to be an option letter:
  2466.         case 'K':
  2467.             aKeyDelay = atoi(cp1);
  2468.             break;
  2469.         case 'P':
  2470.             aPriority = atoi(cp1);
  2471.             break;
  2472.         case 'R':
  2473.             aSendRaw = (*cp1 != '0');
  2474.             break;
  2475.         case 'S':
  2476.             if (*cp1)
  2477.                 ++cp; // Skip over S's sub-letter (if any) to exclude it from  further consideration.
  2478.             switch (toupper(*cp1))
  2479.             {
  2480.             // There is no means to choose SM_INPUT because it seems too rarely desired (since auto-replace
  2481.             // hotstrings would then become interruptible, allowing the keystrokes of fast typists to get
  2482.             // interspersed with the replacement text).
  2483.             case 'I': aSendMode = SM_INPUT_FALLBACK_TO_PLAY; break;
  2484.             case 'E': aSendMode = SM_EVENT; break;
  2485.             case 'P': aSendMode = SM_PLAY; break;
  2486.             //default: leave it unchanged.
  2487.             }
  2488.             break;
  2489.         case 'Z':
  2490.             aDoReset = (*cp1 != '0');
  2491.             break;
  2492.         // Otherwise: Ignore other characters, such as the digits that comprise the number after the P option.
  2493.         }
  2494.     }
  2495. }