IT Notes

Настройка dwm: Патчи

Расширение функционала dwm производится с помощью патчей. Это менее гибкое решение по сравнению с плагинами. У него есть свои преимущества и недостатки. Но сейчас не о них, а о том, как же применять готовые патчи в своей сборке dwm.

Официальные патчи для dwm ищите на ресурсе: http://dwm.suckless.org/patches/. В этой статье я их буду брать именно оттуда.

Устанавливаем первый dwm-патч: fancybar

Как и во вводной статье по dwm, я буду пользоваться abs в Archlinux. Если вы собираете dwm из исходников, то отличий будет не так уж много. На самом деле, процедура становится даже проще.

Первым делом нужно скачать сам патч. Одним из самых простых является fancybar. Он меняет стандартное поведение dwm таким образом, что на верхней панели отображаются имена всех окон в текущем тэге. По умолчанию же выводится только имя окна в фокусе.

Ссылка на страницу патча fancybar: http://dwm.suckless.org/patches/fancybar. Имеет значение то, какая у вас версия dwm. Патч может не заработать, если версия окажется несовместимой. Узнать версию dwm можно с помощью команды:

dwm -v

У меня dwm-6.1. Поэтому я скачиваю соответствующий патч dwm-6.1-fancybar.diff. Сохранять его лучше всего в /var/abs/local/dwm/.

Патч представляет собой файл описания различий между чистой версией программы и ее доработанной копией.

Если вы используете abs и следуете моим советам из прошлой статьи, то откройте файл /var/abs/local/dwm/PKGBUILD. Отредактируем в нем секции source и prepare():

…

source=(http://dl.suckless.org/dwm/dwm-$pkgver.tar.gz
	config.h
	dwm.desktop
	dwm-6.1-fancybar.diff)

…

prepare() {
  cd $srcdir/$pkgname-$pkgver
  cp $srcdir/config.h config.h
  patch -p1 <../dwm-6.1-fancybar.diff
}

…

Мы изменили только последние строки. Указали, что используем дополнительный файл dwm-6.1-fancybar.diff. А также задали, что перед сборкой нужно применить патч с помощью вызова команды:

patch -p1 <../dwm-6.1-fancybar.diff

Если вы собираете dwm из исходников, то просто используйте патч, выполнив команду выше. Учитывайте пути к файлам.

Теперь осталось обновить dwm. Выполните следующую команду из каталога /var/abs/local/dwm/:

updpkgsums && makepkg -fi

Если все прошло нормально, то после перезапуска dwm панель будет выглядеть так:

dwm-fancybar-patch-thumbnail

Патч dwm для отображения системного трея: systray

Теперь перейдем к более полезному, но и более сложному патчу. В dwm по умолчанию нет никакого системного трея. Исправим это.

Скачать патч systray можно по адресу http://dwm.suckless.org/patches/systray.

Вновь меняем /var/abs/local/dwm/PKGBUILD:

…

source=(http://dl.suckless.org/dwm/dwm-$pkgver.tar.gz
	config.h
	dwm.desktop
	dwm-6.1-fancybar.diff
	dwm-6.1-systray.diff)

…

prepare() {
  cd $srcdir/$pkgname-$pkgver
  cp $srcdir/config.h config.h
  patch -p1 <../dwm-6.1-fancybar.diff
  patch -p1 <../dwm-6.1-systray.diff
}

…

Запускаем команду:

patch -p1 <../dwm-6.1-fancybar.diff

И получаем целую кучу ошибок. Битый патч? Нет. С патчем все в порядке. Загляните в него. И обратите внимание на фрагмент:

diff --git a/config.def.h b/config.def.h
index 7054c06..8393a58 100644
--- a/config.def.h
+++ b/config.def.h
@ -13,6 +13,10 @ static const char selbgcolor[]      = "#005577";
 static const char selfgcolor[]      = "#eeeeee";
 static const unsigned int borderpx  = 1;        /* border pixel of windows */
 static const unsigned int snap      = 32;       /* snap pixel */
+static const unsigned int systraypinning = 0;   /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
+static const unsigned int systrayspacing = 2;   /* systray spacing */
+static const int systraypinningfailfirst = 1;   /* 1: if pinning fails, display systray on the first monitor, 0: display systray on the last monitor*/
+static const int showsystray        = 1;        /* 0 means no systray */
 static const int showbar            = 1;        /* 0 means no bar */
 static const int topbar             = 1;        /* 0 means bottom bar */

