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

nixos/xserver: add option to install custom xkb layouts #47764

Merged
merged 3 commits into from Jul 26, 2019

Conversation

rnhmjoj
Copy link
Contributor

@rnhmjoj rnhmjoj commented Oct 3, 2018

Motivation for this change

I have found no way to add a custom keyboard layout to xkb declaratively from nixos configuration.
I wrote this module based on a blog post and it's working as intended so far. See also #31138.

The usage is as follows:

  services.xserver = {  
    layout = "rn";
    extraLayouts.rn = {
      description = "US with alt-gr, greek, math and other simbols";
      languages = [ "eng" "ita" ];
      filepath = /home/rnhmjoj/.config/xkb/symbols/rn;
    };
  };

It works by patching various packages so I'm not sure if this is acceptable.

Things done

@peterhoeg
Copy link
Member

This is great - I'm (hopefully) going to try this out over the weekend. Instead of overriding the various packages, can't we use a wrapper derivation instead that consists of vanilla upstream + whatever additional layouts you want?

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Nov 11, 2018

Update? I don't think it's possibile to do that because the file base.xml and others must be modified to account for the added layouts.

@duckonomy
Copy link

Dang this works!
I've been trying to mess around with the origin blog post and failed on my own layout.
But this works! Thank you!

nixos/modules/services/x11/extra-layouts.nix Outdated Show resolved Hide resolved
nixos/modules/services/x11/extra-layouts.nix Outdated Show resolved Hide resolved
@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Jan 25, 2019

I tested it again with overlays and seems ok. I also added a comment to explain the situation.

@infinisil
Copy link
Member

LGTM, but I'd like another pair of eyes to take a look too, the way only certain things get patched is very unorthodox

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Jan 26, 2019

Thank you for the review! I'm aware of that, unfortunately I don't know of any other way.

@infinisil
Copy link
Member

An idea I have is to mark packages that use xkeyboard_config as being either essential or not. If it's marked as essential, it will get the patched version. You have this comment in the code:

    # Only the following packages are necessary to set
    # a custom layout anyway:

I'm wondering how you determined this list of packages, they're the ones we'd mark as essential.

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Jan 26, 2019

From the blog post, besides xkbcomp and xbkmap:

  • xkbvalidate: needed to make the keyboard layout test pass
  • xserver: otherwise the xkbcomp will not find the layout even if setxkbmap accepts it.

@infinisil
Copy link
Member

Okay so I tried this PR out myself, with the following config:

{
  services.xserver = {
    exportConfiguration = true;
    extraLayouts.cdvp = {
      description = "Custom Programmer Dvorak";
      languages = [ "eng" ];
      layoutFile = builtins.toFile "layout.xkb" ''
        xkb_keymap {
	      xkb_keycodes  { include "evdev+aliases(qwerty)"  };
	      xkb_types     { include "complete"	};
	      xkb_compat    { include "complete"	};
	      xkb_symbols   {
		    include "pc+us(dvp)+inet(evdev)+capslock(backspace)"
		    key <LSGT> { [ Control_L ] };
		    key <RWIN> { [ Break ] };
		    modifier_map Control { <LSGT> };
	      };
	      xkb_geometry  { include "pc(pc105)"	};
       };
     '';
   };
 };

But no luck, after a bunch of compilations (and even restarting my display manager), it's as if nothing changed, there's no cdvp file in /etc/X11/xkb/symbols, as I'd have expected it (exportConfiguration should make that work), and setxkbsymbols cdvp says Error loading new keyboard description (which it says for all layouts that aren't known).

What am I missing here?

@infinisil
Copy link
Member

I also tried

{
  layoutFile = builtins.toFile "layout" ''
    xkb_symbols {
      include "pc+us(dvp)+inet(evdev)+capslock(backspace)"
      key <LSGT> { [ Control_L ] };
      key <RWIN> { [ Break ] };
      modifier_map Control { <LSGT> };
    };
  '';
}

but no luck either, same result

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Mar 26, 2019

So, I fixed the issue left when renaming the attributes.
I tested adding more than one layout and it works as expected.
I also added options to install all possible xkb components (compat, geometry, ...) but I haven't tested it properly. I doesn't break the existing functionality, though.

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Jul 26, 2019

You should also set services.xserver.xkbDir to the newly patched one

Done.

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Jul 26, 2019

I also moved the patchable xkeyboardconfig to the xorg packages set.

@infinisil
Copy link
Member

