PálcikaWM III. : Sway

Segítséget kaptál? Szívesen töltöd itt az idődet? Visszajársz hozzánk? Támogasd a munkákat: Ko-fi és Paypal!

A Sway egy Wayland alapú tiling ablakkezelő, ami az i3 WM Wayland alpú alternatívája.
Nagyban kompatibilis az i3 konfigurációs szintaxisával, de natív Wayland (wlroots) támogatással, hardveres gyorsítással és modern grafikus stack-kel.
Tehát a Laci i3-as cikkében leírtak jó eséllyel ráhúzhatóak a Sway-re, ezért ez egy rövidebb bemutató lesz...

Először is nézzük a Wayland alapú eszközöket, amikre szükségünk van/lehet:
  - swaylock: képernyőzároló;
  - swayidle: képernyőkímélő;
  - swaybg: háttérkép;
  - swaymsg: üzenet küldő;
  - swaynag: hibajelző;
  - grim és slurp: képernyőkép készítés;
  - waybar: panel (használhatjuk a beépítettet is, a swaybart);
  - wlogout: "power" menü;
  - wlsunset: gamma-korrekció (gyk. redshift klón);
  - rofi: alkalmazás indító (már natív Wayland támogatással, alternatíva wofi);
  - bemenu: alkalmazás indító (dmenu klón, működik X alatt is).

Nézzük is a konfigurációs állományomat ( ~/.config/sway/config ):

# Berus Sway konfiguráció

### Változók (Variables)

# Módosító billentyű.
set $mod Mod4
# Iránybillentyűk, mint a vim-ben
set $left h
set $down j
set $up k
set $right l
# A preferált terminál
set $term kitty
# A preferált alkalmazásindítód
# Megjegyzés: a végső parancsot add át a swaymsg-nek, hogy az eredményül kapott ablak
# az eredeti munkaterületen nyíljon meg, ahol a parancsot futtattad.
set $rofi_cmd rofi \
        -terminal '$term'
#set $menu $rofi_cmd -show combi -combi-modes drun#run -modes combi
set $menu $rofi_cmd -show drun

### Kimeneti konfiguráció (Output configuration)

# Alapértelmezett háttérkép
# Szükséges: desktop-backgrounds-compat, swaybg, jxl-pixbuf-loader
output * bg /home/berus/Képek/wp.jpg fill
#
# Példa konfiguráció:
#
#   output HDMI-A-1 resolution 1920x1080 position 1920,0
#
# A kimenetek nevét a következő futtatásával kaphatod meg: swaymsg -t get_outputs

### Üresjárati (Idle) konfiguráció

# Példa konfiguráció:
#
# exec swayidle -w \
#          timeout 300 'swaylock -f -c 000000' \
#          timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \
#          before-sleep 'swaylock -f -c 000000'
#
# Ez 300 másodperc inaktivitás után zárolja a képernyőt, majd további 300 másodperc után
# kikapcsolja a kijelzőket, és újra bekapcsolja őket, amikor folytatódik a munka.
# A számítógép alvás előtti zárolását is elvégzi.

### Bemeneti konfiguráció (Input configuration)

# Példa konfiguráció:
#
#   input "2:14:SynPS/2_Synaptics_TouchPad" {
#       dwt enabled
#       tap enabled
#       natural_scroll enabled
#       middle_emulation enabled
#   }
#
# A bemenetek nevét a következő futtatásával kaphatod meg: swaymsg -t get_inputs

### Billentyűparancsok (Key bindings)

# Alapok (Basics):
#
    # Terminál indítása
    bindsym $mod+Return exec $term

    # A fókuszált ablak bezárása
    bindsym $mod+q kill

    # Az indító (launcher) elindítása
    bindsym $mod+d exec $menu

    # Lebegő ablakok mozgatása a $mod és a bal egérgomb lenyomva tartásával.
    # Átméretezésük a jobb egérgomb + $mod használatával.
    # Cseréld normal-ról inverse-re, ha a bal egérgombot az átméretezéshez, a jobbat pedig a húzáshoz szeretnéd használni.
    floating_modifier $mod normal

    # A konfigurációs fájl újratöltése
    bindsym $mod+Shift+c reload

    # Kilépés a sway-ből (kijelentkezés a Wayland munkamenetből)
    # Figyelmeztető üzenet megjelenítése, mielőtt ténylegesen kilép
    bindsym $mod+Shift+e exec swaynag -t warning -m 'Biztosan ki akarsz lépni a Swayből? Ezzel befejezed a Wayland-es munkamenetedet.' -B 'Igen, kilépés.' 'swaymsg exit'
    # Power menü
    bindsym $mod+Shift+p exec wlogout
    
### Ablakkeret Színek Beállítása (Client Colors)

# A színek hexadecimális formátumban (#RRGGBB) adandók meg.
# Szintaxis: client.<állapot> border_color background_color text_color indicator_color child_border_color

# Fókuszált ablak kerete (Jelenleg aktív ablak)
#client.focused              #4c7899 #285577 #ffffff #2e9ef4 #285577
client.focused              #AFD700 #AFD700 #000000 #AFD700

# Fókuszált, de NEM aktív munkaterületen lévő ablak kerete
#client.focused_inactive     #333333 #5f676a #ffffff #484e50 #5f676a
client.focused_inactive     #111111 #768e10 #000000 #000000

# Nem fókuszált ablak kerete (Egyéb ablakok)
#client.unfocused            #333333 #5f676a #888888 #292d2e #5f676a
client.unfocused            #111111 #768e10 #000000 #000000

# Sürgősnek jelölt ablak (pl. új üzenet érkezett) kerete
#client.urgent               #2f343a #900000 #ffffff #900000 #900000
client.urgent               #d42121 #d42121 #f7f7f7 #d42121

# Tartalék (Placeholder) keret
#client.placeholder          #000000 #0c0c0c #ffffff #000000 #0c0c0c
client.placeholder          #111111 #768e10 #000000 #000000

# Szegély vastagság és stílus (gyk. ablakcímmező ki)
default_border pixel 2
default_floating_border pixel 2

# Mozgás (Moving around):
#
    # A fókusz mozgatása
    bindsym $mod+$left focus left
    bindsym $mod+$down focus down
    bindsym $mod+$up focus up
    bindsym $mod+$right focus right
    # Vagy használd a $mod+[fel|le|bal|jobb] nyilakat
    bindsym $mod+Left focus left
    bindsym $mod+Down focus down
    bindsym $mod+Up focus up
    bindsym $mod+Right focus right

    # A fókuszált ablak mozgatása ugyanígy, de Shift hozzáadásával
    bindsym $mod+Shift+$left move left
    bindsym $mod+Shift+$down move down
    bindsym $mod+Shift+$up move up
    bindsym $mod+Shift+$right move right
    # Ugyanez, nyíl billentyűkkel
    bindsym $mod+Shift+Left move left
    bindsym $mod+Shift+Down move down
    bindsym $mod+Shift+Up move up
    bindsym $mod+Shift+Right move right

# Munkaterületek (Workspaces):
#
    # Váltás munkaterületre
    bindsym $mod+1 workspace number 1
    bindsym $mod+2 workspace number 2
    bindsym $mod+3 workspace number 3
    bindsym $mod+4 workspace number 4
    bindsym $mod+5 workspace number 5
    bindsym $mod+6 workspace number 6
    bindsym $mod+7 workspace number 7
    bindsym $mod+8 workspace number 8
    bindsym $mod+9 workspace number 9
    bindsym $mod+0 workspace number 10
    # A fókuszált konténer áthelyezése munkaterületre
    bindsym $mod+Shift+1 move container to workspace number 1
    bindsym $mod+Shift+2 move container to workspace number 2
    bindsym $mod+Shift+3 move container to workspace number 3
    bindsym $mod+Shift+4 move container to workspace number 4
    bindsym $mod+Shift+5 move container to workspace number 5
    bindsym $mod+Shift+6 move container to workspace number 6
    bindsym $mod+Shift+7 move container to workspace number 7
    bindsym $mod+Shift+8 move container to workspace number 8
    bindsym $mod+Shift+9 move container to workspace number 9
    bindsym $mod+Shift+0 move container to workspace number 10
    # Megjegyzés: a munkaterületeknek bármilyen nevet adhatsz, nem csak számokat.

# Elrendezés (Layout stuff):
#
    # Az aktuálisan fókuszált objektumot "feloszthatod" (split)
    # $mod+b-vel vízszintesen és $mod+v-vel függőlegesen.
    bindsym $mod+b splith
    bindsym $mod+v splitv

    # A jelenlegi konténer váltása különböző elrendezési stílusok között
    bindsym $mod+s layout stacking
    bindsym $mod+w layout tabbed
    bindsym $mod+e layout toggle split

    # A jelenlegi fókusz teljes képernyőre állítása
    bindsym $mod+f fullscreen

    # A jelenlegi fókusz váltása csempéző és lebegő mód között
    bindsym $mod+Shift+space floating toggle

    # Fókusz váltása a csempéző terület és a lebegő terület között
    bindsym $mod+space focus mode_toggle

    # Fókusz áthelyezése a szülő konténerre
    bindsym $mod+a focus parent

