PálcikaWM VII. : XMonad

enlightened Ez az oldal a közösségért készül. heart Kövess minket máshol is:  Linux Mint Magyar Közösség a Mastodon-on  Telegram csatorna – csak hírek  Beszélgessünk a Telegram – Linux csevegő csoport  Hírek olvasása RSS segítségével  Linux Mint Hivatalos Magyar Közösség a Facebook-on      Linux Mint Baráti Kör a Facebook-on
wink Ha hasznosnak találod, és szeretnéd, hogy folytatódjon, támogasd a munkát Ko-fi vagy Paypal segítségével. laugh

Már jó ideje írogatom a pálcikawm sorozatot, és hogy az XMonad csak most kerül szóba, annak bizony oka van! Amikor rácuppantam a témára hamar képbe került, rengeteg jót írtak róla: hatékony, rendkívüli módon testreszabható és beton stabil. Gyorsan nekiestem, aztán szembesültem a dologgal, hogy itt bizony nincs konfigurációs állomány, maga a Haskell kód a konfiguráció. A kódot megnézve pedig olyan érzés fogott el, mintha egy kínai nyelvű leírás alapján kéne várat építenem, hamar feladtam a küzdelmet, úgy gondoltam ennyit nem ér az egész... Aztán telt-múlt az idő, rengeteget foglalkoztam a pálcikawm témával, köztük olyanokkal is, ahol LUA vagy C kódot kellett simogatni, ezért aztán elérkezettnek láttam az időt újra elővenni az XMonadot, és elhatároztam, ha a fene fenét eszik is, összeütök egy élhető konfigurációt...! 

Tehát az XMonad egy dinamikus csempéző ablakkezelő, és ahogy már említettem, ami igazán különlegessé teszi, hogy a konfigurációs fájlja egy teljes értékű Haskell program. Ezért először is készülj arra, hogy telepítéskor kicsit több függőséget hoz magával, hisz egy komplett Haskell fordító környezetet kell létrehozni.

Miért Haskell és mi az alapja a híres stabilitásnak? Típusbiztosság: a Haskell fordító a te őrszemed. Ha elírsz egy változót, vagy rossz típust adsz meg, a rendszer nem fog lefagyni vagy hibásan működni, egyszerűen nem fordul le, amíg ki nem javítod! Igazi "bolond biztos" rendszer! 
Végtelen bővíthetőség: az xmonad-contrib csomag rengeteg bővítményt tartalmaz, pl. ha kell egy olyan ablakelrendezés (layout), ami spirálisan rendezi a termináljaidat, azt egy importálással megkapod. De ha ez sem lenne elég, további csomagokat, bővítményeket találsz a tárolókban, ill. a hálón.
Nálam a koncepció nem változott, pálcikawm = hatékonyság, szal az xmonad-contrib-nál nem mentem messzebb, bőven tartalmazza a számomra mindennapi használathoz szükséges bővítményeket.

Ne áruljunk zsákbamacskát, ha még sosem láttál Haskell kódot, az első napokban a xmonad.hs fájl szerkesztése nem egy könnyű menet, bizony időt kell szánni a szintaxis és a logika megértésére. Nyilván nem kell Haskell programozónak lenni (de ha az vagy, máris nyertél), de azért van egy tanulási görbe, ha nem egyszerűen mások konfigját akarod használni. De ha van egy kis elszántság meg idő, akkor bizony beüt a hacker-élmény, hogy ténylegesen ura vagy a rendszernek!

Na ennyi duma után nézzük a konkrétumokat!

Ahogy az eredeti dokumentáció is javasolja, nem egy több száz soros kódot generáltam, hanem szétszedtem kisebb egységekre a konfigurációt, a struktúra:

~/.config/xmonad/
│
├── xmonad.hs
│
└── lib/
    │
    ├── Colors.hs
    │
    ├── Keys.hs
    │
    ├── Layouts.hs
    │
    ├── Manage.hs
    │
    └── Xmobar.hs

~/.config/xmobar/xmobarrc