Проблема в том, что патч применяется к файлу config.def.h. А при сборке dwm используется наша версия /var/abs/local/dwm/config.h. Добавим в начало этого файла следующие объявления констант:

/* SYSTRAY PATCH */
static const unsigned int systraypinning = 0;   /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
static const unsigned int systrayspacing = 2;   /* systray spacing */
static const int systraypinningfailfirst = 1;   /* 1: if pinning fails, display systray on the first monitor, 0: display systray on the last monitor*/
static const int showsystray        = 1;        /* 0 means no systray */

Сохраняем файл и вновь запускаем сборку dwm. Теперь все хорошо и мы можем порадоваться полноценному системному трею после перезапуска:

dwm-systray-patch

Патч dwm для сохранения позиций плавающих окон: save floats

Патч save floats не настолько полезен, как предыдущие, но на его примере мы рассмотрим еще одну потенциальную проблему. Сам патч всего лишь сохраняет позицию и размеры плавающих окон, которые будут восстановлены, если окно перейдет в тайловый режим и обратно.

Патч я скачал отсюда: http://dwm.suckless.org/patches/save_floats. Затем обновил PKGBUILD. Запустил сборку и увидел сообщение об ошибке:

> updpkgsums && makepkg -fi
# Лишнее я убрал
# …

patching file dwm.c
Hunk #1 succeeded at 106 (offset 19 lines).
Hunk #2 succeeded at 1211 with fuzz 2 (offset 165 lines).
Hunk #3 FAILED at 1646.
1 out of 3 hunks FAILED -- saving rejects to file dwm.c.rej
==> ОШИБКА: Произошел сбой в prepare().
    Прерывание…

Патчи зависят от состояния кода. Если есть несоответствия, то патч применить не получится. В частности, все может сломаться даже от изменения порядка использования патчей. Например, если сначала пропатчить dwm systray, то fancybar уже работать не будет.

Откроем файл /var/abs/local/dwm/src/dwm-6.1/dwm.c.rej:

--- dwm.c.orig	2014-02-09 15:24:10.344116918 +0100
+++ dwm.c	2014-02-09 15:24:10.336116918 +0100
@ -1646,8 +1651,16 @
 		return;
 	selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
 	if(selmon->sel->isfloating)
-		resize(selmon->sel, selmon->sel->x, selmon->sel->y,
-		       selmon->sel->w, selmon->sel->h, False);
+		/*restore last known float dimensions*/
+		resize(selmon->sel, selmon->sel->sfx, selmon->sel->sfy,
+		       selmon->sel->sfw, selmon->sel->sfh, False);
+	else {
+		/*save last known float dimensions*/
+		selmon->sel->sfx = selmon->sel->x;
+		selmon->sel->sfy = selmon->sel->y;
+		selmon->sel->sfw = selmon->sel->w;
+		selmon->sel->sfh = selmon->sel->h;
+	}
 	arrange(selmon);
 }

Здесь указан проблемный фрагмент. Строки, в начале которых стоит -, должны быть убраны, а у которых +, - добавлены.

Сравниваем с оригинальным файлом /var/abs/local/dwm/src/dwm-6.1/dwm.c.orig:

void
togglefloating(const Arg *arg)
{
	if (!selmon->sel)
		return;
	if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
		return;
	selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
	if (selmon->sel->isfloating)
		resize(selmon->sel, selmon->sel->x, selmon->sel->y,
		       selmon->sel->w, selmon->sel->h, 0);
	arrange(selmon);
}

Я скопировал всю функцию, которая будет подвержена изменениям. Теперь давайте поиграем в игру "Найти отличия". Я вижу ровно два. Первое:

if (selmon->sel->isfloating)

В оригинальном файле после if есть пробел. А в патче нет.

Второе:

selmon->sel->w, selmon->sel->h, 0);

В качестве последнего аргумента в оригинальном файле передается 0, а в патче - False.

Исправим файл патча /var/abs/local/dwm/dwm-6.1-save_floats.diff:

URL: http://dwm.suckless.org/patches/historical/save_floats
This patch saves size and position of every floating window before it is forced
into tiled mode. If the window is made floating again, the old dimensions will
be restored.

Index: dwm/dwm.c
===================================================================
--- dwm/dwm.c.orig	2014-02-09 15:24:10.344116918 +0100
+++ dwm/dwm.c	2014-02-09 15:24:10.336116918 +0100
@ -87,6 +87,7 @
 	char name[256];
 	float mina, maxa;
 	int x, y, w, h;
