Closed BeardDKing closed 1 month ago
Here is the corrected code to include that feature:
// g++ -o SnapKey SnapKey.cpp -mwindows -std=c++11
#include <windows.h>
#include <shellapi.h>
#define ID_TRAY_APP_ICON 1001
#define ID_TRAY_EXIT_CONTEXT_MENU_ITEM 3000
#define WM_TRAYICON (WM_USER + 1)
// Global variables
bool keyA_pressed = false;
bool keyD_pressed = false;
bool keyA_was_pressed = false;
bool keyD_was_pressed = false;
HHOOK hHook = NULL;
NOTIFYICONDATA nid;
// Function declarations
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void InitNotifyIconData(HWND hwnd);
int main()
{
// Create a named mutex
HANDLE hMutex = CreateMutex(NULL, TRUE, TEXT("SnapKeyMutex"));
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
MessageBox(NULL, TEXT("SnapKey is already running!"), TEXT("Error"), MB_ICONINFORMATION | MB_OK);
return 1; // Exit the program
}
// Create a window class
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = TEXT("SnapKeyClass");
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, TEXT("Window Registration Failed!"), TEXT("Error"), MB_ICONEXCLAMATION | MB_OK);
ReleaseMutex(hMutex);
CloseHandle(hMutex);
return 1;
}
// Create a window
HWND hwnd = CreateWindowEx(
0,
wc.lpszClassName,
TEXT("SnapKey"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, wc.hInstance, NULL);
if (hwnd == NULL) {
MessageBox(NULL, TEXT("Window Creation Failed!"), TEXT("Error"), MB_ICONEXCLAMATION | MB_OK);
ReleaseMutex(hMutex);
CloseHandle(hMutex);
return 1;
}
// Initialize and add the system tray icon
InitNotifyIconData(hwnd);
// Set the hook
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);
if (hHook == NULL)
{
MessageBox(NULL, TEXT("Failed to install hook!"), TEXT("Error"), MB_ICONEXCLAMATION | MB_OK);
ReleaseMutex(hMutex); // Release the mutex before exiting
CloseHandle(hMutex); // Close the handle
return 1;
}
// Message loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Unhook the hook
UnhookWindowsHookEx(hHook);
// Remove the system tray icon
Shell_NotifyIcon(NIM_DELETE, &nid);
// Release and close the mutex
ReleaseMutex(hMutex);
CloseHandle(hMutex);
return 0;
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT *pKeyBoard = (KBDLLHOOKSTRUCT *)lParam;
switch (wParam)
{
case WM_KEYDOWN:
if (pKeyBoard->vkCode == 'A')
{
if (keyD_pressed)
{
// Release D
keyD_pressed = false;
keyD_was_pressed = true;
INPUT input = {0};
input.type = INPUT_KEYBOARD;
input.ki.wVk = 'D';
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof(INPUT));
}
keyA_pressed = true;
keyA_was_pressed = false;
}
else if (pKeyBoard->vkCode == 'D')
{
if (keyA_pressed)
{
// Release A
keyA_pressed = false;
keyA_was_pressed = true;
INPUT input = {0};
input.type = INPUT_KEYBOARD;
input.ki.wVk = 'A';
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof(INPUT));
}
keyD_pressed = true;
keyD_was_pressed = false;
}
break;
case WM_KEYUP:
if (pKeyBoard->vkCode == 'A')
{
keyA_pressed = false;
if (keyD_was_pressed)
{
// Press D again
keyD_was_pressed = false;
keyD_pressed = true;
INPUT input = {0};
input.type = INPUT_KEYBOARD;
input.ki.wVk = 'D';
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof(INPUT));
}
}
else if (pKeyBoard->vkCode == 'D')
{
keyD_pressed = false;
if (keyA_was_pressed)
{
// Press A again
keyA_was_pressed = false;
keyA_pressed = true;
INPUT input = {0};
input.type = INPUT_KEYBOARD;
input.ki.wVk = 'A';
input.ki.dwFlags = 0;
SendInput(1, &input, sizeof(INPUT));
}
}
break;
default:
break;
}
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
void InitNotifyIconData(HWND hwnd)
{
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.uID = ID_TRAY_APP_ICON;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_TRAYICON;
// Load the icon from the current directory
HICON hIcon = (HICON)LoadImage(NULL, TEXT("icon.ico"), IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
if (hIcon)
{
nid.hIcon = hIcon;
}
else
{
// If loading the icon fails, fallback to a default icon
nid.hIcon = LoadIcon(NULL, IDI_APPLICATION);
}
lstrcpy(nid.szTip, TEXT("SnapKey"));
Shell_NotifyIcon(NIM_ADD, &nid);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_TRAYICON:
if (lParam == WM_RBUTTONDOWN)
{
POINT curPoint;
GetCursorPos(&curPoint);
SetForegroundWindow(hwnd);
// Create a context menu
HMENU hMenu = CreatePopupMenu();
AppendMenu(hMenu, MF_STRING, ID_TRAY_EXIT_CONTEXT_MENU_ITEM, TEXT("Exit SnapKey"));
// Display the context menu
TrackPopupMenu(hMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, curPoint.x, curPoint.y, 0, hwnd, NULL);
DestroyMenu(hMenu);
}
break;
case WM_COMMAND:
if (LOWORD(wParam) == ID_TRAY_EXIT_CONTEXT_MENU_ITEM)
{
PostQuitMessage(0);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Hi,
First of all, thank you very much for providing the code. It's working better than the code I attempted earlier today.
However, there's an issue with key inputs in-game. When holding the 'A' key and tapping the 'D' key, then releasing 'A' first followed by 'D', the 'A' key is automatically registered in-game, causing the character to move left without the 'A' key being actually pressed.
The same issue occurs in reverse: holding 'D', then 'A', and releasing 'D' makes the character move right.
The bug appears in these scenarios:
Hold 'A' -> Hold 'D' -> Release 'A' (while still holding 'D') -> "A" is active, without being physically pressed. Hold 'D' -> Hold 'A' -> Release 'D'(while still holding 'A') -> "D" is active, without being physically pressed.
Thanks again for your help!
I'm having the same problem with the provided code.
Hold 'A' -> Hold 'D' -> Release 'A' (while still holding 'D') -> "A" is active, without being physically pressed. Hold 'D' -> Hold 'A' -> Release 'D'(while still holding 'A') -> "D" is active, without being physically pressed.
Any solution?
well, can't wait for it to be solved!!! <3 @cafali you are fucking amazing for making SnapKey available for everyone
there's already some null binds for CS i'm just going to leave it here, maybe logic might help (i dont know anything about coding)
// de-subticked movement
alias +forward_ "+forward;+forward"
alias -forward_ "-forward;-forward;-forward"
alias +left_ "+left;+left"
alias -left_ "-left;-left;-left"
alias +back_ "+back;+back"
alias -back_ "-back;-back;-back"
alias +right_ "+right;+right"
alias -right_ "-right;-right;-right"
// null binds
alias checkfwd ""
alias checkback ""
alias checkleft ""
alias checkright ""
alias +mfwd "-back_; +forward_; alias checkfwd +forward_"
alias +mback "-forward_; +back_; alias checkback +back_"
alias +mleft "-right_; +left_; alias checkleft +left_"
alias +mright "-left_; +right_; alias checkright +right_"
alias -mfwd "-forward_; -back_; checkback; alias checkfwd"
alias -mback "-back_; -forward_; checkfwd; alias checkback"
alias -mleft "-left_; -right_; checkright; alias checkleft"
alias -mright "-right_; -left_; checkleft; alias checkright"
bind W +mfwd
bind A +mleft
bind S +mback
bind D +mright
Hello everyone! The Sticky Key feature is now available in SnapKey version 1.1.4. Enjoy!
1.1.4 - 25/07/2024
Sticky Keys:
Special thanks to @minteeaa for making this possible.
Config:
Hello,
I am very pleased with this application greatly. There is a feature I would love to have added and I don't think it will require too many lines of code. I am interested in adding a feature that retains the pressed state of the originally held button after releasing the newly pressed button.
https://youtu.be/Feny5bs2JCg?t=273 4:37 - 4:51
The following YouTube link is to a video that explains the feature I would love to have in detail. As such would be unnecessary to type it all out.
Thank you.