- Colors.hs: színek:

module Colors
    ( Theme(..)
    , activeTheme

    , catppuccin
    , nord
    , gruvbox
    , tokyoNight
    , everforest
    , dracula
    , solarized
    ) where

-- ============================================================================
-- Téma adatszerkezete
-- ============================================================================

data Theme = Theme
    { themeName       :: String
    , themeBackground :: String
    , themeForeground :: String
    , themeAccent     :: String
    , themeMuted      :: String
    , themeBorder     :: String
    , themeFocus      :: String
    , themeUrgent     :: String
    }

-- ============================================================================
-- AKTÍV TÉMA
--
-- Csak ezt az egy sort kell megváltoztatni.
--
-- catppuccin
-- nord
-- gruvbox
-- tokyoNight
-- everforest
-- dracula
-- solarized
-- ============================================================================

activeTheme :: Theme
activeTheme = catppuccin

-- ============================================================================
-- Catppuccin Mocha
-- ============================================================================

catppuccin :: Theme
catppuccin = Theme
    { themeName        = "Catppuccin"
    , themeBackground  = "#1e1e2e"
    , themeForeground  = "#cdd6f4"
    , themeAccent      = "#89b4fa"
    , themeMuted       = "#6c7086"
    , themeBorder      = "#313244"
    , themeFocus       = "#89b4fa"
    , themeUrgent      = "#f38ba8"
    }

-- ============================================================================
-- Nord
-- ============================================================================

nord :: Theme
nord = Theme
    { themeName        = "Nord"
    , themeBackground  = "#2e3440"
    , themeForeground  = "#d8dee9"
    , themeAccent      = "#88c0d0"
    , themeMuted       = "#4c566a"
    , themeBorder      = "#3b4252"
    , themeFocus       = "#88c0d0"
    , themeUrgent      = "#bf616a"
    }

-- ============================================================================
-- Gruvbox Dark
-- ============================================================================

gruvbox :: Theme
gruvbox = Theme
    { themeName        = "Gruvbox"
    , themeBackground  = "#282828"
    , themeForeground  = "#ebdbb2"
    , themeAccent      = "#fabd2f"
    , themeMuted       = "#928374"
    , themeBorder      = "#3c3836"
    , themeFocus       = "#fabd2f"
    , themeUrgent      = "#fb4934"
    }

-- ============================================================================
-- Tokyo Night
-- ============================================================================

tokyoNight :: Theme
tokyoNight = Theme
    { themeName        = "Tokyo Night"
    , themeBackground  = "#1a1b26"
    , themeForeground  = "#c0caf5"
    , themeAccent      = "#7aa2f7"
    , themeMuted       = "#565f89"
    , themeBorder      = "#24283b"
    , themeFocus       = "#7aa2f7"
    , themeUrgent      = "#f7768e"
    }

-- ============================================================================
-- Everforest Dark
-- ============================================================================

everforest :: Theme
everforest = Theme
    { themeName        = "Everforest"
    , themeBackground  = "#2d353b"
    , themeForeground  = "#d3c6aa"
    , themeAccent      = "#a7c080"
    , themeMuted       = "#859289"
    , themeBorder      = "#343f44"
    , themeFocus       = "#a7c080"
    , themeUrgent      = "#e67e80"
    }

-- ============================================================================
-- Dracula
-- ============================================================================

dracula :: Theme
dracula = Theme
    { themeName        = "Dracula"
    , themeBackground  = "#282a36"
    , themeForeground  = "#f8f8f2"
    , themeAccent      = "#bd93f9"
    , themeMuted       = "#6272a4"
    , themeBorder      = "#44475a"
    , themeFocus       = "#bd93f9"
    , themeUrgent      = "#ff5555"
    }

-- ============================================================================
-- Solarized Dark
-- ============================================================================

solarized :: Theme
solarized = Theme
    { themeName        = "Solarized"
    , themeBackground  = "#002b36"
    , themeForeground  = "#839496"
    , themeAccent      = "#268bd2"
    , themeMuted       = "#586e75"
    , themeBorder      = "#073642"
    , themeFocus       = "#268bd2"
    , themeUrgent      = "#dc322f"
    }

