klesh / JigsawWM

JigsawWM is a dynamic window manager for Windows10/11 just like the suckless dwm for the X
GNU Lesser General Public License v3.0
137 stars 3 forks source link

Quick Start broken #8

Open minguo33 opened 8 months ago

minguo33 commented 8 months ago

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?

klesh commented 8 months ago

Thanks for reporting the problem, will look into it ASAP.

klesh commented 7 months ago

Hi, @minguo33 , Sorry for taking so long to investigate the issue. I think the problem is that you tried to import the .pyw file in the examples which is not what they are meant for. Please copy the code from the files you need and create your own pyw file to launch the jigsawwm.

minguo33 commented 7 months ago

Hi, @minguo33 , Sorry for taking so long to investigate the issue. I think the problem is that you tried to import the .pyw file in the examples which is not what they are meant for. Please copy the code from the files you need and create your own pyw file to launch the jigsawwm.

Hi @klesh , thanks for taking a look. Unfortunately, I haven't been able to get any of the examples to run whether or not I copy the code into a new file or download the source and try to run it. Can you give some guidance on how to get a working configuration?

When trying to test import statements from my python interpreter, I can import jmk, but not hks. (which is where my previous attempts got caught up.)


>>> from jigsawwm import jmk
>>> from jigsawwm import hks
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'hks' from 'jigsawwm' (\JigsawWM-main\src\jigsawwm\__init__.py)

My environment is python 3.12, jigsawwm was installed with pip, which self reports as version 2.0.

klesh commented 7 months ago

Hi, @minguo33 There is no hks module under the jigsawwm. I think you copied the code from one of the examples, in this case, the hks is a variable from the jmk.pyw file which is an instance of a jmk.Hotkeys class for hotkeys subscription. Did you want to subscribe to hotkey events? if that so, you should creating your own instances just like the jmk.pyw does:

sysin, jmk, hks, sysout = create_jmk(layers, hotkeys, bypass_exe=bypass_exe)
minguo33 commented 7 months ago

Hi again, @klesh , thank you for the assistance in trying to understand what is going on.

Here's my objective: Use a tiling window manager on windows.

I wanted to test out how yours performs, so I was less interested in the other modules but it seems I cannot directly use the wm.pym you link in the quickstart on the main github page to test that functionality.

So I started over and using your advice created a new file and copied the contents from jmk.pyw that seems relevant. I then tried to integrate the statements from wm.pym that make the window management function.

I think it is working now, thank you for your efforts and help.

Here is the configuration I created, in case anyone else gets caught up trying to make the quickstart work as is. (IIUC, they cannot be used as is, correct?)


from datetime import datetime

#from log import *

from jigsawwm import daemon, ui
from jigsawwm.jmk import *
from jigsawwm.tiler import tilers
from jigsawwm.w32.sendinput import send_combination, send_text
from jigsawwm.w32.vk import Vk
from jigsawwm.w32.window import minimize_active_window, toggle_maximize_active_window,inspect_active_window
from jigsawwm.wm import Theme, WindowManager

#######################
#  configuration
#######################