@GrahamcOfBorg eval

Copy link
Member

@infinisil infinisil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@infinisil infinisil merged commit d3dfe06 into NixOS:master Jul 26, 2019
@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Jul 26, 2019

@infinisil Thank you for all the help!

@ghost
Copy link

ghost commented Jul 29, 2019

Yes, finally! Thank you guys!

@dasj19
Copy link
Contributor

dasj19 commented Aug 16, 2019

Hello, I tried to use this to add a system-wide custom keyboard layout. But this does not seem to work in gnome.

I have the following in my configuration.nix:

  services.xserver.layout = "esrodk,es,dk,ro";
  services.xserver.extraLayouts.esrodk = {
    description = "Spanish layout with romanian and danish diacritics";
    languages = ["spa"];
    symbolsFile = /etc/nixos/esrodk;
  };

When I build and log in gnome the new layout shows up as a language instead of a variant of a language layout.
I come from debian and there I applied the following patch:

diff -ur /usr/share/X11/xkb/symbols/es /usr/share/X11/xkb/symbols/es
--- /usr/share/X11/xkb/symbols/es	2019-02-10 22:47:30.962363954 +0100
+++ /usr/share/X11/xkb/symbols/es	2019-02-10 23:03:05.664200582 +0100
@@ -264,3 +264,29 @@
 	xkb_symbols "sun_type6" {
 	include "sun_vndr/es(sun_type6)"
 };
+
+partial alphanumeric_keys
+xkb_symbols "esrodk" {
+
+    include "es(basic)"
+
+    name[Group1]="Spanish +RO/DK";
+
+    key <AE10>	{ [         0,      equal,   0x00002260,       degree ]	};
+    key <AE11>	{ [apostrophe, question, doublelowquotemark, question ]	};
+    key <AE12>	{ [exclamdown, questiondown, rightdoublequotemark, exclam ]	};
+
+    key <AD01>  { [         q,          Q,  acircumflex,  Acircumflex ] };
+    key <AD02>	{ [         w,          W,        aring,	Aring ]	};
+    key <AD05>  { [         t,          T,    0x100021b,    0x100021a ] };
+    key <AD06>	{ [         y,          Y,    leftarrow,   rightarrow ]	};
+    key <AD07>	{ [         u,          U,    uparrow,      downarrow ]	};
+    key <AD08>  { [         i,          I,  icircumflex,  Icircumflex ] };
+
+    key <AC01>  { [         a,          A,       abreve,       Abreve ] };
+    key <AC02>  { [         s,          S,    0x1000219,    0x1000218 ] };
+
+    key <LSGT>	{ [      less,    greater,    	     ae,	   AE ]	};
+
+    include "level3(ralt_switch)"
+};
diff -ur /usr/share/X11/xkb/rules/evdev.lst /usr/share/X11/xkb/rules/evdev.lst
--- /usr/share/X11/xkb/rules/evdev.lst	2019-02-10 22:46:55.358727634 +0100
+++ /usr/share/X11/xkb/rules/evdev.lst	2019-02-10 23:02:15.597083721 +0100
@@ -650,6 +650,7 @@
   ast             es: Asturian (Spain, with bottom-dot H and bottom-dot L)
   cat             es: Catalan (Spain, with middle-dot L)
   mac             es: Spanish (Macintosh)
+  esrodk            es: Spanish (+RO/DK)
   nodeadkeys      se: Swedish (no dead keys)
   dvorak          se: Swedish (Dvorak)
   rus             se: Russian (Sweden, phonetic)
diff -ur /usr/share/X11/xkb/rules/evdev.xml /usr/share/X11/xkb/rules/evdev.xml
--- /usr/share/X11/xkb/rules/evdev.xml	2019-02-10 22:47:19.018488400 +0100
+++ /usr/share/X11/xkb/rules/evdev.xml	2019-02-10 23:03:57.375281353 +0100
@@ -4930,6 +4930,12 @@
             <description>Spanish (Macintosh)</description>
           </configItem>
         </variant>
+	<variant>
+          <configItem>
+            <name>esrodk</name>
+            <description>Spanish (+RO/DK)</description>
+          </configItem>
+        </variant>
       </variantList>
     </layout>
     <layout>
@@ -7388,4 +7394,4 @@
       </option>
     </group>
   </optionList>
-</xkbConfigRegistry>
\ Nici un element de linie nouă la sfârşitul fişierului
+</xkbConfigRegistry>

