aboutsummaryrefslogtreecommitdiff
path: root/src/Rahm/Desktop/Swap.hs
blob: 9415dc2cccb40f03418a541f5deb8e2c611166f8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
module Rahm.Desktop.Swap
  ( SwapBuddy (..),
    getSwapBuddy,
    recordSwap,
    swapWindows,
    removeSwapBuddy,
  )
where

import Control.Monad (forM_, void)
import Data.Default (Default (..))
import Data.Map (Map)
import qualified Data.Map as Map
import Rahm.Desktop.Common
import Rahm.Desktop.Hooks.WindowChange
import Rahm.Desktop.Logger
import qualified Rahm.Desktop.StackSet as SS
import XMonad (ExtensionClass (..), Window, X, withWindowSet, StateExtension (PersistentExtension), windows)
import qualified XMonad.Util.ExtensibleState as XS
import Data.Maybe (maybeToList)

newtype SwapBuddy = SwapBuddy
  { swapBuddies :: Map Window Window
  }
  deriving (Read, Show)

instance Default SwapBuddy where
  def = SwapBuddy Map.empty

instance ExtensionClass SwapBuddy where
  initialValue = def
  extensionType = PersistentExtension

-- | Record a swap pairing between two windows.
-- The buddy relationship is bidirectional: w1's buddy becomes w2 and vice versa.
recordSwap :: Window -> Window -> X ()
recordSwap w1 w2 = do
  XS.modify $ \sw@(SwapBuddy m) ->
    SwapBuddy $ Map.insert w1 w2 $ Map.insert w2 w1 m
  logs Debug "Recorded swap pairing: %s <-> %s" (show w1) (show w2)

-- | Get the current (most recent) swap buddy for a window.
getSwapBuddy :: Window -> X (Maybe Window)
getSwapBuddy w = do
  SwapBuddy m <- XS.get
  return (Map.lookup w m)

-- | Swap windows between two lists.
-- Each window in w1s is swapped with the corresponding window in w2s.
-- Both windows in each pair become each other's swap buddy.
swapWindows :: [Window] -> [Window] -> X ()
swapWindows ws1 ws2 = do
  windows $ SS.swapWindows (zip ws1 ws2)
  mapM_ (uncurry recordSwap) (zip ws1 ws2)

-- | Clean up all swap buddy entries for a given window.
-- Should be called when a window is killed or moves to hidden workspace.
removeSwapBuddy :: Window -> X ()
removeSwapBuddy w = do
  SwapBuddy m <- XS.get
  let buddy = Map.lookup w m
      cleaned = foldr Map.delete (Map.delete w m) (maybeToList buddy)
  XS.put $ SwapBuddy cleaned
  logs Debug "Removed swap buddy for window: %s" (show w)