воскресенье, 15 февраля 2009 г.

EeePC, Debian,Xmonad с яблочным вкусом, заметки ламера :)

Итак, господа, кажется свершилось и "lenny" таки зарелизили. Не то чтобы на меня этот факт сильно уж повлиял: на рабочем десктопе у меня вялообновляемый unstable, на домашнем сервере "etch", который пока нет никакого желания и большой необходимости обновлять, "lenny" фактически только на EeePC, вот там его таки надо будет обновить до актуального состояния: 49 пакетов требующих обновления набежало. Но все-равно как-то приятно, что любимый дистрибъютив живет, и развивается в своем традиционном ключе.

Выходные выдались на удивление тихими, хотя оживленная рабочая неделя обещала совсем другое, и я продолжил играться с EeePC, в ключе "стянуть привычные вещи с Мака", на этот раз существенной корректировке подвергся конфиг Xmonad'а.

1. Фактически, активно пользоваться множеством рабочих столов я начал только в OS X - в Leopard'е они называются Spaces, и почему-то эти Spaces мне понравились и я нашел им достойное применение. По-умолчанию рабочих стола четыре, на каждом из которых я стараюсь запускать лишь определенные приложения: на первом - IM-клиент, на втором - терминал(ы), на третьем - почтовый клиент, на четвертом - различные браузеры. Такой расклад сложился исторически, и я к нему привык. Остальные приложения запускаются где прийдется, особой стратегии нет.

Что в этом плане прежде всего хотелось от Xmonad'а? - привязать запуск конкретного приложения к конкретному рабочему столу, для каждого рабочего стола установить свои специфические Layout'ы, которые подходят для запускаемых на нем приложений. Оказывается, сделать это достаточно просто. Допустим у нас четыре рабочих стола:

myWorkspaces = ["1","2","3","4"]

Для того чтобы firefox(iceweasel в дебиане) запускался на четвертом, в ManageHooks надо добавить что-то типа:

className =? "Iceweasel" --> doF(W.shift "4")

className можно подсмотреть в выводе утилиты xprop:

diesel@xenocefal:~$ xprop | grep WM_CLASS
WM_CLASS(STRING) = "xterm", "XTerm"

после запуска этой строчки курсор изменит форму и нужно будет щелкнуть им по окну WM_CLASS которого мы хотим узнать.

Вторая задача: каждому воркспейсу по своему Layout'у решается с помощью расширения XMonad.Layout.PerWorkspace, соответствующий import нужно добавить в начало конфига, а затем в layoutHook настройки будут выглядеть следующим образом:

myLayout = avoidStruts $
onWorkspace "1" (( windowNavigation $ (mytabs ****|* mytabs)) ||| mytabs) $
onWorkspaces ["2","3"] mytabs $
onWorkspace "4" (noBorders Full ||| mytabs)$
noBorders Full

я думаю комментировать тут особо нечего.

2. Apple'овцы для своих Spaces, кроме казалось бы логичной схемы переключения между рабочими столами а-ля Alt+F1, or smth like, когда нужно четко указать рабочий стол на который хочешь переключится, сделали еще одну, которой я активно пользуюсь. Основана эта схема переключения на, скажем так, задании относительного пути :) Допустим наша схема расположения рабочих столов выглядит вот так:




12
34

тогда, допустим если я нахожусь на рабочем столе за нумером два, и жму Ctrl + Down - я попадаю на рабочий стол за нумером четыре, Ctrl+Right - приведет на рабочий стол за нумером 3, а Ctrl+Up никуда не приведет, ну и так далее. При четырех рабочих столах, в принципе такими сочетаниями клавиш можно попасть с любого рабочего стола на любой. И хотя, вроде бы, такой способ переключения перечит заветам интерфейсостроителей в плане "хорошо, когда какое-нибудь действие приводит всегда к одному и тому же результату", что в нашем случае можно интерпретировать как "приводит на вполне определенный за каждым конкретным хоткеем рабочий стол", но тем не менее, как я уже сказал, подобную схему нахожу удобной.

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