Now when I look at the files that get builded from configuration.nix ... in the evdev.lst there is an entry:

! model
..........
  teck227         Truly Ergonomic Computer Keyboard Model 227 (Wide Alt keys)
  teck229         Truly Ergonomic Computer Keyboard Model 229 (Standard sized Alt keys, additional Super and Menu key)
  esrodk 
! layout
...........
  md              Moldavian
  id              Indonesian (Jawi)
  my              Malay (Jawi, Arabic Keyboard)
  esrodk 

Note that esrodk has no human-friendly name.. this might be the reason it has issues with gnome-control-center region settings.

Note that even though it shows up as the first keyboard layout it is not used... and when trying to search within regional settings after this layout it does not show up.
Screenshot

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Aug 17, 2019

But this does not seem to work in gnome.

I'm not a gnome use so I've never tested this. What does localectl says? Is the layout selected?
You could also set i18n.consoleUseXkbConfig = true; and see if it works in the linux vt;

When I build and log in gnome the new layout shows up as a language instead of a variant of a language layout.

This is intended: this options installs a layout by placing a new file in the xkb source tree.
To make a layout variant It would have to edit an existing xkb: It can be done but it's more complicated. Maybe in the future...

Note that esrodk has no human-friendly name.. this might be the reason it has issues with gnome-control-center region settings.

The description should be in there... this maybe be a bug.

@dasj19
Copy link
Contributor

dasj19 commented Aug 17, 2019

The result of localectl...

[root@nixos:~]# localectl
   System Locale: LANG=ro_RO.UTF-8
       VC Keymap: es
      X11 Layout: esrodk,es,dk,ro
       X11 Model: pc104
     X11 Options: terminate:ctrl_alt_bksp

I'll get back when I build with i18n.consoleUseXkbConfig = true;

Another thing... it is really needed to build all the gnome packages from source when using services.xserver.extraLayouts ?

@dasj19
Copy link
Contributor

dasj19 commented Aug 18, 2019

Hello I can confirm that with the following:

  # Enable the X11 windowing system.
  services.xserver.enable = true;
  services.xserver.layout = "esrodk,es,dk,ro";
  services.xserver.xkbModel = "esrodk";
  services.xserver.extraLayouts.esrodk = {
    description = "Spanish layout with romanian and danish diacritics";
    languages = ["spa"];
    symbolsFile = /etc/nixos/esrodk;
  };

  # Select internationalisation properties.
  i18n = {
    consoleFont = "Lat2-Terminus16";
    consoleUseXkbConfig = true;
    defaultLocale = "ro_RO.UTF-8";
    supportedLocales = [ "en_US.UTF-8/UTF-8" "ro_RO.UTF-8/UTF-8" "da_DK.UTF-8/UTF-8" "es_ES.UTF-8/UTF-8" ];
  };

configuration in configuration.nix I can use "esrodk" as default in a tty terminal... but still does not work in gnome.
And I believe the reason for this is the missing human-friendly name in the xkeyboard-config-2.27/share/X11/xkb/rules/evdev.lst ... as I only have a few days experience with nixos I don't know how to test my theory/debug further.

localectl ouputs the following now:

System Locale: LANG=ro_RO.UTF-8
       VC Keymap: /nix/store/k43rdd6m1c1b713xisynkqmqi45j18km-xkb-console-keymap
      X11 Layout: esrodk,es,dk,ro
       X11 Model: esrodk
     X11 Options: terminate:ctrl_alt_bksp

Update: I fixed the human-friendly name issue in this commit... dasj19@87ec211 however I'm still trying to get it working in gnome.

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Aug 20, 2019

Another thing... it is really needed to build all the gnome packages from source when using services.xserver.extraLayouts ?

We are already trying to reduce the number of rebuilds by not overriding xkb directly, however xorgserver, setxkbmap, xkbcomp, ckbcomp and xkbvalidate must be overriden.

It seems the problem is gdm and mutter depends on xorgserver.
Perhaps we could pass xkbcomp to the X server without reconfiguring the build but it seems unlikely.

Update: I fixed the human-friendly name issue in this commit... dasj19/nixpkgs@87ec211

Oh, right: that broke when xkb dropped intltool. Could you make a PR?

however I'm still trying to get it working in gnome.

I'll try it too as soon as I have some spare time; I should also look into other DEs as well. I know it, at least, works in XFCE.

@dasj19
Copy link
Contributor

dasj19 commented Aug 21, 2019

