arbtt-0.4.5: src/Graphics/Win32/Window/Extra.hsc
{-# LANGUAGE ForeignFunctionInterface #-}
module Graphics.Win32.Window.Extra
( fetchWindowTitles
, getForegroundWindow
, getIdleTime
) where
import Graphics.Win32.GDI.Types
import System.Win32.Types
import System.Win32.Process
import System.Win32.File ( closeHandle )
import System.IO
import Control.Exception ( bracket )
import Control.Monad
import Foreign
import Data.IORef
#include <windows.h>
type EnumWindowsProc = HWND -> LPARAM -> IO Bool
foreign import ccall "wrapper"
mkEnumWindowsProc :: EnumWindowsProc -> IO (FunPtr EnumWindowsProc)
foreign import stdcall safe "windows.h EnumWindows"
c_EnumWindows :: (FunPtr EnumWindowsProc) -> LPARAM -> IO Bool
foreign import stdcall unsafe "windows.h GetWindowTextW"
c_GetWindowText :: HWND -> LPWSTR -> Int -> IO Int
foreign import stdcall unsafe "windows.h GetWindowTextLengthW"
c_GetWindowTextLength :: HWND -> IO Int
foreign import stdcall unsafe "windows.h GetForegroundWindow"
c_GetForegroundWindow :: IO HWND
foreign import stdcall unsafe "windows.h GetLastInputInfo"
c_GetLastInputInfo :: Ptr LASTINPUTINFO -> IO Bool
foreign import stdcall unsafe "windows.h GetTickCount"
c_GetTickCount :: IO DWORD
foreign import stdcall unsafe "windows.h GetWindowThreadProcessId"
c_GetWindowThreadProcessId :: HWND -> LPDWORD -> IO DWORD
foreign import stdcall unsafe "psapi.h GetProcessImageFileNameW"
c_GetProcessImageFileName :: HANDLE -> LPTSTR -> DWORD -> IO DWORD
foreign import stdcall unsafe "windows.h SetLastError"
c_SetLastError :: DWORD -> IO ()
foreign import stdcall unsafe "windows.h IsWindowVisible"
c_IsWindowVisible :: HWND -> IO Bool
data LASTINPUTINFO = LASTINPUTINFO DWORD deriving (Show)
instance Storable LASTINPUTINFO where
sizeOf = const (#size LASTINPUTINFO)
alignment = sizeOf
poke buf (LASTINPUTINFO t) = do
(#poke LASTINPUTINFO, cbSize) buf ((#size LASTINPUTINFO) :: UINT)
(#poke LASTINPUTINFO, dwTime) buf t
peek buf = do
t <- (#peek LASTINPUTINFO, dwTime) buf
return $ LASTINPUTINFO t
fetchWindowTitles :: IO [(HWND, String,String)]
fetchWindowTitles = do
resultRef <- newIORef []
callback <- mkEnumWindowsProc $ \winh _ -> do
v <- c_IsWindowVisible winh -- only consider visible windows
if not v then return True else do
proc <- alloca $ \pid_p -> do
c_GetWindowThreadProcessId winh pid_p
pid <- peek pid_p
bracket (openProcess pROCESS_QUERY_INFORMATION False pid) closeHandle $ \ph ->
allocaArray0 (#const MAX_PATH) $ \c_test -> do
c_SetLastError (#const ERROR_SUCCESS)
r <- c_GetProcessImageFileName ph c_test ((#const MAX_PATH)+1)
err <- getLastError
if r == 0 && err /= (#const ERROR_SUCCESS)
then do hPutStrLn stderr $ "GetProcessImageFileName returned error " ++ show err ++ "."
return ""
else peekTString c_test
len <- c_GetWindowTextLength winh
str <- allocaArray0 len $ \c_test -> do
c_SetLastError (#const ERROR_SUCCESS)
r <- c_GetWindowText winh c_test (len+1)
err <- getLastError
if r == 0 && err /= (#const ERROR_SUCCESS)
then do hPutStrLn stderr $ "GetWindowText returned error " ++ show err ++ "."
return ""
else peekTString c_test
unless (str `elem` ["", "Default IME"]) $ do -- Ignore some windows by default
modifyIORef resultRef ((winh,str,proc):)
return True
c_EnumWindows callback 0
readIORef resultRef
getForegroundWindow :: IO HWND
getForegroundWindow = c_GetForegroundWindow
-- | Idle time in milliseconds
getIdleTime :: IO Integer
getIdleTime = with (LASTINPUTINFO 0) $ \lii_p -> do
failIfFalse_ "GetLastInputInfo" $ c_GetLastInputInfo lii_p
LASTINPUTINFO lii <- peek lii_p
now <- c_GetTickCount
return (fromIntegral (now - lii))