switchSpace "1" 2 = "1"
switchSpace "1" 4 = "4"
switchSpace "1" 6 = "2"
switchSpace "1" 8 = "3"
switchSpace "2" 2 = "2"
switchSpace "2" 4 = "1"
switchSpace "2" 6 = "3"
switchSpace "2" 8 = "4"
switchSpace "3" 2 = "1"
switchSpace "3" 4 = "2"
switchSpace "3" 6 = "4"
switchSpace "3" 8 = "3"
switchSpace "4" 2 = "2"
switchSpace "4" 4 = "3"
switchSpace "4" 6 = "1"
switchSpace "4" 8 = "4"

не очень красиво, но жить можно, если не понадобится больше рабочих столов. 2,4,6,8 - здесь, это отфонарные коэфициенты для стрелочек, на основе того как они на NumPad'е есть.

Когда такая функция в конфиге появится, в хоткеи можно дописать строчки типа:

((modMask, xK_Right), withWindowSet $ \s -> do windows $ W.view (switchSpace ( W.tag . W.workspace . W.current $ s ) 6))

и если мы ничего не напутали с цифрами - все будет работать. Попробуем немного упростить эту строчку, и сделать так чтобы нам было меньше писать:

((modMask, xK_Right), switchFrom 6)

где switchFrom объявим отдельно как:

switchFrom x = withWindowSet $ \s -> do windows $ W.view (switchSpace (currentTag s) x)
currentTag s = W.tag . W.workspace . W.current $ s

currentTag здесь возвращает название текущего воркспейса, которое нам нужно дать switchSpace для определения воркспейса на который мы хотим переключится, а собственно в функции switchFrom происходит переключение.

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

switchSpace w x = myWorkspaces!!((myNextIndex w x)-1)

в переводе на человеческий: вытащить из списка myWorkspaces элемент за нумером, который получится в результате выполнения функции myNextIndex, ну вернее (myNextIndex-1). w - имя текущего воркспейса, x - "номер ассоциированный со стрелочкой".

myNextIndex w x = myNextWorkspace ( (myElemIndex w myWorkspaces)+1 ) x

здесь myElemIndex находит под каким индексом в списке myWorkspaces спрятано имя текущего десктоп, прибавляет к нему единицу(мне не хочется считать десктопы с нуля), и отдает на съедение myNextWorkspace:

myNextWorkspace current turn | current + turn > 0 && current + turn <= rows * cols = current + turn
| current + turn == 0 && turn == -1 = rows*cols
| current + turn == rows+cols + 1 && turn == 1 = 1
| otherwise = current

myNextWorkspace самая главная функция. Логика работы следующая, мы немного меняем циферки которые передаются при нажатии каждой клавиши:

Up = (-колличество строк)
Down = колличество строк
Left = -1
Up = +1

и.... пробуем: если сумма номера текущего рабочего стола влазит в количество рабочих столов - возвращаем эту сумму, если не влазит - возвращаем другие странные значения. Все вместе это выглядит примерно так. Хоткеи:

[ ((modMask, xK_Right), switchFrom 1)
,((modMask, xK_Left), switchFrom (-1))
,((modMask, xK_Up), switchFrom (-rows))
,((modMask, xK_Down), switchFrom rows) ]



Прочая ерунда:

switchFrom x = withWindowSet $ \s -> do windows $ W.view (switchSpace (currentTag s) x)
currentTag s = W.tag . W.workspace . W.current $ s
rows = 2
cols = 2
myNextWorkspace :: Int->Int->Int
myNextWorkspace current turn | current + turn > 0 && current + turn <= rows * cols = current + turn
| current + turn == 0 && turn == -1 = rows*cols
| current + turn == rows+cols + 1 && turn ==1=1
| otherwise = current myElemIndex :: Eq a => a -> [a] -> Int
myElemIndex x a = head (elemIndices x a)
myNextIndex w x = myNextWorkspace ( (myElemIndex w myWorkspaces)+1 ) x
switchSpace w x = myWorkspaces!!((myNextIndex w x)-1)


Я не силен в haskell'е - много раз начинал разбираться, но кажется надо очень много свободного времени для таких разборок... и этот десяток строк занял у мну достаточно много времени на разбирательства, хотя не скажу что было скучно и неинтересно. Уверен, что можно это записать как-то более разумно, но я рад получить хоть и ugly, но работающий вариант. :)

1 комментарий:

Анонимный комментирует...

дистрибъютив — это сильно :)