packages feed

window-utils-0.1.0.0: src/OS/Window/Win32.hs

module OS.Window.Win32 (
    Window, -- it's important that the implementation is hidden here, since it will vary between platforms
    findByName,
    setTitle,
    setIcon,
) where

import Codec.Picture
import Control.Monad
import Data.ByteString (ByteString, useAsCString)
import Data.ByteString qualified as BS
import Data.Text (Text)
import Data.Text qualified as T
import Data.Vector.Storable qualified as Vec
import Foreign.Ptr
import Graphics.Win32
import Unsafe.Coerce

newtype Window = Window HWND

findByName ::
    -- | substring which must appear in the window title
    Text ->
    IO Window
findByName name = do
    Just w <- findWindow Nothing . Just $ T.unpack name
    pure $ Window w

setTitle :: Window -> Text -> IO ()
setTitle (Window w) t = setWindowText w $ T.unpack t

setIcon ::
    Window ->
    -- | PNG image
    ByteString ->
    IO ()
setIcon (Window w) img = case decodePng img of
    Left e -> error e
    Right (ImageRGBA8 Image{..}) -> do
        let bs = BS.pack . reorderPixels $ Vec.toList imageData
        icon <- useAsCString bs $ createIcon nullPtr imageWidth imageHeight 1 32 nullPtr . castPtr
        void $ sendMessage w wM_SETICON iCON_BIG $ unsafeCoerce icon
      where
        reorderPixels = \case
            r : g : b : a : ps ->
                b : g : r : a : reorderPixels ps
            [] -> []
            _ -> error "vector length not a multiple of 4"
    _ -> error "wrong pixel type"

--TODO these should all be upstreamed to Win32 - see https://github.com/haskell/win32/pull/194
{- HLINT ignore "Use camelCase" -}
createIcon :: HINSTANCE -> Int -> Int -> BYTE -> BYTE -> Ptr BYTE -> Ptr BYTE -> IO HICON
createIcon instance_ width height planes bitsPixel andBits xorBits =
    failIfNull "CreateIcon" $ c_CreateIcon instance_ width height planes bitsPixel andBits xorBits
foreign import ccall unsafe "windows.h CreateIcon" -- when upstreaming, use WINDOWS_CCONV rather than ccall
    c_CreateIcon :: HINSTANCE -> Int -> Int -> BYTE -> BYTE -> Ptr BYTE -> Ptr BYTE -> IO HICON
wM_SETICON :: WindowMessage -- https://github.com/haskell/win32/blob/master/Graphics/Win32/Message.hsc
wM_SETICON = 128
iCON_BIG :: WPARAM -- not sure there's currently a relevant module for this (also, add `iCON_SMALL = 0`)
iCON_BIG = 1