sezanzeb / input-remapper

🎮 ⌨ An easy to use tool to change the behaviour of your input devices.
GNU General Public License v3.0
3.66k stars 153 forks source link

NixOS compatibility (read-only filesystem) #663

Open bphenriques opened 1 year ago

bphenriques commented 1 year ago

Hello!

I have been using version 1.5 (the last one in NixOS packages) and so far the experience is great. However, I would like to configure input-remapper as part of my NixOS settings like so:

# This mount two JSON files under the the right place
{
  services.input-remapper.enable = true;
  home-manager.users.${config.user.name}.xdg.configFile = {
    "input-remapper/config.json".text = builtins.toJSON
       {
          "version" = "1.6"; # Not ideal, as this depends on the binary version.
          "autoload"."Logitech G305" = "Media.json";
       };
    "input-remapper/presets/Logitech G305/Media.json".text = builtins.toJSON
        {
          mapping = {
            "1,275,1" = ["XF86AudioPrev" "keyboard"]; # "Side" button
            "1,276,1" = ["XF86AudioNext" "keyboard"]; # "Extra" button
            "1,277,1" = ["XF86AudioPlay" "keyboard"]; # "FORWARD" button
        };
      };
  };
}

But the following happens:

$ input-remapper-gtk -d
18:00:32.728909 16537 GUI INFO logger.py:223: input-remapper-gtk 1.5.0 e31a1b2bc5d23fe13130afcc242063196335399f https://github.com/sezanzeb/input-remapper
18:00:32.729025 16537 GUI INFO logger.py:231: python-evdev 1.6.1
18:00:32.729085 16537 GUI WARNING logger.py:234: Debug level will log all your keystrokes! Do not post this output in the internet if you typed in sensitive or private information with your device!
18:00:32.729157 16537 GUI DEBUG .input-remapper-gtk-wrapped:56: Using locale directory: /nix/store/nxg6gvinpc5zisvzxjr2wngslnqw2mn0-input-remapper-1.5.0/usr/share/input-remapper/lang
18:00:32.734551 16537 GUI DEBUG pipe.py:62: Creating new pipe for "/tmp/input-remapper-bphenriques/results"
18:00:32.736928 16537 GUI DEBUG pipe.py:62: Creating new pipe for "/tmp/input-remapper-bphenriques/commands"
Traceback (most recent call last):
  File "/nix/store/nxg6gvinpc5zisvzxjr2wngslnqw2mn0-input-remapper-1.5.0/bin/.input-remapper-gtk-wrapped", line 63, in <module>
    migrate()
  File "/nix/store/nxg6gvinpc5zisvzxjr2wngslnqw2mn0-input-remapper-1.5.0/lib/python3.10/site-packages/inputremapper/configs/migrations.py", line 267, in migrate
    _mapping_keys()
  File "/nix/store/nxg6gvinpc5zisvzxjr2wngslnqw2mn0-input-remapper-1.5.0/lib/python3.10/site-packages/inputremapper/configs/migrations.py", line 127, in _mapping_keys
    with open(preset, "w") as file:
OSError: [Errno 30] Read-only file system: '/home/bphenriques/.config/input-remapper/presets/Logitech G305/Media.json'

Questions:

Thank you!

Edit: just noticed that maybe this one https://github.com/sezanzeb/input-remapper/issues/448 may be linked here

bphenriques commented 8 months ago

What is the recommended way to use with NixOS?

Using home-manager, one can do:

 xdg.configFile = {
      "input-remapper/config.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/modules/input-remapper/config.json";
      "input-remapper/presets/Logitech G305/Media.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/modules/input-remapper/Media.json";
    };
Myrkskog commented 4 months ago

Trying my best to follow your dot files. As I am new to nix and programming as a whole, I am lost.

In your peripheral.nix are other attributes being referenced other than what is at the top of the file? The note "ensure config is within home-manager's context" I am not sure exactly what home = { config, ... }: is doing.

Does home reference /home/default.nix and DOTFILES_LOCATION= ?

