packages feed

ymonad-0.1.0.0: src/YMonad/Backend/River/Translate.hs

{-# LANGUAGE OverloadedStrings #-}

module YMonad.Backend.River.Translate (
  stageRequests,
  pumpPending,
) where

import Data.Map.Strict qualified as M

import YMonad.Backend.River.Types
import YMonad.Core
import YMonad.Protocol.Wayland.Types

stageRequests :: RiverRuntime -> [BackendReq] -> RiverRuntime
stageRequests = foldl' stageRequest

stageRequest :: RiverRuntime -> BackendReq -> RiverRuntime
stageRequest rt req = case req of
  ReqShowView vid -> enqueueRender rt vid (\handle -> [mkWindowRequest rt handle "show" []])
  ReqHideView vid -> enqueueRender rt vid (\handle -> [mkWindowRequest rt handle "hide" []])
  ReqSetViewRect vid rect -> enqueueRender (enqueueManage rt vid manageMessages) vid renderMessages
    where
      manageMessages handle = [mkWindowRequest rt handle "propose_dimensions" [ArgIntValue (fromIntegral (rectWidth rect)), ArgIntValue (fromIntegral (rectHeight rect))]]
      renderMessages handle =
        [ mkNodeRequest rt handle "set_position" [ArgIntValue (fromIntegral (rectX rect)), ArgIntValue (fromIntegral (rectY rect))]
        , mkWindowRequest rt handle "show" []
        ]
  ReqSetViewStyle vid style -> enqueueRender (enqueueManage rt vid manageMessages) vid renderMessages
    where
      manageMessages handle =
        [ mkWindowRequest rt handle (case viewStyleTitleBarMode style of NoTitleBar -> "use_ssd"; ServerSideTitleBar -> "use_ssd") []
        ]
      renderMessages handle = [mkSetBordersRequest rt handle style]
  ReqFocusView Nothing -> case rrSeat rt of
    Nothing -> rt
    Just seat -> rt {rrPendingManage = rrPendingManage rt ++ [mkSeatRequest rt seat "clear_focus" []]}
  ReqFocusView (Just vid) ->
    enqueueManage
      rt
      vid
      ( \handle -> case rrSeat rt of
          Nothing -> []
          Just seat -> [mkSeatFocusWindow rt seat handle]
      )
  ReqCloseView vid -> enqueueManage rt vid (\handle -> [mkWindowRequest rt handle "close" []])
  ReqExitSession -> case rrManagerObject rt of
    Nothing -> rt
    Just managerId -> rt {rrOutgoing = rrOutgoing rt ++ [managerRequest rt managerId "exit_session" []]}
  ReqCommit -> rt

enqueueManage :: RiverRuntime -> ViewId -> (RiverViewHandle -> [WireMessage]) -> RiverRuntime
enqueueManage rt vid f = case M.lookup vid (rrViews rt) of
  Nothing -> rt
  Just handle -> rt {rrPendingManage = rrPendingManage rt ++ f handle}

enqueueRender :: RiverRuntime -> ViewId -> (RiverViewHandle -> [WireMessage]) -> RiverRuntime
enqueueRender rt vid f = case M.lookup vid (rrViews rt) of
  Nothing -> rt
  Just handle -> rt {rrPendingRender = rrPendingRender rt ++ f handle}

pumpPending :: RiverRuntime -> RiverRuntime
pumpPending rt = pumpRender (pumpManage rt)

pumpManage :: RiverRuntime -> RiverRuntime
pumpManage rt = case rrPhase rt of
  RiverPhaseManage ->
    let finish = maybe [] (\managerId -> [managerRequest rt managerId "manage_finish" []]) (rrManagerObject rt)
     in rt
          { rrOutgoing = rrOutgoing rt ++ rrPendingManage rt ++ finish
          , rrPendingManage = []
          , rrManageDirtySent = False
          , rrPhase = RiverPhaseIdle
          }
  _otherPhase
    | null (rrPendingManage rt) -> requestManageIfNeeded rt
    | otherwise -> requestManageIfNeeded rt

pumpRender :: RiverRuntime -> RiverRuntime
pumpRender rt = case rrPhase rt of
  RiverPhaseRender ->
    let finish = maybe [] (\managerId -> [managerRequest rt managerId "render_finish" []]) (rrManagerObject rt)
     in rt
          { rrOutgoing = rrOutgoing rt ++ rrPendingRender rt ++ finish
          , rrPendingRender = []
          , rrPhase = RiverPhaseIdle
          }
  _otherPhase -> rt

requestManageIfNeeded :: RiverRuntime -> RiverRuntime
requestManageIfNeeded rt
  | null (rrPendingManage rt) = rt
  | rrManageDirtySent rt = rt
  | otherwise = case rrManagerObject rt of
      Nothing -> rt
      Just managerId ->
        rt
          { rrOutgoing = rrOutgoing rt ++ [managerRequest rt managerId "manage_dirty" []]
          , rrManageDirtySent = True
          }

managerRequest :: RiverRuntime -> Word32 -> Text -> [ArgValue] -> WireMessage
managerRequest rt managerId name args =
  WireMessage
    { wireObjectId = managerId
    , wireInterface = "river_window_manager_v1"
    , wireMessageName = name
    , wireOpcode = requestOpcode rt "river_window_manager_v1" name
    , wireArgs = args
    }

mkWindowRequest :: RiverRuntime -> RiverViewHandle -> Text -> [ArgValue] -> WireMessage
mkWindowRequest rt handle name args =
  WireMessage
    { wireObjectId = riverWindowObjectId handle
    , wireInterface = "river_window_v1"
    , wireMessageName = name
    , wireOpcode = requestOpcode rt "river_window_v1" name
    , wireArgs = args
    }

mkNodeRequest :: RiverRuntime -> RiverViewHandle -> Text -> [ArgValue] -> WireMessage
mkNodeRequest rt handle name args =
  WireMessage
    { wireObjectId = fromMaybe 0 (riverNodeObjectId handle)
    , wireInterface = "river_node_v1"
    , wireMessageName = name
    , wireOpcode = requestOpcode rt "river_node_v1" name
    , wireArgs = args
    }

mkSeatRequest :: RiverRuntime -> RiverSeatHandle -> Text -> [ArgValue] -> WireMessage
mkSeatRequest rt seat name args =
  WireMessage
    { wireObjectId = riverSeatObjectId seat
    , wireInterface = "river_seat_v1"
    , wireMessageName = name
    , wireOpcode = requestOpcode rt "river_seat_v1" name
    , wireArgs = args
    }

mkSeatFocusWindow :: RiverRuntime -> RiverSeatHandle -> RiverViewHandle -> WireMessage
mkSeatFocusWindow rt seat handle = mkSeatRequest rt seat "focus_window" [ArgObjectValue (Just (riverWindowObjectId handle))]

mkSetBordersRequest :: RiverRuntime -> RiverViewHandle -> ViewStyle -> WireMessage
mkSetBordersRequest rt handle style =
  mkWindowRequest
    rt
    handle
    "set_borders"
    [ ArgUintValue edges
    , ArgIntValue (fromIntegral width)
    , ArgUintValue (rgbaRed color)
    , ArgUintValue (rgbaGreen color)
    , ArgUintValue (rgbaBlue color)
    , ArgUintValue (rgbaAlpha color)
    ]
  where
    width = max 0 (unDimension (viewStyleBorderWidth style))
    edges = if width > 0 then allEdges else 0
    color = viewStyleBorderColor style

allEdges :: Word32
allEdges = 1 + 2 + 4 + 8

requestOpcode :: RiverRuntime -> Text -> Text -> Word16
requestOpcode rt ifaceName name = maybe 0 messageSpecOpcode (lookupRequest (rrProtocol rt) ifaceName name)