pyprland icon indicating copy to clipboard operation
pyprland copied to clipboard

[FEAT] [Scratchpads] Fully unmanaged scratchpads (eg: no command given)

Open fdev31 opened this issue 1 year ago • 11 comments

Check #118 for the details.

Proposition:

  • enabled when no command is provided
  • requires match_by="class" (for performance reasons, to not query clients on each new window)

fdev31 avatar Jul 16 '24 20:07 fdev31

@kuba-gaj can you provide some feedback testing the latest git version ?

If you also want less automatic changes on the style you can may try preserve_aspect=true.

fdev31 avatar Jul 19 '24 17:07 fdev31

Note: match_by="class" will be forced, for performance reasons there is no other matching type when no command is provided.

fdev31 avatar Jul 19 '24 17:07 fdev31

thank you for the update,

I started testing it with one scratchpad like this:

[scratchpads.onepass]
animation = "fromBottom"
class = "1Password"
preserve_aspect = true

And I start the app myself with some initial rules:

exec-once=sleep 1 && 1password
windowrulev2 = float, class:^(1Password)$
windowrulev2 = pin, class:^(1Password)$
windowrulev2 = focusonactivate 1, class:^(1Password)$

It seems to kind of work.

  • 1pass app starts and is visible
  • First call to toggle just moves the app down and keeps it visible
  • Subsequent calls to toggle work as expected

I think preferably I would like to be able to control if the scratchpad starts visible or not and not have an extra call to toggle that is currently required.

It also looks like preserve_aspect isn't really working at the moment.

punk-dev-robot avatar Jul 20 '24 10:07 punk-dev-robot

It just occurred to me that maybe I could script running toggle after starting the app:

  • one time to keep it visible and ready for toggling
  • twice to have it ready for toggling and not visible

punk-dev-robot avatar Jul 20 '24 10:07 punk-dev-robot

Ok so there is another small issue.

Steps:

  • onepass app started, visible
  • pyprland service started
  • run toggle - as mentioned above looks like first visibility check is failing and it just moves the visible window without hiding it
  • run toggle - working fine but error message shows? why is that? how to disable it
  • after that multiple toggles seem to be working as expected
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - run_toggle('onepass',) // command.py:202
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - visibility_check: ('', '') == ('2', 'DP-3') // __init__.py:456
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - onepass visibility: False and False // __init__.py:482
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - Showing onepass // __init__.py:559
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - clients // ipc.py:101
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - dispatch movetoworkspacesilent special:scratch_onepass,address:0x606241923de0 // ipc.py:144
Jul 20 13:07:26 arch pypr[8184]:                  pyprland - event_activewindowv2('606241829d20',) // command.py:202
Jul 20 13:07:26 arch pypr[8184]:                  pyprland - active_window = 0x606241829d20 // pyprland.py:76
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - monitors // ipc.py:101
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - clients (CACHE HIT) // ipc.py:98
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - dispatch ['moveworkspacetomonitor special:scratch_onepass DP-3', 'movetoworkspacesilent 2,address:0x606241923de0', 'alterzorder top,address:0x606241923de0'] // ipc.py:144
Jul 20 13:07:26 arch pypr[8184]:                  pyprland - event_activewindowv2('606241923de0',) // command.py:202
Jul 20 13:07:26 arch pypr[8184]:                  pyprland - active_window = 0x606241923de0 // pyprland.py:76
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - dispatch ['movewindowpixel exact 2160 780,address:0x606241923de0'] // ipc.py:144
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - dispatch focuswindow address:0x606241923de0 // ipc.py:144
Jul 20 13:07:26 arch pypr[8184]:                  pyprland - event_activewindowv2('606241923de0',) // command.py:202
Jul 20 13:07:26 arch pypr[8184]:                  pyprland - active_window = 0x606241923de0 // pyprland.py:76
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - event_activewindowv2('606241829d20',) // command.py:202
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - event_activewindowv2('606241923de0',) // command.py:202
Jul 20 13:07:26 arch pypr[8184]:               scratchpads - event_activewindowv2('606241923de0',) // command.py:202
Jul 20 13:07:29 arch pypr[8184]:               scratchpads - run_toggle('onepass',) // command.py:202
Jul 20 13:07:29 arch pypr[8184]:               scratchpads - visibility_check: ('2', 'DP-3') == ('2', 'DP-3') // __init__.py:456
Jul 20 13:07:29 arch pypr[8184]:               scratchpads - onepass visibility: True and True // __init__.py:482
Jul 20 13:07:29 arch pypr[8184]:               scratchpads - clients // ipc.py:101
Jul 20 13:07:29 arch pypr[8184]:               scratchpads - Hiding onepass // __init__.py:769
Jul 20 13:07:29 arch pypr[8184]:               scratchpads - dispatch ['movewindowpixel 0 660,address:0x606241923de0'] // ipc.py:144
Jul 20 13:07:29 arch pypr[8184]:               scratchpads - dispatch pin address:0x606241923de0 // ipc.py:144
Jul 20 13:07:29 arch pypr[8184]:               scratchpads - dispatch movetoworkspacesilent special:scratch_onepass,address:0x606241923de0 // ipc.py:144
Jul 20 13:07:29 arch pypr[8184]:                  pyprland - event_activewindowv2('606241829d20',) // command.py:202
Jul 20 13:07:29 arch pypr[8184]:                  pyprland - active_window = 0x606241829d20 // pyprland.py:76
Jul 20 13:07:29 arch pypr[8184]:                      pypr - This could be a bug in Pyprland, if you think so, report on https://github.com/fdev31/pyprland/issues // command.py:210

