rszyma / kanata-tray

Tray Icon for Kanata
GNU General Public License v3.0
55 stars 1 forks source link

Feature: Multiple Kanata instances with unique configurations #10

Closed reidprichard closed 6 months ago

reidprichard commented 7 months ago

I use kanata_wintercept to independently remap two devices. This allows me to map my macropad buttons to a-z for convenience while still being able to use my keyboard, but it requires me to run two separate Kanata instances.

I would love to just be able to run kanata-tray and have both instances spawn. I realize this would require some substantial change to the config file. Perhaps something of the following would be possible, where each file has its own unique configuration overrides defined in an inline table; any values not specified therein are replaced by the global/default values. I don't see a reason this couldn't coexist with the existing format.

configurations = {
    "./kanata_1.kbd" = {layer_icons.base = "hello.ico", general.tcp_port = 8000},
    "./kanata_2.kbd" = {layer_icons.base = "qwerty.ico", general.tcp_port = 8001},
    "./kanata_3.kbd" = {} # I'm not sure if this is valid TOML syntax?
}

Or maybe a format like the following would be better for simpler compatibility with the current format:

configurations = [
    {filename = "./kanata_1.kbd", config = {layer_icons.base = "hello.ico", general.tcp_port = 8000}},
    {filename = "./kanata_2.kbd", config = {layer_icons.base = "qwerty.ico", general.tcp_port = 8001}},
    "./kanata_3.kbd",
]

Disclaimer: I briefly read through the TOML spec but am otherwise unfamiliar with the format.

rszyma commented 7 months ago

Sure, this sounds like a useful feature.

I'm not against making backwards incompatible changes to config.toml, we can even flip the config completely, as long as the result is a substantial improvement. While changing config format, #11 should also be taken into account, so breaking changes don't need to be done twice. But if a few iterations would be needed, I'm ok with it too.

rszyma commented 7 months ago

Implementing this is going to require major changes in both config and tray UI, so I'd like to handle it myself.

rszyma commented 7 months ago

A preview of a new config file format: https://gist.github.com/rszyma/dea3de8a6013070bba9cfeabf24e1fce

rszyma commented 7 months ago

and an example config:

"$schema" = "https://gist.githubusercontent.com/rszyma/dea3de8a6013070bba9cfeabf24e1fce/raw"

[general]
allow_running_multiple_presets_concurrently = true

[defaults]
exe = "" # Kanata executable path. If empty/omitted, system PATH is searched.
tcp_port = 5829 # [optional] Default TCP port to use. Optional. Default is 5829 (it's an arbitrary number)

[defaults.layer_icons]
layer1 = "icon.ico"
layer2 = "qwerty.ico"

# Presets can be minimal just like the one below.
[presets.my-default-config]
autorun = true

# A preset can also be configured extensively:
[presets.'My config 2']
autorun = true
exe = "" # [optional] kanata executable override. If empty, the default kanata executable is used. 
cfg = "" # [optional] `--cfg` argument passed to kanata
tcp_port = 5829 # Must be set for each preset if `allow_running_multiple_configurations` is set to `true`, otherwise optional.
extra_args = "--nodelay --debug"
rszyma commented 7 months ago

naming change suggestions welcome

reidprichard commented 7 months ago

Cool stuff! A few thoughts:

reidprichard commented 7 months ago

Ok, I think my confusion mostly comes down to lack of familiarity with TOML. To me,

[defaults]
exe = "" # Kanata executable path. If empty/omitted, system PATH is searched.
tcp_port = 5829 # [optional] Default TCP port to use. Optional. Default is 5829 (it's an arbitrary number)

[defaults.layer_icons]
layer1 = "icon.ico"
layer2 = "qwerty.ico"

would be clearer as

[defaults]
exe = "" # Kanata executable path. If empty/omitted, system PATH is searched.
tcp_port = 5829 # [optional] Default TCP port to use. Optional. Default is 5829 (it's an arbitrary number)
layer_icons = {
  layer1 = "icon.ico"
  layer2 = "qwerty.ico"
}

but I now realize that's not valid TOML. Not to mention the fact that how the example is presented has little to do with the actual config file specification.

That said, if this is to eventually integrate other utilities, what about organizing the config in terms of application? I've edited your example below with the following changes:

  1. Rename "general" section to "kanata_tray"
  2. Rename "defaults" section to "kanata"
  3. Added another example with inline table in case people aren't familiar with TOML
  4. Rename exe key to path so you don't have the potentially-confusing kanata.exe key name.
"$schema" = "https://gist.githubusercontent.com/rszyma/dea3de8a6013070bba9cfeabf24e1fce/raw"

[kanata_tray]
allow_running_multiple_presets_concurrently = true

[kanata]
path = "" # Kanata executable path. If empty/omitted, system PATH is searched.
tcp_port = 5829 # [optional] Default TCP port to use. Optional. Default is 5829 (it's an arbitrary number)

