Используйте ввод-вывод при создании конфигурации Xmonad (ключевая карта зависит от количества подключенных мониторов)

#haskell #xmonad

Вопрос:

Я пытаюсь настроить различные сопоставления клавиш Xmonad в зависимости от количества подключенных мониторов. Причина в том, что я использую один и тот же конфигурационный файл Xmonad в нескольких системах (настольные компьютеры, ноутбук с различными конфигурациями мониторов, включая 3 дисплея). Дисплеи перечислены в разном порядке в разных системах, поэтому мне нужно жестко закодировать индексы отображения при использовании 3-х мониторов.

Моя нынешняя лучшая попытка-что-то в этом роде (все, что не имеет отношения к делу, было удалено).:

 import qualified Graphics.X11.Xlib as X11
import qualified Graphics.X11.Xinerama as X11

screenKeysFor2Monitors conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
  [((m .|. mod4Mask, key), screenWorkspace sc >>= flip whenJust (windows . f)) -- Replace 'mod1Mask' with your mod key of choice.
      | (key, sc) <- zip [xK_w, xK_e] [0, 1] -- Usual screen order
      , (f, m) <- [(W.view, 0), (W.shift, shiftMask), (W.greedyView, mod1Mask)]]

screenKeysFor3Monitors conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
  [((m .|. mod4Mask, key), screenWorkspace sc >>= flip whenJust (windows . f)) -- Replace 'mod1Mask' with your mod key of choice.
      | (key, sc) <- zip [xK_w, xK_e, xK_q] [0, 2, 1] -- hardcoded according to laptop driver
      , (f, m) <- [(W.view, 0), (W.shift, shiftMask), (W.greedyView, mod1Mask)]]


screenKeys x = do
        numberOfScreens <- getScreens
        keyConfig <- case numberOfScreens of
                        3 -> screenKeysFor3Monitors x
                        _ -> screenKeysFor2Monitors x
        return keyConfig

-- | Get number of screens
getScreens = do
  screens <- do
    dpy <- X11.openDisplay ""
    rects <- X11.getScreenInfo dpy
    X11.closeDisplay dpy
    return rects
  pure $ length screens

xmonadConfig = ewmh xfceConfig{
          modMask = mod4Mask
          keys = MyKeys.screenKeys
}

 

Я получаю эту ошибку

 Error detected while loading xmonad configuration file: /home/me/.xmonad/xmonad.hs

lib/MyXMonad/Keys.hs:51:64: error:
    * Couldn't match expected type `M.Map (KeyMask, KeySym) (X ())`
                  with actual type `IO (X ())`
    * In the expression: (screenKeys x)
      In the second argument of `($)`, namely
        `[(myKeysToAdd x), (workspaceKeys x), (screenKeys x)]`
      In the expression:
        M.unions $ [(myKeysToAdd x), (workspaceKeys x), (screenKeys x)]
   |
51 | keysToAdd x = M.unions $ [(myKeysToAdd x), (workspaceKeys x), (screenKeys x)]
   |                                                                ^^^^^^^^^^^^

lib/MyXMonad/Keys.hs:242:30: error:
    * Couldn't match type `M.Map (KeyMask, KeySym)` with `IO`
      Expected type: IO (X ())
        Actual type: M.Map (KeyMask, KeySym) (X ())
    * In the expression: screenKeysFor3Monitors x
      In a case alternative: 3 -> screenKeysFor3Monitors x
      In a stmt of a 'do' block:
        keyConfig <- case numberOfScreens of
                       3 -> screenKeysFor3Monitors x
                       _ -> screenKeysFor2Monitors x
    |
242 |                         3 -> screenKeysFor3Monitors x
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^

lib/MyXMonad/Keys.hs:243:30: error:
    * Couldn't match type `M.Map (KeyMask, KeySym)` with `IO`
      Expected type: IO (X ())
        Actual type: M.Map (KeyMask, KeySym) (X ())
    * In the expression: screenKeysFor2Monitors x
      In a case alternative: _ -> screenKeysFor2Monitors x
      In a stmt of a 'do' block:
        keyConfig <- case numberOfScreens of
                       3 -> screenKeysFor3Monitors x
                       _ -> screenKeysFor2Monitors x
    |
243 |                         _ -> screenKeysFor2Monitors x
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^

Please check the file for errors.
 

Если я правильно понимаю, проблема здесь в том, что мой код зависит от побочных эффектов (при работе с конфигурацией монитора используется монада ввода-вывода) и становится нечистым. Я могу преобразовать IO монаду в X монаду с помощью liftIO . Но X монада доступна только внутри обработчиков привязки ключей. Код, который создает привязки ключей для конфигурации Xmonad, должен быть чистым, и X монада здесь не ожидается.

Другими словами, если я правильно понимаю ситуацию, невозможно определить привязки клавиш с помощью не чистых функций (например, посмотрев на подключенные мониторы). Может быть, есть какой-то обходной путь? Мне не хватает приличного понимания Хаскелла, и, возможно, я упускаю что-то очевидное для обычных программистов Хаскелла.

Комментарии:

1. Ты пишешь keyConfig <- ... . Но ... это чистая вещь (ничего не делает IO ). Используйте let keyConfig = ... для чистых вещей, <- для IO -у вещей. Вам также может понравиться Монада ввода-вывода для Людей, которым просто все равно .

2. Я вижу, посмотрю. Спасибо!

3. Просто обратите внимание, что в качестве альтернативы вы можете использовать условие в самой привязке ключей — с тем преимуществом, что оно выполняется во время его вызова, в отличие от запуска xmonad.

4. да, я думал об этом как о обходном пути, который в любом случае сработал бы в рамках монадической области. Но это должно было привести к несколько сложному коду, и, к счастью, lsmor указал мне, чтобы я взглянул на main

Ответ №1:

не слишком знаком с Xmonad, но, я думаю, вы легко можете сделать следующее. создайте чистую функцию mkConfig , которая принимает количество экранов и возвращает желаемое сопоставление клавиш. Затем в вашем main пропуске он начнет xmonad функционировать. Я не пытался скомпилировать что-либо из этого, но, вероятно, вы можете легко изменить это

 mkConfig numberOfScreens  = -- Notice that this is a pure function
 case numberOfScreens of
   3 -> screenKeysFor3Monitors x
   _ -> screenKeysFor2Monitors x


main :: IO ()
main = do
 numberOfScreens <- getScreens                                              -- Retrive the number of screens from the system
 let keyConfig = mkConfig numberOfScreens                                   -- Makes a key mapping out of this
     xmonadConfig = ewmh xfceConfig{ modMask = mod4Mask, keys = keyConfig } -- Creates a Xmonad configuration

 xmonad xmonadConfig  -- Launch Xmonad.

 

Комментарии:

1. Ты мой спаситель! Я пытаюсь изучить Haskell методом проб (исключительно для целей настройки Xmonad), и мой опыт работы с Java, Python, Scala, Bash, Typescript и React мне очень помогает. Так что у меня было небольшое предчувствие того, что происходит, но я полностью упустил main это . Большое спасибо!