nixos: add river xmonad session
This commit is contained in:
322
dotfiles/config/river-xmonad/Main.hs
Normal file
322
dotfiles/config/river-xmonad/Main.hs
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Control.Concurrent (forkIO)
|
||||||
|
import Data.Bits ((.&.), complement)
|
||||||
|
import Data.Function (on)
|
||||||
|
import Data.List (minimumBy)
|
||||||
|
import qualified Data.Map.Strict as M
|
||||||
|
import Data.Maybe (mapMaybe)
|
||||||
|
import Graphics.X11.ExtraTypes.XF86
|
||||||
|
import System.IO (hFlush, stdout)
|
||||||
|
import System.Process (spawnCommand, waitForProcess)
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Layout.Accordion
|
||||||
|
import XMonad.Layout.Cross
|
||||||
|
import XMonad.Layout.Grid
|
||||||
|
import XMonad.Layout.MultiColumns
|
||||||
|
import qualified XMonad.Layout.Renamed as RN
|
||||||
|
import XMonad.River.WindowManager
|
||||||
|
import XMonad.River.WindowManager.Wayland
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
|
data Direction = DirectionUp | DirectionDown | DirectionLeft | DirectionRight
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
let bindings = keyBindings
|
||||||
|
configLog $ "starting imalison-river-xmonad with keybindings=" ++ show (length bindings)
|
||||||
|
initialState <- initialRiverWMState (defaultRiverWMConfig riverLayouts)
|
||||||
|
runRiverWMWaylandConfig
|
||||||
|
RiverWMWaylandConfig
|
||||||
|
{ riverWMWaylandInitialState = initialState
|
||||||
|
, riverWMWaylandKeyBindings = bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
riverLayouts =
|
||||||
|
renamed "4 Columns" (multiCol [1, 1, 1] 2 0.0 (-0.5))
|
||||||
|
||| renamed "3 Columns" (multiCol [1, 1] 2 0.01 (-0.5))
|
||||||
|
||| renamed "Grid" Grid
|
||||||
|
||| renamed "Large Main" (Tall 1 (3 / 100) (3 / 4))
|
||||||
|
||| renamed "2 Columns" (Tall 1 (3 / 100) (1 / 2))
|
||||||
|
||| renamed "Mirror 2 Columns" (Mirror (Tall 1 (3 / 100) (1 / 2)))
|
||||||
|
||| renamed "Accordion" Accordion
|
||||||
|
||| renamed "Cross" simpleCross
|
||||||
|
||| Full
|
||||||
|
where
|
||||||
|
renamed name = RN.renamed [RN.Replace name]
|
||||||
|
|
||||||
|
keyBindings
|
||||||
|
:: (LayoutClass l Window, Read (l Window))
|
||||||
|
=> [RiverWMWaylandKeyBinding l]
|
||||||
|
keyBindings =
|
||||||
|
addHyperChordBindings hyper hyperChord $
|
||||||
|
concat
|
||||||
|
[ directionalBindings super directionalFocus
|
||||||
|
, directionalBindings (super .|. shift) directionalSwap
|
||||||
|
, workspaceBindings
|
||||||
|
, layoutBindings
|
||||||
|
, spawnBindings
|
||||||
|
, mediaBindings
|
||||||
|
]
|
||||||
|
|
||||||
|
directionalBindings
|
||||||
|
:: RiverWMWaylandModifiers
|
||||||
|
-> (Direction -> RiverWMWaylandAction l)
|
||||||
|
-> [RiverWMWaylandKeyBinding l]
|
||||||
|
directionalBindings mods command =
|
||||||
|
[ key mods xK_w (command DirectionUp)
|
||||||
|
, key mods xK_s (command DirectionDown)
|
||||||
|
, key mods xK_a (command DirectionLeft)
|
||||||
|
, key mods xK_d (command DirectionRight)
|
||||||
|
]
|
||||||
|
|
||||||
|
workspaceBindings
|
||||||
|
:: [RiverWMWaylandKeyBinding l]
|
||||||
|
workspaceBindings =
|
||||||
|
[ key (mods .|. super) keysym (stackAction $ command workspace)
|
||||||
|
| (workspace, keysym) <- zip (map show [(1 :: Int) .. 9]) [xK_1 .. xK_9]
|
||||||
|
, (command, mods) <-
|
||||||
|
[ (W.greedyView, noMods)
|
||||||
|
, (W.shift, shift)
|
||||||
|
, (\workspaceId stackSet -> W.greedyView workspaceId (W.shift workspaceId stackSet), ctrl)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
layoutBindings
|
||||||
|
:: (LayoutClass l Window, Read (l Window))
|
||||||
|
=> [RiverWMWaylandKeyBinding l]
|
||||||
|
layoutBindings =
|
||||||
|
[ key super xK_space (layoutAction NextLayout)
|
||||||
|
, key super xK_bracketleft (layoutAction Shrink)
|
||||||
|
, key super xK_bracketright (layoutAction Expand)
|
||||||
|
, key super xK_comma (layoutAction (IncMasterN 1))
|
||||||
|
, key super xK_period (layoutAction (IncMasterN (-1)))
|
||||||
|
]
|
||||||
|
|
||||||
|
spawnBindings
|
||||||
|
:: [RiverWMWaylandKeyBinding l]
|
||||||
|
spawnBindings =
|
||||||
|
[ key super xK_Return (spawnAction "ghostty --gtk-single-instance=false")
|
||||||
|
, key super xK_p (spawnAction "rofi -show drun -show-icons")
|
||||||
|
, key (super .|. shift) xK_p (spawnAction "rofi -show run")
|
||||||
|
, key (super .|. alt) xK_c (spawnAction "google-chrome-stable")
|
||||||
|
, key super xK_e (spawnAction "emacsclient --eval '(emacs-everywhere)'")
|
||||||
|
, key super xK_v (spawnAction "wl-paste | wtype -")
|
||||||
|
, key hyper xK_v (spawnAction "rofi -modi 'clipboard:greenclip print' -show clipboard")
|
||||||
|
, key hyper xK_p (spawnAction "rofi-pass")
|
||||||
|
, key hyper xK_h (spawnAction "rofi_shutter")
|
||||||
|
, key hyper xK_c (spawnAction "shell_command.sh")
|
||||||
|
, key hyper xK_x (spawnAction "rofi_command.sh")
|
||||||
|
, key (hyper .|. shift) xK_l (spawnAction "loginctl lock-session")
|
||||||
|
, key hyper xK_k (spawnAction "rofi_kill_process.sh")
|
||||||
|
, key (hyper .|. shift) xK_k (spawnAction "rofi_kill_all.sh")
|
||||||
|
, key hyper xK_r (spawnAction "rofi-systemd")
|
||||||
|
, key hyper xK_9 (spawnAction "start_synergy.sh")
|
||||||
|
, key hyper xK_backslash (spawnAction "$HOME/dotfiles/dotfiles/lib/functions/mpg341cx_input toggle")
|
||||||
|
, key hyper xK_i (spawnAction "rofi_select_input.hs")
|
||||||
|
, key hyper xK_o (spawnAction "rofi_paswitch")
|
||||||
|
, key hyper xK_w (spawnAction "rofi_wallpaper.sh")
|
||||||
|
, key hyper xK_y (spawnAction "rofi_agentic_skill")
|
||||||
|
]
|
||||||
|
|
||||||
|
mediaBindings
|
||||||
|
:: [RiverWMWaylandKeyBinding l]
|
||||||
|
mediaBindings =
|
||||||
|
[ key super xK_semicolon (spawnAction "playerctl play-pause")
|
||||||
|
, key noMods xF86XK_AudioPause (spawnAction "playerctl play-pause")
|
||||||
|
, key noMods xF86XK_AudioPlay (spawnAction "playerctl play-pause")
|
||||||
|
, key super xK_l (spawnAction "playerctl next")
|
||||||
|
, key noMods xF86XK_AudioNext (spawnAction "playerctl next")
|
||||||
|
, key super xK_j (spawnAction "playerctl previous")
|
||||||
|
, key noMods xF86XK_AudioPrev (spawnAction "playerctl previous")
|
||||||
|
, key noMods xF86XK_AudioRaiseVolume (spawnAction "set_volume --unmute --change-volume +5")
|
||||||
|
, key noMods xF86XK_AudioLowerVolume (spawnAction "set_volume --unmute --change-volume -5")
|
||||||
|
, key noMods xF86XK_AudioMute (spawnAction "set_volume --toggle-mute")
|
||||||
|
, key super xK_i (spawnAction "set_volume --unmute --change-volume +5")
|
||||||
|
, key super xK_k (spawnAction "set_volume --unmute --change-volume -5")
|
||||||
|
, key super xK_u (spawnAction "set_volume --toggle-mute")
|
||||||
|
, key (hyper .|. shift) xK_q (spawnAction "toggle_mute_current_window.sh")
|
||||||
|
, key (hyper .|. ctrl) xK_q (spawnAction "toggle_mute_current_window.sh only")
|
||||||
|
, key noMods xF86XK_MonBrightnessUp (spawnAction "brightness.sh up")
|
||||||
|
, key noMods xF86XK_MonBrightnessDown (spawnAction "brightness.sh down")
|
||||||
|
]
|
||||||
|
|
||||||
|
key
|
||||||
|
:: RiverWMWaylandModifiers
|
||||||
|
-> KeySym
|
||||||
|
-> RiverWMWaylandAction l
|
||||||
|
-> RiverWMWaylandKeyBinding l
|
||||||
|
key modifiers keysym action =
|
||||||
|
RiverWMWaylandKeyBinding
|
||||||
|
{ riverWMWaylandKeyModifiers = modifiers
|
||||||
|
, riverWMWaylandKeyKeysym = fromIntegral keysym
|
||||||
|
, riverWMWaylandKeyAction = action
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnAction :: String -> RiverWMWaylandAction l
|
||||||
|
spawnAction command state = do
|
||||||
|
configLog $ "spawn start: " ++ command
|
||||||
|
process <- spawnCommand (riverSpawnPrelude ++ command)
|
||||||
|
_ <- forkIO $ do
|
||||||
|
exitCode <- waitForProcess process
|
||||||
|
configLog $ "spawn exit: " ++ command ++ " -> " ++ show exitCode
|
||||||
|
pure ()
|
||||||
|
pure ([], state)
|
||||||
|
|
||||||
|
riverSpawnPrelude :: String
|
||||||
|
riverSpawnPrelude =
|
||||||
|
"XDG_RUNTIME_DIR=\"${XDG_RUNTIME_DIR:-/run/user/$(id -u)}\"; "
|
||||||
|
++ "export XDG_RUNTIME_DIR; "
|
||||||
|
++ "if [ -z \"${WAYLAND_DISPLAY:-}\" ]; then "
|
||||||
|
++ "for socket in \"$XDG_RUNTIME_DIR\"/wayland-*; do "
|
||||||
|
++ "[ -S \"$socket\" ] || continue; "
|
||||||
|
++ "WAYLAND_DISPLAY=\"$(basename \"$socket\")\"; "
|
||||||
|
++ "break; "
|
||||||
|
++ "done; "
|
||||||
|
++ "fi; "
|
||||||
|
++ "export WAYLAND_DISPLAY=\"${WAYLAND_DISPLAY:-wayland-1}\"; "
|
||||||
|
++ "export XDG_CURRENT_DESKTOP=river; "
|
||||||
|
++ "export XDG_SESSION_DESKTOP=river-xmonad; "
|
||||||
|
++ "export XDG_SESSION_TYPE=wayland; "
|
||||||
|
++ "export IMALISON_SESSION_TYPE=wayland; "
|
||||||
|
++ "export IMALISON_WINDOW_MANAGER=river-xmonad; "
|
||||||
|
|
||||||
|
configLog :: String -> IO ()
|
||||||
|
configLog message = do
|
||||||
|
putStrLn $ "imalison-river-xmonad: " ++ message
|
||||||
|
hFlush stdout
|
||||||
|
|
||||||
|
layoutAction
|
||||||
|
:: (LayoutClass l Window, Read (l Window), Message message)
|
||||||
|
=> message
|
||||||
|
-> RiverWMWaylandAction l
|
||||||
|
layoutAction = handleRiverWMLayoutMessage
|
||||||
|
|
||||||
|
stackAction
|
||||||
|
:: (W.StackSet WorkspaceId (l Window) Window RiverWMOutputId ScreenDetail
|
||||||
|
-> W.StackSet WorkspaceId (l Window) Window RiverWMOutputId ScreenDetail)
|
||||||
|
-> RiverWMWaylandAction l
|
||||||
|
stackAction f state =
|
||||||
|
pure $ modifyRiverWMStackSet f state
|
||||||
|
|
||||||
|
directionalSwap :: Direction -> RiverWMWaylandAction l
|
||||||
|
directionalSwap direction =
|
||||||
|
stackAction $
|
||||||
|
case direction of
|
||||||
|
DirectionUp -> W.swapUp
|
||||||
|
DirectionLeft -> W.swapUp
|
||||||
|
DirectionDown -> W.swapDown
|
||||||
|
DirectionRight -> W.swapDown
|
||||||
|
|
||||||
|
directionalFocus :: Direction -> RiverWMWaylandAction l
|
||||||
|
directionalFocus direction state =
|
||||||
|
pure $ modifyRiverWMStackSet focusDirectionalWindow state
|
||||||
|
where
|
||||||
|
focusDirectionalWindow stackSet =
|
||||||
|
maybe stackSet (`W.focusWindow` stackSet) $
|
||||||
|
directionalTarget direction state
|
||||||
|
|
||||||
|
directionalTarget :: Direction -> RiverWMState l -> Maybe Window
|
||||||
|
directionalTarget direction RiverWMState{riverWMStackSet, riverWMWindows, riverWMWindowIds} = do
|
||||||
|
focused <- W.peek riverWMStackSet
|
||||||
|
focusedId <- M.lookup focused riverWMWindowIds
|
||||||
|
focusedRect <- riverWMWindowDesired =<< M.lookup focusedId riverWMWindows
|
||||||
|
let focusedCenter = rectCenter focusedRect
|
||||||
|
candidates =
|
||||||
|
[ (window, directionScore direction focusedCenter (rectCenter rect))
|
||||||
|
| (windowId, RiverWMWindowState{riverWMWindowXWindow = window, riverWMWindowDesired = Just rect}) <-
|
||||||
|
M.toList riverWMWindows
|
||||||
|
, windowId /= focusedId
|
||||||
|
]
|
||||||
|
viable = mapMaybe sequenceCandidate candidates
|
||||||
|
fst <$> minimumMaybeBy (compare `on` snd) viable
|
||||||
|
|
||||||
|
sequenceCandidate :: (a, Maybe b) -> Maybe (a, b)
|
||||||
|
sequenceCandidate (value, Just score) = Just (value, score)
|
||||||
|
sequenceCandidate (_, Nothing) = Nothing
|
||||||
|
|
||||||
|
rectCenter :: Rectangle -> (Double, Double)
|
||||||
|
rectCenter (Rectangle x y width height) =
|
||||||
|
( fromIntegral x + fromIntegral width / 2
|
||||||
|
, fromIntegral y + fromIntegral height / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
directionScore :: Direction -> (Double, Double) -> (Double, Double) -> Maybe (Double, Double)
|
||||||
|
directionScore direction (fx, fy) (cx, cy) =
|
||||||
|
case direction of
|
||||||
|
DirectionUp | cy < fy -> Just (fy - cy, abs (cx - fx))
|
||||||
|
DirectionDown | cy > fy -> Just (cy - fy, abs (cx - fx))
|
||||||
|
DirectionLeft | cx < fx -> Just (fx - cx, abs (cy - fy))
|
||||||
|
DirectionRight | cx > fx -> Just (cx - fx, abs (cy - fy))
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
minimumMaybeBy :: (a -> a -> Ordering) -> [a] -> Maybe a
|
||||||
|
minimumMaybeBy _ [] = Nothing
|
||||||
|
minimumMaybeBy compareFn xs = Just (minimumBy compareFn xs)
|
||||||
|
|
||||||
|
addHyperChordBindings
|
||||||
|
:: RiverWMWaylandModifiers
|
||||||
|
-> RiverWMWaylandModifiers
|
||||||
|
-> [RiverWMWaylandKeyBinding l]
|
||||||
|
-> [RiverWMWaylandKeyBinding l]
|
||||||
|
addHyperChordBindings hyperMask chordMask bindings =
|
||||||
|
bindings ++ M.elems chosen
|
||||||
|
where
|
||||||
|
existingKeys =
|
||||||
|
M.fromList
|
||||||
|
[ ((riverWMWaylandKeyModifiers binding, riverWMWaylandKeyKeysym binding), ())
|
||||||
|
| binding <- bindings
|
||||||
|
]
|
||||||
|
|
||||||
|
chordBinding binding@RiverWMWaylandKeyBinding{riverWMWaylandKeyModifiers} =
|
||||||
|
binding
|
||||||
|
{ riverWMWaylandKeyModifiers =
|
||||||
|
(riverWMWaylandKeyModifiers .&. complement hyperMask) .|. chordMask
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates =
|
||||||
|
[ ( (riverWMWaylandKeyModifiers chorded, riverWMWaylandKeyKeysym chorded)
|
||||||
|
, (score (riverWMWaylandKeyModifiers binding), chorded)
|
||||||
|
)
|
||||||
|
| binding <- bindings
|
||||||
|
, riverWMWaylandKeyModifiers binding .&. hyperMask /= 0
|
||||||
|
, let chorded = chordBinding binding
|
||||||
|
, M.notMember (riverWMWaylandKeyModifiers chorded, riverWMWaylandKeyKeysym chorded) existingKeys
|
||||||
|
]
|
||||||
|
|
||||||
|
chosen =
|
||||||
|
fmap snd $
|
||||||
|
foldl' keepBest M.empty candidates
|
||||||
|
|
||||||
|
keepBest selected (bindingKey, candidate@(candidateScore, _binding)) =
|
||||||
|
case M.lookup bindingKey selected of
|
||||||
|
Nothing -> M.insert bindingKey candidate selected
|
||||||
|
Just (bestScore, _) ->
|
||||||
|
if candidateScore < bestScore
|
||||||
|
then M.insert bindingKey candidate selected
|
||||||
|
else selected
|
||||||
|
|
||||||
|
score modifiers =
|
||||||
|
length $
|
||||||
|
filter (/= 0)
|
||||||
|
[ modifiers .&. shift
|
||||||
|
, modifiers .&. ctrl
|
||||||
|
, modifiers .&. alt
|
||||||
|
, modifiers .&. hyper
|
||||||
|
, modifiers .&. super
|
||||||
|
, modifiers .&. riverWMWaylandModifierMod5
|
||||||
|
]
|
||||||
|
|
||||||
|
noMods, shift, ctrl, alt, hyper, super, hyperChord :: RiverWMWaylandModifiers
|
||||||
|
noMods = riverWMWaylandModifierNone
|
||||||
|
shift = riverWMWaylandModifierShift
|
||||||
|
ctrl = riverWMWaylandModifierCtrl
|
||||||
|
alt = riverWMWaylandModifierAlt
|
||||||
|
hyper = riverWMWaylandModifierHyper
|
||||||
|
super = riverWMWaylandModifierSuper
|
||||||
|
hyperChord = ctrl .|. alt .|. super
|
||||||
18
dotfiles/config/river-xmonad/imalison-river-xmonad.cabal
Normal file
18
dotfiles/config/river-xmonad/imalison-river-xmonad.cabal
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
cabal-version: 2.4
|
||||||
|
name: imalison-river-xmonad
|
||||||
|
version: 0.1.0.0
|
||||||
|
license: BSD-3-Clause
|
||||||
|
author: Ivan Malison
|
||||||
|
maintainer: IvanMalison@gmail.com
|
||||||
|
build-type: Simple
|
||||||
|
|
||||||
|
executable imalison-river-xmonad
|
||||||
|
main-is: Main.hs
|
||||||
|
build-depends: base >= 4.12 && < 5
|
||||||
|
, containers
|
||||||
|
, process
|
||||||
|
, X11
|
||||||
|
, xmonad
|
||||||
|
, xmonad-contrib
|
||||||
|
ghc-options: -Wall -Wno-unused-do-bind -Wno-deprecations -Wno-missing-signatures -Wno-name-shadowing
|
||||||
|
default-language: Haskell2010
|
||||||
13
dotfiles/config/river-xmonad/overlay.nix
Normal file
13
dotfiles/config/river-xmonad/overlay.nix
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
_: pkgs: {
|
||||||
|
haskellPackages = pkgs.haskellPackages.override (old: {
|
||||||
|
overrides = pkgs.lib.composeExtensions (old.overrides or (_: _: {})) (self: _super: {
|
||||||
|
imalison-river-xmonad = self.callCabal2nix "imalison-river-xmonad" (
|
||||||
|
pkgs.lib.sourceByRegex ./.
|
||||||
|
[
|
||||||
|
"Main.hs"
|
||||||
|
"imalison-river-xmonad.cabal"
|
||||||
|
]
|
||||||
|
) { };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
./rabbitmq.nix
|
./rabbitmq.nix
|
||||||
./quickshell.nix
|
./quickshell.nix
|
||||||
./remote-hyprland.nix
|
./remote-hyprland.nix
|
||||||
|
./river-xmonad.nix
|
||||||
./secrets.nix
|
./secrets.nix
|
||||||
./ssh.nix
|
./ssh.nix
|
||||||
./sni.nix
|
./sni.nix
|
||||||
|
|||||||
29
nixos/flake.lock
generated
29
nixos/flake.lock
generated
@@ -1808,7 +1808,8 @@
|
|||||||
"systems": "systems_4",
|
"systems": "systems_4",
|
||||||
"vscode-server": "vscode-server",
|
"vscode-server": "vscode-server",
|
||||||
"xmonad": "xmonad_2",
|
"xmonad": "xmonad_2",
|
||||||
"xmonad-contrib": "xmonad-contrib_2"
|
"xmonad-contrib": "xmonad-contrib_2",
|
||||||
|
"xmonad-river": "xmonad-river"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
@@ -2120,6 +2121,32 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"xmonad-river": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": [
|
||||||
|
"flake-utils"
|
||||||
|
],
|
||||||
|
"git-ignore-nix": [
|
||||||
|
"git-ignore-nix"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"unstable": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777476488,
|
||||||
|
"narHash": "sha256-2Adfi0BDJEM/ITFbXJkS21wVQ96oNXLLUh4HsHonCl4=",
|
||||||
|
"path": "/home/imalison/Projects/xmonad-river",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"path": "/home/imalison/Projects/xmonad-river",
|
||||||
|
"type": "path"
|
||||||
|
}
|
||||||
|
},
|
||||||
"xmonad_2": {
|
"xmonad_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": [
|
"flake-utils": [
|
||||||
|
|||||||
@@ -147,6 +147,16 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
xmonad-river = {
|
||||||
|
url = "path:/home/imalison/Projects/xmonad-river";
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.follows = "nixpkgs";
|
||||||
|
unstable.follows = "nixpkgs";
|
||||||
|
flake-utils.follows = "flake-utils";
|
||||||
|
git-ignore-nix.follows = "git-ignore-nix";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
xmonad-contrib = {
|
xmonad-contrib = {
|
||||||
url = "github:IvanMalison/xmonad-contrib/withMyChanges";
|
url = "github:IvanMalison/xmonad-contrib/withMyChanges";
|
||||||
inputs = {
|
inputs = {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
myModules.base.enable = true;
|
myModules.base.enable = true;
|
||||||
myModules.desktop.enable = true;
|
myModules.desktop.enable = true;
|
||||||
myModules.xmonad.enable = true;
|
myModules.xmonad.enable = true;
|
||||||
|
myModules.riverXmonad.enable = true;
|
||||||
myModules.extra.enable = false;
|
myModules.extra.enable = false;
|
||||||
myModules.code.enable = true;
|
myModules.code.enable = true;
|
||||||
myModules.games.enable = false;
|
myModules.games.enable = false;
|
||||||
|
|||||||
169
nixos/river-xmonad.nix
Normal file
169
nixos/river-xmonad.nix
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
makeEnable,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
session = import ./session-variables.nix;
|
||||||
|
|
||||||
|
riverXmonadPkgs = pkgs.extend (
|
||||||
|
lib.composeManyExtensions [
|
||||||
|
inputs.xmonad-river.overlay
|
||||||
|
inputs.xmonad-contrib.overlay
|
||||||
|
(import ../dotfiles/config/river-xmonad/overlay.nix)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
riverXmonadPackage = riverXmonadPkgs.haskellPackages.imalison-river-xmonad;
|
||||||
|
|
||||||
|
riverRofi = pkgs.writeShellScriptBin "rofi" ''
|
||||||
|
exec ${pkgs.rofi}/bin/rofi -normal-window "$@"
|
||||||
|
'';
|
||||||
|
|
||||||
|
riverInit = pkgs.writeShellScript "river-xmonad-init" ''
|
||||||
|
log_dir="''${XDG_STATE_HOME:-$HOME/.local/state}/river-xmonad"
|
||||||
|
mkdir -p "$log_dir"
|
||||||
|
echo "[$(${pkgs.coreutils}/bin/date --iso-8601=seconds)] river init start"
|
||||||
|
|
||||||
|
export PATH=${lib.makeBinPath [ riverRofi ]}:$PATH
|
||||||
|
export XDG_CURRENT_DESKTOP=river
|
||||||
|
export XDG_SESSION_DESKTOP=river-xmonad
|
||||||
|
export XDG_SESSION_TYPE=wayland
|
||||||
|
export ${session.sessionType}=wayland
|
||||||
|
export ${session.windowManager}=river-xmonad
|
||||||
|
|
||||||
|
systemctl --user stop hyprland-session.target || true
|
||||||
|
systemctl --user unset-environment HYPRLAND_INSTANCE_SIGNATURE || true
|
||||||
|
|
||||||
|
${pkgs.dbus}/bin/dbus-update-activation-environment --systemd \
|
||||||
|
WAYLAND_DISPLAY DISPLAY XAUTHORITY XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE \
|
||||||
|
${session.sessionType} ${session.windowManager} DBUS_SESSION_BUS_ADDRESS PATH || true
|
||||||
|
systemctl --user set-environment \
|
||||||
|
"WAYLAND_DISPLAY=''${WAYLAND_DISPLAY:-}" \
|
||||||
|
"DISPLAY=''${DISPLAY:-}" \
|
||||||
|
"XAUTHORITY=''${XAUTHORITY:-}" \
|
||||||
|
XDG_CURRENT_DESKTOP=river \
|
||||||
|
XDG_SESSION_DESKTOP=river-xmonad \
|
||||||
|
XDG_SESSION_TYPE=wayland \
|
||||||
|
${session.sessionType}=wayland \
|
||||||
|
${session.windowManager}=river-xmonad \
|
||||||
|
"DBUS_SESSION_BUS_ADDRESS=''${DBUS_SESSION_BUS_ADDRESS:-}" \
|
||||||
|
"PATH=$PATH" || true
|
||||||
|
systemctl --user import-environment \
|
||||||
|
WAYLAND_DISPLAY DISPLAY XAUTHORITY XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE \
|
||||||
|
${session.sessionType} ${session.windowManager} DBUS_SESSION_BUS_ADDRESS PATH || true
|
||||||
|
systemctl --user start river-xmonad-session.target || true
|
||||||
|
|
||||||
|
echo "[$(${pkgs.coreutils}/bin/date --iso-8601=seconds)] exec imalison-river-xmonad"
|
||||||
|
exec ${riverXmonadPackage}/bin/imalison-river-xmonad
|
||||||
|
'';
|
||||||
|
|
||||||
|
riverSession = pkgs.writeShellScriptBin "river-xmonad-session" ''
|
||||||
|
log_dir="''${XDG_STATE_HOME:-$HOME/.local/state}/river-xmonad"
|
||||||
|
mkdir -p "$log_dir"
|
||||||
|
log_file="$log_dir/session.log"
|
||||||
|
exec >>"$log_file" 2>&1
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "===== river-xmonad session start: $(${pkgs.coreutils}/bin/date --iso-8601=seconds) ====="
|
||||||
|
|
||||||
|
export XDG_CURRENT_DESKTOP=river
|
||||||
|
export XDG_SESSION_DESKTOP=river-xmonad
|
||||||
|
export XDG_SESSION_TYPE=wayland
|
||||||
|
export ${session.sessionType}=wayland
|
||||||
|
export ${session.windowManager}=river-xmonad
|
||||||
|
export PATH=${lib.makeBinPath [ riverRofi ]}:$PATH
|
||||||
|
|
||||||
|
echo "river-xmonad: environment before river"
|
||||||
|
env | ${pkgs.coreutils}/bin/sort
|
||||||
|
|
||||||
|
systemctl --user stop hyprland-session.target || true
|
||||||
|
systemctl --user unset-environment HYPRLAND_INSTANCE_SIGNATURE || true
|
||||||
|
|
||||||
|
${pkgs.river}/bin/river -c ${lib.escapeShellArg "${riverInit}"}
|
||||||
|
status=$?
|
||||||
|
echo "river-xmonad: river exited with status $status at $(${pkgs.coreutils}/bin/date --iso-8601=seconds)"
|
||||||
|
systemctl --user stop river-xmonad-session.target || true
|
||||||
|
exit "$status"
|
||||||
|
'';
|
||||||
|
|
||||||
|
riverDiagnostics = pkgs.writeShellScriptBin "river-xmonad-diagnostics" ''
|
||||||
|
set -u
|
||||||
|
|
||||||
|
log_dir="''${XDG_STATE_HOME:-$HOME/.local/state}/river-xmonad"
|
||||||
|
echo "river-xmonad diagnostics: $(${pkgs.coreutils}/bin/date --iso-8601=seconds)"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "== processes =="
|
||||||
|
${pkgs.procps}/bin/pgrep -a 'river|imalison-river-xmonad|rofi|ghostty|hyprpaper|xsettingsd|picom|autorandr' || true
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "== user manager environment =="
|
||||||
|
systemctl --user show-environment | ${pkgs.coreutils}/bin/sort | ${pkgs.gnugrep}/bin/grep -E '^(HYPR|IMALISON|XDG_CURRENT_DESKTOP|XDG_SESSION_DESKTOP|XDG_SESSION_TYPE|WAYLAND_DISPLAY|DISPLAY)=' || true
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "== session unit guards =="
|
||||||
|
systemctl --user cat river-xmonad-session.target dunst.service hyprpaper.service hyprland-session.target xsettingsd.service picom.service autorandr.service 2>/dev/null \
|
||||||
|
| ${pkgs.gnugrep}/bin/grep -E '^(# |\\[Unit\\]|Description=|ConditionEnvironment=|PartOf=|After=|WantedBy=|ExecStart=|\\[Install\\])' || true
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "== recent user journal =="
|
||||||
|
journalctl --user -b --since '10 minutes ago' --no-pager \
|
||||||
|
| ${pkgs.gnugrep}/bin/grep -Ei 'river|xmonad|rofi|ghostty|hyprpaper|xsettingsd|picom|autorandr|failed|error|segfault|core-dump' || true
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ -f "$log_dir/session.log" ]; then
|
||||||
|
echo "== $log_dir/session.log tail =="
|
||||||
|
${pkgs.coreutils}/bin/tail -n 250 "$log_dir/session.log"
|
||||||
|
else
|
||||||
|
echo "no session log at $log_dir/session.log"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
riverSessionPackage = (pkgs.writeTextFile {
|
||||||
|
name = "river-xmonad-session";
|
||||||
|
destination = "/share/wayland-sessions/river-xmonad.desktop";
|
||||||
|
text = ''
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=river-xmonad
|
||||||
|
Comment=river with xmonad as its external window manager
|
||||||
|
Exec=${riverSession}/bin/river-xmonad-session
|
||||||
|
Type=Application
|
||||||
|
DesktopNames=river
|
||||||
|
'';
|
||||||
|
}).overrideAttrs (_old: {
|
||||||
|
passthru.providedSessions = [ "river-xmonad" ];
|
||||||
|
});
|
||||||
|
in
|
||||||
|
makeEnable config "myModules.riverXmonad" false {
|
||||||
|
services.displayManager.sessionPackages = [
|
||||||
|
riverSessionPackage
|
||||||
|
];
|
||||||
|
|
||||||
|
home-manager.sharedModules = [
|
||||||
|
{
|
||||||
|
systemd.user.targets.river-xmonad-session = {
|
||||||
|
Unit = {
|
||||||
|
Description = "river-xmonad session";
|
||||||
|
ConditionEnvironment = session.riverXmonad;
|
||||||
|
BindsTo = [ "graphical-session.target" ];
|
||||||
|
Wants = [ "graphical-session-pre.target" ];
|
||||||
|
After = [ "graphical-session-pre.target" ];
|
||||||
|
Before = [ "graphical-session.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
brightnessctl
|
||||||
|
river
|
||||||
|
riverDiagnostics
|
||||||
|
riverXmonadPackage
|
||||||
|
wl-clipboard
|
||||||
|
wtype
|
||||||
|
];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user