【问题标题】:GTK: How do I grab keyboard input for a dialog/splash window, so that keyinput works out of window region?GTK:如何获取对话框/启动窗口的键盘输入,以便键盘输入在窗口区域之外工作?
【发布时间】:2022-06-11 11:14:44
【问题描述】:

我注意到当我的鼠标离开对话框区域时,键盘输入停止工作。

这是有害的,因为我希望这个小应用程序能够抓取键盘,这样我就可以通过键盘处理它而无需移动鼠标。

我试过了: windowSetKeepAbove, windowSetSkipPagerHint, windowSetSkipTaskbarHint, 和windowPresentWithTime。我仍然无法专注于窗口。这些似乎都不起作用。

还尝试了Seat.grab 函数,它给了我GDK_GRAB_NOT_VIEWABLE。但是我在主窗口上调用showAll 后运行它。为什么看不到?

我现在很困惑。任何帮助将不胜感激。

编辑:它是用haskell 的gi-gtk 绑定编写的,但我认为该语言不相关——它几乎是与gtk 库本身的1-1 绑定。 (例如windowSetTypeHint对应Gtk.Window.set_type_hint

这是一个接近最小的可重现示例。 (我猜windowSetPosition 之类的东西可能会被剔除,但应该不会影响太大。onWidgetKeyPressEvent 是挂钩按键事件)

{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE LambdaCase #-}

module Main where

import Control.Monad
import Data.Foldable
import Data.Text qualified as T
import GI.Gdk qualified as Gdk
import GI.Gio.Objects qualified as Gio
import GI.Gtk qualified as Gtk
import System.Exit

main :: IO ()
main = do
  -- Does not care crashing here
  Just app <- Gtk.applicationNew (Just $ T.pack "test.program") []
  Gio.onApplicationActivate app (activating app)
  status <- Gio.applicationRun app Nothing
  when (status /= 0) $ exitWith (ExitFailure $ fromIntegral status)
  where
    activating :: Gtk.Application -> IO ()
    activating app = do
      window <- Gtk.applicationWindowNew app >>= Gtk.toWindow
      Gtk.windowSetTitle window (T.pack "Test Program")
      Gtk.windowSetDefaultSize window 560 140
      Gtk.windowSetTypeHint window Gdk.WindowTypeHintDialog
      Gtk.windowSetPosition window Gtk.WindowPositionCenterAlways
      Gtk.windowSetKeepAbove window True
      Gtk.windowSetSkipPagerHint window True
      Gtk.windowSetSkipTaskbarHint window True
      Gtk.onWidgetKeyPressEvent window $
        Gdk.getEventKeyKeyval >=> \case
          Gdk.KEY_Escape -> True <$ Gtk.windowClose window
          _ -> pure False

      Gtk.widgetShowAll window

      screen <- Gtk.windowGetScreen window
      gdkWins <- Gdk.screenGetToplevelWindows screen
      seat <- Gdk.screenGetDisplay screen >>= Gdk.displayGetDefaultSeat
      event <- Gtk.getCurrentEvent
      putStrLn "Finding window"
      filterM (fmap (Gdk.WindowStateAbove `elem`) . Gdk.windowGetState) gdkWins
        >>= traverse_
          ( \win -> do
              putStrLn "Window found"
              Gdk.windowShow win
              stat <- Gdk.seatGrab seat win [Gdk.SeatCapabilitiesAll] True (Nothing @Gdk.Cursor) event Nothing
              print stat
          )

      pure ()

我知道,可怕的 hack,但我不知道其他获取 Gdk.Window 的方法。搜遍了gtk library,找不到将Gdk.WindowGtk.Window中取出的方法。

但事实证明,这个 hack 已经找到了 gdk 窗口。

与例如运行cabal run 打印:

Finding window
Window found
GrabStatusNotViewable

所以我得到了:GDK_GRAB_NOT_VIEWABLE。 事实证明,后来当例如触发焦点事件,抓取正常工作。但我想早点抓住鼠标/键盘。

【问题讨论】:

  • 您能给我们提供一个minimal reproducible example吗?
  • @SylvesterKruin 是的,完成。抱歉,我不知道如何用 C++ 构建 gtk,所以语言与我遇到问题的项目相同。语言应该无关紧要。我想要可靠的方法来抓取指针/键盘,但我不知道哪种方法是要走的路。

标签: gtk gtk3 gdk


【解决方案1】:

原来需要映射窗口才能抢到座位。我怀疑showAll 不会立即映射窗口,所以在那里调用seatGrab 会导致错误。

因此,更好的方法是在 Widget 的 map-event 处获取焦点。它在其事件结构中方便地携带 Gdk 窗口字段,我可以用它来抢座位。在 haskell 中,getEventAnyWindowEventAny 结构中获取 gdk 窗口。

一个获取焦点的haskell sn-p设置窗口:

-- | Grab the screen on window map.
windowGrabOnMap :: MonadIO m => Window -> m ()
windowGrabOnMap window = do
  afterWidgetMapEvent window $
    Gdk.getEventAnyWindow >=> \case
      Nothing -> pure False
      Just win -> do
        event <- getCurrentEvent
        seat <- Gdk.windowGetDisplay win >>= Gdk.displayGetDefaultSeat
        Gdk.seatGrab seat win [Gdk.SeatCapabilitiesAll] True (Nothing @Gdk.Cursor) event Nothing
        pure False
  pure ()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-20
    • 1970-01-01
    • 1970-01-01
    • 2016-10-24
    • 2014-08-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多