From b54bad36768c76d6e3c124a009a5d1249f72180b Mon Sep 17 00:00:00 2001 From: Demetry Date: Tue, 30 Jan 2024 21:35:37 -0500 Subject: [PATCH] Moved handling hotkeys to the keyboard Rework how messages are handled Found typo in Makefile for release builds Fixed format string to handle values properly. Reworking the config since there is a memory leak Config works now works as expected. Removed unused resource header include in the main application code. GetMessage with a -1 as second param get all thread messages not window. Cleanup comments --- Makefile | 6 +-- config.c | 115 +++++++++++++++++++++++++++++------------------------ debug.h | 2 + keyboard.c | 42 +++++++++++++++++++ keyboard.h | 1 + messages.h | 3 ++ wm.c | 80 +++++++++++++++---------------------- wm_dll.c | 58 ++++++--------------------- wm_dll.def | 4 +- 9 files changed, 161 insertions(+), 150 deletions(-) create mode 100644 messages.h diff --git a/Makefile b/Makefile index 119c8ce..26047cf 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ CC = cl LD = link RC = rc -CFLAGS = /EHsc +CFLAGS = EXE_SRCS = wm.c tiling.c error.c config.c keyboard.c -DLL_SRCS = error.c wm_dll.c +DLL_SRCS = wm_dll.c error.c EXE_NAME = lightwm.exe DLL_NAME = lightwm_dll.dll EXE_RC = wm_resources.obj @@ -52,7 +52,7 @@ $(DBGDIR)/%.obj: %.rc # release: prep $(RELEXE) $(RELDLL) -$(RELEXE): $(REL_EXE_OBJS) $(RELDIR)/$(EXE_RESS) +$(RELEXE): $(REL_EXE_OBJS) $(RELDIR)/$(EXE_RC) $(CC) $(RELCFLAGS) /Fe:$@ $^ /link user32.lib shell32.lib ole32.lib shlwapi.lib $(RELDLL): $(REL_DLL_OBJS) diff --git a/config.c b/config.c index b0ddc7c..1469710 100644 --- a/config.c +++ b/config.c @@ -15,11 +15,11 @@ #define BUFF_SIZE 65536 -PWSTR szConfigFilePath[MAX_PATH]; +PWSTR szConfigFilePath; + char* defaultConfigData = NULL; -//Should probably create a meta structure that holds the total count for now just another global variable -ConfigItems configItems; +ConfigItems* configItems; BOOL createDefaultConfigFile(HINSTANCE); BOOL loadDefaultConfigResourceData(HINSTANCE); @@ -27,11 +27,15 @@ BOOL writeDefaultConfigDataToFile(); void trim(char* str); void removeControlChars(char* str); size_t getLineCount(FILE* file); - + +void freeConfigItems(ConfigItems* items); + DWORD readConfigFile() { + assert(szConfigFilePath != NULL); + //Try to open the config file based on the path - FILE* configFileHandle = _wfopen(*szConfigFilePath, L"r"); + FILE* configFileHandle = _wfopen(szConfigFilePath, L"r"); if(configFileHandle == NULL) { @@ -41,11 +45,13 @@ DWORD readConfigFile() } char line[BUFF_SIZE]; //TODO must have a more clever way of getting a line length - - configItems.configItem = (ConfigItem*)malloc(sizeof(ConfigItem) * getLineCount(configFileHandle) + 1); - configItems.configItemsCount = getLineCount(configFileHandle) + 1; + + configItems = (ConfigItems*)malloc(sizeof(ConfigItems)); + + configItems->configItem = (ConfigItem*)malloc(sizeof(ConfigItem) * getLineCount(configFileHandle) + 1); + configItems->configItemsCount = getLineCount(configFileHandle) + 1; - if(configItems.configItem == NULL) + if(configItems->configItem == NULL) { reportWin32Error(L"Allocation ConfigItem memory"); cleanupConfigReader(); @@ -61,20 +67,20 @@ DWORD readConfigFile() //Get the first half of the line char* token = strtok(line, " "); - configItems.configItem[lineCount].name = (char*)malloc(strlen(token) + 1); - strncpy(configItems.configItem[lineCount].name, token, strlen(token) + 1); + configItems->configItem[lineCount].name = (char*)malloc(strlen(token) + 1); + strncpy(configItems->configItem[lineCount].name, token, strlen(token) + 1); //Get the second half of the line token = strtok(NULL, " "); removeControlChars(token); - configItems.configItem[lineCount].value = (char*)malloc(strlen(token) + 1); - strncpy(configItems.configItem[lineCount].value, token, strlen(token) + 1); + configItems->configItem[lineCount].value = (char*)malloc(strlen(token) + 1); + strncpy(configItems->configItem[lineCount].value, token, strlen(token) + 1); - DEBUG_PRINT("Name: %s Value: %s Name LEN: %lu Value LEN: %lu Count: %lu", - configItems.configItem[lineCount].name, - configItems.configItem[lineCount].value, - strlen(configItems.configItem[lineCount].name), - strlen(configItems.configItem[lineCount].value), + DEBUG_PRINT("Name: %s Value: %s Name LEN: %zu Value LEN: %zu Count: %zu", + configItems->configItem[lineCount].name, + configItems->configItem[lineCount].value, + strlen(configItems->configItem[lineCount].name), + strlen(configItems->configItem[lineCount].value), lineCount); } @@ -85,17 +91,19 @@ DWORD readConfigFile() } void getConfigFilePath() -{ +{ //TODO: We don't check other results possible results, i.e E_FAIL - HRESULT getAppDataPathResult = SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, szConfigFilePath); - + HRESULT getAppDataPathResult = SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &szConfigFilePath); + if(!SUCCEEDED(getAppDataPathResult)) { reportWin32Error(L"Could not get the users appdata directory"); - CoTaskMemFree(szConfigFilePath); + cleanupConfigReader(); exit(ERROR_PATH_NOT_FOUND); } - HRESULT concatStringResult = StringCchCatW(*szConfigFilePath, MAX_PATH, L"\\lightwm.config"); + assert(szConfigFilePath != NULL); + + HRESULT concatStringResult = StringCchCatW(szConfigFilePath, MAX_PATH, L"\\lightwm.config"); if(!SUCCEEDED(concatStringResult)) { switch(concatStringResult){ @@ -107,16 +115,18 @@ void getConfigFilePath() break; } reportWin32Error(L"Could not append file name to appdata path"); - CoTaskMemFree(szConfigFilePath); + cleanupConfigReader(); exit(GetLastError()); - } + } } uint8_t loadConfigFile(HINSTANCE resourceModuleHandle) { + szConfigFilePath = (PWSTR)malloc(sizeof(WCHAR) * MAX_PATH); getConfigFilePath(); - - if(!PathFileExistsW(*szConfigFilePath)) + assert(szConfigFilePath != NULL); + + if(!PathFileExistsW(szConfigFilePath)) { if(!createDefaultConfigFile(resourceModuleHandle)) { @@ -130,34 +140,35 @@ uint8_t loadConfigFile(HINSTANCE resourceModuleHandle) return ERROR_SUCCESS; } -void cleanupConfigReader() -{ - CoTaskMemFree(szConfigFilePath); - - if(configItems.configItem) //TODO Cleanup the name and value pointers for the strings - { - for(size_t i = 0; i < configItems.configItemsCount; i++) - { - if(&configItems.configItem[i] != NULL) - { - if(configItems.configItem[i].name) - { - free(configItems.configItem[i].name); - } - - if(configItems.configItem[i].value) - { - free(configItems.configItem[i].value); - } +void freeConfigItems(ConfigItems* items) { + if(items != NULL) { + for (size_t i = 0; i < items->configItemsCount; i++) { + if(&items->configItem[i] != NULL) + { + free(items->configItem[i].name); + free(items->configItem[i].value); } } - free(configItems.configItem); + if(items->configItem != NULL) + { + free(items->configItem); + } + free(items); } } +void cleanupConfigReader() +{ + freeConfigItems(configItems); + DEBUG_PRINT("Cleaned up config items"); + + CoTaskMemFree(szConfigFilePath); + DEBUG_PRINT("Cleaned up filePath ptr"); +} + ConfigItems* getConfigItems() { - return &configItems; + return configItems; } /** @@ -186,7 +197,7 @@ BOOL loadDefaultConfigResourceData(HINSTANCE resourceModuleHandle) if(hRes == NULL) { puts("Could not get HRSRC Handle"); - printf("%s %i\n", "FindResource Error: ", GetLastError()); + DEBUG_PRINT("FindResource Error: %i", GetLastError()); return FALSE; } @@ -195,7 +206,7 @@ BOOL loadDefaultConfigResourceData(HINSTANCE resourceModuleHandle) if(hData == NULL) { puts("Could not load resource"); - printf("%s %i\n", "LoadResource Error: ", GetLastError()); + DEBUG_PRINT("LoadResource Error: %i", GetLastError()); return FALSE; } @@ -204,7 +215,7 @@ BOOL loadDefaultConfigResourceData(HINSTANCE resourceModuleHandle) if(defaultConfigResourceData == NULL) { puts("Could not read resource"); - printf("%s %i\n", "LockResource Error: ", GetLastError()); + DEBUG_PRINT("LockResource Error: ", GetLastError()); return FALSE; } @@ -217,7 +228,7 @@ BOOL loadDefaultConfigResourceData(HINSTANCE resourceModuleHandle) BOOL writeDefaultConfigDataToFile() { - FILE* configFileHandle = _wfopen(*szConfigFilePath, L"w"); + FILE* configFileHandle = _wfopen(szConfigFilePath, L"w"); if(configFileHandle == NULL) { diff --git a/debug.h b/debug.h index a7c809a..c0ecb06 100644 --- a/debug.h +++ b/debug.h @@ -2,6 +2,8 @@ #ifdef DEBUG #define DEBUG_PRINT(format, ...) printf("DEBUG: %s Line %d: " format "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#define DEBUG_WPRINT(format, ...) wprintf(L"DEBUG: %ls Line %d: " format "\n", __FILE__, __LINE__, ##__VA_ARGS__) #else #define DEBUG_PRINT(format, ...) +#define DEBUG_WPRINT(format, ...) #endif \ No newline at end of file diff --git a/keyboard.c b/keyboard.c index a3f1413..fa9dee3 100644 --- a/keyboard.c +++ b/keyboard.c @@ -42,6 +42,7 @@ void cleanupKeyboard() UnregisterHotKey(NULL, WINDOW_DOWN); UnregisterHotKey(NULL, WINDOW_LEFT); UnregisterHotKey(NULL, WINDOW_RIGHT); + DEBUG_PRINT("Unregistered all hotkeys"); } UINT getModifier(char* value) @@ -74,4 +75,45 @@ void addKeyboardKeybind(enum Action action, UINT modifier, UINT keyCode) } DEBUG_PRINT("Registered %s hotkey", ACTION_STRINGS[action]); +} + +LRESULT handleHotkey(WPARAM wparam, LPARAM lparam) +{ + DEBUG_PRINT("handleHotkey called - %lli %lli", wparam, lparam); + + switch(wparam) + { + //TODO Can either trigger an event like the ShellProc callback, or handle directly. + // one method to handle virtual desktops is using the IVirtualDesktopManager in ShObjIdl but + // that is only available for Window 10 1809 or later. + case WORKSPACE_1: + puts("Switch to workspace 1"); + break; + case WORKSPACE_2: + puts("Switch to workspace 2"); + break; + case WORKSPACE_3: + puts("Switch to workspace 3"); + break; + case WORKSPACE_4: + puts("Switch to workspace 4"); + break; + case WINDOW_UP: + puts("Highlight window above"); + break; + case WINDOW_DOWN: + puts("Highlight window below"); + break; + case WINDOW_LEFT: + puts("Highlight window left"); + break; + case WINDOW_RIGHT: + puts("Highlight window right"); + break; + default: + DEBUG_PRINT("Unhandled hotkey message! Hotkey ID: %lli", wparam); + break; + } + + return ERROR_SUCCESS; } \ No newline at end of file diff --git a/keyboard.h b/keyboard.h index e39e2b6..615d725 100644 --- a/keyboard.h +++ b/keyboard.h @@ -18,3 +18,4 @@ if(strcmp(configItems->configItem[i].name, ACTION_STRINGS[action]) == 0) \ BOOL initializeKeyboardConfig(ConfigItems* configItems); void cleanupKeyboard(); +LRESULT handleHotkey(WPARAM lparam, LPARAM wparam); \ No newline at end of file diff --git a/messages.h b/messages.h new file mode 100644 index 0000000..f2d148f --- /dev/null +++ b/messages.h @@ -0,0 +1,3 @@ +//System reserves messages from range of 0x0000 through 0x03FF + +#define LWM_WINDOW_EVENT 0x0400 \ No newline at end of file diff --git a/wm.c b/wm.c index 5aadb42..eb80191 100644 --- a/wm.c +++ b/wm.c @@ -3,23 +3,25 @@ #include #include #include + #include "error.h" #include "tiling.h" #include "keyboard.h" #include "config.h" - -#include "resource.h" +#include "messages.h" #include "debug.h" HMODULE wmDll; HHOOK hookShellProcHandle; -HANDLE windowEvent; -//Has to absolutely match the definition in the dll -typedef LRESULT (*HotKeyProcType)(int, WPARAM, LPARAM); +typedef BOOL (*SetMessageThreadIdType)(DWORD); void cleanupObjects() { + cleanupKeyboard(); + + cleanupConfigReader(); + if (hookShellProcHandle) { UnhookWindowsHookEx(hookShellProcHandle); } @@ -27,13 +29,6 @@ void cleanupObjects() { if (wmDll) { FreeLibrary(wmDll); } - - if (windowEvent) { - CloseHandle(windowEvent); - } - - cleanupConfigReader(); - cleanupKeyboard(); } void ctrlc(int sig) { @@ -56,31 +51,26 @@ int main() { FARPROC shellProc = GetProcAddress(wmDll, "ShellProc"); if (shellProc == NULL) { - reportWin32Error(L"GetProcAddress failed for shell even callback"); - goto cleanup; - } - - HotKeyProcType HotKeyProc = (HotKeyProcType)GetProcAddress(wmDll, "HotkeyProc"); - - if (HotKeyProc == NULL) { - reportWin32Error(L"GetProcAddress failed for shell even callback"); + reportWin32Error(L"GetProcAddress for ShellProc"); goto cleanup; } - windowEvent = CreateEventW(NULL, FALSE, FALSE, L"LightWMWindowEvent"); - - if (windowEvent == NULL) { - reportWin32Error(L"CreateEvent"); - goto cleanup; - } - hookShellProcHandle = SetWindowsHookExW(WH_SHELL, (HOOKPROC)shellProc, wmDll, 0); if (hookShellProcHandle == NULL) { - reportWin32Error(L"SetWindowsHookExW failed for shell hook"); + reportWin32Error(L"SetWindowsHookExW for shell hook"); goto cleanup; } + SetMessageThreadIdType setMessageThreadId = (SetMessageThreadIdType)GetProcAddress(wmDll, "SetMessageThreadId"); + + if (setMessageThreadId == NULL) { + reportWin32Error(L"GetProcAddress for setMessageThreadId"); + goto cleanup; + } + + setMessageThreadId(GetCurrentThreadId()); + signal(SIGINT, ctrlc); //Load the configuration @@ -95,28 +85,24 @@ int main() { reportWin32Error(L"Setup keyboard config"); goto cleanup; } - + // Handle a message loop - tileWindows(); + //tileWindows(); MSG msg; - while (GetMessage(&msg, NULL, 0, 0) != 0) { - if(msg.message == WM_HOTKEY) { - //Because Win32 doesn't support hook callbacks with RegisterHotkey lets make our own callback. - assert(HotKeyProc != NULL); - - LRESULT ret = HotKeyProc(0, msg.wParam, msg.lParam); - if(ret != ERROR_SUCCESS) { - DEBUG_PRINT("HotKey was unhandled! Ret: %i", ret); - } - continue; - } else if (WaitForSingleObject(windowEvent, INFINITE) == WAIT_FAILED) { - reportWin32Error(L"WaitForSingleObject"); - goto cleanup; + while (GetMessage(&msg, -1, 0, 0) != 0) { + switch(msg.message) + { + case WM_HOTKEY: + LRESULT ret = handleHotkey(msg.wParam, msg.lParam); + if(ret != ERROR_SUCCESS) { + DEBUG_PRINT("HotKey was unhandled! Ret: %lli", ret); + } + break; + case LWM_WINDOW_EVENT: + //tileWindows(); + DEBUG_PRINT("LWM_WINDOW_EVENT Message handled"); + break; } - - Sleep(100); - - tileWindows(); } cleanup: diff --git a/wm_dll.c b/wm_dll.c index fea21d0..051d098 100644 --- a/wm_dll.c +++ b/wm_dll.c @@ -1,74 +1,40 @@ -#include #include +#include #include "targetver.h" -#include "actions.h" +#include "messages.h" #include "debug.h" +DWORD messageThreadId = 0; + __declspec(dllexport) LRESULT CALLBACK ShellProc(int code, WPARAM wparam, LPARAM lparam) { + DEBUG_PRINT("ShellProc hook called with a thread id of %i", messageThreadId); //TODO Cleanup this debug if (code == HSHELL_WINDOWCREATED || code == HSHELL_WINDOWDESTROYED) { - const HANDLE windowEvent = OpenEventW(EVENT_ALL_ACCESS, FALSE, L"LightWMWindowEvent"); - if (windowEvent) { - SetEvent(windowEvent); - CloseHandle(windowEvent); - } + PostThreadMessageW(messageThreadId, LWM_WINDOW_EVENT, 0, 0); } return CallNextHookEx(NULL, code, wparam, lparam); } -__declspec(dllexport) LRESULT HotkeyProc(int code, WPARAM wparam, LPARAM lparam) { - DEBUG_PRINT("HotkeyProc called - %i %i %i", code, wparam, lparam); - - switch(wparam) - { - //TODO Can either trigger an event like the ShellProc callback, or handle directly. - // one method to handle virtual desktops is using the IVirtualDesktopManager in ShObjIdl but - // that is only available for Window 10 1809 or later. - case WORKSPACE_1: - puts("Switch to workspace 1"); - break; - case WORKSPACE_2: - puts("Switch to workspace 2"); - break; - case WORKSPACE_3: - puts("Switch to workspace 3"); - break; - case WORKSPACE_4: - puts("Switch to workspace 4"); - break; - case WINDOW_UP: - puts("Highlight window above"); - break; - case WINDOW_DOWN: - puts("Highlight window below"); - break; - case WINDOW_LEFT: - puts("Highlight window left"); - break; - case WINDOW_RIGHT: - puts("Highlight window right"); - break; - default: - DEBUG_PRINT("Unhandled hotkey message! Hotkey ID: %i", wparam); - break; - } - - return ERROR_SUCCESS; - // return CallNextHookEx(NULL, code, wparam, lparam); +__declspec(dllexport) void SetMessageThreadId(DWORD threadId) +{ + DEBUG_PRINT("Set thread ID to %i", threadId); //TODO Cleanup this debug string + messageThreadId = threadId; } BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD ulReasonForCall, LPVOID lpReserved) { switch(ulReasonForCall) { case DLL_PROCESS_ATTACH: + DEBUG_PRINT("DLL Loaded"); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: + DEBUG_PRINT("DLL Freed"); break; default: break; diff --git a/wm_dll.def b/wm_dll.def index b674209..1da7f92 100644 --- a/wm_dll.def +++ b/wm_dll.def @@ -1,4 +1,4 @@ LIBRARY lightwm_dll EXPORTS -ShellProc -HotkeyProc \ No newline at end of file +SetMessageThreadId +ShellProc \ No newline at end of file