StumpWM

27 September 2009 #wm#lisp#emacs

Я уже рассматривал простой тайловый оконный менеджер, который очень удобен в работе – RatPoison. Проблема заключается только в том, что данный оконный менеджер можно настраивать только в рамках, предусмотренных программистами. И ничего более… К примеру, строку информации необходимо уже реализовывать с помощью внешних программ. Что порой бывает не удобно и порой не обладает достаточным функционалом.

Я уже несколько раз до сего момента обращал внимание на тайловый менеджер StumpWM, который написан на языке Lisp и который, судя по описанию, можно настраивать полностью, фактически переписывая код самого оконного менеджера. Но столкнулся с проблемами в установке, а потому довольно надолго оставлял эту затею.

Один раз мне удалось установить stumpwm непосредственно из AUR. Я об этом описывал в статье Установка StumpWM. Но чуть позже, после того, как пакет был обновлен, сборка стала невозможна. Каждый раз прерывалась ошибками, то компиляции, то ошибками сборки самого пакета.

В конце концов Павел Вязовой подсказал мне, что не нужно собирать пакет. Достаточно только откомпилировать пакет и использовать при запуске указание полного пути до папки сбинарником. Что я, естественно, и сделал. Все оказалось много проще, чем я думал.

Подготавливаем систему к установке, для этого ставим пакетыsbcl, cl-ppcre и git, если они еще не были установлены до этого. И создаем файл ~/.sbclrc со следующим содержимым:

(require 'asdf)
(pushnew #p"/usr/share/common-lisp/systems/" asdf:*central-registry* :test #'equal)
(asdf:operate 'asdf:load-op 'cl-ppcre)

Теперь выполняем следующие действия для установки stumpwm:

$ git clone git://git.savannah.nongnu.org/stumpwm.git
$ cd stumpwm
$ autoconf && ./configure && make

Теперь прописываем в ~/.xinitrc строку запуска данного оконного менеджера:

exec /PATH/TO/stumpwm

Теперь можно запускать иксы, или перезапускать их для входа в новое окружение.

Если вы ничего не увидите, не пугайтесь, все нормально. По умолчанию stumpwm не запускает никаких программ и не задает внешний вид своего окружения. Все, что необходимо, можно будет запускать из файла конфигурации stumpwm или из файла ~/.xinitrc, который используется для запуска самого оконного менеджера. У меня данный файл выглядит так:

#!/bin/bash
#
# .xsession/.xinitrc

export G_FILENAME_ENCODING=@locale
export G_BROKEN_FILENAMES=1

export OOO_FORCE_DESKTOP=gnome
export GDK_USE_XFT=1
export EDITOR=emacsclient

#переключение раскладки
#export LC_CTYPE=ru_RU.utf8
export XMODIFIERS="@im=SCIM"
export GTK_IM_MODULE="scim"
export QT_IM_MODULE="scim"
scim -d &

#загрузка коинфига клавиш и внешнего вида
xmodmap ~/.Xmodmap
xrdb ~/.Xdefaults

# D-bus
if which dbus-launch >/dev/null && test -z "$DBUS_SESSION_BUS_ADDRESS"; then
 eval `dbus-launch --sh-syntax --exit-with-session`
fi

#установка параметров клавы, энергосбережения, шрифтов и скорости мыши
xset r rate 350 30
xset -dpms &
xset s off &
xset fp+ /usr/share/fonts/local/
xset fp+ /usr/share/fonts/artwiz-fonts/
xset fp+ ~/.fonts/snap-11/
xset fp rehach
xset m 3

#установка рисунка рабочего стола
eval `cat ~/.fehbg` &

#синхронизация буферов обмена
autocutsel -f

#управление мышью логитех
lomoco -g &

#скрываем курсор мыши через определенное время
unclutter -idle 20 &

# run daemon scripts
/etc/X11/Xsession

#запуск демона автомонтирования
pgrep halevt >> /dev/null || halevt &

#установка темы курсоров
xsetroot -cursor_name left_ptr &

#включение композита
xcompmgr -a &

exec /home/git/stumpwm/stumpwm

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

Для того, чтобы изменить конфигурацию, внешний вид оконного менеджера, необходимо создать в домашней папке файл ~/.stumpwmrc, который в дальнейшем редактируем. Текст файла представляет собой обычную программу на языке программирования lisp.

Для задания внешнего вида фреймов используется следующий код:

(set-font "-*-terminus-medium-r-normal-*-12-*-*-*-*-*-iso10646-1")
(setf *normal-border-width* 0
      *maxsize-border-width* 0
      *transient-border-width* 1
      *window-border-style* :thick)     ; :thick :thin :tight :none

(setf *startup-message* "Never Stop Hacking!"
      *mouse-focus-policy* :sloppy ;; :click, :ignore, :sloppy
      ;;Set the message and input box to the bottom right. This way it overlaps with mode-line.
      *message-window-gravity* :bottom-left
      *input-window-gravity* :bottom-left
      *frame-number-map* "123qwe"
      *window-number-map* "123qwe")

Для определения префикса клавиш используется строка:

(set-prefix-key (kbd "s-s"))

В данном случае назначена комбинация Win-S.

Для определения основных клавиатурных комбинаций лучше всего определить пару функций:

;; define keys
(defmacro defkey-top (key cmd)
   `(define-key *top-map* (kbd ,key) ,cmd))

(defmacro defkeys-top (&rest keys)
   (let ((ks (mapcar #'(lambda (k) (cons 'defkey-top k)) keys)))
   `(progn ,@ks)))

(defmacro defkey-root (key cmd)
   `(define-key *root-map* (kbd ,key) ,cmd))

(defmacro defkeys-root (&rest keys)
   (let ((ks (mapcar #'(lambda (k) (cons 'defkey-root k)) keys)))
   `(progn ,@ks)))

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

(defkeys-top
 ("s-RET"     "exec sakura")
 ("s-p"       "dmenu")
 ("s-R"       "reinit")
 ("s-Q"       "quit")
 ("s-b"       "mode-line"))

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

  1. Используя в качестве команды exec command

  2. Определить функцию через RunOnRaise. Данная функция позволяет, если программа не была запущена до сих пор, запустить ее, а если была запущена, то переключиться на нее. Очень удобно! Сначала прописываем саму функцию, а затем прописываем сочетание клавиш:

     (defcommand firefox () ()
        "Run or switch to firefox."
        (run-or-raise "firefox" '(:class "Firefox")))
    
     (defkeys-root
        ("f"         "firefox"))
    

Для того, чтобы получить информацию о группах и окнах, которые расположены в этих группах или для вывода дополнительной информации, типа температуры процессора, не нужно использовать сторонних программ, достаточно прописать в конфигурации использование modeline… Для этого прописываем в конфигурации следующие строки, которые задают внешний вид строки и описывают ее содержимое.

;;;; The Mode Line
(setf *mode-line-background-color* "DarkRed"
      *mode-line-foreground-color* "White"
      *mode-line-border-color*     "White"
      *mode-line-timeout*          1
      *mode-line-screen-position*  :top
      *window-format*           "^B^8*%n%s%m%30t  :: ^7*"
      *group-format*           "%t"
      *screen-mode-line-format* (list "^B" `(:eval (stumpwm:run-shell-command "printf \"`date +'%a, %d %B %H:%M'`\"" t))
              "  ^ B||  "
              `(:eval (stumpwm:run-shell-command "printf \"`sensors| grep temp3| awk '{print ($2)}'`\"" t))
              "   ||   ^b^7*%g^B   ||   %w  ^1*%N" ; Current group and frames
))

;; turn on/off the mode line for the current screen only.
(if (not (head-mode-line (current-head)))
    (toggle-mode-line (current-screen) (current-head)))

При использовании внешних программ для получения информации следует обратить внимание на то, что вывод обычно заканчивается символом перевода строки. И если в modeline вывести данный вывод, то modeline получиться высотой в две строки, причем вторая строка будет пустовать. Для избежание данной ситуации существует два пути:

  1. Использование потока вывода с преобразованием последовательности символов.

     (run-shell-command "date +'%a %d %b %H:%M:%S' | tr -d [:cntrl:]" t)
    
  2. Использование для вывода функции printf (как это показано выше в конфигурации modeline)

Я использую второй вариант, просто потому, что он мне больше нравиться. Как уже стало понятным, modeline можно разбивать на несколько строк, для получения более информативного вывода.

Необходимо так же помнить, что в modeline до сих пор не реализована возможность выравнивания текста по правому краю.

Для автоматического запуска программ при старте оконного менеджера необходимо использовать следующий код:

(run-shell-command "xmodmap ~/.Xmodmap")
(run-shell-command "xrdb ~/.Xdefaults")

Конфигурирование stumpwm возможно осуществлять несколькими способами:

  1. Правкой файла конфигурации, с последующей его повторной загрузкой

     (defcommand reinit () ()
       (run-commands "reload" "loadrc"))
    
     (defkeys-top
        ("s-R"       "reinit"))
    
  2. Использование сочетаний клавиш для ввода нужного кода lisp. Для этого жмем C-t : и вводим нужные нам функции или команды. В примере используется префикс по умолчанию, если он был изменен в конфиге, то нужно использовать именно его.

В stumpwm так же, как и во многих тайловых оконных менеджерах можно определять правила расположения окон по определенным группам. Сначала нужно описать сами группы:

;; Create groups
(defvar group-names "123qwe")

(dotimes (i 6)
   (define-key *top-map* (kbd (format nil "s-~a" (char group-names i)))
   (format nil "gselect ~a" (1+ i)))
   (define-key *top-map* (kbd (format nil "s-C-~a" (char group-names i)))
   (format nil "gmove ~a" (1+ i))))

(defmacro make-groups (&rest names)
   (let ((ns (mapcar #'(lambda (n) (cat "gnew " n)) names)))
   `(run-commands ,@ns)))

(make-groups "2" "3" "q" "w" "e")
(run-commands "gselect 1" "grename 1")

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

;; Clear rules
(clear-window-placement-rules)

(define-frame-preference "1"
  ;; frame raise lock (lock AND raise == jumpto)
  (0 t   t :title "emacs")
  (0 t   t :class "XTerm"))

(define-frame-preference "2"
  ;; frame raise lock (lock AND raise == jumpto)
  (0 t   t :class "Firefox"))

(define-frame-preference "3"
  ;; frame raise lock (lock AND raise == jumpto)
  (0 t   t :instance "aumix")
  (0 t   t :class "MPlayer")
  (0 t   t :class "Avidemux"))

Можно еще долго перечислять возможности данного оконного менеджера. Но тут я думаю уже остановиться.

Мой файл конфигурации stumpwm можно найти по адресу github.com/Juev/stumpwm-config

И напоследок внешний вид:

StumpWM