- Keys.hs: billentyűzet beállítások:

module Keys
    ( myKeys
    , myWorkspaces
    ) where

import XMonad

import System.Exit (exitSuccess)

import XMonad.Actions.CycleWS
import XMonad.Hooks.ManageDocks
import XMonad.Layout.ToggleLayouts
import XMonad.Util.EZConfig
import XMonad.Util.NamedScratchpad

import qualified XMonad.StackSet as W

import Manage
    ( scratchpads
    )

-- ============================================================================
-- Workspace-ek
-- ============================================================================

myWorkspaces :: [WorkspaceId]
myWorkspaces =
    map show [1 .. 9]

-- ============================================================================
-- Billentyűparancsok
-- ============================================================================

myKeys :: [(String, X ())]
myKeys =
    --------------------------------------------------------------------------
    -- Alap
    --------------------------------------------------------------------------

    [ -- Kitty
      ("C-M1-t", spawn "kitty")
      --("M-<Return>", spawn "kitty")

      -- Rofi programindító
    , ("M-d", spawn "rofi -show drun")

      -- Rofi command launcher
    , ("M-S-d", spawn "rofi -show run")

      -- Ablak bezárása
    , ("M-q", kill)

      -- XMonad újrafordítása + újraindítása
    , ("M-S-c", spawn "xmonad --recompile && xmonad --restart")

      -- Rendszer menü
    , ("M-S-p", spawn "/home/berus/.local/bin/rmenu.sh")

      -- XMonad kilépés
    , ("M-S-e", io exitSuccess)

    --------------------------------------------------------------------------
    -- Layout
    --------------------------------------------------------------------------

      -- Következő layout
    , ("M-<Space>", sendMessage NextLayout)

      -- Fullscreen layout
    , ("M-f", sendMessage ToggleLayout)

      -- Borderless fullscreen toggle
    , ("M-b", sendMessage ToggleStruts)

    --------------------------------------------------------------------------
    -- Fókusz
    --------------------------------------------------------------------------

      -- Következő ablak
    , ("M-j", windows W.focusDown)

      -- Előző ablak
    , ("M-k", windows W.focusUp)

      -- Master ablak
    , ("M-m", windows W.focusMaster)

      -- Master csere
    , ("M-S-<Return>", windows W.swapMaster)

    --------------------------------------------------------------------------
    -- Ablak mozgatás
    --------------------------------------------------------------------------

      -- Ablak lefelé
    , ("M-S-j", windows W.swapDown)

      -- Ablak felfelé
    , ("M-S-k", windows W.swapUp)

    --------------------------------------------------------------------------
    -- Master méret
    --------------------------------------------------------------------------

      -- Master kisebb
    , ("M-h", sendMessage Shrink)

      -- Master nagyobb
    , ("M-l", sendMessage Expand)

      -- Master ablakok +1
    , ("M-i", sendMessage (IncMasterN 1))

      -- Master ablakok -1
    , ("M-S-i", sendMessage (IncMasterN (-1)))

    --------------------------------------------------------------------------
    -- Ablak lebegtetés megszüntetése
    --------------------------------------------------------------------------

    , ("M-t", withFocused $ windows . W.sink)

    --------------------------------------------------------------------------
    -- Scratchpad
    --------------------------------------------------------------------------

    , ("M-S-s", namedScratchpadAction scratchpads "terminal")

    --------------------------------------------------------------------------
    -- Workspace váltás
    --------------------------------------------------------------------------

    , ("M-1", windows $ W.view "1")
    , ("M-2", windows $ W.view "2")
    , ("M-3", windows $ W.view "3")
    , ("M-4", windows $ W.view "4")
    , ("M-5", windows $ W.view "5")
    , ("M-6", windows $ W.view "6")
    , ("M-7", windows $ W.view "7")
    , ("M-8", windows $ W.view "8")
    , ("M-9", windows $ W.view "9")

    --------------------------------------------------------------------------
    -- Ablak áthelyezése másik workspace-re
    --------------------------------------------------------------------------

    , ("M-S-1", windows $ W.shift "1")
    , ("M-S-2", windows $ W.shift "2")
    , ("M-S-3", windows $ W.shift "3")
    , ("M-S-4", windows $ W.shift "4")
    , ("M-S-5", windows $ W.shift "5")
    , ("M-S-6", windows $ W.shift "6")
    , ("M-S-7", windows $ W.shift "7")
    , ("M-S-8", windows $ W.shift "8")
    , ("M-S-9", windows $ W.shift "9")

    --------------------------------------------------------------------------
    -- Workspace léptetés
    --------------------------------------------------------------------------

    , ("M-C-<Right>", nextWS)
    , ("M-C-<Left>", prevWS)

    , ("M-C-S-<Right>", shiftToNext)
    , ("M-C-S-<Left>", shiftToPrev)

    --------------------------------------------------------------------------
    -- PulseAudio hangerő
    --
    -- pactl a PulseAudio kompatibilis parancssori eszköz.
    --------------------------------------------------------------------------

    , ("<XF86AudioRaiseVolume>",
        spawn "pactl set-sink-volume @DEFAULT_SINK@ +5%")

    , ("<XF86AudioLowerVolume>",
        spawn "pactl set-sink-volume @DEFAULT_SINK@ -5%")

    , ("<XF86AudioMute>",
        spawn "pactl set-sink-mute @DEFAULT_SINK@ toggle")

    ]

