繁体   English   中英

XMonad:如何使 X () 操作在按下时只运行一次?

[英]XMonad: How to make an X () action run only once on key down?

我想设置我的 XMonad,以便我有一个键绑定,当按下并按住一个键时切换到特定布局,然后在释放同一个键时切换回另一个特定布局。

要在按下按键时切换到第一个布局,我在 XMonad 中定义了我的键绑定,如下所示:

myKeyDownBindings :: XConfig l -> M.Map ( KeyMask, KeySym ) ( X () )
myKeyDownBindings conf@(XConfig {XMonad.modMask = myModMask}) = mkKeymap conf $
[
, ("M-<Space>", sendMessage $ JumpToLayout "mySpecialLayout")
-- ...
]

要在按键释放时切换回其他布局,我定义了另一个按键绑定,如下所示:

myKeyUpBindings :: XConfig l -> M.Map ( KeyMask, KeySym ) ( X () )
myKeyUpBindings conf@(XConfig {XMonad.modMask = myModMask}) = mkKeymap conf $
    [
       ("M-<Space>", sendMessage $ JumpToLayout "myRegularLayout")
    ]

...我根据这个myKeyUpBindings作为参数的答案非常紧密地制作了一个事件挂钩模块:

module Hooks.KeyUp  (keyUpEventHook) where

import              XMonad
import              Data.Monoid
import qualified    Data.Map as M  (Map, lookup)

keyUpEventHook :: M.Map ( KeyMask, KeySym ) ( X () ) -> Event -> X All
keyUpEventHook ks ev =
    handle ev ks
 >> return (All True)

handle :: Event -> M.Map ( KeyMask, KeySym ) ( X () ) -> X ()
handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code}) ks
    | t == keyRelease =
    withDisplay $ \dpy -> do
        s  <- io $ keycodeToKeysym dpy code 0
        mClean <- cleanMask m
        userCodeDef () $ whenJust (M.lookup (mClean, s) ks) id
handle _ _ = return ()

然后我将myKeyUpBindingsmyKeyDownBindings都传递给 XMonad,如下所示:

myEventHook :: Event -> X All
myEventHook ev = keyUpEventHook (myKeyUpBindings myConfig)
               $ ev

myConfig = def
    {
      keys                  = myKeyDownBindings
    , handleEventHook       = myEventHook
    -- ...
    }

这几乎可以工作; 它在按下键时切换到"mySpecialLayout" ,在按下键时切换到“myRegularLayout "myRegularLayout" ...但问题是,当我按住空格键超过片刻时,XMonad 开始在两种布局之间快速闪烁,而不仅仅是切换一次到"mySpecialLayout" 我怎样才能使 XMonad 在按下键时仅从keys运行一次X ()操作?

可能的方法?

我认为可以通过使用XMonad.Util.ExtensibleState在键按下和键上切换 boolean 变量来做到这一点,并让我的键按下X ()操作是return () (如果值为True )或sendMessage $ JumpToLayout "mySpecialLayout" (如果值为False ),但我不确定我将如何实现它。 我如何从可变状态中读取 boolean 值——比如说,在if语句中? 我知道这是不正确的,但这是我的想法:

myKeyDownBindings :: XConfig l -> M.Map ( KeyMask, KeySym ) ( X () )
myKeyDownBindings conf@(XConfig {XMonad.modMask = myModMask}) = mkKeymap conf $
    [
       ("M-<Space>", jumpToKeyDownLayout)
    ]
myKeyUpBindings :: XConfig l -> M.Map ( KeyMask, KeySym ) ( X () )
myKeyUpBindings conf@(XConfig {XMonad.modMask = myModMask}) = mkKeymap conf $
    [
       ("M-<Space>", jumpToKeyUpLayout)
    ]

data KeyDownStatus = KeyDownStatus Bool
instance ExtensionClass KeyDownStatus where
  initialValue = KeyDownStatus False

jumpToKeyUpLayout :: X ()
jumpToKeyUpLayout = XS.put (KeyDownStatus False)
                  >> (sendMessage $ JumpToLayout "myRegularLayout")

jumpToKeyDownLayout :: X ()
jumpToKeyDownLayout = (XS.get :: X KeyDownStatus)
                  >>= \keyAlreadyDown ->      -- this is the wrong type
                      case keyAlreadyDown of   -- how do I do the equivalent of this for my type?
                          True  -> return ()
                          False -> XS.put (KeyDownStatus True)
                                >> (sendMessage $ JumpToLayout "mySpecialLayout")

这会产生以下编译错误,我有点预料到但不知道如何解决:

    • Couldn't match expected type ‘KeyDownStatus’
                  with actual type ‘Bool’
    • In the pattern: True
      In a case alternative: True -> return ()
      In the expression:
        case keyAlreadyDown of
          True -> return ()
          False
            -> XS.put (KeyDownStatus True)
                 >> (sendMessage $ JumpToLayout "grid")
    |
118 |                           True  -> return ()
    |                           ^^^^

我查看了这个Reddit 帖子,里面有一个关于那个模块的答案,结果更加困惑了。

更新 #1

由于提供了答案,修复了编译错误。 这是我当前用于切换工作区的代码:

data KeyStatus = Down | Up deriving (Eq, Read, Show)
instance ExtensionClass KeyStatus where initialValue = Up

myLayoutToggle :: KeyStatus -> String -> X ()
myLayoutToggle s l = (XS.get :: X KeyStatus)
                 >>= \key ->
                        if key == s then return ()
                        else             XS.put (s)
                                      >> (sendMessage $ JumpToLayout l)

然后我这样称呼它:

myKeyUpBindings conf@(XConfig {XMonad.modMask = myModMask}) = mkKeymap conf $
    [
      ("M-<Space>", myLayoutToggle Up "myRegularLayout")
    ]
myKeyDownBindings conf@(XConfig {XMonad.modMask = myModMask}) = mkKeymap conf $
    [
      ("M-<Space>", myLayoutToggle Down "mySpecialLayout")
    ]

这与以前完全一样; 它仍然闪烁。 如果我将myLayoutToggle中的一行替换为调试...

myLayoutToggle s l = (XS.get :: X KeyStatus)
                 >>= \key ->
                        if key == s then (spawn $ "echo Returning because key is already " ++ (show s) ++ ">>" ++ myPath ++ ".tmp" )
                                      >> return ()
                        else             XS.put (s)
                                        >> (spawn $ "echo Key switched status to " ++ (show s) ++ ">>" ++ myPath ++ ".tmp" )

...然后按下M-<Space>一次并保持大约一秒钟,这就是写入.tmp的内容:

Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Down
Key switched status to Up
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Down
Key switched status to Up
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up
Key switched status to Down
Key switched status to Up

这里发生了什么? 为什么key == s永远不会返回True

很接近。 您只需要在匹配中包含KeyDownStatus构造函数:

case keyAlreadyDown of
    KeyDownStatus True -> ...
    KeyDownStatus False -> ...

此外,您可以使用newtype而不是data 在这种情况下,它几乎肯定无关紧要,但通常对于单构造函数、单字段类型,使用newtype比使用data更好; 这减少了一点点 memory 分配和间接。

newtype KeyDownStatus = KeyDownStatus Bool
-- OR, you could mimic the declaration `data Bool = False | True` directly
data KeyDownStatus = Down | Up

好的,所以我没有从更新 #1中找到我的问题的正确答案,但我确实找到了解决方法。 我发现在初始按键之后将任何按键发送到 X 都会阻止myLayoutToggleelse部分无限重复。 所以,以下解决了我的问题:

data KeyStatus = Down | Up deriving (Eq, Read, Show)
instance ExtensionClass KeyStatus where initialValue = Up

myLayoutToggle :: KeyStatus -> String -> X ()
myLayoutToggle s l = (XS.get :: X KeyStatus)
                 >>= \key ->
                        if key == s then return ()
                        else             XS.put (s)
                                      >> (sendMessage $ JumpToLayout l)
                                      >> (spawn $ "xdotool key F1")

为什么会这样我不能告诉你,但至少任何想要在他们的 XMonad 构建中使用这个功能的人现在都有这个作为参考。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM