I noticed that when my mouse is out of the dialog area, the keyboard input stops working.
This is detrimental since I want this small app to grab keyboard, so that I could handle it through keyboard without having to move my mouse.
I tried:
windowSetKeepAbove
, windowSetSkipPagerHint
, windowSetSkipTaskbarHint
,
and windowPresentWithTime
. I still could not focus in the window. None of these seem to work.
Also tried Seat.grab
function, it gave me GDK_GRAB_NOT_VIEWABLE
. But I am running this after calling showAll
on the main window. Why is it not viewable?
I am so confused now. Any help would be appreciated.
EDIT: It is written in gi-gtk
binding of haskell, but I don't think the language would be relevant - it is pretty much 1-1 binding to the gtk library itself. (E.g. windowSetTypeHint
corresponds toGtk.Window.set_type_hint
)
Here is the close-to-minimal reproducible example. (I guess things like windowSetPosition
could have culled out, but it should not affect much. onWidgetKeyPressEvent
is to hook into key press event)
{-# 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 ()
I know, horrible hack, but I don't know other ways to get Gdk.Window
. Searched through the gtk library, could not find the way to take Gdk.Window
out of Gtk.Window
.
Still, it turns out that this hack have found the gdk window.
Running with e.g. cabal run
prints:
Finding window
Window found
GrabStatusNotViewable
So I got: GDK_GRAB_NOT_VIEWABLE
somehow.
It turns out that later on when e.g. focus event is fired, grab works normally. But I want to grab the mouse/keyboard earlier.
It turns out that the window needs to be mapped to grab the seat. I suspect that showAll
does not immediately map the window, so calling seatGrab
there leads to an error.
Hence, the better way would be grabbing the focus at Widget's map-event
. It conveniently carries the Gdk window field in its event struct, which I could use to grab the seat. In haskell, getEventAnyWindow
gets the gdk window from the EventAny
struct.
A haskell snippet setting window to grab the focus:
-- | 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 ()