+	int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */
 	int oldx, oldy, oldw, oldh;
 	int basew, baseh, incw, inch, maxw, maxh, minw, minh;
 	int bw, oldbw;
@ -1045,6 +1046,10 @
 	updatewindowtype(c);
 	updatesizehints(c);
 	updatewmhints(c);
+	c->sfx = c->x;
+	c->sfy = c->y;
+	c->sfw = c->w;
+	c->sfh = c->h;
 	XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
 	grabbuttons(c, False);
 	if(!c->isfloating)
@ -1641,8 +1646,16 @
 		return;
 	selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
 	if (selmon->sel->isfloating)
-		resize(selmon->sel, selmon->sel->x, selmon->sel->y,
-		       selmon->sel->w, selmon->sel->h, 0);
+		/*restore last known float dimensions*/
+		resize(selmon->sel, selmon->sel->sfx, selmon->sel->sfy,
+		       selmon->sel->sfw, selmon->sel->sfh, False);
+	else {
+		/*save last known float dimensions*/
+		selmon->sel->sfx = selmon->sel->x;
+		selmon->sel->sfy = selmon->sel->y;
+		selmon->sel->sfw = selmon->sel->w;
+		selmon->sel->sfh = selmon->sel->h;
+	}
 	arrange(selmon);
 }

Повторная попытка привела нас к успеху. Патч работает.

Патч dwm для отображения второй строки статуса: dualstatus

Еще один патч, который не заработал у меня сразу. К тому же, он может оказаться полезным. Встречайте dualstatus. Он позволяет отображать вторую панель в нижней части экрана. Чем не альтернатива conky?

Итак. Скачиваем патч: http://dwm.suckless.org/patches/dualstatus. Делаем все, что нужно. Запускаем на сборку, и получаем ошибку:

> updpkgsums && makepkg -fi
# Лишнее я убрал
# …

patching file dwm.c
Hunk #1 succeeded at 106 (offset 19 lines).
Hunk #2 succeeded at 1211 with fuzz 2 (offset 165 lines).
Hunk #3 succeeded at 1946 (offset 300 lines).
patching file config.def.h
Hunk #1 succeeded at 19 with fuzz 1 (offset 4 lines).
Hunk #2 succeeded at 67 (offset 4 lines).
patching file dwm.c
Hunk #1 succeeded at 166 with fuzz 2 (offset 25 lines).
Hunk #2 succeeded at 248 (offset 31 lines).
Hunk #3 succeeded at 312 (offset 38 lines).
Hunk #4 succeeded at 524 with fuzz 1 (offset 38 lines).
Hunk #5 FAILED at 589.
Hunk #6 succeeded at 883 with fuzz 1 (offset 121 lines).
Hunk #7 succeeded at 1776 (offset 204 lines).
Hunk #8 succeeded at 1953 (offset 239 lines).
Hunk #9 succeeded at 2094 with fuzz 1 (offset 258 lines).
Hunk #10 succeeded at 2114 (offset 258 lines).
Hunk #11 succeeded at 2290 (offset 258 lines).
1 out of 11 hunks FAILED -- saving rejects to file dwm.c.rej
==> ОШИБКА: Произошел сбой в prepare().
    Прерывание…

Снова смотрим проблемный фрагмент в /var/abs/local/dwm/src/dwm-6.1/dwm.c.rej:

--- dwm.c
+++ dwm.c
@ -589,6 +600,7 @ configurenotify(XEvent *e)
 			updatebars();
 			for (m = mons; m; m = m->next)
 				XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
+			XMoveResizeWindow(dpy, eb.win, mons->wx, eb.y, mons->ww, bh);
 			focus(NULL);
 			arrange(NULL);
 		}

Сравниваем с /var/abs/local/dwm/src/dwm-6.1/dwm.c.orig:

void
configurenotify(XEvent *e)
{
	Monitor *m;
	XConfigureEvent *ev = &e->xconfigure;
	int dirty;

	/* TODO: updategeom handling sucks, needs to be simplified */
	if (ev->window == root) {
		dirty = (sw != ev->width || sh != ev->height);
		sw = ev->width;
		sh = ev->height;
		if (updategeom() || dirty) {
			drw_resize(drw, sw, bh);
			updatebars();
			for (m = mons; m; m = m->next)
				resizebarwin(m);
			focus(NULL);
			arrange(NULL);
		}
	}
}

В этот раз отличие сразу бросается в глаза. В патче мы видим:

XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);

А в оригинале:

resizebarwin(m);