Some developments...

  1. I opened the above PR
  2. I asked over at gnome about a related issue which seems to be a side-effect : https://gitlab.gnome.org/GNOME/libgnomekbd/issues/3
  3. I noticed that I can use my new keyboard layout in gnome if I do setxkbmap esrodk, but this is kind of a hardcode because the interface does not show that the 'esrodk' layout is in use.
  4. The output of setxkbmap -print -verbose 10 is:
Setting verbose level to 10
locale is C
Trying to load rules file ./rules/evdev...
Trying to load rules file /nix/store/l246g9yz3y85qaiqh3y510qc0yq77j3v-setxkbmap-1.3.2/share/X11/xkb/rules/evdev...
Success.
Applied rules from evdev:
rules:      evdev
model:      es
layout:     es,dk,ro,us
variant:    ,,,
options:    terminate:ctrl_alt_bksp
Trying to build keymap using the following components:
keycodes:   evdev+aliases(qwerty)
types:      complete
compat:     complete
symbols:    pc+es+dk:2+ro:3+us:4+inet(evdev)+terminate(ctrl_alt_bksp)
geometry:   pc(pc104)
xkb_keymap {
	xkb_keycodes  { include "evdev+aliases(qwerty)"	};
	xkb_types     { include "complete"	};
	xkb_compat    { include "complete"	};
	xkb_symbols   { include "pc+es+dk:2+ro:3+us:4+inet(evdev)+terminate(ctrl_alt_bksp)"	};
	xkb_geometry  { include "pc(pc104)"	};
};

and after setxkbmap esrodk

Setting verbose level to 10
locale is C
Trying to load rules file ./rules/evdev...
Trying to load rules file /nix/store/l246g9yz3y85qaiqh3y510qc0yq77j3v-setxkbmap-1.3.2/share/X11/xkb/rules/evdev...
Success.
Applied rules from evdev:
rules:      evdev
model:      es
layout:     esrodk
options:    terminate:ctrl_alt_bksp
Trying to build keymap using the following components:
keycodes:   evdev+aliases(qwerty)
types:      complete
compat:     complete
symbols:    pc+esrodk+inet(evdev)+terminate(ctrl_alt_bksp)
geometry:   pc(pc104)
xkb_keymap {
	xkb_keycodes  { include "evdev+aliases(qwerty)"	};
	xkb_types     { include "complete"	};
	xkb_compat    { include "complete"	};
	xkb_symbols   { include "pc+esrodk+inet(evdev)+terminate(ctrl_alt_bksp)"};
	xkb_geometry  { include "pc(pc104)"	};
};

Note: the layout part of the 2 outputs.
I do not know if my findings are of any help... but I guess they are a step in the right direction

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Aug 21, 2019

Did you try setting only one layout? I mean like services.xserver.layout = "esrodk";

@dasj19
Copy link
Contributor

dasj19 commented Aug 21, 2019

I just tried... It looks like it uses the US layout ...

This is what i have in configuration.nix:

  # Enable the X11 windowing system.
  services.xserver.enable = true;
  services.xserver.layout = "esrodk";
  services.xserver.xkbModel = "esrodk";
  services.xserver.extraLayouts.esrodk = {
    description = "Spanish layout with romanian and danish diacritics";
    languages = ["spa"];
    symbolsFile = /etc/nixos/esrodk;
  };

  # Select internationalisation properties.
  i18n = {
    consoleFont = "Lat2-Terminus16";
    consoleUseXkbConfig = true;
    defaultLocale = "ro_RO.UTF-8";
    supportedLocales = [ "en_US.UTF-8/UTF-8" "ro_RO.UTF-8/UTF-8" "da_DK.UTF-8/UTF-8" "es_ES.UTF-8/UTF-8" ];
  };

After rebooting GDM uses US layout, same as the rest of Gnome, while TTY uses "esrodk".

Some more info:
Output of setxkbmap -print -verbose 10