- Layouts.hs: ablak elrendezések:

module Layouts
    ( myLayout
    ) where

import XMonad

import XMonad.Hooks.ManageDocks
import XMonad.Layout.LayoutModifier (ModifiedLayout)
import XMonad.Layout.NoBorders
import XMonad.Layout.ResizableTile
import XMonad.Layout.Spacing
import XMonad.Layout.ToggleLayouts

-- ============================================================================
-- Alap csempézett layout
--
-- spacingRaw False: mindig legyen térköz, külső és belső külön beállítva
-- spacing: egyszerű beállítás
-- ============================================================================

tiled :: ModifiedLayout Spacing ResizableTall Window
tiled =
    spacingRaw False (Border 10 10 10 10) True (Border 5 5 5 5) True $
     
    --spacing 6 $
    ResizableTall
        1
        (3 / 100)
        (1 / 2)
        []

-- ============================================================================
-- Layoutok
--
-- Super+Space = következő layout
-- Super+f     = Full / normál mód
-- Super+b     = Xmobar helyének ki/be kapcsolása
-- ============================================================================

myLayout =
    avoidStruts $
    --smartBorders $
    toggleLayouts Full $
        tiled
        ||| Mirror tiled

- Manage.hs: ablakkezelési szabályok:

module Manage
    ( myManageHook
    , scratchpads
    ) where

import XMonad

import XMonad.Hooks.ManageHelpers
import XMonad.Util.NamedScratchpad

import qualified XMonad.StackSet as W

-- ============================================================================
-- Scratchpad
--
-- Mod+Shift+s
--
-- Kitty indul "scratchpad" WM_CLASS-szal.
-- ============================================================================

scratchpads :: [NamedScratchpad]
scratchpads =
    [ NS
        "terminal"
        "kitty --class scratchpad"
        (className =? "scratchpad")
        (customFloating $ W.RationalRect 0.10 0.10 0.80 0.80)
    ]

-- ============================================================================
-- Ablakkezelési szabályok
-- ============================================================================

myManageHook :: ManageHook
myManageHook =
    composeAll
        [ -- A scratchpad kezelése
          namedScratchpadManageHook scratchpads

          -- Dialógusablakok lebegjenek
        , isDialog --> doCenterFloat

          -- Fullscreen ablakok legyenek fullscreenben
        , isFullscreen --> doFullFloat

          -- Néhány tipikus kis utility ablak
        , className =? "Pavucontrol" --> doFloat
        , className =? "Nm-connection-editor" --> doFloat
        , className =? "Gpick" --> doFloat
        ]