punk-dev-robot avatar Jul 20 '24 12:07 punk-dev-robot

Hmm, I think I got rid of above error by starting onepass after pyprland, or it's random atm :)

Now I toggled onepass a couple of times and killed the process.

When I run toggle I see error message (I would like not to as this is dummy mode without any tracking). What is even worse other scratchpad also stopped working:

Jul 20 13:36:18 arch pypr[8308]:               scratchpads - run_toggle('onepass',) // command.py:202
Jul 20 13:36:18 arch pypr[8308]:               scratchpads - visibility_check: ('2', 'DP-3') == ('2', 'DP-3') // __init__.py:456
Jul 20 13:36:18 arch pypr[8308]:               scratchpads - onepass visibility: True and True // __init__.py:482
Jul 20 13:36:18 arch pypr[8308]:               scratchpads - clients // ipc.py:101
Jul 20 13:36:18 arch pypr[8308]: ERROR:scratch:The client window 0x5574909ff800 vanished
Jul 20 13:36:18 arch pypr[8308]:                      pypr - scratchpads::run_toggle(('onepass',)) failed: // command.py:213
Jul 20 13:36:18 arch pypr[8308]: Traceback (most recent call last):
Jul 20 13:36:18 arch pypr[8308]:   File "/usr/lib/python3.12/site-packages/pyprland/command.py", line 208, in _run_plugin_handler
Jul 20 13:36:18 arch pypr[8308]:     await getattr(plugin, full_name)(*params)
Jul 20 13:36:18 arch pypr[8308]:   File "/usr/lib/python3.12/site-packages/pyprland/plugins/scratchpads/__init__.py", line 487, in run_toggle
Jul 20 13:36:18 arch pypr[8308]:     await asyncio.gather(*(asyncio.create_task(t()) for t in tasks))
Jul 20 13:36:18 arch pypr[8308]:   File "/usr/lib/python3.12/site-packages/pyprland/plugins/scratchpads/__init__.py", line 755, in run_hide
Jul 20 13:36:18 arch pypr[8308]:     await scratch.update_client_info(clients=clients)
Jul 20 13:36:18 arch pypr[8308]:   File "/usr/lib/python3.12/site-packages/pyprland/plugins/scratchpads/objects.py", line 179, in update_client_info
Jul 20 13:36:18 arch pypr[8308]:     raise KeyError(msg)
Jul 20 13:36:18 arch pypr[8308]: KeyError: 'Client window 0x5574909ff800 not found'
Jul 20 13:36:18 arch pypr[8308]:                       ipc - notify 0 5000 rgb(ff1010)  Pypr error scratchpads::run_toggle: 'Client window 0x5574909ff800 not found' // ipc.py:144
Jul 20 13:36:33 arch pypr[10276]: [07/20/24, 13:36:33:387] info: [DND_V2] (T033J3T2Y) Checking for changes in DND status for the following members: U014H9J7CUD
Jul 20 13:36:33 arch pypr[10276]: [07/20/24, 13:36:33:388] info: [DND_V2] (T033J3T2Y) Will check for changes in DND status again in 5 minutes
Jul 20 13:36:39 arch pypr[8308]:                  pyprland - event_activewindowv2('55749097e450',) // command.py:202
Jul 20 13:36:39 arch pypr[8308]:                  pyprland - active_window = 0x55749097e450 // pyprland.py:76
Jul 20 13:36:39 arch pypr[8308]:               scratchpads - event_activewindowv2('55749097e450',) // command.py:202
Jul 20 13:38:40 arch pypr[8308]:               scratchpads - run_toggle('dropterm',) // command.py:202
Jul 20 13:38:40 arch pypr[8308]:               scratchpads - visibility_check: ('2', 'DP-3') == ('2', 'DP-3') // __init__.py:456
Jul 20 13:38:40 arch pypr[8308]:               scratchpads - dropterm visibility: False and False // __init__.py:482
Jul 20 13:38:40 arch pypr[8308]:               scratchpads - Showing dropterm // __init__.py:559
Jul 20 13:38:40 arch pypr[8308]:               scratchpads - keyword windowrule unset,^(term-drop)$ // ipc.py:144
Jul 20 13:38:40 arch pypr[8308]:               scratchpads - clients // ipc.py:101
Jul 20 13:38:40 arch pypr[8308]: ERROR:scratch:The client window 0x5574909ff800 vanished
Jul 20 13:38:40 arch pypr[8308]:                      pypr - scratchpads::run_toggle(('dropterm',)) failed: // command.py:213
Jul 20 13:38:40 arch pypr[8308]: Traceback (most recent call last):
Jul 20 13:38:40 arch pypr[8308]:   File "/usr/lib/python3.12/site-packages/pyprland/command.py", line 208, in _run_plugin_handler
Jul 20 13:38:40 arch pypr[8308]:     await getattr(plugin, full_name)(*params)
Jul 20 13:38:40 arch pypr[8308]:   File "/usr/lib/python3.12/site-packages/pyprland/plugins/scratchpads/__init__.py", line 487, in run_toggle
Jul 20 13:38:40 arch pypr[8308]:     await asyncio.gather(*(asyncio.create_task(t()) for t in tasks))
Jul 20 13:38:40 arch pypr[8308]:   File "/usr/lib/python3.12/site-packages/pyprland/plugins/scratchpads/__init__.py", line 573, in run_show
Jul 20 13:38:40 arch pypr[8308]:     await self.run_hide(e_uid, flavor=HideFlavors.TRIGGERED_BY_AUTOHIDE | HideFlavors.IGNORE_TILED)
Jul 20 13:38:40 arch pypr[8308]:   File "/usr/lib/python3.12/site-packages/pyprland/plugins/scratchpads/__init__.py", line 755, in run_hide
Jul 20 13:38:40 arch pypr[8308]:     await scratch.update_client_info(clients=clients)
Jul 20 13:38:40 arch pypr[8308]:   File "/usr/lib/python3.12/site-packages/pyprland/plugins/scratchpads/objects.py", line 179, in update_client_info
Jul 20 13:38:40 arch pypr[8308]:     raise KeyError(msg)
Jul 20 13:38:40 arch pypr[8308]: KeyError: 'Client window 0x5574909ff800 not found'
Jul 20 13:38:40 arch pypr[8308]:                       ipc - notify 0 5000 rgb(ff1010)  Pypr error scratchpads::run_toggle: 'Client window 0x5574909ff800 not found' // ipc.py:144
Jul 20 13:39:27 arch pypr[8308]:                  pyprland - event_activewindowv2('5574909bec30',) // command.py:202
Jul 20 13:39:27 arch pypr[8308]:                  pyprland - active_window = 0x5574909bec30 // pyprland.py:76

