Меню

Рабочий стол dwm

Это минималистичный рабочий стол, написанный на языке Си и состоящий из 2000 строк кода. В скомпилированном виде это одиночный исполняемый файл размером 49 кбайт. На самом деле это всего лишь оконный менеджер, но его вполне можно считать рабочим столом управляемым с клавиатуры. Для dwm существуют патчи, расширяющие его функционал и находятся они на официальном сайте https://dwm.suckless.org/patches/.

Перезагружать и выключать придется командами:

shutdown -r now
shutdown -p now

Быстрая установка с необходимыми зависимостями:

pkg install drm-kmod xorg dwm alacritty hack-font google-fonts mc librewolf telegram-desktop lite-xl vim transmission-qt keepassxc mpv claws-mail audacious syncthing wget yt-dlp gnupg mixertui flameshot djview qpdfview-qt5 qview zeal xclip xkb-switch fusefs-ntfs pv 7-zip cloc galculator fusefs-sshfs

sysrc mixer_enable="YES"

Скачать порты и пересобрать drm-kmod для отличных от 14.0 релизов:

git clone https://git.FreeBSD.org/ports.git /usr/ports
git -C /usr/ports switch 2025Q1
cd /usr/ports/graphics/drm-61-kmod/
pkg delete drm-61-kmod
make install clean
pkg install drm-kmod
pkg lock drm-61-kmod

Чтобы заработал mc нужно в файл ~/.shrc дописать одну строчку:

alias mc='mc -u'

Чтобы монтировать удаленные папки по ssh в файл /etc/sysctl.conf добавить строчку:

vfs.usermount=1

Для запуска нужно создать файл ~/.xinitrc и запускать командой startx.

# Переключение раскладок по CapsLock
setxkbmap -model pc105 -layout us,ru -variant ,winkeys -option grp:caps_toggle
# Отключить отключение монитора
xset -dpms
# Скринсейвер через час
xset s 3600 3600
syncthing -no-browser > /dev/null &
exec dwm

Если FreeBSD запускается внутри Virtualbox (включите EFI, время в UTC и видео SVGA), то нужно выполнить еще пять команд. В настройках машины выставить адаптер SVGA и не включать 3d ускорение. Так написано в pkg info -D virtualbox-ose-additions, хотя сам VirtualBox будет ругаться что это неправильная настройка.

pkg install virtualbox-ose-additions
sysrc vboxguest_enable="YES"
sysrc vboxservice_enable="YES"
service vboxguest start
service vboxservice start

А в файл ~/.xinitrc, в начало, добавить строчек (для Windows 10, VirtualBox-6.1.50 и монитора 1680x1050):

# cvt 1680 945
xrandr --newmode "1680_945_60.00" 130.75 1680 1776 1952 2224  945 948 953 981 -hsync +vsync
xrandr --addmode VGA-0 1680_945_60.00
xrandr --output VGA-0 --mode 1680_945_60.00
# чтобы колесико не работало как кнопка "назад" в браузере
xmodmap -e "pointer = 1 2 3 4 5 6 7 0 0 0 0 0"

Но вот был найден интересный альтернативный способ поиска нужного разрешения (уже после установки VirtualBox-7.1.4). Если окно гостевой системы развернуто в полный экран, то появляется это самое нужное дополнительное разрешение. Достаточно запустить команду xrandr и оно будет вверху с пометкой +. Тогда вместо трех команд xrandr можно использовать одну:

xrandr --output VGA-0 --mode 1680x944

После запуска системы нужно каждый раз вводить логин/пароль своего пользователя и выполнять команду startx.

Уже в таком варианте можно пользоваться графическими приложениями. Alt+P запустит dmenu и можно вводить имя приложения для запуска. Alt+Shift+Enter запустит терминал st из пакета sterm. Но терминал st не умеет прокручивать экран вверх чтобы посмотреть вывод команд, поэтому лучше запускать alacritty. Alacritty умеет переходить в режим vim и обратно путем нажатия Ctrl+Shift+Space и осуществлять поиск по нажатию на / в прямом направлении и ? для обратного направления.

Переключаться между рабочими столами можно через Alt+1-9. Выйти из dwm можно нажав Alt+Shift+q. Закрытие приложений обычно выполняется через Ctrl+q, это стандартная комбинация клавиш. Если же у окна нет меню и не реагирует на Ctrl+q, то можно воспользоваться функционалом dwm и нажимать Alt+Shift+c.

Больше информации можно получить на официальном сайте, благо справка там компактная и по существу.

