diff --git a/.gitignore b/.gitignore index c0eb2ce..80604b8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,13 @@ *.obj *.exp *.exe +*.pdb +*.ilk + tags + +.idea/ +.vs/ +debug/ +release/ +*.res diff --git a/Makefile b/Makefile index 16a14db..621abf6 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,57 @@ +# Define compiler and linker CC = cl -LD = link +LINKER = link +RC = rc -EXEC = lightwm.exe -DLL = lightwm_dll.dll +# Define common compiler flags +CFLAGS = /W3 /EHsc -all: clean $(DLL) $(EXEC) +# Define debug specific compiler flags +DBGCFLAGS = $(CFLAGS) /DDEBUG /Zi /Wall -$(EXEC): wm.c - $(CC) wm.c tiling.c error.c user32.lib /link /out:$(EXEC) +# Define release specific compiler flags +RELCFLAGS = $(CFLAGS) /Ox -$(DLL): wm_dll.obj - $(LD) wm_dll.obj user32.lib /dll /out:$(DLL) +# Define source files +EXE_SRCS = wm.c tiling.c error.c config.c keyboard.c +DLL_SRCS = wm_dll.c error.c +RES_FILE = wm_resources.rc -wm_dll.obj: wm_dll.c - $(CC) /c wm_dll.c +# Define directories +DBGDIR = debug +RELDIR = release + +# Define output names +EXE_NAME = lightwm.exe +DLL_NAME = lightwm_dll.dll + +all: debug release + +debug: clean prep resource wm.c + $(CC) $(DBGCFLAGS) $(EXE_SRCS) $(RES_FILE).res /link user32.lib shell32.lib ole32.lib shlwapi.lib /out:$(DBGDIR)/$(EXE_NAME) + $(CC) $(DBGCFLAGS) $(DLL_SRCS) /LD /link user32.lib /DEF:wm_dll.def /out:$(DBGDIR)/$(DLL_NAME) + +release: clean prep resource wm.c + $(CC) $(RELCFLAGS) $(EXE_SRCS) $(RES_FILE).res /link user32.lib shell32.lib ole32.lib shlwapi.lib /out:$(RELDIR)/$(EXE_NAME) + $(CC) $(RELCFLAGS) $(DLL_SRCS) /LD /link user32.lib /DEF:wm_dll.def /out:$(RELDIR)/$(DLL_NAME) + +resource: $(RES_FILE) + $(RC) /fo $(RES_FILE).res $(RES_FILE) + +prep: + @echo off > temp.bat && \ + echo IF NOT EXIST $(DBGDIR) mkdir $(DBGDIR) >> temp.bat && \ + echo IF NOT EXIST $(RELDIR) mkdir $(RELDIR) >> temp.bat && \ + temp.bat && \ + del temp.bat clean: - del *.obj *.exe *.dll *.lib *.exp + del *.obj *.exe *.dll *.lib *.exp *.ilk *.pdb *.res + + @echo off > temp.bat && \ + echo IF EXIST $(DBGDIR) del /S /Q $(DBGDIR) >> temp.bat && \ + echo IF EXIST $(RELDIR) del /S /Q $(RELDIR) >> temp.bat && \ + echo IF EXIST $(DBGDIR) rd /S /Q $(DBGDIR) >> temp.bat && \ + echo IF EXIST $(RELDIR) rd /S /Q $(RELDIR) >> temp.bat && \ + temp.bat && \ + del temp.bat \ No newline at end of file diff --git a/actions.h b/actions.h new file mode 100644 index 0000000..64ec8f1 --- /dev/null +++ b/actions.h @@ -0,0 +1,26 @@ +#pragma once + +//Just add the action to this preprocessor macro to generate the ENUM and string array +#define FOREACH_ACTION(ACTION) \ + ACTION(ACTION_NONE) \ + ACTION(WORKSPACE_1) \ + ACTION(WORKSPACE_2) \ + ACTION(WORKSPACE_3) \ + ACTION(WORKSPACE_4) \ + ACTION(WINDOW_LEFT) \ + ACTION(WINDOW_RIGHT) \ + ACTION(WINDOW_UP) \ + ACTION(WINDOW_DOWN) + + +#define GENERATE_ENUM(ENUM) ENUM, +#define GENERATE_STRINGS(STRING) #STRING, + +typedef enum +{ + FOREACH_ACTION(GENERATE_ENUM) +} Actions; + +static const char* ACTION_STRINGS[] = { + FOREACH_ACTION(GENERATE_STRINGS) +}; \ No newline at end of file diff --git a/config.c b/config.c new file mode 100644 index 0000000..501f997 --- /dev/null +++ b/config.c @@ -0,0 +1,258 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "resource.h" +#include "string_helpers.h" +#include "debug.h" + +#define BUFF_SIZE 65536 + +PWSTR szConfigFilePath; + +char *defaultConfigData = NULL; + +ConfigItems *configItems; + +BOOL createDefaultConfigFile(HINSTANCE); + +BOOL loadDefaultConfigResourceData(HINSTANCE); + +BOOL writeDefaultConfigDataToFile(); + +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"); + + if (configFileHandle == NULL) { + reportWin32Error(L"Config file could not be opened"); + cleanupConfigReader(); + return ERROR_INVALID_HANDLE; + } + + char line[BUFF_SIZE]; + + configItems = (ConfigItems *) malloc(sizeof(ConfigItems)); + + configItems->configItem = (ConfigItem *) malloc(sizeof(ConfigItem) * getLineCount(configFileHandle) + 1); + configItems->configItemsCount = getLineCount(configFileHandle); + + if (configItems->configItem == NULL) { + reportWin32Error(L"Allocation ConfigItem memory"); + cleanupConfigReader(); + return ERROR_NOT_ENOUGH_MEMORY; + } + + for (size_t lineCount = 0; fgets(line, sizeof(line), configFileHandle); lineCount++) { + if (strlen(line) == 0) { + continue; + } + + //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); + + //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); + + 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); + } + + + fclose(configFileHandle); + + return ERROR_SUCCESS; +} + +void getConfigFilePath() { + //TODO: We don't check other results possible results, i.e E_FAIL + const HRESULT getAppDataPathResult = SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &szConfigFilePath); + + if (!SUCCEEDED(getAppDataPathResult)) { + switch (getAppDataPathResult) { + case E_FAIL: + SetLastError(ERROR_DIRECTORY_NOT_SUPPORTED); + break; + case E_INVALIDARG: + SetLastError(ERROR_TIERING_INVALID_FILE_ID); + break; + default: + SetLastError(ERROR_GEN_FAILURE); + } + reportWin32Error(L"Could not get the users appdata directory"); + cleanupConfigReader(); + exit(ERROR_PATH_NOT_FOUND); + } + + assert(szConfigFilePath != NULL); + + //TODO just found a bug that causes the path to become corrupt + const HRESULT concatStringResult = StringCchCatW(szConfigFilePath, MAX_PATH, L"\\lightwm.config\0"); + + if (!SUCCEEDED(concatStringResult)) { + switch (concatStringResult) { + case STRSAFE_E_INVALID_PARAMETER: + SetLastError(ERROR_INVALID_PARAMETER); + break; + case STRSAFE_E_INSUFFICIENT_BUFFER: + SetLastError(ERROR_INSUFFICIENT_BUFFER); + break; + default: + SetLastError(ERROR_GEN_FAILURE); + } + reportWin32Error(L"Could not append file name to appdata path"); + cleanupConfigReader(); + exit(GetLastError()); + } +} + +BOOL loadConfigFile(HINSTANCE resourceModuleHandle) { + szConfigFilePath = (PWSTR) malloc(sizeof(WCHAR) * MAX_PATH); + getConfigFilePath(); + assert(szConfigFilePath != NULL); + + if (!PathFileExistsW(szConfigFilePath)) { + if (!createDefaultConfigFile(resourceModuleHandle)) { + reportWin32Error(L"Create a default config file"); + return FALSE; + } + } + + readConfigFile(); + + return TRUE; +} + +void freeConfigItems(ConfigItems *items) { + if (items != NULL) { + for(size_t i = 0; i < items->configItemsCount; i++) { + free(items->configItem[i].name); + free(items->configItem[i].value); + } + free(items); + } else { + DEBUG_PRINT("items ptr was freed earlier!"); + } +} + +void cleanupConfigReader() { + freeConfigItems(configItems); + DEBUG_PRINT("Cleaned up config items"); + + CoTaskMemFree(szConfigFilePath); + DEBUG_PRINT("Cleaned up filePath ptr"); +} + +ConfigItems *getConfigItems() { + return configItems; +} + +/** + * Private definitions here + **/ +BOOL createDefaultConfigFile(HINSTANCE resourceModuleHandle) { + if (!loadDefaultConfigResourceData(resourceModuleHandle)) { + return FALSE; + } + + if (!writeDefaultConfigDataToFile()) { + return FALSE; + } + + return TRUE; +} + +BOOL loadDefaultConfigResourceData(HINSTANCE resourceModuleHandle) { + const HRSRC hRes = FindResource(resourceModuleHandle, MAKEINTRESOURCE(IDR_DEFAULT_CONFIG), RT_RCDATA); + + if (hRes == NULL) { + puts("Could not get HRSRC Handle"); + DEBUG_PRINT("FindResource Error: %lu", GetLastError()); + return FALSE; + } + + const HGLOBAL hData = LoadResource(resourceModuleHandle, hRes); + + if (hData == NULL) { + puts("Could not load resource"); + DEBUG_PRINT("LoadResource Error: %lu", GetLastError()); + return FALSE; + } + + const LPVOID defaultConfigResourceData = LockResource(hData); + + if (defaultConfigResourceData == NULL) { + puts("Could not read resource"); + DEBUG_PRINT("LockResource Error: %lu", GetLastError()); + return FALSE; + } + + size_t defaultConfigResourceDataLen = strlen(defaultConfigResourceData) + 1; //+1 for the null char + defaultConfigData = (char *) malloc(sizeof(char) * defaultConfigResourceDataLen); //TODO Error checking + strcpy(defaultConfigData, defaultConfigResourceData); + + return TRUE; +} + +BOOL writeDefaultConfigDataToFile() { + FILE *configFileHandle = _wfopen(szConfigFilePath, L"w"); + + if (configFileHandle == NULL) { + return FALSE; + } + + fprintf(configFileHandle, defaultConfigData); + + puts("Created default config file"); + + fclose(configFileHandle); + + return TRUE; +} + +size_t getLineCount(FILE *file) { + int counter = 0; + for (;;) { + char buf[BUFF_SIZE]; + const size_t res = fread(buf, 1, BUFF_SIZE, file); + if (ferror(file)) { + return -1; + } + + for (int i = 0; i < res; i++) { + if (buf[i] == '\n') { + counter++; + } + } + + if (feof(file)) { + break; + } + } + + fseek(file, 0, SEEK_SET); + + return counter; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..6467cb8 --- /dev/null +++ b/config.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include + +typedef struct _ConfigItem ConfigItem; +typedef struct _ConfigItems ConfigItems; + +typedef struct _ConfigItem { + char *name; + char *value; +} ConfigItem; + +typedef struct _ConfigItems { + ConfigItem *configItem; + size_t configItemsCount; +} ConfigItems; + +BOOL loadConfigFile(HINSTANCE); + +void cleanupConfigReader(); + +ConfigItems *getConfigItems(); diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..7de66a3 --- /dev/null +++ b/debug.h @@ -0,0 +1,33 @@ +#pragma once + +#ifdef DEBUG + +#include +#include + +inline void DebugPrintHelper(const char* format, ...) { + char buffer[1024]; + va_list args; + va_start(args, format); + vsprintf_s(buffer, 1024, format, args); + va_end(args); + OutputDebugStringA(buffer); +} + +inline void DebugWPrintHelper(const wchar_t* format, ...) { + wchar_t buffer[1024]; + va_list args; + va_start(args, format); + vswprintf_s(buffer, 1024, format, args); + va_end(args); + OutputDebugStringW(buffer); +} + +#define DEBUG_PRINT(format, ...) printf("DEBUG: %s Line %d: " format "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ + DebugPrintHelper(format, ##__VA_ARGS__) +#define DEBUG_WPRINT(format, ...) wprintf(L"DEBUG: %ls Line %d: " format "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ + DebugWPrintHelper(format, ##__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 new file mode 100644 index 0000000..c0e9eff --- /dev/null +++ b/keyboard.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include + +#include "keyboard.h" + +#include "debug.h" + +UINT getModifier(const char *value); + +UINT getKeyCode(const char *value); + +void addKeyboardKeybind(enum Action action, UINT modifier, UINT keyCode); + +BOOL initializeKeyboardConfig(const ConfigItems *configItems) { + assert(configItems != NULL); + assert(configItems->configItem != NULL); + assert(configItems->configItemsCount != 0); + + for (size_t i = 1; i <= configItems->configItemsCount; i++) { + ADD_KEYBOARD_KEYBIND(i, WORKSPACE_1); + ADD_KEYBOARD_KEYBIND(i, WORKSPACE_2); + ADD_KEYBOARD_KEYBIND(i, WORKSPACE_3); + ADD_KEYBOARD_KEYBIND(i, WORKSPACE_4); + ADD_KEYBOARD_KEYBIND(i, WINDOW_UP); + ADD_KEYBOARD_KEYBIND(i, WINDOW_DOWN); + ADD_KEYBOARD_KEYBIND(i, WINDOW_LEFT); + ADD_KEYBOARD_KEYBIND(i, WINDOW_RIGHT); + } + + return TRUE; +} + +void cleanupKeyboard() { + UnregisterHotKey(NULL, WORKSPACE_1); + UnregisterHotKey(NULL, WORKSPACE_2); + UnregisterHotKey(NULL, WORKSPACE_3); + UnregisterHotKey(NULL, WORKSPACE_4); + UnregisterHotKey(NULL, WINDOW_UP); + UnregisterHotKey(NULL, WINDOW_DOWN); + UnregisterHotKey(NULL, WINDOW_LEFT); + UnregisterHotKey(NULL, WINDOW_RIGHT); + DEBUG_PRINT("Unregistered all hotkeys"); +} + +UINT getModifier(const char *value) { + if (strncmp(value, "alt", 3) == 0) { + return MOD_ALT; + } + if (strncmp(value, "win", 3) == 0) { + return MOD_WIN; + } + if (strncmp(value, "ctrl", 4) == 0) { + return MOD_CONTROL; + } + if (strncmp(value, "shift", 5) == 0) { + return MOD_SHIFT; + } + return MOD_ALT; //TODO Throw an error here +} + +UINT getKeyCode(const char *value) { + DEBUG_PRINT("GetKeyCode char value '%c'", value[strlen(value) - 1]); + return VkKeyScanEx(value[strlen(value) - 1], GetKeyboardLayout(0)); +} + +void addKeyboardKeybind(enum Action action, UINT modifier, UINT keyCode) { + if (!RegisterHotKey(NULL, action, modifier | MOD_NOREPEAT, keyCode)) { + if (GetLastError() == ERROR_HOTKEY_ALREADY_REGISTERED) { + puts("Warn: Hotkey already registerd\n"); + return; + } + + MessageBox(NULL, "Failed to register hotkey.", "Error", MB_OK | MB_ICONERROR); + } + + DEBUG_PRINT("Registered %s hotkey", ACTION_STRINGS[action]); +} + +LRESULT handleHotkey(WPARAM wparam, LPARAM lparam) { + switch (wparam) { + case WORKSPACE_1: + DEBUG_PRINT("Switch to workspace 1"); + break; + case WORKSPACE_2: + DEBUG_PRINT("Switch to workspace 2"); + break; + case WORKSPACE_3: + DEBUG_PRINT("Switch to workspace 3"); + break; + case WORKSPACE_4: + DEBUG_PRINT("Switch to workspace 4"); + break; + case WINDOW_UP: + DEBUG_PRINT("Highlight window above"); + break; + case WINDOW_DOWN: + DEBUG_PRINT("Highlight window below"); + break; + case WINDOW_LEFT: + DEBUG_PRINT("Highlight window left"); + break; + case WINDOW_RIGHT: + DEBUG_PRINT("Highlight window right"); + break; + default: + DEBUG_PRINT("Unhandled hotkey message! Hotkey ID: %lli", wparam); + break; + } + + return ERROR_SUCCESS; +} diff --git a/keyboard.h b/keyboard.h new file mode 100644 index 0000000..615d725 --- /dev/null +++ b/keyboard.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "config.h" +#include "actions.h" + +#define ADD_KEYBOARD_KEYBIND(i, action) \ +if(strcmp(configItems->configItem[i].name, ACTION_STRINGS[action]) == 0) \ +{ \ + addKeyboardKeybind( \ + action, \ + getModifier(configItems->configItem[i].value), \ + getKeyCode(configItems->configItem[i].value) \ + ); \ +} + +BOOL initializeKeyboardConfig(ConfigItems* configItems); +void cleanupKeyboard(); +LRESULT handleHotkey(WPARAM lparam, LPARAM wparam); \ No newline at end of file diff --git a/lightwm.config b/lightwm.config new file mode 100644 index 0000000..f07f0ae --- /dev/null +++ b/lightwm.config @@ -0,0 +1,8 @@ +WORKSPACE_1 alt+1 +WORKSPACE_2 alt+2 +WORKSPACE_3 alt+3 +WORKSPACE_4 alt+4 +WINDOW_UP alt+i +WINDOW_DOWN alt+k +WINDOW_LEFT alt+j +WINDOW_RIGHT alt+l 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/resource.h b/resource.h new file mode 100644 index 0000000..4a8245b --- /dev/null +++ b/resource.h @@ -0,0 +1,12 @@ +#define IDR_DEFAULT_CONFIG 101 +#define IDS_LIGHTWM_CNF_NAME 102 + +// Next default values for new objects +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/string_helpers.h b/string_helpers.h new file mode 100644 index 0000000..64fc506 --- /dev/null +++ b/string_helpers.h @@ -0,0 +1,40 @@ +#pragma once + +void trim(char* str); +void removeControlChars(char* str); + +inline void strip(char* str) +{ + size_t len = strlen(str); + char *start = str; + char *end = str + len - 1; + + while (*start == ' ') + { + start++; + } + + while (*end == ' ') + { + end--; + } + + memmove(str, start, end - start + 1); + str[end - start + 1] = '\0'; +} + +inline void removeControlChars(char* str) +{ + int i, j = 0; + char temp[1024]; // Assuming the maximum length of the string is 1024 + + for (i = 0; str[i] != '\0'; ++i) + { + if ((unsigned char)str[i] < 32) continue; // Skip control characters + temp[j] = str[i]; + ++j; + } + + temp[j] = '\0'; // Null terminate the new string + strncpy(str, temp, sizeof(temp)); +} \ No newline at end of file diff --git a/targetver.h b/targetver.h new file mode 100644 index 0000000..17a0f88 --- /dev/null +++ b/targetver.h @@ -0,0 +1,3 @@ +#pragma once + +#include \ No newline at end of file diff --git a/wm.c b/wm.c index 2481f78..2d5f5cb 100644 --- a/wm.c +++ b/wm.c @@ -2,25 +2,32 @@ #include #include #include -#include "error.h" + #include "tiling.h" +#include "error.h" +#include "keyboard.h" +#include "config.h" +#include "messages.h" + +#include "debug.h" + -HHOOK hookHandle; HMODULE wmDll; -HANDLE windowEvent; +HHOOK hookShellProcHandle; void cleanupObjects() { - if (hookHandle) { - UnhookWindowsHookEx(hookHandle); - } + cleanupKeyboard(); + + cleanupConfigReader(); + + if (hookShellProcHandle) { + UnhookWindowsHookEx(hookShellProcHandle); + } + if (wmDll) { FreeLibrary(wmDll); } - - if (windowEvent) { - CloseHandle(windowEvent); - } } void ctrlc(int sig) { @@ -31,9 +38,54 @@ void ctrlc(int sig) { exit(ERROR_SUCCESS); } +LPVOID createAddressSharedMemory() { + // Create a shared memory region + HANDLE hMapFile = CreateFileMapping( + INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + sizeof(DWORD), + "lightwmthreadid" + ); + + if (hMapFile == NULL) { + DEBUG_PRINT("Could not create file mapping object (%lu).", GetLastError()); + return NULL; + } + + LPVOID lpMapAddress = MapViewOfFile( + hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + sizeof(DWORD) + ); + + if (lpMapAddress == NULL) { + DEBUG_PRINT("Could not map view of file (%lu).", GetLastError()); + CloseHandle(hMapFile); + return NULL; + } + + return lpMapAddress; +} + int main() { SetProcessDPIAware(); + //Create shared memory + LPVOID sharedMemoryAddress = createAddressSharedMemory(); + + if(sharedMemoryAddress == NULL) { + reportWin32Error(L"Create Shared Memory"); + goto cleanup; + } + + DWORD dwThreadId = GetCurrentThreadId(); + DEBUG_PRINT("Lightwm.exe thread id: %lu", dwThreadId); + CopyMemory((PVOID)sharedMemoryAddress, &dwThreadId, sizeof(DWORD)); + wmDll = LoadLibraryW(L"lightwm_dll"); if (wmDll == NULL) { @@ -44,41 +96,54 @@ int main() { FARPROC shellProc = GetProcAddress(wmDll, "ShellProc"); if (shellProc == NULL) { - reportWin32Error(L"GetProcAddress"); + reportWin32Error(L"GetProcAddress for ShellProc"); goto cleanup; } + + hookShellProcHandle = SetWindowsHookExW(WH_SHELL, (HOOKPROC)shellProc, wmDll, 0); - windowEvent = CreateEventW(NULL, FALSE, FALSE, L"LightWMWindowEvent"); - - if (windowEvent == NULL) { - reportWin32Error(L"CreateEvent"); + if (hookShellProcHandle == NULL) { + reportWin32Error(L"SetWindowsHookExW for shell hook"); goto cleanup; } - hookHandle = SetWindowsHookExW(WH_SHELL, (HOOKPROC)shellProc, wmDll, 0); + signal(SIGINT, ctrlc); - if (hookHandle == NULL) { - reportWin32Error(L"SetWindowsHookExW"); - goto cleanup; + if(!loadConfigFile(NULL)) + { + reportWin32Error(L"Load config file"); + goto cleanup; + } + + if(!initializeKeyboardConfig(getConfigItems())) + { + reportWin32Error(L"Setup keyboard config"); + goto cleanup; } - signal(SIGINT, ctrlc); - + // Handle a message loop tileWindows(); - - for (;;) { - if (WaitForSingleObject(windowEvent, INFINITE) == WAIT_FAILED) { - reportWin32Error(L"WaitForSingleObject"); - goto cleanup; + MSG msg; + while (GetMessage(&msg, (HWND)-1, 0, 0) != 0) { + switch(msg.message) + { + case WM_HOTKEY: + const 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; + default: + break; } - - Sleep(200); - - tileWindows(); } cleanup: cleanupObjects(); - + return EXIT_FAILURE; } diff --git a/wm_dll.c b/wm_dll.c index 11b55e5..fd41217 100644 --- a/wm_dll.c +++ b/wm_dll.c @@ -1,32 +1,72 @@ #include +#include -__declspec(dllexport) LRESULT CALLBACK ShellProc(int code, WPARAM wparam, LPARAM lparam) { +#include "targetver.h" +#include "messages.h" + +#include "debug.h" + +DWORD lightwmMainThreadId = 0; + +void readAddress() { + // Open the shared memory region + HANDLE hMapFile = OpenFileMapping( + FILE_MAP_ALL_ACCESS, // read/write access + FALSE, // do not inherit the name + "lightwmthreadid" // name of mapping object + ); + + if (hMapFile == NULL) { + DEBUG_PRINT("Could not open file mapping object (%lu).", GetLastError()); + return; + } + + LPVOID lpMapAddress = MapViewOfFile( + hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + sizeof(DWORD) + ); + + if (lpMapAddress == NULL) { + DEBUG_PRINT("Could not map view of file (%lu).", GetLastError()); + CloseHandle(hMapFile); + return; + } + CopyMemory(&lightwmMainThreadId, (PVOID)lpMapAddress, sizeof(DWORD)); + + UnmapViewOfFile(lpMapAddress); + CloseHandle(hMapFile); +} + +__declspec(dllexport) LRESULT CALLBACK ShellProc(int code, WPARAM wparam, LPARAM lparam) { if (code == HSHELL_WINDOWCREATED || code == HSHELL_WINDOWDESTROYED) { - HANDLE windowEvent = OpenEventW(EVENT_ALL_ACCESS, FALSE, L"LightWMWindowEvent"); - if (windowEvent) { - SetEvent(windowEvent); - CloseHandle(windowEvent); - } + PostThreadMessageW(lightwmMainThreadId, LWM_WINDOW_EVENT, 0, 0); } return CallNextHookEx(NULL, code, wparam, lparam); } - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReasonForCall, LPVOID lpReserved) +BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD ulReasonForCall, LPVOID lpReserved) { switch(ulReasonForCall) { - case DLL_PROCESS_ATTACH: + case DLL_PROCESS_ATTACH: + DEBUG_PRINT("DLL Loaded"); + readAddress(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: - break; + DEBUG_PRINT("DLL Freed"); + break; + default: + break; } - + return TRUE; } diff --git a/wm_dll.def b/wm_dll.def new file mode 100644 index 0000000..81088c8 --- /dev/null +++ b/wm_dll.def @@ -0,0 +1,3 @@ +LIBRARY lightwm_dll +EXPORTS +ShellProc \ No newline at end of file diff --git a/wm_resources.rc b/wm_resources.rc new file mode 100644 index 0000000..e7f769f --- /dev/null +++ b/wm_resources.rc @@ -0,0 +1,50 @@ +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +IDR_DEFAULT_CONFIG RCDATA "lightwm.config" + +STRINGTABLE +BEGIN + IDS_LIGHTWM_CNF_NAME "lightwm.config" +END +#endif + + +///////////////////////////////////////////////////////////////////////////// +// English (Canada) resources (just copies English US) +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENC) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_CAN + +#ifdef APSTUDIO_INVOKED + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif + +#endif + +#ifndef APSTUDIO_INVOKED +#endif \ No newline at end of file