Setting verbose level to 10
locale is C
Trying to load rules file ./rules/evdev...
Trying to load rules file /nix/store/lx0czczcaribbiiy6frsrv4g70vb6ic7-setxkbmap-1.3.1/share/X11/xkb/rules/evdev...
Success.
Applied rules from evdev:
rules:      evdev
model:      esrodk
layout:     us,us
variant:    ,
options:    terminate:ctrl_alt_bksp
Trying to build keymap using the following components:
keycodes:   evdev+aliases(qwerty)
types:      complete
compat:     complete
symbols:    pc+us+us:2+inet(evdev)+terminate(ctrl_alt_bksp)
geometry:   pc(pc104)
xkb_keymap {
	xkb_keycodes  { include "evdev+aliases(qwerty)"	};
	xkb_types     { include "complete"	};
	xkb_compat    { include "complete"	};
	xkb_symbols   { include "pc+us+us:2+inet(evdev)+terminate(ctrl_alt_bksp)"	};
	xkb_geometry  { include "pc(pc104)"	};
};

Output of localectl:

   System Locale: LANG=ro_RO.UTF-8
       VC Keymap: /nix/store/phd5cpzk3rrbn6px97pj89nn46i4yign-xkb-console-keymap
      X11 Layout: esrodk
       X11 Model: esrodk
     X11 Options: terminate:ctrl_alt_bksp

Next thing I'll try is to remove "en_US.UTF-8/UTF-8" from i18n.supportedLocales
Update: Removing "en_US.UTF-8/UTF-8" did not made any difference to the situation.

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Aug 27, 2019

@dasj19 So, I have tested KDE Plasma, XFCE and GNOME. It seems to be a problem only in GNOME. I couldn't figure out much besides wht you already found.
Could you open a issue and continue the discussion there?

@jtojnar
Copy link
Contributor

jtojnar commented Dec 28, 2019

The GNOME issue was fixed in #76591.

@dylanowen
Copy link

I'm pretty new to Nix and xkb but I've been trying to setup a keyboard layout using custom types. To actually use xkb types applied using this method it seems like we would need to update the types/complete file to reference the newly created types/${name} file? Is there a better way to do this? If not I can open a PR with the necessary changes once I get a chance to test them.

@rnhmjoj
Copy link
Contributor Author

rnhmjoj commented Sep 26, 2021

I don't know how types files work, is it always necessary to update types/complete after a new one is added?
I see there are a number of types files that are not references by types/complete.

Anyway, If you need to replace an existing file in xkb, you could try to define another layout like:

extraLayouts.complete.typesFile = ...;

This should override types/complete. Otherwise, you have to extend the script in pkgs/servers/x11/xorg/overrides.nix:483.

@dylanowen
Copy link

@rnhmjoj Thanks for the quick response. After looking at your comment I did some more research and I think your work on the rules file should cover what I was imagining. I'll give it a try when I'm back to a computer.

@attila-lendvai
Copy link
Contributor

the loading of user specific xkb files should be ready according to this: https://who-t.blogspot.com/2020/09/user-specific-xkb-configuration-putting.html

not sure when these changes get/got into NixOS.

@dylanowen
Copy link

@rnhmjoj I haven't been able to figure out how types are attached besides this post. But I was attempting to try your suggestion of overwriting the complete types file using this

services.xserver.extraLayouts.complete = {
   description = "Complete types override";
   languages = [ "eng" ];
   typesFile = ./keyboard/complete.xkb;
  };

and I ran into this error:

make[2]: Nothing to be done for 'install-exec-am'.
 /nix/store/1c1r48qa0m23vr9jy8sm0dc04vv14dak-coreutils-8.32/bin/mkdir -p '/nix/store/xlqwv7waszd0f8xk651jfrml4fw2301b-xkeyboard-config-2.31/share/X11/xkb/compat'
 /nix/store/1c1r48qa0m23vr9jy8sm0dc04vv14dak-coreutils-8.32/bin/install -c -m 644 complete accessx basic caps complete iso9995 japan ledcaps ledcompose lednum ledscroll level5 misc mousekeys olpc pc pc98 xfree86 xtest README '/nix/store/xlqwv7waszd0f8xk651jfrml4fw2301b-xkeyboard-config-2.31/share/X11/xkb/compat'
/nix/store/1c1r48qa0m23vr9jy8sm0dc04vv14dak-coreutils-8.32/bin/install: will not overwrite just-created '/nix/store/xlqwv7waszd0f8xk651jfrml4fw2301b-xkeyboard-config-2.31/share/X11/xkb/compat/complete' with 'complete'

Should we reverse the check in this line

if ! test -f "$type/${name}"; then
so that we're adding our types to our Makefiles if they don't already exist? My assumption of what was happening was that the makefile was ending up with duplicates of the complete line in the _DATA section. Also should we only be adding files to the _DATA section if they were configured? I believe right now we'd be putting files in the _DATA section that don't exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants