Komorebi – A tiling window manager for Windows written in Rust

https://github.com/LGUG2Z/komorebi

komorebi

Tiling Window Management for Windows.

Tech for Palestine GitHub Workflow Status GitHub GitHub all releases GitHub commits since latest release (by date) for a branch Discord GitHub Sponsors Ko-fi Notado Feed YouTube

screenshot

komorebi is a tiling window manager that works as an extension to Microsoft's Desktop Window Manager in Windows 10 and above.

komorebi allows you to control application windows, virtual workspaces and display monitors with a CLI which can be used with third-party software such as whkd and AutoHotKey to set user-defined keyboard shortcuts.

komorebi aims to make as few modifications as possible to the operating system and desktop environment by default. Users are free to make such modifications in their own configuration files for komorebi, but these will remain opt-in and off-by-default for the foreseeable future.

Please refer to the documentation for instructions on how to install and configure komorebi, common workflows, a complete configuration schema reference and a complete CLI reference.

There is a Discord server available for komorebi-related discussion, help, troubleshooting etc. If you have any specific feature requests or bugs to report, please create an issue in this repository.

There is a YouTube channel where I post komorebi development videos. If you would like to be notified of upcoming videos please subscribe and turn on notifications.

komorebi is a free and source-available project, and one that encourages you to make charitable donations if you find the software to be useful and have the financial means.

I encourage you to make a charitable donation to the Palestine Children's Relief Fund before you consider sponsoring me on GitHub.

GitHub Sponsors is enabled for this project. Unfortunately I don't have anything specific to offer besides my gratitude and shout outs at the end of komorebi live development videos and tutorials.

If you would like to tip or sponsor the project but are unable to use GitHub Sponsors, you may also sponsor through Ko-fi.

Installation

A detailed installation and quickstart guide is available which shows how to get started using scoop, winget or building from source.

Watch the quickstart walkthrough video

Comparison With Fancy Zones

Community member Olge has created an excellent video which compares the default window management features of Windows 11, Fancy Zones and komorebi.

If you are not familiar with tiling window managers or if you are looking at komorebi and wondering "how is this different from Fancy Zones? 🤔", this short video will answer the majority of your questions.

Watch the comparison video

Demonstrations

@amnweb showing komorebi v0.1.28 running on Windows 11 with window borders, unfocused window transparency and animations enabled, using a custom status bar integrated using komorebi's Window Manager Event Subscriptions.

Windows.11.and.Komorebi.mp4

@haxibami showing komorebi running on Windows 11 with a terminal emulator, a web browser and a code editor. The original video can be viewed here.

0vadqFOyIJHglPRY.mp4

@aik2mlj showing komorebi running on Windows 11 with multiple workspaces, terminal emulators, a web browser, and the yasb status bar with the komorebi workspace widget enabled. The original video can be viewed here.

0e0dd7fc-7115-11ec-a064-42a1fbc2e1e9-v4_t10000011-wbjnuX5De6.mp4

Contribution Guidelines

If you would like to contribute to komorebi please take the time to carefully read the guidelines below.

Commit hygiene

  • Flatten all use statements
  • Run cargo +stable clippy and ensure that all lints and suggestions have been addressed before committing
  • Run cargo +nightly fmt --all to ensure consistent formatting before committing
  • Use git cz with the Commitizen CLI to prepare commit messages
  • Provide at least one short sentence or paragraph in your commit message body to describe your thought process for the changes being committed

PRs should contain only a single feature or bug fix

It is very difficult to review pull requests which touch multiple unrelated features and parts of the codebase.

Please do not submit pull requests like this; you will be asked to separate them into smaller PRs that deal only with one feature or bug fix at a time.

If you are working on multiple features and bug fixes, I suggest that you cut a branch called local-trunk from master which you keep up to date, and rebase the various independent branches you are working on onto that branch if you want to test them together or create a build with everything integrated.

Refactors to the codebase must have prior approval

komorebi is a mature codebase with an internal consistency and structure that has developed organically over close to half a decade.

There are countless hours of live coding videos demonstrating work on this project and showing new contributors how to do everything from basic tasks like implementing new komorebic commands to distinguishing monitors by manufacturer hardware identifiers and video card ports.

Refactors to the structure of the codebase are not taken lightly and require prior discussion and approval.

Please do not start refactoring the codebase with the expectation of having your changes integrated until you receive an explicit approval or a request to do so.

Similarly, when implementing features and bug fixes, please stick to the structure of the codebase as much as possible and do not take this as an opportunity to do some "refactoring along the way".

It is extremely difficult to review PRs for features and bug fixes if they are lost in sweeping changes to the structure of the codebase.

Breaking changes to user-facing interfaces are unacceptable

This includes but is not limited to:

No user should ever find that their configuration file has stopped working after upgrading to a new version of komorebi.

More often than not there are ways to reformulate changes that may initially seem like they require breaking user-facing interfaces into additive changes.

For some inspiration please take a look at this commit which added the ability for users to specify colours in komorebi.json in Hex format alongside RGB.

There is also a process in place for graceful, non-breaking, deprecation of configuration options that are no longer required.

License

komorebi is licensed under the PolyForm Strict 1.0.0 license. On a high level this means that you are free to do whatever you want with komorebi other than redistribution, or distribution of new works (ie. hard-forks) based on the software.

Anyone is free to make their own fork of komorebi with changes intended either for personal use or for integration back upstream via pull requests.

Please see CONTRIBUTING.md for more information about how code contributions to komorebi are licensed.

Development

If you use IntelliJ, you should enable the following settings to ensure that code generated by macros is recognised by the IDE for completions and navigation:

  • Set Expand declarative macros to Use new engine under "Settings > Langauges & Frameworks > Rust"
  • Enable the following experimental features:
    • org.rust.cargo.evaluate.build.scripts
    • org.rust.macros.proc

Logs and Debugging

Logs from komorebi will be appended to %LOCALAPPDATA%/komorebi/komorebi.log; this file is never rotated or overwritten, so it will keep growing until it is deleted by the user.

Whenever running the komorebic stop command or sending a Ctrl-C signal to komorebi directly, the komorebi process ensures that all hidden windows are restored before termination.

If however, you ever end up with windows that are hidden and cannot be restored, a list of window handles known to komorebi are stored and continuously updated in %LOCALAPPDATA%/komorebi//komorebi.hwnd.json.

Restoring Windows

Running komorebic restore-windows will read the list of window handles and forcibly restore them, regardless of whether the main komorebi process is running.

Panics and Deadlocks

If komorebi ever stops responding, it is most likely either due to either a panic or a deadlock. In the case of a panic, this will be reported in the log. In the case of a deadlock, there will not be any errors in the log, but the process and the log will appear frozen.

If you believe you have encountered a deadlock, you can compile komorebi with --features deadlock_detection and try reproducing the deadlock again. This will check for deadlocks every 5 seconds in the background, and if a deadlock is found, information about it will appear in the log which can be shared when opening an issue.

Window Manager State and Integrations

The current state of the window manager can be queried using the komorebic state command, which returns a JSON representation of the State struct.