hyprctl

Window 557490ac5f10 -> tmux:
        mapped: 1
        hidden: 0
        at: 1600,-1296
        size: 1920,1296
        workspace: -93 (special:scratch_dropterm)

Then I started onepass again but pyprland is still in broken state (window not found)

punk-dev-robot avatar Jul 20 '24 12:07 punk-dev-robot

In the first log, you stopped it exactly at the line which starts to explain the error, so I can't help without the details.

The relevant content (eg: class) has also been removed from your second log.

  • Can you provide full logs and configuration files in use?
  • Does it also behaves the same using another app?
  • What makes you think preserve_aspect is broken?

fdev31 avatar Jul 27 '24 18:07 fdev31

Can I close this one?

fdev31 avatar Jan 28 '25 22:01 fdev31

hey - of course you can - it's your project! :D I think that without changing architecture to not track pids it will often have issues with some apps. It is not an easy topic and reimplementing part of systemd functionality doesn't make sense. In systemd service can be simple, exec, forking, oneshot, dbus, notify and idle - and i don't think that covers everything, especially the electron bootloaders. Can be done in a bit hacky way with wrapper script and sd_notify?

I gave up chasing the issues for months :) Recently switched to more robust uwsm from my own systemd scripts for session handling, then switched most of my service files to uwsm app (actually compatible app2unit) and in the last days I started experimenting with hyprscratch - much smaller focused binary with more basic functionality but without process tracking and I managed to set it up. I guess if anyone else loves scratchpads (I have 15 - really core to my workflow) and went all in with systemd it may be a valid option

Thank you for your work, kudos for creating and maintaining pyprland, and great support, help debugging issues and patience :)

punk-dev-robot avatar Aug 03 '25 19:08 punk-dev-robot

Actually pyprland's scratchpads don't only match by pid, it's configurable. Check the "match_by" configuration option.

fdev31 avatar Aug 04 '25 16:08 fdev31

I know, i was using match_by pretty extensively, but it stil had some issues:

  • it was creating startup dependency between pyprland and these apps
  • when the window started first toggle call would only postion the window by pyprland instead of hiding it
  • in the event of window being closed and new created pyprland was erroring with errors about window id not being found, maybe some caching issues?

punk-dev-robot avatar Aug 04 '25 21:08 punk-dev-robot