I need to get input-remapper up and running on my new system. I have become very reliant on these customizations.

bphenriques commented 4 months ago

In your peripheral.nix are other attributes being referenced other than what is at the top of the file? The note "ensure config is within home-manager's context" I am not sure exactly what home = { config, ... }: is doing.

Will do my best to break it down, I am not super proficient with some concepts but I can try to help.

Does home reference /home/default.nix and DOTFILES_LOCATION= ?

home refers to an alias that I created:

{
  imports = [
    (mkAliasOptionModule ["home"] ["home-manager" "users" "bphenriques"])
  ];

Let me try to break it down:

{ config, ... }:
{
  ...
  home = { config, ... }: {
    xdg.configFile = {
      "input-remapper/config.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/input-remapper/config.json";
      "input-remapper/presets/Logitech G305/Media.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/input-remapper/Media.json";
    };
  };
}

home is actually a alias to my user's home:

{
  imports = [
    (mkAliasOptionModule ["home"] ["home-manager" "users" "bphenriques"])
  ];

Let's re-write without the alias:

{ config, ... }:
{
  ...
  home-manager.users.bphenriques = { config, ... }: {
    xdg.configFile = {
      "input-remapper/config.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/input-remapper/config.json";
      "input-remapper/presets/Logitech G305/Media.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/input-remapper/Media.json";
    };
  };
}

Now, lets focus on this bit:

home-manager.users.bphenriques = { config, ... }: {

home-manager typically receives a set ({ key = value; key2 = value2 }) but it also supports receiving a function. Note that, the current peripherals.nix file is a NixOS configuration, therefore the config on the top of the file represents a NixOS configuration, not a home-manager one. Therefore the following would fail because config.lib.file.mkOutOfStoreSymlink is not defined in NixOS:

{ config, ... }:
{
  ...
  # Fails because `config` refers to NixOS where `config.lib.file.mkOutOfStoreSymlink` is not defined
  home-manager.users.bphenriques = {
    xdg.configFile = {
      "input-remapper/config.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/input-remapper/config.json";
      "input-remapper/presets/Logitech G305/Media.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/input-remapper/Media.json";
    };
  };
}

Therefore, I need to use the alternative syntax where I can pass in a function that receives { config, ... } and returns a set { ... }. That config that is passed is a point to home-manager's configuration where config.lib.file.mkOutOfStoreSymlink is defined (see source):

{ config, ... }:
{
  ...
  home-manager.users.bphenriques = { config, ... }: {
    xdg.configFile = {
      "input-remapper/config.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/input-remapper/config.json";
      "input-remapper/presets/Logitech G305/Media.json".source = config.lib.file.mkOutOfStoreSymlink "${hostDir}/input-remapper/Media.json";
    };
  };
}

Note: depending on your setup, you may avoid { config, ... }:. I am doing this way because I want to keep both input-remapper system service and and the home configuration in the same file.

Now, why: config.lib.file.mkOutOfStoreSymlink "${hostDir}/input-remapper/Media.json" and not ./input-remapper/Media.json"?

The reason is because files in the nix-store are readonly to the user that runs input-remapper:

OSError: [Errno 30] Read-only file system: '/home/bphenriques/.config/input-remapper/presets/Logitech G305/Media.json'

As a workaround, we can actually create "out of store" files (config.lib.file.mkOutOfStoreSymlink) where the file in the store actually ends up pointing to a absolute path ${hostDir}/modules/input-remapper/config.json that the input-remapper can write to using the default user. The path has to be absolute.

Each setup is unique and Nix is not the easiest language to learn, specially if you are not familiar with some programming concepts (functions and scope is one of them). I hope this break down helps you a bit more :)

Myrkskog commented 4 months ago

You my friend went above and beyond! I appreciate it very much. Looking over this now.

I'll report back if I get things working. With the time and effort placed in your explanation; I will most likely learn something!

Thank you again 👍

bphenriques commented 4 months ago

Btw, if you have more questions create a issue in my dotfiles repository to avoid discussing in this thread (would be off-topic).