Теперь докрутим dmw до идеала, наложим три патча:

cd /root/
mkdir dwm
cd dwm
wget https://dwm.suckless.org/patches/pertag/dwm-pertag-20200914-61bb8b2.diff
wget https://dwm.suckless.org/patches/pwkl/dwm-pwkl-6.2.diff
wget https://dwm.suckless.org/patches/setstatus/dwm-setstatus-6.2.diff

Теперь нам понадобится создать несколько файлов:

config.h
/* See LICENSE file for copyright and license details. */

/* appearance */
static const unsigned int borderpx  = 1;        /* border pixel of windows */
static const unsigned int snap      = 3;        /* snap pixel */
static const int showbar            = 1;        /* 0 means no bar */
static const int topbar             = 1;        /* 0 means bottom bar */
static const char *fonts[]          = { "monospace:size=10" };
static const char dmenufont[]       = "monospace:size=10";
static const char col_gray1[]       = "#222222";
static const char col_gray2[]       = "#444444";
static const char col_gray3[]       = "#bbbbbb";
static const char col_gray4[]       = "#eeeeee";
static const char col_cyan[]        = "#005577";
static const char *colors[][3]      = {
    /*               fg         bg         border   */
    [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
    [SchemeSel]  = { col_gray4, col_cyan,  col_cyan  },
};

/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };

static const Rule rules[] = {
    /* xprop(1):
     *      WM_CLASS(STRING) = instance, class
     *      WM_NAME(STRING) = title
     */
    /* class             instance    title       tags mask     isfloating   monitor */
    { "Galculator",      NULL,       NULL,       0,            1,           -1       }, // калькулятор
    { "transmission",    NULL,       NULL,       0,            1,           -1       }, // плавающий transmission
    { "librewolf",       "Devtools", NULL,       0,            1,           -1       },
    //{ "Firefox",       NULL,       NULL,       1 << 8,       0,           -1       },
    //{ "Gimp",          NULL,       NULL,       0,            1,           -1       },
};
/* Как определить класс окна:
 * xprop
 * Указатель мыши изменится на перекрестие. Щелкните по окну, класс которого хотите определить.
 * Найдите в выводе строку, начинающуюся с WM_CLASS.
 * Она будет выглядеть примерно так:
 * WM_CLASS(STRING) = "classname", "classname"
 * Первое значение — имя экземпляра.
 * Второе значение — имя класса окна.
 */

/* layout(s) */
static const float mfact     = 0.55; /* factor of master area size [0.05..0.95] */
static const int nmaster     = 1;    /* number of clients in master area */
static const int resizehints = 1;    /* 1 means respect size hints in tiled resizals */
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */

static const Layout layouts[] = {
    /* symbol     arrange function */
    { "[]=",      tile },    /* first entry is default */
    { "><>",      NULL },    /* no layout function means floating behavior */
    { "[M]",      monocle },
};

/* key definitions */
/* Mod1Mask = Atl */
/* Mod4Mask = Win */
#define MODKEY Mod1Mask
#define TAGKEYS(KEY,TAG) \
    { MODKEY,                       KEY,      view,           {.ui = 1 << TAG} }, \
    { MODKEY|ControlMask,           KEY,      toggleview,     {.ui = 1 << TAG} }, \
    { MODKEY|ShiftMask,             KEY,      tag,            {.ui = 1 << TAG} }, \
    { MODKEY|ControlMask|ShiftMask, KEY,      toggletag,      {.ui = 1 << TAG} },

/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }

/* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[]  = { "alacritty", NULL };
static const char *incrvol[] = { "mixer", "vol.volume=+0.05", NULL };
static const char *decrvol[] = { "mixer", "vol.volume=-0.05", NULL };
static const char *mutevol[] = { "mixer", "vol.mute=^", NULL };
static const char *flameshot[] = { "flameshot", "gui", NULL }; // скриншотер
static const char *calc[] = { "galculator", NULL }; // калькулятор

#define XK_XF86AudioLowerVolume 0x1008ff11
#define XK_XF86AudioMute        0x1008ff12
#define XK_XF86AudioRaiseVolume 0x1008ff13
#define XK_XF86Calculator       0x1008ff1d

static const Key keys[] = {
    /* modifier                     key        function        argument */
    { MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
    { MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },
    { 0,                            XK_Print,  spawn,          {.v = flameshot } }, // скриншотер
    { 0,                            XK_XF86Calculator,  spawn, {.v = calc } },      // калькулятор
    { MODKEY,                       XK_b,      togglebar,      {0} },
    { MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
    { MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
    { MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
    { MODKEY,                       XK_d,      incnmaster,     {.i = -1 } },
    { MODKEY,                       XK_h,      setmfact,       {.f = -0.05} },
    { MODKEY,                       XK_l,      setmfact,       {.f = +0.05} },
    { MODKEY,                       XK_Return, zoom,           {0} },
    { MODKEY,                       XK_Tab,    view,           {0} },
    { MODKEY|ShiftMask,             XK_c,      killclient,     {0} },
    { MODKEY,                       XK_t,      setlayout,      {.v = &layouts[0]} },
    { MODKEY,                       XK_f,      setlayout,      {.v = &layouts[1]} },
    { MODKEY,                       XK_m,      setlayout,      {.v = &layouts[2]} },
    { MODKEY,                       XK_space,  setlayout,      {0} },
    { MODKEY|ShiftMask,             XK_space,  togglefloating, {0} },
    { MODKEY,                       XK_0,      view,           {.ui = ~0 } },
    { MODKEY|ShiftMask,             XK_0,      tag,            {.ui = ~0 } },
    { MODKEY,                       XK_comma,  focusmon,       {.i = -1 } },
    { MODKEY,                       XK_period, focusmon,       {.i = +1 } },
    { MODKEY|ShiftMask,             XK_comma,  tagmon,         {.i = -1 } },
    { MODKEY|ShiftMask,             XK_period, tagmon,         {.i = +1 } },
    TAGKEYS(                        XK_1,                      0)
    TAGKEYS(                        XK_2,                      1)
    TAGKEYS(                        XK_3,                      2)
    TAGKEYS(                        XK_4,                      3)
    TAGKEYS(                        XK_5,                      4)
    TAGKEYS(                        XK_6,                      5)
    TAGKEYS(                        XK_7,                      6)
    TAGKEYS(                        XK_8,                      7)
    TAGKEYS(                        XK_9,                      8)
    { MODKEY|ShiftMask,             XK_q,      quit,           {0} },
    { 0x0,              XK_XF86AudioRaiseVolume,    spawn,  {.v = incrvol } },
    { 0x0,              XK_XF86AudioLowerVolume,    spawn,  {.v = decrvol } },
    { 0x0,              XK_XF86AudioMute,       spawn,  {.v = mutevol } },
};

/* button definitions */
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
static const Button buttons[] = {
    /* click                event mask      button          function        argument */
    { ClkLtSymbol,          0,              Button1,        setlayout,      {0} },
    { ClkLtSymbol,          0,              Button3,        setlayout,      {.v = &layouts[2]} },
    { ClkWinTitle,          0,              Button2,        zoom,           {0} },
    { ClkStatusText,        0,              Button2,        spawn,          {.v = termcmd } },
    { ClkClientWin,         MODKEY,         Button1,        movemouse,      {0} },
    { ClkClientWin,         MODKEY,         Button2,        togglefloating, {0} },
    { ClkClientWin,         MODKEY,         Button3,        resizemouse,    {0} },
    { ClkTagBar,            0,              Button1,        view,           {0} },
    { ClkTagBar,            0,              Button3,        toggleview,     {0} },
    { ClkTagBar,            MODKEY,         Button1,        tag,            {0} },
    { ClkTagBar,            MODKEY,         Button3,        toggletag,      {0} },
};
config.mk.diff
--- config.mk.orig	2024-08-31 17:56:01.575985000 +0300
+++ config.mk	2024-08-31 17:56:58.462037000 +0300
@@ -7,8 +7,8 @@
 PREFIX = /usr/local
 MANPREFIX = ${PREFIX}/share/man
 
-X11INC = /usr/X11R6/include
-X11LIB = /usr/X11R6/lib
+X11INC = ${PREFIX}/include
+X11LIB = ${PREFIX}/lib
 
 # Xinerama, comment if you don't want it
 XINERAMALIBS  = -lXinerama
@@ -16,7 +16,7 @@
 
 # freetype
 FREETYPELIBS = -lfontconfig -lXft
-FREETYPEINC = /usr/include/freetype2
+FREETYPEINC = ${PREFIX}/include/freetype2
 # OpenBSD (uncomment)
 #FREETYPEINC = ${X11INC}/freetype2
 #MANPREFIX = ${PREFIX}/man
install.sh
#!/bin/sh
DST1=/usr/ports/x11-wm/dwm
DST2=$DST1/work/dwm-6.5
SRC=/root/dwm
cd $DST1
make patch
cd $DST2
patch < $SRC/dwm-pertag-20200914-61bb8b2.diff
patch < $SRC/dwm-pwkl-6.2.diff
patch < $SRC/dwm-setstatus-6.2.diff
patch < $SRC/config.mk.diff
cp $SRC/config.h $DST2/
make deinstall
make install
reinstall.sh
#!/bin/sh
DST1=/usr/ports/x11-wm/dwm
DST2=$DST1/work/dwm-6.5
SRC=/root/dwm
cd $DST1
#make deinstall
cp $SRC/config.h $DST2/
touch $DST2/config.h
make restage reinstall

Теперь выполняем команды чтобы пересобрать dwm

chmod +x install.sh
chmod +x reinstall.sh

И перезагружаемся чтобы запустился новый dwm shutdown -r now.

Теперь все готово для того чтобы в правом верхнем углу выводить время вместо dwm-6.5. Создаем скрипт и добавляем ему бит исполнимости:

~/bin/dwm_status
#!/bin/sh
while true; do
    sleep 10
    dwm -s "$(date "+%e %b %a %H:%M")"
done

А в .xinitrc добавляем строчку перед exec dwm:

dwm_status &

Донастроим alacritty чтобы был получше шрифт в котором нельзя спутать буквы.

Что еще полезного можно дописать в .xinitrc - замедлить движение мыши, но это зависит от модели. Ниже приведен пример для Razer Viper Mini. Добавлять нужно перед `exec dwm`.

# Мышка
array="
Razer Razer Viper Mini Mouse
Razer Razer Viper Mini
Razer product 0x008a Mouse
Razer product 0x008a
vendor 0x1532 product 0x008a Mouse
vendor 0x1532 product 0x008a
vendor 0x1532 Razer Viper Mini Mouse
vendor 0x1532 Razer Viper Mini
"
IFS="
"
for i in $array
do
    echo "test $i"
    LINE=$(xinput list | grep "$i.*pointer")
    if [ ! -z $LINE ]
    then
        echo $LINE
        MOUSE_ID=$(echo $LINE | sed 's/.*id=//' | sed 's/\t.*//')
        echo $MOUSE_ID
        xinput set-prop $MOUSE_ID 296 -1.0
        echo xinput set-prop $MOUSE_ID 296 -1.0
    fi
done

Ну а чтобы сбрасывать клавиатуру Axeman, чтобы работали мультимедийные клавиши, нужно создать скрипт:

/etc/rc.local
#!/bin/sh

echo ""

sleep 1
DEV=$(usbconfig | grep SEMICO | sed 's/^\(.......\).*/\1/')
if [ ! -z $DEV ]; then
    echo reset keyboard \"$DEV\"
    usbconfig -d $DEV reset
fi
# В /boot/loader.conf добавить
# hw.usb.usbhid.enable=1

Чтобы победить тиринг на Nvidia создаем файлик:

/usr/local/etc/X11/xorg.conf.d/video.conf
Section "Screen"
    Identifier "Screen0"
    Device "Card0"
    SubSection "Display"
        Modes "1680x1050"
    EndSubSection
    Option "metamodes" "nvidia-auto-select +0+0 { ForceFullCompositionPipeline = On }"
EndSection

Чтобы подключать ntfs-диски нужно дописать в /boot/loader.conf

fusefs_load="YES"

Это модуль ядра и нужно либо перезагрузиться, либо набрать kldload fusefs.

Полезные докрутки

~/.profile
EDITOR=vim; export EDITOR
PAGER='less -i'; export PAGER
~/.shrc
alias l='ls -l -a -h'

Чтобы включить полный хинтинг шрифтов нужно создать файл

~/.config/fontconfig/fonts.conf
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
    <!-- Включить сглаживание и хинтинг -->
    <match target="font">
        <edit name="antialias" mode="assign"><bool>true</bool></edit>
        <edit name="hinting" mode="assign"><bool>true</bool></edit>
        <edit name="hintstyle" mode="assign"><const>hintfull</const></edit>
        <!--edit name="hintstyle" mode="assign"><const>hintmedium</const></edit-->
        <!--edit name="hintstyle" mode="assign"><const>hintslight</const></edit-->
        <edit name="rgba" mode="assign"><const>rgb</const></edit>
        <edit name="lcdfilter" mode="assign"><const>lcddefault</const></edit>
    </match>
</fontconfig>

Обновить кеш шрифтов командой fc-cache -fv и перезапустить X-сервер.

Комментировать