# Jegyzettömb (Scratchpad):
#
    # A Sway rendelkezik egy "jegyzettömbbel" (scratchpad).
    # Küldhetsz oda ablakokat, és később visszahozhatod őket.
    # A jelenleg fókuszált ablak áthelyezése a jegyzettömbbe
    bindsym $mod+Shift+minus move scratchpad

    # A következő jegyzettömb ablak megjelenítése vagy a fókuszált jegyzettömb ablak elrejtése.
    # Ha több jegyzettömb ablak van, ez a parancs ciklikusan vált köztük.
    bindsym $mod+minus scratchpad show

# Konténerek átméretezése (Resizing containers):
#
mode "resize" {
    # left csökkenti a konténer szélességét
    # right növeli a konténer szélességét
    # up csökkenti a konténer magasságát
    # down növeli a konténer magasságát
    bindsym $left resize shrink width 10px
    bindsym $down resize grow height 10px
    bindsym $up resize shrink height 10px
    bindsym $right resize grow width 10px

    # Ugyanez, nyíl billentyűkkel
    bindsym Left resize shrink width 10px
    bindsym Down resize grow height 10px
    bindsym Up resize shrink height 10px
    bindsym Right resize grow width 10px

    # Visszatérés az alapértelmezett módba
    bindsym Return mode "default"
    bindsym Escape mode "default"
}
bindsym $mod+r mode "resize"

# Konfigurációk befoglalása (Include configs)
# Konfigurációk befoglalása 3 helyről:
#  - /usr/share/sway/config.d
#  - /etc/sway/config.d
#  - $XDG_CONFIG_HOME/sway/config.d ($HOME/.config/sway/config.d)
#
# Ha több könyvtár is tartalmaz azonos nevű fájlokat, a később szereplő könyvtár élvez elsőbbséget;
# a `$XDG_CONFIG_HOME/sway/config.d/20-swayidle.conf`
# mindig be lesz töltve a `/usr/share/sway/config.d/20-swayidle.conf`
# vagy a `/etc/sway/config.d/20-swayidle.conf` helyett.
#
# Ez a mechanizmus lehetővé teszi az alapértelmezett konfiguráció felülírását rendszerszinten
# (/etc) vagy felhasználónként ($XDG_CONFIG_HOME) is.
# Egyszerűen hozd létre a módosítani/felülírni kívánt fájlt a magasabb szintű könyvtárban.
#
include '$(/usr/libexec/sway/layered-include "/usr/share/sway/config.d/*.conf" "/etc/sway/config.d/*.conf" "${XDG_CONFIG_HOME:-$HOME/.config}/sway/config.d/*.conf")'

# Hézagok (Gaps)
gaps inner 5
gaps outer 5

# Automatikus indítás (Autostart)
exec /home/berus/.config/sway/xkb-config
exec wlsunset -t NAPPALI -T ÉJSZAKAI -l SZÉLESSÉG -L HOSSZÚSÁG -g GAMMA
# exec foot -s

Szokás szerint magyarázatokkal ellátott, ill. a Laci cikke is segít a megértésben, így nem részletezném, csak annyi megjegyzés, hogy a billentyűzet beállításokat külön szkriptből állítom be (Fedora féle xkb-config),
ami átveszi a localectl beállításait, de természetesen hagyományosan a fenti fájlban is beállítható pl. a magyar kiosztás. A szkript (Fedora xkb-config):

#!/usr/bin/python3
"""
Sync Sway input configuration with org.freedesktop.locale1.

Usage:
    Configure keyboard mappings with `localectl set-x11-keymap`.
    Add `exec /path/to/script` to your Sway config.

See also:
    https://www.freedesktop.org/software/systemd/man/org.freedesktop.locale1.html

Dependencies: dbus-next, i3ipc
"""
import argparse
import asyncio
import logging
from typing import Any, Dict

from dbus_next import BusType, DBusError, Variant
from dbus_next.aio import MessageBus
from i3ipc.aio import Connection

DEFAULT_DEVICE = 'type:keyboard'
LOG = logging.getLogger("sway.locale1")
LOCALE1_BUS_NAME = "org.freedesktop.locale1"
LOCALE1_OBJECT_PATH = "/org/freedesktop/locale1"
LOCALE1_INTERFACE = "org.freedesktop.locale1"
PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties"
PROPERTIES = {
    'X11Layout': 'layout',
    'X11Model': 'model',
    'X11Variant': 'variant',
    'X11Options': 'options'
}

