Hyprland on Ubuntu 24.04

使用hyprland替换i3wm

去掉apparmor对unprivileged user的处理:

sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

这个是为了应对在启动很多eclectron based的程序的时候会出现的问题,例如Edge/Chrome:

38360:38360:0509/004508.945070:FATAL:credentials.cc(127)] Check failed: . : Permission denied (13)
Trace/breakpoint trap (core dumped)

参考reddit的这个讨论, 另外也可以参考这个, 以及这个.

使用的是github中JaKooLit的安装脚本及其hyprland dot-files. 具体可以参考其仓库的ReadMe

注意点: 脚本运行的时候不要执行sudo apt install或者其他会占用apt-get的操作, 否者可能出现安装脚本也调用apt-get来安装依赖失败, 例如出现类似下面的log:

[NOTE] Installing liblocale-msgfmt-perl ...
E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 266526 (apt)
[ERROR] liblocale-msgfmt-perl failed to install :( , please check the install.log. You may need to install manually! Sorry, I have tried :(
[OK] gettext is already installed. Skipping...
[NOTE] Installing libgtk-3-dev ...
E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 266526 (apt)
[ERROR] libgtk-3-dev failed to install :( , please check the install.log. You may need to install manually! Sorry, I have tried :(
E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 266526 (apt)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?
E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 266526 (apt)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?
E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 266526 (apt)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?
[NOTE] Installing swappy from source...

如果出现了这种情况,可以再次执行这个脚本从新运行。但是可能会耗费很多的时间。

按下Win + h查看快捷键来快速使用:

  • win + enter: terminal
  • win + d: Rofi启动器
  • win + f: 全屏
  • win + 0~9: 切换到对应的workspace

顶上的status bar是waybar, 如果修改了配置,可以使用: win+Alt+R: 重新载入waybar

或者更详细的可以看作者的视频: KooL’s Hyprland-Dotfiles / Configurations Walkthrough

上面的安装完成后并没有中文输入法,下面是配置方法。

Linux下一般使用ibus或者fcitx framework来使用中文输入法,但是在我尝试了ibus以后,发现有许多问题,于是到ibus和hyprland的github repo issue中查看,发现ibus对wayland支持很差,因此换成了fcitx5,按照下面的配置方法,基本上就可以使用了。

sudo apt install fcitx5-frontend-qt5 fcitx5-pinyin fcitx5-config-qt fcitx5-chinese-addons fcitx5-material-color fcitx5-frontend-gtk3 fcitx5-frontend-gtk2 fcitx5-frontend-gtk4

/etc/security/pam_env.conf中添加下面这些内容:

# Wayland compatibility
QT_QPA_PLATFORM         DEFAULT=wayland
CLUTTER_BACKEND         DEFAULT=wayland
SDL_VIDEODRIVER         DEFAULT=wayland
MOZ_ENABLE_WAYLAND      DEFAULT=1
MOZ_WEBRENDER           DEFAULT=1
XDG_SESSION_TYPE        DEFAULT=wayland
XDG_CURRENT_DESKTOP     DEFAULT=sway

# QT-related theming
QT_QPA_PLATFORMTHEME    DEFAULT=qt5ct

# FCITX input-related
#GLFW_IM_MODULE         DEFAULT=ibus
GLFW_IM_MODULE          DEFAULT=fcitx
GTK_IM_MODULE           DEFAULT=fcitx
INPUT_METHOD            DEFAULT=fcitx
XMODIFIERS              DEFAULT=@im=fcitx
IMSETTINGS_MODULE       DEFAULT=fcitx
QT_IM_MODULE            DEFAULT=fcitx
SDL_IM_MODULE           DEFAULT=fcitx

打开文件~/.config/UserConfigs/Startup_Apps.conf,添加:

exec-once = fcitx5 -d

然后重启hyprland(直接重启电脑即可), 然后接下来继续配置。

重启后,会有一个Notification表示fcitx5已经启动:

Fcitx 5 running

也可以用ps aux | grep fcitx5来确认。

打开Fcitx 5 configuration工具, 然后添加输入法(去掉only show current language勾选),然后添加pinyin输入法:

Fcitx 5 add PinYin

还可以设置模糊音:

Fcitx 5 Fuzz

还可以设置备选词数:

Fcitx 5 number

配置字体与主题:

Fcitx 5 font

参考arch wiki, 可以简单的添加alias:

alias code='code --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime'

我的键盘有一个Lock按键,还有一个计算器按键,但是这两个都没有被默认支持,因此需要添加。 对于计算器按键,非常简单,直接添在KeyBinds.conf中添加:

bindl = , xf86Calculator, exec, qalculate

其中这里的xf86Calculator可以参考这个xkbcommon-keysyms.h头文件, 用calcu作为keyword搜索即可。

对于键盘中的锁定按键,我得需要先看看它按下后发送了什么keycode,因此,先安装evtest:

sudo apt-get install -y evtest

然后找到对应的input event设备节点,参考这个文章 的这个部分:

Type the following in your VT.
$cat /proc/bus/input/devices 
You may find a input device something like this in your devices file.
 I: Bus=0011 Vendor=0002 Product=0007 Version=01b1
 N: Name="SynPS/2 Synaptics TouchPad"
 P: Phys=isa0060/serio2/input0
 S: Sysfs=/devices/platform/i8042/serio2/input/input8
 U: Uniq=
 H: Handlers=mouse2 event8
 B: EV=b   B: KEY=420 0 70000 0 0 0 0 0 0 0 0
 B: ABS=11000003 
Or find some other relevant input device which you want to debug.
Take note of X in eventX. In this case its event8 (H: Handlers=mouse2 event8)
Now start evtest by typing in your VT
$ sudo evtest /dev/input/event8
Type an identifier (say a) from the keyboard.

然后按下后可以看到下面的log:

Event: time 1718337038.855395, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e3
Event: time 1718337038.855395, type 1 (EV_KEY), code 125 (KEY_LEFTMETA), value 1
Event: time 1718337038.855395, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7000f
Event: time 1718337038.855395, type 1 (EV_KEY), code 38 (KEY_L), value 1
Event: time 1718337038.855395, -------------- SYN_REPORT ------------
^[[108;9uEvent: time 1718337038.929978, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e3
Event: time 1718337038.929978, type 1 (EV_KEY), code 125 (KEY_LEFTMETA), value 0
Event: time 1718337038.929978, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7000f
Event: time 1718337038.929978, type 1 (EV_KEY), code 38 (KEY_L), value 0
Event: time 1718337038.929978, -------------- SYN_REPORT ------------
^C

关键点就是里面的key code,可以看到按下后实际上是发送了两个按键: Win + L,其中Win为LEFTMETA即左边的Windows按键,然后是L按键。 因此直接添加Win+L也作为锁定按键:

bind = $mainMod, L, exec, $scriptsDir/LockScreen.sh

修改保持后,hyprland会自动加载,即可生效,然后按下按键测试即可。

hyprland锁屏用的是hypridle + hyprlock来实现的,前者对idle进行有些处理,例如idle到了某个时间后就会调用后者进行锁屏。

对于这个问题,在hyprlock的issue中发现其他人也有类似的问题,我想了想,干脆用比这个老一些的wayland的其他锁屏程序:swaylock, 然后发现这个默认的官方仓库已经不维护,无法配合hyprland工作了,因此找了一个新的仓库,还在积极维护的 swaylock-effects. 然后根据里面的提示使用meson + ninja编译即可。 然后将编译好的swaylock elf拷贝到/usr/local/bin/中:

sudo cp build/swaylock /usr/local/bin/

接下来就是替换hyprlock为swaylock了,参考如下修改:

$ cd ~/.config/hypr

$ ag swaylock
scripts/LockScreen.sh
7:#pidof swaylock || swaylock --clock -i ~/Pictures/1.png 
8:pidof swaylock || swaylock --screenshots --clock --indicator --indicator-radius 100 --indicator-thickness 7 --effect-blur 7x5 --effect-vignette 0.5:0.5 --ring-color bb00cc --key-hl-color 880033 --line-color 00000000 --inside-color 00000088 --separator-color 00000000 --grace 2 --fade-in 0.2

hypridle.conf
8:    #lock_cmd = pidof swaylock || swaylock -i ~/Pcitures/1.png # runs hyprlock if it is not already running (this is always run when "loginctl lock-session" is called) 
9:    lock_cmd = pidof swaylock || swaylock --screenshots --clock --indicator --indicator-radius 100 --indicator-thickness 7 --effect-blur 7x5 --effect-vignette 0.5:0.5 --ring-color bb00cc --key-hl-color 880033 --line-color 00000000 --inside-color 00000088 --separator-color 00000000 --grace 2 --fade-in 0.2
21:     on-timeout = pidof swaylock && hyprctl dispatch dpms off # turns off the screen if hyprlock is active
22:     on-resume = pidof swaylock && hyprctl dispatch dpms on    # command to run when activity is detected after timeout has fired.

非常奇怪的一个问题出现了,在准备切换到其他X11 session的时候,发现在gdm3 login的窗口没有其他X11的sessions, 例如安装了xfce4和cinnamon之后,这两个都没有, 而且Ubuntu on Xorg也没有。 尝试调查和安装了其他dm(slim, sddm, lightdm等等),最终发现一个解决的方法, 安装xubuntu-desktop-environment, 然后发现就可以了。

另外还需要去掉前面提到的修改环境变量,对/etc/security/pam_env.conf 文件里面的添加wayland env需要去掉。

在cinnamon中可以直接使用pipewire, 在xfce中最开始发现出现问题,猜测是因为配置了前面的wayland env, 删除后没有重启,因此需要删除后在测试。

  • 截图,尤其是截取菜单的时候非常不方便
  • 腾讯会议共享屏幕基本不行,虽然网上有方法,但是基本上可以认为效果极差而无法使用。
  • swaylock同样存在有时候无法输入password而无法解锁的情况,这个时候依然可以用键盘切换到其他tty。这个时候hyprland已经退出,可以认为是hyprland的bug。
  • 从Windows远程ssh -X访问无法在windows中打开和显示GUI程序。

综上,有的时候还得是X11.

在Ubuntu gnome中有时候期望快速切换到某个具体的程序,可以使用类似下面的方式来处理。

安装必要的工具: xdotoolwmctrl.

在设置中配置自定义快捷键,使用wmctl来切换。先可以用wmctrl -l来查看application/windows的title,例如:

0x05e00004  0 user DeepSeek - Into the unknown and 28 more pages - Personal - Microsoft​ Edge
0x05a00007  0 user /bin/bash
0x05800227  0 user SW
0x02206a2e  0 user Xplorer
0x02e0003e  0 user Inbox - xxxx - Mozilla Thunderbird
0x08800033  0 user retro - GoldenDict-ng
0x08600004  0 user Settings
0x08600021  0 user Keyboard Shortcuts
0x0a00000e -1 user wmctrl -l
0x0560000c -1 user @!0,0;BDHF
0x05600010 -1 user @!1920,0;BDHF

然后根据需要的进行配置,使用的方式类似于: wmctrl -a Thunderbird

也可以设置类似的快捷键来处理: xdotool mousemove 960 540

这个工具还有其他用法,可以结合起来一起用,包括获取当前activeWindows等等,参考下面的命令:

CURRENT_WINDOW_ID=$(xdotool getactivewindow)
ACTIVE_WINDOW_CLASS=$(xprop -id "$CURRENT_WINDOW_ID" WM_CLASS 2>/dev/null)
ACTIVE_WINDOW_TITLE=$(xdotool getwindowname "$CURRENT_WINDOW_ID")
xdotool windowactivate "$PREVIOUS_WINDOW_ID"

例如可以写一个脚本来实现切换到某个程序,同时鼠标移动到这个程序的中心:

#!/bin/bash

# Check if an application partial name was provided as an argument
if [ -z "$1" ]; then
    echo "Usage: $0 <partial_application_name>"
    echo "Example: $0 thund (for Thunderbird)"
    exit 1
fi

# Partial application name from the argument
APP_NAME="$1"

# Define the temporary file to store the previous window ID
PREVIOUS_WINDOW_FILE="/tmp/previous_window_id.txt"

# Load the previous window ID from the file if it exists
if [ -f "$PREVIOUS_WINDOW_FILE" ]; then
    PREVIOUS_WINDOW_ID=$(cat "$PREVIOUS_WINDOW_FILE")
else
    PREVIOUS_WINDOW_ID=""
fi

# Function to move mouse to the center of the specified window
move_mouse_to_window() {
    WINDOW_ID="$1"
    # Get window geometry
    WINDOW_GEOMETRY=$(xdotool getwindowgeometry --shell "$WINDOW_ID")
    eval "$WINDOW_GEOMETRY"  # Exports variables like WIDTH, HEIGHT, X, Y

    # Calculate the center of the window
    CENTER_X=$((X + WIDTH / 2))
    CENTER_Y=$((Y + HEIGHT / 2))

    # Move the mouse to the center of the window
    xdotool mousemove "$CENTER_X" "$CENTER_Y"
}

# Get the current active window and its class/title
CURRENT_WINDOW_ID=$(xdotool getactivewindow)
ACTIVE_WINDOW_CLASS=$(xprop -id "$CURRENT_WINDOW_ID" WM_CLASS 2>/dev/null)
ACTIVE_WINDOW_TITLE=$(xdotool getwindowname "$CURRENT_WINDOW_ID")

# Log the current active window information
echo "Active window class: $ACTIVE_WINDOW_CLASS, title: $ACTIVE_WINDOW_TITLE, target app name: $APP_NAME" >> ~/testing.log

# Check if the active window matches the given partial application name
if [[ "$ACTIVE_WINDOW_CLASS" == *"$APP_NAME"* ]] || [[ "$ACTIVE_WINDOW_TITLE" == *"$APP_NAME"* ]]; then
    # If the specified application is active, switch back to the previous window
    echo "Switching to previous window ID: $PREVIOUS_WINDOW_ID" >> ~/testing.log
    if [ -n "$PREVIOUS_WINDOW_ID" ]; then
        xdotool windowactivate "$PREVIOUS_WINDOW_ID"
    fi
else
    # Store the current window as the previous one in the file before switching
    echo "$CURRENT_WINDOW_ID" > "$PREVIOUS_WINDOW_FILE"

    # Find the target window with a partial name match
    TARGET_WINDOW_ID=$(xdotool search --onlyvisible --name "$APP_NAME" | head -n 1)

    if [ -n "$TARGET_WINDOW_ID" ]; then
        # Activate the matching window
        xdotool windowactivate "$TARGET_WINDOW_ID"
        move_mouse_to_window "$TARGET_WINDOW_ID"
        echo "Activated window ID: $TARGET_WINDOW_ID" >> ~/testing.log
    else
        # If no matching window is found, launch the application
        nohup "${APP_NAME}" &> /dev/null &
        sleep 1  # Give the application time to open
        TARGET_WINDOW_ID=$(xdotool search --onlyvisible --name "$APP_NAME" | head -n 1)
        if [ -n "$TARGET_WINDOW_ID" ]; then
            xdotool windowactivate "$TARGET_WINDOW_ID"
            move_mouse_to_window "$TARGET_WINDOW_ID"
            echo "Launched and activated new window ID: $TARGET_WINDOW_ID" >> ~/testing.log
        fi
    fi
fi

然后保存到某个位置,例如 /home/XXX/bin/toggle_app.sh, 然后设置快捷键的时候可以如下使用:

切换到Microsoft Edge浏览器: /home/XXX/bin/toggle_app.sh Microsoft

在Line 50添加一行还可以达到,如果要求打开的程序已经是activeWindows的话,再按一次快捷键就隐藏/最小化的处理:

  xdotool windowminimize $(xdotool getactivewindow)

这样子就有按一下出来,再按一下隐藏的效果了。 类似于老板键?