{-# LINE 1 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_HADDOCK hide #-}
module GHC.IO.Windows.Handle
(
NativeHandle(),
ConsoleHandle(),
IoHandle(),
HANDLE,
Io(),
convertHandle,
toHANDLE,
fromHANDLE,
handleToMode,
isAsynchronous,
optimizeFileAccess,
stdin,
stdout,
stderr,
openFile,
openFileAsTemp,
release
) where
#include "windows_cconv.h"
import Data.Bits ((.|.), (.&.), shiftL)
import Data.Functor ((<$>))
import Data.Typeable
import GHC.Base
import GHC.Enum
import GHC.Num
import GHC.Real
import GHC.List
import GHC.Word (Word8, Word16, Word64)
import GHC.IO hiding (mask)
import GHC.IO.Buffer
import GHC.IO.BufferedIO
import qualified GHC.IO.Device
import GHC.IO.Device (SeekMode(..), IODeviceType(..), IODevice(), devType, setSize)
import GHC.IO.Exception
import GHC.IO.IOMode
import GHC.IO.Windows.Encoding (withGhcInternalToUTF16, withUTF16ToGhcInternal)
import GHC.IO.Windows.Paths (getDevicePath)
import GHC.IO.Handle.Internals (debugIO)
import GHC.IORef
import GHC.Event.Windows (LPOVERLAPPED, withOverlappedEx, IOResult(..))
import Foreign.Ptr
import Foreign.C
import Foreign.Marshal.Array (pokeArray)
import Foreign.Marshal.Alloc (alloca, allocaBytes)
import Foreign.Marshal.Utils (with, fromBool)
import Foreign.Storable (Storable (..))
import qualified GHC.Event.Windows as Mgr
import GHC.Windows (LPVOID, LPDWORD, DWORD, HANDLE, BOOL, LPCTSTR, ULONG, WORD,
UCHAR, failIf, iNVALID_HANDLE_VALUE, failWith,
failIfFalse_, getLastError)
import Text.Show
data NativeHandle
data ConsoleHandle
data IoHandle a where
NativeHandle :: { getNativeHandle :: HANDLE
, isAsynchronous :: Bool } -> IoHandle NativeHandle
ConsoleHandle :: { getConsoleHandle :: HANDLE
, cookedHandle :: IORef Bool
} -> IoHandle ConsoleHandle
type Io a = IoHandle a
convertHandle :: Io ConsoleHandle -> Bool -> Io NativeHandle
convertHandle io async
= let !hwnd = getConsoleHandle io
in NativeHandle hwnd async
instance Show (Io NativeHandle) where
show = show . toHANDLE
instance Show (Io ConsoleHandle) where
show = show . getConsoleHandle
instance GHC.IO.Device.RawIO (Io NativeHandle) where
read = hwndRead
readNonBlocking = hwndReadNonBlocking
write = hwndWrite
writeNonBlocking = hwndWriteNonBlocking
instance GHC.IO.Device.RawIO (Io ConsoleHandle) where
read = consoleRead True
readNonBlocking = consoleReadNonBlocking
write = consoleWrite
writeNonBlocking = consoleWriteNonBlocking
class (GHC.IO.Device.RawIO a, IODevice a, BufferedIO a, Typeable a)
=> RawHandle a where
toHANDLE :: a -> HANDLE
fromHANDLE :: HANDLE -> a
isLockable :: a -> Bool
setCooked :: a -> Bool -> IO a
isCooked :: a -> IO Bool
instance RawHandle (Io NativeHandle) where
toHANDLE = getNativeHandle
fromHANDLE = flip NativeHandle True
isLockable _ = True
setCooked = const . return
isCooked _ = return False
instance RawHandle (Io ConsoleHandle) where
toHANDLE = getConsoleHandle
fromHANDLE h = unsafePerformIO $ ConsoleHandle h <$> newIORef False
isLockable _ = False
setCooked h val =
do writeIORef (cookedHandle h) val
return h
isCooked h = readIORef (cookedHandle h)
instance GHC.IO.Device.IODevice (Io NativeHandle) where
ready = handle_ready
close = handle_close
isTerminal = handle_is_console
isSeekable = handle_is_seekable
seek = handle_seek
tell = handle_tell
getSize = handle_get_size
setSize = handle_set_size
setEcho = handle_set_echo
getEcho = handle_get_echo
setRaw = handle_set_buffering
devType = handle_dev_type
dup = handle_duplicate
instance GHC.IO.Device.IODevice (Io ConsoleHandle) where
ready = handle_ready
close = handle_close . flip convertHandle False
isTerminal = handle_is_console
isSeekable = handle_is_seekable
seek = handle_console_seek
tell = handle_console_tell
getSize = handle_get_console_size
setSize = handle_set_console_size
setEcho = handle_set_echo
getEcho = handle_get_echo
setRaw = console_set_buffering
devType = handle_dev_type
dup = handle_duplicate
dEFAULT_BUFFER_SIZE :: Int
dEFAULT_BUFFER_SIZE = 8192
instance BufferedIO (Io NativeHandle) where
newBuffer _dev state = newByteBuffer dEFAULT_BUFFER_SIZE state
fillReadBuffer = readBuf'
fillReadBuffer0 = readBufNonBlocking
flushWriteBuffer = writeBuf'
flushWriteBuffer0 = writeBufNonBlocking
instance BufferedIO (Io ConsoleHandle) where
newBuffer _dev state = newByteBuffer dEFAULT_BUFFER_SIZE state
fillReadBuffer = readBuf'
fillReadBuffer0 = readBufNonBlocking
flushWriteBuffer = writeBuf'
flushWriteBuffer0 = writeBufNonBlocking
readBuf' :: RawHandle a => a -> Buffer Word8 -> IO (Int, Buffer Word8)
readBuf' hnd buf = do
debugIO ("readBuf handle=" ++ show (toHANDLE hnd) ++ " " ++
summaryBuffer buf ++ "\n")
(r,buf') <- readBuf hnd buf
debugIO ("after: " ++ summaryBuffer buf' ++ "\n")
return (r,buf')
writeBuf' :: RawHandle a => a -> Buffer Word8 -> IO (Buffer Word8)
writeBuf' hnd buf = do
debugIO ("writeBuf handle=" ++ show (toHANDLE hnd) ++ " " ++
summaryBuffer buf ++ "\n")
writeBuf hnd buf
type StdHandleId = DWORD
sTD_INPUT_HANDLE :: StdHandleId
sTD_INPUT_HANDLE = 4294967286
sTD_OUTPUT_HANDLE :: StdHandleId
sTD_OUTPUT_HANDLE = 4294967285
sTD_ERROR_HANDLE :: StdHandleId
sTD_ERROR_HANDLE = 4294967284
{-# LINE 259 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
getStdHandle :: StdHandleId -> IO HANDLE
getStdHandle hid =
failIf (== iNVALID_HANDLE_VALUE) "GetStdHandle" $ c_GetStdHandle hid
stdin, stdout, stderr :: Io ConsoleHandle
stdin = unsafePerformIO $ mkConsoleHandle =<< getStdHandle sTD_INPUT_HANDLE
stdout = unsafePerformIO $ mkConsoleHandle =<< getStdHandle sTD_OUTPUT_HANDLE
stderr = unsafePerformIO $ mkConsoleHandle =<< getStdHandle sTD_ERROR_HANDLE
mkConsoleHandle :: HANDLE -> IO (Io ConsoleHandle)
mkConsoleHandle hwnd
= do ref <- newIORef False
return $ ConsoleHandle hwnd ref
acCtrlD :: Int
acCtrlD = 0x04
acCtrlZ :: Int
acCtrlZ = 0x1A
acEotMask :: ULONG
acEotMask = (1 `shiftL` acCtrlD) .|. (1 `shiftL` acCtrlZ)
type PCONSOLE_READCONSOLE_CONTROL = Ptr CONSOLE_READCONSOLE_CONTROL
data CONSOLE_READCONSOLE_CONTROL = CONSOLE_READCONSOLE_CONTROL
{ crcNLength :: ULONG
, crcNInitialChars :: ULONG
, crcDwCtrlWakeupMask :: ULONG
, crcDwControlKeyState :: ULONG
} deriving Show
instance Storable CONSOLE_READCONSOLE_CONTROL where
sizeOf = const (16)
{-# LINE 301 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
alignment = const 4
{-# LINE 302 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
poke buf crc = do
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) buf
{-# LINE 304 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
(crcNLength crc)
((\hsc_ptr -> pokeByteOff hsc_ptr 4)) buf
{-# LINE 306 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
(crcNInitialChars crc)
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) buf
{-# LINE 308 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
(crcDwCtrlWakeupMask crc)
((\hsc_ptr -> pokeByteOff hsc_ptr 12)) buf
{-# LINE 310 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
(crcDwControlKeyState crc)
peek buf = do
vNLength <-
((\hsc_ptr -> peekByteOff hsc_ptr 0)) buf
{-# LINE 315 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
vNInitialChars <-
((\hsc_ptr -> peekByteOff hsc_ptr 4)) buf
{-# LINE 317 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
vDwCtrlWakeupMask <-
((\hsc_ptr -> peekByteOff hsc_ptr 8)) buf
{-# LINE 319 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
vDwControlKeyState <-
((\hsc_ptr -> peekByteOff hsc_ptr 12)) buf
{-# LINE 321 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
return $ CONSOLE_READCONSOLE_CONTROL {
crcNLength = vNLength,
crcNInitialChars = vNInitialChars,
crcDwCtrlWakeupMask = vDwCtrlWakeupMask,
crcDwControlKeyState = vDwControlKeyState
}
eotControl :: CONSOLE_READCONSOLE_CONTROL
eotControl =
CONSOLE_READCONSOLE_CONTROL
{ crcNLength = fromIntegral $
sizeOf (undefined :: CONSOLE_READCONSOLE_CONTROL)
, crcNInitialChars = 0
, crcDwCtrlWakeupMask = acEotMask
, crcDwControlKeyState = 0
}
type PINPUT_RECORD = Ptr ()
foreign import WINDOWS_CCONV safe "windows.h CreateFileW"
c_CreateFile :: LPCTSTR -> DWORD -> DWORD -> LPSECURITY_ATTRIBUTES
-> DWORD -> DWORD -> HANDLE
-> IO HANDLE
foreign import WINDOWS_CCONV safe "windows.h SetFileCompletionNotificationModes"
c_SetFileCompletionNotificationModes :: HANDLE -> UCHAR -> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h ReadFile"
c_ReadFile :: HANDLE -> LPVOID -> DWORD -> LPDWORD -> LPOVERLAPPED
-> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h WriteFile"
c_WriteFile :: HANDLE -> LPVOID -> DWORD -> LPDWORD -> LPOVERLAPPED
-> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h GetStdHandle"
c_GetStdHandle :: StdHandleId -> IO HANDLE
foreign import ccall safe "__handle_ready"
c_handle_ready :: HANDLE -> BOOL -> CInt -> IO CInt
foreign import ccall safe "__is_console"
c_is_console :: HANDLE -> IO BOOL
foreign import ccall safe "__set_console_buffering"
c_set_console_buffering :: HANDLE -> BOOL -> IO BOOL
foreign import ccall safe "__set_console_echo"
c_set_console_echo :: HANDLE -> BOOL -> IO BOOL
foreign import ccall safe "__get_console_echo"
c_get_console_echo :: HANDLE -> IO BOOL
foreign import ccall safe "__close_handle"
c_close_handle :: HANDLE -> IO Bool
foreign import ccall safe "__handle_type"
c_handle_type :: HANDLE -> IO Int
foreign import ccall safe "__set_file_pointer"
c_set_file_pointer :: HANDLE -> CLong -> DWORD -> Ptr CLong -> IO BOOL
foreign import ccall safe "__get_file_pointer"
c_get_file_pointer :: HANDLE -> IO CLong
foreign import ccall safe "__get_file_size"
c_get_file_size :: HANDLE -> IO CLong
foreign import ccall safe "__set_file_size"
c_set_file_size :: HANDLE -> CLong -> IO BOOL
foreign import ccall safe "__duplicate_handle"
c_duplicate_handle :: HANDLE -> Ptr HANDLE -> IO BOOL
foreign import ccall safe "__set_console_pointer"
c_set_console_pointer :: HANDLE -> CLong -> DWORD -> Ptr CLong -> IO BOOL
foreign import ccall safe "__get_console_pointer"
c_get_console_pointer :: HANDLE -> IO CLong
foreign import ccall safe "__get_console_buffer_size"
c_get_console_buffer_size :: HANDLE -> IO CLong
foreign import ccall safe "__set_console_buffer_size"
c_set_console_buffer_size :: HANDLE -> CLong -> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h ReadConsoleW"
c_read_console :: HANDLE -> Ptr Word16 -> DWORD -> Ptr DWORD
-> PCONSOLE_READCONSOLE_CONTROL -> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h WriteConsoleW"
c_write_console :: HANDLE -> Ptr Word16 -> DWORD -> Ptr DWORD -> Ptr ()
-> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h ReadConsoleInputW"
c_read_console_input :: HANDLE -> PINPUT_RECORD -> DWORD -> LPDWORD -> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h GetNumberOfConsoleInputEvents"
c_get_num_console_inputs :: HANDLE -> LPDWORD -> IO BOOL
type LPSECURITY_ATTRIBUTES = LPVOID
hwndRead :: Io NativeHandle -> Ptr Word8 -> Word64 -> Int -> IO Int
hwndRead hwnd ptr offset bytes = do
mngr <- Mgr.getSystemManager
fmap fromIntegral $ Mgr.withException "hwndRead" $
withOverlappedEx mngr "hwndRead" (toHANDLE hwnd) (isAsynchronous hwnd)
offset (startCB ptr) completionCB
where
startCB outBuf lpOverlapped = do
debugIO ":: hwndRead"
ret <- c_ReadFile (toHANDLE hwnd) (castPtr outBuf)
(fromIntegral bytes) nullPtr lpOverlapped
return $ Mgr.CbNone ret
completionCB err dwBytes
| err == 0 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 451 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 38 = Mgr.ioSuccess 0
{-# LINE 452 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == -1073741807 = Mgr.ioSuccess 0
{-# LINE 453 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 109 = Mgr.ioSuccess 0
{-# LINE 454 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == -1073741493 = Mgr.ioSuccess 0
{-# LINE 455 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 259 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 456 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 234 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 457 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| otherwise = Mgr.ioFailed err
hwndReadNonBlocking :: Io NativeHandle -> Ptr Word8 -> Word64 -> Int
-> IO (Maybe Int)
hwndReadNonBlocking hwnd ptr offset bytes
= do mngr <- Mgr.getSystemManager
val <- withOverlappedEx mngr "hwndReadNonBlocking" (toHANDLE hwnd)
(isAsynchronous hwnd) offset (startCB ptr)
completionCB
return $ ioValue val
where
startCB inputBuf lpOverlapped = do
debugIO ":: hwndReadNonBlocking"
ret <- c_ReadFile (toHANDLE hwnd) (castPtr inputBuf)
(fromIntegral bytes) nullPtr lpOverlapped
return $ Mgr.CbNone ret
completionCB err dwBytes
| err == 0 = Mgr.ioSuccess $ Just $! fromIntegral dwBytes
{-# LINE 481 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 38 = Mgr.ioSuccess Nothing
{-# LINE 482 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == -1073741807 = Mgr.ioSuccess Nothing
{-# LINE 483 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 109 = Mgr.ioSuccess Nothing
{-# LINE 484 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == -1073741493 = Mgr.ioSuccess Nothing
{-# LINE 485 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 259 = Mgr.ioSuccess Nothing
{-# LINE 486 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 234 = Mgr.ioSuccess $ Just $! fromIntegral dwBytes
{-# LINE 487 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| otherwise = Mgr.ioFailedAny err
hwndWrite :: Io NativeHandle -> Ptr Word8 -> Word64 -> Int -> IO ()
hwndWrite hwnd ptr offset bytes
= do mngr <- Mgr.getSystemManager
_ <- Mgr.withException "hwndWrite" $
withOverlappedEx mngr "hwndWrite" (toHANDLE hwnd)
(isAsynchronous hwnd) offset (startCB ptr)
completionCB
return ()
where
startCB outBuf lpOverlapped = do
debugIO ":: hwndWrite"
ret <- c_WriteFile (toHANDLE hwnd) (castPtr outBuf)
(fromIntegral bytes) nullPtr lpOverlapped
return $ Mgr.CbNone ret
completionCB err dwBytes
| err == 0 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 507 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 38 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 508 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| otherwise = Mgr.ioFailed err
hwndWriteNonBlocking :: Io NativeHandle -> Ptr Word8 -> Word64 -> Int -> IO Int
hwndWriteNonBlocking hwnd ptr offset bytes
= do mngr <- Mgr.getSystemManager
val <- withOverlappedEx mngr "hwndReadNonBlocking" (toHANDLE hwnd)
(isAsynchronous hwnd) offset (startCB ptr)
completionCB
return $ fromIntegral $ ioValue val
where
startCB :: Ptr a -> LPOVERLAPPED -> IO (Mgr.CbResult a1)
startCB outBuf lpOverlapped = do
debugIO ":: hwndWriteNonBlocking"
ret <- c_WriteFile (toHANDLE hwnd) (castPtr outBuf)
(fromIntegral bytes) nullPtr lpOverlapped
return $ Mgr.CbNone ret
completionCB err dwBytes
| err == 0 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 528 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| err == 38 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 529 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| otherwise = Mgr.ioFailed err
consoleWrite :: Io ConsoleHandle -> Ptr Word8 -> Word64 -> Int -> IO ()
consoleWrite hwnd ptr _offset bytes
= alloca $ \res ->
do failIfFalse_ "GHC.IO.Handle.consoleWrite" $ do
debugIO ":: consoleWrite"
withGhcInternalToUTF16 ptr bytes $ \(w_ptr, w_len) -> do
success <- c_write_console (toHANDLE hwnd) w_ptr
(fromIntegral w_len) res nullPtr
if not success
then return False
else do val <- fromIntegral <$> peek res
return $ val == w_len
consoleWriteNonBlocking :: Io ConsoleHandle -> Ptr Word8 -> Word64 -> Int -> IO Int
consoleWriteNonBlocking hwnd ptr _offset bytes
= alloca $ \res ->
do failIfFalse_ "GHC.IO.Handle.consoleWriteNonBlocking" $ do
debugIO ":: consoleWriteNonBlocking"
withGhcInternalToUTF16 ptr bytes $ \(w_ptr, w_len) -> do
c_write_console (toHANDLE hwnd) w_ptr (fromIntegral w_len)
res nullPtr
val <- fromIntegral <$> peek res
return val
consoleRead :: Bool -> Io ConsoleHandle -> Ptr Word8 -> Word64 -> Int -> IO Int
consoleRead blocking hwnd ptr _offset bytes
= alloca $ \res -> do
cooked <- isCooked hwnd
case cooked || not blocking of
False -> withUTF16ToGhcInternal ptr bytes $ \reqBytes w_ptr -> do
debugIO "consoleRead :: un-cooked I/O read."
res_code <- with eotControl $ \p_eotControl ->
c_read_console (toHANDLE hwnd) w_ptr (fromIntegral reqBytes) res
p_eotControl
err <- getLastError
when (not res_code) $
case () of
_ | err == 1 -> return ()
{-# LINE 612 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| otherwise -> failWith "GHC.IO.Handle.consoleRead" err
b_read <- fromIntegral <$> peek res
if b_read /= 1
then return b_read
else do w_first <- peekElemOff w_ptr 0
case () of
_ | w_first == fromIntegral acCtrlD -> return 0
| w_first == fromIntegral acCtrlZ -> return 0
| otherwise -> return b_read
True -> do
debugIO "consoleRead :: cooked I/O read."
let entries = fromIntegral $ bytes `div` ((20))
{-# LINE 631 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
allocaBytes entries $ \p_inputs ->
maybeReadEvent p_inputs entries res ptr
where maybeReadEvent p_inputs entries res w_ptr =
case (not blocking) of
True -> do
avail <- with (0 :: DWORD) $ \num_events_ptr -> do
failIfFalse_ "GHC.IO.Handle.consoleRead [non-blocking]" $
c_get_num_console_inputs (toHANDLE hwnd) num_events_ptr
peek num_events_ptr
debugIO $ "consoleRead [avail] :: " ++ show avail
if avail > 0
then readEvent p_inputs entries res w_ptr
else return 0
False -> readEvent p_inputs entries res w_ptr
readEvent p_inputs entries res w_ptr = do
failIfFalse_ "GHC.IO.Handle.consoleRead" $
c_read_console_input (toHANDLE hwnd) p_inputs
(fromIntegral entries) res
b_read <- fromIntegral <$> peek res
read <- cobble b_read w_ptr p_inputs
debugIO $ "readEvent: =" ++ show read
if read > 0
then return $ fromIntegral read
else maybeReadEvent p_inputs entries res w_ptr
cobble :: Int -> Ptr Word8 -> PINPUT_RECORD -> IO Int
cobble 0 _ _ = do debugIO "cobble: done."
return 0
cobble n w_ptr p_inputs =
do eventType <- peekByteOff p_inputs 0 :: IO WORD
debugIO $ "cobble: Length=" ++ show n
debugIO $ "cobble: Type=" ++ show eventType
let ni_offset = (20)
{-# LINE 676 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
let event = 4
{-# LINE 677 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
let char_offset = event + 10
{-# LINE 678 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
let btnDown_offset = event + 0
{-# LINE 679 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
let repeat_offset = event + 4
{-# LINE 680 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
let n' = n - 1
let p_inputs' = p_inputs `plusPtr` ni_offset
btnDown <- peekByteOff p_inputs btnDown_offset
repeated <- fromIntegral <$> (peekByteOff p_inputs repeat_offset :: IO WORD)
debugIO $ "cobble: BtnDown=" ++ show btnDown
if eventType == 1 && btnDown
{-# LINE 687 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
then do debugIO $ "cobble: read-char."
char <- peekByteOff p_inputs char_offset
let w_ptr' = w_ptr `plusPtr` 1
debugIO $ "cobble: offset - " ++ show char_offset
debugIO $ "cobble: show > " ++ show char
debugIO $ "cobble: repeat: " ++ show repeated
pokeArray w_ptr $ replicate repeated char
(+repeated) <$> cobble n' w_ptr' p_inputs'
else do debugIO $ "cobble: skip event."
cobble n' w_ptr p_inputs'
consoleReadNonBlocking :: Io ConsoleHandle -> Ptr Word8 -> Word64 -> Int
-> IO (Maybe Int)
consoleReadNonBlocking hwnd ptr offset bytes
= Just <$> consoleRead False hwnd ptr offset bytes
handle_ready :: RawHandle a => a -> Bool -> Int -> IO Bool
handle_ready hwnd write msecs = do
r <- throwErrnoIfMinus1Retry "GHC.IO.Windows.Handle.handle_ready" $
c_handle_ready (toHANDLE hwnd) write (fromIntegral msecs)
return (toEnum (fromIntegral r))
handle_is_console :: RawHandle a => a -> IO Bool
handle_is_console = c_is_console . toHANDLE
handle_close :: RawHandle a => a -> IO ()
handle_close h = do release h
failIfFalse_ "handle_close" $ c_close_handle (toHANDLE h)
handle_dev_type :: RawHandle a => a -> IO IODeviceType
handle_dev_type hwnd = do _type <- c_handle_type $ toHANDLE hwnd
return $ case _type of
_ | _type == 3 -> Stream
| _type == 5 -> RawDevice
| otherwise -> RegularFile
handle_is_seekable :: RawHandle a => a -> IO Bool
handle_is_seekable hwnd = do
t <- handle_dev_type hwnd
return (t == RegularFile || t == RawDevice)
handle_seek :: RawHandle a => a -> SeekMode -> Integer -> IO Integer
handle_seek hwnd mode off =
with 0 $ \off_rel -> do
failIfFalse_ "GHC.IO.Handle.handle_seek" $
c_set_file_pointer (toHANDLE hwnd) (fromIntegral off) seektype off_rel
fromIntegral <$> peek off_rel
where
seektype :: DWORD
seektype = case mode of
AbsoluteSeek -> 0
{-# LINE 752 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
RelativeSeek -> 1
{-# LINE 753 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
SeekFromEnd -> 2
{-# LINE 754 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
handle_tell :: RawHandle a => a -> IO Integer
handle_tell hwnd =
fromIntegral `fmap`
(throwErrnoIfMinus1Retry "GHC.IO.Handle.handle_tell" $
c_get_file_pointer (toHANDLE hwnd))
handle_set_size :: RawHandle a => a -> Integer -> IO ()
handle_set_size hwnd size =
failIfFalse_ "GHC.IO.Handle.handle_set_size" $
c_set_file_size (toHANDLE hwnd) (fromIntegral size)
handle_get_size :: RawHandle a => a -> IO Integer
handle_get_size hwnd =
fromIntegral `fmap`
(throwErrnoIfMinus1Retry "GHC.IO.Handle.handle_set_size" $
c_get_file_size (toHANDLE hwnd))
handle_set_echo :: RawHandle a => a -> Bool -> IO ()
handle_set_echo hwnd value =
failIfFalse_ "GHC.IO.Handle.handle_set_echo" $
c_set_console_echo (toHANDLE hwnd) value
handle_get_echo :: RawHandle a => a -> IO Bool
handle_get_echo = c_get_console_echo . toHANDLE
handle_duplicate :: RawHandle a => a -> IO a
handle_duplicate hwnd = alloca $ \ptr -> do
failIfFalse_ "GHC.IO.Handle.handle_duplicate" $
c_duplicate_handle (toHANDLE hwnd) ptr
fromHANDLE <$> peek ptr
console_set_buffering :: Io ConsoleHandle -> Bool -> IO ()
console_set_buffering hwnd value = setCooked hwnd value >> return ()
handle_set_buffering :: RawHandle a => a -> Bool -> IO ()
handle_set_buffering hwnd value =
failIfFalse_ "GHC.IO.Handle.handle_set_buffering" $
c_set_console_buffering (toHANDLE hwnd) value
handle_console_seek :: RawHandle a => a -> SeekMode -> Integer -> IO Integer
handle_console_seek hwnd mode off =
with 0 $ \loc_ptr -> do
failIfFalse_ "GHC.IO.Handle.handle_console_seek" $
c_set_console_pointer (toHANDLE hwnd) (fromIntegral off) seektype loc_ptr
fromIntegral <$> peek loc_ptr
where
seektype :: DWORD
seektype = case mode of
AbsoluteSeek -> 0
{-# LINE 804 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
RelativeSeek -> 1
{-# LINE 805 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
SeekFromEnd -> 2
{-# LINE 806 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
handle_console_tell :: RawHandle a => a -> IO Integer
handle_console_tell hwnd =
fromIntegral `fmap`
(throwErrnoIfMinus1Retry "GHC.IO.Handle.handle_console_tell" $
c_get_console_pointer (toHANDLE hwnd))
handle_set_console_size :: RawHandle a => a -> Integer -> IO ()
handle_set_console_size hwnd size =
failIfFalse_ "GHC.IO.Handle.handle_set_console_size" $
c_set_console_buffer_size (toHANDLE hwnd) (fromIntegral size)
handle_get_console_size :: RawHandle a => a -> IO Integer
handle_get_console_size hwnd =
fromIntegral `fmap`
(throwErrnoIfMinus1Retry "GHC.IO.Handle.handle_get_console_size" $
c_get_console_buffer_size (toHANDLE hwnd))
data TempFileOptions = NoTemp | TempNonExcl | TempExcl deriving Eq
openFile
:: FilePath
-> IOMode
-> Bool
-> IO (Io NativeHandle, IODeviceType)
openFile filepath iomode non_blocking = openFile' filepath iomode non_blocking NoTemp
openFileAsTemp
:: FilePath
-> Bool
-> Bool
-> IO (Io NativeHandle, IODeviceType)
openFileAsTemp filepath non_blocking excl
= openFile' filepath ReadWriteMode non_blocking (if excl then TempExcl else TempNonExcl)
openFile'
:: FilePath
-> IOMode
-> Bool
-> TempFileOptions
-> IO (Io NativeHandle, IODeviceType)
openFile' filepath iomode non_blocking tmp_opts =
do devicepath <- getDevicePath filepath
h <- createFile devicepath
Mgr.associateHandle' h
let hwnd = fromHANDLE h
_type <- devType hwnd
let write_lock = iomode /= ReadMode
case _type of
RegularFile -> do
optimizeFileAccess h
(unique_dev, unique_ino) <- getUniqueFileInfo hwnd
r <- lockFile (fromIntegral $ ptrToWordPtr h) unique_dev unique_ino
(fromBool write_lock)
when (r == -1) $
ioException (IOError Nothing ResourceBusy "openFile"
"file is locked" Nothing Nothing)
_ -> return ()
when (iomode == WriteMode && _type == RegularFile) $
setSize hwnd 0
return (hwnd, _type)
where
flagIf p f2
| p = f2
| otherwise = 0
file_share_mode = 1
{-# LINE 900 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
.|. 4
{-# LINE 901 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
.|. (flagIf (tmp_opts == NoTemp)
2)
{-# LINE 904 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
file_access_mode =
case iomode of
ReadMode -> 2147483648
{-# LINE 908 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
WriteMode -> 1073741824
{-# LINE 909 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
ReadWriteMode -> 2147483648
{-# LINE 910 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
.|. 1073741824
{-# LINE 911 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
AppendMode -> 1073741824
{-# LINE 912 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
.|. 4
{-# LINE 913 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
file_open_mode =
case iomode of
ReadMode -> 3
{-# LINE 917 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
WriteMode -> 4
{-# LINE 918 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
ReadWriteMode ->
case tmp_opts of
NoTemp -> 4
{-# LINE 921 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
TempNonExcl -> 2
{-# LINE 922 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
TempExcl -> 1
{-# LINE 923 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
AppendMode -> 4
{-# LINE 924 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
file_create_flags =
if non_blocking
then 1073741824
{-# LINE 937 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
.|. 134217728
{-# LINE 946 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
.|. (flagIf (tmp_opts /= NoTemp)
256 )
{-# LINE 949 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
else 128
{-# LINE 950 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
.|. (flagIf (tmp_opts /= NoTemp)
256 )
{-# LINE 953 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
createFile devicepath =
withCWString devicepath $ \fp ->
failIf (== iNVALID_HANDLE_VALUE) "CreateFile" $
c_CreateFile fp file_access_mode
file_share_mode
nullPtr
file_open_mode
file_create_flags
nullPtr
optimizeFileAccess :: HANDLE -> IO ()
optimizeFileAccess handle =
failIfFalse_ "SetFileCompletionNotificationModes" $
c_SetFileCompletionNotificationModes handle
( 1
{-# LINE 971 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
.|. 2)
{-# LINE 972 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
handleToMode :: HANDLE -> IO IOMode
handleToMode hwnd = do
mask <- c_get_handle_access_mask hwnd
let hasFlag flag = (flag .&. mask) == flag
case () of
() | hasFlag (4) -> return AppendMode
{-# LINE 980 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| hasFlag (1073741824 .|. 2147483648) -> return ReadWriteMode
{-# LINE 981 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| hasFlag (2147483648) -> return ReadMode
{-# LINE 982 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| hasFlag (1073741824) -> return WriteMode
{-# LINE 983 "libraries\\base\\GHC\\IO\\Windows\\Handle.hsc" #-}
| otherwise -> error "unknown access mask in handleToMode."
foreign import ccall unsafe "__get_handle_access_mask"
c_get_handle_access_mask :: HANDLE -> IO DWORD
release :: RawHandle a => a -> IO ()
release h = if isLockable h
then do let handle = fromIntegral $ ptrToWordPtr $ toHANDLE h
_ <- unlockFile handle
return ()
else return ()
foreign import ccall unsafe "lockFile"
lockFile :: CUIntPtr -> Word64 -> Word64 -> CInt -> IO CInt
foreign import ccall unsafe "unlockFile"
unlockFile :: CUIntPtr -> IO CInt
foreign import ccall unsafe "get_unique_file_info_hwnd"
c_getUniqueFileInfo :: HANDLE -> Ptr Word64 -> Ptr Word64 -> IO ()
getUniqueFileInfo :: RawHandle a => a -> IO (Word64, Word64)
getUniqueFileInfo handle = do
with 0 $ \devptr -> do
with 0 $ \inoptr -> do
c_getUniqueFileInfo (toHANDLE handle) devptr inoptr
liftM2 (,) (peek devptr) (peek inoptr)