XMonad и разные команды на одной клавише в зависимости от текущего layout

Опубликовано 10 Января, 2015 под тегами Cheats, Haskell, tiling, X, XMonad, Cheats, Soft

Последние дни играюсь с XMonad. Для разных целей пользуюсь разными движками. В частности, стандартными Tall и Full и дополнительно, скажем, MosaicAlt. Иногда оказывается удобно сбросить настройки движка на умолчания, однако делается это по-разному. Возникает вопрос, можно ли эти разные действия привязать на одно сочетание клавиш и вызывать нужное в зависимости от текущего движка? Оказывается, можно, хотя гугл рецептов почему-то не предлагает.

В случае стандартных движков, умолчальное сочетание клавиш modm+shift+space делает что-то в таком духе:

setLayout (XMonad.layoutHook conf)

В случае же MosaicAlt, сброс производится как-то так:

sendMessage resetAlt

Определить название текущего движка можно при помощи такой конструкции:

withWindowSet $ description . W.layout . W.workspace . W.current

где W – это XMonad.StackSet:

import qualified XMonad.StackSet as W

current получает текущий стек, workspace – текущее рабочее окружение (ака виртуальный рабочий стол), layout – выбранный в данном рабочем окружении движок. description получает строку-описание движка. withWindowSet вызывает свой аргумент, передавая ему активный StackSet.

На основе имени текущего движка, нам нужно выбрать соответствующую команду. Используем для этого pattern matching:

let
    --  Reset MosaicAlt layout to default
    bindkey "MosaicAlt" = sendMessage resetAlt
    --  Reset the layouts on the current workspace to default
    bindkey layoutName  = setLayout (XMonad.layoutHook conf)
in withWindowSet $ bindkey . description . W.layout . W.workspace . W.current)

layoutName совпадает со всеми шаблонами, кроме определенных ранее (в нашем случае “MosaicAlt”). Те, кто знают haskell, возможно спросят – а почему я обозначил эту переменную, а не использовал “пустышку” _? Дело в том, что setLayout layoutHook сбрасывает не только настройки движков, но и устанавливает умолчальный движок. Чтобы с этим бороться, я предлагаю просто после сброса настроек восстанавливать выбранный движок.

Для этого, во-первых, нужно заменить стандартный оператор выбора движка (|||) на оператор из XMonad.Layout.LayoutCombinators:

import XMonad hiding ( (|||) )
import XMonad.Layout.LayoutCombinators

Затем мы можем использовать сообщение JumpToLayout String все из того же LayoutCombinators:

sendMessage (LC.JumpToLayout layoutName)

Чтобы последовательно выполнить действия в монаде IO (которая обернута внутри монады X a, которую возвращают едва ли не все команды XMonad) – или вообще говоря любой другой – можно использовать оператор “далее” >>, который по сути выбрасывает результат правого вычисления в монаде (однако производит его) и возвращает результат левого.

Окончательно получаем

let
    --  Reset MosaicAlt layout to default
    bindkey "MosaicAlt" = sendMessage resetAlt
    --  Reset the layouts on the current workspace to default
    bindkey layoutName  = setLayout (XMonad.layoutHook conf) >> sendMessage (JumpToLayout layoutName)
in withWindowSet $ bindkey . description . W.layout . W.workspace . W.current)

Всю эту конструкцию можно привязать на нажатие клавиши (например, как второй аргумент пары клавиша-команда в XConfig.keys)

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

P.S. Если это можно сделать проще, милости прошу в комментарии. Я пока не могу похвастаться глубоким знанием XMonad.