Cursor size in wayland/sway

I’m using wayland/sway and find that my cursor size changes when hovering over different windows. It is smaller on window borders than on most “regular” windows. It is tiny when hoving over a mpv window. Does somebody know the reason for this?

1 Like

Did you ever find a solution? It’s driving me crazy

No, unfortunately not

I managed to find the culprit of my particular issue, I’m not sure if yours is the same:
https://github.com/hyprwm/Hyprland/issues/1577#issuecomment-1436033647

Other than that I’ve seen a lot of people recommend to set XCUSOR_SIZE=size env variable for XWayland/Flatpak apps to maintain consistency:
https://www.reddit.com/r/kde/comments/sumh0u/comment/j95h75o/

Also, using Home Manager allowed me to set the cursor theme for GDK globally (I haven’t tested any QT apps with this though). This way I don’t have to define the cursor theme in gtk.cursorTheme or my .config:

home.pointerCursor = {
  gtk.enable = true;
  package = pkgs.bibata-cursors;
  name = "Bibata-Modern-Ice";
  size = 22;
};

Hope this helps!
If you find any other workaround to your issue, make sure to update this thread so others can learn :slight_smile:

2 Likes

I think we have different issues - if I understood you right, your mouse cursor slightly changed size when moving, even within the same window. What I see is that the cursor size can be way different depending on the window it’s hovering.

Regarding my issue:

As far as I understand, in wayland every application is responsible for drawing the cursor hovering over its windows, so I would have to teach each application how to use the correct size.

I found various places indicating that some applications read the XCURSOR_SIZE environment variable and scale the cursor accordingly, but some quick experiments didn’t confirm this for mpv, alacritty and gedit.

Thanks for posting this. My mouse cursor would disappear whenever I opened Wezterm, but this brought it back… although I went with Vanilla-DMZ. Strangely enough, this cursor only works inside the window and then immediately switches back to GNOME’s default cursor outside of it.

For non-home-manager users, I think this is everything done in home-manager/modules/config/home-cursor.nix at e3ad5108f54177e6520535768ddbf1e6af54b59d · nix-community/home-manager · GitHub (the following assumes you want Adwaita size 24):

n.b. adding settings to .config/gtk-3.0/settings.ini was the only solution that worked for me.

  • $HOME/.config/gtk-3.0/settings.ini containing
  [Settings]
  gtk-cursor-theme-name=Adwaita
  gtk-cursor-theme-size=24
  • Symbolic links in $HOME:
  .icons
  └── default
      ├── Adwaita.source -> /home/me/.nix-profile/share/icons/Adwaita
      └── index.theme -> /home/me/.nix-profile/share/icons/Adwaita/index.theme
  • Same symbolic links in $HOME/.local/share/icons/default
  • $HOME/.Xresources containing
    Xcursor.theme: Adwaita
    Xcursor.size: 24
  • ${pkgs.xorg.xsetroot}/bin/xsetroot -xcf $HOME/.nix-profile/share/icons/Adwaita/cursors/left_ptr 24
1 Like

I have revisited this as well and found

  • I already had exec configure-gtk Adwaita in my Sway config.
  • When hovering over Waybar and xterm I think I had the Adwaita theme and cursor size 24
  • I think configure-gtk doesn’t let me specify a cursor size
  • When hovering over Alacritty or mpv the cursor was tiny. It was also tiny when hovering over Sway’s gaps between windows.
  • I found Mouse Cursor Tiny · Issue #4610 · swaywm/sway · GitHub which suggested to add seat * xcursor_theme Adwaita 32 to Sway config, and this works for Alacritty and mpv windows and gaps between windows.
  • I still need to figure out how to increase the size from 24 to 32 on Waybar and Xterm, but it’s already way better than before
  • Setting XCURSOR_SIZE as suggested in Cursor themes - ArchWiki didn’t have an effect
  • Setting Xcursor.size: 32 in ~/.Xresources didn’t have any effect.
  • I don’t have the $HOME/.nix-profile/share/icons/Adwaita that is described in the previous post
  • Trying to modify the cursor size via $HOME/.config/gtk-3.0/settings.ini didn’t have an effect. But maybe that’s because I don’t have the theme in my home folder.
2 Likes

More cursor-related observations:

  • after installing Plasma6, I have KDE themed cursors, even in Sway
  • after uninstalling Plasma6 again, the cursor is extremely small when hovering over some windows like Waybar and Firefox, way smaller than before I installed Plasma6
    • Thus I think there is some implicit state (not tracked by NixOS) involved here
  • I installed Plasma6 again for now, just for the cursors
  • In my home folder GTK config I find
~: grep cursor .config/gtk-3.0/settings.ini
gtk-cursor-theme-name=breeze_cursors
gtk-cursor-theme-size=24

~: grep cursor .config/gtk-4.0/settings.ini
gtk-cursor-theme-name=breeze_cursors
gtk-cursor-theme-size=24

