4
\$\begingroup\$

I'm making a hotkey program to aid me using PC faster. By saying hotkey here, I mean any combination of two or more keys in the keyboard.

For example, if I press G, hold it then press M, I have hotkey GM. Then I can launch Gmail from the default browser.

The idea is same with the famous AutoHotkey software, but with a slight difference (note that this program I make for myself so I will make it best suite for me).

That is, based on my typing, I see that when I type fast, I often press next key before I release the previous key. For example, when typing GMAIL.COM, after press G, I press M, before release G.

I see that the time between I press G and M is less than 100 milliseconds. But if I intend to press a hotkey, I will press G, hold it for 200 milliseconds before press M. To determine if a combination of 2+ keys is a hotkey or not, I check to make sure the press time between these keys are greater than 200 milliseconds.

Here is the definition:

void gh_Both(int vkCode); // This function is check if keys is a valid hotkey
std::map<int, int> src;   // Store [keyCode, Tickcount] for keys
                          // src: [keyCode, tickcount]

Low level keyboard hook:

LRESULT CALLBACK k_Callback(int nCode, WPARAM wparam, LPARAM lparam)
{
    if (nCode >= 0) 
    {
        int kCode=((KBDLLHOOKSTRUCT*)lparam)->vkCode;
        switch (wparam)
        {
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:

            // if the key is not repeated key, add it and tickcount to src
            if (src.find(kCode) == src.end()) src[kCode] = GetTickCount();
            gh_Both(kCode);
            break;
        case WM_KEYUP:
        case WM_SYSKEYUP:

            // clear kCode in src
            src.erase(kCode);
            break;
        }
    }
    return CallNextHookEx(_k_hook, nCode, wparam, lparam);
}

The same for low level mouse hook:

LRESULT CALLBACK m_Callback(int nCode, WPARAM wparam, LPARAM lparam)
{
    switch (wparam)
    {
    case 513:  // left mouse down
    case 516:  // right mouse down
    case 519:  // middle mouse down
        if (src.find(wparam) == src.end()) src[wparam] = GetTickCount();
        gh_Both(wparam);
        break;
    case 514:  // left mouse up
    case 517:  // right mouse up
    case 520:  // middle mouse up
        src.erase(wparam - 1);
        break;
    case 522:  // mouse wheel
        gh_Both((short)HIWORD(((MSLLHOOKSTRUCT*)lparam)->mouseData) > 0 ? 522 : -522);
        break;
    }
    return CallNextHookEx(_k_hook, nCode, wparam, lparam);
}

gh_Both function:

void gh_Both(int vkCode)
{
    // Sort pressed key by Tickcount, save to "dst"
    // dst: [tickcount, keyCode]
    std::map<int, int> dst;
    for each (auto& i in src)
    {
        dst[i.second] = i.first;
    }

    int time = 0;    // Store key's tickcount
    int keys = 0;    // Store hotkey
    bool modifier = false;

    // If have any modifier key (Ctrl/Shift/Alt) or key have keyCode > 90
    // or second key is press after first key 200 miliseconds
    // then they are considered a valid hotkey combination
    for each (auto i in dst)
    {
        if (i.second > 90) modifier = true;
        if (i.first - time > 200 || modifier || keys > 1000)
        {
            // if hotkey is "GM" with keycode 71 and 77 then keys = 71 * 1000 + 77 = 71077
            // all my hotkeys are combination of less than 4 keys
            keys = keys * 1000 + i.second;
            time = i.first;  // Save Tickcount of key to "time"
        }
    }
    switch (keys)
    {
    case 71077: // 071: G, 077: M
        cr("http://mail.google.com", "");  // Launch Gmail
        break;
    case 66067: // 066: B, 067: C
        cr("http://www.bbc.co.uk/", "");
        break;
    case 70066:
        cr("http://www.facebook.com/", "");
        break;
    case 516:
    case 519:
        if (vkCode == 522) sms(APPCOMMAND_VOLUME_UP, false); // VOLUME +
        else if (vkCode == -522) sms(APPCOMMAND_VOLUME_DOWN, false);  // VOLUME -
        break;
    default:
        break;
    }
}

My target OS: Windows XP and later. The program is working as I intend. But I post here to check if there are any errors in my code.

Questions:

  • Can I improve the way I store [key, tickcount] and gh_Both function?
  • Are there any bugs/errors in my code?
\$\endgroup\$
1
  • \$\begingroup\$ I love that. I'd like to know if there is a way to register the hotkeys for a specific application only and unregister them when this application is not on top. Example: in my image editor softwares the "e" strike is the equivalent of "ctrl+shift+n" + wait a bit + "return" + put the cursor at a specific location on screen. Of course when I am in a text editor I don't want this behavior. BUT the Hotkeys are defined for the whole system. Is there a way to register the hotkeys when an application is on top and unregister them when it is not? Of course the application has many top windows. \$\endgroup\$ Feb 14 '19 at 8:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.