[英]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 ()
然后我将myKeyUpBindings
和myKeyDownBindings
都传递给 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 帖子,里面有一个关于那个模块的答案,结果更加困惑了。
由于提供了答案,修复了编译错误。 这是我当前用于切换工作区的代码:
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 都会阻止myLayoutToggle
的else
部分无限重复。 所以,以下解决了我的问题:
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.