where “breeze” is the KDE cursor theme.

  • When uninstalling Plasma6, the ~/.config/gtk-{3,4}.0/settings.ini remain the same but the KDE cursor theme is gone and the cursor becomes tiny
  • in my sway config, I have exec configure-gtk Adwaita, but this doesn’t modify .config/gtk-{3,4}.0/settings.ini. I don’t find anything relevant with find .config -exec grep -inH adwaita {} \; 2>/dev/null neither, not sure what configure-gtk actually does.

Issues is that programs and apps may use different configuration files (e.g ~/.config/gtk-${GTK_VERSION}, ~/.gtkrc-2.0, env / session variables, etc) so you gotta make sure all are set accordingly and everywhere it needs to.
Some files may also be overridden if not set correctly and permanently (e.g through nixos config).

Here’s a module I wrote for it (requires home-manager and with hyprland instead of sway):

{ lib, config, pkgs, ... }:

let
  _config = config._config;
in
{
  options._config = {
    username = lib.mkOption {
      type = lib.types.nullOr(lib.types.str);
    };

    gtk = {
      enable = lib.mkOption {
        type = lib.types.bool;
        default = false;
      };

      withHyprland = lib.mkOption {
        type = lib.types.bool;
        default = false;
      };

      config = {
        font = lib.mkOption {
          type = lib.types.nullOr(lib.types.str);
        };
        theme = {
          dark = lib.mkOption {
            type = lib.types.bool;
            default = false;
          };
          name = lib.mkOption {
            type = lib.types.nullOr(lib.types.str);
          };
          package = lib.mkOption {
            type = lib.types.nullOr(lib.types.package);
          };
        };
        icons = {
          name = lib.mkOption {
            type = lib.types.nullOr(lib.types.str);
          };
          package = lib.mkOption {
            type = lib.types.nullOr(lib.types.package);
          };
        };
        cursor = {
          name = lib.mkOption {
            type = lib.types.nullOr(lib.types.str);
          };
          package = lib.mkOption {
            type = lib.types.nullOr(lib.types.package);
          };
          size = lib.mkOption {
            type = lib.types.nullOr(lib.types.int);
          };
        };

        extraConfig = lib.mkOption {
          type = lib.types.attrs;
          default = {
            # gtk-application-prefer-dark-theme = "";
            # gtk-font-name = "";
            # gtk-cursor-theme-size = 24;
            # gtk-toolbar-style = 3;
            # gtk-toolbar-icon-size = "GTK_ICON_SIZE_SMALL_TOOLBAR";
            # gtk-button-images = 0;
            # gtk-menu-images = 0;
            # gtk-enable-event-sounds = 1;
            # gtk-enable-input-feedback-sounds = 0;
            # gtk-xft-antialias = 1;
            # gtk-xft-hinting = 1;
            # gtk-xft-hintstyle = "hintslight";
            # gtk-xft-rgba = "rgb";
            # gtk-decoration-layout = "icon:minimize,maximize,close";
            # gtk-enable-animations = true;
            # gtk-modules = "colorreload-gtk-module";
            # gtk-primary-button-warps-slider = true;
            # gtk-sound-theme-name = "ocean";
            # gtk-xft-dpi = 98304;
          };
        };
        debug = lib.mkOption {
          type = lib.types.bool;
          default = false;
        };
      };
    };
  };

  config = lib.mkIf (
    _config.username != null
    && _config.gtk.enable != null
    && _config.gtk.config.font !=null
    && _config.gtk.config.theme !=null
    && _config.gtk.config.icons !=null
    && _config.gtk.config.cursor !=null
  ) {
    # ui to check / change GTK config
    environment.systemPackages = with pkgs; lib.mkIf _config.gtk.config.debug [
      nwg-look
    ];
  
    home-manager = {
      users.${_config.username} = {
        home = {
          pointerCursor = {
            name = _config.gtk.config.cursor.name;
            gtk.enable = true;
            package = _config.gtk.config.cursor.package;
          };

          sessionVariables = {
            GTK_THEME = _config.gtk.config.theme.name;
          };
        };

        gtk = {
          enable = true;

          theme = {
            name = _config.gtk.config.theme.name;
            package = _config.gtk.config.theme.package;
          };
          iconTheme = {
            name = _config.gtk.config.icons.name;
            package = _config.gtk.config.icons.package;
          };
          cursorTheme = {
            name = _config.gtk.config.cursor.name;
            package = _config.gtk.config.cursor.package;
          };

          gtk3 = {
            extraConfig = lib.mkMerge [
              {
                gtk-application-prefer-dark-theme = _config.gtk.config.theme.dark;
                gtk-font-name = _config.gtk.config.font;
                gtk-cursor-theme-size = _config.gtk.config.cursor.size;
              }
              _config.gtk.config.extraConfig
            ];
          };
        };


        # gtk 4
        dconf.settings = {
          "org/gnome/desktop/interface" = {
            gtk-theme = _config.gtk.config.theme.name;
            color-scheme = lib.mkIf _config.gtk.config.theme.dark "prefer-dark";
          };
        };

        # hyprland
        wayland.windowManager = lib.mkIf _config.gtk.withHyprland {
          hyprland = {
            settings = {
              # redefine `GTK_THEME` for `hyprland` session
              # as `hyprland` may not pick up `home.sessionVariables` variables
              env = [
                "GTK_THEME, ${_config.gtk.config.theme.name}"
                (lib.mkIf _config.gtk.config.debug "GTK_DEBUG, interactive")
              ];
            };
          };
        };
      };
    };
  };
}

To use:

{ lib, config, pkgs, ... }:

let
  USERNAME = "john-doe";

  #
  GTK_THEME_CONFIG = ./files/gtk-theme;
  GTK_THEME_NAME = "GtkThemeCustom";
  GTK_CURSOR_NAME = "Cursors-Win10OS";
  GTK_CURSOR_SIZE = 24;
  GTK_ICONS_NAME = "Archdroid-Grey";
  GTK_FONT = "Liberation Mono 9";
  GTK_THEME_PATH = /path/to/theme;

  GTK_THEME_DARK = true;
  GTK_WITH_HYPRLAND = false;
  GTK_DEBUG = false;

  # packaging themes
  ## local theme
  GTK_THEME_PACKAGE = pkgs.stdenv.mkDerivation {
    name = "gtk-theme";
    src = GTK_THEME_PATH;
    dontBuild = true;

    installPhase = ''
      mkdir -p $out/share/themes/
      cp -r . $out/share/themes/${GTK_THEME_NAME}
    '';
  };
  ## from source
  GTK_CURSOR_PACKAGE = pkgs.stdenv.mkDerivation {
    name = "gtk-cursor-theme";
    # https://github.com/yeyushengfan258/Win10OS-cursors
    src = builtins.fetchGit { 
      url = "https://github.com/yeyushengfan258/Win10OS-cursors.git"; 
    };

    buildPhase = ''
      sh ./build.sh
    '';
    installPhase = ''
      mkdir -p $out/share/icons/
      cp -r ./dist/. $out/share/icons/${GTK_CURSOR_NAME}
    '';
  };
  GTK_ICONS_PACKAGE = pkgs.stdenv.mkDerivation {
    name = "gtk-icons-theme";
    # https://github.com/GreenRaccoon23/archdroid-icon-theme
    src = builtins.fetchGit { 
      url = "https://github.com/GreenRaccoon23/archdroid-icon-theme.git"; 

    };
    dontBuild = true;

    installPhase = ''
      mkdir -p $out/share/icons/
      cp -r ./archdroid-icon-theme/Archdroid-Grey/. $out/share/icons/${GTK_ICONS_NAME}
    '';
  };
in
{
  imports = [
    ./gtk.nix
  ];

  config = {
    _config = {
      username = USERNAME;

      gtk = {
        enable = GTK_THEME_DARK;
        withHyprland = GTK_WITH_HYPRLAND;
        config = {
          font = GTK_FONT;
          theme = {
            dark = GTK_THEME_DARK;
            name = GTK_THEME_NAME;
            package = GTK_THEME_PACKAGE;
          };
          icons = {
            name = GTK_ICONS_NAME;
            package = GTK_ICONS_PACKAGE;
          };
          cursor = {
            name = GTK_CURSOR_NAME;
            size = GTK_CURSOR_SIZE;
            package = GTK_CURSOR_PACKAGE;
          };

          debug = GTK_DEBUG;
          # extraConfig = {
          #   # gtk-application-prefer-dark-theme = "";
          #   # gtk-font-name = "";
          #   # gtk-cursor-theme-size = 24;
          #   # gtk-toolbar-style = 3;
          #   # gtk-toolbar-icon-size = "GTK_ICON_SIZE_SMALL_TOOLBAR";
          #   # gtk-button-images = 0;
          #   # gtk-menu-images = 0;
          #   # gtk-enable-event-sounds = 1;
          #   # gtk-enable-input-feedback-sounds = 0;
          #   # gtk-xft-antialias = 1;
          #   # gtk-xft-hinting = 1;
          #   # gtk-xft-hintstyle = "hintslight";
          #   # gtk-xft-rgba = "rgb";
          #   # gtk-decoration-layout = "icon:minimize,maximize,close";
          #   # gtk-enable-animations = true;
          #   # gtk-modules = "colorreload-gtk-module";
          #   # gtk-primary-button-warps-slider = true;
          #   # gtk-sound-theme-name = "ocean";
          #   # gtk-xft-dpi = 98304;
          # };
        };
      };
    };
  };
}