- Xmobar.hs: panel formázás:

module Xmobar
    ( myXmobarPP
    ) where

import XMonad
import XMonad.Hooks.StatusBar.PP

import Colors

-- ============================================================================
-- Xmobar formázás
-- ============================================================================

myXmobarPP :: PP
myXmobarPP =
    def
        { -- Aktív workspace
          ppCurrent =
              xmobarColor (themeAccent activeTheme) ""
              . wrap " " " "

          -- Látható workspace
        , ppVisible =
              xmobarColor (themeForeground activeTheme) ""
              . wrap " " " "

          -- Ablakot tartalmazó rejtett workspace
        , ppHidden =
              xmobarColor (themeForeground activeTheme) ""
              . wrap " " " "

          -- Üres workspace
        , ppHiddenNoWindows =
              xmobarColor (themeMuted activeTheme) ""

          -- Urgent workspace
        , ppUrgent =
              xmobarColor (themeUrgent activeTheme) ""
              . wrap "!" "!"

          -- Ablak címe
        , ppTitle =
              xmobarColor (themeForeground activeTheme) ""
              . shorten 60

          -- Layout neve
        , ppLayout =
              xmobarColor (themeAccent activeTheme) ""

          -- Elválasztó
        , ppSep =
              xmobarColor (themeMuted activeTheme) ""
              "  •  "

          -- Workspace + layout + title sorrend
        , ppOrder =
              \(ws:l:t:_) -> [ws, l, t]
        }

- xmonad.hs: központi modul:

import XMonad

import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.ManageDocks
import XMonad.Hooks.StatusBar

import XMonad.Util.EZConfig
import XMonad.Util.SpawnOnce

import Colors
import Keys
import Layouts
import Manage
import Xmobar

-- ============================================================================
-- Alap konfiguráció
-- ============================================================================

myConfig =
    def
        { terminal           = "kitty"

        , modMask            = mod4Mask

        , workspaces         = myWorkspaces

        , borderWidth        = 2
        , normalBorderColor  = themeBorder activeTheme
        , focusedBorderColor = themeFocus activeTheme

        , layoutHook         = myLayout

        , manageHook         = myManageHook

        , focusFollowsMouse  = False
        , clickJustFocuses   = True

        -- Induló programok
        , startupHook = do
            spawnOnce "picom --daemon --config ~/.config/picom.conf"
            spawnOnce "redshift -c ~/.config/redshift/redshift.conf"
            spawnOnce "nitrogen --restore"
        }

-- ============================================================================
-- XMonad + EWMH + Docks + Xmobar
-- ============================================================================

main :: IO ()
main =
    xmonad
        . docks
        . ewmhFullscreen
        . ewmh
        . withEasySB
            (statusBarProp
                "xmobar ~/.config/xmobar/xmobarrc"
                (pure myXmobarPP))
            defToggleStrutsKey
        $ myConfig
        `additionalKeysP`
            myKeys

- xmobarrc: panel beállításai

