i3ipc 0.1.0.1 → 0.2.0.0
raw patch · 12 files changed
+1763/−110 lines, 12 filesdep +exceptionsdep ~networkPVP ok
version bump matches the API change (PVP)
Dependencies added: exceptions
Dependency ranges changed: network
API changes (from Hackage documentation)
+ I3IPC: connectsway :: (MonadThrow m, MonadIO m) => m Socket
+ I3IPC: getSwaySocketPath :: MonadIO m => m (Maybe ByteString)
+ I3IPC: instance GHC.Classes.Eq I3IPC.I3Exception
+ I3IPC: instance GHC.Exception.Type.Exception I3IPC.I3Exception
+ I3IPC: instance GHC.Show.Show I3IPC.I3Exception
+ I3IPC.Event: UnknownChange :: WorkspaceChange
+ I3IPC.Event: WinUnknown :: WindowChange
+ I3IPC.Reply: UnknownBarPart :: BarPart
+ I3IPC.Reply: UnknownBorder :: NodeBorder
+ I3IPC.Reply: UnknownLayout :: NodeLayout
+ I3IPC.Reply: UnknownOrient :: NodeOrientation
+ I3IPC.Reply: UnknownProperty :: WindowProperty
+ I3IPC.Reply: UnknownType :: NodeType
- I3IPC: connecti3 :: IO Socket
+ I3IPC: connecti3 :: (MonadThrow m, MonadIO m) => m Socket
- I3IPC: getBarConfig :: Socket -> ByteString -> IO (Either String MsgReply)
+ I3IPC: getBarConfig :: MonadIO m => Socket -> ByteString -> m (Either String MsgReply)
- I3IPC: getBarConfig' :: Socket -> ByteString -> IO (Either String MsgReply)
+ I3IPC: getBarConfig' :: MonadIO m => Socket -> ByteString -> m (Either String MsgReply)
- I3IPC: getBarIds :: Socket -> IO (Either String BarIds)
+ I3IPC: getBarIds :: MonadIO m => Socket -> m (Either String BarIds)
- I3IPC: getBindingModes :: Socket -> IO (Either String MsgReply)
+ I3IPC: getBindingModes :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getBindingModes' :: Socket -> IO (Either String MsgReply)
+ I3IPC: getBindingModes' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getConfig :: Socket -> IO (Either String MsgReply)
+ I3IPC: getConfig :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getConfig' :: Socket -> IO (Either String MsgReply)
+ I3IPC: getConfig' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getMarks :: Socket -> IO (Either String MsgReply)
+ I3IPC: getMarks :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getMarks' :: Socket -> IO (Either String MsgReply)
+ I3IPC: getMarks' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getOutputs :: Socket -> IO (Either String MsgReply)
+ I3IPC: getOutputs :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getOutputs' :: Socket -> IO (Either String MsgReply)
+ I3IPC: getOutputs' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getReply :: Socket -> IO (Either String (Int, ByteString))
+ I3IPC: getReply :: MonadIO m => Socket -> m (Either String (Int, ByteString))
- I3IPC: getSocketPath :: IO (Maybe ByteString)
+ I3IPC: getSocketPath :: MonadIO m => m (Maybe ByteString)
- I3IPC: getSync :: Socket -> IO (Either String MsgReply)
+ I3IPC: getSync :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getSync' :: Socket -> IO (Either String MsgReply)
+ I3IPC: getSync' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getTick :: Socket -> IO (Either String MsgReply)
+ I3IPC: getTick :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getTick' :: Socket -> IO (Either String MsgReply)
+ I3IPC: getTick' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getTree :: Socket -> IO (Either String MsgReply)
+ I3IPC: getTree :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getTree' :: Socket -> IO (Either String MsgReply)
+ I3IPC: getTree' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getVersion :: Socket -> IO (Either String MsgReply)
+ I3IPC: getVersion :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getVersion' :: Socket -> IO (Either String MsgReply)
+ I3IPC: getVersion' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getWorkspaces :: Socket -> IO (Either String MsgReply)
+ I3IPC: getWorkspaces :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: getWorkspaces' :: Socket -> IO (Either String MsgReply)
+ I3IPC: getWorkspaces' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: receive :: Socket -> IO (Either String Response)
+ I3IPC: receive :: MonadIO m => Socket -> m (Either String Response)
- I3IPC: receive' :: Socket -> IO (Either String Response)
+ I3IPC: receive' :: MonadIO m => Socket -> m (Either String Response)
- I3IPC: receiveEvent :: Socket -> IO (Either String Event)
+ I3IPC: receiveEvent :: MonadIO m => Socket -> m (Either String Event)
- I3IPC: receiveEvent' :: Socket -> IO (Either String Event)
+ I3IPC: receiveEvent' :: MonadIO m => Socket -> m (Either String Event)
- I3IPC: receiveMsg :: Socket -> IO (Either String MsgReply)
+ I3IPC: receiveMsg :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: receiveMsg' :: Socket -> IO (Either String MsgReply)
+ I3IPC: receiveMsg' :: MonadIO m => Socket -> m (Either String MsgReply)
- I3IPC: runCommand :: Socket -> ByteString -> IO (Either String MsgReply)
+ I3IPC: runCommand :: MonadIO m => Socket -> ByteString -> m (Either String MsgReply)
- I3IPC: runCommand' :: Socket -> ByteString -> IO (Either String MsgReply)
+ I3IPC: runCommand' :: MonadIO m => Socket -> ByteString -> m (Either String MsgReply)
- I3IPC: subscribe :: (Either String Event -> IO ()) -> [Subscribe] -> IO ()
+ I3IPC: subscribe :: (MonadThrow m, MonadIO m) => (Either String Event -> m ()) -> [Subscribe] -> m ()
- I3IPC: subscribeM :: MonadIO m => (Either String Event -> m ()) -> [Subscribe] -> m ()
+ I3IPC: subscribeM :: (MonadThrow m, MonadIO m) => (Either String Event -> m ()) -> [Subscribe] -> m ()
- I3IPC.Message: sendMsg :: Socket -> MessageType -> IO Int64
+ I3IPC.Message: sendMsg :: MonadIO m => Socket -> MessageType -> m Int64
- I3IPC.Message: sendMsgPayload :: Socket -> MessageType -> ByteString -> IO Int64
+ I3IPC.Message: sendMsgPayload :: MonadIO m => Socket -> MessageType -> ByteString -> m Int64
Files
- README.md +13/−9
- i3ipc.cabal +4/−3
- src/I3IPC.hs +93/−55
- src/I3IPC/Event.hs +15/−11
- src/I3IPC/Message.hs +7/−4
- src/I3IPC/Reply.hs +44/−28
- test/event/winevent.json +57/−0
- test/event/workspace.json +204/−0
- test/reply/output.json +23/−0
- test/reply/tree.json +1249/−0
- test/reply/version.json +7/−0
- test/reply/workspace.json +47/−0
README.md view
@@ -2,19 +2,20 @@ [](https://travis-ci.com/leshow/i3ipc) -Haskell type-safe bindings for working with i3 using it's unix socket IPC+Haskell type-safe bindings for working with i3 using it's unix socket IPC. Sway is supposed to be protocl compatible with i3, I've included a function to bind to sway's socket also. ## Subscribing Subscribe to events: ```haskell-import qualified I3IPC.Subscribe as Sub-import I3IPC ( subscribe )+import qualified I3IPC.Subscribe as Sub+import I3IPC ( subscribe )+import Control.Monad.IO.Class -- will print all events main :: IO ()-main = subscribe print [Sub.Workspace, Sub.Window]+main = liftIO $ subscribe print [Sub.Workspace, Sub.Window] ``` An example of explicitly matching on some events and printing their fields:@@ -22,12 +23,13 @@ ```haskell import qualified I3IPC.Subscribe as Sub import I3IPC.Event-import I3IPC ( subscribe )+import I3IPC ( subscribe )+import Control.Monad.IO.Class main :: IO ()-main = subscribe handle [Sub.Workspace, Sub.Window]+main = liftIO $ subscribe handle [Sub.Workspace, Sub.Window] where- handle :: Either String Event -> IO ()+ handle :: MonadIO m => Either String Event -> m () handle (Right evt) = case evt of Workspace WorkspaceEvent { wrk_current } -> print wrk_current Window WindowEvent { win_container } -> print win_container@@ -43,10 +45,11 @@ import I3IPC ( connecti3 , getWorkspaces )+import Control.Monad.IO.Class main :: IO () main = do- soc <- connecti3+ soc <- liftIO $ connecti3 print $ getWorkspaces soc ``` @@ -57,10 +60,11 @@ import I3IPC ( connecti3 , receiveMsg )+import Control.Monad.IO.Class main :: IO () main = do- soc <- connecti3+ soc <- liftIO $ connecti3 print $ Msg.sendMsg soc Msg.Workspaces >> receiveMsg soc ```
i3ipc.cabal view
@@ -1,5 +1,5 @@ name: i3ipc-version: 0.1.0.1+version: 0.2.0.0 description: Library for controlling i3 through it's IPC. i3 communicates using a JSON interface over a unix socket. For JSON parsing I'm using Aeson. I've written out all the records and types to allow anyone to easily interact with i3 from a Haskell application.@@ -12,7 +12,7 @@ copyright: 2019 Evan Cameron category: Lib build-type: Simple-extra-source-files: README.md+extra-source-files: README.md test/event/*.json test/reply/*.json cabal-version: >=1.10 library@@ -29,8 +29,9 @@ , binary >= 0.8.6 && < 0.10 , text >= 1.2.1.0 && < 1.3 , vector >= 0.11.0 && < 0.13- , network >= 2.6.3.5 && < 3.1+ , network >= 2.6.3.5 && < 3.2 , typed-process >= 0.2.4 && < 0.3+ , exceptions >= 0.8.1 && < 0.10.5 default-language: Haskell2010 ghc-options: -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
src/I3IPC.hs view
@@ -19,6 +19,7 @@ -- ** Convenience functions -- $func getSocketPath+ , getSwaySocketPath , Response(..) , subscribe , subscribeM@@ -28,6 +29,7 @@ , receiveMsg' , getReply , connecti3+ , connectsway , receiveEvent , receiveEvent' , runCommand@@ -62,15 +64,17 @@ import I3IPC.Reply import Control.Monad.IO.Class+import Control.Exception ( Exception )+import Control.Monad.Catch ( MonadThrow+ , throwM+ ) import System.Environment ( lookupEnv ) import Data.Maybe ( isJust ) import Data.Semigroup ( (<>) ) import System.Process.Typed ( proc , readProcess )-import System.Exit ( ExitCode(..)- , exitFailure- )+import System.Exit ( ExitCode(..) ) import Network.Socket hiding ( send , sendTo@@ -82,14 +86,22 @@ import Data.Binary.Get import Data.Bifunctor ( second ) import qualified Data.ByteString.Lazy.Char8 as BL+import Data.Typeable ( Typeable )+import qualified Data.Text as T import Data.Bits ( testBit , clearBit ) +-- | Exception type+data I3Exception = ConnectException T.Text | ProcessException+ deriving stock (Show, Eq, Typeable)++instance Exception I3Exception+ -- | Get a new unix socket path from i3-getSocketPath :: IO (Maybe BL.ByteString)+getSocketPath :: MonadIO m => m (Maybe BL.ByteString) getSocketPath = do- res <- lookupEnv "I3SOCK"+ res <- liftIO $ lookupEnv "I3SOCK" if isJust res then pure $ fmap BL.pack res else do@@ -98,61 +110,81 @@ then pure Nothing else pure $ Just (BL.filter (/= '\n') out) +-- | Get a new unix socket path from sway+getSwaySocketPath :: MonadIO m => m (Maybe BL.ByteString)+getSwaySocketPath = fmap BL.pack <$> liftIO (lookupEnv "SWAYSOCK") -- | Subscribe with a list of 'I3IPC.Subscribe.Subscribe' types, and subscribe will to respond with specific 'I3IPC.Event.Event'-subscribe :: (Either String Evt.Event -> IO ()) -> [Sub.Subscribe] -> IO ()+subscribe+ :: (MonadThrow m, MonadIO m)+ => (Either String Evt.Event -> m ())+ -> [Sub.Subscribe]+ -> m () subscribe handle subtypes = do soc <- connecti3 Msg.sendMsgPayload soc Msg.Subscribe (encode subtypes) >> receiveMsg soc >> handleSoc soc- >> close soc+ >> liftIO (close soc) where handleSoc soc = do r <- receiveEvent soc handle r handleSoc soc --- | A version of 'subscribe' that allows the use of any monad transformer on top of MonadIO+-- | A version of 'subscribe' that allows the use of any monad transformer on top of MonadIO (kept around for backwards compatibility) subscribeM- :: MonadIO m => (Either String Evt.Event -> m ()) -> [Sub.Subscribe] -> m ()+ :: (MonadThrow m, MonadIO m)+ => (Either String Evt.Event -> m ())+ -> [Sub.Subscribe]+ -> m () subscribeM handle subtypes = do- soc <- liftIO connecti3- liftIO- $ Msg.sendMsgPayload soc Msg.Subscribe (encode subtypes)+ soc <- connecti3+ Msg.sendMsgPayload soc Msg.Subscribe (encode subtypes) >> receiveMsg soc >> pure () handleSoc soc >> liftIO (close soc) where handleSoc soc = do- r <- liftIO $ receiveEvent soc+ r <- receiveEvent soc handle r handleSoc soc -- | Connect to an i3 socket and return it-connecti3 :: IO Socket+connecti3 :: (MonadThrow m, MonadIO m) => m Socket connecti3 = do- soc <- socket AF_UNIX Stream 0+ soc <- liftIO $ socket AF_UNIX Stream 0 getSocketPath >>= \case- Nothing -> putStrLn "Failed to get i3 socket path" >> exitFailure+ Nothing -> throwM $ ConnectException "Failed to get i3 socket path" Just addr' -> do- connect soc (SockAddrUnix $ BL.unpack addr')+ liftIO $ connect soc (SockAddrUnix $ BL.unpack addr') pure soc +-- | Connect to SWAY socket and return it+connectsway :: (MonadThrow m, MonadIO m) => m Socket+connectsway = do+ soc <- liftIO $ socket AF_UNIX Stream 0+ getSwaySocketPath >>= \case+ Nothing -> throwM $ ConnectException "Failed to get i3 socket path"+ Just addr' -> do+ liftIO $ connect soc (SockAddrUnix $ BL.unpack addr')+ pure soc+ -- | Useful for when you are receiving Events or Messages. data Response = Message MsgReply | Event Evt.Event deriving (Show, Eq) -- | Get and parse the response using i3's IPC-getReply :: Socket -> IO (Either String (Int, BL.ByteString))+getReply :: MonadIO m => Socket -> m (Either String (Int, BL.ByteString)) getReply soc = do- magic <- recv soc 6+ magic <- liftIO $ recv soc 6 if magic == "i3-ipc" then do- len <- fromIntegral . runGet getWord32le <$> recv soc 4- ty <- fromIntegral . runGet getWord32le <$> recv soc 4- body <- recv soc len- pure $ Right (ty, body)+ len <- getInt <$> liftIO (recv soc 4)+ ty <- getInt <$> liftIO (recv soc 4)+ body <- liftIO $ recv soc len+ pure $ Right (fromIntegral ty, body) else pure $ Left "Failed to get reply"+ where getInt = fromIntegral . runGet getWord32le test :: Int -> BL.ByteString -> IO Int test ty body = do@@ -162,7 +194,7 @@ pure ty -- | Parse response from socket, returning either an error or a 'I3IPC.Response', representing a sum type of a 'I3IPC.Reply.MsgReply' or 'I3IPC.Event.Event'-receive :: Socket -> IO (Either String Response)+receive :: MonadIO m => Socket -> m (Either String Response) receive soc = do reply <- getReply soc case reply of@@ -172,7 +204,7 @@ _ -> pure $ Left "Get Reply failed" -- | Like receive but strict-- will use eitherDecode' under the hood to parse-receive' :: Socket -> IO (Either String Response)+receive' :: MonadIO m => Socket -> m (Either String Response) receive' soc = do reply <- getReply soc case reply of@@ -182,7 +214,7 @@ _ -> pure $ Left "Get Reply failed" -- | Receive but specifically for msgs, for when you know the response won't include any Events-receiveMsg :: Socket -> IO (Either String MsgReply)+receiveMsg :: MonadIO m => Socket -> m (Either String MsgReply) receiveMsg soc = do r <- getReply soc pure $ do@@ -190,7 +222,7 @@ toMsgReply ty body -- | Like 'I3IPC.receiveMsg' but strict-- uses eitherDecode'-receiveMsg' :: Socket -> IO (Either String MsgReply)+receiveMsg' :: MonadIO m => Socket -> m (Either String MsgReply) receiveMsg' soc = do r <- getReply soc pure $ do@@ -198,7 +230,7 @@ toMsgReply' ty body -- | 'I3IPC.receive' specifically for Event-receiveEvent :: Socket -> IO (Either String Evt.Event)+receiveEvent :: MonadIO m => Socket -> m (Either String Evt.Event) receiveEvent soc = do r <- getReply soc pure $ do@@ -206,7 +238,7 @@ Evt.toEvent (ty `clearBit` 31) body -- | like 'receiveEvent' but strict-- uses eitherDecode'-receiveEvent' :: Socket -> IO (Either String Evt.Event)+receiveEvent' :: MonadIO m => Socket -> m (Either String Evt.Event) receiveEvent' soc = do r <- getReply soc pure $ do@@ -220,37 +252,38 @@ -- Or, if there is no message body: -- -- > Msg.sendMsg soc Msg.X >> receiveMsg soc-runCommand :: Socket -> BL.ByteString -> IO (Either String MsgReply)+runCommand :: MonadIO m => Socket -> BL.ByteString -> m (Either String MsgReply) runCommand soc b = Msg.sendMsgPayload soc Msg.RunCommand b >> receiveMsg soc -runCommand' :: Socket -> BL.ByteString -> IO (Either String MsgReply)+runCommand'+ :: MonadIO m => Socket -> BL.ByteString -> m (Either String MsgReply) runCommand' soc b = Msg.sendMsgPayload soc Msg.RunCommand b >> receiveMsg' soc -getWorkspaces :: Socket -> IO (Either String MsgReply)+getWorkspaces :: MonadIO m => Socket -> m (Either String MsgReply) getWorkspaces soc = Msg.sendMsg soc Msg.Workspaces >> receiveMsg soc -getWorkspaces' :: Socket -> IO (Either String MsgReply)+getWorkspaces' :: MonadIO m => Socket -> m (Either String MsgReply) getWorkspaces' soc = Msg.sendMsg soc Msg.Workspaces >> receiveMsg' soc -getOutputs :: Socket -> IO (Either String MsgReply)+getOutputs :: MonadIO m => Socket -> m (Either String MsgReply) getOutputs soc = Msg.sendMsg soc Msg.Outputs >> receiveMsg soc -getOutputs' :: Socket -> IO (Either String MsgReply)+getOutputs' :: MonadIO m => Socket -> m (Either String MsgReply) getOutputs' soc = Msg.sendMsg soc Msg.Outputs >> receiveMsg' soc -getTree :: Socket -> IO (Either String MsgReply)+getTree :: MonadIO m => Socket -> m (Either String MsgReply) getTree soc = Msg.sendMsg soc Msg.Tree >> receiveMsg soc -getTree' :: Socket -> IO (Either String MsgReply)+getTree' :: MonadIO m => Socket -> m (Either String MsgReply) getTree' soc = Msg.sendMsg soc Msg.Tree >> receiveMsg' soc -getMarks :: Socket -> IO (Either String MsgReply)+getMarks :: MonadIO m => Socket -> m (Either String MsgReply) getMarks soc = Msg.sendMsg soc Msg.Marks >> receiveMsg soc -getMarks' :: Socket -> IO (Either String MsgReply)+getMarks' :: MonadIO m => Socket -> m (Either String MsgReply) getMarks' soc = Msg.sendMsg soc Msg.Marks >> receiveMsg' soc -getBarIds :: Socket -> IO (Either String BarIds)+getBarIds :: MonadIO m => Socket -> m (Either String BarIds) getBarIds soc = do _ <- Msg.sendMsg soc Msg.BarConfig r <- getReply soc@@ -259,41 +292,43 @@ decodeBarIds (snd body) -- | Get a bar's config based on it's id-getBarConfig :: Socket -> BL.ByteString -> IO (Either String MsgReply)+getBarConfig+ :: MonadIO m => Socket -> BL.ByteString -> m (Either String MsgReply) getBarConfig soc b = Msg.sendMsgPayload soc Msg.BarConfig b >> receiveMsg' soc -- | Like 'I3IPC.getBarConfig' but strict-getBarConfig' :: Socket -> BL.ByteString -> IO (Either String MsgReply)+getBarConfig'+ :: MonadIO m => Socket -> BL.ByteString -> m (Either String MsgReply) getBarConfig' soc b = Msg.sendMsgPayload soc Msg.BarConfig b >> receiveMsg' soc -getVersion :: Socket -> IO (Either String MsgReply)+getVersion :: MonadIO m => Socket -> m (Either String MsgReply) getVersion soc = Msg.sendMsg soc Msg.Version >> receiveMsg soc -getVersion' :: Socket -> IO (Either String MsgReply)+getVersion' :: MonadIO m => Socket -> m (Either String MsgReply) getVersion' soc = Msg.sendMsg soc Msg.Version >> receiveMsg' soc -getBindingModes :: Socket -> IO (Either String MsgReply)+getBindingModes :: MonadIO m => Socket -> m (Either String MsgReply) getBindingModes soc = Msg.sendMsg soc Msg.BindingModes >> receiveMsg soc -getBindingModes' :: Socket -> IO (Either String MsgReply)+getBindingModes' :: MonadIO m => Socket -> m (Either String MsgReply) getBindingModes' soc = Msg.sendMsg soc Msg.BindingModes >> receiveMsg' soc -getConfig :: Socket -> IO (Either String MsgReply)+getConfig :: MonadIO m => Socket -> m (Either String MsgReply) getConfig soc = Msg.sendMsg soc Msg.Config >> receiveMsg soc -getConfig' :: Socket -> IO (Either String MsgReply)+getConfig' :: MonadIO m => Socket -> m (Either String MsgReply) getConfig' soc = Msg.sendMsg soc Msg.Config >> receiveMsg' soc -getTick :: Socket -> IO (Either String MsgReply)+getTick :: MonadIO m => Socket -> m (Either String MsgReply) getTick soc = Msg.sendMsg soc Msg.Tick >> receiveMsg soc -getTick' :: Socket -> IO (Either String MsgReply)+getTick' :: MonadIO m => Socket -> m (Either String MsgReply) getTick' soc = Msg.sendMsg soc Msg.Tick >> receiveMsg' soc -getSync :: Socket -> IO (Either String MsgReply)+getSync :: MonadIO m => Socket -> m (Either String MsgReply) getSync soc = Msg.sendMsg soc Msg.Sync >> receiveMsg soc -getSync' :: Socket -> IO (Either String MsgReply)+getSync' :: MonadIO m => Socket -> m (Either String MsgReply) getSync' soc = Msg.sendMsg soc Msg.Sync >> receiveMsg' soc @@ -305,9 +340,10 @@ -- > import qualified I3IPC.Subscribe as Sub -- > import I3IPC.Event -- > import I3IPC ( subscribe )+-- > import Control.Monad.IO.Class -- > -- > main :: IO ()--- > main = subscribe handle [Sub.Workspace, Sub.Window]+-- > main = liftIO $ subscribe handle [Sub.Workspace, Sub.Window] -- > where -- > handle :: Either String Event -> IO () -- > handle (Right evt) = case evt of@@ -324,10 +360,11 @@ -- > import I3IPC ( connecti3 -- > , getWorkspaces -- > )+-- > import Control.Monad.IO.Class -- > -- > main :: IO () -- > main = do--- > soc <- connecti3+-- > soc <- liftIO $ connecti3 -- > print getWorkspaces -- -- $func@@ -340,8 +377,9 @@ -- > import I3IPC ( connecti3 -- > , receiveMsg -- > )+-- > import Control.Monad.IO.Class -- > -- > main :: IO () -- > main = do--- > soc <- connecti3+-- > soc <- liftIO $ connecti3 -- > print $ Msg.sendMsg soc Msg.Workspaces >> receiveMsg soc
src/I3IPC/Event.hs view
@@ -40,7 +40,7 @@ toEvent' :: Int -> BL.ByteString -> Either String Event toEvent' 0 = (Workspace <$>) . eitherDecode' toEvent' 1 = (Output <$>) . eitherDecode'-toEvent' 2 = (Mode <$>) . eitherDecode' +toEvent' 2 = (Mode <$>) . eitherDecode' toEvent' 3 = (Window <$>) . eitherDecode' toEvent' 4 = (BarConfigUpdate <$>) . eitherDecode' toEvent' 5 = (Binding <$>) . eitherDecode'@@ -68,18 +68,20 @@ | Reload | Restored | Move+ | UnknownChange deriving (Eq, Generic, Show) instance ToJSON WorkspaceChange where toEncoding = \case- Focus -> text "focus"- Init -> text "init"- Empty -> text "empty"- Urgent -> text "urgent"- Rename -> text "rename"- Reload -> text "reload"- Restored -> text "restored"- Move -> text "move"+ Focus -> text "focus"+ Init -> text "init"+ Empty -> text "empty"+ Urgent -> text "urgent"+ Rename -> text "rename"+ Reload -> text "reload"+ Restored -> text "restored"+ Move -> text "move"+ UnknownChange -> text "unknown" instance FromJSON WorkspaceChange where parseJSON (String s) = pure $! case s of@@ -91,7 +93,7 @@ "reload" -> Reload "restored" -> Restored "move" -> Move- _ -> error "Received unrecognized WorkspaceChange"+ _ -> UnknownChange parseJSON _ = mzero -- | Workspace Event@@ -168,6 +170,7 @@ | WinFloating -- ^ the window has transitioned to or from floating | WinUrgent -- ^ the window has become urgent or lost its urgent status | WinMark -- ^ a mark has been added to or removed from the window + | WinUnknown -- ^ an unknown change, submit a PR to add a new WindowChange if you get this deriving (Eq, Show, Generic) instance ToJSON WindowChange where@@ -181,6 +184,7 @@ WinUrgent -> text "urgent" WinMark -> text "mark" WinClose -> text "close"+ WinUnknown -> text "unknown" instance FromJSON WindowChange where parseJSON (String s) = pure $! case s of@@ -193,7 +197,7 @@ "urgent" -> WinUrgent "mark" -> WinMark "close" -> WinClose- _ -> error "Received unrecognized WorkspaceChange"+ _ -> WinUnknown parseJSON _ = mzero
src/I3IPC/Message.hs view
@@ -7,6 +7,7 @@ ) where +import Control.Monad.IO.Class import Network.Socket.ByteString.Lazy import Network.Socket ( Socket ) import qualified Data.ByteString.Lazy as BSL@@ -60,9 +61,11 @@ putWord32host $ fromIntegral (fromEnum msgtype) -- | Send a message over the socket of 'MessageType' and some content-sendMsgPayload :: Socket -> MessageType -> BSL.ByteString -> IO Int64-sendMsgPayload soc msgtype msg = createMsgPayload msgtype msg & send soc+sendMsgPayload+ :: MonadIO m => Socket -> MessageType -> BSL.ByteString -> m Int64+sendMsgPayload soc msgtype msg =+ liftIO $ createMsgPayload msgtype msg & send soc -- | Similar to 'sendMsg' but with no message body-sendMsg :: Socket -> MessageType -> IO Int64-sendMsg soc msgtype = createMsg msgtype & send soc+sendMsg :: MonadIO m => Socket -> MessageType -> m Int64+sendMsg soc msgtype = liftIO $ createMsg msgtype & send soc
src/I3IPC/Reply.hs view
@@ -161,6 +161,7 @@ Horizontal | Vertical | OrientNone+ | UnknownOrient deriving (Eq, Generic, Show) instance FromJSON NodeOrientation where@@ -168,14 +169,15 @@ "none" -> OrientNone "horizontal" -> Horizontal "vertical" -> Vertical- _ -> error "Unrecognized NodeOrientation"+ _ -> UnknownOrient parseJSON _ = mzero instance ToJSON NodeOrientation where toEncoding = \case- OrientNone -> text "none"- Vertical -> text "vertical"- Horizontal -> text "horizontal"+ OrientNone -> text "none"+ Vertical -> text "vertical"+ Horizontal -> text "horizontal"+ UnknownOrient -> text "unknown" instance ToJSON Node where toEncoding =@@ -190,6 +192,7 @@ | Class | WindowRole | TransientFor+ | UnknownProperty deriving (Eq, Enum, Ord, Generic, Show) instance FromJSONKey WindowProperty where@@ -201,23 +204,25 @@ "class" -> Class "window_role" -> WindowRole "transient_for" -> TransientFor- _ -> error "Unrecognized window property"+ _ -> UnknownProperty instance ToJSONKey WindowProperty where toJSONKey = ToJSONKeyText f g where f x = case x of- Title -> "title"- Instance -> "instance"- Class -> "class"- WindowRole -> "window_role"- TransientFor -> "transient_for"+ Title -> "title"+ Instance -> "instance"+ Class -> "class"+ WindowRole -> "window_role"+ TransientFor -> "transient_for"+ UnknownProperty -> "unknown" g x = case x of- Title -> text "title"- Instance -> text "instance"- Class -> text "class"- WindowRole -> text "window_role"- TransientFor -> text "transient_for"+ Title -> text "title"+ Instance -> text "instance"+ Class -> text "class"+ WindowRole -> text "window_role"+ TransientFor -> text "transient_for"+ UnknownProperty -> text "unknown" instance FromJSON WindowProperty where parseJSON (String s) = pure $! case s of@@ -226,17 +231,18 @@ "class" -> Class "window_role" -> WindowRole "transient_for" -> TransientFor- _ -> error "Unrecognized WindowProperty variant found"+ _ -> UnknownProperty parseJSON _ = mzero instance ToJSON WindowProperty where toEncoding = \case- Title -> text "title"- Instance -> text "instance"- Class -> text "class"- WindowRole -> text "window_role"- TransientFor -> text "transient_for"+ Title -> text "title"+ Instance -> text "instance"+ Class -> text "class"+ WindowRole -> text "window_role"+ TransientFor -> text "transient_for"+ UnknownProperty -> text "unknown" -- | Marks Reply -- The reply consists of a single array of strings for each container that has a mark. A mark can only be set on one container, so the array is unique. The order of that array is undefined.@@ -250,20 +256,22 @@ Normal | None | Pixel+ | UnknownBorder deriving (Eq, Generic, Show) instance ToJSON NodeBorder where toEncoding = \case- Normal -> text "normal"- None -> text "none"- Pixel -> text "pixel"+ Normal -> text "normal"+ None -> text "none"+ Pixel -> text "pixel"+ UnknownBorder -> text "unknown" instance FromJSON NodeBorder where parseJSON (String s) = pure $! case s of "normal" -> Normal "none" -> None "pixel" -> Pixel- _ -> error "Unrecognized NodeBorder found"+ _ -> UnknownBorder parseJSON _ = mzero data Rect = Rect {@@ -283,6 +291,7 @@ | FloatingConType | WorkspaceType | DockAreaType+ | UnknownType deriving (Eq, Generic, Show) instance ToJSON NodeType where@@ -293,6 +302,7 @@ FloatingConType -> text "floating_con" WorkspaceType -> text "workspace" DockAreaType -> text "dockarea"+ UnknownType -> text "unknown" instance FromJSON NodeType where parseJSON (String s) = pure $! case s of@@ -302,7 +312,7 @@ "floating_con" -> FloatingConType "workspace" -> WorkspaceType "dockarea" -> DockAreaType- _ -> error "Received unrecognized NodeType"+ _ -> UnknownType parseJSON _ = mzero data NodeLayout =@@ -312,6 +322,7 @@ | TabbedLayout | DockAreaLayout | OutputLayout+ | UnknownLayout deriving (Eq, Generic, Show) instance ToJSON NodeLayout where@@ -322,6 +333,7 @@ TabbedLayout -> text "tabbed" DockAreaLayout -> text "dockarea" OutputLayout -> text "output"+ UnknownLayout -> text "unknown" instance FromJSON NodeLayout where parseJSON (String s) = pure $! case s of@@ -331,7 +343,7 @@ "tabbed" -> TabbedLayout "dockarea" -> DockAreaLayout "output" -> OutputLayout- _ -> error "Received unrecognized NodeLayout"+ _ -> UnknownLayout parseJSON _ = mzero -- | BarConfig Reply@@ -385,6 +397,7 @@ | BindingModeText | BindingModeBg | BindingModeBorder+ | UnknownBarPart deriving (Eq, Enum, Ord, Generic, Show, FromJSONKey) instance ToJSONKey BarPart where@@ -412,6 +425,7 @@ BindingModeText -> "binding_mode_text" BindingModeBg -> "binding_mode_bg" BindingModeBorder -> "binding_mode_border"+ UnknownBarPart -> "unknown" g x = case x of Background -> text "background" Statusline -> text "statusline"@@ -434,6 +448,7 @@ BindingModeText -> text "binding_mode_text" BindingModeBg -> text "binding_mode_bg" BindingModeBorder -> text "binding_mode_border"+ UnknownBarPart -> text "unknown" instance FromJSON BarPart where parseJSON (String s) = pure $! case s of@@ -458,7 +473,7 @@ "binding_mode_text" -> BindingModeText "binding_mode_bg" -> BindingModeBg "binding_mode_border" -> BindingModeBorder- _ -> error "Unrecognized BarPart variant found"+ _ -> UnknownBarPart parseJSON _ = mzero instance ToJSON BarPart where@@ -484,6 +499,7 @@ BindingModeText -> text "binding_mode_text" BindingModeBg -> text "binding_mode_bg" BindingModeBorder -> text "binding_mode_border"+ UnknownBarPart -> text "unknown" -- | Version Reply data VersionReply = VersionReply {
+ test/event/winevent.json view
@@ -0,0 +1,57 @@+{+ "change": "focus",+ "container": {+ "id": 94131357905936,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": true,+ "output": "DP-4.8",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 3,+ "rect": {+ "x": 3207,+ "y": 54,+ "width": 1249,+ "height": 1362+ },+ "deco_rect": {+ "x": 0,+ "y": 0,+ "width": 0,+ "height": 0+ },+ "window_rect": {+ "x": 3,+ "y": 3,+ "width": 1243,+ "height": 1356+ },+ "geometry": {+ "x": 0,+ "y": 0,+ "width": 980,+ "height": 435+ },+ "name": "i3ipc: stack ghci",+ "window": 33554442,+ "window_properties": {+ "class": "URxvt",+ "instance": "urxvt",+ "title": "i3ipc: stack ghci",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+}
+ test/event/workspace.json view
@@ -0,0 +1,204 @@+{+ "change": "focus",+ "current": {+ "id": 106996225489920,+ "type": "workspace",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 2560, "y": 29, "width": 2560, "height": 1571 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "9",+ "num": 9,+ "gaps": { "inner": 0, "outer": 0 },+ "window": null,+ "nodes": [+ {+ "id": 106996225488640,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": true,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 2572, "y": 41, "width": 1261, "height": 1547 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 1, "y": 1, "width": 1259, "height": 1545 },+ "geometry": { "x": 3328, "y": 416, "width": 1024, "height": 768 },+ "name": "I3IPC.hs - i3ipc - Visual Studio Code",+ "window": 62915005,+ "window_properties": {+ "class": "Code",+ "instance": "code",+ "window_role": "browser-window",+ "title": "I3IPC.hs - i3ipc - Visual Studio Code",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225489280,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 3847, "y": 41, "width": 1261, "height": 1547 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 1, "y": 1, "width": 1259, "height": 1545 },+ "geometry": { "x": 0, "y": 0, "width": 860, "height": 460 },+ "name": "i3ipc: stack ghci",+ "window": 58720329,+ "window_properties": {+ "class": "URxvt",+ "instance": "urxvt",+ "title": "i3ipc: stack ghci",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225488640, 106996225489280],+ "fullscreen_mode": 1,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ "old": {+ "id": 106996225529600,+ "type": "workspace",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 30, "width": 2560, "height": 1570 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "4",+ "num": 4,+ "gaps": { "inner": 0, "outer": 0 },+ "window": null,+ "nodes": [+ {+ "id": 106996225528960,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.30000000000000004441,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 12, "y": 42, "width": 749, "height": 1546 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 1, "y": 1, "width": 747, "height": 1544 },+ "geometry": { "x": 0, "y": 0, "width": 747, "height": 1544 },+ "name": "Inbox - evan@vectorface.com - Mozilla Thunderbird",+ "window": 73400336,+ "window_properties": {+ "class": "Thunderbird",+ "instance": "Mail",+ "window_role": "3pane",+ "title": "Inbox - evan@vectorface.com - Mozilla Thunderbird",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225528320,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.69999999999999995559,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 775, "y": 42, "width": 1773, "height": 1546 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 1, "y": 1, "width": 1771, "height": 1544 },+ "geometry": { "x": 0, "y": 0, "width": 1771, "height": 1544 },+ "name": "haskell - No output will be generated because there is no Main module - Stack Overflow - Mozilla Firefox",+ "window": 31457296,+ "window_properties": {+ "class": "Firefox",+ "instance": "Navigator",+ "window_role": "browser",+ "title": "haskell - No output will be generated because there is no Main module - Stack Overflow - Mozilla Firefox",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225528320, 106996225528960],+ "fullscreen_mode": 1,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+}
+ test/reply/output.json view
@@ -0,0 +1,23 @@+[+ {+ "name": "xroot-0",+ "active": false,+ "primary": false,+ "rect": { "x": 0, "y": 0, "width": 5120, "height": 1600 },+ "current_workspace": null+ },+ {+ "name": "DVI-I-2",+ "active": true,+ "primary": false,+ "rect": { "x": 0, "y": 0, "width": 2560, "height": 1600 },+ "current_workspace": "1"+ },+ {+ "name": "DVI-I-3",+ "active": true,+ "primary": false,+ "rect": { "x": 2560, "y": 0, "width": 2560, "height": 1600 },+ "current_workspace": "7"+ }+]
+ test/reply/tree.json view
@@ -0,0 +1,1249 @@+{+ "id": 106996225351040,+ "type": "root",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 0, "width": 5120, "height": 1600 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "root",+ "window": null,+ "nodes": [+ {+ "id": 106996225350400,+ "type": "output",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.33333333333333331483,+ "urgent": false,+ "focused": false,+ "layout": "output",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 0, "width": 2560, "height": 1600 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "__i3",+ "window": null,+ "nodes": [+ {+ "id": 106996225349760,+ "type": "con",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "__i3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "content",+ "window": null,+ "nodes": [+ {+ "id": 106996225349120,+ "type": "workspace",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "__i3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "__i3_scratch",+ "num": -1,+ "gaps": { "inner": 0, "outer": 0 },+ "window": null,+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 1,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225349120],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225349760],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225345920,+ "type": "output",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.33333333333333331483,+ "urgent": false,+ "focused": false,+ "layout": "output",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 2560, "y": 0, "width": 2560, "height": 1600 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "DVI-I-3",+ "window": null,+ "nodes": [+ {+ "id": 106996225345280,+ "type": "dockarea",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "dockarea",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 2560, "y": 0, "width": 2560, "height": 29 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "topdock",+ "window": null,+ "nodes": [+ {+ "id": 106996225536000,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 1.0,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 3,+ "rect": { "x": 2560, "y": 0, "width": 2560, "height": 29 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 2560, "height": 29 },+ "geometry": { "x": 2560, "y": 0, "width": 2560, "height": 29 },+ "name": "polybar-dvi3_DVI-I-3",+ "window": 23068674,+ "window_properties": {+ "class": "Polybar",+ "instance": "polybar",+ "title": "polybar-dvi3_DVI-I-3",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": true,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225536000],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": [{ "dock": 2, "insert_where": 2 }]+ },+ {+ "id": 106996225344640,+ "type": "con",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 2560, "y": 29, "width": 2560, "height": 1571 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "content",+ "window": null,+ "nodes": [+ {+ "id": 106996225538560,+ "type": "workspace",+ "orientation": "vertical",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splitv",+ "workspace_layout": "default",+ "last_split_layout": "splitv",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 2560, "y": 29, "width": 2560, "height": 1571 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "2",+ "num": 2,+ "gaps": { "inner": 0, "outer": 0 },+ "window": null,+ "nodes": [+ {+ "id": 106996225532160,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 1.0,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 2560, "y": 29, "width": 2560, "height": 1571 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 0,+ "y": 0,+ "width": 2560,+ "height": 1571+ },+ "geometry": {+ "x": 3847,+ "y": 41,+ "width": 1259,+ "height": 1545+ },+ "name": "Rust Track | Exercism - Mozilla Firefox",+ "window": 31457330,+ "window_properties": {+ "class": "Firefox",+ "instance": "Navigator",+ "window_role": "browser",+ "title": "Rust Track | Exercism - Mozilla Firefox",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225532160],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225511680,+ "type": "workspace",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 2560, "y": 29, "width": 2560, "height": 1571 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "7",+ "num": 7,+ "gaps": { "inner": 0, "outer": 0 },+ "window": null,+ "nodes": [+ {+ "id": 106996225509760,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 2572, "y": 41, "width": 1261, "height": 1547 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 1,+ "y": 1,+ "width": 1259,+ "height": 1545+ },+ "geometry": {+ "x": 3328,+ "y": 416,+ "width": 1024,+ "height": 768+ },+ "name": "Reply.hs - i3ipc - Visual Studio Code",+ "window": 62914600,+ "window_properties": {+ "class": "Code",+ "instance": "code",+ "window_role": "browser-window",+ "title": "Reply.hs - i3ipc - Visual Studio Code",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225509120,+ "type": "con",+ "orientation": "vertical",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splitv",+ "workspace_layout": "default",+ "last_split_layout": "splitv",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 3840, "y": 29, "width": 1280, "height": 1571 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": null,+ "window": null,+ "nodes": [+ {+ "id": 106996225511040,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": true,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": {+ "x": 3847,+ "y": 41,+ "width": 1261,+ "height": 766+ },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 1,+ "y": 1,+ "width": 1259,+ "height": 764+ },+ "geometry": {+ "x": 0,+ "y": 0,+ "width": 860,+ "height": 460+ },+ "name": "i3ipc: stack ghci",+ "window": 58720285,+ "window_properties": {+ "class": "URxvt",+ "instance": "urxvt",+ "title": "i3ipc: stack ghci",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225510400,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": {+ "x": 3847,+ "y": 821,+ "width": 1261,+ "height": 767+ },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 1,+ "y": 1,+ "width": 1259,+ "height": 765+ },+ "geometry": {+ "x": 0,+ "y": 0,+ "width": 860,+ "height": 460+ },+ "name": "~/personal/haskell/i3ipc",+ "window": 58720305,+ "window_properties": {+ "class": "URxvt",+ "instance": "urxvt",+ "title": "~/personal/haskell/i3ipc",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225511040, 106996225510400],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225509120, 106996225509760],+ "fullscreen_mode": 1,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225516160,+ "type": "workspace",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 2560, "y": 29, "width": 2560, "height": 1571 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "8",+ "num": 8,+ "gaps": { "inner": 0, "outer": 0 },+ "window": null,+ "nodes": [+ {+ "id": 106996225514880,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5999999999999999778,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 2572, "y": 41, "width": 1517, "height": 1547 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 1,+ "y": 1,+ "width": 1515,+ "height": 1545+ },+ "geometry": {+ "x": 2573,+ "y": 42,+ "width": 1515,+ "height": 1545+ },+ "name": "Game.js - unified-components - Visual Studio Code",+ "window": 62914561,+ "window_properties": {+ "class": "Code",+ "instance": "code",+ "window_role": "browser-window",+ "title": "Game.js - unified-components - Visual Studio Code",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225515520,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.4000000000000000222,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 4103, "y": 41, "width": 1005, "height": 1547 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 1,+ "y": 1,+ "width": 1003,+ "height": 1545+ },+ "geometry": { "x": 0, "y": 0, "width": 860, "height": 460 },+ "name": "~/dev/unified-components",+ "window": 58720266,+ "window_properties": {+ "class": "URxvt",+ "instance": "urxvt",+ "title": "~/dev/unified-components",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225514880, 106996225515520],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225511680, 106996225516160, 106996225538560],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225344000,+ "type": "dockarea",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-3",+ "layout": "dockarea",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 2560, "y": 1600, "width": 2560, "height": 0 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "bottomdock",+ "window": null,+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": [{ "dock": 3, "insert_where": 2 }]+ }+ ],+ "floating_nodes": [],+ "focus": [106996225344640, 106996225345280, 106996225344000],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225348480,+ "type": "output",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.33333333333333331483,+ "urgent": false,+ "focused": false,+ "layout": "output",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 0, "width": 2560, "height": 1600 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "DVI-I-2",+ "window": null,+ "nodes": [+ {+ "id": 106996225347840,+ "type": "dockarea",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "dockarea",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 0, "width": 2560, "height": 30 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "topdock",+ "window": null,+ "nodes": [+ {+ "id": 106996225535360,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 1.0,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 3,+ "rect": { "x": 0, "y": 0, "width": 2560, "height": 30 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 2560, "height": 30 },+ "geometry": { "x": 0, "y": 0, "width": 2560, "height": 30 },+ "name": "polybar-dvi2_DVI-I-2",+ "window": 10485762,+ "window_properties": {+ "class": "Polybar",+ "instance": "polybar",+ "title": "polybar-dvi2_DVI-I-2",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": true,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225535360],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": [{ "dock": 2, "insert_where": 2 }]+ },+ {+ "id": 106996225347200,+ "type": "con",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 30, "width": 2560, "height": 1570 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "content",+ "window": null,+ "nodes": [+ {+ "id": 106996225343360,+ "type": "workspace",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 30, "width": 2560, "height": 1570 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "1",+ "num": 1,+ "gaps": { "inner": 0, "outer": 0 },+ "window": null,+ "nodes": [+ {+ "id": 106996225525760,+ "type": "con",+ "orientation": "vertical",+ "scratchpad_state": "none",+ "percent": 0.25999999999999989786,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splitv",+ "workspace_layout": "default",+ "last_split_layout": "splitv",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 30, "width": 666, "height": 1570 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": null,+ "window": null,+ "nodes": [+ {+ "id": 106996225518080,+ "type": "con",+ "orientation": "vertical",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "stacked",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 12, "y": 42, "width": 647, "height": 766 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 0,+ "y": 0,+ "width": 0,+ "height": 0+ },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": null,+ "window": null,+ "nodes": [+ {+ "id": 106996225537920,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": {+ "x": 12,+ "y": 78,+ "width": 647,+ "height": 730+ },+ "deco_rect": {+ "x": 0,+ "y": 0,+ "width": 647,+ "height": 18+ },+ "window_rect": {+ "x": 1,+ "y": 0,+ "width": 645,+ "height": 729+ },+ "geometry": {+ "x": 12,+ "y": 42,+ "width": 747,+ "height": 1544+ },+ "name": "Buddy List",+ "window": 20971615,+ "window_properties": {+ "class": "Pidgin",+ "instance": "Pidgin",+ "window_role": "buddy_list",+ "title": "Buddy List",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225517440,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": {+ "x": 12,+ "y": 78,+ "width": 647,+ "height": 730+ },+ "deco_rect": {+ "x": 0,+ "y": 18,+ "width": 647,+ "height": 18+ },+ "window_rect": {+ "x": 1,+ "y": 0,+ "width": 645,+ "height": 729+ },+ "geometry": {+ "x": 13,+ "y": 78,+ "width": 800,+ "height": 1509+ },+ "name": "Signal",+ "window": 48234497,+ "window_properties": {+ "class": "Signal",+ "instance": "signal",+ "window_role": "browser-window",+ "title": "Signal",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225537920, 106996225517440],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225526400,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.5,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": {+ "x": 12,+ "y": 822,+ "width": 647,+ "height": 766+ },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 1,+ "y": 1,+ "width": 645,+ "height": 764+ },+ "geometry": {+ "x": 0,+ "y": 0,+ "width": 491,+ "height": 753+ },+ "name": "Francis",+ "window": 20974804,+ "window_properties": {+ "class": "Pidgin",+ "instance": "Pidgin",+ "window_role": "conversation",+ "title": "Francis",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225526400, 106996225518080],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225512960,+ "type": "con",+ "orientation": "vertical",+ "scratchpad_state": "none",+ "percent": 0.73999999999999999112,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splitv",+ "workspace_layout": "default",+ "last_split_layout": "splitv",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 666, "y": 30, "width": 1894, "height": 1570 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": null,+ "window": null,+ "nodes": [+ {+ "id": 106996225532800,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 1.0,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": {+ "x": 673,+ "y": 42,+ "width": 1875,+ "height": 1546+ },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 1,+ "y": 1,+ "width": 1873,+ "height": 1544+ },+ "geometry": {+ "x": 775,+ "y": 42,+ "width": 1771,+ "height": 1544+ },+ "name": "i3: IPC interface (interprocess communication) - Mozilla Firefox",+ "window": 31457319,+ "window_properties": {+ "class": "Firefox",+ "instance": "Navigator",+ "window_role": "browser",+ "title": "i3: IPC interface (interprocess communication) - Mozilla Firefox",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225532800],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225512960, 106996225525760],+ "fullscreen_mode": 1,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225531520,+ "type": "workspace",+ "orientation": "horizontal",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 30, "width": 2560, "height": 1570 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "4",+ "num": 4,+ "gaps": { "inner": 0, "outer": 0 },+ "window": null,+ "nodes": [+ {+ "id": 106996225513600,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.30000000000000004441,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 12, "y": 42, "width": 749, "height": 1546 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 1,+ "y": 1,+ "width": 747,+ "height": 1544+ },+ "geometry": { "x": 0, "y": 0, "width": 747, "height": 1544 },+ "name": "Inbox - evan@vectorface.com - Mozilla Thunderbird",+ "window": 73400336,+ "window_properties": {+ "class": "Thunderbird",+ "instance": "Mail",+ "window_role": "3pane",+ "title": "Inbox - evan@vectorface.com - Mozilla Thunderbird",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225533440,+ "type": "con",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": 0.69999999999999995559,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "splith",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": 1,+ "rect": { "x": 775, "y": 42, "width": 1773, "height": 1546 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": {+ "x": 1,+ "y": 1,+ "width": 1771,+ "height": 1544+ },+ "geometry": { "x": 0, "y": 0, "width": 1771, "height": 1544 },+ "name": "Phil lobby gamelist p2 (!96) · Merge Requests · Client / unified-components · GitLab - Mozilla Firefox",+ "window": 31457296,+ "window_properties": {+ "class": "Firefox",+ "instance": "Navigator",+ "window_role": "browser",+ "title": "Phil lobby gamelist p2 (!96) · Merge Requests · Client / unified-components · GitLab - Mozilla Firefox",+ "transient_for": null+ },+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225513600, 106996225533440],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225343360, 106996225531520],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ },+ {+ "id": 106996225346560,+ "type": "dockarea",+ "orientation": "none",+ "scratchpad_state": "none",+ "percent": null,+ "urgent": false,+ "focused": false,+ "output": "DVI-I-2",+ "layout": "dockarea",+ "workspace_layout": "default",+ "last_split_layout": "splith",+ "border": "pixel",+ "current_border_width": -1,+ "rect": { "x": 0, "y": 1600, "width": 2560, "height": 0 },+ "deco_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "window_rect": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "geometry": { "x": 0, "y": 0, "width": 0, "height": 0 },+ "name": "bottomdock",+ "window": null,+ "nodes": [],+ "floating_nodes": [],+ "focus": [],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": [{ "dock": 3, "insert_where": 2 }]+ }+ ],+ "floating_nodes": [],+ "focus": [106996225347200, 106996225347840, 106996225346560],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+ }+ ],+ "floating_nodes": [],+ "focus": [106996225345920, 106996225348480, 106996225350400],+ "fullscreen_mode": 0,+ "sticky": false,+ "floating": "auto_off",+ "swallows": []+}
+ test/reply/version.json view
@@ -0,0 +1,7 @@+{+ "major": 4,+ "minor": 16,+ "patch": 0,+ "human_readable": "4.16-3-gecbfb2b4 (2018-11-04, branch \"gaps\")",+ "loaded_config_file_name": "/home/evan/.config/i3/config"+}
+ test/reply/workspace.json view
@@ -0,0 +1,47 @@+[+ {+ "num": 2,+ "name": "2",+ "visible": false,+ "focused": false,+ "rect": { "x": 2560, "y": 29, "width": 2560, "height": 1571 },+ "output": "DVI-I-3",+ "urgent": false+ },+ {+ "num": 7,+ "name": "7",+ "visible": true,+ "focused": true,+ "rect": { "x": 2560, "y": 29, "width": 2560, "height": 1571 },+ "output": "DVI-I-3",+ "urgent": false+ },+ {+ "num": 8,+ "name": "8",+ "visible": false,+ "focused": false,+ "rect": { "x": 2560, "y": 29, "width": 2560, "height": 1571 },+ "output": "DVI-I-3",+ "urgent": false+ },+ {+ "num": 1,+ "name": "1",+ "visible": true,+ "focused": false,+ "rect": { "x": 0, "y": 30, "width": 2560, "height": 1570 },+ "output": "DVI-I-2",+ "urgent": false+ },+ {+ "num": 4,+ "name": "4",+ "visible": false,+ "focused": false,+ "rect": { "x": 0, "y": 30, "width": 2560, "height": 1570 },+ "output": "DVI-I-2",+ "urgent": false+ }+]