send_today = lambda: send_text(datetime.now().strftime("%Y-%m-%d"))
send_now = lambda: send_text(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
ctrl_w = lambda: send_combination(Vk.LCONTROL, Vk.W)
ctrl_shift_t = lambda: send_combination(Vk.LCONTROL, Vk.LSHIFT, Vk.T)
ctrl_pgup = lambda: send_combination(Vk.LCONTROL, Vk.PRIOR)
ctrl_pgdn = lambda: send_combination(Vk.LCONTROL, Vk.NEXT)

layers = [
    {  # layer 0
        # map capslock to ctrl when held and `  when tapped
        Vk.CAPITAL: JmkTapHold(tap=parse_key("`"), hold=Vk.LCONTROL),
        # Vk.ESCAPE: JmkTapHold(tap=Vk.ESCAPE, hold=Vk.LWIN),
        # Vk.T: JmkTapHold(tap=Vk.T, hold=3),
        # Vk.Y: JmkTapHold(tap=Vk.Y, hold=3),
        # Vk.A: JmkTapHold(tap=Vk.A, hold=Vk.LMENU),
        # Vk.S: JmkTapHold(tap=Vk.S, hold=Vk.LSHIFT),
        # Vk.D: JmkTapHold(tap=Vk.D, hold=Vk.LWIN),
        # Vk.F: JmkTapHold(tap=Vk.F, hold=Vk.LCONTROL),
        # Vk.G: JmkTapHold(tap=Vk.G, hold=1),
        # Vk.H: JmkTapHold(tap=Vk.H, hold=1),
        # Vk.J: JmkTapHold(tap=Vk.J, hold=Vk.RCONTROL),
        # Vk.K: JmkTapHold(tap=Vk.K, hold=Vk.RWIN),
        # Vk.L: JmkTapHold(tap=Vk.L, hold=Vk.RSHIFT),
        # hold ; as Alt
        Vka.SEMICOLON: JmkTapHold(tap=Vka.SEMICOLON, hold=Vk.RMENU),
        # Vk.TAB: JmkTapHold(tap=Vk.TAB, hold=2),
        # hold ' to switch to layer 2
        Vk.N: JmkTapHold(tap=Vk.N, hold=2),
        Vk.B: JmkTapHold(tap=Vk.B, hold=2),
        # hold Forward Button on the Mouse for swithcing to layer 1
        Vk.XBUTTON2: JmkTapHold(tap=Vk.XBUTTON2, hold=2),
        Vk.BROWSER_FORWARD: JmkTapHold(tap=Vk.XBUTTON2, hold=2),
        Vk.SPACE: JmkTapHold(tap=Vk.SPACE, hold=Vk.LSHIFT),
        Vka.SLASH: JmkTapHold(
            tap=Vka.SLASH, on_hold_up=lambda: rootLogger.setLevel(logging.DEBUG)
        ),
    },
    {  # layer 1
        # left hand
        Vk.A: JmkKey(Vk.HOME),
        Vk.E: JmkKey(Vk.END),
        Vk.D: JmkKey(Vk.DELETE),
        Vk.B: JmkKey("LCtrl+Left"),
        Vk.F: JmkKey("LCtrl+Right"),
        # right hand
        Vk.H: JmkKey(Vk.LEFT),
        Vk.J: JmkKey(Vk.DOWN),
        Vk.K: JmkKey(Vk.UP),
        Vk.L: JmkKey(Vk.RIGHT),
        Vk.U: JmkKey(ctrl_pgup),
        Vk.I: JmkKey(ctrl_pgdn),
        Vk.N: JmkKey(Vk.MEDIA_NEXT_TRACK),
        Vk.P: JmkKey(Vk.MEDIA_PREV_TRACK),
        Vk.OEM_COMMA: JmkKey(Vk.VOLUME_DOWN),
        Vk.OEM_PERIOD: JmkKey(Vk.VOLUME_UP),
        Vka.SLASH: JmkKey(Vk.MEDIA_PLAY_PAUSE),
    },
    {  # layer 2
        # tap to send today's date, hold to send now
        Vk.T: JmkTapHold(on_tap=send_today, on_hold_down=send_now),
        # tap to close tab, hold to reopen for Chrome
        Vk.LBUTTON: JmkTapHold(on_tap=ctrl_w, on_hold_down=ctrl_shift_t, term=0.5),
        # forward button + whell up  = ctrl + page up (previous tab)
        Vk.WHEEL_UP: JmkKey(ctrl_pgup),
        # forward button + wheel down  = ctrl + page down (next tab)
        Vk.WHEEL_DOWN: JmkKey(ctrl_pgdn),
        # exit
        Vk.ESCAPE: JmkKey(daemon.stop),
        # symbol
        Vk.A: JmkKey(lambda: send_text("@")),
        Vk.E: JmkKey(lambda: send_text("!")),
        Vk.S: JmkKey(lambda: send_text("#")),
        Vk.D: JmkKey(lambda: send_text("$")),
        Vk.X: JmkKey(lambda: send_text("%")),
        Vk.Y: JmkKey(lambda: send_text("^")),
        Vk.N: JmkKey(lambda: send_text("&")),
        Vk.R: JmkKey(lambda: send_text("*")),
        Vk.F: JmkKey(lambda: send_text("(")),
        Vk.G: JmkKey(lambda: send_text(")")),
        Vk.M: JmkKey(lambda: send_text("-")),
        Vk.P: JmkKey(lambda: send_text("+")),
        Vk.U: JmkKey(lambda: send_text("_")),
        Vk.Q: JmkKey(lambda: send_text("=")),
    },
    {  # layer 3
        # left hand
        Vk.Z: JmkKey(Vk.F1),
        Vk.X: JmkKey(Vk.F2),
        Vk.C: JmkKey(Vk.F3),
        Vk.V: JmkKey(Vk.F4),
        Vk.A: JmkKey(Vk.F5),
        Vk.S: JmkKey(Vk.F6),
        Vk.D: JmkKey(Vk.F7),
        Vk.F: JmkKey(Vk.F8),
        Vk.Q: JmkKey(Vk.F9),
        Vk.W: JmkKey(Vk.F10),
        Vk.E: JmkKey(Vk.F11),
        Vk.R: JmkKey(Vk.F12),
        # right hand
        Vk.Y: JmkKey(Vk.OEM_PERIOD),
        Vk.SPACE: JmkKey(Vk.KEY_0),
        Vk.M: JmkKey(Vk.KEY_1),
        Vk.OEM_COMMA: JmkKey(Vk.KEY_2),
        Vk.OEM_PERIOD: JmkKey(Vk.KEY_3),
        Vk.J: JmkKey(Vk.KEY_4),
        Vk.K: JmkKey(Vk.KEY_5),
        Vk.L: JmkKey(Vk.KEY_6),
        Vk.U: JmkKey(Vk.KEY_7),
        Vk.I: JmkKey(Vk.KEY_8),
        Vk.O: JmkKey(Vk.KEY_9),
        Vk.H: JmkKey(Vk.SUBTRACT),
        Vka.SEMICOLON: JmkKey(Vk.ADD),
        Vk.P: JmkKey(Vk.OEM_PLUS),
        Vk.N: JmkKey(Vk.MULTIPLY),
        Vka.SLASH: JmkKey(Vk.DIVIDE),
    },
]

wm = WindowManager(
    themes=[
        # Theme(
        #     name="OBS Dwindle",
        #     layout_tiler=tilers.obs_dwindle_layout_tiler,
        #     icon_name="obs.png",
        #     gap=2,
        #     strict=True,
        # ),
        Theme(
            name="Mono",
            layout_tiler=tilers.mono_layout_tiler,
            strict=True,
        ),
        Theme(
            name="WideScreen Dwindle",
            layout_tiler=tilers.widescreen_dwindle_layout_tiler,
            icon_name="wide-dwindle.png",
            gap=2,
            strict=True,
            new_window_as_master=True,
        ),
        Theme(
            name="Dwindle",
            layout_tiler=tilers.dwindle_layout_tiler,
            strict=True,
            gap=2,
            new_window_as_master=True,
        ),
    ],
    ignore_exe_names=[
        "7zFM.exe",
        "explorer.exe",
        # "Feishu.exe",
        "fdm.exe",
        # "WeChat.exe",
        "foobar2000.exe",
        "ApplicationFrameHost.exe",
        "notepad++.exe",
        "PotPlayerMini64.exe",
        "mintty.exe",
        "openvpn-gui.exe",
        "Cloudflare WARP.exe",
        "MediaInfo.exe",
        "SnippingTool.exe",
        "WeChat.exe",
    ],
    force_managed_exe_names=["Lens.exe"],
)

hotkeys = [
    ([Vk.WIN, Vk.J], wm.activate_next, ui.hide_windows_splash),
    ([Vk.WIN, Vk.K], wm.activate_prev, ui.hide_windows_splash),
    ([Vk.WIN, Vk.SHIFT, Vk.J], wm.swap_next),
    ([Vk.WIN, Vk.SHIFT, Vk.K], wm.swap_prev),
    ("Win+/", wm.set_master),
    ([Vk.WIN, Vk.SPACE], wm.next_theme),
    ([Vk.WIN, Vk.U], wm.prev_monitor),
    ([Vk.WIN, Vk.I], wm.next_monitor),
    ([Vk.WIN, Vk.SHIFT, Vk.U], wm.move_to_prev_monitor),
    ([Vk.WIN, Vk.SHIFT, Vk.I], wm.move_to_next_monitor),
    ([Vk.WIN, Vk.CONTROL, Vk.I], inspect_active_window),
]

bypass_exe = {
    "Snipaste.exe",
    "TextInputHost.exe",
}

#######################
#  setup jmk
#######################

sysin, jmk, hks, sysout = create_jmk(layers, hotkeys, bypass_exe=bypass_exe)

class WindowManagerService(daemon.Service):
    name = "Window Manager"
    is_running = False

    def start(self):
        self.is_running = True
        wm.install_hooks()
        for args in hotkeys:
            hks.register(*args)

    def stop(self):
        wm.uninstall_hooks()
        for args in hotkeys:
            hks.unregister(args[0])
        self.is_running = False

class JmkService(daemon.Service):
    name = "jmk"
    is_running = False

    def start(self):
        self.is_running = True
        sysin.install()

    def stop(self):
        sysin.uninstall()
        self.is_running = False

daemon.register(JmkService)
daemon.register(WindowManagerService)

if __name__ == "__main__":
    daemon.message_loop()
gazpachoking commented 6 months ago

Looks like the wm.pwy example is wrong. Specifically this line:

https://github.com/klesh/JigsawWM/blob/c2756b898353f7df0966217693846c3a82fbec35/examples/wm.pyw#L1

EDIT: Oh, I get it now. The quickstart seems to imply that you can just pick a file from the examples folder, but in fact they all depend on each other, and you have to download the whole examples folder to do anything with them.

klesh commented 6 months ago

@gazpachoking You are totally correct. I meant to show cases that you may do for different features and how to combine them together. However, it does seem a little bit confusing, I'm open to suggestion of making it clearer.