Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Profiles #83

Open
Tracked by #85
edrex opened this issue Apr 3, 2023 · 16 comments
Open
Tracked by #85

Feature: Profiles #83

edrex opened this issue Apr 3, 2023 · 16 comments
Labels
feature New feature or request

Comments

@edrex
Copy link
Collaborator

edrex commented Apr 3, 2023

As a floating laptop user, I would like have different config blocks dynamically applied depending on which outputs are connected.


Design constraints:

  • Maximum expressivity when specifying a new station layout, at which way-displays succeeds 100% via ARRANGE and ALIGN vs kanshi's pixel coordinate based layout specification.
  • Fewest instances of manual intervention required: I only want to have to mess with config when adding a new novel station that doesn't conform to my default layout. Here, way-displays doesn't have a sufficient mechanism yet. While the current mechanism allows for some flexibility in output ORDER, it doesn't allow conditional application of other config keys (ARRANGE, ALIGN, etc) and so requires manual intervention when switching stations whenever these keys need to change.

IMO what is needed is a kanshi-style "profiles" mechanism where each profile has a set of matching rules and a config block to apply. The profiles could be mutually-exclusive (first match wins) or layered (which would allow flexibly specifying defaults). This way, all config keys are subject to matching, and new matching rules can be added to introduce new axes of conditionality.

Workarounds

Currently, I have multiple commented blocks and whenever I switch stations, I open the config in my editor and switch the active one. It's not a huge deal, and I like it way better than the kanshi UX. This could also be done via the IPC mechanism, but that just kicks the ball down the road.

Anyway, I'm happy with things as they are now but I see the focus making ORDER super flexible as a distraction.

@alex-courtis
Copy link
Owner

IMO what is needed is a kanshi-style "profiles" mechanism where each profile has a set of matching rules and a config block to apply.

I think that's the key part here. The story will be achieved by setting the explicit configuration per-profile. Falling back to a default will still be useful. The behaviour of new displays can be best effort configured by profile or default, adding a new profile if needed.

My workarounds:

  • Originally I had separate config files per machine, configured via my dotfile manager
  • Now I just set the order in such a specific way that it matches the 3 machines I use, which are all left-to-right
    That's not sufficient.

@alex-courtis
Copy link
Owner

Currently, I have multiple commented blocks and whenever I switch stations, I open the config in my editor and switch the active one.

Keep in mind the CLI. You can configure persistently or ephemerally.

You can also use the IPC API if you're sufficiently motivated.

See #65 which will add user hooks.

Regardless, profiles are a desirable solution.

@alex-courtis
Copy link
Owner

IMO what is needed is a kanshi-style "profiles" mechanism where each profile has a set of matching rules and a config block to apply.

Can you please draft some rules that we may add to cfg.yaml? My imagination is poor and can't see much beyond hostname or display name.

@edrex
Copy link
Collaborator Author

edrex commented Apr 5, 2023

... separate config files per machine, configured via my dotfile manager

I use YADM - its per-host files feature can do this. I plan to add a Nix Home-Manager module for way-displays, which can differentiate per-host. Still, one config across machines seems like a nice convenience.

CLI ... persistently or ephemerally.

Oh, a comp/shell keybind to toggle the arrange/align would be nicer than editing the config file. Thanks!

I just set the order ... matches the 3 machines I use ... all left-to-right

I'd guess for a majority of users one arrange/align value will work across all their stations. I don't want to nudge you into adding features YAGNI!

@edrex
Copy link
Collaborator Author

edrex commented Apr 5, 2023

Can you please draft some rules that we may add to cfg.yaml?

It seems like the only really necessary dynamic profile matching criteria is that a given set of outputs are present.

  • Lid switch state already does what everyone wants it seems
  • Host matching could be useful, but since it won't change dynamically, it could be switched as a preprocessing step by eg YADM or home-manager. Also support could be added in a follow-up.