This may also be polled to build further integrations and widgets on top of.

Window Manager Event Subscriptions

Named Pipes

It is possible to subscribe to notifications of every WindowManagerEvent and SocketMessage handled by komorebi using Named Pipes.

First, your application must create a named pipe. Once the named pipe has been created, run the following command:

komorebic.exe subscribe-pipe <your pipe name>

Note that you do not have to include the full path of the named pipe, just the name.

If the named pipe exists, komorebi will start pushing JSON data of successfully handled events and messages:

{"event":{"type":"AddSubscriber","content":"yasb"},"state":{}}
{"event":{"type":"FocusWindow","content":"Left"},"state":{}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":131444,"title":"komorebi – README.md","exe":"idea64.exe","class":"SunAwtFrame","rect":{"left":13,"top":60,"right":1520,"bottom":1655}}]},"state":{}}
{"event":{"type":"MonitorPoll","content":["ObjectCreate",{"hwnd":5572450,"title":"OLEChannelWnd","exe":"explorer.exe","class":"OleMainThreadWndClass","rect":{"left":0,"top":0,"right":0,"bottom":0}}]},"state":{}}
{"event":{"type":"FocusWindow","content":"Right"},"state":{}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":132968,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]},"state":{}}
{"event":{"type":"FocusWindow","content":"Down"},"state":{}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":329264,"title":"den — Mozilla Firefox","exe":"firefox.exe","class":"MozillaWindowClass","rect":{"left":1539,"top":894,"right":1520,"bottom":821}}]},"state":{}}
{"event":{"type":"FocusWindow","content":"Up"},"state":{}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":132968,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]},"state":{}}

You may then filter on the type key to listen to the events that you are interested in. For a full list of possible notification types, refer to the enum variants of WindowManagerEvent in komorebi and SocketMessage in komorebi::core.

Below is an example of how you can subscribe to and filter on events using a named pipe in nodejs.

const { exec } = require("child_process");
const net = require("net");
const pipeName = "\\\\.\\pipe\\komorebi-js";
const server = net.createServer((stream) => {
  console.log("Client connected");
  // Every time there is a workspace-related event, let's log the names of all
  // workspaces on the currently focused monitor, and then log the name of the
  // currently focused workspace on that monitor
  stream.on("data", (data) => {
    let json = JSON.parse(data.toString());
    let event = json.event;
    if (event.type.includes("Workspace")) {
      let monitors = json.state.monitors;
      let current_monitor = monitors.elements[monitors.focused];
      let workspaces = monitors.elements[monitors.focused].workspaces;
      let current_workspace = workspaces.elements[workspaces.focused];
      console.log(
        workspaces.elements
          .map((workspace) => workspace.name)
          .filter((name) => name !== null)
      );
      console.log(current_workspace.name);
    }
  });
  stream.on("end", () => {
    console.log("Client disconnected");
  });
});
server.listen(pipeName, () => {
  console.log("Named pipe server listening");
});
const command = "komorebic subscribe-pipe komorebi-js";
exec(command, (error, stdout, stderr) => {
  if (error) {
    console.error(`Error executing command: ${error}`);
    return;
  }
});

Unix Domain Sockets

It is possible to subscribe to notifications of every WindowManagerEvent and SocketMessage handled by komorebi using Unix Domain Sockets.

UDS are also the only mode of communication between komorebi and komorebic.

First, your application must create a socket in $ENV:LocalAppData\komorebi. Once the socket has been created, run the following command:

komorebic.exe subscribe-socket <your socket name>

If the socket exists, komorebi will start pushing JSON data of successfully handled events and messages as in the example above in the Named Pipes section.

Rust Client

As of v0.1.22 it is possible to use the komorebi-client crate to subscribe to notifications of every WindowManagerEvent and SocketMessage handled by komorebi in a Rust codebase.

Below is a simple example of how to use komorebi-client in a basic Rust application.

// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.28"}
use anyhow::Result;
use komorebi_client::Notification;
use komorebi_client::NotificationEvent;
use komorebi_client::UnixListener;
use komorebi_client::WindowManagerEvent;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
pub fn main() -> anyhow::Result<()> {
  let socket = komorebi_client::subscribe(NAME)?;
  for incoming in socket.incoming() {
    match incoming {
      Ok(data) => {
        let reader = BufReader::new(data.try_clone()?);
        for line in reader.lines().flatten() {
          let notification: Notification = match serde_json::from_str(&line) {
            Ok(notification) => notification,
            Err(error) => {
              log::debug!("discarding malformed komorebi notification: {error}");
              continue;
            }
          };
          // match and filter on desired notifications
        }
      }
      Err(error) => {
        log::debug!("{error}");
      }
    }
  }
}

A read-world example can be found in komokana.

Subscription Event Notification Schema

A JSON Schema of the event notifications emitted to subscribers can be generated with the komorebic notification-schema command. The output of this command can be redirected to the clipboard or a file, which can be used with services such as Quicktype to generate type definitions in different programming languages.

Communication over TCP

A TCP listener can optionally be exposed on a port of your choosing with the --tcp-port=N flag. If this flag is not provided to komorebi or komorebic start, no TCP listener will be created.

Once created, your client may send any SocketMessage to komorebi in the same way that komorebic would.

This can be used if you would like to create your own alternative to komorebic which incorporates scripting and various middleware layers, and similarly it can be used if you would like to integrate komorebi with a custom input handler.

If a client sends an unrecognized message, it will be disconnected and have to reconnect before trying to communicate again.

Socket Message Schema

A JSON Schema of socket messages used to send instructions to komorebi can be generated with the komorebic socket-schema command. The output of this command can be redirected to the clipboard or a file, which can be used with services such as Quicktype to generate type definitions in different programming languages.

Appreciations

  • First and foremost, thank you to my wife, both for naming this project and for her patience throughout its never-ending development

  • Thank you to @sitiom for being an exemplary open source community leader

  • Thank you to the developers of nog who came before me and whose work taught me more than I can ever hope to repay

  • Thank you to the developers of GlazeWM for pushing the boundaries of tiling window management on Windows with me and having an excellent spirit of collaboration

  • Thank you to @Ciantic for helping me bring the hidden Virtual Desktops cloaking function to komorebi

{
"by": "bsnnkv",
"descendants": 0,
"id": 40248304,
"score": 4,
"time": 1714747681,
"title": "Komorebi – A tiling window manager for Windows written in Rust",
"type": "story",
"url": "https://github.com/LGUG2Z/komorebi"
}
{
"author": "LGUG2Z",
"date": null,
"description": "A tiling window manager for Windows 🍉. Contribute to LGUG2Z/komorebi development by creating an account on GitHub.",
"image": "https://opengraph.githubassets.com/420f8ff08d780172e675daa42344f6198d47054b2703d099be677c60429b3830/LGUG2Z/komorebi",
"logo": "https://logo.clearbit.com/github.com",
"publisher": "GitHub",
"title": "GitHub - LGUG2Z/komorebi: A tiling window manager for Windows 🍉",
"url": "https://github.com/LGUG2Z/komorebi"
}
{
"url": "https://github.com/LGUG2Z/komorebi",
"title": "GitHub - LGUG2Z/komorebi: A tiling window manager for Windows 🍉",
"description": "komorebi Tiling Window Management for Windows. komorebi is a tiling window manager that works as an extension to...",
"links": [
"https://github.com/LGUG2Z/komorebi"
],
"image": "https://opengraph.githubassets.com/420f8ff08d780172e675daa42344f6198d47054b2703d099be677c60429b3830/LGUG2Z/komorebi",
"content": "<div><article><p></p><h2>komorebi</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#komorebi\"></a><p></p>\n<p>Tiling Window Management for Windows.</p>\n<p>\n <a target=\"_blank\" href=\"https://techforpalestine.org/learn-more\">\n <img alt=\"Tech for Palestine\" src=\"https://camo.githubusercontent.com/7e092f78eafdcf13ff2c471fd6c1ee3a7b1168359d569b05ff1b5ee3875c08ea/68747470733a2f2f62616467652e74656368666f7270616c657374696e652e6f72672f64656661756c74\" />\n </a>\n <a target=\"_blank\" href=\"https://camo.githubusercontent.com/02042e0f87a27bf714b99d3cb4dd99e5ad5ea539e14fd7c7ff1adcbd495f5643/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f4c475547325a2f6b6f6d6f726562692f2e6769746875622f776f726b666c6f77732f77696e646f77732e79616d6c\"><img alt=\"GitHub Workflow Status\" src=\"https://camo.githubusercontent.com/02042e0f87a27bf714b99d3cb4dd99e5ad5ea539e14fd7c7ff1adcbd495f5643/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f4c475547325a2f6b6f6d6f726562692f2e6769746875622f776f726b666c6f77732f77696e646f77732e79616d6c\" /></a>\n <a target=\"_blank\" href=\"https://camo.githubusercontent.com/cb2b6620a32676e3ac15274cde84233dea44f91e883564e42a1d230814a51761/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4c475547325a2f6b6f6d6f72656269\"><img alt=\"GitHub\" src=\"https://camo.githubusercontent.com/cb2b6620a32676e3ac15274cde84233dea44f91e883564e42a1d230814a51761/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4c475547325a2f6b6f6d6f72656269\" /></a>\n <a target=\"_blank\" href=\"https://camo.githubusercontent.com/3c5a9a3c25fbe9068b8330f15bdb77e37b57d92d4d3978f830e16906bbf78112/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f646f776e6c6f6164732f4c475547325a2f6b6f6d6f726562692f746f74616c\"><img alt=\"GitHub all releases\" src=\"https://camo.githubusercontent.com/3c5a9a3c25fbe9068b8330f15bdb77e37b57d92d4d3978f830e16906bbf78112/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f646f776e6c6f6164732f4c475547325a2f6b6f6d6f726562692f746f74616c\" /></a>\n <a target=\"_blank\" href=\"https://camo.githubusercontent.com/c2686251f3d428d0fab254e68a87c62adcd8cfd666d44ea9eead495707cc6dd2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6d6d6974732d73696e63652f4c475547325a2f6b6f6d6f726562692f6c6174657374\"><img alt=\"GitHub commits since latest release (by date) for a branch\" src=\"https://camo.githubusercontent.com/c2686251f3d428d0fab254e68a87c62adcd8cfd666d44ea9eead495707cc6dd2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6d6d6974732d73696e63652f4c475547325a2f6b6f6d6f726562692f6c6174657374\" /></a>\n <a target=\"_blank\" href=\"https://discord.gg/mGkn66PHkx\">\n <img alt=\"Discord\" src=\"https://camo.githubusercontent.com/71f1c2d897f502fd28b01cce316afdcb7207e38cf7d9026be142806ec6dd1f0b/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f383938353534363930313236363330393134\" />\n </a>\n <a target=\"_blank\" href=\"https://github.com/sponsors/LGUG2Z\">\n <img alt=\"GitHub Sponsors\" src=\"https://camo.githubusercontent.com/b4091ce8540df4f2dba52f43ca3bcd240bd935b758c7738681d7d61417de33ce/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73706f6e736f72732f4c475547325a\" />\n </a>\n <a target=\"_blank\" href=\"https://ko-fi.com/lgug2z\">\n <img alt=\"Ko-fi\" src=\"https://camo.githubusercontent.com/231a2982909f9140f3c7110c1d37020b27715f49cbcfecd0ac854d50e22bf6c5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6b6f66692d7469702d677265656e\" />\n </a>\n <a target=\"_blank\" href=\"https://notado.app/feeds/jado/software-development\">\n <img alt=\"Notado Feed\" src=\"https://camo.githubusercontent.com/2ddc13a66d654e5b7c4decb01d120f47a1c393fa556e0ea8c075cb40c3c2642f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4e6f7461646f2d5375627363726962652d696e666f726d6174696f6e616c\" />\n </a>\n <a target=\"_blank\" href=\"https://www.youtube.com/channel/UCeai3-do-9O4MNy9_xjO6mg?sub_confirmation=1\">\n <img alt=\"YouTube\" src=\"https://camo.githubusercontent.com/cfc12a03cafc7ecdfc5d64e2c150a5c9035309d167edb46bc8390c50f5f8f25b/68747470733a2f2f696d672e736869656c64732e696f2f796f75747562652f6368616e6e656c2f73756273637269626572732f5543656169332d646f2d394f344d4e79395f786a4f366d67\" />\n </a>\n</p>\n<p><a target=\"_blank\" href=\"https://user-images.githubusercontent.com/13164844/184027064-f5a6cec2-2865-4d65-a549-a1f1da589abf.png\"><img src=\"https://user-images.githubusercontent.com/13164844/184027064-f5a6cec2-2865-4d65-a549-a1f1da589abf.png\" alt=\"screenshot\" /></a></p>\n<p><em>komorebi</em> is a tiling window manager that works as an extension to Microsoft's\n<a target=\"_blank\" href=\"https://docs.microsoft.com/en-us/windows/win32/dwm/dwm-overview\">Desktop Window\nManager</a> in\nWindows 10 and above.</p>\n<p><em>komorebi</em> allows you to control application windows, virtual workspaces and display monitors with a CLI which can be\nused with third-party software such as <a target=\"_blank\" href=\"https://github.com/LGUG2Z/whkd\"><code>whkd</code></a>\nand <a target=\"_blank\" href=\"https://github.com/Lexikos/AutoHotkey_L\">AutoHotKey</a> to set user-defined keyboard shortcuts.</p>\n<p><em>komorebi</em> aims to make <em>as few modifications as possible</em> to the operating\nsystem and desktop environment by default. Users are free to make such\nmodifications in their own configuration files for <em>komorebi</em>, but these will\nremain opt-in and off-by-default for the foreseeable future.</p>\n<p>Please refer to the <a target=\"_blank\" href=\"https://lgug2z.github.io/komorebi\">documentation</a> for instructions on how\nto <a target=\"_blank\" href=\"https://lgug2z.github.io/komorebi/installation.html\">install</a> and\n<a target=\"_blank\" href=\"https://lgug2z.github.io/komorebi/example-configurations.html\">configure</a>\n<em>komorebi</em>, <a target=\"_blank\" href=\"https://lgug2z.github.io/komorebi/common-workflows/komorebi-config-home.html\">common workflows</a>, a complete\n<a target=\"_blank\" href=\"https://komorebi.lgug2z.com/schema\">configuration schema reference</a> and a\ncomplete <a target=\"_blank\" href=\"https://lgug2z.github.io/komorebi/cli/quickstart.html\">CLI reference</a>.</p>\n<p>There is a <a target=\"_blank\" href=\"https://discord.gg/mGkn66PHkx\">Discord server</a> available for\n<em>komorebi</em>-related discussion, help, troubleshooting etc. If you have any\nspecific feature requests or bugs to report, please create an issue in this\nrepository.</p>\n<p>There is a <a target=\"_blank\" href=\"https://www.youtube.com/channel/UCeai3-do-9O4MNy9_xjO6mg\">YouTube\nchannel</a> where I post\n<em>komorebi</em> development videos. If you would like to be notified of upcoming\nvideos please subscribe and turn on notifications.</p>\n<p><em>komorebi</em> is a free and source-available project, and one that encourages you to\nmake charitable donations if you find the software to be useful and have the\nfinancial means.</p>\n<p>I encourage you to make a charitable donation to the <a target=\"_blank\" href=\"https://pcrf1.app.neoncrm.com/forms/gaza-recovery\">Palestine Children's\nRelief Fund</a> before you\nconsider sponsoring me on GitHub.</p>\n<p><a target=\"_blank\" href=\"https://github.com/sponsors/LGUG2Z\">GitHub Sponsors is enabled for this\nproject</a>. Unfortunately I don't have\nanything specific to offer besides my gratitude and shout outs at the end of\n<em>komorebi</em> live development videos and tutorials.</p>\n<p>If you would like to tip or sponsor the project but are unable to use GitHub\nSponsors, you may also sponsor through <a target=\"_blank\" href=\"https://ko-fi.com/lgug2z\">Ko-fi</a>.</p>\n<p></p><h2>Installation</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#installation\"></a><p></p>\n<p>A <a target=\"_blank\" href=\"https://lgug2z.github.io/komorebi/installation.html\">detailed installation and quickstart\nguide</a> is available which shows how to get started\nusing <code>scoop</code>, <code>winget</code> or building from source.</p>\n<p><a target=\"_blank\" href=\"https://www.youtube.com/watch?v=H9-_c1egQ4g\"><img src=\"https://camo.githubusercontent.com/456314f0080b1b5ddbb9183782bf28589252c335661276371ae829941c53f00a/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f48392d5f633165675134672f687164656661756c742e6a7067\" alt=\"Watch the quickstart walkthrough video\" /></a></p>\n<p></p><h2>Comparison With Fancy Zones</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#comparison-with-fancy-zones\"></a><p></p>\n<p>Community member <a target=\"_blank\" href=\"https://www.youtube.com/@polle5555\">Olge</a> has created an\nexcellent video which compares the default window management features of\nWindows 11, Fancy Zones and komorebi.</p>\n<p>If you are not familiar with tiling window managers or if you are looking at\nkomorebi and wondering \"how is this different from Fancy Zones? 🤔\", this short\nvideo will answer the majority of your questions.</p>\n<p><a target=\"_blank\" href=\"https://www.youtube.com/watch?v=0LCbS_gm0RA\"><img src=\"https://camo.githubusercontent.com/aaf752b7a96d913e6e3085d11f1df81caf06929cfbdf4f94d5a8ff87dcecdfa2/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f304c4362535f676d3052412f687164656661756c742e6a7067\" alt=\"Watch the comparison video\" /></a></p>\n<p></p><h2>Demonstrations</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#demonstrations\"></a><p></p>\n<p><a target=\"_blank\" href=\"https://github.com/amnweb\">@amnweb</a> showing <em>komorebi</em> <code>v0.1.28</code> running on Windows 11 with window borders,\nunfocused window transparency and animations enabled, using a custom status bar integrated using\n<em>komorebi</em>'s <a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi?tab=readme-ov-file#window-manager-event-subscriptions\">Window Manager Event Subscriptions</a>.</p>\n<details>\n <summary>\n <span>Windows.11.and.Komorebi.mp4</span>\n <span></span>\n </summary>\n <video src=\"https://private-user-images.githubusercontent.com/13164844/342515179-21be8dc4-fa76-4f70-9b37-1d316f4b40c2.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjU3NjU2MzEsIm5iZiI6MTcyNTc2NTMzMSwicGF0aCI6Ii8xMzE2NDg0NC8zNDI1MTUxNzktMjFiZThkYzQtZmE3Ni00ZjcwLTliMzctMWQzMTZmNGI0MGMyLm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA5MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwOTA4VDAzMTUzMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTA0YzFmOGZiZGZjZWQ5NTZhYzA1ODQyMTY1NDI1MzVkMzNlMDBhYmYzODg0MmM3ZTcyYzU2ZTEzOTk2MzI5ZmUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.CZ2lCvQPL7NZDKbMivzab4p1GarvAtnebxfXLMH0IDk\" controls=\"controls\" muted=\"muted\">\n </video>\n</details>\n<p><a target=\"_blank\" href=\"https://github.com/haxibami\">@haxibami</a> showing <em>komorebi</em> running on Windows\n11 with a terminal emulator, a web browser and a code editor. The original\nvideo can be viewed\n<a target=\"_blank\" href=\"https://twitter.com/haxibami/status/1501560766578659332\">here</a>.</p>\n<details>\n <summary>\n <span>0vadqFOyIJHglPRY.mp4</span>\n <span></span>\n </summary>\n <video src=\"https://private-user-images.githubusercontent.com/13164844/163496447-20c3ff0a-c5d8-40d1-9cc8-156c4cebf12e.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjU3NjU2MzEsIm5iZiI6MTcyNTc2NTMzMSwicGF0aCI6Ii8xMzE2NDg0NC8xNjM0OTY0NDctMjBjM2ZmMGEtYzVkOC00MGQxLTljYzgtMTU2YzRjZWJmMTJlLm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA5MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwOTA4VDAzMTUzMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTE2NGU1ZTVjNGE5MjQ2ZmYxNjA0NGI4OGJmM2JjNjVhYTMwMzc5YjZkMDQ4NDQ3ZTNiZDExYTNiZDNiMzhjM2YmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.XQe2cXnYon1ZJW4JS2ypqR80DuSKsLE6jCwRJy-DlKg\" controls=\"controls\" muted=\"muted\">\n </video>\n</details>\n<p><a target=\"_blank\" href=\"https://github.com/aik2mlj\">@aik2mlj</a> showing <em>komorebi</em> running on Windows 11\nwith multiple workspaces, terminal emulators, a web browser, and the\n<a target=\"_blank\" href=\"https://github.com/DenBot/yasb\">yasb</a> status bar with the <em>komorebi</em> workspace\nwidget enabled. The original video can be viewed\n<a target=\"_blank\" href=\"https://zhuanlan.zhihu.com/p/455064481\">here</a>.</p>\n<details>\n <summary>\n <span>0e0dd7fc-7115-11ec-a064-42a1fbc2e1e9-v4_t10000011-wbjnuX5De6.mp4</span>\n <span></span>\n </summary>\n <video src=\"https://private-user-images.githubusercontent.com/13164844/163496414-a9cde3d1-b8a7-4a7a-96fb-a8985380bc70.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjU3NjU2MzEsIm5iZiI6MTcyNTc2NTMzMSwicGF0aCI6Ii8xMzE2NDg0NC8xNjM0OTY0MTQtYTljZGUzZDEtYjhhNy00YTdhLTk2ZmItYTg5ODUzODBiYzcwLm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA5MDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwOTA4VDAzMTUzMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTg4ODZkYWU0OTkyMTg0ODcwOTg4ODI3YWQyOWVkMWU2OGYwYjU2ZjMxYjNhYzE0MGE0ZjQ2MjQyOGY5ODQ0NzcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.zHKPhPbpb1N4R6N4_2Z5QmUq8K8-53M3WT-NQMeNhKo\" controls=\"controls\" muted=\"muted\">\n </video>\n</details>\n<p></p><h2>Contribution Guidelines</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#contribution-guidelines\"></a><p></p>\n<p>If you would like to contribute to <code>komorebi</code> please take the time to carefully read the guidelines below.</p>\n<p></p><h2>Commit hygiene</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#commit-hygiene\"></a><p></p>\n<ul>\n<li>Flatten all <code>use</code> statements</li>\n<li>Run <code>cargo +stable clippy</code> and ensure that all lints and suggestions have been addressed before committing</li>\n<li>Run <code>cargo +nightly fmt --all</code> to ensure consistent formatting before committing</li>\n<li>Use <code>git cz</code> with\nthe <a target=\"_blank\" href=\"https://github.com/commitizen/cz-cli#conventional-commit-messages-as-a-global-utility\">Commitizen CLI</a> to prepare\ncommit messages</li>\n<li>Provide <strong>at least</strong> one short sentence or paragraph in your commit message body to describe your thought process for the\nchanges being committed</li>\n</ul>\n<p></p><h2>PRs should contain only a single feature or bug fix</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#prs-should-contain-only-a-single-feature-or-bug-fix\"></a><p></p>\n<p>It is very difficult to review pull requests which touch multiple unrelated features and parts of the codebase.</p>\n<p>Please do not submit pull requests like this; you will be asked to separate them into smaller PRs that deal only with\none feature or bug fix at a time.</p>\n<p>If you are working on multiple features and bug fixes, I suggest that you cut a branch called <code>local-trunk</code>\nfrom <code>master</code> which you keep up to date, and rebase the various independent branches you are working on onto that branch\nif you want to test them together or create a build with everything integrated.</p>\n<p></p><h2>Refactors to the codebase must have prior approval</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#refactors-to-the-codebase-must-have-prior-approval\"></a><p></p>\n<p><code>komorebi</code> is a mature codebase with an internal consistency and structure that has developed organically over close to\nhalf a decade.</p>\n<p>There are <a target=\"_blank\" href=\"https://youtube.com/@LGUG2Z\">countless hours of live coding videos</a> demonstrating work on this project and\nshowing new contributors how to do everything from basic tasks like implementing new <code>komorebic</code> commands to\ndistinguishing monitors by manufacturer hardware identifiers and video card ports.</p>\n<p>Refactors to the structure of the codebase are not taken lightly and require prior discussion and approval.</p>\n<p>Please do not start refactoring the codebase with the expectation of having your changes integrated until you receive an\nexplicit approval or a request to do so.</p>\n<p>Similarly, when implementing features and bug fixes, please stick to the structure of the codebase as much as possible\nand do not take this as an opportunity to do some \"refactoring along the way\".</p>\n<p>It is extremely difficult to review PRs for features and bug fixes if they are lost in sweeping changes to the structure\nof the codebase.</p>\n<p></p><h2>Breaking changes to user-facing interfaces are unacceptable</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#breaking-changes-to-user-facing-interfaces-are-unacceptable\"></a><p></p>\n<p>This includes but is not limited to:</p>\n<ul>\n<li>All <code>komorebic</code> commands</li>\n<li>The <code>komorebi.json</code> schema</li>\n<li>The <a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi-application-specific-configuration\"><code>komorebi-application-specific-configuration</code></a>\nschema</li>\n</ul>\n<p>No user should ever find that their configuration file has stopped working after upgrading to a new version\nof <code>komorebi</code>.</p>\n<p>More often than not there are ways to reformulate changes that may initially seem like they require breaking user-facing\ninterfaces into additive changes.</p>\n<p>For some inspiration please take a look\nat <a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi/commit/e7d928a065eb63bb4ea1fb864c69c1cae8cc763b\">this commit</a> which added the\nability for users to specify colours in <code>komorebi.json</code> in Hex format alongside RGB.</p>\n<p>There is also a process in place for graceful, non-breaking, deprecation of configuration options that are no longer\nrequired.</p>\n<p></p><h2>License</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#license\"></a><p></p>\n<p><code>komorebi</code> is licensed under the <a target=\"_blank\" href=\"https://polyformproject.org/licenses/strict/1.0.0\">PolyForm Strict 1.0.0\nlicense</a>. On a high level\nthis means that you are free to do whatever you want with <code>komorebi</code> other than\nredistribution, or distribution of new works (ie. hard-forks) based on the\nsoftware.</p>\n<p>Anyone is free to make their own fork of <code>komorebi</code> with changes intended\neither for personal use or for integration back upstream via pull requests.</p>\n<p>Please see <a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi/blob/master/CONTRIBUTING.md\">CONTRIBUTING.md</a> for more information about how\ncode contributions to <code>komorebi</code> are licensed.</p>\n<p></p><h2>Development</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#development\"></a><p></p>\n<p>If you use IntelliJ, you should enable the following settings to ensure that code generated by macros is recognised by\nthe IDE for completions and navigation:</p>\n<ul>\n<li>Set <code>Expand declarative macros</code>\nto <code>Use new engine</code> under \"Settings &gt; Langauges &amp; Frameworks &gt; Rust\"</li>\n<li>Enable the following experimental features:\n<ul>\n<li><code>org.rust.cargo.evaluate.build.scripts</code></li>\n<li><code>org.rust.macros.proc</code></li>\n</ul>\n</li>\n</ul>\n<p></p><h2>Logs and Debugging</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#logs-and-debugging\"></a><p></p>\n<p>Logs from <code>komorebi</code> will be appended to <code>%LOCALAPPDATA%/komorebi/komorebi.log</code>; this file is never rotated or overwritten, so it will keep\ngrowing until it is deleted by the user.</p>\n<p>Whenever running the <code>komorebic stop</code> command or sending a Ctrl-C signal to <code>komorebi</code> directly, the <code>komorebi</code> process\nensures that all hidden windows are restored before termination.</p>\n<p>If however, you ever end up with windows that are hidden and cannot be restored, a list of window handles known\nto <code>komorebi</code> are stored and continuously updated in <code>%LOCALAPPDATA%/komorebi//komorebi.hwnd.json</code>.</p>\n<p></p><h2>Restoring Windows</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#restoring-windows\"></a><p></p>\n<p>Running <code>komorebic restore-windows</code> will read the list of window handles and forcibly restore them, regardless of\nwhether the main <code>komorebi</code> process is running.</p>\n<p></p><h2>Panics and Deadlocks</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#panics-and-deadlocks\"></a><p></p>\n<p>If <code>komorebi</code> ever stops responding, it is most likely either due to either a panic or a deadlock. In the case of a\npanic, this will be reported in the log. In the case of a deadlock, there will not be any errors in the log, but the\nprocess and the log will appear frozen.</p>\n<p>If you believe you have encountered a deadlock, you can compile <code>komorebi</code> with <code>--features deadlock_detection</code> and try\nreproducing the deadlock again. This will check for deadlocks every 5 seconds in the background, and if a deadlock is\nfound, information about it will appear in the log which can be shared when opening an issue.</p>\n<p></p><h2>Window Manager State and Integrations</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#window-manager-state-and-integrations\"></a><p></p>\n<p>The current state of the window manager can be queried using the <code>komorebic state</code> command, which returns a JSON\nrepresentation of the <code>State</code> struct.</p>\n<p>This may also be polled to build further integrations and widgets on top of.</p>\n<p></p><h2>Window Manager Event Subscriptions</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#window-manager-event-subscriptions\"></a><p></p>\n<p></p><h2>Named Pipes</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#named-pipes\"></a><p></p>\n<p>It is possible to subscribe to notifications of every <code>WindowManagerEvent</code> and <code>SocketMessage</code> handled\nby <code>komorebi</code> using <a target=\"_blank\" href=\"https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes\">Named Pipes</a>.</p>\n<p>First, your application must create a named pipe. Once the named pipe has been created, run the following command:</p>\n<div><pre><span>komorebic.exe</span> subscribe<span>-</span>pipe <span>&lt;</span>your pipe name<span>&gt;</span></pre></div>\n<p>Note that you do not have to include the full path of the named pipe, just the name.</p>\n<p>If the named pipe exists, <code>komorebi</code> will start pushing JSON data of successfully handled events and messages:</p>\n<div><pre>{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>AddSubscriber<span>\"</span></span>,<span>\"content\"</span>:<span><span>\"</span>yasb<span>\"</span></span>},<span>\"state\"</span>:{}}\n{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>FocusWindow<span>\"</span></span>,<span>\"content\"</span>:<span><span>\"</span>Left<span>\"</span></span>},<span>\"state\"</span>:{}}\n{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>FocusChange<span>\"</span></span>,<span>\"content\"</span>:[<span><span>\"</span>SystemForeground<span>\"</span></span>,{<span>\"hwnd\"</span>:<span>131444</span>,<span>\"title\"</span>:<span><span>\"</span>komorebi – README.md<span>\"</span></span>,<span>\"exe\"</span>:<span><span>\"</span>idea64.exe<span>\"</span></span>,<span>\"class\"</span>:<span><span>\"</span>SunAwtFrame<span>\"</span></span>,<span>\"rect\"</span>:{<span>\"left\"</span>:<span>13</span>,<span>\"top\"</span>:<span>60</span>,<span>\"right\"</span>:<span>1520</span>,<span>\"bottom\"</span>:<span>1655</span>}}]},<span>\"state\"</span>:{}}\n{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>MonitorPoll<span>\"</span></span>,<span>\"content\"</span>:[<span><span>\"</span>ObjectCreate<span>\"</span></span>,{<span>\"hwnd\"</span>:<span>5572450</span>,<span>\"title\"</span>:<span><span>\"</span>OLEChannelWnd<span>\"</span></span>,<span>\"exe\"</span>:<span><span>\"</span>explorer.exe<span>\"</span></span>,<span>\"class\"</span>:<span><span>\"</span>OleMainThreadWndClass<span>\"</span></span>,<span>\"rect\"</span>:{<span>\"left\"</span>:<span>0</span>,<span>\"top\"</span>:<span>0</span>,<span>\"right\"</span>:<span>0</span>,<span>\"bottom\"</span>:<span>0</span>}}]},<span>\"state\"</span>:{}}\n{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>FocusWindow<span>\"</span></span>,<span>\"content\"</span>:<span><span>\"</span>Right<span>\"</span></span>},<span>\"state\"</span>:{}}\n{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>FocusChange<span>\"</span></span>,<span>\"content\"</span>:[<span><span>\"</span>SystemForeground<span>\"</span></span>,{<span>\"hwnd\"</span>:<span>132968</span>,<span>\"title\"</span>:<span><span>\"</span>Windows PowerShell<span>\"</span></span>,<span>\"exe\"</span>:<span><span>\"</span>WindowsTerminal.exe<span>\"</span></span>,<span>\"class\"</span>:<span><span>\"</span>CASCADIA_HOSTING_WINDOW_CLASS<span>\"</span></span>,<span>\"rect\"</span>:{<span>\"left\"</span>:<span>1539</span>,<span>\"top\"</span>:<span>60</span>,<span>\"right\"</span>:<span>1520</span>,<span>\"bottom\"</span>:<span>821</span>}}]},<span>\"state\"</span>:{}}\n{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>FocusWindow<span>\"</span></span>,<span>\"content\"</span>:<span><span>\"</span>Down<span>\"</span></span>},<span>\"state\"</span>:{}}\n{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>FocusChange<span>\"</span></span>,<span>\"content\"</span>:[<span><span>\"</span>SystemForeground<span>\"</span></span>,{<span>\"hwnd\"</span>:<span>329264</span>,<span>\"title\"</span>:<span><span>\"</span>den — Mozilla Firefox<span>\"</span></span>,<span>\"exe\"</span>:<span><span>\"</span>firefox.exe<span>\"</span></span>,<span>\"class\"</span>:<span><span>\"</span>MozillaWindowClass<span>\"</span></span>,<span>\"rect\"</span>:{<span>\"left\"</span>:<span>1539</span>,<span>\"top\"</span>:<span>894</span>,<span>\"right\"</span>:<span>1520</span>,<span>\"bottom\"</span>:<span>821</span>}}]},<span>\"state\"</span>:{}}\n{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>FocusWindow<span>\"</span></span>,<span>\"content\"</span>:<span><span>\"</span>Up<span>\"</span></span>},<span>\"state\"</span>:{}}\n{<span>\"event\"</span>:{<span>\"type\"</span>:<span><span>\"</span>FocusChange<span>\"</span></span>,<span>\"content\"</span>:[<span><span>\"</span>SystemForeground<span>\"</span></span>,{<span>\"hwnd\"</span>:<span>132968</span>,<span>\"title\"</span>:<span><span>\"</span>Windows PowerShell<span>\"</span></span>,<span>\"exe\"</span>:<span><span>\"</span>WindowsTerminal.exe<span>\"</span></span>,<span>\"class\"</span>:<span><span>\"</span>CASCADIA_HOSTING_WINDOW_CLASS<span>\"</span></span>,<span>\"rect\"</span>:{<span>\"left\"</span>:<span>1539</span>,<span>\"top\"</span>:<span>60</span>,<span>\"right\"</span>:<span>1520</span>,<span>\"bottom\"</span>:<span>821</span>}}]},<span>\"state\"</span>:{}}</pre></div>\n<p>You may then filter on the <code>type</code> key to listen to the events that you are interested in. For a full list of possible\nnotification types, refer to the enum variants of <code>WindowManagerEvent</code> in <code>komorebi</code> and <code>SocketMessage</code>\nin <code>komorebi::core</code>.</p>\n<p>Below is an example of how you can subscribe to and filter on events using a named pipe in <code>nodejs</code>.</p>\n<div><pre><span>const</span> <span>{</span> exec <span>}</span> <span>=</span> <span>require</span><span>(</span><span>\"child_process\"</span><span>)</span><span>;</span>\n<span>const</span> <span>net</span> <span>=</span> <span>require</span><span>(</span><span>\"net\"</span><span>)</span><span>;</span>\n<span>const</span> <span>pipeName</span> <span>=</span> <span>\"\\\\\\\\.\\\\pipe\\\\komorebi-js\"</span><span>;</span>\n<span>const</span> <span>server</span> <span>=</span> <span>net</span><span>.</span><span>createServer</span><span>(</span><span>(</span><span>stream</span><span>)</span> <span>=&gt;</span> <span>{</span>\n <span>console</span><span>.</span><span>log</span><span>(</span><span>\"Client connected\"</span><span>)</span><span>;</span>\n <span>// Every time there is a workspace-related event, let's log the names of all</span>\n <span>// workspaces on the currently focused monitor, and then log the name of the</span>\n <span>// currently focused workspace on that monitor</span>\n <span>stream</span><span>.</span><span>on</span><span>(</span><span>\"data\"</span><span>,</span> <span>(</span><span>data</span><span>)</span> <span>=&gt;</span> <span>{</span>\n <span>let</span> <span>json</span> <span>=</span> <span>JSON</span><span>.</span><span>parse</span><span>(</span><span>data</span><span>.</span><span>toString</span><span>(</span><span>)</span><span>)</span><span>;</span>\n <span>let</span> <span>event</span> <span>=</span> <span>json</span><span>.</span><span>event</span><span>;</span>\n <span>if</span> <span>(</span><span>event</span><span>.</span><span>type</span><span>.</span><span>includes</span><span>(</span><span>\"Workspace\"</span><span>)</span><span>)</span> <span>{</span>\n <span>let</span> <span>monitors</span> <span>=</span> <span>json</span><span>.</span><span>state</span><span>.</span><span>monitors</span><span>;</span>\n <span>let</span> <span>current_monitor</span> <span>=</span> <span>monitors</span><span>.</span><span>elements</span><span>[</span><span>monitors</span><span>.</span><span>focused</span><span>]</span><span>;</span>\n <span>let</span> <span>workspaces</span> <span>=</span> <span>monitors</span><span>.</span><span>elements</span><span>[</span><span>monitors</span><span>.</span><span>focused</span><span>]</span><span>.</span><span>workspaces</span><span>;</span>\n <span>let</span> <span>current_workspace</span> <span>=</span> <span>workspaces</span><span>.</span><span>elements</span><span>[</span><span>workspaces</span><span>.</span><span>focused</span><span>]</span><span>;</span>\n <span>console</span><span>.</span><span>log</span><span>(</span>\n <span>workspaces</span><span>.</span><span>elements</span>\n <span>.</span><span>map</span><span>(</span><span>(</span><span>workspace</span><span>)</span> <span>=&gt;</span> <span>workspace</span><span>.</span><span>name</span><span>)</span>\n <span>.</span><span>filter</span><span>(</span><span>(</span><span>name</span><span>)</span> <span>=&gt;</span> <span>name</span> <span>!==</span> <span>null</span><span>)</span>\n <span>)</span><span>;</span>\n <span>console</span><span>.</span><span>log</span><span>(</span><span>current_workspace</span><span>.</span><span>name</span><span>)</span><span>;</span>\n <span>}</span>\n <span>}</span><span>)</span><span>;</span>\n <span>stream</span><span>.</span><span>on</span><span>(</span><span>\"end\"</span><span>,</span> <span>(</span><span>)</span> <span>=&gt;</span> <span>{</span>\n <span>console</span><span>.</span><span>log</span><span>(</span><span>\"Client disconnected\"</span><span>)</span><span>;</span>\n <span>}</span><span>)</span><span>;</span>\n<span>}</span><span>)</span><span>;</span>\n<span>server</span><span>.</span><span>listen</span><span>(</span><span>pipeName</span><span>,</span> <span>(</span><span>)</span> <span>=&gt;</span> <span>{</span>\n <span>console</span><span>.</span><span>log</span><span>(</span><span>\"Named pipe server listening\"</span><span>)</span><span>;</span>\n<span>}</span><span>)</span><span>;</span>\n<span>const</span> <span>command</span> <span>=</span> <span>\"komorebic subscribe-pipe komorebi-js\"</span><span>;</span>\n<span>exec</span><span>(</span><span>command</span><span>,</span> <span>(</span><span>error</span><span>,</span> <span>stdout</span><span>,</span> <span>stderr</span><span>)</span> <span>=&gt;</span> <span>{</span>\n <span>if</span> <span>(</span><span>error</span><span>)</span> <span>{</span>\n <span>console</span><span>.</span><span>error</span><span>(</span><span>`Error executing command: <span><span>${</span><span>error</span><span>}</span></span>`</span><span>)</span><span>;</span>\n <span>return</span><span>;</span>\n <span>}</span>\n<span>}</span><span>)</span><span>;</span></pre></div>\n<p></p><h2>Unix Domain Sockets</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#unix-domain-sockets\"></a><p></p>\n<p>It is possible to subscribe to notifications of every <code>WindowManagerEvent</code> and <code>SocketMessage</code> handled\nby <code>komorebi</code> using <a target=\"_blank\" href=\"https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/\">Unix Domain Sockets</a>.</p>\n<p>UDS are also the only mode of communication between <code>komorebi</code> and <code>komorebic</code>.</p>\n<p>First, your application must create a socket in <code>$ENV:LocalAppData\\komorebi</code>. Once the socket has been created, run the\nfollowing command:</p>\n<div><pre><span>komorebic.exe</span> subscribe<span>-</span>socket <span>&lt;</span>your socket name<span>&gt;</span></pre></div>\n<p>If the socket exists, komorebi will start pushing JSON data of successfully handled events and messages as in the\nexample above in the Named Pipes section.</p>\n<p></p><h2>Rust Client</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#rust-client\"></a><p></p>\n<p>As of <code>v0.1.22</code> it is possible to use the <code>komorebi-client</code> crate to subscribe to notifications of\nevery <code>WindowManagerEvent</code> and <code>SocketMessage</code> handled by <code>komorebi</code> in a Rust codebase.</p>\n<p>Below is a simple example of how to use <code>komorebi-client</code> in a basic Rust application.</p>\n<div><pre><span>// komorebi-client = { git = \"https://github.com/LGUG2Z/komorebi\", tag = \"v0.1.28\"}</span>\n<span>use</span> anyhow<span>::</span><span>Result</span><span>;</span>\n<span>use</span> komorebi_client<span>::</span><span>Notification</span><span>;</span>\n<span>use</span> komorebi_client<span>::</span><span>NotificationEvent</span><span>;</span>\n<span>use</span> komorebi_client<span>::</span><span>UnixListener</span><span>;</span>\n<span>use</span> komorebi_client<span>::</span><span>WindowManagerEvent</span><span>;</span>\n<span>use</span> std<span>::</span>io<span>::</span><span>BufRead</span><span>;</span>\n<span>use</span> std<span>::</span>io<span>::</span><span>BufReader</span><span>;</span>\n<span>use</span> std<span>::</span>io<span>::</span><span>Read</span><span>;</span>\n<span>pub</span> <span>fn</span> <span>main</span><span>(</span><span>)</span> -&gt; anyhow<span>::</span><span>Result</span><span>&lt;</span><span>(</span><span>)</span><span>&gt;</span> <span>{</span>\n <span>let</span> socket = komorebi_client<span>::</span><span>subscribe</span><span>(</span><span>NAME</span><span>)</span>?<span>;</span>\n <span>for</span> incoming <span>in</span> socket<span>.</span><span>incoming</span><span>(</span><span>)</span> <span>{</span>\n <span>match</span> incoming <span>{</span>\n <span>Ok</span><span>(</span>data<span>)</span> =&gt; <span>{</span>\n <span>let</span> reader = <span>BufReader</span><span>::</span><span>new</span><span>(</span>data<span>.</span><span>try_clone</span><span>(</span><span>)</span>?<span>)</span><span>;</span>\n <span>for</span> line <span>in</span> reader<span>.</span><span>lines</span><span>(</span><span>)</span><span>.</span><span>flatten</span><span>(</span><span>)</span> <span>{</span>\n <span>let</span> notification<span>:</span> <span>Notification</span> = <span>match</span> serde_json<span>::</span><span>from_str</span><span>(</span><span>&amp;</span>line<span>)</span> <span>{</span>\n <span>Ok</span><span>(</span>notification<span>)</span> =&gt; notification<span>,</span>\n <span>Err</span><span>(</span>error<span>)</span> =&gt; <span>{</span>\n log<span>::</span>debug!<span>(</span><span>\"discarding malformed komorebi notification: {error}\"</span><span>)</span><span>;</span>\n <span>continue</span><span>;</span>\n <span>}</span>\n <span>}</span><span>;</span>\n <span>// match and filter on desired notifications</span>\n <span>}</span>\n <span>}</span>\n <span>Err</span><span>(</span>error<span>)</span> =&gt; <span>{</span>\n log<span>::</span>debug!<span>(</span><span>\"{error}\"</span><span>)</span><span>;</span>\n <span>}</span>\n <span>}</span>\n <span>}</span>\n<span>}</span></pre></div>\n<p>A read-world example can be found\nin <a target=\"_blank\" href=\"https://github.com/LGUG2Z/komokana/blob/feature/komorebi-uds/src/main.rs\">komokana</a>.</p>\n<p></p><h2>Subscription Event Notification Schema</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#subscription-event-notification-schema\"></a><p></p>\n<p>A <a target=\"_blank\" href=\"https://json-schema.org/\">JSON Schema</a> of the event notifications emitted to subscribers can be generated with\nthe <code>komorebic notification-schema</code> command. The output of this command can be redirected to the clipboard or a file,\nwhich can be used with services such as <a target=\"_blank\" href=\"https://app.quicktype.io/\">Quicktype</a> to generate type definitions in different\nprogramming languages.</p>\n<p></p><h2>Communication over TCP</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#communication-over-tcp\"></a><p></p>\n<p>A TCP listener can optionally be exposed on a port of your choosing with the <code>--tcp-port=N</code> flag. If this flag is not\nprovided to <code>komorebi</code> or <code>komorebic start</code>, no TCP listener will be created.</p>\n<p>Once created, your client may send\nany <a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi/blob/master/komorebi/src/core/mod.rs#L37\">SocketMessage</a> to <code>komorebi</code> in the\nsame way that <code>komorebic</code> would.</p>\n<p>This can be used if you would like to create your own alternative to <code>komorebic</code> which incorporates scripting and\nvarious middleware layers, and similarly it can be used if you would like to integrate <code>komorebi</code> with\na <a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi/issues/176#issue-1302643961\">custom input handler</a>.</p>\n<p>If a client sends an unrecognized message, it will be disconnected and have to reconnect before trying to communicate\nagain.</p>\n<p></p><h2>Socket Message Schema</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#socket-message-schema\"></a><p></p>\n<p>A <a target=\"_blank\" href=\"https://json-schema.org/\">JSON Schema</a> of socket messages used to send instructions to <code>komorebi</code> can be generated\nwith the <code>komorebic socket-schema</code> command. The output of this command can be redirected to the clipboard or a file,\nwhich can be used with services such as <a target=\"_blank\" href=\"https://app.quicktype.io/\">Quicktype</a> to generate type definitions in different\nprogramming languages.</p>\n<p></p><h2>Appreciations</h2><a target=\"_blank\" href=\"https://github.com/LGUG2Z/komorebi#appreciations\"></a><p></p>\n<ul>\n<li>\n<p>First and foremost, thank you to my wife, both for naming this project and for her patience throughout its never-ending development</p>\n</li>\n<li>\n<p>Thank you to <a target=\"_blank\" href=\"https://github.com/sitiom\">@sitiom</a> for being <a target=\"_blank\" href=\"https://jeezy.substack.com/p/the-open-source-contributions-i-appreciate\">an exemplary open source community leader</a></p>\n</li>\n<li>\n<p>Thank you to the developers of <a target=\"_blank\" href=\"https://github.com/TimUntersberger/nog\">nog</a> who came before me and whose work taught me more than I can ever hope to repay</p>\n</li>\n<li>\n<p>Thank you to the developers of <a target=\"_blank\" href=\"https://github.com/lars-berger/GlazeWM\">GlazeWM</a> for pushing the boundaries of tiling window management on Windows with me and having an excellent spirit of collaboration</p>\n</li>\n<li>\n<p>Thank you to <a target=\"_blank\" href=\"https://github.com/Ciantic\">@Ciantic</a> for helping me bring the <a target=\"_blank\" href=\"https://github.com/Ciantic/AltTabAccessor/issues/1\">hidden Virtual Desktops cloaking function</a> to <code>komorebi</code></p>\n</li>\n</ul>\n</article></div>",
"author": "",
"favicon": "https://github.githubassets.com/favicons/favicon.svg",
"source": "github.com",
"published": "",
"ttr": 439,
"type": "object"
}