Xmonad (Вопросы и обсуждение)
Модератор: Модераторы разделов
-
- Сообщения: 1137
- ОС: fedora
Re: Xmonad
Вариант с eventHadler-ом даёт как раз нужное поведение.
-
- Сообщения: 586
- Статус: -
Re: Xmonad
Что касается ответа на ваш старый вопрос..
Назовем workspace, на котором нельзя открывать окна, lock workspace. Тогда определим функцию для выбора какого-то другого workspace, куда окно будет перекинуто в случае, если оно должно быть помещено на lock:
Тогда manageHook для реализации lock workspace можно сделать так:
1. В первом варианте будем перемещать окно в фокусе на anotherWorkspace только, если текущий workspace - lock:
В этой реализации:
- если в ManageHook используется focusDown (чтобы избежать изменения фокуса при открытии новых окон), на anotherWorkspace переместится *не* новое окно, а какое-то из открытых ранее.
- если текущий workspace не lock и новое окно открывается на lock workspace (например, так `className =? "XClock" --> doShift "7"`), то этот manageLockWorkspace вообще не сработает.
2. Второй вариант работает всегда (те вне зависимости от фокуса и текущего workspace):
Разница между этими вариантами - какая функция используется для перемещения окна: в первом - shift, во втором - shiftWin.
Также, хочу обратить внимание, что получение workspace, где откроется новое окно (с помощью `findTag`), должно производится в pure функции (Endo WindowSet), которую возвращает ManageHook. Те, это нельзя делать в Query монаде - такой вариант работать вообще не будет:
Дело в том, что все действия в монаде запускаются до того, как новое окно будет добавлено в WindowSet (из XMonad/Operations.hs):
Все действия в manageHook отработали, чтобы получить функцию g . Новое окно добавляется в WindowSet pure функцией f (определенной тоже в `manage`), и, соответственно, только у функции g есть возможность работать с WindowSet-ом, в котором уже есть новое окно (чтобы определить workspace, где будет помещено окно, нужно работать именно с WindowSet-ом).
Еще хочу обратить внимание, что в случае использования composeOne (из XMonad.Hooks.ManageHelpers) или перемещений окна на другой workspace с помощью `className =? "XClock" --> doShift "7"` и подобного, надо следить, чтобы manageLockWorkspace запускался последним (те самый левый член в сумме моноидов <+>), тк моноид Endo - это просто композиция функций, в которой члены слева применяются позже. Другими словами, функция "видит" окно там, куда его поместили функции, выполнившиеся до нее, а не после.
Код: Выделить всё
import Data.Maybe
import Control.Applicative
import XMonad
import qualified XMonad.StackSet as W
Назовем workspace, на котором нельзя открывать окна, lock workspace. Тогда определим функцию для выбора какого-то другого workspace, куда окно будет перекинуто в случае, если оно должно быть помещено на lock:
Код: Выделить всё
anotherWorkspace :: WorkspaceId -> WindowSet -> WorkspaceId
anotherWorkspace t = head . filter (/= t) . map W.tag . W.workspaces
Тогда manageHook для реализации lock workspace можно сделать так:
1. В первом варианте будем перемещать окно в фокусе на anotherWorkspace только, если текущий workspace - lock:
Код: Выделить всё
-- Move away focused window (on current workspace), if current workspace is
-- "locked". Note, that such implementation will move *not* new window, if
-- focusDown is used (for keeping focus still, when new window appears), and
-- does not work at all, if new window gets moved to "lock" workspace, while
-- current workspace is another (not "lock").
manageLockWorkspace :: WorkspaceId -> ManageHook
manageLockWorkspace t = do
w <- ask
--doF $ \ws -> lockWorkspace t (W.findTag w ws) ws
doF $ flip (lockWorkspace t) <*> W.findTag w
lockWorkspace :: WorkspaceId -> Maybe WorkspaceId -> WindowSet -> WindowSet
lockWorkspace t = maybe id $ \i ->
if i == t
then flip W.shift <*> (anotherWorkspace t)
else id
В этой реализации:
- если в ManageHook используется focusDown (чтобы избежать изменения фокуса при открытии новых окон), на anotherWorkspace переместится *не* новое окно, а какое-то из открытых ранее.
- если текущий workspace не lock и новое окно открывается на lock workspace (например, так `className =? "XClock" --> doShift "7"`), то этот manageLockWorkspace вообще не сработает.
2. Второй вариант работает всегда (те вне зависимости от фокуса и текущего workspace):
Код: Выделить всё
-- Moves away new window from "lock" workspace regardless of current workspace
-- and focus.
manageLockWorkspace2 :: WorkspaceId -> ManageHook
manageLockWorkspace2 t = ask >>= doF . lockWorkspace2 t
lockWorkspace2 :: WorkspaceId -> Window -> WindowSet -> WindowSet
lockWorkspace2 t w ws = fromMaybe ws $ do
i <- W.findTag w ws
return $ if i == t
then W.shiftWin (anotherWorkspace t ws) w ws
else ws
Разница между этими вариантами - какая функция используется для перемещения окна: в первом - shift, во втором - shiftWin.
Также, хочу обратить внимание, что получение workspace, где откроется новое окно (с помощью `findTag`), должно производится в pure функции (Endo WindowSet), которую возвращает ManageHook. Те, это нельзя делать в Query монаде - такой вариант работать вообще не будет:
Код: Выделить всё
-- Not working version of "moving away new window".
manageLockWorkspace3 :: WorkspaceId -> ManageHook
manageLockWorkspace3 t = do
w <- ask
mi <- liftX $ withWindowSet (return . W.findTag w)
trace (show mi)
doF $ \ws -> flip (maybe ws) mi $ \i ->
if i == t
then W.shiftWin (anotherWorkspace t ws) w ws
else ws
Дело в том, что все действия в монаде запускаются до того, как новое окно будет добавлено в WindowSet (из XMonad/Operations.hs):
Код: Выделить всё
manage ... =
mh <- asks (manageHook . config)
g <- appEndo <$> userCodeDef (Endo id) (runQuery mh w)
windows (g . f)
Все действия в manageHook отработали, чтобы получить функцию g . Новое окно добавляется в WindowSet pure функцией f (определенной тоже в `manage`), и, соответственно, только у функции g есть возможность работать с WindowSet-ом, в котором уже есть новое окно (чтобы определить workspace, где будет помещено окно, нужно работать именно с WindowSet-ом).
Еще хочу обратить внимание, что в случае использования composeOne (из XMonad.Hooks.ManageHelpers) или перемещений окна на другой workspace с помощью `className =? "XClock" --> doShift "7"` и подобного, надо следить, чтобы manageLockWorkspace запускался последним (те самый левый член в сумме моноидов <+>), тк моноид Endo - это просто композиция функций, в которой члены слева применяются позже. Другими словами, функция "видит" окно там, куда его поместили функции, выполнившиеся до нее, а не после.
-
- Сообщения: 586
- Статус: -
Re: Xmonad
Не прошло и года, как я могу предложить другой способ реализации рабочего стола блокировки (lock workspace), те просто рабочего стола, на котором не создаются новые окна:
В отличие от предыдущего здесь я использовал предикат `newOn` для определения, что новое окно появилось на рабочем столе блокировки. Те то, про что выше я писал, можно определить только в pure функции (WindowSet -> WindowSet). И теперь.. пришло время переписывать xmonad, xmonad сам себе не перепишет представить модуль XMonad.Hooks.Focus . Насколько мне известно, в xmonad-contrib нету пока что модуля, предоставляющего похожие возможности (хотя я не очень старался его найти).
Модуль реализует монад над Query, который предоставляет дополнительную информацию о новом окне:
- рабочий стол, где появится новое окно.
- сфокусированное (сейчас) окно на этом рабочем столе.
- текущий рабочий стол (просто для полноты информации, это можно узнать и без всяких модулей).
И два флага в extensible state:
- включена ли блокировка фокуса? Если блокировка включена, то библиотечные функции (этого модуля) не будут перемещать фокус на новое окно.
- было ли новое окно активировано (_NET_ACTIVE_WINDOW)? Точнее, в этом случае это уже будет не новое окно, но с ним можно работать, как с новым.
Модуль также предоставляет операции "поднимающие" (lift) стандартные комбинаторы ManageHook-а в монад FocusQuery, чтобы их (комбинаторы) можно было запускать на сфокусированном окне, и функции, работающие в FocusQuery, для сохранения фокуса и рабочего стола, перемещения фокуса и переключения рабочих столов.
Пример FocusHook-а:
Вот пример перемещения всех активируемых окон на текущий рабочий стол с сохранением фокуса (на текущем окне):
Обратите внимание, что нужны более общие версии `composeOne` и (-?>), чем определены в X.H.ManageHelpers. К сожалению, X.H.ManageHelpers предоставляет комбинаторы, работающие только на ManageHook, хотя многие (к счастью) легко переписать на более общие.
Ограничения:
- детям до 18 лет..
- Модуль запускает весь `manageHook` для активированного окна. Это значит, что если какие-то действия должны выполняться только для новых окон, перед ними надо добавить предикат `not <$> activated`. После этого, соответственно, тип выражения станет FocusHook, и выражение надо будет сконвертировать обратно в ManageHook с помощью `manageFocus` .
- Тк этот модуль обрабатывает активацию окон, он не совместим с `ewmh` из X.H.EwmhDesktops. Те если их включить вместе, все скомпилится, просто активированное окно в результате окажется там, куда его отправит `ewmh`, и комбинаторы этого модуля не сработают.
Код модуля gist. Это версия, которую можно скопировать в '~/.xmonad/lib/XMonad/Hooks/Focus.hs' . Она компилируется на Haskell Platform 2013.2.0.0 (ghc 7.6.3), xmonad 0.12 на Debian 8 .
Репозиторий, в котором есть этот этот модуль - github/sgf-xmonad-modules. И мой конфиг xmonad-а, который включает этот модуль, собирается `stack`-ом и устанавливается `make install`-ом - sgf-xmonad-config (автоматика!).
На данный момент для модуля нету тестов, и докуменатция написана не в haddock формате (хотя комменты с примерами есть).
Перед использованием, прочитайте инструкцию в комментах (в начале файла).
Код: Выделить всё
manageLock :: WorkspaceId -> (WindowSet -> WorkspaceId) -> ManageHook
manageLock lockWs anotherWs = manageFocus (newOn lockWs --> moveTo anotherWs)
moveTo :: (WindowSet -> WorkspaceId) -> FocusHook
moveTo anotherWs = new $ asks pure >>= doF . (shiftWin <*> anotherWs <*>)
where
shiftWin :: WindowSet -> WorkspaceId -> Window -> WindowSet
shiftWin ws i w = W.shiftWin i w ws
В отличие от предыдущего здесь я использовал предикат `newOn` для определения, что новое окно появилось на рабочем столе блокировки. Те то, про что выше я писал, можно определить только в pure функции (WindowSet -> WindowSet). И теперь.. пришло время переписывать xmonad, xmonad сам себе не перепишет представить модуль XMonad.Hooks.Focus . Насколько мне известно, в xmonad-contrib нету пока что модуля, предоставляющего похожие возможности (хотя я не очень старался его найти).
Модуль реализует монад над Query, который предоставляет дополнительную информацию о новом окне:
- рабочий стол, где появится новое окно.
- сфокусированное (сейчас) окно на этом рабочем столе.
- текущий рабочий стол (просто для полноты информации, это можно узнать и без всяких модулей).
И два флага в extensible state:
- включена ли блокировка фокуса? Если блокировка включена, то библиотечные функции (этого модуля) не будут перемещать фокус на новое окно.
- было ли новое окно активировано (_NET_ACTIVE_WINDOW)? Точнее, в этом случае это уже будет не новое окно, но с ним можно работать, как с новым.
Модуль также предоставляет операции "поднимающие" (lift) стандартные комбинаторы ManageHook-а в монад FocusQuery, чтобы их (комбинаторы) можно было запускать на сфокусированном окне, и функции, работающие в FocusQuery, для сохранения фокуса и рабочего стола, перемещения фокуса и переключения рабочих столов.
Пример FocusHook-а:
Код: Выделить всё
-- FocusHook для активированных окон.
activateFocusHook :: FocusHook
activateFocusHook = composeAll
-- Не перемещать фокус, если `gmrun` сфокусирован на
-- рабочем столе, где появилось активированное окно.
-- Переключение рабочего стола все еще может произойти.
[ focused (className =? "Gmrun")
--> keepFocus
-- Поведение по умолчанию для активированных окон:
-- переключиться на рабочий стол с активированным окном и
-- переместить на него фокус.
, return True --> switchWorkspace <+> switchFocus
]
-- FocusHook для новых окон.
newFocusHook :: FocusHook
newFocusHook = composeOne
-- Всегда перемещать фокус на (новое окно) `gmrun`.
[ new (className =? "Gmrun") -?> switchFocus
-- Всегда сохранять фокус на окне `gmrun`. Если новое
-- окно тоже `gmrun`, то фокус переместится на него.
, focused (className =? "Gmrun") -?> keepFocus
-- Если на текущем рабочем столе сфокусировано диалоговое
-- окно firefox-а (например, для ввода мастер-пароля) и
-- новое окно появляется тоже на текущем рабочем столе,
-- не перемещать фокус на новое окно.
, newOnCur <&&> focused
((className =? "Iceweasel" <||> className =? "Firefox") <&&> isDialog)
-?> keepFocus
-- Поведение по умолчанию для новых окон: перемещать
-- фокус на новое окно.
, return True -?> switchFocus
]
Вот пример перемещения всех активируемых окон на текущий рабочий стол с сохранением фокуса (на текущем окне):
Код: Выделить всё
import Data.Monoid
import Control.Applicative
import Control.Monad
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Hooks.Focus
import XMonad.Hooks.ManageHelpers hiding (composeOne, (-?>))
main :: IO ()
main = do
let xcf = handleFocusQuery (Just (0, xK_v)) (composeOne
[ activated -?> (newOnCur --> keepFocus)
, Just <$> newFocusHook
])
$ defaultConfig
{ modMask = mod4Mask
, manageHook = manageFocus activateOnCurrentWs
}
xmonad xcf
activateOnCurrentWs :: FocusHook
activateOnCurrentWs = activated --> asks currentWorkspace >>=
new . unlessFocusLock . doShift
composeOne :: (Monoid a, Monad m) => [m (Maybe a)] -> m a
composeOne [] = return mempty
composeOne (mx : xs) = do
x <- mx
case x of
Just y -> return y
Nothing -> composeOne xs
infixr 0 -?>
(-?>) :: Monad m => m Bool -> m a -> m (Maybe a)
(-?>) mb mx = do
b <- mb
if b
then liftM Just mx
else return Nothing
Обратите внимание, что нужны более общие версии `composeOne` и (-?>), чем определены в X.H.ManageHelpers. К сожалению, X.H.ManageHelpers предоставляет комбинаторы, работающие только на ManageHook, хотя многие (к счастью) легко переписать на более общие.
Ограничения:
- детям до 18 лет..
- Модуль запускает весь `manageHook` для активированного окна. Это значит, что если какие-то действия должны выполняться только для новых окон, перед ними надо добавить предикат `not <$> activated`. После этого, соответственно, тип выражения станет FocusHook, и выражение надо будет сконвертировать обратно в ManageHook с помощью `manageFocus` .
- Тк этот модуль обрабатывает активацию окон, он не совместим с `ewmh` из X.H.EwmhDesktops. Те если их включить вместе, все скомпилится, просто активированное окно в результате окажется там, куда его отправит `ewmh`, и комбинаторы этого модуля не сработают.
Код модуля gist. Это версия, которую можно скопировать в '~/.xmonad/lib/XMonad/Hooks/Focus.hs' . Она компилируется на Haskell Platform 2013.2.0.0 (ghc 7.6.3), xmonad 0.12 на Debian 8 .
Репозиторий, в котором есть этот этот модуль - github/sgf-xmonad-modules. И мой конфиг xmonad-а, который включает этот модуль, собирается `stack`-ом и устанавливается `make install`-ом - sgf-xmonad-config (автоматика!).
На данный момент для модуля нету тестов, и докуменатция написана не в haddock формате (хотя комменты с примерами есть).
Перед использованием, прочитайте инструкцию в комментах (в начале файла).
-
- Сообщения: 1137
- ОС: fedora
Re: Xmonad
А известно ли кому решение, для:
1. Отключения gaps для workspace.
2. Отключения gaps для layout.
3. Отключения gaps для любых workspace и layout на мониторе N?
1. Отключения gaps для workspace.
2. Отключения gaps для layout.
3. Отключения gaps для любых workspace и layout на мониторе N?
-
- Сообщения: 1137
- ОС: fedora
Re: Xmonad
Ну и ещё вопрос: можно как-то запретить приложениям самовольно переключать workspace? Особенно касается всякого хлама на электроне, который не может начать работу до того, как установит соединение.