I first thought overloading the ORDER field, so that all entries in the order field would have to have a match for the profile to be applied (similar to kanshi's approach). I'm not sure how that would work with the new regex support though. Some rules may match multiple outputs?

Maybe there's still a way to make profile matching work via the ORDER field, but another possibility would be to add a dedicated field for profile matching:

PROFILES:
  a-profile-with-no-match-rule-always-applies:
    ARRANGE: COL # by default, unknown displays go above the laptop screen, eg for projectors
    ALIGN: MIDDLE
    ORDER:
      - '!.*$'
      - 'eDP-1' # eDP-1 defaults to last
  horizontal-laptop-right:
    MATCH:
      - '!(^Sharp Corporation 0x148D$)|(^Dell)' # When either of these displays are present, apply this profile atop the previous one
    ARRANGE: ROW
    ALIGN: BOTTOM
  hackerspace:
    MATCH:
      - 'Sharp Corporation 0x148D'
      - 'Monitor Maker ABC123' # When both of these displays are present, apply this profile too
    ORDER:
      - 'eDP-1' # in the hackerspace, laptop is on left
      - '!^Sharp'
      - '!^Monitor Maker'

WDYT?

MATCH could be named something else like DISPLAYS. If we ever wanted to additionally match on eg host, a HOST field could be added.

@matthewwardrop also curious if you have feedback/ideas here.

@alex-courtis
Copy link
Owner

alex-courtis commented Apr 7, 2023

PROFILES:
  horizontal-laptop-right:
     MATCH:

That works. Keeping it simple, all params could go in each profile, overriding defaults. Merge methods are already present.
LAPTOP_DISPLAY_PREFIX would be annoying to build but possible.
LOG_THRESHOLD doesn't make much sense and would likely result in strange output.

PROFILES:
  a-profile-with-no-match-rule-always-applies:

We could just use the toplevel defaults instead of a default profile.

Bonus points for Martin Fowler reference.

@alex-courtis
Copy link
Owner

There will be a nice solution for MATCH, but I cannot imagine it.

Even if we don't implement host matching, we should leave the door open for it and other future matches.

@matthewwardrop ideas please!

@alex-courtis
Copy link
Owner

alex-courtis commented Apr 7, 2023

Some rules may match multiple outputs?

I think we can trust the user. If they get a match for each of the match displays, the (edit: first) profile applies.

It would be documented.

@matthewwardrop
Copy link
Contributor

matthewwardrop commented Apr 9, 2023

Hi! Sorry for the delay, with things busy at work and with four munchkins at home, I don't have as much time as I'd like to clear out my inbox.

I also like the idea of context specific overrides... But like @edrex I use YADM as a workaround for this on my machine. If support were added, I'd keep the config as is, and then have context overlays that are matched in... Something like:

# usual config
ARRANGE: ...
ALIGN: ...
ORDER: ...

OVERLAYS:
    WORK: # name
        # nested config, dedented and processed like normal config, and then merged into parent config if MATCH is satisfied
        MATCH:
            OUTPUTS: # nested just in case we want to support matches on other things
                - usual regex or strings
        ALIGN: ....
        ....

You could even have match at the top-level too, where if provided and match is not satisfied, way-displays disables itself. This simplifies parsing a bit too, since the syntax could be arbitrarily nested and always have the same schema.

@alex-courtis
Copy link
Owner

way-displays disables itself.

Interesting. That would need to be an explicit setting as there's no means to set options that will disable everything.

arbitrarily nested and always have the same schema.

Yes. Same schema at root or inside a profile. The above setting would just be another entry in the schema.

@matthewwardrop
Copy link
Contributor

matthewwardrop commented Apr 9, 2023

If we went with something like I suggested, an empty parsed config would be that configuration option.

Oh, and I guess I'm less a fan of "profiles" as context specific instructions or "overlays". Profiles makes it sound more top-level (all or nothing, take the work profile, or the home profile, etc). I guess for me a profile is the evaluated entire state that is derived from matches /etc. This is probably just semantics.

@alex-courtis
Copy link
Owner

If we went with something like I suggested, an empty parsed config would be that configuration option.

Thinking more, I'm not sure how "do nothing" would look; there are no "defaults" to go back to when the user instructs "do nothing". It should become more clear after profiles exist and the user switch between them via CLI or hotplug.

Oh, and I guess I'm less a fan of "profiles" as context specific instructions or "overlays".

That makes sense - it's the base overridden by the profile. Fortunately all that code exists.

@edrex
Copy link
Collaborator Author

edrex commented Apr 10, 2023

We could just use the toplevel defaults instead of a default profile.

If support were added, I'd keep the config as is, and then have context overlays that are matched in

Okay, I see there are strong reasons to keep config backwards compatible by leaving toplevel options in place.

  • Smooth migration path without forcing config updates. Users can learn new syntax when they need it, or never if they don't.
  • The top-level syntax already exists and is starting to stabilize, so keeping it unchanged should help shelter existing users from breakages as new features are developed (opt-in, sort of like a feature flag).

the syntax could be arbitrarily nested and always have the same schema.

I can't speak to ease of implementation, but allowing deeply nested blocks seems confusing for users. No strong opinion as long as the use case is satisfied.

Profiles makes it sound more top-level (all or nothing, take the work profile, or the home profile, etc). I guess for me a profile is the evaluated entire state that is derived from matches /etc. This is probably just semantics.

... and then have context overlays that are matched in ...

Good names are important, and worth thinking about. Overlays does tell you how they work, if you know what an overlay is, which is a nice property. I feel like it sounds like a compositor thing which might be confusing. What about CONTEXTS? Each block is a context scoped by match rules. Contexts don't imply exclusivity like profiles do. Again though, don't care much as long as the use case is satisfied. Bike shed.

You could even have match at the top-level too, where if provided and match is not satisfied, way-displays disables itself.

Thinking more, I'm not sure how "do nothing" would look; there are no "defaults" to go back to when the user instructs "do nothing". It should become more clear after profiles exist and the user switch between them via CLI or hotplug.

I feel like if neither any currently matching blocks nor the root block set a value for an option, it should act the same as when the option isn't set in the current implementation (ie, it should be reinitialized to the programmatically-defined default value). (update: see next comment)

I don't see a use case for having a matching rule at the top level, but maybe I'm missing something.

Sorry slow response, got dogpiled by life :)

