From 89605d843ddf353e29e0727f90770a4db3097030 Mon Sep 17 00:00:00 2001 From: Spencer Janssen Date: Wed, 6 Jun 2007 23:40:06 +0200 Subject: Fix unmap handling According to the ICCCM, clients should send a synthetic unmap event when they initiate an unmap. The old code waited for these synthetic unmaps to unmanage windows. However, certain 'obsolete' clients do not send synthetic unmaps (notably xpdf's find dialog). These windows entered a zombified state: xmonad does not manage them, yet they are still mapped and raised on screen. The new algorithm (derived from wmii): - track windows that are mapped on screen - track the number of expected unmap events for each window, increment every time 'hide' is called on a window that is not mapped. - decrement the expected unmap counter on each unmap event - treat an unmap event as genuine (ie. unmap the window) when: - the event is synthetic (per ICCCM) - OR there are no expected unmap events for this window darcs-hash:20070606214006-a5988-7c2eced85319ff506a9b7c9dc86d5946ca0da8e5 --- Operations.hs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'Operations.hs') diff --git a/Operations.hs b/Operations.hs index 707d210..bd85637 100644 --- a/Operations.hs +++ b/Operations.hs @@ -23,6 +23,7 @@ import Data.List (genericIndex, intersectBy, partition) import Data.Bits ((.|.), (.&.), complement) import Data.Ratio import qualified Data.Map as M +import qualified Data.Set as S import Control.Monad.State import Control.Monad.Reader @@ -66,7 +67,10 @@ manage w = withDisplay $ \d -> do -- should also unmap? -- unmanage :: Window -> X () -unmanage w = setWMState w 0 {-withdrawn-} >> windows (W.sink w . W.delete w) +unmanage w = do + setWMState w 0 {-withdrawn-} + windows (W.sink w . W.delete w) + modify (\s -> s {mapped = S.delete w (mapped s), waitingUnmap = M.delete w (waitingUnmap s)}) -- | focus. focus window up or down. or swap various windows. focusUp, focusDown, swapUp, swapDown, swapMaster :: X () @@ -196,9 +200,15 @@ setWMState w v = withDisplay $ \dpy -> do -- | hide. Hide a window by unmapping it, and setting Iconified. hide :: Window -> X () -hide w = withDisplay $ \d -> do - io $ unmapWindow d w +hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do + io $ do selectInput d w (clientMask .&. complement structureNotifyMask) + unmapWindow d w + selectInput d w clientMask setWMState w 3 --iconic + -- this part is key: we increment the waitingUnmap counter to distinguish + -- between client and xmonad initiated unmaps. + modify (\s -> s { waitingUnmap = M.insertWith (+) w 1 (waitingUnmap s) + , mapped = S.delete w (mapped s) }) -- | reveal. Show a window by mapping it and setting Normal -- this is harmless if the window was already visible @@ -206,11 +216,16 @@ reveal :: Window -> X () reveal w = withDisplay $ \d -> do setWMState w 1 --normal io $ mapWindow d w + modify (\s -> s { mapped = S.insert w (mapped s) }) + +-- | The client events that xmonad is interested in +clientMask :: EventMask +clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask -- | Set some properties when we initially gain control of a window setInitialProperties :: Window -> X () setInitialProperties w = withDisplay $ \d -> io $ do - selectInput d w $ structureNotifyMask .|. enterWindowMask .|. propertyChangeMask + selectInput d w $ clientMask setWindowBorderWidth d w borderWidth -- | refresh. Render the currently visible workspaces, as determined by -- cgit v1.2.3