Code Monkey home page Code Monkey logo

jigsawwm's Introduction

JigsawWM

JigsawWM is a free and open-source project that aims to increase your productivity by offering a set of automation facilities, including the jmk module as an AHK alternative, a Tiling Window Manager to free you from managing windows manually and the Daemon to support any customization you may have in mind.

What Can I Do?

Service: jmk

Software-defined keyboard/mouse automation which mimics the QMK as an alternative to AutoHotkey

  1. My pinky is hurting - the dual-role key You should try using F as the Control key, the following code would turn it into a dual-role key: it acts as the LControl when held, F when tapped:
        Vk.F: JmkTapHold(tap=Vk.F, hold=Vk.LCONTROL),

What if I need to enter a bunch of F? Tap it and then hold it down within quick_tap_term(default 120ms)

  1. I use F12 a lot, can I tap it without looking at the keyboard? - the layers Sure, the problem with modern keyboards is they are enormous, 104 or even more. It is inconvenient when the keys you use frequently are far away from the Home Row (a, s, d, f, ...). With layer you may "move" the needed key into your reach:
layers = [
    { # layer 0
        # activate layer 3 when held
        Vk.T: JmkTapHold(tap=Vk.T, hold=3),
    },
    { # layer 1
      ...
    },
    { # layer 2
      ...
    },
    { # layer 3
        Vk.Z: JmkKey(Vk.F1),
    }

Now, hold the key T and press Z, you get F1

  1. I would like to press Win+Q to close the active Window. hotkey at your service, furthermore, you may press Win+N to minimize or Win+M to maximize the active window
hotkeys = [
    ("Win+q", "LAlt+F4"),
    # Win+n to minimize active window
    ([Vk.WIN, Vk.N], minimize_active_window),
    # Win+m to maximize active window
    ([Vk.WIN, Vk.M], toggle_maximize_active_window),
]
  1. For browsing smoothingly with the Mouse, try the following setup
  • Mouse Forward + Left Button: send Ctrl + w (close tab in Chrome and other apps)
  • Mouse Forward + Wheel Up: send Ctrl + PageUp (previous tab in Chrome and other apps)
  • Mouse Forward + Wheel Down: send Ctrl + PageDown (next tab in Chrome and other apps)
  1. Check out the examples/jmk.pyw to find out more

Service: tiling window manager

The WindowManager follows the suckless philosophy and works just like the dwm. All windows are treated as an Ordered List, they will be moved into places based on their Order and specified Layout automatically, save you from arranging them manually.

Demo

2023-01-01_18-56-36.mp4

Default keybindings

  • Win + j: activate next window and move the cursor to its center
  • Win + k: activate the previous window and move the cursor to its center
  • Win + Shift + j: move the active window down in the list (swap with the next one)
  • Win + Shift + k: move the active window up in the list (swap with the previous one)
  • Win + /: swap the active window with the first window in the list or the second window if it is the first window already
  • Win + Space: next theme, Theme consists of Layout, Background, gap, etc. to determine how windows should be placed
  • Win + i: activate the first window of the next monitor if any or move cursor only
  • Win + u: activate the first window of the previous monitor if any or move cursor only
  • Win + Shift + i: move the active window to the next monitor
  • Win + Shift + u: move the active window to the previous monitor

Service: run console program in the background

Sometimes, software that should be run as a system service may not offer an installer to do the job, worse, it may ship as a Console Program. Nobody wants a Console Window stays on their desktops, and yes, I'm talking about SyncThing. I love it but I would like it to run in the background quietly.

class SyncthingService(daemon.ProcessService):
    name = "syncthing"
    args = [
        r"C:\Programs\syncthing-windows-amd64-v1.23.2\syncthing.exe",
        "-no-browser",
        "-no-restart",
        "-no-upgrade",
    ]
    log_path = r"C:\Programs\syncthing-windows-amd64-v1.23.2\syncthing.log"


daemon.register(SyncthingService)

Task: automate your workflows with tasks

  1. I would like to open a folder inside the Chromium Bookmark on the first boot-up every day.
class DailyRoutine(daemon.Task):
    name = "daily routine"

    def run(self):
        chrome.open_fav_folder("bookmark_bar", "daily")

    def condition(self):
        # trigger only once on daily-basis
        return smartstart.daily_once("daily websites")


daemon.register(DailyRoutine)
  1. I would like to launch my IM / Mail Client on workdays
class WorkdayRoutine(daemon.Task):
    name = "workday routine"

    def __init__(self) -> None:
        super().__init__()
        self.holiday_book = ChinaHolidayBook()

    def run(self):
        smartstart.start_if_not_running(
            r"C:\Users\Klesh\AppData\Local\Feishu\Feishu.exe"
        )
        smartstart.start_if_not_running(
            r"C:\Program Files\Mozilla Thunderbird\thunderbird.exe"
        )

    def condition(self):
        return self.holiday_book.is_workhour(extend=timedelta(hours=2))


daemon.register(WorkdayRoutine)

Installation

Tested on Windows 11 Build 22000 and Python 3.11.1. Should work on Windows 10 and Python 3.8

Install from pypi

pip install jigsawwm

Install from Github repo

pip install git+https://github.com/klesh/JigsawWM.git

Quick Start

Step 1: Create a .pyw file as your "Configuration"

Choose services you like from the examples folder, you may use any of the following directly.

Step 2: Launch your .pyw file and manage your services

Double-click the .pyw file and a tray icon should appear, right-click the icon to manage your services. image

Step 3: Launch at startup

  1. Open your Startup folder by pressing Win + r to activate the Run dialog and type in shell:startup, a FileExplorer should pop up.
  2. Create a shortcut to your .pyw file. Done!

Document

Read the Docs

jigsawwm's People

Contributors

klesh avatar nnako avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

nnako saberzero1

jigsawwm's Issues

set fixed sequence of application windows

the tilers / layouts depend on a given sequence of application windows. so, the startup sequence of applications seems to determine the initial window configuration. e.g. when I want to realize the following window configuration on a big screen, I would first have to arrange my applications into the fitting sequence, then start the desired pyw example, before the screen would look as expected:

+----------+----------+----------+----------+
|          |          |          |          |
|          |          |          |          |
|     6    |     7    |     8    |          |
|          |          |          |          |
|          |          |          |          |
+----------+--+-----+-+----------+          |
|             |     |            |          |
|             |     |            |          |
|             |     |            |          |
|             |     |            |     4    |
|             |  3  |      1     |          |
|     5       |     |            |          |
|             |     |            |          |
|             |     |            |          |
|             +-----+------------|          |
|             |                  |          |
|             |         2        |          |
+-------------+------------------+----------+

I would want the following applications to fill the respective locations within my big screen:

1    NEOVIM
2    CMD
3    NOTEPAD++
4    FREEPLANE
5    CHROME
6    OUTLOOK
7    TOTAL COMMANDER
8    EXCEL PLAN

normally, the initial application sequence is far from the desired one. thus leading to first having to use the appropriate key combinations to shift the applications onto their expected sequence within the application list.

Is there a way to start up the applications in the correct order so that JigsawWM displays the application windows in the desired way? without having to "correct" their order?

here is the code I use in layouts.py:

def static_bigscreen_8(n: int) -> Iterator[FloatRect]:
    """layout for a big screen (like a television) of
    55 inches or more. here, the 'eve line' should
    define the main horizontal segregation. due to
    an attempt to stay below it for main actions on
    the screen. the screen will be optimal for 8
    application windows.

    .. code-block:: text

 ...

    :param n: total number of currently active windows
    :rtype: Iterator[FloatRect]
    """

...
    if n==8:
        yield 0.45, 0.37, 0.75, 0.80
        yield 0.30, 0.80, 0.75, 1.00
        yield 0.30, 0.37, 0.45, 0.80
        yield 0.75, 0.00, 1.00, 1.00
        yield 0.00, 0.37, 0.30, 1.00
        yield 0.00, 0.00, 0.25, 0.37
        yield 0.25, 0.00, 0.50, 0.37
        yield 0.50, 0.00, 0.75, 0.37

Quick Start broken

Hi, I have tried to get this working on Windows 11 and Windows 10, each time the experience was the same.

Installing jigsawwm from PIP and trying to use the quickstart example wm.pyw nothing happens.

When trying to call wm.pyw from a python interpreter receive this error:

wm.pyw, line 1, in <module>
    from jmk import hks
ImportError: cannot import name 'hks' from 'jmk' 

Trying to run jigsaw.pyw results again in nothing happening, and when calling it from a python interpreter receives this error:

jigsaw.pyw", line 2, in <module>
    from log import *
ModuleNotFoundError: No module named 'log'

Investigating that issue seems to imply python 'log' library has been renamed to 'logging' but I can't tell if that's what jigsawwm is trying use.

Interesting project, would like to test it out.
Are the example files still current? If not, do you have a working example to use?

when switching between applications, key presses bleed

When switching between applications (in JigsawWM done using e.g. <WIN> + <J> or <WIN> + <K>), there should not be any "bleeding" of keypresses into the new application. No acidentally transferred keypresses to an unintended application.

Using JigsawWM, after switching between applications, I get e.g. k characters inside the editor of the new application, which must stem from the previous <CTRL> + <K> to switch applications.

development focus for future features

Hi,

first of all: AMAZING work you did!!!

I just stumbled across your project, today. And as I myself started writing a very much related application, a couple of years ago, I was wondering if there were possibilities of joining forces for higher goals ;-) .