@edrex
Copy link
Collaborator Author

edrex commented Apr 10, 2023

it should be reinitialized to the programmatically-defined default value

I realized that keys like like ORDER probably don't actually have a default value in way-displays, and instead the compositor is providing a default (which might just come from the enumeration order from the kernel).

Thinking about this some more, I don't see a need for (programmatic) default values at all: when matched blocks change (default or no) all config from matching blocks (including default) applies, but keys that aren't explicitly set aren't applied (and the compositor decides what to do). Easy.

@alex-courtis
Copy link
Owner

don't actually have a default value in way-displays, and instead the compositor is providing a default (which might just come from the enumeration order from the kernel).

That's the issue. Wayland itself does little other than enable preferred mode on displays. River does nothing, sway applies a general left to right in discovered (unpredictable) order.

all config from matching blocks (including default) applies, but keys that aren't explicitly set aren't applied

That is all we can do.

@alex-courtis
Copy link
Owner

alex-courtis commented Apr 10, 2023

Okay, I see there are strong reasons to keep config backwards compatible by leaving toplevel options in place.

That's non-negotiable. Profiles/overlays are additions.

I can't speak to ease of implementation, but allowing deeply nested blocks seems confusing for users. No strong opinion as long as the use case is satisfied.

We don't have any need for nesting; one level is enough. The user can write match rules for each one. There may be a bit of repetition but that's Too Bad.

Good names are important, and worth thinking about. Overlays does tell you how they work, if you know what an overlay is, which is a nice property. I feel like it sounds like a compositor thing which might be confusing. What about CONTEXTS? Each block is a context scoped by match rules. Contexts don't imply exclusivity like profiles do. Again though, don't care much as long as the use case is satisfied. Bike shed.

Naming is always hard. Build it with a placeholder and things will become clearer during implementation. After a merge is implemented and demonstrated in a real situation it should give some insight.

I don't see a use case for having a matching rule at the top level, but maybe I'm missing something.

I don't think that adds anything but confusion. KISS: profile/context + top level or top level

@alex-courtis alex-courtis mentioned this issue Apr 10, 2023
6 tasks
@alex-courtis alex-courtis changed the title "profiles" Feature: Profiles May 15, 2023
@alex-courtis alex-courtis added the feature New feature or request label May 15, 2023
@alex-courtis alex-courtis added this to the 1.9.0 Roadmap milestone Jun 24, 2023
@alex-courtis alex-courtis modified the milestones: 1.9, 1.10 Sep 4, 2023
@alex-courtis alex-courtis modified the milestones: 1.10, 1.11 Nov 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants