- Workspacer
- Komorebi
(Most features and concepts are copied from these or DWM or I3)
I wrote this application to target my specific workflow where 90% of windows come from a few different applications (browser, terminal, IDE, chat, email) and I want to have those windows auto layed out for me into tiles and workspaces so that I can quickly navigate between them using the keyboard. All other windows I let float.
- You will need to compile the application to install it
- Make sure that MSVC (C compiler) is installed.
- https://visualstudio.microsoft.com/downloads/
- Visual Studio Build Tools is enough if you don't want to install Visual Studio
- Run setvccars.bar to add nmake and cl.exe to the path. (You make need to adjust the paths in the batch depending one where MSVC is installed)
- Make any configuration/customizations
- Run nmake
- Run nmake publish
- Run bin\SimpleWindowManager.exe (to run the application)
- Make sure that MSVC (C compiler) is installed.
- Moving between workpsaces is implemented by moving the windows that are not assigned to a monitor to screen coordinates that are outside of display monitor.
- This seems to provide the cleanest transitions but when the window manager is stopped the window remain positioned outside of the display and can be a pain move back onto the screen.
- There are a couple programs that have logic to guard against this (SSMS, Visual Studio, ConEmu), these can be configured to fallback to using minimize un-minimize as the hide unhide method. This works but the transitions are a little awkward.
- Workspaces
- Define up to 9 different workspaces along with rules about which windows will be managed by the workspace. (Usually defined by the process name, window class, window title or any combination of those). Windows matching these rules will automatically be layed out by the workspace in its current layout
- Layouts
- Tile Layout
- Monacle
- Deck (hybrid of Tile and Monacle where the main position is fixed and the windows in the secondary position can be cycled through)
- Scratch Windows
- Simple short lived terminal application mapped to key bindings.
- Scratch Menus
- A really simple generic menu/filter that can be used as a launcher, window switcher etc (similar to fzf)
- KeyBindings
- Map keybindings to arbitrary commands
- Workspace rules and definitions are set in Config.c
Key Binding | Action |
---|---|
ALT+? | Show keybindings |
ALT+J | Select Next Window |
ALT+K | Select Previous Window |
ALT+Enter | Move selected window to main |
ALT+Shift+J | Move window to next postion |
ALT+Shift+K | Move window to previous position |
ALT+L | Increase width of main window |
ALT+H | Decrease width of main window |
ALT+T | Switch to tile layout |
ALT+D | Switch to deck layout |
ALT+M | Switch to monacle layout |
ALT+Space | Toggle next layout |
ALT+Shirt+C | Close selected Window |
ALT+, | Select next monitor |
ALT+[1-9] | Select workspace by number |
ALT+O | Goto previously selected workspace |
ALT+Shift+[1-9] | Move selected window to workspace by number |
ALT+V | Toggle Windows Taskbar |
ALT+A | Toggle mode where all windows that match a filter are added to the current workspace |
ALT+Z | Toggle mode where all windows are added to current workspace |
ALT+X | Toggle menu to view all available hotkeys |
tile_layoutip.mp4
monacle_layoutip.mp4
deck_layoutip.mp4
multiple_workspacesip.mp4
Below is an example workflow using the Programs Menu (Alt-P) to open VS Code and then using the same menu to open notepad, then the List Processes Menu (Super-9) to kill the notepad process, and then the List Windows Menu (Alt-Space) to find the NeoVim window switch to that workspace. The definitions for these windows are included in the sample config, adding custom workflow using menus is intended to be easy using shell commands or custom c functions.
Menu.mp4
Below is an example workflow using a scratch terminal defined as the NeoVim terminal with powershell running inside of it to quickly toggle the terminal generate a new guid and copy it into a editor
ScratchTerminal.mp4
moving_windows_between_workspacesip.mp4
multiple_monitorsip.mp4
- Configuration is done in C and is compiled into the binary
- There is a SampleConfig.c provided in the repository that the props.mk uses by default.
- For customizing, I believe it is easiest to create a new Config.c file and place it in a seperate repository and point the props.mk to that file. ex ..\dotfiles\SimpleWindowManager\Config.c
- You will need to a Config.c
- You will need to implement a void configure(Configuration *configuration) function
- This is where you can adjust bar color/bar height etc on the configuration structure
- This is also where you will define workspaces, keybindings, scratch windows and menu commands
- You will need to implement a void configure(Configuration *configuration) function
- The makefile currently looks for this in ..\SimpleWindowManagerConfig\config.c but can be updated in the make file
- Specifying workspaces: https://github.com/e82eric/SimpleWindowManager/blob/main/SampleConfig.c#L77
- Can be configured with simple contains matching using the exe name, windows title, or window class.
- It is also possible to use a C function to do advanced filtering using the Client struct
Example Workspace defined using exe name filters
WCHAR chromeTag = { 0xfa9e };
Workspace *browserWorkspace = workspace_register(L"Chrome", &chromeTag, &tileLayout);
workspace_register_processimagename_contains_filter(browserWorkspace, L"chrome.exe");
workspace_register_processimagename_contains_filter(browserWorkspace, L"brave.exe");
workspace_register_processimagename_contains_filter(browserWorkspace, L"firefox.exe");
workspace_register_processimagename_contains_filter(browserWorkspace, L"msedge.exe");
- A number of keybindings are registered by default: https://github.com/e82eric/SimpleWindowManager/blob/main/SimpleWindowManager.c#L3966
- These can be overridden by registering a keybinding using the same modifier key combination
- Simple key binding mapped to function with no args: https://github.com/e82eric/SimpleWindowManager/blob/main/SampleConfig.c#L196
- Key binding mapped to function with single argument: https://github.com/e82eric/SimpleWindowManager/blob/main/SampleConfig.c#L188
- Both menu items and OnSelection actions, can be provided by the stdout of a shell command or by a custom C function.
Example Menu items defined with shell command
MenuDefinition *programLauncher = menu_create_and_register();
MenuDefinition_AddNamedCommand(programLauncher, "ld:cmd /c fd -t f -g \"*{.lnk,.exe}\" \
\"%USERPROFILE%\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\" \
\"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\" \
\"%USERPROFILE%\\AppData\\Local\\Microsoft\\WindowsApps\" \
\"%USERPROFILE%\\Utilites\"",
FALSE,
FALSE);
MenuDefinition_ParseAndAddLoadCommand(programLauncher, "ld");
programLauncher->onSelection = open_program_scratch_callback;
keybinding_create_with_menu_arg("ProgramLauncherNotElevatedMenu", LAlt | LShift, VK_P, menu_run, programLauncher);
Example Menu items defined with C function
MenuDefinition *listProcessMenu = menu_create_and_register();
menu_definition_set_load_action(listProcessMenu, list_processes_run_no_sort);
MenuDefinition_AddNamedCommand(listProcessMenu, "procKill:cmd /c taskkill /f /pid {}", TRUE, FALSE);
MenuDefinition_AddNamedCommand(listProcessMenu, "windbg:powershell.exe -C '{}' -match '\\d{8}';windbg -p \"\"\"$([int]$Matches[0])\"\"\"", FALSE, TRUE);
MenuDefinition_AddLoadActionKeyBinding(listProcessMenu, VK_CONTROL, VK_1, list_processes_run_sorted_by_private_bytes);
MenuDefinition_AddLoadActionKeyBinding(listProcessMenu, VK_CONTROL, VK_2, list_processes_run_sorted_by_working_set);
MenuDefinition_AddLoadActionKeyBinding(listProcessMenu, VK_CONTROL, VK_3, list_processes_run_sorted_by_cpu);
MenuDefinition_AddLoadActionKeyBinding(listProcessMenu, VK_CONTROL, VK_4, list_processes_run_sorted_by_pid);
MenuDefinition_ParseAndSetRange(listProcessMenu, "46,8");
MenuDefinition_ParseAndAddKeyBinding(listProcessMenu, "ctl-k:procKill", FALSE);
MenuDefinition_ParseAndAddKeyBinding(listProcessMenu, "ctl-d:windbg", FALSE);
listProcessMenu->hasHeader = TRUE;
keybinding_create_with_menu_arg("ProcessListMenu", LWin, VK_9, menu_run, listProcessMenu);
- OnSelection action:
- Shell:
- C function: https://github.com/e82eric/SimpleWindowManager/blob/main/SampleConfig.c#L150
- Custom actions can also be defined using shell commands: https://github.com/e82eric/SimpleWindowManager/blob/main/SampleConfig.c#L156
- Shell:
Example scratch terminal to toggle neovim terminal with powershell running in it
ScratchWindow *nPowershellScratch = register_scratch_terminal_with_unique_string(
"Nvim Powershell",
"cmd /c nvim -c \"terminal powershell -nologo\"",
L"643763f5-f5cd-416e-a5c9-bef1f516863c");
keybinding_create_with_scratchwindow_arg("NvimPowershellScratchWindow", LAlt, VK_W, nPowershellScratch);
- These are defined using header text: width of variable part of segment: C function for getting the variable text:
Example bar segment definitions
configuration_add_bar_segment(configuration, L"UTC", 5, fill_system_time);
configuration_add_bar_segment(configuration, L"Time", 16, fill_local_time);
configuration_add_bar_segment(configuration, L"CPU", 6, fill_cpu);
configuration_add_bar_segment(configuration, L"Memory", 6, fill_memory_percent);
configuration_add_bar_segment(configuration, L"Volume", 6, fill_volume_percent);
configuration_add_bar_segment(configuration, L"Internet", 3, fill_is_connected_to_internet);
There is some support for using the mouse.
- If you drag a managed window into another client space a drop overlay will be show and if dropped the window will be moved to that location.
- If you drag a unmanaged/float window while holding ALT (default modifier) you will be able to move the window to another client area.
- If you hold down the ALT (default modifier) and drag your mouse you will be able to resize the x offset of the workspace.