My goal is to provide an application which runs in the Windows OS'es background (like yours) and listens for key presses (mouse clicking would not be highest priority). The main goal of the application would be to provide the following features to its users:

  • switch between applications (could even be done using Window's native keys like <ALT> + <TAB>, ...)
  • on each change-of-application, an application-specific set of hotkey configurations gets activated. hotkey configurations translate the user's key presses into application-specific key-press-sequences necessary to realize a specific functionality.
  • so, this application-specific sets of hotkey configurations provide translation between user-desired key sequences and their application-specific realizations (e.g. using the sequence :w to save a document in MS WORD. So, translated into <CTRL> + <S> when MS WORD is active window)
  • the application should provide different "operation modes" to be used / configured for any applications. Using these modes, a mechanism as realized in the VIM editor could be implemented. Thus, being able to even provide hotkeys for each "mode" an application was currently in. VIM uses "normal" mode e.g. to navigate within a given text, "insert" mode to actually add / remove characters, "command" mode to issue all kinds of commands / functions, ...
  • the application should provide different kinds of low-level-input-schemes. E.g.
    • normal text, where each key press results in a character within the selected application's editor window
    • abbreviations, where a pre-defined (short) sequence of text triggers a whole text block to be inserted
    • commands, where a leading ":" opens a dedicated command line to insert specific commands
    • ...

For all this, I need a "crisp" and reliable keyboard interface on the lowest layer:

  • 100% stable keyboard interface, see #4

  • 100% stateless operation
    Even when pressing the weirdest key combinations in any humanly possible sequence and typing speed, I would want to have a clear and clean status whenever I switch from one application to the next one. No hanging control keys, no hidden state influencing the current key sequences for the current application. Hanging control keys I have encountered in Python's keyboard module. Which disqualifies the usage of that great library for the intended purpose.

  • 100% stable switching between applications, see #5

error message "invalid monitor handle" when starting example

Hi,

starting about two weeks ago, JigsawWM suddenly will not operate correctly when I start my widescreen example.

Normally, I start a batch script which opens the virtual environment and starts the example from the repo:

:: walk into local source folder for files
c:
cd c:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM

:: walk into virtual environment
call _venv\Scripts\activate

:: start tooling
python examples\nnako.pyw

Then the windows which I have placed on my big screen get arranged and I am given the control over the functionality by hotkeys as defined within the example. But now, after arranging the windows, I get error messages within the console. And I can only exit by pressing <CTRL> + <C>.

I did no change to the code for months. Maybe the Windows OS changed its behaviour and the implemented JigsawWM interface is not able to work out these differences? At least, the error essentially shows an "invalid monitor handle".

What could this be? Where should I start looking for the cause within the code?

Here the debug and error outputs from the console (it is essentially the same error code block printed four times, which seems odd to me as well):

arrange None static_bigscreen_8
arrange None static_bigscreen_8
arrange None static_bigscreen_8
Uncaught exception:
   File "c:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\examples\nnako.pyw", line 51, in <module>
    wm = WindowManager(
         ^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 90, in __init__
    self.sync(init=True)
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 189, in sync
    monitor_state.sync(
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 117, in sync
    self.arrange(theme)
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 126, in arrange
    wr = self.monitor.get_info().rcWork
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\w32\monitor.py", line 156, in get_info
    raise WinError(get_last_error())

OSError: [WinError 1461] Ungültiges Handle für den Monitor.
Traceback (most recent call last):
  File "c:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\examples\nnako.pyw", line 51, in <module>
    wm = WindowManager(
         ^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 90, in __init__
    self.sync(init=True)
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 189, in sync
    monitor_state.sync(
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 117, in sync
    self.arrange(theme)
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 126, in arrange
    wr = self.monitor.get_info().rcWork
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\w32\monitor.py", line 156, in get_info
    raise WinError(get_last_error())
OSError: [WinError 1461] Ungültiges Handle für den Monitor.
2024-03-16 07:30:13,426 [MainThread  ] [CRITI]  Uncaught exception:
   File "c:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\examples\nnako.pyw", line 51, in <module>
    wm = WindowManager(
         ^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 90, in __init__
    self.sync(init=True)
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 189, in sync
    monitor_state.sync(
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 117, in sync
    self.arrange(theme)
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 126, in arrange
    wr = self.monitor.get_info().rcWork
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\w32\monitor.py", line 156, in get_info
    raise WinError(get_last_error())

OSError: [WinError 1461] Ungültiges Handle für den Monitor.
Traceback (most recent call last):
  File "c:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\examples\nnako.pyw", line 51, in <module>
    wm = WindowManager(
         ^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 90, in __init__
    self.sync(init=True)
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 189, in sync
    monitor_state.sync(
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 117, in sync
    self.arrange(theme)
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 126, in arrange
    wr = self.monitor.get_info().rcWork
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\w32\monitor.py", line 156, in get_info
    raise WinError(get_last_error())
OSError: [WinError 1461] Ungültiges Handle für den Monitor.

stability of low-level keyboard interface

When pressing sequences of keys, it would be expected that the keyboard interface behaves exactly as the native Windows keyboard interface does. This means that slow and fast typists would recognize no change in the behavior of the keyboard interface when working inside a specific application. Even on multiple sequential key presses with overlap of keys, the interface should behave just like native windows. That would be the optimal case, I guess..

With JigsawWM I see some issues:

  • one example is that when e.g. changing to neovim application and trying to issue commands in command mode, the command mode is suddenly closed when trying to insert a standard Windows path containing a \ BACHSLASH character.
  • another example is, when typing fast and thus sometimes holding two keys down simultaneously while typing a word. The keys don't get recognized in a reliably stable way. So, sometimes the <SPACE> character is missing. Or other keys.

TypeError: 'NoneType' object is not callable

Hi again,

I just updated to the latest commit on main branch in order to prepare a small PR, but sadly, jigsawwm throws an error. Now, the error occurs before the windows have been arranged on my big monitor (which is different than the now closed issue#9, where the error occured after it). In order to at least be able to let jigsawwm arrange my windows, I will have to go back in commit history...

Here are the details: After walking into the project and virtual environment (where I already updated the last new modules using pip) I start the tool. After starting jigsawwm with the configuration nnako.pyw from the examples folder (as I generally do), without any change on the window locations, I get the following error message in the calling terminal window:

Traceback (most recent call last):
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 142, in _consume_sync_queue
    self._sync(init, restrict)
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\manager.py", line 183, in _sync
    monitor_state = virtdesk_state.get_monitor(monitor)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 206, in get_monitor
    theme = sorted(self.themes, key=lambda x: x.affinity_index(monitor.get_screen_info()), reverse=True)[0]
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\USER\_NEXTCLOUD\_TOOLS\_Python\APP__JigsawWM\src\jigsawwm\wm\state.py", line 206, in <lambda>
    theme = sorted(self.themes, key=lambda x: x.affinity_index(monitor.get_screen_info()), reverse=True)[0]
                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not callable

The execution stops within the function get_monitor() at this location (line 206):

    def get_monitor(self, monitor: Monitor) -> MonitorState:
        """Retrieves the monitor state for the specified monitor in the virtual desktop

        :param Monitor monitor: monitor
        :returns: monitor state
        :rtype: MonitorState
        """
        monitor_state = self.monitors.get(monitor)
        if monitor_state is None:
-->         theme = sorted(self.themes, key=lambda x: x.affinity_index(monitor.get_screen_info()), reverse=True)[0]
            logger.info("default to theme %s for monito %s", theme.name, monitor.name)
            monitor_state = MonitorState(self, monitor, theme=theme.name)
            self.monitors[monitor] = monitor_state
        return monitor_state

After pressing <CTRL> + <C> within the terminal window, I see the application completely stops:

QObject::killTimer: Timers cannot be stopped from another thread
QObject::~QObject: Timers cannot be stopped from another thread

I've tried to set a breakpoint to line 204, but it oddly seems to be ignored when executing. Maybe, the code is already running at some other thread / place and would thus rather be inaccessible for the debugger?

Any hints? Can I check something?

Import Error and computer freezes freezes

->C:\Users\nahid ⚡ python C:\Users\nahid\JigsawWM\examples\wm.pyw


wm.pyw 5
from jigsawwm import daemon, ui

ImportError:
cannot import name 'ui' from 'jigsawwm' (C:\Users\nahid\scoop\apps\python\current\Lib\site-packages\jigsawwm_init_.py)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.