Исправляем /var/abs/local/dwm/dwm-6.1-dualstatus.diff:

diff --git a/config.def.h b/config.def.h
index 7054c06..b96107a 100644
--- a/config.def.h
+++ b/config.def.h
@ -15,6 +15,7 @ static const unsigned int borderpx  = 1;        /* border pixel of windows */
 static const unsigned int snap      = 32;       /* snap pixel */
 static const int showbar            = 1;        /* 0 means no bar */
 static const int topbar             = 1;        /* 0 means bottom bar */
+static const int extrabar           = 1;        /* 0 means no extra bar */

 /* tagging */
 static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
@ -62,6 +63,7 @ static Key keys[] = {
 	{ MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
 	{ MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },
 	{ MODKEY,                       XK_b,      togglebar,      {0} },
+	{ MODKEY,                       XK_b,      toggleextrabar, {0} },
 	{ MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
 	{ MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
 	{ MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
diff --git a/dwm.c b/dwm.c
index 0362114..9b7cd74 100644
--- a/dwm.c
+++ b/dwm.c
@ -141,6 +141,13 @ typedef struct {
 	int monitor;
 } Rule;

+typedef struct {
+	int y;
+	int show;
+	Window win;
+	char text[256];
+} Bar;
+
 /* function declarations */
 static void applyrules(Client *c);
 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
@ -210,6 +217,7 @ static void tag(const Arg *arg);
 static void tagmon(const Arg *arg);
 static void tile(Monitor *);
 static void togglebar(const Arg *arg);
+static void toggleextrabar(const Arg *arg);
 static void togglefloating(const Arg *arg);
 static void toggletag(const Arg *arg);
 static void toggleview(const Arg *arg);
@ -266,6 +274,7 @ static Display *dpy;
 static Drw *drw;
 static Monitor *mons, *selmon;
 static Window root;
+static Bar eb;

 /* configuration, allows nested code to access above variables */
 #include "config.h"
@ -477,6 +486,8 @ cleanup(void)
 		while (m->stack)
 			unmanage(m->stack, 0);
 	XUngrabKey(dpy, AnyKey, AnyModifier, root);
+	XUnmapWindow(dpy, eb.win);
+	XDestroyWindow(dpy, eb.win);
 	while (mons)
 		cleanupmon(mons);
 	for (i = 0; i < CurLast; i++)
@ -578,6 +589,7 @ configurenotify(XEvent *e)
 			updatebars();
 			for (m = mons; m; m = m->next)
				resizebarwin(m);
+			XMoveResizeWindow(dpy, eb.win, mons->wx, eb.y, mons->ww, bh);
 			focus(NULL);
 			arrange(NULL);
 		}
@ -751,6 +763,9 @ drawbar(Monitor *m)
 		}
 	}
 	drw_map(drw, m->barwin, 0, 0, m->ww, bh);
+	drw_setscheme(drw, &scheme[SchemeNorm]);
+	drw_text(drw, 0, 0, mons->ww, bh, eb.text, 0);
+	drw_map(drw, eb.win, 0, 0, mons->ww, bh);
 }

 void
@ -1558,6 +1573,7 @ setup(void)
 	root = RootWindow(dpy, screen);
 	drw = drw_create(dpy, screen, root, sw, sh);
 	drw_load_fonts(drw, fonts, LENGTH(fonts));
+	eb.show = extrabar;
 	if (!drw->fontcount)
 		die("no fonts could be loaded.\n");
 	bh = drw->fonts[0]->h + 2;
@ -1699,6 +1715,17 @ togglebar(const Arg *arg)
 }

 void
+toggleextrabar(const Arg *arg)
+{
+	if(selmon == mons) {
+		eb.show = !eb.show;
+		updatebarpos(selmon);
+		XMoveResizeWindow(dpy, eb.win, selmon->wx, eb.y, selmon->ww, bh);
+		arrange(selmon);
+	}
+}
+
+void
 togglefloating(const Arg *arg)
 {
 	if (!selmon->sel)
@ -1810,6 +1837,13 @ updatebars(void)
 		XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
 		XMapRaised(dpy, m->barwin);
 	}
+	if(!eb.win) {
+		eb.win = XCreateWindow(dpy, root, mons->wx, eb.y, mons->ww, bh, 0, DefaultDepth(dpy, screen),
+				       CopyFromParent, DefaultVisual(dpy, screen),
+				       CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
+		XDefineCursor(dpy, eb.win, cursor[CurNormal]->cursor);
+		XMapRaised(dpy, eb.win);
+	}
 }

 void