class Locale1Client:
    """Handle org.freedesktop.locale1 updates and pass XKB configuration to Sway"""

    layout: str = ''
    model: str = ''
    variant: str = ''
    options: str = ''

    def __init__(self,
                 bus: MessageBus,
                 conn: Connection,
                 device: str = DEFAULT_DEVICE):
        self._bus = bus
        self._conn = conn
        self._proxy = None
        self._device = device

    async def connect(self):
        """asynchronous initialization code"""
        introspection = await self._bus.introspect(LOCALE1_BUS_NAME,
                                                   LOCALE1_OBJECT_PATH)
        self._proxy = self._bus.get_proxy_object(LOCALE1_BUS_NAME,
                                                 LOCALE1_OBJECT_PATH,
                                                 introspection)
        self._proxy.get_interface(PROPERTIES_INTERFACE).on_properties_changed(
            self.on_properties_changed)

        locale1 = self._proxy.get_interface(LOCALE1_INTERFACE)
        self.layout = await locale1.get_x11_layout()
        self.model = await locale1.get_x11_model()
        self.variant = await locale1.get_x11_variant()
        self.options = await locale1.get_x11_options()

        await self.update()

    async def on_properties_changed(self,
                                    interface: str,
                                    changed: Dict[str, Any],
                                    _invalidated=None):
        """Handle updates from localed"""
        if interface != LOCALE1_INTERFACE:
            return

        apply = False

        for name, value in changed.items():
            if name not in PROPERTIES:
                continue
            if isinstance(value, Variant):
                value = value.value
            self.__dict__[PROPERTIES[name]] = value
            apply = True

        if apply:
            await self.update()

    async def update(self):
        """Pass the updated xkb configuration to Sway"""
        LOG.info("xkb(%s): layout '%s' model '%s', variant '%s' options '%s'",
                 self._device, self.layout, self.model, self.variant,
                 self.options)
        cmd = [f"input {self._device} xkb_variant ''"]
        cmd.extend([
            f"input {self._device} xkb_{name} '{self.__dict__[name]}'"
            for name in PROPERTIES.values()
        ])
        replies = await self._conn.command(', '.join(cmd))
        for cmd, reply in zip(cmd, replies):
            if reply.error is not None:
                LOG.error("command '%s' failed: %s", cmd, reply.error)

async def main(args: argparse.Namespace):
    """Async entrypoint"""
    try:
        bus = await MessageBus(bus_type=BusType.SYSTEM).connect()
        conn = await Connection(auto_reconnect=False).connect()
        await Locale1Client(bus, conn, device=args.device).connect()

        if not args.oneshot:
            await conn.main()
    except DBusError as exc:
        LOG.error("DBus connection error: %s", exc)
    except (ConnectionError, EOFError) as exc:
        LOG.error("Sway IPC connection error: %s", exc)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Sync Sway input configuration with org.freedesktop.locale1"
    )
    parser.add_argument(
        "-l",
        "--loglevel",
        choices=["critical", "error", "warning", "info", "debug"],
        default="info",
        dest="loglevel",
        help="set logging level",
    )
    parser.add_argument(
        "--device",
        default=DEFAULT_DEVICE,
        metavar='identifier',
        nargs='?',
        type=str,
        help="control settings for a specific device "
        "(see man sway-input; default: %(default)s)",
    )
    parser.add_argument("--oneshot",
                        action='store_true',
                        help="apply current settings and exit immediately")
    args = parser.parse_args()
    logging.basicConfig(level=args.loglevel.upper())
    asyncio.run(main(args))

Tipp: ha a Foot terminált (kifejezetten Wayland-hez tervezve) akarjuk használni, érdemes a foot -s paranccsal szerver módban indítani és footclient-ként meghívni ($term változó), én maradok a Kitty vonalon...

Ha már Wayland, akkor szerintem érdemes a wlroots vonalon (Sway, Hyprland, River)  elindulni, tisztább, szárazabb érzés, ahogy szoktuk volt mondani, ezek a probléma mentesebb megoldások kevesebb anomáliával. Szerintem jelenleg egyértelmüen a wlroots adja a legstabilabb alapot a Wayland-hez.
Az i3 ismeretekkel rendelkezőknek nem is kérdés, a Sway a helyes választás, de a pálcikawm világban újaknak is ez talán a legegyszerűbb kiindulópont ha Wayland alapon építünk rendszert.

Laci i3-as cikke: https://www.linuxmint.hu/blog/2025/09/i3-konfiguralasa-magyarul

Berus