Config
    { -- =====================================================================
      -- Alap
      -- =====================================================================

      font = "xft:JetBrainsMono Nerd Font-10"

    , additionalFonts = []

    -- Catppuccin Mocha alapértelmezett színek.
    -- Ha más témát választasz, ezeket igazítsd a Colors.hs-hez.

    , bgColor = "#1e1e2e"
    , fgColor = "#cdd6f4"

    , alpha = 255

      -- =====================================================================
      -- Pozíció
      -- =====================================================================

    --, position = Top

    , position = TopSize L 100 28

      -- TopB, BottomB, FullB, NoBorder 
    , border = BottomB
    , borderColor = "#7f849c"
    , borderWidth = 2

    , textOffset = 0
    , iconOffset = 0

    , lowerOnStart = False
    , hideOnStart = False
    , persistent = True
    , allDesktops = True

      -- =====================================================================
      -- Monitorozók
      -- =====================================================================

    , commands =
        [ -- XMonad workspace / layout / title
          Run XMonadLog

          -- CPU
        , Run Cpu
            [ "-t", "CPU <total>%"
            , "-L", "30"
            , "-H", "80"
            , "--low", "#a6e3a1"
            , "--normal", "#f9e2af"
            , "--high", "#f38ba8"
            ]
            10

          -- RAM
        , Run Memory
            [ "-t", "RAM <usedratio>%"
            , "-L", "30"
            , "-H", "80"
            , "--low", "#a6e3a1"
            , "--normal", "#f9e2af"
            , "--high", "#f38ba8"
            ]
            10

          -- Hangerő
        , Run Com
            "sh"
            [ "-c"
            , "pactl get-sink-volume @DEFAULT_SINK@ | awk 'NR==1 {print $5}'"
            ]
            "volume"
            5

          -- Dátum / idő
        , Run Date
            "%Y-%m-%d  %H:%M"
            "date"
            10
        ]

     -- =====================================================================
     -- Template
     -- =====================================================================

   , sepChar = "%"

   , alignSep = "}{"

   , template = "  %XMonadLog% }{ %cpu%  %memory%  VOL %volume%  %date%  "
   }

Amennyire lehet, ellátva magyarázatokkal, most csak néhány megjegyzés:

- Mit jelent a "RationalRect"? Nem pixelben, hanem a képernyő arányában adhatjuk meg a helyet és méretet.
- Mit jelent a "isDialog --> doCenterFloat"? Ha az alkalmazás dialog ablakot hoz létre, lebegő és középre helyezett ablak legyen.
- Mit jelent a "spawnOnce"? Csak egyszer induljon a program, nyugodtam nyomhatsz fordítás+ újra indítást, nem akadnak össze.
- Mit jelent a "smartBorders"? Ha csak egy ablak van, az legyen teljesképernyős.
- További elrendezéseket (layouts) is felvehetsz, pl. ThreeCol, Full, Tabbed. Én egyiket sem használom, így nincs bonyolítva velük a kód.
- Adott program adott asztalra (pl. a 2-esre): className =? "Alacritty"
                                                   --> doShift "2" 

A rendszer felépítése valahogy így néz ki:


                  ┌──────────────┐
                  │    XMonad    │
                  └──────┬───────┘
                         │
       ┌─────────────────┼──────────────────┐
       │                 │                  │
       ▼                 ▼                  ▼
   Keyboard           Layouts           ManageHook
       │                 │                  │
       │                 │                  │
       │                 │                  │
       └─────────────────┼──────────────────┘
                         │
                         ▼
                     XMONAD STATE
                         │
                         ▼
                      Xmobar
                         │
       ┌─────────────────┼─────────────────┐
       │                 │                 │
   Workspaces           CPU/RAM          Clock

Ha minden beállítással megvagy, jöhet az xmonad --recompile parancs, ha nem kapsz hibát győztél, egyébként meg jöhet a javítgatás...
Ha az xmobarral van gond, a javítás után érdemes külön ellenőrizni: (pkill xmobar) xmobar ~/.config/xmobar/xmobarrc.

Az XMonad és az Xmobar párosa nem a "kényelmes, mindent a kezünk alá" kategória. Ez egy szerszám, egy precíziós szerszám, amit ha megtanulsz használni, az élmény, a hatékonyság mindent visz! 
A Haskell alapú konfiguráció pedig biztosítja, hogy a rendszered pontosan úgy viselkedjen, ahogy te akarod, kompromisszumok nélkül!
Tényleg sokat szenvedtem vele, de azt hiszem megérte, tanulságos kaland volt, remek végeredménnyel!

 

ui.: A konfigban hivatkozok saját szkriptre, "Rendszer menü" (rofi alapú leállítás, altatás, stb.), vagy kihagyod, vagy kiváltod...

Az eredeti dokumentációt mindenképpen érdemes nézegetni:  https://xmonad.org/documentation.html

Berus