├── .gitignore ├── regolith-look-default ├── result └── default.nix ├── sway-regolith ├── overlay.nix ├── series ├── fix-paths.patch ├── sway-config-no-nix-store-references.patch ├── 03-disable-wallpaper ├── sway-config-nixos-paths.patch ├── 05-remove-config ├── 02-version-fix.patch ├── load-configuration-from-etc.patch ├── default.nix ├── 01-regolith-trawl.patch └── 04-dbus-tray ├── xresources ├── regolith3 │ ├── wallpaper │ │ └── regolith.png │ ├── core.Xresources │ ├── Xresources │ ├── gestures.Xresources │ ├── power-management.Xresources │ ├── window-management.Xresources │ ├── applications.Xresources │ ├── workspaces.Xresources │ ├── keybindings.Xresources │ ├── ilia-launcher.Xresources │ └── colors-bar.Xresources └── package.nix ├── testing ├── nixos │ ├── hardware-configuration.nix │ └── configuration.nix ├── flake.lock ├── flake.nix └── home-manager │ └── home.nix ├── flake.lock ├── packages ├── regolith-i3status-config.nix ├── trawl.nix ├── regolith-systemd-units.nix ├── regolith-look-extra.nix ├── regolith-look-default.nix ├── i3-swap-focus.nix ├── i3xrocks.nix ├── rofication.nix ├── regolith-styles.nix ├── xrescat.nix ├── regolith-inputd.nix ├── regolith-session.nix ├── regolith-powerd.nix ├── regolith-displayd.nix ├── i3status-rs.nix ├── default.nix ├── libtrawldb.nix ├── regolith-ftue.nix ├── remontoire.nix ├── ilia.nix └── regolith-wm-config.nix ├── regolith-powerd └── default.nix ├── regolith-look-extra └── default.nix ├── tree.txt ├── README.md ├── modules └── regolith.nix ├── fhs.nix ├── test.log ├── .github └── workflows │ └── test.yml ├── flake.nix └── regolith.drawio /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | nixos.qcow2 -------------------------------------------------------------------------------- /regolith-look-default/result: -------------------------------------------------------------------------------- 1 | /nix/store/92cazpbmr5r05cpjvvzysf4vl3k1m37l-regolith-look-default-3.1 -------------------------------------------------------------------------------- /sway-regolith/overlay.nix: -------------------------------------------------------------------------------- 1 | self: super: { 2 | libtrawldb = import ./libtrawldb/default.nix { inherit (super) pkgs; }; 3 | } 4 | -------------------------------------------------------------------------------- /sway-regolith/series: -------------------------------------------------------------------------------- 1 | 01-regolith-trawl.patch 2 | 02-version-fix.patch 3 | 03-disable-wallpaper 4 | 04-dbus-tray 5 | 05-remove-config 6 | -------------------------------------------------------------------------------- /xresources/regolith3/wallpaper/regolith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regolith-lab/regolith-nix/HEAD/xresources/regolith3/wallpaper/regolith.png -------------------------------------------------------------------------------- /testing/nixos/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | # This is just an example, you should generate yours with nixos-generate-config and put it in here. 2 | { 3 | boot.loader.systemd-boot.enable = true; 4 | 5 | fileSystems."/" = { 6 | device = "/dev/sda1"; 7 | fsType = "ext4"; 8 | }; 9 | 10 | # Set your system kind (needed for flakes) 11 | nixpkgs.hostPlatform = "x86_64-linux"; 12 | } 13 | -------------------------------------------------------------------------------- /sway-regolith/fix-paths.patch: -------------------------------------------------------------------------------- 1 | --- a/sway/config.c 2 | +++ b/sway/config.c 3 | @@ -276,7 +276,7 @@ 4 | 5 | if (!(config->active_bar_modifiers = create_list())) goto cleanup; 6 | 7 | - if (!(config->swaybg_command = strdup("swaybg"))) goto cleanup; 8 | + if (!(config->swaybg_command = strdup("@swaybg@/bin/swaybg"))) goto cleanup; 9 | 10 | if (!(config->config_chain = create_list())) goto cleanup; 11 | config->current_config_path = NULL; -------------------------------------------------------------------------------- /xresources/package.nix: -------------------------------------------------------------------------------- 1 | { lib 2 | , stdenv 3 | , writeTextFile 4 | }: 5 | 6 | stdenv.mkDerivation { 7 | pname = "regolith-xresources"; 8 | version = "1.0.0"; 9 | src = ./.; 10 | 11 | installPhase = '' 12 | mkdir -p $out/regolith3 13 | cp -r $src/regolith3 $out 14 | ''; 15 | 16 | meta = with lib; { 17 | description = "Xresources configuration for Regolith Linux"; 18 | license = licenses.mit; 19 | platforms = platforms.linux; 20 | maintainers = [ ]; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /xresources/regolith3/core.Xresources: -------------------------------------------------------------------------------- 1 | ! ============================================================================= 2 | ! Core Regolith Configuration 3 | ! ============================================================================= 4 | 5 | wm.binding.terminal: Return 6 | wm.program.terminal: kitty fish || alacritty || xterm 7 | regolith.wallpaper.file: ~/.config/regolith3/wallpaper/regolith.png 8 | regolith.wallpaper.options: zoom 9 | 10 | wm.mod: Mod4 11 | wm.alt: Mod1 12 | 13 | gtk.font_name: Sans 13 14 | -------------------------------------------------------------------------------- /xresources/regolith3/Xresources: -------------------------------------------------------------------------------- 1 | ! ============================================================================= 2 | ! Main Regolith Xresources Configuration 3 | ! ============================================================================= 4 | 5 | #include "core.Xresources" 6 | #include "workspaces.Xresources" 7 | #include "colors-bar.Xresources" 8 | #include "keybindings.Xresources" 9 | #include "window-management.Xresources" 10 | #include "gestures.Xresources" 11 | #include "applications.Xresources" 12 | #include "power-management.Xresources" 13 | #include "ilia-launcher.Xresources" 14 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1749285348, 6 | "narHash": "sha256-frdhQvPbmDYaScPFiCnfdh3B/Vh81Uuoo0w5TkWmmjU=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "3e3afe5174c561dee0df6f2c2b2236990146329f", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /packages/regolith-i3status-config.nix: -------------------------------------------------------------------------------- 1 | { stdenv }: 2 | 3 | stdenv.mkDerivation { 4 | name = "regolith-i3status-config"; 5 | 6 | phases = [ "installPhase" ]; 7 | 8 | installPhase = '' 9 | mkdir -p $out/share/regolith/i3status-rust 10 | 11 | cat > $out/share/regolith/i3status-rust/config.toml < $out/lib/systemd/user/regolith-wayland.target < $out/lib/systemd/user/regolith-init-kanshi.service < Our main nixos configuration file < 30 | modules = [ ./nixos/configuration.nix ]; 31 | }; 32 | }; 33 | 34 | # Standalone home-manager configuration entrypoint 35 | # Available through 'home-manager --flake .#your-username@your-hostname' 36 | homeConfigurations = { 37 | # FIXME replace with your username@hostname 38 | "your-username@your-hostname" = home-manager.lib.homeManagerConfiguration { 39 | pkgs = nixpkgs.legacyPackages.x86_64-linux; # Home-manager requires 'pkgs' instance 40 | extraSpecialArgs = { inherit inputs outputs; }; 41 | # > Our main home-manager configuration file < 42 | modules = [ ./home-manager/home.nix ]; 43 | }; 44 | }; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /sway-regolith/02-version-fix.patch: -------------------------------------------------------------------------------- 1 | Index: sway-regolith/meson.build 2 | =================================================================== 3 | --- sway-regolith.orig/meson.build 4 | +++ sway-regolith/meson.build 5 | @@ -157,18 +156,18 @@ endif 6 | add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c') 7 | 8 | version = '"@0@"'.format(meson.project_version()) 9 | -git = find_program('git', native: true, required: false) 10 | -if git.found() 11 | - git_commit = run_command([git, '--git-dir=.git', 'rev-parse', '--short', 'HEAD'], check: false) 12 | - git_branch = run_command([git, '--git-dir=.git', 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) 13 | - if git_commit.returncode() == 0 and git_branch.returncode() == 0 14 | - version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( 15 | - meson.project_version(), 16 | - git_commit.stdout().strip(), 17 | - git_branch.stdout().strip(), 18 | - ) 19 | - endif 20 | -endif 21 | +# git = find_program('git', native: true, required: false) 22 | +# if git.found() 23 | +# git_commit = run_command([git, '--git-dir=.git', 'rev-parse', '--short', 'HEAD'], check: false) 24 | +# git_branch = run_command([git, '--git-dir=.git', 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) 25 | +# if git_commit.returncode() == 0 and git_branch.returncode() == 0 26 | +# version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( 27 | +# meson.project_version(), 28 | +# git_commit.stdout().strip(), 29 | +# git_branch.stdout().strip(), 30 | +# ) 31 | +# endif 32 | +# endif 33 | add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') 34 | 35 | # Compute the relative path used by compiler invocations. -------------------------------------------------------------------------------- /regolith-look-default/default.nix: -------------------------------------------------------------------------------- 1 | # {pkgs,...}: 2 | let 3 | nixpkgs = builtins.fetchTarball { 4 | url = "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz"; 5 | }; 6 | 7 | pkgs = import nixpkgs { config = { }; }; 8 | in 9 | 10 | pkgs.stdenv.mkDerivation { 11 | pname = "regolith-look-default"; 12 | version = "3.1"; 13 | 14 | src = pkgs.fetchFromGitHub { 15 | owner = "regolith-linux"; 16 | repo = "regolith-look-default"; 17 | rev = "r3_2"; 18 | hash = "sha256-219VC5Tg9erTWktFv0BiNIeWXzNemngELKpTWWWUKSw="; 19 | }; 20 | # src=./.; 21 | nativeBuildInputs = [ 22 | 23 | ]; 24 | 25 | buildInputs = with pkgs;[ 26 | 27 | ]; 28 | 29 | buildPhase = '' 30 | # chmod -R +x $src 31 | patchShebangsAuto $src 32 | ''; 33 | 34 | installPhase = '' 35 | # Install your scripts or binaries 36 | mkdir -p $out/usr/share/regolith-look/default/ 37 | cp -r $src/usr $out 38 | 39 | # mkdir -p $out/etc 40 | # cp -r $src/etc $out 41 | 42 | # mkdir -p $out/bin 43 | # cp -r $src/usr/bin $out 44 | 45 | substituteInPlace $out/usr/share/regolith-look/default_loader.sh \ 46 | --replace-quiet /usr /run/current-system/sw/usr \ 47 | --replace-quiet "#! /bin/bash" "#!/usr/bin/env bash" \ 48 | 49 | substituteInPlace $out/usr/share/regolith-look/default/* \ 50 | --replace-quiet /usr /run/current-system/sw/usr \ 51 | 52 | 53 | patchShebangsAuto $out/usr/share/regolith-look 54 | 55 | ''; 56 | 57 | # pathsToLink = [ /bin /usr /lib]; 58 | 59 | 60 | meta = { 61 | # mainProgram = ""; 62 | description = "Default Regolith Xresource definitions for the desktop. "; 63 | homepage = "https://github.com/regolith-linux/regolith-look-default"; 64 | license = pkgs.lib.licenses.gpl3Plus; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /testing/home-manager/home.nix: -------------------------------------------------------------------------------- 1 | # This is your home-manager configuration file 2 | # Use this to configure your home environment (it replaces ~/.config/nixpkgs/home.nix) 3 | { inputs 4 | , lib 5 | , config 6 | , pkgs 7 | , ... 8 | }: { 9 | # You can import other home-manager modules here 10 | imports = [ 11 | # If you want to use home-manager modules from other flakes (such as nix-colors): 12 | # inputs.nix-colors.homeManagerModule 13 | 14 | # You can also split up your configuration and import pieces of it here: 15 | # ./nvim.nix 16 | ]; 17 | 18 | nixpkgs = { 19 | # You can add overlays here 20 | overlays = [ 21 | # If you want to use overlays exported from other flakes: 22 | # neovim-nightly-overlay.overlays.default 23 | 24 | # Or define it inline, for example: 25 | # (final: prev: { 26 | # hi = final.hello.overrideAttrs (oldAttrs: { 27 | # patches = [ ./change-hello-to-hi.patch ]; 28 | # }); 29 | # }) 30 | ]; 31 | # Configure your nixpkgs instance 32 | config = { 33 | # Disable if you don't want unfree packages 34 | allowUnfree = true; 35 | # Workaround for https://github.com/nix-community/home-manager/issues/2942 36 | allowUnfreePredicate = _: true; 37 | }; 38 | }; 39 | 40 | # TODO: Set your username 41 | home = { 42 | username = "your-username"; 43 | homeDirectory = "/home/your-username"; 44 | }; 45 | 46 | # Add stuff for your user as you see fit: 47 | # programs.neovim.enable = true; 48 | # home.packages = with pkgs; [ steam ]; 49 | 50 | # Enable home-manager and git 51 | programs.home-manager.enable = true; 52 | programs.git.enable = true; 53 | 54 | # Nicely reload system units when changing configs 55 | systemd.user.startServices = "sd-switch"; 56 | 57 | # https://nixos.wiki/wiki/FAQ/When_do_I_update_stateVersion 58 | home.stateVersion = "23.05"; 59 | } 60 | -------------------------------------------------------------------------------- /packages/regolith-wm-config.nix: -------------------------------------------------------------------------------- 1 | { lib 2 | , stdenv 3 | , fetchFromGitHub 4 | , rsync 5 | , excludeFiles ? [ ] 6 | , extraSwayConfig ? "" 7 | , extraI3Config ? "" 8 | , 9 | }: 10 | stdenv.mkDerivation rec { 11 | pname = "regolith-wm-config"; 12 | version = "4.11.8"; 13 | src = fetchFromGitHub { 14 | owner = "sandptel"; 15 | repo = "regolith-wm-config"; 16 | rev = "nix"; 17 | hash = "sha256-rxF4uEOBWYnj9oQY6Aq/OPeT9IyPJGwyNvkskTapvnw="; 18 | }; 19 | nativeBuildInputs = [ rsync ]; 20 | buildPhase = '' 21 | patchShebangsAuto $src 22 | ''; 23 | installPhase = '' 24 | mkdir -p $out/share $out/etc $out/bin 25 | mkdir -p $out/share/regolith/sway/config.d 26 | mkdir -p $out/share/regolith/i3/config.d 27 | mkdir -p $out/share/regolith/sway/config.d 28 | # Write the extraSwayConfig to a file 29 | echo "$extraSwayConfig" > $out/share/regolith/sway/config.d/user-extra-config 30 | echo "$extraI3Config" > $out/share/regolith/i3/config.d/user-extra-config 31 | cd $src 32 | # Copy files maintaining the correct structure, excluding specified files 33 | if [ -d usr/share ]; then 34 | ${rsync}/bin/rsync -av --exclude=${lib.concatStringsSep " --exclude=" excludeFiles} usr/share/* $out/share/ 35 | fi 36 | if [ -d etc ]; then 37 | ${rsync}/bin/rsync -av --exclude=${lib.concatStringsSep " --exclude=" excludeFiles} etc/* $out/etc/ 38 | fi 39 | if [ -d scripts ]; then 40 | ${rsync}/bin/rsync -av --exclude=${lib.concatStringsSep " --exclude=" excludeFiles} scripts/* $out/bin/ 41 | fi 42 | 43 | ''; 44 | postInstall = '' 45 | ''; 46 | meta = { 47 | description = "Configuration files related to window manager in X11 and Wayland"; 48 | homepage = "https://github.com/regolith-linux/regolith-wm-config"; 49 | license = lib.licenses.gpl3; 50 | maintainers = with lib.maintainers; [ ]; 51 | mainProgram = "regolith-wm-config"; 52 | platforms = lib.platforms.all; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /sway-regolith/load-configuration-from-etc.patch: -------------------------------------------------------------------------------- 1 | From 92283df3acbffa5c1bb21f23cdd686113d905114 Mon Sep 17 00:00:00 2001 2 | From: Patrick Hilhorst 3 | Date: Wed, 31 Mar 2021 21:14:13 +0200 4 | Subject: [PATCH] Load configs from /etc but fallback to /nix/store 5 | 6 | This change will load all configuration files from /etc, to make it easy 7 | to override them, but fallback to /nix/store/.../etc/sway/config to make 8 | Sway work out-of-the-box with the default configuration on non NixOS 9 | systems. 10 | 11 | Original patch by Michael Weiss, updated for Sway 1.6 by Patrick Hilhorst 12 | 13 | Co-authored-by: Michael Weiss 14 | --- 15 | meson.build | 3 ++- 16 | sway/config.c | 3 ++- 17 | 2 files changed, 4 insertions(+), 2 deletions(-) 18 | 19 | diff --git a/meson.build b/meson.build 20 | index b7a29660..8ae8ceb3 100644 21 | --- a/meson.build 22 | +++ b/meson.build 23 | @@ -164,7 +164,8 @@ if scdoc.found() 24 | endforeach 25 | endif 26 | 27 | -add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c') 28 | +add_project_arguments('-DSYSCONFDIR="/@0@"'.format(sysconfdir), language : 'c') 29 | +add_project_arguments('-DNIX_SYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c') 30 | 31 | version = '"@0@"'.format(meson.project_version()) 32 | git = find_program('git', native: true, required: false) 33 | diff --git a/sway/config.c b/sway/config.c 34 | index 76b9ec08..fb5b51aa 100644 35 | --- a/sway/config.c 36 | +++ b/sway/config.c 37 | @@ -374,7 +374,8 @@ static char *get_config_path(void) { 38 | { .prefix = home, .config_folder = ".i3"}, 39 | { .prefix = config_home, .config_folder = "i3"}, 40 | { .prefix = SYSCONFDIR, .config_folder = "sway"}, 41 | - { .prefix = SYSCONFDIR, .config_folder = "i3"} 42 | + { .prefix = SYSCONFDIR, .config_folder = "i3"}, 43 | + { .prefix = NIX_SYSCONFDIR, .config_folder = "sway"}, 44 | }; 45 | 46 | size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]); 47 | -- 48 | 2.30.1 -------------------------------------------------------------------------------- /tree.txt: -------------------------------------------------------------------------------- 1 | result 2 | ├── bin 3 | │ ├── regolith-look-selector 4 | │ ├── regolith-set-swaybg 5 | │ └── regolith-sway-clamshell 6 | ├── etc 7 | │ └── regolith 8 | │ ├── i3 9 | │ │ └── config 10 | │ └── sway 11 | │ └── config 12 | └── share 13 | └── regolith 14 | ├── common 15 | │ └── config.d 16 | │ ├── 15_base_launchers 17 | │ ├── 30_navigation 18 | │ ├── 40_i3-swap-focus 19 | │ ├── 40_workspace-config 20 | │ ├── 50_resize-mode 21 | │ ├── 82_rofication-ilia 22 | │ ├── 84_ftue 23 | │ ├── 88_network-manager 24 | │ └── 90_user-programs 25 | ├── i3 26 | │ └── config.d 27 | │ ├── 10_dbus-activation 28 | │ ├── 20_ilia 29 | │ ├── 20_rofi 30 | │ ├── 35_gaps 31 | │ ├── 40_default-style 32 | │ ├── 40_next-workspace 33 | │ ├── 55_session_keybindings 34 | │ ├── 60_config_keybindings 35 | │ ├── 60_gnome_config_keybindings 36 | │ ├── 70_bar 37 | │ ├── 80_compositor 38 | │ ├── 82_rofication 39 | │ └── 86_unclutter 40 | └── sway 41 | └── config.d 42 | ├── 10_dbus-activation 43 | ├── 20_ilia 44 | ├── 25_xdg_desktop_portal 45 | ├── 30_keyboard_layout 46 | ├── 35_gaps 47 | ├── 40_childe 48 | ├── 40_default-style 49 | ├── 42_gtklock 50 | ├── 55_session_keybindings 51 | ├── 60_config_keybindings 52 | ├── 60_gnome_config_keybindings 53 | ├── 61_touchpad_guestures 54 | ├── 65_media_keybindings 55 | ├── 70_bar_i3status-rs 56 | ├── 80_grimshot 57 | ├── 80_swaybg 58 | ├── 81_gsd_integrations 59 | ├── 82_audio_idle_inhibit 60 | ├── 85_polkit 61 | ├── 85_polkit_xwayland 62 | ├── 86_unclutter 63 | └── 96_clamshell 64 | -------------------------------------------------------------------------------- /xresources/regolith3/colors-bar.Xresources: -------------------------------------------------------------------------------- 1 | ! ============================================================================= 2 | ! Color Scheme Configuration 3 | ! ============================================================================= 4 | 5 | ! Focused Window Colors 6 | wm.client.focused.color.border: "#002b36" 7 | wm.client.focused.color.background: "#586e75" 8 | wm.client.focused.color.text: "#fdf6e3" 9 | wm.client.focused.color.indicator: "#268bd2" 10 | wm.client.focused.color.child_border: "#268bd2" 11 | 12 | ! Focused Inactive Window Colors 13 | wm.client.focused_inactive.color.border: "#002b36" 14 | wm.client.focused_inactive.color.background: "#073642" 15 | wm.client.focused_inactive.color.text: "#839496" 16 | wm.client.focused_inactive.color.indicator: "#073642" 17 | wm.client.focused_inactive.color.child_border: "#073642" 18 | 19 | ! Unfocused Window Colors 20 | wm.client.unfocused.color.border: "#002b36" 21 | wm.client.unfocused.color.background: "#073642" 22 | wm.client.unfocused.color.text: "#839496" 23 | wm.client.unfocused.color.indicator: "#073642" 24 | wm.client.unfocused.color.child_border: "#073642" 25 | 26 | ! Urgent Window Colors 27 | wm.client.urgent.color.border: "#002b36" 28 | wm.client.urgent.color.background: "#dc322f" 29 | wm.client.urgent.color.text: "#fdf6e3" 30 | wm.client.urgent.color.indicator: "#002b36" 31 | wm.client.urgent.color.child_border: "#dc322f" 32 | 33 | 34 | ! ============================================================================= 35 | ! Status Bar Configuration 36 | ! ============================================================================= 37 | 38 | ! Bar General Settings 39 | wm.binding.bar_toggle: i 40 | wm.bar.position: bottom 41 | wm.bar.font: monospace 42 | wm.bar.separatorchar: " " 43 | wm.bar.stripworkspacenumbers: yes 44 | wm.bar.mode: dock 45 | wm.bar.workspace_min_width: 36 46 | wm.bar.status_config: /etc/regolith/i3status-rust/config.toml 47 | wm.bar.status_command: i3status-rs /etc/regolith/i3status-rust/config.toml 48 | 49 | ! Bar Colors 50 | wm.bar.background.color: "#002b36" 51 | wm.bar.statusline.color: "#93a1a1" 52 | wm.bar.separator.color: "#268bd2" 53 | 54 | ! Workspace Bar Colors 55 | wm.bar.workspace.focused.border.color: "#073642" 56 | wm.bar.workspace.focused.background.color: "#073642" 57 | wm.bar.workspace.focused.text.color: "#eee8d5" 58 | wm.bar.workspace.active.border.color: "#073642" 59 | wm.bar.workspace.active.background.color: "#073642" 60 | wm.bar.workspace.active.text.color: "#586e75" 61 | wm.bar.workspace.inactive.border.color: "#002b36" 62 | wm.bar.workspace.inactive.background.color: "#002b36" 63 | wm.bar.workspace.inactive.text.color: "#586e75" 64 | wm.bar.workspace.urgent.border.color: "#dc322f" 65 | wm.bar.workspace.urgent.background.color: "#dc322f" 66 | wm.bar.workspace.urgent.text.color: "#fdf6e3" 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Installing Regolith 2 | Installing Nix 3 | ``` 4 | sh <(curl -L https://nixos.org/nix/install) --no-daemon 5 | . ~/.nix-profile/etc/profile.d/nix.sh 6 | export NIX_CONFIG="experimental-features = nix-command flakes" 7 | ``` 8 | 9 | ## Cachix Cache 10 | With `cachix` and `nix` installed use `cachix use sandptel` to add my cachix server to be used for pulling regolith-nix binaries. 11 | 12 | Nix commands will use the cache: 13 | ``` 14 | $ nix-build 15 | copying path '/nix/store/n1gwpmvmcgsbnr0a8ncflhvc59db775h-myproject-1.0.0' from 'https://sandptel.cachix.org 16 | ... 17 | ``` 18 | Using this would save you compute power for unchanged derivations. 19 | > https://www.cachix.org/ | 20 | > https://docs.cachix.org/ 21 | 22 | ## Install Regolith using flake 23 | Add regolith.url to the inputs and import its NixosModule 24 | ``` 25 | { 26 | description = "A sample flake for regolith-nix"; 27 | 28 | inputs = { 29 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 30 | homeManager={ 31 | url = "github:nix-community/home-manager"; 32 | inputs.nixpkgs.follows = "nixpkgs"; 33 | }; 34 | regolith.url = "github:regolith-lab/regolith-nix"; 35 | }; 36 | 37 | outputs = { self,nixpkgs,home-manager, ... }@inputs: 38 | let 39 | system = "x86_64-linux"; 40 | pkgs = nixpkgs.legacyPackages.${system}; 41 | in 42 | { 43 | nixosConfigurations."" = nixpkgs.lib.nixosSystem { 44 | specialArgs = {inherit system inputs;}; 45 | modules = [ 46 | inputs.regolith.nixosModules.regolith 47 | ]; 48 | }; 49 | 50 | }; 51 | } 52 | 53 | ``` 54 | ## Enable Regolith in configuration 55 | In your config file add the following lines to enable regolith and extraConfig to add your own custom config lines to sway 56 | 57 | ``` 58 | regolith.enable=true; 59 | regolith.sway.extraConfig= '' 60 | # Bind Alt+Enter to open Alacritty 61 | bindsym Mod1+Return exec alacritty''; 62 | ``` 63 | 64 | This will enable add all the required packages along with a displaymanager session. 65 | 66 | Use `regolith-session-wayland` command to envoke the session. 67 | 68 | ## To create a development shell with the following packages.. 69 | 70 | ```nix develop github:sandptel/regolith-nix``` 71 | 72 | 73 | ## Features Todo 74 | - [x] Bootable Session on Nixos 75 | - [x] Open for Testing 76 | - [ ] System Daemons Integrations 77 | - [ ] Stable Release 78 | - [ ] flake files to regolith-official repository 79 | - [ ] Regolith-Applications Pushed to Nixpkgs 80 | - [ ] regolith-enable moved to upstream nixpkgs 81 | - [ ] nix-colors integration 82 | - [ ] Install Starter's Scripts Similar to JakooLit Nixos-Hyprland 83 | - [ ] virtual machine for testing (Hydenix by richen604) 84 | - [ ] Contribution Docs :) 85 | 86 | # Design 87 | ### More Information at my [GSoC'24 Report](https://github.com/sandptel/gsoc24 ) 88 | ![regolith(1)](https://github.com/user-attachments/assets/4f152932-f4e2-41e8-8d8c-cd2e2d0e1a9a) 89 | 90 | 91 | -------------------------------------------------------------------------------- /modules/regolith.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | with lib; 3 | let 4 | cfg = config.regolith; 5 | regolith-session = pkgs.callPackage ../packages/regolith-session.nix { }; 6 | regolith-xresources = pkgs.callPackage ../xresources/package.nix { }; 7 | regolith-session-wayland = pkgs.callPackage ../fhs.nix { runScript = "${regolith-session}/bin/regolith-session-wayland"; }; 8 | regolith-environment = pkgs.callPackage ../fhs.nix { }; 9 | # Create a proper session package with the required providedSessions 10 | regolith-session-package = pkgs.runCommand "regolith-session-wayland" 11 | { 12 | # Define the providedSessions that the error is asking for 13 | passthru.providedSessions = [ "regolith-session-wayland" ]; 14 | } '' 15 | mkdir -p $out/share/wayland-sessions 16 | cat > $out/share/wayland-sessions/regolith-session-wayland.desktop << EOF 17 | [Desktop Entry] 18 | Name=Regolith Wayland 19 | Comment=Regolith Desktop Environment on Wayland 20 | Exec=${regolith-session-wayland}/bin/regolith-environment 21 | Type=Application 22 | EOF 23 | ''; 24 | regolith-packages = import ../packages { inherit pkgs; }; 25 | 26 | in 27 | { 28 | options.regolith = { 29 | enable = mkEnableOption "Enable Regolith"; 30 | extraSwayConfig = mkOption { 31 | type = types.str; 32 | default = ""; 33 | description = "Extra configuration for Sway window manager."; 34 | }; 35 | extraI3Config = mkOption { 36 | type = types.str; 37 | default = ""; 38 | description = "Extra configuration for i3 window manager."; 39 | }; 40 | }; 41 | 42 | config = mkIf cfg.enable { 43 | environment.systemPackages = [ 44 | #for dubugging purposes 45 | regolith-environment 46 | 47 | regolith-session-wayland 48 | # regolith-packages.regolith-session-wayland 49 | regolith-packages.regolith-session 50 | regolith-packages.regolith-look-default 51 | regolith-packages.regolith-look-extra 52 | (pkgs.callPackage ../packages/regolith-wm-config.nix { 53 | extraSwayConfig = cfg.extraSwayConfig; 54 | extraI3Config = cfg.extraI3Config; 55 | }) 56 | regolith-session-wayland 57 | regolith-packages.regolith-i3status-config 58 | regolith-packages.regolith-systemd-units 59 | regolith-packages.regolith-i3status-config 60 | (pkgs.callPackage ../xresources/package.nix { }) 61 | regolith-packages.regolith-displayd 62 | regolith-packages.regolith-inputd 63 | regolith-packages.regolith-powerd 64 | regolith-packages.regolith-ftue 65 | regolith-packages.xrescat 66 | regolith-packages.ilia 67 | regolith-packages.rofication 68 | regolith-packages.remontoire 69 | regolith-packages.trawl 70 | regolith-packages.i3xrocks 71 | regolith-packages.libtrawldb 72 | regolith-packages.i3-swap-focus 73 | regolith-packages.regolith-systemd-units 74 | regolith-packages.regolith-i3status-config 75 | pkgs.xorg.xrdb 76 | regolith-xresources 77 | ]; 78 | 79 | # Use the sessionPackages option with our properly formatted session package 80 | services.displayManager.sessionPackages = [ 81 | regolith-session-package 82 | ]; 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /testing/nixos/configuration.nix: -------------------------------------------------------------------------------- 1 | # This is your system's configuration file. 2 | # Use this to configure your system environment (it replaces /etc/nixos/configuration.nix) 3 | { inputs 4 | , lib 5 | , config 6 | , pkgs 7 | , ... 8 | }: { 9 | # You can import other NixOS modules here 10 | imports = [ 11 | # If you want to use modules from other flakes (such as nixos-hardware): 12 | # inputs.hardware.nixosModules.common-cpu-amd 13 | # inputs.hardware.nixosModules.common-ssd 14 | 15 | # You can also split up your configuration and import pieces of it here: 16 | # ./users.nix 17 | 18 | # Import your generated (nixos-generate-config) hardware configuration 19 | ./hardware-configuration.nix 20 | ]; 21 | 22 | nixpkgs = { 23 | # You can add overlays here 24 | overlays = [ 25 | # If you want to use overlays exported from other flakes: 26 | # neovim-nightly-overlay.overlays.default 27 | 28 | # Or define it inline, for example: 29 | # (final: prev: { 30 | # hi = final.hello.overrideAttrs (oldAttrs: { 31 | # patches = [ ./change-hello-to-hi.patch ]; 32 | # }); 33 | # }) 34 | ]; 35 | # Configure your nixpkgs instance 36 | config = { 37 | # Disable if you don't want unfree packages 38 | allowUnfree = true; 39 | }; 40 | }; 41 | 42 | nix = 43 | let 44 | flakeInputs = lib.filterAttrs (_: lib.isType "flake") inputs; 45 | in 46 | { 47 | settings = { 48 | # Enable flakes and new 'nix' command 49 | experimental-features = "nix-command flakes"; 50 | # Opinionated: disable global registry 51 | flake-registry = ""; 52 | # Workaround for https://github.com/NixOS/nix/issues/9574 53 | nix-path = config.nix.nixPath; 54 | }; 55 | # Opinionated: disable channels 56 | channel.enable = false; 57 | 58 | # Opinionated: make flake registry and nix path match flake inputs 59 | registry = lib.mapAttrs (_: flake: { inherit flake; }) flakeInputs; 60 | nixPath = lib.mapAttrsToList (n: _: "${n}=flake:${n}") flakeInputs; 61 | }; 62 | 63 | # FIXME: Add the rest of your current configuration 64 | 65 | # TODO: Set your hostname 66 | networking.hostName = "your-hostname"; 67 | 68 | # TODO: Configure your system-wide user settings (groups, etc), add more users as needed. 69 | users.users = { 70 | # FIXME: Replace with your username 71 | your-username = { 72 | # TODO: You can set an initial password for your user. 73 | # If you do, you can skip setting a root password by passing '--no-root-passwd' to nixos-install. 74 | # Be sure to change it (using passwd) after rebooting! 75 | initialPassword = "correcthorsebatterystaple"; 76 | isNormalUser = true; 77 | openssh.authorizedKeys.keys = [ 78 | # TODO: Add your SSH public key(s) here, if you plan on using SSH to connect 79 | ]; 80 | # TODO: Be sure to add any other groups you need (such as networkmanager, audio, docker, etc) 81 | extraGroups = [ "wheel" ]; 82 | }; 83 | }; 84 | 85 | # This setups a SSH server. Very important if you're setting up a headless system. 86 | # Feel free to remove if you don't need it. 87 | services.openssh = { 88 | enable = true; 89 | settings = { 90 | # Opinionated: forbid root login through SSH. 91 | PermitRootLogin = "no"; 92 | # Opinionated: use keys only. 93 | # Remove if you want to SSH using passwords 94 | PasswordAuthentication = false; 95 | }; 96 | }; 97 | 98 | # https://nixos.wiki/wiki/FAQ/When_do_I_update_stateVersion 99 | system.stateVersion = "23.05"; 100 | } 101 | -------------------------------------------------------------------------------- /sway-regolith/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenv, 4 | callPackage, 5 | fetchFromGitHub, 6 | replaceVars, 7 | swaybg, 8 | meson, 9 | ninja, 10 | pkg-config, 11 | wayland-scanner, 12 | scdoc, 13 | libGL, 14 | wayland, 15 | libxkbcommon, 16 | pcre2, 17 | json_c, 18 | libevdev, 19 | pango, 20 | cairo, 21 | libinput, 22 | gdk-pixbuf, 23 | librsvg, 24 | wlroots_0_18, 25 | wayland-protocols, 26 | libdrm, 27 | nixosTests, 28 | # Used by the NixOS module: 29 | isNixOS ? false, 30 | enableXWayland ? true, 31 | xorg, 32 | systemdSupport ? lib.meta.availableOn stdenv.hostPlatform systemd, 33 | systemd, 34 | trayEnabled ? systemdSupport, 35 | }: 36 | let 37 | libtrawldb = callPackage ../packages/libtrawldb.nix { }; 38 | in 39 | stdenv.mkDerivation (finalAttrs: { 40 | pname = "sway-unwrapped"; 41 | version = "1.10.1"; 42 | 43 | inherit 44 | enableXWayland 45 | isNixOS 46 | systemdSupport 47 | trayEnabled 48 | ; 49 | src = fetchFromGitHub { 50 | owner = "swaywm"; 51 | repo = "sway"; 52 | rev = finalAttrs.version; 53 | hash = "sha256-uBtQk8uhW/i8lSbv6zwsRyiiImFBw1YCQHVWQ8jot5w="; 54 | }; 55 | 56 | patches = 57 | [ 58 | ./01-regolith-trawl.patch 59 | ./load-configuration-from-etc.patch 60 | ./02-version-fix.patch 61 | # ./03-disable-wallpaper 62 | ./04-dbus-tray 63 | ./05-remove-config 64 | (replaceVars ./fix-paths.patch { 65 | inherit swaybg; 66 | }) 67 | ] 68 | ++ lib.optionals (!finalAttrs.isNixOS) [ 69 | # References to /nix/store/... will get GC'ed which causes problems when 70 | # copying the default configuration: 71 | ./sway-config-no-nix-store-references.patch 72 | ] 73 | ++ lib.optionals finalAttrs.isNixOS [ 74 | # Use /run/current-system/sw/share and /etc instead of /nix/store 75 | # references: 76 | ./sway-config-nixos-paths.patch 77 | ]; 78 | 79 | strictDeps = true; 80 | depsBuildBuild = [ 81 | pkg-config 82 | ]; 83 | 84 | nativeBuildInputs = [ 85 | meson 86 | ninja 87 | pkg-config 88 | wayland-scanner 89 | scdoc 90 | ]; 91 | 92 | buildInputs = 93 | [ 94 | libtrawldb 95 | swaybg 96 | libGL 97 | wayland 98 | libxkbcommon 99 | pcre2 100 | json_c 101 | libevdev 102 | pango 103 | cairo 104 | libinput 105 | gdk-pixbuf 106 | librsvg 107 | wayland-protocols 108 | libdrm 109 | (wlroots_0_18.override { inherit (finalAttrs) enableXWayland; }) 110 | ] 111 | ++ lib.optionals finalAttrs.enableXWayland [ 112 | xorg.xcbutilwm 113 | ]; 114 | 115 | # dependencies = 116 | # [ 117 | # libtrawldb 118 | # swaybg 119 | # ]; 120 | 121 | mesonFlags = 122 | let 123 | inherit (lib.strings) mesonEnable mesonOption; 124 | 125 | # The "sd-bus-provider" meson option does not include a "none" option, 126 | # but it is silently ignored iff "-Dtray=disabled". We use "basu" 127 | # (which is not in nixpkgs) instead of "none" to alert us if this 128 | # changes: https://github.com/swaywm/sway/issues/6843#issuecomment-1047288761 129 | # assert trayEnabled -> systemdSupport && dbusSupport; 130 | 131 | sd-bus-provider = if systemdSupport then "libsystemd" else "basu"; 132 | in 133 | [ 134 | (mesonOption "sd-bus-provider" sd-bus-provider) 135 | (mesonEnable "tray" finalAttrs.trayEnabled) 136 | ]; 137 | 138 | passthru.tests.basic = nixosTests.sway; 139 | 140 | meta = { 141 | description = "I3-compatible tiling Wayland compositor"; 142 | longDescription = '' 143 | Sway is a tiling Wayland compositor and a drop-in replacement for the i3 144 | window manager for X11. It works with your existing i3 configuration and 145 | supports most of i3's features, plus a few extras. 146 | Sway allows you to arrange your application windows logically, rather 147 | than spatially. Windows are arranged into a grid by default which 148 | maximizes the efficiency of your screen and can be quickly manipulated 149 | using only the keyboard. 150 | ''; 151 | homepage = "https://swaywm.org"; 152 | changelog = "https://github.com/swaywm/sway/releases/tag/${finalAttrs.version}"; 153 | license = lib.licenses.mit; 154 | platforms = lib.platforms.linux; 155 | maintainers = with lib.maintainers; [ 156 | primeos 157 | synthetica 158 | ]; 159 | mainProgram = "sway"; 160 | }; 161 | }) 162 | -------------------------------------------------------------------------------- /fhs.nix: -------------------------------------------------------------------------------- 1 | { name ? "regolith-environment", pkgs, runScript ? "${pkgs.fish}/bin/fish" }: 2 | 3 | let 4 | regolith-packages = import ./packages { inherit pkgs; }; 5 | regolith-xresources = pkgs.callPackage ./xresources/package.nix { }; 6 | 7 | # Get all packages including their dependencies 8 | regolith-pkgs = [ 9 | regolith-packages.regolith-styles 10 | regolith-packages.ilia 11 | regolith-packages.regolith-powerd 12 | regolith-packages.regolith-displayd 13 | regolith-packages.regolith-inputd 14 | regolith-packages.regolith-ftue 15 | regolith-packages.xrescat 16 | regolith-packages.rofication 17 | regolith-packages.remontoire 18 | regolith-packages.trawl 19 | regolith-packages.i3xrocks 20 | regolith-packages.libtrawldb 21 | regolith-packages.regolith-look-extra 22 | regolith-packages.i3status-rs 23 | regolith-packages.sway-regolith 24 | regolith-packages.regolith-session 25 | regolith-packages.regolith-look-default 26 | regolith-packages.regolith-wm-config 27 | regolith-packages.i3-swap-focus 28 | regolith-packages.regolith-systemd-units 29 | regolith-packages.regolith-i3status-config 30 | ]; 31 | 32 | # Collect all build inputs recursively 33 | all-inputs = builtins.concatMap 34 | (p: if builtins.hasAttr "buildInputs" p then p.buildInputs ++ [ p ] else [ p ]) 35 | regolith-pkgs; 36 | in 37 | pkgs.buildFHSEnv { 38 | inherit name; 39 | 40 | targetPkgs = pkgs: with pkgs; [ 41 | # Basic system utilities 42 | mate.mate-polkit 43 | bash 44 | coreutils 45 | gtk3 46 | glib 47 | kitty 48 | alacritty 49 | pavucontrol 50 | # icon-themes 51 | papirus-icon-theme 52 | 53 | gnome-session 54 | gnome-settings-daemon 55 | gnome-shell 56 | gnome-control-center 57 | # gnome-keyring 58 | gnome-terminal 59 | gnome-control-center 60 | 61 | # Add dbus and window manager related packages 62 | dbus 63 | xorg.xmodmap 64 | xorg.xrdb 65 | wlr-randr 66 | 67 | # Add missing dependencies 68 | networkmanager 69 | networkmanagerapplet 70 | systemd 71 | # i3 72 | kanshi 73 | wl-clipboard 74 | sway-audio-idle-inhibit 75 | avizo 76 | 77 | # Add polkit and related packages 78 | polkit 79 | polkit_gnome 80 | mate.mate-polkit 81 | 82 | # Python dependencies 83 | (python3.withPackages (ps: with ps; [ 84 | i3ipc 85 | ])) 86 | 87 | # Sway specific tools 88 | gtklock 89 | playerctl 90 | sway-contrib.grimshot 91 | 92 | # System utilities 93 | xdg-desktop-portal 94 | xdg-desktop-portal-wlr 95 | 96 | # Media controls 97 | nautilus 98 | ]; 99 | 100 | multiPkgs = pkgs: all-inputs; 101 | 102 | extraOutputsToInstall = [ "usr" "etc" "lib" "share" ]; 103 | 104 | # Add Xresources to the environment 105 | profile = '' 106 | export REGOLITH_PATH=/usr/share/regolith 107 | export XDG_DATA_DIRS=$XDG_DATA_DIRS:/usr/share/regolith 108 | export XDG_CONFIG_HOME=$HOME/.config 109 | export GNOME_SHELL_SESSION_MODE=regolith 110 | alias mate-polkit="${pkgs.mate.mate-polkit}/bin/mate-polkit" 111 | 112 | # Create systemd user directory if it doesn't exist 113 | mkdir -p $HOME/.config/systemd/user 114 | 115 | # Copy systemd user service files 116 | if [ -d "/usr/lib/systemd/user" ]; then 117 | cp -rf /usr/lib/systemd/user/* $HOME/.config/systemd/user/ > /dev/null 2>&1 118 | fi 119 | 120 | # Reload systemd user services 121 | ${pkgs.systemd}/bin/systemctl --user daemon-reload 122 | echo "Reloaded systemd user services" 123 | ${pkgs.systemd}/bin/systemctl --user enable regolith-wayland.target 124 | echo "Enabled regolith-wayland.target" 125 | ${pkgs.systemd}/bin/systemctl --user enable regolith-init-kanshi.service 126 | echo "Enabled regolith-init-kanshi.service" 127 | ${pkgs.systemd}/bin/systemctl --user enable regolith-init-displayd.service 128 | echo "Enabled regolith-init-displayd.service" 129 | ${pkgs.systemd}/bin/systemctl --user enable regolith-init-powerd.service 130 | echo "Enabled regolith-init-powerd.service" 131 | ${pkgs.systemd}/bin/systemctl --user enable regolith-init-inputd.service 132 | echo "Enabled regolith-init-inputd.service" 133 | 134 | # Ensure XDG_RUNTIME_DIR exists 135 | if [ -z "$XDG_RUNTIME_DIR" ]; then 136 | export XDG_RUNTIME_DIR=/run/user/$(id -u) 137 | mkdir -p $XDG_RUNTIME_DIR 138 | chmod 700 $XDG_RUNTIME_DIR 139 | fi 140 | 141 | # Load Xresources 142 | if [ -f $HOME/.config/regolith3/Xresources ]; then 143 | echo -e "\033[32mXresources file exists.\033[0m" 144 | else 145 | echo "Xresources file does not exist." 146 | # Load Xresources 147 | mkdir -p $HOME/.config/regolith3 148 | cp -r ${regolith-xresources}/regolith3 $HOME/.config/regolith3 149 | echo "Copied Xresources to home directory." 150 | fi 151 | 152 | xrdb -merge $HOME/.config/regolith3/Xresources 153 | 154 | echo "Regolith is ready :)" 155 | ''; 156 | 157 | # runScript = "${pkgs.fish}/bin/fish"; 158 | inherit runScript; 159 | } 160 | 161 | # Enabled regolith-init-kanshi.service 162 | # Enabled regolith-init-displayd.service 163 | # Failed to enable unit: Unit regolith-init-powerd.service does not exist 164 | # Enabled regolith-init-powerd.service 165 | # Failed to enable unit: Unit regolith-init-inputd.service does not exist 166 | # Enabled regolith-init-inputd.service 167 | -------------------------------------------------------------------------------- /test.log: -------------------------------------------------------------------------------- 1 | Regolith is launching Sway with /etc/regolith/sway/config 2 | { 3 | "success": true 4 | } 5 | IDLE INHIBITED 6 | [ 7 | { 8 | "success": true 9 | } 10 | ] 11 | IDLE INHIBITED 12 | IDLE INHIBITED 13 | IDLE INHIBITED 14 | IDLE INHIBITED 15 | IDLE INHIBITED 16 | IDLE INHIBITED 17 | IDLE INHIBITED 18 | IDLE INHIBITED 19 | IDLE INHIBITED 20 | IDLE INHIBITED 21 | IDLE INHIBITED 22 | IDLE INHIBITED 23 | IDLE INHIBITED 24 | IDLE INHIBITED 25 | IDLE INHIBITED 26 | IDLE INHIBITED 27 | IDLE INHIBITED 28 | IDLE INHIBITED 29 | IDLE INHIBITED 30 | IDLE INHIBITED 31 | IDLE INHIBITED 32 | IDLE INHIBITED 33 | IDLE INHIBITED 34 | IDLE INHIBITED 35 | IDLE INHIBITED 36 | IDLE INHIBITED 37 | IDLE INHIBITED 38 | IDLE INHIBITED 39 | IDLE INHIBITED 40 | IDLE INHIBITED 41 | IDLE INHIBITED 42 | IDLE INHIBITED 43 | IDLE INHIBITED 44 | IDLE INHIBITED 45 | IDLE INHIBITED 46 | IDLE INHIBITED 47 | IDLE INHIBITED 48 | IDLE INHIBITED 49 | IDLE INHIBITED 50 | IDLE INHIBITED 51 | IDLE INHIBITED 52 | IDLE INHIBITED 53 | IDLE INHIBITED 54 | IDLE INHIBITED 55 | IDLE INHIBITED 56 | IDLE INHIBITED 57 | IDLE INHIBITED 58 | IDLE INHIBITED 59 | IDLE INHIBITED 60 | IDLE INHIBITED 61 | IDLE INHIBITED 62 | IDLE INHIBITED 63 | IDLE INHIBITED 64 | IDLE INHIBITED 65 | IDLE INHIBITED 66 | IDLE INHIBITED 67 | IDLE INHIBITED 68 | IDLE INHIBITED 69 | IDLE INHIBITED 70 | IDLE INHIBITED 71 | IDLE INHIBITED 72 | IDLE INHIBITED 73 | IDLE INHIBITED 74 | IDLE INHIBITED 75 | IDLE INHIBITED 76 | IDLE INHIBITED 77 | IDLE INHIBITED 78 | IDLE INHIBITED 79 | IDLE INHIBITED 80 | IDLE INHIBITED 81 | IDLE INHIBITED 82 | IDLE INHIBITED 83 | IDLE INHIBITED 84 | IDLE INHIBITED 85 | IDLE INHIBITED 86 | IDLE INHIBITED 87 | IDLE INHIBITED 88 | IDLE INHIBITED 89 | IDLE INHIBITED 90 | IDLE INHIBITED 91 | IDLE INHIBITED 92 | IDLE INHIBITED 93 | IDLE INHIBITED 94 | IDLE INHIBITED 95 | IDLE INHIBITED 96 | IDLE INHIBITED 97 | IDLE INHIBITED 98 | IDLE INHIBITED 99 | IDLE INHIBITED 100 | IDLE INHIBITED 101 | IDLE INHIBITED 102 | IDLE INHIBITED 103 | IDLE INHIBITED 104 | IDLE INHIBITED 105 | IDLE INHIBITED 106 | IDLE INHIBITED 107 | IDLE INHIBITED 108 | IDLE INHIBITED 109 | IDLE INHIBITED 110 | IDLE INHIBITED 111 | IDLE INHIBITED 112 | IDLE INHIBITED 113 | IDLE INHIBITED 114 | IDLE INHIBITED 115 | IDLE INHIBITED 116 | IDLE INHIBITED 117 | IDLE INHIBITED 118 | IDLE INHIBITED 119 | IDLE INHIBITED 120 | IDLE INHIBITED 121 | IDLE INHIBITED 122 | IDLE INHIBITED 123 | IDLE INHIBITED 124 | IDLE INHIBITED 125 | IDLE INHIBITED 126 | IDLE INHIBITED 127 | IDLE INHIBITED 128 | IDLE INHIBITED 129 | IDLE INHIBITED 130 | IDLE INHIBITED 131 | IDLE INHIBITED 132 | IDLE INHIBITED 133 | IDLE INHIBITED 134 | IDLE INHIBITED 135 | IDLE INHIBITED 136 | IDLE INHIBITED 137 | IDLE INHIBITED 138 | IDLE INHIBITED 139 | IDLE INHIBITED 140 | IDLE INHIBITED 141 | IDLE INHIBITED 142 | IDLE INHIBITED 143 | IDLE INHIBITED 144 | IDLE INHIBITED 145 | IDLE INHIBITED 146 | IDLE INHIBITED 147 | IDLE INHIBITED 148 | IDLE INHIBITED 149 | IDLE INHIBITED 150 | IDLE INHIBITED 151 | IDLE INHIBITED 152 | IDLE INHIBITED 153 | IDLE INHIBITED 154 | IDLE INHIBITED 155 | IDLE INHIBITED 156 | IDLE INHIBITED 157 | IDLE INHIBITED 158 | IDLE INHIBITED 159 | IDLE INHIBITED 160 | IDLE INHIBITED 161 | IDLE INHIBITED 162 | IDLE INHIBITED 163 | IDLE INHIBITED 164 | IDLE INHIBITED 165 | IDLE INHIBITED 166 | IDLE INHIBITED 167 | IDLE INHIBITED 168 | IDLE INHIBITED 169 | IDLE INHIBITED 170 | IDLE INHIBITED 171 | IDLE INHIBITED 172 | IDLE INHIBITED 173 | IDLE INHIBITED 174 | IDLE INHIBITED 175 | IDLE INHIBITED 176 | IDLE INHIBITED 177 | IDLE INHIBITED 178 | IDLE INHIBITED 179 | IDLE INHIBITED 180 | IDLE INHIBITED 181 | IDLE INHIBITED 182 | IDLE INHIBITED 183 | IDLE INHIBITED 184 | IDLE INHIBITED 185 | IDLE INHIBITED 186 | IDLE INHIBITED 187 | IDLE INHIBITED 188 | IDLE INHIBITED 189 | IDLE INHIBITED 190 | IDLE INHIBITED 191 | IDLE INHIBITED 192 | IDLE INHIBITED 193 | IDLE INHIBITED 194 | IDLE INHIBITED 195 | IDLE INHIBITED 196 | IDLE INHIBITED 197 | IDLE INHIBITED 198 | IDLE INHIBITED 199 | IDLE INHIBITED 200 | IDLE INHIBITED 201 | IDLE INHIBITED 202 | IDLE INHIBITED 203 | IDLE INHIBITED 204 | IDLE INHIBITED 205 | IDLE INHIBITED 206 | IDLE INHIBITED 207 | IDLE INHIBITED 208 | IDLE INHIBITED 209 | IDLE INHIBITED 210 | IDLE INHIBITED 211 | IDLE INHIBITED 212 | IDLE INHIBITED 213 | IDLE INHIBITED 214 | IDLE INHIBITED 215 | IDLE INHIBITED 216 | IDLE INHIBITED 217 | IDLE INHIBITED 218 | IDLE INHIBITED 219 | IDLE INHIBITED 220 | IDLE INHIBITED 221 | IDLE INHIBITED 222 | IDLE INHIBITED 223 | IDLE INHIBITED 224 | IDLE INHIBITED 225 | IDLE INHIBITED 226 | IDLE INHIBITED 227 | IDLE INHIBITED 228 | IDLE INHIBITED 229 | IDLE INHIBITED 230 | IDLE INHIBITED 231 | IDLE INHIBITED 232 | IDLE INHIBITED 233 | IDLE INHIBITED 234 | IDLE INHIBITED 235 | IDLE INHIBITED 236 | IDLE INHIBITED 237 | IDLE INHIBITED 238 | IDLE INHIBITED 239 | IDLE INHIBITED 240 | IDLE INHIBITED 241 | IDLE INHIBITED 242 | IDLE INHIBITED 243 | IDLE INHIBITED 244 | IDLE INHIBITED 245 | IDLE INHIBITED 246 | IDLE INHIBITED 247 | IDLE INHIBITED 248 | IDLE INHIBITED 249 | IDLE INHIBITED 250 | IDLE INHIBITED 251 | IDLE INHIBITED 252 | IDLE INHIBITED 253 | IDLE INHIBITED 254 | IDLE INHIBITED 255 | IDLE INHIBITED 256 | IDLE INHIBITED 257 | IDLE INHIBITED 258 | IDLE INHIBITED 259 | IDLE INHIBITED 260 | IDLE INHIBITED 261 | IDLE INHIBITED 262 | IDLE INHIBITED 263 | IDLE INHIBITED 264 | IDLE INHIBITED 265 | IDLE INHIBITED 266 | IDLE INHIBITED 267 | IDLE INHIBITED 268 | IDLE INHIBITED 269 | IDLE INHIBITED 270 | IDLE INHIBITED 271 | IDLE INHIBITED 272 | IDLE INHIBITED 273 | IDLE INHIBITED 274 | IDLE INHIBITED 275 | IDLE INHIBITED 276 | IDLE INHIBITED 277 | IDLE INHIBITED 278 | IDLE INHIBITED 279 | IDLE INHIBITED 280 | IDLE INHIBITED 281 | IDLE INHIBITED 282 | IDLE INHIBITED 283 | IDLE INHIBITED 284 | IDLE INHIBITED 285 | IDLE INHIBITED 286 | IDLE INHIBITED 287 | IDLE INHIBITED 288 | IDLE INHIBITED 289 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Test" 2 | on: 3 | pull_request: 4 | push: 5 | jobs: 6 | nix-flake-check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: cachix/install-nix-action@v22 11 | with: 12 | nix_path: nixpkgs=channel:nixos-unstable 13 | - uses: cachix/cachix-action@v14 14 | with: 15 | name: sandptel 16 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 17 | 18 | - run: nix flake check 19 | - run: echo "The following changes do not evaluate as proper nix expressions - https://nix.dev/manual/nix/2.17/command-ref/new-cli/nix3-flake-check" 20 | 21 | ilia: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v3 25 | - uses: cachix/install-nix-action@v22 26 | with: 27 | nix_path: nixpkgs=channel:nixos-unstable 28 | - uses: cachix/cachix-action@v14 29 | with: 30 | name: sandptel 31 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 32 | - run: nix build .#ilia 33 | 34 | rofication: 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v3 38 | - uses: cachix/install-nix-action@v22 39 | with: 40 | nix_path: nixpkgs=channel:nixos-unstable 41 | - uses: cachix/cachix-action@v14 42 | with: 43 | name: sandptel 44 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 45 | - run: nix build .#rofication 46 | 47 | regolith-powerd-inputd-displayd: 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@v3 51 | - uses: cachix/install-nix-action@v22 52 | with: 53 | nix_path: nixpkgs=channel:nixos-unstable 54 | - uses: cachix/cachix-action@v14 55 | with: 56 | name: sandptel 57 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 58 | - run: nix build .#regolith-powerd 59 | - run: nix build .#regolith-inputd 60 | - run: nix build .#regolith-displayd 61 | - run: nix build .#libtrawldb 62 | 63 | sway-regolith: 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v3 67 | - uses: cachix/install-nix-action@v22 68 | with: 69 | nix_path: nixpkgs=channel:nixos-unstable 70 | - uses: cachix/cachix-action@v14 71 | with: 72 | name: sandptel 73 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 74 | - run: nix build .#sway-regolith 75 | 76 | trawl: 77 | runs-on: ubuntu-latest 78 | steps: 79 | - uses: actions/checkout@v3 80 | - uses: cachix/install-nix-action@v22 81 | with: 82 | nix_path: nixpkgs=channel:nixos-unstable 83 | - uses: cachix/cachix-action@v14 84 | with: 85 | name: sandptel 86 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 87 | - run: nix build .#trawl 88 | 89 | xrescat: 90 | runs-on: ubuntu-latest 91 | steps: 92 | - uses: actions/checkout@v3 93 | - uses: cachix/install-nix-action@v22 94 | with: 95 | nix_path: nixpkgs=channel:nixos-unstable 96 | - uses: cachix/cachix-action@v14 97 | with: 98 | name: sandptel 99 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 100 | - run: nix build .#xrescat 101 | 102 | fhs: 103 | runs-on: ubuntu-latest 104 | steps: 105 | - uses: actions/checkout@v3 106 | - uses: cachix/install-nix-action@v22 107 | with: 108 | nix_path: nixpkgs=channel:nixos-unstable 109 | - uses: cachix/cachix-action@v14 110 | with: 111 | name: sandptel 112 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 113 | - run: nix build .#fhs 114 | 115 | virtual-machine: 116 | runs-on: ubuntu-latest 117 | steps: 118 | - uses: actions/checkout@v3 119 | - uses: cachix/install-nix-action@v22 120 | with: 121 | nix_path: nixpkgs=channel:nixos-unstable 122 | - uses: cachix/cachix-action@v14 123 | with: 124 | name: sandptel 125 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 126 | - run: nix build .#nixosConfigurations.vm.config.system.build.vm 127 | 128 | regolith-session: 129 | runs-on: ubuntu-latest 130 | steps: 131 | - uses: actions/checkout@v3 132 | - uses: cachix/install-nix-action@v22 133 | with: 134 | nix_path: nixpkgs=channel:nixos-unstable 135 | - uses: cachix/cachix-action@v14 136 | with: 137 | name: sandptel 138 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 139 | - run: nix build .#regolith-session 140 | - run: nix build .#regolith-session-wayland 141 | - run: nix build .#regolith-session-x11 142 | 143 | status-bar: 144 | runs-on: ubuntu-latest 145 | steps: 146 | - uses: actions/checkout@v3 147 | - uses: cachix/install-nix-action@v22 148 | with: 149 | nix_path: nixpkgs=channel:nixos-unstable 150 | - uses: cachix/cachix-action@v14 151 | with: 152 | name: sandptel 153 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 154 | - run: nix build .#i3-swap-focus 155 | - run: nix build .#i3status-rs 156 | - run: nix build .#i3xrocks 157 | 158 | regolith-config-styles-essentials: 159 | runs-on: ubuntu-latest 160 | steps: 161 | - uses: actions/checkout@v3 162 | - uses: cachix/install-nix-action@v22 163 | with: 164 | nix_path: nixpkgs=channel:nixos-unstable 165 | - uses: cachix/cachix-action@v14 166 | with: 167 | name: sandptel 168 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 169 | - run: nix build .#regolith-wm-config 170 | - run: nix build .#regolith-xresources 171 | - run: nix build .#regolith-ftue 172 | - run: nix build .#remontoire 173 | - run: nix build .#regolith-styles 174 | - run: nix build .#regolith-look-default 175 | - run: nix build .#regolith-look-extra 176 | 177 | -------------------------------------------------------------------------------- /sway-regolith/01-regolith-trawl.patch: -------------------------------------------------------------------------------- 1 | Index: sway-regolith/meson.build 2 | =================================================================== 3 | --- sway-regolith.orig/meson.build 4 | +++ sway-regolith/meson.build 5 | @@ -1,5 +1,5 @@ 6 | project( 7 | - 'sway', 8 | + 'sway-regolith', 9 | 'c', 10 | version: '1.10.1', 11 | license: 'MIT', 12 | @@ -80,6 +80,7 @@ math = cc.find_library('m') 13 | rt = cc.find_library('rt') 14 | xcb_icccm = wlroots_features['xwayland'] ? dependency('xcb-icccm') : null_dep 15 | threads = dependency('threads') # for pthread_setschedparam 16 | +trawldb = dependency('trawldb-0') 17 | 18 | if get_option('sd-bus-provider') == 'auto' 19 | if not get_option('tray').disabled() 20 | Index: sway-regolith/sway/commands/set_from_resource.c 21 | =================================================================== 22 | --- /dev/null 23 | +++ sway-regolith/sway/commands/set_from_resource.c 24 | @@ -0,0 +1,47 @@ 25 | +#define _POSIX_C_SOURCE 200809L 26 | +#include "log.h" 27 | +#include "stringop.h" 28 | +#include "sway/commands.h" 29 | +#include "sway/config.h" 30 | +#include 31 | +#include 32 | +#include 33 | +#include 34 | + 35 | +struct cmd_results *cmd_set_from_resource(int argc, char **argv) { 36 | + struct cmd_results *error = NULL; 37 | + if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) { 38 | + return error; 39 | + } 40 | + if (argv[0][0] != '$') { 41 | + return cmd_results_new(CMD_INVALID, "variable '%s' must start with $", 42 | + argv[0]); 43 | + } 44 | + 45 | + conf_client proxy = NULL; 46 | + GError *err = NULL; 47 | + conf_client_init(&proxy, &err); 48 | + 49 | + char *resource_value = NULL; 50 | + conf_client_get(proxy, argv[1], &resource_value, &err); 51 | + 52 | + if (err || resource_value == NULL || strlen(resource_value) == 0) { 53 | + if (argc < 3) { 54 | + return cmd_results_new(CMD_FAILURE, 55 | + "failed to fetch value of resource '%s' and " 56 | + "fallback value not provided", 57 | + argv[1]); 58 | + } 59 | + resource_value = join_args(argv + 2, argc - 2); 60 | + } 61 | + char **argv_new = calloc(argc - 1, sizeof(char *)); 62 | + argv_new[0] = strdup(argv[0]); 63 | + for (int i = 2; i < argc; i++) { 64 | + argv_new[i - 1] = argv[i]; 65 | + } 66 | + 67 | + argv_new[1] = strdup(resource_value); 68 | + 69 | + free(resource_value); 70 | + return cmd_set(2, argv_new); 71 | +} 72 | Index: sway-regolith/include/sway/commands.h 73 | =================================================================== 74 | --- sway-regolith.orig/include/sway/commands.h 75 | +++ sway-regolith/include/sway/commands.h 76 | @@ -173,6 +173,7 @@ sway_cmd cmd_resize; 77 | sway_cmd cmd_scratchpad; 78 | sway_cmd cmd_seamless_mouse; 79 | sway_cmd cmd_set; 80 | +sway_cmd cmd_set_from_resource; 81 | sway_cmd cmd_shortcuts_inhibitor; 82 | sway_cmd cmd_show_marks; 83 | sway_cmd cmd_smart_borders; 84 | Index: sway-regolith/sway/commands.c 85 | =================================================================== 86 | --- sway-regolith.orig/sway/commands.c 87 | +++ sway-regolith/sway/commands.c 88 | @@ -83,6 +83,7 @@ static const struct cmd_handler handlers 89 | { "popup_during_fullscreen", cmd_popup_during_fullscreen }, 90 | { "seat", cmd_seat }, 91 | { "set", cmd_set }, 92 | + { "set_from_resource", cmd_set_from_resource }, 93 | { "show_marks", cmd_show_marks }, 94 | { "smart_borders", cmd_smart_borders }, 95 | { "smart_gaps", cmd_smart_gaps }, 96 | @@ -279,8 +280,8 @@ list_t *execute_command(char *_exec, str 97 | goto cleanup; 98 | } 99 | 100 | - // Var replacement, for all but first argument of set 101 | - for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 102 | + // Var replacement, for all but first argument of set and xresource 103 | + for (int i = handler->handle == cmd_set || handler->handle == cmd_set_from_resource ? 2 : 1; i < argc; ++i) { 104 | argv[i] = do_var_replacement(argv[i]); 105 | } 106 | 107 | @@ -392,7 +393,7 @@ struct cmd_results *config_command(char 108 | } 109 | 110 | // Do variable replacement 111 | - if (handler->handle == cmd_set && argc > 1 && *argv[1] == '$') { 112 | + if ((handler->handle == cmd_set || handler->handle == cmd_set_from_resource )&& argc > 1 && *argv[1] == '$') { 113 | // Escape the variable name so it does not get replaced by one shorter 114 | char *temp = calloc(1, strlen(argv[1]) + 2); 115 | temp[0] = '$'; 116 | @@ -407,7 +408,7 @@ struct cmd_results *config_command(char 117 | free(command); 118 | 119 | // Strip quotes and unescape the string 120 | - for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 121 | + for (int i = handler->handle == cmd_set || handler->handle == cmd_set_from_resource ? 2 : 1; i < argc; ++i) { 122 | if (handler->handle != cmd_exec && handler->handle != cmd_exec_always 123 | && handler->handle != cmd_mode 124 | && handler->handle != cmd_bindsym 125 | @@ -415,6 +416,7 @@ struct cmd_results *config_command(char 126 | && handler->handle != cmd_bindswitch 127 | && handler->handle != cmd_bindgesture 128 | && handler->handle != cmd_set 129 | + && handler->handle != cmd_set_from_resource 130 | && handler->handle != cmd_for_window 131 | && (*argv[i] == '\"' || *argv[i] == '\'')) { 132 | strip_quotes(argv[i]); 133 | Index: sway-regolith/sway/meson.build 134 | =================================================================== 135 | --- sway-regolith.orig/sway/meson.build 136 | +++ sway-regolith/sway/meson.build 137 | @@ -103,6 +103,7 @@ sway_sources = files( 138 | 'commands/seat/shortcuts_inhibitor.c', 139 | 'commands/seat/xcursor_theme.c', 140 | 'commands/set.c', 141 | + 'commands/set_from_resource.c', 142 | 'commands/show_marks.c', 143 | 'commands/shortcuts_inhibitor.c', 144 | 'commands/smart_borders.c', 145 | @@ -234,6 +235,7 @@ sway_deps = [ 146 | xkbcommon, 147 | xcb, 148 | xcb_icccm, 149 | + trawldb, 150 | ] 151 | 152 | if wlroots_features['xwayland'] -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 4 | }; 5 | 6 | outputs = inputs@{ self, nixpkgs }: 7 | let 8 | inherit (self) outputs; 9 | system = "x86_64-linux"; 10 | inherit (nixpkgs) lib; 11 | pkgs = nixpkgs.legacyPackages.${system}; 12 | # Helper to collect all packages 13 | allPackages = with self.packages.${system}; [ 14 | ilia 15 | regolith-powerd 16 | regolith-displayd 17 | regolith-inputd 18 | regolith-ftue 19 | xrescat 20 | rofication 21 | remontoire 22 | trawl 23 | i3xrocks 24 | libtrawldb 25 | regolith-look-extra 26 | i3status-rs 27 | sway-regolith 28 | regolith-session 29 | i3-swap-focus 30 | regolith-xresources 31 | regolith-look-default 32 | regolith-systemd-units 33 | regolith-i3status-config 34 | regolith-session-wayland 35 | ]; 36 | in 37 | { 38 | # regolith-nix module 39 | # nixosModules.regolith = import ./regolith.nix; 40 | 41 | # this section can be run using nix run .# --> https://nixos.wiki/wiki/Flakes 42 | # can perform build checks unsing nix build .# --> https://nixos.wiki/wiki/Flakes 43 | packages."x86_64-linux".ilia = pkgs.callPackage ./packages/ilia.nix { }; # todo--> Stablize --> different icon theme crashes ilia 44 | 45 | # todo --> Error :- 46 | #(process:171187): GLib-GIO-ERROR **: 05:32:45.056: Settings schema 'org.gnome.settings-daemon.plugins.power' is not installed 47 | packages."x86_64-linux".regolith-powerd = pkgs.callPackage ./packages/regolith-powerd.nix { }; 48 | 49 | 50 | packages."x86_64-linux".regolith-displayd = pkgs.callPackage ./packages/regolith-displayd.nix { }; 51 | 52 | #works 53 | packages."x86_64-linux".regolith-inputd = pkgs.callPackage ./packages/regolith-inputd.nix { }; 54 | 55 | packages."x86_64-linux".regolith-ftue = pkgs.callPackage ./packages/regolith-ftue.nix { }; 56 | 57 | packages."x86_64-linux".xrescat = pkgs.callPackage ./packages/xrescat.nix { }; 58 | 59 | packages."x86_64-linux".rofication = pkgs.callPackage ./packages/rofication.nix { }; 60 | 61 | packages."x86_64-linux".remontoire = pkgs.callPackage ./packages/remontoire.nix { }; 62 | 63 | packages."x86_64-linux".trawl = pkgs.callPackage ./packages/trawl.nix { }; 64 | 65 | packages."x86_64-linux".i3xrocks = pkgs.callPackage ./packages/i3xrocks.nix { }; 66 | 67 | packages."x86_64-linux".libtrawldb = pkgs.callPackage ./packages/libtrawldb.nix { }; 68 | 69 | packages."x86_64-linux".regolith-look-extra = pkgs.callPackage ./packages/regolith-look-extra.nix { }; 70 | 71 | packages."x86_64-linux".i3status-rs = pkgs.callPackage ./packages/i3status-rs.nix { }; 72 | 73 | packages."x86_64-linux".sway-regolith = pkgs.callPackage ./sway-regolith/default.nix { }; 74 | 75 | packages."x86_64-linux".regolith-session = pkgs.callPackage ./packages/regolith-session.nix { }; 76 | 77 | packages."x86_64-linux".regolith-wm-config = pkgs.callPackage ./packages/regolith-wm-config.nix { }; 78 | 79 | packages."x86_64-linux".regolith-look-default = pkgs.callPackage ./packages/regolith-look-default.nix { }; 80 | 81 | packages."x86_64-linux".i3-swap-focus = pkgs.callPackage ./packages/i3-swap-focus.nix { }; 82 | 83 | packages."x86_64-linux".regolith-systemd-units = pkgs.callPackage ./packages/regolith-systemd-units.nix { }; 84 | 85 | packages."x86_64-linux".regolith-i3status-config = pkgs.callPackage ./packages/regolith-i3status-config.nix { }; 86 | 87 | packages."x86_64-linux".regolith-styles = pkgs.callPackage ./packages/regolith-styles.nix { }; 88 | 89 | packages."x86_64-linux".regolith-xresources = pkgs.callPackage ./xresources/package.nix { }; 90 | # the default runScript is fish and this creates a shell that follows fhs file format -->https://ryantm.github.io/nixpkgs/builders/special/fhs-environments/ 91 | packages."x86_64-linux".fhs = pkgs.callPackage ./fhs.nix { }; 92 | 93 | # this runs via --> nix run .#nixosConfigurations.vm.config.system.build.vm 94 | devShells.${system}.default = 95 | let 96 | fhs = pkgs.callPackage ./fhs.nix { }; 97 | in 98 | pkgs.mkShell { 99 | packages = [ fhs ] ++ allPackages; 100 | shellHook = '' 101 | exec ${fhs}/bin/regolith-environment 102 | ''; 103 | }; 104 | #pass regolith-session-wayland to regolith.nix 105 | # nixosModules.regolith-session-wayland = { config, pkgs, lib, ... }: 106 | # let 107 | # regolith-session = pkgs.callPackage ./packages/regolith-session.nix {}; 108 | # regolith-session-wayland = pkgs.callPackage ./fhs.nix { 109 | # runScript = "${regolith-session}/bin/regolith-session-wayland"; 110 | # name = "regolith-nix-session-wayland"; 111 | # }; 112 | # in { 113 | # imports = [ ./modules/regolith.nix ]; 114 | 115 | # # Make packages available to the regolith module 116 | # _module.args.regolith-session-wayland = regolith-session-wayland; 117 | # }; 118 | nixosModules.regolith = import ./modules/regolith.nix; 119 | # here I am trying to set runScript to regolith-session-wayland package 120 | #directly runs session-wayland 121 | packages."x86_64-linux".regolith-session-wayland = 122 | let 123 | regolith-session = pkgs.callPackage ./packages/regolith-session.nix { }; 124 | in 125 | pkgs.callPackage ./fhs.nix { 126 | runScript = "${regolith-session}/bin/regolith-session-wayland"; 127 | # name = "regolith-nix-session-wayland"; 128 | }; 129 | 130 | # this also runs via --> nix run .# 131 | packages."x86_64-linux".regolith-session-x11 = 132 | let 133 | regolith-session = pkgs.callPackage ./packages/regolith-session.nix { }; 134 | in 135 | pkgs.callPackage ./fhs.nix { 136 | runScript = "${regolith-session}/bin/regolith-session-x11"; 137 | }; 138 | 139 | # this is the nixos configuration for the vm !todo 140 | nixosConfigurations.vm = nixpkgs.lib.nixosSystem { 141 | inherit system; 142 | modules = [ 143 | ({ pkgs, ... }: { 144 | # Basic system configuration 145 | fileSystems."/" = { 146 | device = "none"; 147 | fsType = "tmpfs"; 148 | options = [ "size=2G" "mode=755" ]; 149 | }; 150 | 151 | boot.loader.grub.enable = true; 152 | boot.loader.grub.devices = [ "nodev" ]; 153 | 154 | # VM-specific settings 155 | virtualisation.vmVariant = { 156 | virtualisation = { 157 | memorySize = 4096; # MB 158 | cores = 4; 159 | }; 160 | }; 161 | imports = [ 162 | ./modules/regolith.nix 163 | ]; 164 | regolith.enable = true; 165 | # Basic system services 166 | # services.xserver = { 167 | # enable = true; 168 | # # displayManager.gdm.enable = true; 169 | # # desktopManager.gnome.enable = true; 170 | # }; 171 | 172 | # Packages and user configuration 173 | environment.systemPackages = allPackages; 174 | 175 | users.users.demo = { 176 | isNormalUser = true; 177 | extraGroups = [ "wheel" ]; 178 | initialPassword = "demo"; 179 | }; 180 | 181 | system.stateVersion = "23.11"; 182 | }) 183 | ]; 184 | }; 185 | }; 186 | } 187 | -------------------------------------------------------------------------------- /regolith.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /sway-regolith/04-dbus-tray: -------------------------------------------------------------------------------- 1 | Index: sway-regolith/include/swaybar/bar.h 2 | =================================================================== 3 | --- sway-regolith.orig/include/swaybar/bar.h 4 | +++ sway-regolith/include/swaybar/bar.h 5 | @@ -33,6 +33,7 @@ struct swaybar { 6 | struct zxdg_output_manager_v1 *xdg_output_manager; 7 | struct wp_cursor_shape_manager_v1 *cursor_shape_manager; 8 | struct wl_shm *shm; 9 | + struct xdg_wm_base *wm_base; 10 | 11 | struct swaybar_config *config; 12 | struct status_line *status; 13 | Index: sway-regolith/include/swaybar/input.h 14 | =================================================================== 15 | --- sway-regolith.orig/include/swaybar/input.h 16 | +++ sway-regolith/include/swaybar/input.h 17 | @@ -15,6 +15,7 @@ 18 | 19 | struct swaybar; 20 | struct swaybar_output; 21 | +struct swaybar_seat; 22 | 23 | struct swaybar_pointer { 24 | struct wl_pointer *pointer; 25 | @@ -48,8 +49,8 @@ struct swaybar_hotspot { 26 | struct wl_list link; // swaybar_output::hotspots 27 | int x, y, width, height; 28 | enum hotspot_event_handling (*callback)(struct swaybar_output *output, 29 | - struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, 30 | - bool released, void *data); 31 | + struct swaybar_hotspot *hotspot, struct swaybar_seat *seat, uint32_t serial, 32 | + double x, double y, uint32_t button, bool released, void *data); 33 | void (*destroy)(void *data); 34 | void *data; 35 | }; 36 | Index: sway-regolith/include/swaybar/tray/item.h 37 | =================================================================== 38 | --- sway-regolith.orig/include/swaybar/tray/item.h 39 | +++ sway-regolith/include/swaybar/tray/item.h 40 | @@ -18,6 +18,7 @@ struct swaybar_pixmap { 41 | struct swaybar_sni_slot { 42 | struct wl_list link; // swaybar_sni::slots 43 | struct swaybar_sni *sni; 44 | + int menu_id; 45 | const char *prop; 46 | const char *type; 47 | void *dest; 48 | @@ -48,6 +49,7 @@ struct swaybar_sni { 49 | char *icon_theme_path; // non-standard KDE property 50 | 51 | struct wl_list slots; // swaybar_sni_slot::link 52 | + char **menu_icon_theme_paths; 53 | }; 54 | 55 | struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray); 56 | Index: sway-regolith/include/swaybar/tray/tray.h 57 | =================================================================== 58 | --- sway-regolith.orig/include/swaybar/tray/tray.h 59 | +++ sway-regolith/include/swaybar/tray/tray.h 60 | @@ -32,6 +32,9 @@ struct swaybar_tray { 61 | 62 | list_t *basedirs; // char * 63 | list_t *themes; // struct swaybar_theme * 64 | + 65 | + struct swaybar_dbusmenu *menu; 66 | + struct swaybar_dbusmenu_menu *menu_pointer_focus; 67 | }; 68 | 69 | struct swaybar_tray *create_tray(struct swaybar *bar); 70 | Index: sway-regolith/swaybar/bar.c 71 | =================================================================== 72 | --- sway-regolith.orig/swaybar/bar.c 73 | +++ sway-regolith/swaybar/bar.c 74 | @@ -28,6 +28,7 @@ 75 | #include "pool-buffer.h" 76 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" 77 | #include "xdg-output-unstable-v1-client-protocol.h" 78 | +#include "xdg-shell-client-protocol.h" 79 | 80 | void free_workspaces(struct wl_list *list) { 81 | struct swaybar_workspace *ws, *tmp; 82 | @@ -364,6 +365,8 @@ static void handle_global(void *data, st 83 | } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { 84 | bar->cursor_shape_manager = wl_registry_bind(registry, name, 85 | &wp_cursor_shape_manager_v1_interface, 1); 86 | + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { 87 | + bar->wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); 88 | } 89 | } 90 | 91 | @@ -538,6 +541,7 @@ void bar_teardown(struct swaybar *bar) { 92 | #if HAVE_TRAY 93 | destroy_tray(bar->tray); 94 | #endif 95 | + xdg_wm_base_destroy(bar->wm_base); 96 | free_outputs(&bar->outputs); 97 | free_outputs(&bar->unused_outputs); 98 | free_seats(&bar->seats); 99 | Index: sway-regolith/swaybar/input.c 100 | =================================================================== 101 | --- sway-regolith.orig/swaybar/input.c 102 | +++ sway-regolith/swaybar/input.c 103 | @@ -10,6 +10,10 @@ 104 | #include "swaybar/input.h" 105 | #include "swaybar/ipc.h" 106 | 107 | +#if HAVE_TRAY 108 | +#include "swaybar/tray/dbusmenu.h" 109 | +#endif 110 | + 111 | void free_hotspots(struct wl_list *list) { 112 | struct swaybar_hotspot *hotspot, *tmp; 113 | wl_list_for_each_safe(hotspot, tmp, list, link) { 114 | @@ -131,11 +135,27 @@ static void wl_pointer_enter(void *data, 115 | pointer->serial = serial; 116 | update_cursor(seat); 117 | } 118 | + 119 | +#if HAVE_TRAY 120 | + struct swaybar_config *config = seat->bar->config; 121 | + if (!config->tray_hidden && dbusmenu_pointer_enter(data, wl_pointer, serial, 122 | + surface, surface_x, surface_y)) { 123 | + return; 124 | + } 125 | +#endif 126 | } 127 | 128 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, 129 | uint32_t serial, struct wl_surface *surface) { 130 | +#if HAVE_TRAY 131 | struct swaybar_seat *seat = data; 132 | + struct swaybar_config *config = seat->bar->config; 133 | + if (!config->tray_hidden && dbusmenu_pointer_leave(data, wl_pointer, serial, 134 | + surface)) { 135 | + return; 136 | + } 137 | +#endif 138 | + 139 | seat->pointer.current = NULL; 140 | } 141 | 142 | @@ -144,6 +164,13 @@ static void wl_pointer_motion(void *data 143 | struct swaybar_seat *seat = data; 144 | seat->pointer.x = wl_fixed_to_double(surface_x); 145 | seat->pointer.y = wl_fixed_to_double(surface_y); 146 | +#if HAVE_TRAY 147 | + struct swaybar_config *config = seat->bar->config; 148 | + if (!config->tray_hidden && dbusmenu_pointer_motion(data, wl_pointer, time, 149 | + surface_x, surface_y)) { 150 | + return; 151 | + } 152 | +#endif 153 | } 154 | 155 | static bool check_bindings(struct swaybar *bar, uint32_t button, 156 | @@ -160,6 +187,7 @@ static bool check_bindings(struct swayba 157 | } 158 | 159 | static bool process_hotspots(struct swaybar_output *output, 160 | + struct swaybar_seat *seat, uint32_t serial, 161 | double x, double y, uint32_t button, uint32_t state) { 162 | bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; 163 | struct swaybar_hotspot *hotspot; 164 | @@ -167,7 +195,7 @@ static bool process_hotspots(struct sway 165 | if (x >= hotspot->x && y >= hotspot->y 166 | && x < hotspot->x + hotspot->width 167 | && y < hotspot->y + hotspot->height) { 168 | - if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, 169 | + if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, seat, serial, x, y, 170 | button, released, hotspot->data)) { 171 | return true; 172 | } 173 | @@ -180,13 +208,20 @@ static bool process_hotspots(struct sway 174 | static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, 175 | uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { 176 | struct swaybar_seat *seat = data; 177 | +#if HAVE_TRAY 178 | + struct swaybar_config *config = seat->bar->config; 179 | + if (!config->tray_hidden && dbusmenu_pointer_button(seat, wl_pointer, serial, 180 | + time, button, state)) { 181 | + return; 182 | + } 183 | +#endif 184 | struct swaybar_pointer *pointer = &seat->pointer; 185 | struct swaybar_output *output = pointer->current; 186 | if (!sway_assert(output, "button with no active output")) { 187 | return; 188 | } 189 | 190 | - if (process_hotspots(output, pointer->x, pointer->y, button, state)) { 191 | + if (process_hotspots(output, seat, serial, pointer->x, pointer->y, button, state)) { 192 | return; 193 | } 194 | 195 | @@ -240,7 +275,7 @@ static void process_discrete_scroll(stru 196 | struct swaybar_output *output, struct swaybar_pointer *pointer, 197 | uint32_t axis, wl_fixed_t value) { 198 | uint32_t button = wl_axis_to_button(axis, value); 199 | - if (process_hotspots(output, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) { 200 | + if (process_hotspots(output, seat, 0, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) { 201 | // (Currently hotspots don't do anything on release events, so no need to emit one) 202 | return; 203 | } 204 | @@ -297,6 +332,13 @@ static void wl_pointer_axis(void *data, 205 | return; 206 | } 207 | 208 | +#if HAVE_TRAY 209 | + struct swaybar_config *config = seat->bar->config; 210 | + if (!config->tray_hidden && dbusmenu_pointer_axis(data, wl_pointer)) { 211 | + return; 212 | + } 213 | +#endif 214 | + 215 | // If there's a while since the last scroll event, 216 | // set 'value' to zero as if to reset the "virtual scroll wheel" 217 | if (seat->axis[axis].discrete_steps == 0 && 218 | @@ -313,6 +355,13 @@ static void wl_pointer_frame(void *data, 219 | struct swaybar_pointer *pointer = &seat->pointer; 220 | struct swaybar_output *output = pointer->current; 221 | 222 | +#if HAVE_TRAY 223 | + struct swaybar_config *config = seat->bar->config; 224 | + if (!config->tray_hidden && dbusmenu_pointer_frame(data, wl_pointer)) { 225 | + return; 226 | + } 227 | +#endif 228 | + 229 | if (output == NULL) { 230 | return; 231 | } 232 | @@ -420,7 +469,7 @@ static void wl_touch_up(void *data, stru 233 | } 234 | if (time - slot->time < 500) { 235 | // Tap, treat it like a pointer click 236 | - process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); 237 | + process_hotspots(slot->output, seat, serial, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); 238 | // (Currently hotspots don't do anything on release events, so no need to emit one) 239 | } 240 | slot->output = NULL; 241 | Index: sway-regolith/swaybar/meson.build 242 | =================================================================== 243 | --- sway-regolith.orig/swaybar/meson.build 244 | +++ sway-regolith/swaybar/meson.build 245 | @@ -3,7 +3,8 @@ tray_files = have_tray ? [ 246 | 'tray/icon.c', 247 | 'tray/item.c', 248 | 'tray/tray.c', 249 | - 'tray/watcher.c' 250 | + 'tray/watcher.c', 251 | + 'tray/dbusmenu.c' 252 | ] : [] 253 | 254 | swaybar_deps = [ 255 | Index: sway-regolith/swaybar/render.c 256 | =================================================================== 257 | --- sway-regolith.orig/swaybar/render.c 258 | +++ sway-regolith/swaybar/render.c 259 | @@ -160,6 +160,7 @@ static void render_sharp_line(cairo_t *c 260 | 261 | static enum hotspot_event_handling block_hotspot_callback( 262 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, 263 | + struct swaybar_seat *seat, uint32_t serial, 264 | double x, double y, uint32_t button, bool released, void *data) { 265 | struct i3bar_block *block = data; 266 | struct status_line *status = output->bar->status; 267 | @@ -601,6 +602,7 @@ static uint32_t render_binding_mode_indi 268 | 269 | static enum hotspot_event_handling workspace_hotspot_callback( 270 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, 271 | + struct swaybar_seat *seat, uint32_t serial, 272 | double x, double y, uint32_t button, bool released, void *data) { 273 | if (button != BTN_LEFT) { 274 | return HOTSPOT_PROCESS; 275 | Index: sway-regolith/swaybar/tray/item.c 276 | =================================================================== 277 | --- sway-regolith.orig/swaybar/tray/item.c 278 | +++ sway-regolith/swaybar/tray/item.c 279 | @@ -8,6 +8,7 @@ 280 | #include "swaybar/config.h" 281 | #include "swaybar/image.h" 282 | #include "swaybar/input.h" 283 | +#include "swaybar/tray/dbusmenu.h" 284 | #include "swaybar/tray/host.h" 285 | #include "swaybar/tray/icon.h" 286 | #include "swaybar/tray/item.h" 287 | @@ -332,8 +333,9 @@ void destroy_sni(struct swaybar_sni *sni 288 | free(sni); 289 | } 290 | 291 | -static void handle_click(struct swaybar_sni *sni, int x, int y, 292 | - uint32_t button, int delta) { 293 | +static void handle_click(struct swaybar_sni *sni, struct swaybar_output *output, 294 | + struct swaybar_seat *seat, uint32_t serial, int x, int y, uint32_t button, 295 | + int delta) { 296 | const char *method = NULL; 297 | struct tray_binding *binding = NULL; 298 | wl_list_for_each(binding, &sni->tray->bar->config->tray_bindings, link) { 299 | @@ -364,7 +366,11 @@ static void handle_click(struct swaybar_ 300 | method = "ContextMenu"; 301 | } 302 | 303 | - if (strncmp(method, "Scroll", strlen("Scroll")) == 0) { 304 | + if (strcmp(method, "ContextMenu") == 0) { 305 | + if (sni->menu && !sni->tray->menu) { 306 | + swaybar_dbusmenu_open(sni, output, seat, serial, x, y); 307 | + } 308 | + } else if (strncmp(method, "Scroll", strlen("Scroll")) == 0) { 309 | char dir = method[strlen("Scroll")]; 310 | char *orientation = (dir == 'U' || dir == 'D') ? "vertical" : "horizontal"; 311 | int sign = (dir == 'U' || dir == 'L') ? -1 : 1; 312 | @@ -384,6 +390,7 @@ static int cmp_sni_id(const void *item, 313 | 314 | static enum hotspot_event_handling icon_hotspot_callback( 315 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, 316 | + struct swaybar_seat *seat, uint32_t serial, 317 | double x, double y, uint32_t button, bool released, void *data) { 318 | sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); 319 | 320 | @@ -405,7 +412,8 @@ static enum hotspot_event_handling icon_ 321 | (int) output->output_height - config->gaps.bottom - y); 322 | 323 | sway_log(SWAY_DEBUG, "Guessing click position at (%d, %d)", global_x, global_y); 324 | - handle_click(sni, global_x, global_y, button, 1); // TODO get delta from event 325 | + // TODO get delta from event 326 | + handle_click(sni, output, seat, serial, global_x, global_y, button, 1); 327 | return HOTSPOT_IGNORE; 328 | } else { 329 | sway_log(SWAY_DEBUG, "but it doesn't exist"); 330 | Index: sway-regolith/swaybar/tray/tray.c 331 | =================================================================== 332 | --- sway-regolith.orig/swaybar/tray/tray.c 333 | +++ sway-regolith/swaybar/tray/tray.c 334 | @@ -118,7 +118,7 @@ static int cmp_output(const void *item, 335 | 336 | uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) { 337 | struct swaybar_config *config = output->bar->config; 338 | - if (config->tray_outputs) { 339 | + if (config->tray_outputs && !config->tray_hidden) { 340 | if (list_seq_find(config->tray_outputs, cmp_output, output) == -1) { 341 | return 0; 342 | } 343 | Index: sway-regolith/include/swaybar/tray/dbusmenu.h 344 | =================================================================== 345 | --- /dev/null 346 | +++ sway-regolith/include/swaybar/tray/dbusmenu.h 347 | @@ -0,0 +1,27 @@ 348 | +#ifndef _SWAYBAR_TRAY_DBUSMENU_H 349 | +#define _SWAYBAR_TRAY_DBUSMENU_H 350 | + 351 | +#include "swaybar/bar.h" 352 | +#include "swaybar/tray/item.h" 353 | + 354 | +void swaybar_dbusmenu_open(struct swaybar_sni *sni, 355 | + struct swaybar_output *output, struct swaybar_seat *seat, uint32_t serial, 356 | + int x, int y); 357 | + 358 | +bool dbusmenu_pointer_button(void *data, struct wl_pointer *wl_pointer, 359 | + uint32_t serial, uint32_t time_, uint32_t button, uint32_t state); 360 | + 361 | +bool dbusmenu_pointer_motion(struct swaybar_seat *seat, struct wl_pointer *wl_pointer, 362 | + uint32_t time_, wl_fixed_t surface_x, wl_fixed_t surface_y); 363 | + 364 | +bool dbusmenu_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, 365 | + struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y); 366 | + 367 | +bool dbusmenu_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, 368 | + struct wl_surface *surface); 369 | + 370 | +bool dbusmenu_pointer_frame(struct swaybar_seat *data, struct wl_pointer *wl_pointer); 371 | + 372 | +bool dbusmenu_pointer_axis(struct swaybar_seat *data, struct wl_pointer *wl_pointer); 373 | + 374 | +#endif 375 | Index: sway-regolith/swaybar/tray/dbusmenu.c 376 | =================================================================== 377 | --- /dev/null 378 | +++ sway-regolith/swaybar/tray/dbusmenu.c 379 | @@ -0,0 +1,1348 @@ 380 | +#define _POSIX_C_SOURCE 200809L 381 | +#include 382 | +#include 383 | +#include 384 | +#include 385 | +#include 386 | +#include 387 | +#include 388 | +#include 389 | +#include 390 | + 391 | +#include "cairo.h" 392 | +#include "cairo_util.h" 393 | +#include "list.h" 394 | +#include "log.h" 395 | +#include "pango.h" 396 | +#include "swaybar/bar.h" 397 | +#include "swaybar/config.h" 398 | +#include "swaybar/input.h" 399 | +#include "swaybar/tray/icon.h" 400 | +#include "swaybar/tray/item.h" 401 | +#include "swaybar/tray/tray.h" 402 | + 403 | +static const char *menu_interface = "com.canonical.dbusmenu"; 404 | + 405 | +static void swaybar_dbusmenu_get_layout_root(struct swaybar_dbusmenu *menu); 406 | +static void swaybar_dbusmenu_get_layout(struct swaybar_dbusmenu *menu, int id); 407 | +static void swaybar_dbusmenu_draw(struct swaybar_dbusmenu *dbusmenu, int id); 408 | + 409 | +struct swaybar_dbusmenu_hotspot { 410 | + int x, y, width, height; 411 | +}; 412 | + 413 | +struct swaybar_dbusmenu_surface { 414 | + struct xdg_popup *xdg_popup; 415 | + struct xdg_surface *xdg_surface; 416 | + struct wl_surface *surface; 417 | + struct pool_buffer *current_buffer; 418 | + struct pool_buffer buffers[2]; 419 | + int width, height; 420 | + bool configured; 421 | +}; 422 | + 423 | +enum menu_toggle_type { 424 | + MENU_NONE, 425 | + MENU_CHECKMARK, 426 | + MENU_RADIO 427 | +}; 428 | + 429 | +struct swaybar_dbusmenu_menu_item { 430 | + struct swaybar_dbusmenu_hotspot hotspot; 431 | + // Set if the item has a submenu 432 | + struct swaybar_dbusmenu_menu *submenu; 433 | + // The menu in which the item is displayed 434 | + struct swaybar_dbusmenu_menu *menu; 435 | + struct swaybar_dbusmenu_menu_item *parent_item; 436 | + int id; 437 | + int toggle_state; 438 | + char *label; 439 | + char *icon_name; 440 | + cairo_surface_t *icon_data; 441 | + enum menu_toggle_type toggle_type; 442 | + bool enabled; 443 | + bool visible; 444 | + bool is_separator; 445 | +}; 446 | + 447 | +struct swaybar_dbusmenu_menu { 448 | + struct swaybar_dbusmenu *dbusmenu; 449 | + struct swaybar_dbusmenu_surface *surface; 450 | + struct swaybar_dbusmenu_menu_item *last_hovered_item; 451 | + list_t *items; // struct swaybar_dbusmenu_menu_item 452 | + int item_id; 453 | + int x, y; 454 | +}; 455 | + 456 | +struct swaybar_dbusmenu { 457 | + struct swaybar_sni *sni; 458 | + struct swaybar_output *output; 459 | + struct swaybar_seat *seat; 460 | + struct swaybar_dbusmenu_menu *menu; 461 | + struct swaybar *bar; 462 | + int serial; 463 | + int x, y; 464 | +}; 465 | + 466 | +struct get_layout_callback_data { 467 | + struct swaybar_dbusmenu *menu; 468 | + int id; 469 | +}; 470 | + 471 | +struct png_stream { 472 | + const void *data; 473 | + size_t left; 474 | +}; 475 | + 476 | +static void commit_menu_surface(struct swaybar_dbusmenu_menu *menu) 477 | +{ 478 | + struct swaybar_dbusmenu_surface * dbusmenu_surface = menu->surface; 479 | + if (!dbusmenu_surface->configured || dbusmenu_surface->current_buffer == NULL) { 480 | + return; 481 | + } 482 | + 483 | + struct wl_surface *surface = dbusmenu_surface->surface; 484 | + wl_surface_set_buffer_scale(surface, menu->dbusmenu->output->scale); 485 | + wl_surface_attach(surface, dbusmenu_surface->current_buffer->buffer, 0, 0); 486 | + wl_surface_damage(surface, 0, 0, dbusmenu_surface->width, dbusmenu_surface->height); 487 | + wl_surface_commit(surface); 488 | +} 489 | + 490 | +static int handle_items_properties_updated(sd_bus_message *msg, void *data, 491 | + sd_bus_error *error) { 492 | + struct swaybar_sni *sni = data; 493 | + sway_log(SWAY_DEBUG, "%s%s item properties updated", sni->service, sni->menu); 494 | + 495 | + // TODO: Optimize. Update only needed properties 496 | + if (sni->tray->menu) { 497 | + swaybar_dbusmenu_get_layout_root(sni->tray->menu); 498 | + } 499 | + return 0; 500 | +} 501 | + 502 | +static int handle_layout_updated(sd_bus_message *msg, void *data, 503 | + sd_bus_error *error) { 504 | + struct swaybar_sni *sni = data; 505 | + sway_log(SWAY_DEBUG, "%s%s layout updated", sni->service, sni->menu); 506 | + 507 | + int id; 508 | + sd_bus_message_read(msg, "ui", NULL, &id); 509 | + if (sni->tray->menu) { 510 | + swaybar_dbusmenu_get_layout(sni->tray->menu, id); 511 | + } 512 | + return 0; 513 | +} 514 | + 515 | +static int handle_item_activation_requested(sd_bus_message *msg, void *data, 516 | + sd_bus_error *error) { 517 | + return 0; // TODO: Implement handling of hotkeys for opening the menu 518 | +} 519 | + 520 | +static struct swaybar_dbusmenu_surface *swaybar_dbusmenu_surface_create() { 521 | + struct swaybar_dbusmenu_surface *dbusmenu = calloc(1, 522 | + sizeof(struct swaybar_dbusmenu_surface)); 523 | + if (!dbusmenu) { 524 | + sway_log(SWAY_DEBUG, "Could not allocate dbusmenu"); 525 | + } 526 | + return dbusmenu; 527 | +} 528 | + 529 | +static void xdg_surface_handle_configure(void *data, 530 | + struct xdg_surface *xdg_surface, uint32_t serial) { 531 | + xdg_surface_ack_configure(xdg_surface, serial); 532 | + 533 | + struct swaybar_dbusmenu_menu *menu = data; 534 | + menu->surface->configured = true; 535 | + commit_menu_surface(menu); 536 | +} 537 | + 538 | +static const struct xdg_surface_listener xdg_surface_listener = { 539 | + .configure = xdg_surface_handle_configure, 540 | +}; 541 | + 542 | +static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, 543 | + int32_t x, int32_t y, int32_t width, int32_t height) { 544 | + // intentionally left blank 545 | +} 546 | + 547 | +static void destroy_dbusmenu_surface( 548 | + struct swaybar_dbusmenu_surface *dbusmenu_surface) { 549 | + if (!dbusmenu_surface) { 550 | + return; 551 | + } 552 | + 553 | + if (dbusmenu_surface->xdg_popup) { 554 | + xdg_popup_destroy(dbusmenu_surface->xdg_popup); 555 | + dbusmenu_surface->xdg_popup = NULL; 556 | + } 557 | + if (dbusmenu_surface->surface) { 558 | + xdg_surface_destroy(dbusmenu_surface->xdg_surface); 559 | + wl_surface_destroy(dbusmenu_surface->surface); 560 | + dbusmenu_surface->surface = NULL; 561 | + } 562 | + destroy_buffer(&dbusmenu_surface->buffers[0]); 563 | + destroy_buffer(&dbusmenu_surface->buffers[1]); 564 | + 565 | + free(dbusmenu_surface); 566 | +} 567 | + 568 | +static void close_menu(struct swaybar_dbusmenu_menu *menu) { 569 | + if (!menu) { 570 | + return; 571 | + } 572 | + 573 | + if (menu->surface) { 574 | + destroy_dbusmenu_surface(menu->surface); 575 | + menu->surface = NULL; 576 | + 577 | + int id = menu->item_id; 578 | + struct swaybar_sni *sni = menu->dbusmenu->sni; 579 | + sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->menu, 580 | + menu_interface, "Event", NULL, NULL, "isvu", id, "closed", "y", 581 | + 0, time(NULL)); 582 | + sway_log(SWAY_DEBUG, "%s%s closed id %d", sni->service, sni->menu, id); 583 | + } 584 | +} 585 | + 586 | +static void close_menus(struct swaybar_dbusmenu_menu *menu) { 587 | + if (!menu) { 588 | + return; 589 | + } 590 | + 591 | + if (menu->items) { 592 | + for (int i = 0; i < menu->items->length; ++i) { 593 | + struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; 594 | + if (item->submenu && item->submenu->item_id != 0) { 595 | + close_menus(item->submenu); 596 | + } 597 | + } 598 | + } 599 | + 600 | + close_menu(menu); 601 | +} 602 | + 603 | +static void swaybar_dbusmenu_menu_destroy(struct swaybar_dbusmenu_menu *menu) { 604 | + if (!menu) { 605 | + return; 606 | + } 607 | + 608 | + if (menu->items) { 609 | + for (int i = 0; i < menu->items->length; ++i) { 610 | + struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; 611 | + struct swaybar_dbusmenu_menu *child_menu = item->submenu; 612 | + if (child_menu && child_menu->item_id != 0) { 613 | + swaybar_dbusmenu_menu_destroy(item->submenu); 614 | + } 615 | + free(item->label); 616 | + free(item->icon_name); 617 | + free(item->icon_data); 618 | + free(item); 619 | + } 620 | + } 621 | + list_free(menu->items); 622 | + free(menu); 623 | +} 624 | + 625 | +void swaybar_dbusmenu_destroy(struct swaybar_dbusmenu *menu) { 626 | + if (!menu) { 627 | + return; 628 | + } 629 | + 630 | + menu->sni->tray->menu = NULL; 631 | + menu->sni->tray->menu_pointer_focus = NULL; 632 | + 633 | + close_menus(menu->menu); 634 | + swaybar_dbusmenu_menu_destroy(menu->menu); 635 | + free(menu); 636 | +} 637 | + 638 | +static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { 639 | + struct swaybar_dbusmenu_menu *menu = data; 640 | + swaybar_dbusmenu_destroy(menu->dbusmenu); 641 | +} 642 | + 643 | +static const struct xdg_popup_listener xdg_popup_listener = { 644 | + .configure = xdg_popup_configure, .popup_done = xdg_popup_done}; 645 | + 646 | +static struct swaybar_dbusmenu_menu_item * 647 | +find_item_under_menu(struct swaybar_dbusmenu_menu *menu, int item_id) { 648 | + if (!menu->items) { 649 | + return NULL; 650 | + } 651 | + 652 | + for (int i = 0; i < menu->items->length; ++i) { 653 | + struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; 654 | + if (item->id == item_id) { 655 | + return item; 656 | + } 657 | + if (item->submenu && item->submenu->item_id != 0) { 658 | + struct swaybar_dbusmenu_menu_item *found_item = 659 | + find_item_under_menu(item->submenu, item_id); 660 | + if (found_item) { 661 | + return found_item; 662 | + } 663 | + } 664 | + } 665 | + 666 | + return NULL; 667 | +} 668 | + 669 | +static struct swaybar_dbusmenu_menu_item * 670 | +find_item(struct swaybar_dbusmenu *dbusmenu, int item_id) { 671 | + if (!dbusmenu->menu) { 672 | + return NULL; 673 | + } 674 | + 675 | + return find_item_under_menu(dbusmenu->menu, item_id); 676 | +} 677 | + 678 | +static struct swaybar_dbusmenu_menu * 679 | +find_parent_menu_under_menu(struct swaybar_dbusmenu_menu *menu, 680 | + struct swaybar_dbusmenu_menu *child_menu) { 681 | + if (!menu->items) { 682 | + return NULL; 683 | + } 684 | + 685 | + for (int i = 0; i < menu->items->length; ++i) { 686 | + struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; 687 | + struct swaybar_dbusmenu_menu *maybe_child_menu = item->submenu; 688 | + if (maybe_child_menu && maybe_child_menu->item_id != 0) { 689 | + if (maybe_child_menu == child_menu) { 690 | + return menu; 691 | + } 692 | + maybe_child_menu = find_parent_menu_under_menu(maybe_child_menu, child_menu); 693 | + if (maybe_child_menu) { 694 | + return maybe_child_menu; 695 | + } 696 | + } 697 | + } 698 | + 699 | + return NULL; 700 | +} 701 | + 702 | +static struct swaybar_dbusmenu_menu * 703 | +find_parent_menu(struct swaybar_dbusmenu_menu *menu) { 704 | + if (menu && menu->item_id == 0) { 705 | + return NULL; 706 | + } 707 | + struct swaybar_dbusmenu_menu *root_menu = menu->dbusmenu->menu; 708 | + return find_parent_menu_under_menu(root_menu, menu); 709 | +} 710 | + 711 | +static bool is_in_hotspot(struct swaybar_dbusmenu_hotspot *hotspot, int x, int y) { 712 | + if (!hotspot) { 713 | + return false; 714 | + } 715 | + 716 | + if (hotspot->x <= x && x < hotspot->x + hotspot->width && hotspot->y <= y && 717 | + y < hotspot->y + hotspot->height) { 718 | + return true; 719 | + } 720 | + 721 | + return false; 722 | +} 723 | + 724 | +static void draw_menu_items(cairo_t *cairo, struct swaybar_dbusmenu_menu *menu, 725 | + int *surface_x, int *surface_y, int *surface_width, int *surface_height, 726 | + bool open) { 727 | + struct swaybar_sni *sni = menu->dbusmenu->sni; 728 | + struct swaybar_tray *tray = sni->tray; 729 | + struct swaybar_output *output = menu->dbusmenu->output; 730 | + struct swaybar_config *config = menu->dbusmenu->output->bar->config; 731 | + 732 | + int padding = config->tray_padding * output->scale; 733 | + 734 | + list_t *items = menu->items; 735 | + int height = 0; 736 | + 737 | + *surface_y = 0; 738 | + *surface_x = 0; 739 | + *surface_width = 0; 740 | + bool is_icon_drawn = false; 741 | + int icon_size = 0; 742 | + 743 | + for (int i = 0; i < items->length; ++i) { 744 | + struct swaybar_dbusmenu_menu_item *item = items->items[i]; 745 | + 746 | + if (!item->visible) { 747 | + continue; 748 | + } 749 | + 750 | + int new_height = height; 751 | + if (item->is_separator) { 752 | + // drawn later, after the width is known 753 | + new_height = height + output->scale; 754 | + } else if (item->label) { 755 | + cairo_move_to(cairo, padding, height + padding); 756 | + 757 | + // draw label 758 | + if (item->enabled) { 759 | + cairo_set_source_u32(cairo, config->colors.focused_statusline); 760 | + } else { 761 | + uint32_t c = config->colors.focused_statusline; 762 | + uint32_t disabled_color = c - ((c & 0xFF) >> 1); 763 | + cairo_set_source_u32(cairo, disabled_color); 764 | + } 765 | + render_text(cairo, config->font_description, output->scale, false, "%s", 766 | + item->label); 767 | + 768 | + // draw icon or menu indicator if needed 769 | + int text_height; 770 | + int text_width; 771 | + get_text_size(cairo, config->font_description, &text_width, &text_height, 772 | + NULL, output->scale, false, "%s", item->label); 773 | + text_width += padding; 774 | + int size = text_height; 775 | + int x = -2 * padding - size; 776 | + int y = height + padding; 777 | + icon_size = 2 * padding + size; 778 | + cairo_set_source_u32(cairo, config->colors.focused_statusline); 779 | + if (item->icon_name) { 780 | + list_t *icon_search_paths = create_list(); 781 | + list_cat(icon_search_paths, tray->basedirs); 782 | + if (sni->menu_icon_theme_paths) { 783 | + for (char **path = sni->menu_icon_theme_paths; *path; ++path) { 784 | + list_add(icon_search_paths, *path); 785 | + } 786 | + } 787 | + if (sni->icon_theme_path) { 788 | + list_add(icon_search_paths, sni->icon_theme_path); 789 | + } 790 | + list_free(icon_search_paths); 791 | + } else if (item->icon_data) { 792 | + cairo_surface_t *icon = cairo_image_surface_scale(item->icon_data, size, size); 793 | + cairo_set_source_surface(cairo, icon, x, y); 794 | + cairo_rectangle(cairo, x, y, size, size); 795 | + cairo_fill(cairo); 796 | + cairo_surface_destroy(icon); 797 | + is_icon_drawn = true; 798 | + } else if (item->toggle_type == MENU_CHECKMARK) { 799 | + cairo_rectangle(cairo, x, y, size, size); 800 | + cairo_fill(cairo); 801 | + cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 802 | + if (item->toggle_state == 1) { // tick 803 | + cairo_move_to(cairo, x + size * 3.0 / 4, y + size * 5.0 / 16.0); 804 | + cairo_line_to(cairo, x + size * 3.0 / 8, y + size * 11.0 / 16.0); 805 | + cairo_line_to(cairo, x + size / 4.0, y + size * 9.0 / 16.0); 806 | + cairo_stroke(cairo); 807 | + } else if (item->toggle_state != 0) { // horizontal line 808 | + cairo_rectangle(cairo, x + size / 4.0, y + size / 2.0 - 1, 809 | + size / 2.0, 2); 810 | + cairo_fill(cairo); 811 | + } 812 | + cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); 813 | + is_icon_drawn = true; 814 | + } else if (item->toggle_type == MENU_RADIO) { 815 | + cairo_arc(cairo, x + size / 2.0, y + size / 2.0, size / 2.0, 0, 7); 816 | + cairo_fill(cairo); 817 | + if (item->toggle_state == 1) { 818 | + cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 819 | + cairo_arc(cairo, x + size / 2.0, y + size / 2.0, size / 4.0, 0, 7); 820 | + cairo_fill(cairo); 821 | + cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); 822 | + } 823 | + is_icon_drawn = true; 824 | + } else if (item->submenu) { // arrowhead 825 | + cairo_move_to(cairo, x + size / 4.0, y + size / 2.0); 826 | + cairo_line_to(cairo, x + size * 3.0 / 4, y + size / 4.0); 827 | + cairo_line_to(cairo, x + size * 3.0 / 4, y + size * 3.0 / 4); 828 | + cairo_fill(cairo); 829 | + is_icon_drawn = true; 830 | + } 831 | + 832 | + *surface_width = *surface_width < text_width ? text_width : *surface_width; 833 | + new_height = height + text_height + 2 * padding; 834 | + } else { 835 | + continue; 836 | + } 837 | + 838 | + struct swaybar_dbusmenu_hotspot *hotspot = &item->hotspot; 839 | + hotspot->y = height; 840 | + 841 | + hotspot->y = height; 842 | + hotspot->height = new_height - height; 843 | + // x and width is not known at the moment 844 | + 845 | + height = new_height; 846 | + } 847 | + 848 | + if (height == 0) { 849 | + return; 850 | + } 851 | + 852 | + if (is_icon_drawn) { 853 | + *surface_x = -icon_size - padding; 854 | + *surface_width += icon_size + padding; 855 | + } 856 | + 857 | + *surface_width += padding; 858 | + *surface_height = height; 859 | + 860 | + // Make sure height and width are divideable by scale 861 | + // otherwise the menu will not showup 862 | + if (*surface_width % output->scale != 0) { 863 | + *surface_width -= *surface_width % output->scale; 864 | + } 865 | + if (*surface_height % output->scale != 0) { 866 | + *surface_height -= *surface_height % output->scale; 867 | + } 868 | + 869 | + cairo_set_line_width(cairo, output->scale); 870 | + cairo_set_source_u32(cairo, config->colors.focused_separator); 871 | + for (int i = 0; i < items->length; ++i) { 872 | + struct swaybar_dbusmenu_menu_item *item = items->items[i]; 873 | + struct swaybar_dbusmenu_hotspot *hotspot = &item->hotspot; 874 | + hotspot->x = 0; 875 | + hotspot->width = *surface_width; 876 | + if (item->is_separator) { 877 | + int y = hotspot->y + hotspot->height / 2.0; 878 | + cairo_move_to(cairo, *surface_x, y); 879 | + cairo_line_to(cairo, *surface_x + *surface_width, y); 880 | + cairo_stroke(cairo); 881 | + } else if (!open && item->enabled && 882 | + is_in_hotspot(hotspot, 883 | + tray->menu->seat->pointer.x * output->scale, 884 | + tray->menu->seat->pointer.y * output->scale)) { 885 | + cairo_save(cairo); 886 | + cairo_set_operator(cairo, CAIRO_OPERATOR_DEST_OVER); 887 | + cairo_rectangle(cairo, *surface_x, hotspot->y, *surface_width, 888 | + hotspot->height); 889 | + cairo_set_source_u32(cairo, 890 | + sni->tray->bar->config->colors.focused_separator); 891 | + cairo_fill(cairo); 892 | + cairo_restore(cairo); 893 | + } 894 | + } 895 | +} 896 | + 897 | +struct swaybar_dbusmenu_menu *find_menu_id(struct swaybar_dbusmenu_menu *menu, 898 | + int id) { 899 | + if (!menu) { 900 | + return NULL; 901 | + } 902 | + if (menu->item_id == id) { 903 | + return menu; 904 | + } 905 | + 906 | + if (menu->items) { 907 | + for (int i = 0; i < menu->items->length; ++i) { 908 | + struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; 909 | + struct swaybar_dbusmenu_menu *child_menu = item->submenu; 910 | + if (child_menu) { 911 | + if (child_menu->item_id == id) { 912 | + return child_menu; 913 | + } 914 | + if (child_menu->item_id == 0) { 915 | + continue; 916 | + } 917 | + struct swaybar_dbusmenu_menu *child_child_menu = find_menu_id(child_menu, id); 918 | + if (child_child_menu) { 919 | + return child_child_menu; 920 | + } 921 | + } 922 | + } 923 | + } 924 | + 925 | + return NULL; 926 | +} 927 | + 928 | +static void swaybar_dbusmenu_draw_menu(struct swaybar_dbusmenu_menu *menu, 929 | + int id, bool open) { 930 | + // For now just search for menu with id 931 | + struct swaybar_tray *tray = menu->dbusmenu->sni->tray; 932 | + menu = find_menu_id(menu->dbusmenu->menu, id); 933 | + if (!menu) { 934 | + return; 935 | + } 936 | + 937 | + if (!menu->surface) { 938 | + menu->surface = swaybar_dbusmenu_surface_create(); 939 | + if (!menu->surface) { 940 | + sway_log(SWAY_ERROR, "Could not create surface for menu %d", menu->item_id); 941 | + return; 942 | + } 943 | + } 944 | + 945 | + cairo_surface_t *recorder = 946 | + cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, NULL); 947 | + if (!recorder) { 948 | + return; 949 | + } 950 | + cairo_t *cairo = cairo_create(recorder); 951 | + if (!cairo) { 952 | + cairo_surface_destroy(recorder); 953 | + return; 954 | + } 955 | + int surface_x, surface_y, surface_width, surface_height = 0; 956 | + draw_menu_items(cairo, menu, &surface_x, &surface_y, &surface_width, 957 | + &surface_height, open); 958 | + 959 | + struct swaybar *bar = menu->dbusmenu->sni->tray->bar; 960 | + struct swaybar_dbusmenu_surface *dbusmenu_surface = menu->surface; 961 | + dbusmenu_surface->current_buffer = get_next_buffer( 962 | + bar->shm, dbusmenu_surface->buffers, surface_width, surface_height); 963 | + 964 | + if (!dbusmenu_surface->current_buffer) { 965 | + cairo_surface_destroy(recorder); 966 | + cairo_destroy(cairo); 967 | + return; 968 | + } 969 | + 970 | + cairo_t *shm = dbusmenu_surface->current_buffer->cairo; 971 | + cairo_set_operator(shm, CAIRO_OPERATOR_SOURCE); 972 | + cairo_set_source_u32( 973 | + shm, menu->dbusmenu->sni->tray->bar->config->colors.focused_background); 974 | + cairo_paint(shm); 975 | + 976 | + cairo_set_operator(shm, CAIRO_OPERATOR_OVER); 977 | + cairo_set_source_surface(shm, recorder, -surface_x, -surface_y); 978 | + cairo_paint(shm); 979 | + 980 | + cairo_surface_destroy(recorder); 981 | + cairo_destroy(cairo); 982 | + 983 | + if (dbusmenu_surface->width != surface_width || 984 | + dbusmenu_surface->height != surface_height) { 985 | + if (dbusmenu_surface->surface) { 986 | + xdg_surface_destroy(dbusmenu_surface->xdg_surface); 987 | + dbusmenu_surface->xdg_surface = NULL; 988 | + wl_surface_destroy(dbusmenu_surface->surface); 989 | + dbusmenu_surface->surface = NULL; 990 | + sway_log(SWAY_DEBUG, "Destroy xdg popup"); 991 | + xdg_popup_destroy(dbusmenu_surface->xdg_popup); 992 | + dbusmenu_surface->xdg_popup = NULL; 993 | + } 994 | + 995 | + // configure & position popup surface 996 | + struct wl_surface *surface = wl_compositor_create_surface(bar->compositor); 997 | + struct xdg_surface *xdg_surface = 998 | + xdg_wm_base_get_xdg_surface(menu->dbusmenu->bar->wm_base, surface); 999 | + struct xdg_positioner *positioner = 1000 | + xdg_wm_base_create_positioner(menu->dbusmenu->bar->wm_base); 1001 | + 1002 | + // find the menu item (if any) which requested to open this submenu 1003 | + // to find out on which x and y coordinate the submenu should be drawn 1004 | + struct swaybar_dbusmenu_menu_item *item = 1005 | + find_item(menu->dbusmenu, menu->item_id); 1006 | + struct swaybar_output *output = menu->dbusmenu->output; 1007 | + int x = menu->item_id == 0 ? menu->dbusmenu->x 1008 | + : item->hotspot.x / output->scale; 1009 | + int y = menu->item_id == 0 ? menu->dbusmenu->y 1010 | + : item->hotspot.y / output->scale; 1011 | + 1012 | + xdg_positioner_set_offset(positioner, 0, 0); 1013 | + // Need to divide through scale because surface width/height is scaled 1014 | + xdg_positioner_set_size(positioner, surface_width / output->scale, 1015 | + surface_height / output->scale); 1016 | + 1017 | + int padding = (tray->bar->config->tray_padding * output->scale) / 2; 1018 | + if (bar->config->position & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { // top bar 1019 | + xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_BOTTOM_LEFT); 1020 | + xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_LEFT); 1021 | + xdg_positioner_set_anchor_rect(positioner, x, y - padding, 1, 1); 1022 | + } else { 1023 | + xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); 1024 | + xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_TOP_LEFT); 1025 | + xdg_positioner_set_anchor_rect( 1026 | + positioner, x, item->hotspot.height / output->scale, 1, 1); 1027 | + } 1028 | + 1029 | + struct xdg_popup *xdg_popup; 1030 | + struct swaybar_dbusmenu_menu *parent_menu = find_parent_menu(menu); 1031 | + if (!parent_menu) { 1032 | + // Top level menu 1033 | + xdg_popup = xdg_surface_get_popup(xdg_surface, NULL, positioner); 1034 | + zwlr_layer_surface_v1_get_popup(output->layer_surface, xdg_popup); 1035 | + } else { 1036 | + // Nested menu 1037 | + xdg_popup = xdg_surface_get_popup( 1038 | + xdg_surface, parent_menu->surface->xdg_surface, positioner); 1039 | + } 1040 | + xdg_positioner_destroy(positioner); 1041 | + 1042 | + xdg_popup_grab(xdg_popup, menu->dbusmenu->seat->wl_seat, 1043 | + menu->dbusmenu->serial); 1044 | + xdg_popup_add_listener(xdg_popup, &xdg_popup_listener, menu); 1045 | + xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, menu); 1046 | + wl_surface_commit(surface); 1047 | + 1048 | + dbusmenu_surface->xdg_popup = xdg_popup; 1049 | + dbusmenu_surface->xdg_surface = xdg_surface; 1050 | + dbusmenu_surface->surface = surface; 1051 | + dbusmenu_surface->width = surface_width; 1052 | + dbusmenu_surface->height = surface_height; 1053 | + dbusmenu_surface->configured = false; 1054 | + } 1055 | + 1056 | + commit_menu_surface(menu); 1057 | +} 1058 | + 1059 | +static void swaybar_dbusmenu_draw(struct swaybar_dbusmenu *dbusmenu, int id) { 1060 | + if (!dbusmenu || !dbusmenu->menu) { 1061 | + sway_log(SWAY_ERROR, "Can not draw dbusmenu, menu structure not initialized yet!"); 1062 | + return; 1063 | + } 1064 | + swaybar_dbusmenu_draw_menu(dbusmenu->menu, id, true); 1065 | +} 1066 | + 1067 | +static cairo_status_t read_png_stream(void *closure, unsigned char *data, 1068 | + unsigned int length) { 1069 | + struct png_stream *png_stream = closure; 1070 | + if (length > png_stream->left) { 1071 | + return CAIRO_STATUS_READ_ERROR; 1072 | + } 1073 | + memcpy(data, png_stream->data, length); 1074 | + png_stream->data += length; 1075 | + png_stream->left -= length; 1076 | + return CAIRO_STATUS_SUCCESS; 1077 | +} 1078 | + 1079 | +static cairo_surface_t *read_png(const void *data, size_t data_size) { 1080 | + struct png_stream png_stream = {0}; 1081 | + png_stream.data = data; 1082 | + png_stream.left = data_size; 1083 | + cairo_surface_t *surface = 1084 | + cairo_image_surface_create_from_png_stream(read_png_stream, &png_stream); 1085 | + 1086 | + if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) { 1087 | + return surface; 1088 | + } 1089 | + 1090 | + cairo_surface_destroy(surface); 1091 | + return NULL; 1092 | +} 1093 | + 1094 | +static int about_to_show_callback(sd_bus_message *msg, void *data, 1095 | + sd_bus_error *error) { 1096 | + struct swaybar_sni_slot *slot = data; 1097 | + struct swaybar_sni *sni = slot->sni; 1098 | + int menu_id = slot->menu_id; 1099 | + wl_list_remove(&slot->link); 1100 | + free(slot); 1101 | + 1102 | + int need_update; 1103 | + sd_bus_message_read_basic(msg, 'b', &need_update); 1104 | + if (need_update) { 1105 | + swaybar_dbusmenu_get_layout(sni->tray->menu, menu_id); 1106 | + } 1107 | + 1108 | + swaybar_dbusmenu_draw(sni->tray->menu, menu_id); 1109 | + 1110 | + sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->menu, 1111 | + menu_interface, "Event", NULL, NULL, "isvu", menu_id, "opened", "y", 0, 1112 | + time(NULL)); 1113 | + 1114 | + sway_log(SWAY_DEBUG, "%s%s opened id %d", sni->service, sni->menu, menu_id); 1115 | + 1116 | + return 0; 1117 | +} 1118 | + 1119 | +static void open_menu_id(struct swaybar_dbusmenu *dbusmenu, int menu_id) { 1120 | + struct swaybar_dbusmenu_menu *menu = find_menu_id(dbusmenu->menu, menu_id); 1121 | + if (!menu || menu->surface) { 1122 | + // menu could not be found or is already shown 1123 | + return; 1124 | + } 1125 | + 1126 | + struct swaybar_sni *sni = dbusmenu->sni; 1127 | + struct swaybar_sni_slot *slot = calloc(1, sizeof(struct swaybar_sni_slot)); 1128 | + slot->sni = sni; 1129 | + slot->menu_id = menu_id; 1130 | + 1131 | + int ret = sd_bus_call_method_async(sni->tray->bus, &slot->slot, sni->service, 1132 | + sni->menu, menu_interface, "AboutToShow", about_to_show_callback, slot, "i", 1133 | + menu_id); 1134 | + 1135 | + if (ret >= 0) { 1136 | + wl_list_insert(&sni->slots, &slot->link); 1137 | + } else { 1138 | + sway_log(SWAY_ERROR, "%s%s failed to send AboutToShow signal: %s", 1139 | + sni->service, sni->menu, strerror(-ret)); 1140 | + free(slot); 1141 | + } 1142 | +} 1143 | + 1144 | +static int update_item_properties(struct swaybar_dbusmenu_menu_item *item, 1145 | + sd_bus_message *msg) { 1146 | + sd_bus_message_enter_container(msg, 'a', "{sv}"); 1147 | + while (!sd_bus_message_at_end(msg, 0)) { 1148 | + sd_bus_message_enter_container(msg, 'e', "sv"); 1149 | + char *key, *log_value; 1150 | + sd_bus_message_read_basic(msg, 's', &key); 1151 | + if (strcmp(key, "type") == 0) { 1152 | + char *type; 1153 | + sd_bus_message_read(msg, "v", "s", &type); 1154 | + item->is_separator = strcmp(type, "separator") == 0; 1155 | + log_value = type; 1156 | + } else if (strcmp(key, "label") == 0) { 1157 | + char *label; 1158 | + sd_bus_message_read(msg, "v", "s", &label); 1159 | + item->label = realloc(item->label, strlen(label) + 1); 1160 | + if (!item->label) { 1161 | + return -ENOMEM; 1162 | + } 1163 | + int i = 0; 1164 | + for (char *c = label; *c; ++c) { 1165 | + if (*c == '_' && !*++c) { 1166 | + break; 1167 | + } 1168 | + item->label[i++] = *c; 1169 | + } 1170 | + item->label[i] = '\0'; 1171 | + log_value = label; 1172 | + } else if (strcmp(key, "enabled") == 0) { 1173 | + int enabled; 1174 | + sd_bus_message_read(msg, "v", "b", &enabled); 1175 | + item->enabled = enabled; 1176 | + log_value = item->enabled ? "true" : "false"; 1177 | + } else if (strcmp(key, "visible") == 0) { 1178 | + int visible; 1179 | + sd_bus_message_read(msg, "v", "b", &visible); 1180 | + item->visible = visible; 1181 | + log_value = item->visible ? "true" : "false"; 1182 | + } else if (strcmp(key, "icon-name") == 0) { 1183 | + sd_bus_message_read(msg, "v", "s", &item->icon_name); 1184 | + item->icon_name = strdup(item->icon_name); 1185 | + log_value = item->icon_name; 1186 | + } else if (strcmp(key, "icon-data") == 0) { 1187 | + const void *data; 1188 | + size_t data_size; 1189 | + sd_bus_message_enter_container(msg, 'v', "ay"); 1190 | + sd_bus_message_read_array(msg, 'y', &data, &data_size); 1191 | + sd_bus_message_exit_container(msg); 1192 | + item->icon_data = read_png(data, data_size); 1193 | + log_value = item->icon_data ? "" : ""; 1194 | + } else if (strcmp(key, "toggle-type") == 0) { 1195 | + char *toggle_type; 1196 | + sd_bus_message_read(msg, "v", "s", &toggle_type); 1197 | + if (strcmp(toggle_type, "checkmark") == 0) { 1198 | + item->toggle_type = MENU_CHECKMARK; 1199 | + } else if (strcmp(toggle_type, "radio") == 0) { 1200 | + item->toggle_type = MENU_RADIO; 1201 | + } 1202 | + log_value = toggle_type; 1203 | + } else if (strcmp(key, "toggle-state") == 0) { 1204 | + sd_bus_message_read(msg, "v", "i", &item->toggle_state); 1205 | + log_value = item->toggle_state == 0 ? 1206 | + "off" : item->toggle_state == 1 ? "on" : "indeterminate"; 1207 | + } else if (strcmp(key, "children-display") == 0) { 1208 | + char *children_display; 1209 | + sd_bus_message_read(msg, "v", "s", &children_display); 1210 | + if (strcmp(children_display, "submenu") == 0) { 1211 | + struct swaybar_dbusmenu_menu *submenu; 1212 | + if (item->id != 0) { 1213 | + submenu = calloc(1, sizeof(struct swaybar_dbusmenu_menu)); 1214 | + if (!submenu) { 1215 | + sway_log(SWAY_ERROR, "Could not allocate submenu"); 1216 | + return -ENOMEM; 1217 | + } 1218 | + } else { 1219 | + submenu = item->menu; 1220 | + } 1221 | + submenu->item_id = item->id; 1222 | + submenu->dbusmenu = item->menu->dbusmenu; 1223 | + item->submenu = submenu; 1224 | + } 1225 | + log_value = children_display; 1226 | + } else { 1227 | + // Ignored: shortcut, disposition 1228 | + sd_bus_message_skip(msg, "v"); 1229 | + log_value = ""; 1230 | + } 1231 | + sd_bus_message_exit_container(msg); 1232 | + sway_log(SWAY_DEBUG, "%s%s %s = '%s'", item->menu->dbusmenu->sni->service, 1233 | + item->menu->dbusmenu->sni->menu, key, log_value); 1234 | + } 1235 | + return sd_bus_message_exit_container(msg); 1236 | +} 1237 | + 1238 | +static int get_layout_callback(sd_bus_message *msg, void *data, 1239 | + sd_bus_error *error) { 1240 | + struct swaybar_sni_slot *slot = data; 1241 | + struct swaybar_sni *sni = slot->sni; 1242 | + int menu_id = slot->menu_id; 1243 | + wl_list_remove(&slot->link); 1244 | + free(slot); 1245 | + 1246 | + struct swaybar_dbusmenu *dbusmenu = sni->tray->menu; 1247 | + if (dbusmenu == NULL) { 1248 | + return 0; 1249 | + } 1250 | + 1251 | + if (sd_bus_message_is_method_error(msg, NULL)) { 1252 | + sway_log(SWAY_ERROR, "%s%s failed to get layout: %s", 1253 | + dbusmenu->sni->service, dbusmenu->sni->menu, 1254 | + sd_bus_message_get_error(msg)->message); 1255 | + return sd_bus_message_get_errno(msg); 1256 | + } 1257 | + 1258 | + // Parse the layout. The layout comes as a recursive structure as 1259 | + // dbus message in the following form (ia{sv}av) 1260 | + 1261 | + // Skip the menu revision 1262 | + sd_bus_message_skip(msg, "u"); 1263 | + 1264 | + sni->tray->menu_pointer_focus = NULL; 1265 | + 1266 | + bool already_open = false; 1267 | + struct swaybar_dbusmenu_menu *menu_to_update = 1268 | + find_menu_id(dbusmenu->menu, menu_id); 1269 | + if (menu_to_update && menu_to_update->surface) { 1270 | + already_open = true; 1271 | + } 1272 | + 1273 | + if (dbusmenu->menu) { 1274 | + close_menus(dbusmenu->menu); 1275 | + swaybar_dbusmenu_menu_destroy(dbusmenu->menu); 1276 | + dbusmenu->menu = NULL; 1277 | + } 1278 | + 1279 | + struct swaybar_dbusmenu_menu_item *parent_item = NULL; 1280 | + struct swaybar_dbusmenu_menu *menu = calloc(1, 1281 | + sizeof(struct swaybar_dbusmenu_menu)); 1282 | + if (!menu) { 1283 | + sway_log(SWAY_ERROR, "Could not allocate menu"); 1284 | + return -ENOMEM; 1285 | + } 1286 | + dbusmenu->menu = menu; 1287 | + menu->dbusmenu = dbusmenu; 1288 | + int ret = 0; 1289 | + while (!sd_bus_message_at_end(msg, 1)) { 1290 | + sd_bus_message_enter_container(msg, 'r', "ia{sv}av"); 1291 | + 1292 | + struct swaybar_dbusmenu_menu_item *item 1293 | + = calloc(1, sizeof(struct swaybar_dbusmenu_menu_item)); 1294 | + if (!item) { 1295 | + ret = -ENOMEM; 1296 | + break; 1297 | + } 1298 | + 1299 | + // default properties 1300 | + item->parent_item = parent_item; 1301 | + item->menu = menu; 1302 | + item->enabled = true; 1303 | + item->visible = true; 1304 | + item->toggle_state = -1; 1305 | + 1306 | + // Read the id 1307 | + sd_bus_message_read_basic(msg, 'i', &item->id); 1308 | + 1309 | + // Process a{sv}. a{sv} contains key-value pairs 1310 | + ret = update_item_properties(item, msg); 1311 | + if (!menu->items) { 1312 | + menu->items = create_list(); 1313 | + } 1314 | + list_add(menu->items, item); 1315 | + if (ret < 0) { 1316 | + break; 1317 | + } 1318 | + if (item->id != 0 && item->submenu) { 1319 | + menu = item->submenu; 1320 | + } 1321 | + 1322 | + sd_bus_message_enter_container(msg, 'a', "v"); 1323 | + 1324 | + parent_item = item; 1325 | + while (parent_item && sd_bus_message_at_end(msg, 0)) { 1326 | + if (parent_item->submenu) { 1327 | + menu = find_parent_menu(menu); 1328 | + } 1329 | + parent_item = parent_item->parent_item; 1330 | + 1331 | + sd_bus_message_exit_container(msg); 1332 | + sd_bus_message_exit_container(msg); 1333 | + sd_bus_message_exit_container(msg); 1334 | + } 1335 | + 1336 | + if (parent_item) { 1337 | + sd_bus_message_enter_container(msg, 'v', "(ia{sv}av)"); 1338 | + } 1339 | + } 1340 | + 1341 | + if (already_open) { 1342 | + swaybar_dbusmenu_draw(sni->tray->menu, menu_id); 1343 | + } else { 1344 | + open_menu_id(dbusmenu, 0); 1345 | + } 1346 | + 1347 | + return 0; 1348 | +} 1349 | + 1350 | +static void swaybar_dbusmenu_subscribe_signal(struct swaybar_dbusmenu *menu, 1351 | + const char *signal_name, sd_bus_message_handler_t callback) { 1352 | + int ret = sd_bus_match_signal_async( menu->sni->tray->bus, NULL, 1353 | + menu->sni->service, menu->sni->menu, menu_interface, signal_name, callback, 1354 | + NULL, menu->sni); 1355 | + 1356 | + if (ret < 0) { 1357 | + sway_log(SWAY_ERROR, "%s%s failed to subscribe to signal %s: %s", 1358 | + menu->sni->service, menu->sni->menu, signal_name, strerror(-ret)); 1359 | + } 1360 | +} 1361 | + 1362 | +static void swaybar_dbusmenu_setup_signals(struct swaybar_dbusmenu *menu) { 1363 | + swaybar_dbusmenu_subscribe_signal(menu, "ItemsPropertiesUpdated", 1364 | + handle_items_properties_updated); 1365 | + swaybar_dbusmenu_subscribe_signal(menu, "LayoutUpdated", 1366 | + handle_layout_updated); 1367 | + swaybar_dbusmenu_subscribe_signal(menu, "ItemActivationRequested", 1368 | + handle_item_activation_requested); 1369 | +} 1370 | + 1371 | +static void swaybar_dbusmenu_get_layout(struct swaybar_dbusmenu *menu, int id) { 1372 | + if (menu == NULL) { 1373 | + return; 1374 | + } 1375 | + 1376 | + struct swaybar_sni_slot *slot = calloc(1, sizeof(struct swaybar_sni_slot)); 1377 | + if (slot == NULL) { 1378 | + sway_log(SWAY_ERROR, "Could not allocate swaybar_sni_slot"); 1379 | + return; 1380 | + } 1381 | + slot->sni = menu->sni; 1382 | + slot->menu_id = id; 1383 | + 1384 | + int ret = 1385 | + sd_bus_call_method_async(menu->sni->tray->bus, NULL, menu->sni->service, 1386 | + menu->sni->menu, menu_interface, "GetLayout", 1387 | + get_layout_callback, slot, "iias", id, -1, NULL); 1388 | + 1389 | + if (ret >= 0) { 1390 | + wl_list_insert(&menu->sni->slots, &slot->link); 1391 | + } else { 1392 | + sway_log(SWAY_ERROR, "%s%s failed to call method GetLayout: %s", 1393 | + menu->sni->service, menu->sni->menu, strerror(-ret)); 1394 | + free(slot); 1395 | + } 1396 | +} 1397 | + 1398 | +static void swaybar_dbusmenu_get_layout_root(struct swaybar_dbusmenu *menu) { 1399 | + swaybar_dbusmenu_get_layout(menu, 0); 1400 | +} 1401 | + 1402 | +static int get_icon_theme_path_callback(sd_bus_message *msg, void *data, 1403 | + sd_bus_error *error) { 1404 | + struct swaybar_sni_slot *slot = data; 1405 | + struct swaybar_sni *sni = slot->sni; 1406 | + wl_list_remove(&slot->link); 1407 | + free(slot); 1408 | + 1409 | + int ret; 1410 | + if (!sd_bus_message_is_method_error(msg, NULL)) { 1411 | + ret = sd_bus_message_enter_container(msg, 'v', NULL); 1412 | + if (ret >= 0) { 1413 | + ret = sd_bus_message_read_strv(msg, &sni->menu_icon_theme_paths); 1414 | + } 1415 | + } else { 1416 | + ret = -sd_bus_message_get_errno(msg); 1417 | + } 1418 | + 1419 | + if (ret < 0) { 1420 | + sway_log(SWAY_ERROR, "%s%s failed to read IconThemePath: %s", sni->service, 1421 | + sni->menu, strerror(-ret)); 1422 | + } 1423 | + return ret; 1424 | +} 1425 | + 1426 | +static void swaybar_dbusmenu_setup(struct swaybar_dbusmenu *menu) { 1427 | + struct swaybar_sni_slot *slot = calloc(1, sizeof(struct swaybar_sni_slot)); 1428 | + slot->sni = menu->sni; 1429 | + int ret = sd_bus_call_method_async( menu->sni->tray->bus, &slot->slot, 1430 | + menu->sni->service, menu->sni->path, "org.freedesktop.DBus.Properties", 1431 | + "Get", get_icon_theme_path_callback, slot, "ss", menu->sni->interface, 1432 | + "IconThemePath"); 1433 | + if (ret >= 0) { 1434 | + wl_list_insert(&menu->sni->slots, &slot->link); 1435 | + } else { 1436 | + sway_log(SWAY_ERROR, "%s%s failed to get IconThemePath: %s", 1437 | + menu->sni->service, menu->sni->menu, strerror(-ret)); 1438 | + free(slot); 1439 | + } 1440 | + 1441 | + swaybar_dbusmenu_setup_signals(menu); 1442 | + swaybar_dbusmenu_get_layout_root(menu); 1443 | +} 1444 | + 1445 | +void swaybar_dbusmenu_open(struct swaybar_sni *sni, 1446 | + struct swaybar_output *output, struct swaybar_seat *seat, uint32_t serial, 1447 | + int x, int y) { 1448 | + struct swaybar_dbusmenu *dbusmenu = sni->tray->menu; 1449 | + if (!dbusmenu) { 1450 | + dbusmenu = calloc(1, sizeof(struct swaybar_dbusmenu)); 1451 | + if (!dbusmenu) { 1452 | + sway_log(SWAY_DEBUG, "Could not allocate dbusmenu"); 1453 | + return; 1454 | + } 1455 | + sni->tray->menu = dbusmenu; 1456 | + } 1457 | + 1458 | + dbusmenu->sni = sni; 1459 | + dbusmenu->output = output; 1460 | + dbusmenu->seat = seat; 1461 | + dbusmenu->serial = serial; 1462 | + dbusmenu->x = seat->pointer.x; 1463 | + dbusmenu->y = seat->pointer.y; 1464 | + dbusmenu->bar = output->bar; 1465 | + 1466 | + swaybar_dbusmenu_setup(dbusmenu); 1467 | +} 1468 | + 1469 | +static void close_child_menus_except(struct swaybar_dbusmenu_menu *menu, 1470 | + int id) { 1471 | + if (!menu || !menu->items) { 1472 | + return; 1473 | + } 1474 | + // close all child menus of menu, except the child menu with the given id 1475 | + for (int i = 0; i < menu->items->length; ++i) { 1476 | + struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; 1477 | + if (item->id == id) { 1478 | + continue; 1479 | + } 1480 | + struct swaybar_dbusmenu_menu *menu = item->submenu; 1481 | + if (menu && menu->item_id != 0) { 1482 | + close_menus(menu); 1483 | + } 1484 | + } 1485 | +} 1486 | + 1487 | +static void 1488 | +pointer_motion_process_item(struct swaybar_dbusmenu_menu *focused_menu, 1489 | + struct swaybar_dbusmenu_menu_item *item, struct swaybar_seat *seat) { 1490 | + int scale = focused_menu->dbusmenu->output->scale; 1491 | + double x = seat->pointer.x * scale; 1492 | + double y = seat->pointer.y * scale; 1493 | + if (is_in_hotspot(&item->hotspot, x, y) && item->enabled && 1494 | + !item->is_separator) { 1495 | + struct swaybar_tray *tray = focused_menu->dbusmenu->sni->tray; 1496 | + struct swaybar_sni *sni = tray->menu->sni; 1497 | + if (focused_menu->last_hovered_item != item) { 1498 | + sd_bus_call_method_async(tray->bus, NULL, sni->service, sni->menu, 1499 | + menu_interface, "Event", NULL, NULL, "isvu", item->id, "hovered", 1500 | + "y", 0, time(NULL)); 1501 | + 1502 | + sway_log(SWAY_DEBUG, "%s%s hovered id %d", sni->service, sni->menu, 1503 | + item->id); 1504 | + 1505 | + // open child menu if current item has a child menu and close other 1506 | + // potential open child menus. Only one child menu can be open at a time 1507 | + close_child_menus_except(focused_menu, item->id); 1508 | + open_menu_id(focused_menu->dbusmenu, item->id); 1509 | + focused_menu->last_hovered_item = item; 1510 | + 1511 | + // a different item needs to be highlighted 1512 | + swaybar_dbusmenu_draw_menu(focused_menu, focused_menu->item_id, false); 1513 | + } 1514 | + 1515 | + } 1516 | +} 1517 | + 1518 | +bool dbusmenu_pointer_motion(struct swaybar_seat *seat, 1519 | + struct wl_pointer *wl_pointer, uint32_t time_, wl_fixed_t surface_x, 1520 | + wl_fixed_t surface_y) { 1521 | + struct swaybar_tray *tray = seat->bar->tray; 1522 | + struct swaybar_dbusmenu_menu *focused_menu = tray->menu_pointer_focus; 1523 | + if (!(tray && tray->menu && focused_menu)) { 1524 | + return false; 1525 | + } 1526 | + 1527 | + for (int i = 0; i < focused_menu->items->length; ++i) { 1528 | + struct swaybar_dbusmenu_menu_item *item = focused_menu->items->items[i]; 1529 | + pointer_motion_process_item(focused_menu, item, seat); 1530 | + } 1531 | + 1532 | + return true; 1533 | +} 1534 | + 1535 | +static struct swaybar_dbusmenu_menu * 1536 | +dbusmenu_menu_find_menu_surface(struct swaybar_dbusmenu_menu *menu, 1537 | + struct wl_surface *surface) { 1538 | + if (menu->surface && menu->surface->surface == surface) { 1539 | + return menu; 1540 | + } 1541 | + if (!menu->items) { 1542 | + return NULL; 1543 | + } 1544 | + for (int i = 0; i < menu->items->length; ++i) { 1545 | + struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; 1546 | + struct swaybar_dbusmenu_menu *child_menu = item->submenu; 1547 | + if (child_menu && child_menu->surface 1548 | + && child_menu->surface->surface == surface) { 1549 | + return child_menu; 1550 | + } 1551 | + if (child_menu) { 1552 | + if (child_menu->item_id == 0) { 1553 | + continue; 1554 | + } 1555 | + struct swaybar_dbusmenu_menu *child_child_menu = 1556 | + dbusmenu_menu_find_menu_surface(child_menu, surface); 1557 | + if (child_child_menu != NULL) { 1558 | + return child_child_menu; 1559 | + } 1560 | + } 1561 | + } 1562 | + 1563 | + return NULL; 1564 | +} 1565 | + 1566 | +static void close_menus_by_id(struct swaybar_dbusmenu_menu *menu, int item_id) { 1567 | + if (menu->items == NULL) { 1568 | + return; 1569 | + } 1570 | + for (int i = 0; i < menu->items->length; ++i) { 1571 | + struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; 1572 | + struct swaybar_dbusmenu_menu *child_menu = item->submenu; 1573 | + if (child_menu && child_menu->item_id == item_id && child_menu->item_id != 0) { 1574 | + close_menus(child_menu); 1575 | + } 1576 | + } 1577 | +} 1578 | + 1579 | +static void close_unfocused_child_menus(struct swaybar_dbusmenu_menu *menu, 1580 | + struct swaybar_seat *seat) { 1581 | + for (int i = 0; i < menu->items->length; ++i) { 1582 | + struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; 1583 | + 1584 | + int scale = menu->dbusmenu->output->scale; 1585 | + int x = seat->pointer.x * scale; 1586 | + int y = seat->pointer.y * scale; 1587 | + if (item->submenu && item->submenu->item_id != 0 1588 | + && !is_in_hotspot(&item->hotspot, x, y)) { 1589 | + close_menus_by_id(menu, item->id); 1590 | + } 1591 | + } 1592 | +} 1593 | + 1594 | +bool dbusmenu_pointer_frame(struct swaybar_seat *data, 1595 | + struct wl_pointer *wl_pointer) { 1596 | + struct swaybar_tray *tray = data->bar->tray; 1597 | + if (!(tray && tray->menu && tray->menu_pointer_focus)) { 1598 | + return false; 1599 | + } 1600 | + return true; 1601 | +} 1602 | + 1603 | +bool dbusmenu_pointer_axis(struct swaybar_seat *data, 1604 | + struct wl_pointer *wl_pointer) { 1605 | + struct swaybar_tray *tray = data->bar->tray; 1606 | + if (!(tray && tray->menu && tray->menu_pointer_focus)) { 1607 | + return false; 1608 | + } 1609 | + return true; 1610 | +} 1611 | + 1612 | +bool dbusmenu_pointer_enter(void *data, struct wl_pointer *wl_pointer, 1613 | + uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, 1614 | + wl_fixed_t surface_y) { 1615 | + struct swaybar_seat *seat = data; 1616 | + struct swaybar_tray *tray = seat->bar->tray; 1617 | + if (!(tray && tray->menu)) { 1618 | + return false; 1619 | + } 1620 | + 1621 | + struct swaybar_dbusmenu_menu *new_focused_menu = 1622 | + dbusmenu_menu_find_menu_surface(tray->menu->menu, surface); 1623 | + 1624 | + // Check if there are any child menus 1625 | + bool has_child_menus = false; 1626 | + if (new_focused_menu && new_focused_menu->items) { 1627 | + for (int i = 0; i < new_focused_menu->items->length; ++i) { 1628 | + struct swaybar_dbusmenu_menu_item *item = new_focused_menu->items->items[i]; 1629 | + if (item->submenu && item->submenu->item_id != 0) { 1630 | + has_child_menus = true; 1631 | + } 1632 | + } 1633 | + } 1634 | + 1635 | + if (has_child_menus) { 1636 | + close_unfocused_child_menus(new_focused_menu, seat); 1637 | + } 1638 | + 1639 | + tray->menu_pointer_focus = new_focused_menu; 1640 | + 1641 | + return true; 1642 | +} 1643 | + 1644 | +bool dbusmenu_pointer_leave(void *data, struct wl_pointer *wl_pointer, 1645 | + uint32_t serial, struct wl_surface *surface) { 1646 | + struct swaybar_seat *seat = data; 1647 | + struct swaybar_tray *tray = seat->bar->tray; 1648 | + if (!(tray && tray->menu)) { 1649 | + return false; 1650 | + } 1651 | + 1652 | + tray->menu_pointer_focus = NULL; 1653 | + 1654 | + return true; 1655 | +} 1656 | + 1657 | +static bool dbusmenu_pointer_button_left_process_item(struct swaybar_dbusmenu *dbusmenu, 1658 | + struct swaybar_dbusmenu_menu_item *item, struct swaybar_seat *seat) { 1659 | + struct swaybar_sni *sni = dbusmenu->sni; 1660 | + struct swaybar_tray *tray = sni->tray; 1661 | + int scale = dbusmenu->output->scale; 1662 | + 1663 | + if (is_in_hotspot(&item->hotspot, seat->pointer.x * scale, 1664 | + seat->pointer.y * scale)) { 1665 | + if (!item->enabled || item->is_separator) { 1666 | + return false; 1667 | + } 1668 | + 1669 | + sway_log(SWAY_DEBUG, "%s%s menu clicked id %d", sni->service, sni->menu, 1670 | + item->id); 1671 | + 1672 | + sd_bus_call_method_async(tray->bus, NULL, sni->service, sni->menu, 1673 | + menu_interface, "Event", NULL, NULL, "isvu", item->id, "clicked", "y", 0, 1674 | + time(NULL)); 1675 | + 1676 | + if (item->submenu) { 1677 | + open_menu_id(dbusmenu, item->id); 1678 | + } else { 1679 | + // The user clicked an menu item other than a submenu. That means 1680 | + // the user made it's choise. Close the tray menu. 1681 | + swaybar_dbusmenu_destroy(tray->menu); 1682 | + } 1683 | + return true; 1684 | + } 1685 | + 1686 | + return false; 1687 | +} 1688 | + 1689 | +static bool dbusmenu_pointer_button_left(struct swaybar_dbusmenu *dbusmenu, 1690 | + struct swaybar_seat *seat) { 1691 | + struct swaybar_dbusmenu_menu *focused_menu 1692 | + = dbusmenu->sni->tray->menu_pointer_focus; 1693 | + 1694 | + if (!focused_menu) { 1695 | + return true; 1696 | + } 1697 | + 1698 | + for (int i = 0; i < focused_menu->items->length; ++i) { 1699 | + struct swaybar_dbusmenu_menu_item *item = focused_menu->items->items[i]; 1700 | + if (dbusmenu_pointer_button_left_process_item(dbusmenu, item, seat)) { 1701 | + return true; 1702 | + } 1703 | + } 1704 | + 1705 | + return true; 1706 | +} 1707 | + 1708 | +bool dbusmenu_pointer_button(void *data, struct wl_pointer *wl_pointer, 1709 | + uint32_t serial, uint32_t time_, uint32_t button, uint32_t state) { 1710 | + struct swaybar_seat *seat = data; 1711 | + struct swaybar_tray *tray = seat->bar->tray; 1712 | + if (!(tray && tray->menu)) { 1713 | + return false; 1714 | + } 1715 | + 1716 | + if (state != WL_POINTER_BUTTON_STATE_PRESSED) { 1717 | + // intentionally left blank 1718 | + return true; 1719 | + } else if (!tray->menu_pointer_focus) { 1720 | + swaybar_dbusmenu_destroy(tray->menu); 1721 | + return true; 1722 | + } else if (button == BTN_LEFT) { 1723 | + return dbusmenu_pointer_button_left(tray->menu, seat); 1724 | + } 1725 | + 1726 | + return false; 1727 | +} 1728 | --------------------------------------------------------------------------------