I am working on an ImGui project, and am trying to find a way to create custom key binds. I thought of just listing every single VK key code in a massive,
if
statement, but not only would that be bulky, but I would also not take certain keys such as some unique mouse buttons or any other key I may end up missing. I want a function that will store the next mouse or keyboard input into an integer, without the use of a predefined set of available inputs. I want to dynamically recognize any input key.
Minimal example:
const char* cbind0 = "none";
static bool bbind0 = false;
static int ibind0;
if (ImGui::Button(cbind0))
bbind0 = true
if (bbind0 == true)
{
cbind0 = "press any key...";
CopyNextInputTo(ibind0); // Function to copy pressed key to our integer
}
This code would show up as a box in the GUI, and then the integer ibind0 which is containing our now determined keybind, will be used like so:
static bool option = false;
if (GetAsyncKeyState(ibind0) & 1)
{
option =! option;
}
And now we can toggle our option on and off either using a GUI checkbox or by pressing the user-determined key.
The only problem now being I have no clue how to dynamically record all possible inputs! Does anyone know any possible functions or methods? Thanks!
Assuming you are the one creating the window you're using ImGui on, you can handle WM_KEYDOWN events in your WNDPROC callback. Here's a basic example of this in action that will create a pop-up message box whenever you press a key:
#ifndef UNICODE
#define UNICODE
#endif
#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
const wchar_t WINDOW_CLASS_NAME[] = L"Keyboard Listener Class";
const wchar_t WINDOW_TITLE[] = L"Keyboard Listener";
WNDCLASS windowClass{};
windowClass.lpfnWndProc = WindowProc;
windowClass.hInstance = hInstance;
windowClass.lpszClassName = WINDOW_CLASS_NAME;
RegisterClass(&windowClass);
HWND hWnd = CreateWindowEx(
0,
WINDOW_CLASS_NAME,
WINDOW_TITLE,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
if (hWnd == NULL) {
return EXIT_FAILURE;
}
ShowWindow(hWnd, nCmdShow);
MSG msg{};
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return EXIT_SUCCESS;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return EXIT_SUCCESS;
case WM_KEYDOWN:
wchar_t keyName[64];
GetKeyNameText(lParam, keyName, sizeof(keyName));
MessageBox(hWnd, keyName, L"Key Pressed!", MB_OK);
return EXIT_SUCCESS;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return EXIT_SUCCESS;
}
Or, if you aren't the one actually creating the Window, you can hook into another Window's keyboard events using SetWindowsHookEx with the idHook parameter set to WH_KEYBOARD and passing in a KeyboardProc callback to the lpfn parameter.
Related
I want to make an application that responds to a mouse button so I done this:
case WM_LBUTTONDOWN:
MessageBox(
NULL,
(LPCWSTR)L"HALLOOOO",
(LPCWSTR)L"Worked",
MB_ICONASTERISK | MB_OK | MB_DEFBUTTON2
);
break;
but the problem is that this only works if the user clicks on the window and I want it to work even with the window minimized
this work even if the application is minimized
GetKeyState(VK_LBUTTON);
but if I put this in a loop if I press it once it will detect 1 million times because it will just check if the key is down and if I add delay using Sleep(250) it may work but sometimes it will not detect anything even if the user pressed the key
I want my app to be able to detect if a key is pressed even if it's minimized how can I do this?
Since you already have a window, call SetWindowsHookEx with WH_MOUSE_LL.
The API is documented here and the parameters are explained.
HHOOK SetWindowsHookExW(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);
The lpfn hook procedure can be defined as follows:
HWND hmain_window;
HHOOK hhook;
LRESULT CALLBACK mouse_proc(int code, WPARAM wparam, LPARAM lparam)
{
if (code == HC_ACTION && lparam)
{
if (wparam == WM_LBUTTONDOWN)
{
//MOUSEHOOKSTRUCT* mstruct = (MOUSEHOOKSTRUCT*)lparam;
static int i = 0;
std::wstring str = L"mouse down " + std::to_wstring(i++);
SetWindowText(hmain_window, str.c_str());
}
}
return CallNextHookEx(hhook, code, wparam, lparam);
}
int APIENTRY wWinMain(HINSTANCE hinst, HINSTANCE, LPWSTR, int)
{
...
RegisterClassEx(...);
hmain_window = CreateWindow(...);
hhook = SetWindowsHookEx(WH_MOUSE_LL, mouse_proc, hinst, 0);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hhook);
return 0;
}
You can try SetWindowsHookEx with parameter WH_MOUSE_LL or WH_MOUSE.
This article shows how to install a keyboard hook. You can replace WH_KEYBOARD with WH_MOUSE to install a mouse hook and use this document to handle the callback.
The code below, sends a WM_CHANGEUISTATE to the window procedure itself, with the arguments:
LOWORD(wParam) = UIS_CLEAR
HIWORD(wParam) = UISF_HIDEACCEL
lParam = 0x00000000
when the window client area is left clicked with the mouse.
According to this blog by Raymond Chen this should make the mnemonics in the System menu to be shown, when the menu is accessed with the mouse. The paragraph below was extracted from this article:
Clearing a flag shows the corresponding indicator. For example, if you
have a UIS_CLEAR for UISF_HIDEĀFOCUS, that means that you want to show
focus indicators.
In my case, I have a UIS_CLEAR for UISF_HIDEACCEL, meaning that I want to show the menu accelerators.
If you run the code below and click with the mouse left button on the app client area, you should make the accelerators visible in the System menu, even when this menu is accessed with the mouse. But that doesn't happen, i.e., if you activate the system menu with a left click on the window's icon, or with a right click on the window's title bar, the mnemonics in the System menu will not be shown. What am I missing?
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, int nCmdShow)
{
WNDCLASSEX wndclassx;
wchar_t szAppName[] = L"WM_CHANGEUISTATE";
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = CS_HREDRAW | CS_VREDRAW;
wndclassx.lpfnWndProc = WndProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = LoadCursor(0, IDC_ARROW);
wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclassx.lpszClassName = szAppName;
wndclassx.lpszMenuName = nullptr;
wndclassx.hIconSm = 0;
if (!RegisterClassEx(&wndclassx)) return 0;
HWND hWnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0);
ShowWindow(hWnd, SW_MAXIMIZE);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
{
BOOL b;
// Note that in my system (Windows 10) I have:
//
// Control Panel > Ease of Access > Ease of Access Center > Make the keyboard easier
//
// and the option "Underline keyboard shortcuts and access keys" unmarked (the default). Therefore, the value
// returned in b below will be 0x00000000 (FALSE).
SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &b, 0);
// If b = FALSE, the SendMessage() below should underline the accelerators in the System menu, when this menu is
// accessed with the mouse. But that doesn't work. Why?
if( !b ) SendMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL), NULL);
}
break;
// Setting a break in the return below, one can see that WM_CHANGEUISTATE message is being sent to the window and
// passed to DefWindowProc().
case WM_CHANGEUISTATE:
return DefWindowProc(hwnd, message, wParam, lParam);
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
This seems like a bug/oversight in Windows. DefWindowProc does not send WM_QUERYUISTATE before displaying the system menu. The menu implementation checks the last input event directly and if it was from a keyboard it displays the underlines.
I tried sending and posting WM_CHANGEUISTATE and WM_UPDATEUISTATE in response to WM_ENTERMENULOOP, WM_INITMENUPOPUP, WM_NCRBUTTONDOWN and WM_SYSCOMMAND without any luck.
The only workaround I was able to come up with is to hack the HTSYSMENU/icon menu by changing SC_MOUSEMENU to SC_KEYMENU:
case WM_SYSCOMMAND:
if ((wParam & 0xFFF0) == SC_MOUSEMENU)
{
return SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, ' ');
}
return DefWindowProc(hwnd, message, wParam, lParam);
SC_KEYMENU has special handling in DefWindowProc and forces underlines if applicable.
This does not handle right-click on the icon, caption nor task bar...
I don't have much experience with windows programming, but can't specifically see what's wrong with this code, yet the window doesn't open. Although sometimes, not always, it will be open in task manager, so my guess is that it is registering the class and creating the window, but the problem is with the ShowWindow() function. But, I'm not positive.
To my understanding the flow of the program is:
Window is created with the registered class.
The window is shown.
Continuously looks for messages that are processed in the window Proc.
I feel like I've done all these things, so is my understanding wrong, or is my code missing something?
Thanks.
Source Code:
#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
//Variable for message loop.
MSG msg;
//Setting up the window class.
WNDCLASSEX windowClass;
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_PARENTDC;
windowClass.lpfnWndProc = WindowProc;
windowClass.hInstance = hinstance;
windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
windowClass.lpszClassName = "WindowClass";
RegisterClassEx(&windowClass);
HWND windowHandle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "WindowClass", "My Program", WS_OVERLAPPEDWINDOW, 500, 200, 800, 500, NULL, NULL, hinstance, 0);
if (!windowHandle)
return FALSE;
ShowWindow(windowHandle, nCmdShow);
// Start the message loop.
while (GetMessage(&msg, NULL, 0, 0) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Return the exit code to the system.
return msg.wParam;
}
Your window procedure is invoking DefWindowProc but not actually returning the result, and you have undefined behavior because of that. The return value is important, and it can control how the OS handles successive messages to your window. For example, it's important to return the correct value in response to the WM_CREATE message.
Change your window procedure to:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Also, as Mark Ransom identified in the comments section, you should zero-initialize your WNDCLASSEX structure to ensure that you don't get garbage on any members that you didn't explicitly initialize.
I am working on window creation with Win32 API and I'm having a problem with this part:
GetMessage(&message, NULL, 0, 0);
My problem is that when I try to change the second parameter (hwnd) which is going to receive the messages to the window I previously made, it doesn't work; for example, when I try to close the window, it only hides and doesn't close.
Here is the full code:
#include <windows.h>
LRESULT CALLBACK WinProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS window;
window.cbClsExtra = NULL;
window.cbWndExtra = NULL;
window.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
window.hCursor = LoadCursor(hInst, IDC_ARROW);
window.hIcon = NULL;
window.hInstance = hInst;
window.lpfnWndProc = WinProc;
window.lpszClassName = "WINDOW";
window.lpszMenuName = NULL;
window.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&window);
HWND hwnd = CreateWindow("WINDOW", "Win32 Window Application", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL, hInst, NULL);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
return 1;
}
LRESULT CALLBACK WinProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
{
PostQuitMessage(0);
break;
}
default:
break;
}
return DefWindowProc(window, message, wParam, lParam);
}
"... when I try to change the second parameter (hwnd) which is going to receive the messages to the window I previously made, it doesn't work."
Thread messages are not sent to a window; they're posted to the thread message queue with a NULL window handle, and will NOT be picked up with a GetMessage() loop tailored to a specific window handle.
Ex: PostQuitMessage() posts a thread message; not a window message. You need the NULL. From the GetMessage() docs:
If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed.
I would just like to know how to implement a void function (which for instance returns a string) in a C/C++ GUI program and show the output in a window.
Would you put it in a WM_CREATE case in the WndProc function or under the WinMain function? Or is it more complicated, as in, you have to redirect the Command Prompt output to the window?
Edit:
So, I have, for example...
void function() {
cout << "Hello";
}
and then I want to implement this in a GUI...
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
// Implement function somewhere here?
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
}
I don't think you need to go as low level as WndProc
The most basic way to implement a multiple return function in general is to pass by reference, for example
void someFunction(string* aS)
{ //do something
aS = someString;
}
It should be impemented something like this
string someString;
someFunction(&someString);
ShowMessage(someString);