yavie-0.0.1: src/x11/yavie-x11.hs
{-# LANGUAGE FlexibleContexts #-}
module Main ( main ) where
import Yavie
import Yavie.Editor
import Yavie.Keybind
import Yavie.Keybind.Vi
import Graphics.X11
import Graphics.X11.Xft
import Graphics.X11.Xlib.Extras
import Graphics.X11.Xim
import Data.Bits
import Control.Monad ( zipWithM_, when, unless )
import Control.Monad.Tools
import Data.IORef -- just hack
import System.IO.Unsafe
import System.Exit
import System.Locale.SetLocale
import Data.Time
import Data.Maybe
import Data.Convertible
import Foreign.C.Types
myConfig :: YavieConfig IO XVars ()
myConfig = YavieConfig {
isEventDriven = False ,
withInitEditor = defaultWithInitEditor ,
initialize = initializeX11 ,
finalize = finalizeX11 ,
displaySize = const $ return ( 60, 24 ) ,
drawDisplay = drawDisplayX11 ,
supplyEvent = \xvars act -> doWhile_ $ getEventX11 xvars >>= act ,
keybind = defaultKeybind defaultCmdbind ,
romode = defaultRomode ,
runAction = runIOAction ,
getReadOnlyFlag = defaultGetReadOnlyFlag
}
main :: IO ()
main = runYavie myConfig
font, color :: String
font = "Kochi Gothic-12:style=Regular"
color = "black"
type XVars =
( Display, Window, Visual, Colormap, XftDraw, XftFont, XIC, Atom, IORef String )
initializeX11 :: IO XVars
initializeX11 = do
inbuf <- newIORef ""
ret <- setLocale LC_CTYPE Nothing
case ret of
Nothing -> putStrLn "Can't set locale." >> exitFailure
Just r -> print r
sl <- supportsLocale
unless sl $ putStrLn "Current locale is notSupported." >> exitFailure
_ <- setLocaleModifiers ""
dpy <- openDisplay ""
delWin <- internAtom dpy "WM_DELETE_WINDOW" True
let scr = defaultScreen dpy
black = blackPixel dpy scr
white = whitePixel dpy scr
scrN = defaultScreenOfDisplay dpy
visual = defaultVisual dpy scr
colormap = defaultColormap dpy scr
rootWin <- rootWindow dpy scr
win <- createSimpleWindow dpy rootWin 0 0 100 100 1 black white
setWMProtocols dpy win [ delWin ]
im <- openIM dpy Nothing Nothing Nothing
ic <- createIC im [ XIMPreeditNothing, XIMStatusNothing ] win
fevent <- getICValue ic "filterEvents"
xftDraw <- xftDrawCreate dpy win visual colormap
xftFont <- xftFontOpen dpy scrN font
mapWindow dpy win
selectInput dpy win $ keyPressMask .|. exposureMask .|. fevent
let xvars = ( dpy, win, visual, colormap, xftDraw, xftFont, ic, delWin, inbuf )
return xvars
finalizeX11 :: XVars -> IO ()
finalizeX11 ( dpy, _, _, _, _, xftFont, _, _, _ ) = do
xftFontClose dpy xftFont
closeDisplay dpy
nextNotFilteredEvent :: Display -> XEventPtr -> IO ()
nextNotFilteredEvent dpy e = do
nextEvent dpy e
filtOut <- filterEvent e 0
when filtOut $ nextNotFilteredEvent dpy e
evpToEvent :: ( Convertible CInt a, Eq a ) =>
IORef String -> XIC -> a -> XEventPtr -> IO Yavie.Keybind.Event
evpToEvent inbuf ic delWin ep = do
ev <- getEvent ep
case ev of
ExposeEvent {} -> do
getCurrentTime >>= writeIORef preTime . addUTCTime (-4)
return EvExpose
KeyEvent {} -> do
( mstr, mks ) <- utf8LookupString ic ep
let ch = maybe ' ' head mstr
ks = fromMaybe xK_VoidSymbol mks
key = case ks of
_ | ks == xK_Return -> KEnter
| ks == xK_Escape -> KEsc
| ks == xK_Shift_L -> KUp
| ks == xK_Shift_R -> KUp
| otherwise -> KASCII ch
writeIORef inbuf $ maybe "" tail mstr
return $ EvKey key [ ]
ClientMessageEvent {} ->
if getClientMessageAtom ev == delWin
then exitFailure
else return $ EvKey KEsc [ ]
_ -> error "not yet implemented"
getEventX11 :: XVars -> IO Yavie.Keybind.Event
getEventX11 ( dpy, _, _, _, _, _, ic, delWin, inbuf ) =
getKeyEvent inbuf dpy ic delWin
getKeyEvent :: ( Convertible CInt a, Eq a ) =>
IORef String -> Display -> XIC -> a -> IO Yavie.Keybind.Event
getKeyEvent inbuf dpy ic delWin = allocaXEvent $ \e -> do
ib <- readIORef inbuf
case ib of
c : _ -> do
modifyIORef inbuf tail
return $ EvKey ( KASCII c ) [ ]
_ -> do
nextNotFilteredEvent dpy e
evpToEvent inbuf ic delWin e
getClientMessageAtom :: Convertible CInt a => Graphics.X11.Xlib.Extras.Event -> a
getClientMessageAtom = convert . head . ev_data
preMon :: IORef [ a ]
preMon = unsafePerformIO $ newIORef [ ]
preTime :: IORef UTCTime
preTime = unsafePerformIO $ getCurrentTime >>= newIORef . addUTCTime (-1)
drawDisplayX11 :: XVars -> IO ( Editor c ) -> IO ()
drawDisplayX11 xvars@( dpy, win, _, _, _, _, _, _, _ ) datForDpy = do
dl <- datForDpy
let im = isBoxCursor dl
( cx, cy ) = cursorPosOfDpy dl
lns = displayLines dl
ln = lns !! cy
bc = if im then "[" else "|"
ac = if im then "]" else ""
ch = if cx < 0 || cx >= length ln then ' ' else ln !! cx
nlns = take cy lns ++
[ take cx ln ++ bc ++ [ ch ] ++ ac ++ drop ( cx + 1 ) ln ] ++
drop ( cy + 1 ) lns
pmon <- readIORef preMon
tnow <- getCurrentTime
tpre <- readIORef preTime
let dt = diffUTCTime tnow tpre
when ( pmon /= nlns && dt > 0.05 ) $ do
writeIORef preMon nlns
writeIORef preTime tnow
clearWindow dpy win
zipWithM_ ( putStrX xvars color ( 0 :: Int ) ) [ ( 0 :: Int) .. ] nlns
putStrX :: ( Integral a, Integral b ) =>
XVars -> String -> a -> b -> String -> IO ()
putStrX (dpy,_,visual,colormap,xftDraw,xftFont,_,_,_) col x y str =
withXftColorName dpy visual colormap col $ \clr ->
xftDrawString xftDraw clr xftFont ( 12 + 13 * x `div` 2 ) ( 12 + 15 * y ) str