It is a bug, fixed by v1.1.34.03.
Dead key support is very tricky, because the only system functions for (accurately) translating key codes to characters also affect the "pending dead key" state of the kernel-mode keyboard buffer.
- When we translate a dead key VK, it puts the buffer into a "pending dead key" state. If we leave that as is and do not suppress the dead key, the active window will then translate the dead key and the result will be like pressing the dead key twice. So to work around that, we put the buffer back into its previous state by "re-translating" the key.
- When we translate any key while there's a pending dead key, it takes the pending dead key out of the buffer. That's perfect if we're going to suppress the key, because it means the next keypress won't be affected by the pending dead key. However, if we don't end up suppressing the key, the pending dead key needs to be placed back into the buffer so that whoever else translates the key (typically the active window, but could be another keyboard hook) will get the right result.
- There is no way to ask the OS whether there's a pending dead key in the buffer, so the keyboard hook needs to record that information whenever it translates a dead key VK. (If VK_SPACE translates to something other than " ", we can infer that there was a pending dead key, but not which key combination produced it. Also, that translation would take the pending dead key out of the buffer, and putting it back in would be difficult since we don't know the key combination.)
The main problem in your case is that when a hotstring suppresses the final character, it avoids reinserting the dead key
for this keypress, but forgets to clear the hook's own record of the pending dead key. Next time the hook translates a key, it reinserts the pending dead key. Fixing this is a simple case of adding
sPendingDeadKeyVK = 0; in the right place.
A second problem is that when there are multiple scripts collecting input for hotstrings or InputHook, only the process responsible for suppressing the hotstring end char knows that the pending dead key should be discarded. All other processes assume that the pending dead key is still in effect, and will presume that it needs to be reinserted to restore the buffer to the correct state (which is correct only for hooks that act
before the one that will suppress the key). The fix I have come up with is to perform the translation again - if the result is the same, either there wasn't really a dead key in the buffer, or it didn't affect the result anyway and doesn't need to be reinserted.