@ -1823,6 +1857,13 @ updatebarpos(Monitor *m)
 		m->wy = m->topbar ? m->wy + bh : m->wy;
 	} else
 		m->by = -bh;
+	if(m == mons && eb.show) {
+		m->wh -= bh;
+		eb.y = topbar ? m->wy + m->wh : m->wy;
+		m->wy = m->topbar ? m->wy : m->wy + bh;
+	}
+	else
+		eb.y = -bh;
 }

 void
@ -1992,8 +2033,21 @ updatetitle(Client *c)
 void
 updatestatus(void)
 {
-	if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+	char text[512];
+	if(!gettextprop(root, XA_WM_NAME, text, sizeof(text))) {
 		strcpy(stext, "dwm-"VERSION);
+		eb.text[0] = '\0';
+	}
+	else {
+		char *e = strchr(text, ';');
+		if(e) {
+			*e = '\0'; e++;
+			strncpy(eb.text, e, sizeof(eb.text)-1);
+		}
+		else
+			eb.text[0] = '\0';
+		strncpy(stext, text, sizeof(stext)-1);
+	}
 	drawbar(selmon);
 }

Теперь патч работает. Но рано радоваться. Собрать dwm мы все еще не можем. На этот раз произошла ошибка компиляции:

> updpkgsums && makepkg -fi
# Лишнее я убрал
# …

==> Запускается build()…
dwm build options:
CFLAGS   = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os -I/usr/include/X11 -I/usr/include/freetype2 -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION="6.1" -DXINERAMA
LDFLAGS  = -s -L/usr/lib/X11 -lX11 -lXinerama -lfontconfig -lXft
CC       = cc
CC drw.c
In file included from /usr/include/stdio.h:27:0,
                 from drw.c:2:
/usr/include/features.h:148:3: предупреждение: #warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE" [-Wcpp]
 # warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE"
   ^
CC dwm.c
In file included from /usr/include/errno.h:28:0,
                 from dwm.c:23:
/usr/include/features.h:148:3: предупреждение: #warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE" [-Wcpp]
 # warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE"
   ^
dwm.c: В функции «setup»:
dwm.c:1780:12: ошибка: «extrabar» undeclared (first use in this function)
  eb.show = extrabar;
            ^
dwm.c:1780:12: замечание: each undeclared identifier is reported only once for each function it appears in
dwm.c: На верхнем уровне:
dwm.c:1957:1: предупреждение: «toggleextrabar» определена, но нигде не используется [-Wunused-function]
 toggleextrabar(const Arg *arg)
 ^
Makefile:18: ошибка выполнения рецепта для цели «dwm.o»
make: *** [dwm.o] Ошибка 1
==> ОШИБКА: Произошел сбой в build().
    Прерывание…

Из этого сообщения легко понять, что не хватает определения переменной или константы с именем extrabar. Смотрим еще раз в /var/abs/local/dwm/dwm-6.1-dualstatus.diff. И видим, что там и правда объявлена такая константа:

+static const int extrabar           = 1;        /* 0 means no extra bar */

Еще раз возвращаемся к нашему /var/abs/local/dwm/config.h. И добавляем следующее:

/* DUALSTATUS PATCH */
static const int extrabar           = 1;        /* 0 means no extra bar */

Вот теперь все собирается и работает. Чтобы выводить информацию на нижнюю панель, используйте уже знакомую нам команду xsetroot. Сообщение для верхней и нижней панелей нужно отделять с помощью точки с запятой. Например:

setroot -name 'Hello, top!;Hello, bottom!'

И вот как это выглядит:

dwm-dualstatus-patch-thumbnail

Выводы

Установка патчей для dwm довольно творческий и нетривиальный процесс. Нужно обладать минимальными знаниями программирования. А также иметь терпение и внимательность. Если у вас все это есть, то успех гарантирован.

Возникли вопросы по установке какого-то конкретного патча, который я не рассмотрел? Спрашивайте в комментариях.

Похожие публикации

Комментарии

Патч наш!

не могу поставить патч трея, у меня сразу все симптомы что у вас в конце написаны, но я не пойму что и куда вставлять и удалять

xcod:

не могу поставить патч трея, у меня сразу все симптомы что у вас в конце написаны, но я не пойму что и куда вставлять и удалять

Здравствуйте. Под каким дистрибутивом Вы работаете? Попробуйте посмотреть вводную статью по dwm.

Здравствуйте, vladislav. Спасибо за полезный комментарий