[kanata.layer_icons]
layer1 = "icon.ico"
layer2 = "qwerty.ico"

# Presets can be minimal just like the one below.
[presets.my-default-config]
autorun = true
# Presumably this one would use 5829?

# A preset can also be configured extensively:
[presets.'My config 2']
autorun = true
kanata.path = "" # [optional] kanata executable override. If empty, the default kanata executable is used. 
kanata.config_file = "config.kbd" # [optional] `--cfg` argument passed to kanata
kanata.tcp_port = 5830 # Must be set for each preset if `allow_running_multiple_configurations` is set to `true`, otherwise optional.
kanata.extra_args = "--nodelay --debug"
kanata.layer_icons = {layer1 = "icon_2.ico", layer2 = "qwerty_2.ico"}

[presets.'another config']
autorun = false
kanata = {path = "./kanata_wintercept.exe", tcp_port = 5831}

To me, it makes more sense that presets.preset-name.kanata.path overrides kanata.path than if it were to override defaults.path, but I do see that calling out values as "default" could avoid a different vector of confusion. I also think that having sections named according to their particular binary could enable more flexibility in the future. For example, if we were to integrate kanata_helper_daemon, "My config 2" above could be modified:

[presets.'My config 2']
autorun = true
kanata.path = "" # [optional] kanata executable override. If empty, the default kanata executable is used. 
kanata.config_file = "config.kbd" # [optional] `--cfg` argument passed to kanata
kanata.tcp_port = 5830 # Must be set for each preset if `allow_running_multiple_configurations` is set to `true`, otherwise optional.
kanata.extra_args = "--nodelay --debug"
kanata.layer_icons = {layer1 = "icon_2.ico", layer2 = "qwerty_2.ico"}
# Additions below
kanata_helper_daemon.path = "./kanata_helper_daemon.exe"
kanata_helper_daemon.args = "--config-file=./config.kbd --port=5830"

It's a little clunky having to repeat the config file path and the port number in the above example, but I can't think of a generalizable way to avoid that. But then again, the parser having to know what to do with the kanata_helper_daemon key is already non-general. In that case, maybe a format like below would be best, in which case there's less value in renaming everything as I have.

...
# Additions below
additional_binaries = ["./kanata_helper_daemon.exe --config-file=./config.kbd --port=5830"]

On the other end of the spectrum, things could be tuned to integrate tightly with related programs like below.

[presets.'My config 2']
autorun = true
config_file = "config.kbd" # [optional] `--cfg` argument passed to kanata
tcp_port = 5830 # Must be set for each preset if `allow_running_multiple_configurations` is set to `true`, otherwise optional.
kanata.path = "" # [optional] kanata executable override. If empty, the default kanata executable is used. 
kanata.extra_args = "--nodelay --debug"
kanata.layer_icons = {layer1 = "icon_2.ico", layer2 = "qwerty_2.ico"}
# Additions below
kanata_helper_daemon.path = "./kanata_helper_daemon.exe" 
# --config-file and --port args automatically filled in by config_file and tcp_port args above

Sorry to ramble, but I hope my thoughts help. I suppose it just depends how much you want to manually integrate kanata_tray with other projects. If it were me I think I'd be taking the easy approach with the additional_binaries option :) but either way I think it's more intuitive to me to rename general to kanata_tray and defaults to kanata.

(btw, if you type "toml" after the opening three backticks of your code block you'll get syntax highlighting like above)

rszyma commented 7 months ago
  • allow_running_multiple_presets_concurrently could be shortened to allow_concurrent_presets
  • exe is a Windows-y term, but I can't think of a better option. Would kanata or binary be clear?
  • What about config, config_file, or kanata_config instead of cfg? I see the merit in mirroring Kanata's arguments, but I think more verbosity would be beneficial.

Agree. The field names don't need to be that short. Updated config schema: https://gist.github.com/rszyma/dea3de8a6013070bba9cfeabf24e1fce/revisions

rszyma commented 7 months ago

Sorry to ramble, but I hope my thoughts help. I suppose it just depends how much you want to manually integrate kanata_tray with other projects. If it were me I think I'd be taking the easy approach with the additional_binaries option :) but either way I think it's more intuitive to me to rename general to kanata_tray and defaults to kanata

Yes, thank you, it's helpful to see another point of view.

99% I'm not going to be directly integrating other tools with kanata-tray. And as your suggestion I would rather add support for it adding a field like additional_binaries. This way one would be able to integrate any external tool, not just specific ones.

rszyma commented 7 months ago

but either way I think it's more intuitive to me to rename general to kanata_tray and defaults to kanata

I disagree. The way I see it is that group names for fields should be describing what what kind of settings are contained in the group. Looking at group defaults I can tell that the settings in this group define some kind of defaults. Looking at group kanata I would have no hint at all what it is about. Similar story with general.