module Yi.UI.Pango (start, processEvent) where
import Prelude (filter, map, round, FilePath, (/))
import Control.Concurrent (yield)
import Control.Monad (ap)
import Control.Monad.Reader (liftIO, when, MonadIO)
import Data.Prototype
import Data.IORef
import Data.List (drop, intercalate, nub, zip)
import qualified Data.List.PointedList.Circular as PL
import Data.Maybe
import qualified Data.Map as M
import qualified Data.Rope as Rope
import Graphics.UI.Gtk hiding (on, Region, Window, Action, Point, Style)
import Graphics.UI.Gtk.Gdk.GC hiding (foreground)
import qualified Graphics.UI.Gtk.Gdk.Events as Gdk.Events
import qualified Graphics.UI.Gtk as Gtk
import qualified Graphics.UI.Gtk.Gdk.GC as Gtk
import System.Glib.GError
import Yi.Prelude
import Yi.Buffer
import Yi.Config
import qualified Yi.Editor as Editor
import Yi.Editor hiding (windows)
import Yi.Event
import Yi.Keymap
import Yi.Monad
import Yi.Style
import Yi.Window
import qualified Yi.UI.Common as Common
import Yi.UI.Pango.Utils
import Yi.UI.TabBar
import Yi.UI.Utils
#ifdef GNOME_ENABLED
import Yi.UI.Pango.Gnome(watchSystemFont)
#endif
data UI = UI
{ uiWindow :: Gtk.Window
, uiNotebook :: Notebook
, uiStatusbar :: Statusbar
, tabCache :: IORef [TabInfo]
, uiActionCh :: Action -> IO ()
, uiConfig :: UIConfig
, uiFont :: IORef FontDescription
}
data TabInfo = TabInfo
{ coreTab :: PL.PointedList Window
, page :: VBox
, windowCache :: [WinInfo]
}
instance Show TabInfo where
show t = show (coreTab t)
data WinInfo = WinInfo
{ coreWin :: Window
, shownTos :: IORef Point
, renderer :: IORef (ConnectId DrawingArea)
, winMotionSignal :: IORef (Maybe (ConnectId DrawingArea))
, winLayout :: PangoLayout
, winMetrics :: FontMetrics
, textview :: DrawingArea
, modeline :: Label
, widget :: Box
}
instance Show WinInfo where
show w = show (coreWin w)
mkUI :: UI -> Common.UI
mkUI ui = Common.dummyUI
{ Common.main = main ui
, Common.end = const end
, Common.suspend = windowIconify (uiWindow ui)
, Common.refresh = refresh ui
, Common.layout = doLayout ui
, Common.reloadProject = reloadProject ui
}
updateFont :: UIConfig -> IORef FontDescription -> IORef [TabInfo] -> Statusbar
-> FontDescription -> IO ()
updateFont cfg fontRef tc status font = do
maybe (return ()) (fontDescriptionSetFamily font) (configFontName cfg)
maybe (return ()) (fontDescriptionSetSize font . fromIntegral) (configFontSize cfg)
writeIORef fontRef font
widgetModifyFont status (Just font)
tcs <- readIORef tc
forM_ tcs $ \tabinfo -> do
let wcs = windowCache tabinfo
forM_ wcs $ \wininfo -> do
layoutSetFontDescription (winLayout wininfo) (Just font)
widgetModifyFont (textview wininfo) (Just font)
widgetModifyFont (modeline wininfo) (Just font)
askBuffer :: Window -> FBuffer -> BufferM a -> a
askBuffer w b f = fst $ runBuffer w b f
start :: UIBoot
start cfg ch outCh ed = catchGError (startNoMsg cfg ch outCh ed) (\(GError _dom _code msg) -> fail msg)
startNoMsg :: UIBoot
startNoMsg cfg ch outCh _ed = do
logPutStrLn "startNoMsg"
unsafeInitGUIForThreadedRTS
win <- windowNew
windowSetDefaultSize win 900 700
windowSetTitle win "Yi"
ico <- loadIcon "yi+lambda-fat.32.png"
windowSetIcon win (Just ico)
onKeyPress win (processEvent ch)
paned <- hPanedNew
vb <- vBoxNew False 1
tabs <- notebookNew
panedAdd2 paned tabs
set win [ containerChild := vb ]
onDestroy win mainQuit
status <- statusbarNew
statusbarGetContextId status "global"
set vb [ containerChild := paned,
containerChild := status,
boxChildPacking status := PackNatural ]
fontRef <- newIORef undefined
tc <- newIORef []
#ifdef GNOME_ENABLED
let watchFont = watchSystemFont
#else
let watchFont = (fontDescriptionFromString "Monospace 10" >>=)
#endif
watchFont $ updateFont (configUI cfg) fontRef tc status
timeoutAddFull (yield >> return True) priorityDefaultIdle 50
widgetShowAll win
let ui = UI win tabs status tc (outCh . singleton) (configUI cfg) fontRef
let move n pl = maybe pl id (PL.move n pl)
runAction = uiActionCh ui . makeAction
onSwitchPage (uiNotebook ui) $ \n -> postGUIAsync $
runAction (modA tabsA (move n) :: EditorM ())
return (mkUI ui)
main :: UI -> IO ()
main _ui =
do logPutStrLn "GTK main loop running"
mainGUI
processEvent :: (Event -> IO ()) -> Gdk.Events.Event -> IO Bool
processEvent ch ev = do
case gtkToYiEvent ev of
Nothing -> logPutStrLn $ "Event not translatable: " ++ show ev
Just e -> ch e
return True
gtkToYiEvent :: Gdk.Events.Event -> Maybe Event
gtkToYiEvent (Gdk.Events.Key {Gdk.Events.eventKeyName = key, Gdk.Events.eventModifier = evModifier, Gdk.Events.eventKeyChar = char})
= fmap (\k -> Event k $ (nub $ (if isShift then filter (/= MShift) else id) $ concatMap modif evModifier)) key'
where (key',isShift) =
case char of
Just c -> (Just $ KASCII c, True)
Nothing -> (M.lookup key keyTable, False)
modif Gdk.Events.Control = [MCtrl]
modif Gdk.Events.Alt = [MMeta]
modif Gdk.Events.Shift = [MShift]
modif _ = []
gtkToYiEvent _ = Nothing
keyTable :: M.Map String Key
keyTable = M.fromList
[("Down", KDown)
,("Up", KUp)
,("Left", KLeft)
,("Right", KRight)
,("Home", KHome)
,("End", KEnd)
,("BackSpace", KBS)
,("Delete", KDel)
,("Page_Up", KPageUp)
,("Page_Down", KPageDown)
,("Insert", KIns)
,("Escape", KEsc)
,("Return", KEnter)
,("Tab", KTab)
,("ISO_Left_Tab", KTab)
]
end :: IO ()
end = mainQuit
syncTabs :: Editor -> UI -> [(PL.PointedList Window, Bool)] -> [TabInfo] -> IO [TabInfo]
syncTabs e ui (tfocused@(t,focused):ts) (c:cs)
| t == coreTab c =
do when focused $ setTabFocus ui c
let wCache = windowCache c
(:) <$> syncTab e ui c t wCache <*> syncTabs e ui ts cs
| t `elem` map coreTab cs =
do removeTab ui c
syncTabs e ui (tfocused:ts) cs
| otherwise =
do c' <- insertTabBefore e ui t c
when focused $ setTabFocus ui c'
return (c':) `ap` syncTabs e ui ts (c:cs)
syncTabs e ui ts [] = mapM (\(t,focused) -> do c' <- insertTab e ui t
when focused $ setTabFocus ui c'
return c')
ts
syncTabs _ ui [] cs = mapM_ (removeTab ui) cs >> return []
syncTab :: Editor -> UI -> TabInfo -> PL.PointedList Window -> [WinInfo] -> IO TabInfo
syncTab e ui tab ws cache = do
wCache <- syncWindows e ui tab (toList $ PL.withFocus ws) cache
return tab { windowCache = wCache }
syncWindows :: Editor -> UI -> TabInfo -> [(Window, Bool)]
-> [WinInfo] -> IO [WinInfo]
syncWindows e ui tab (wfocused@(w,focused):ws) (c:cs)
| w == coreWin c =
do when focused $ setWindowFocus e ui tab c
(c { coreWin = w}:) <$> syncWindows e ui tab ws cs
| w `elem` map coreWin cs =
removeWindow ui tab c >> syncWindows e ui tab (wfocused:ws) cs
| otherwise = do
c' <- insertWindowBefore e ui tab w c
when focused (setWindowFocus e ui tab c')
return (c':) `ap` syncWindows e ui tab ws (c:cs)
syncWindows e ui tab ws [] = mapM (\(w,focused) -> do c' <- insertWindowAtEnd e ui tab w
when focused (setWindowFocus e ui tab c')
return c')
ws
syncWindows _ ui tab [] cs = mapM_ (removeWindow ui tab) cs >> return []
setTabFocus :: UI -> TabInfo -> IO ()
setTabFocus ui t = do
p <- notebookPageNum (uiNotebook ui) (page t)
case p of
Just n -> update (uiNotebook ui) notebookPage n
Nothing -> return ()
update :: forall o a. (Eq a) => o -> ReadWriteAttr o a a -> a -> IO ()
update w attr val = do oldVal <- get w attr
when (val /= oldVal) $ set w [attr := val]
setWindowFocus :: Editor -> UI -> TabInfo -> WinInfo -> IO ()
setWindowFocus e ui t w = do
let bufferName = shortIdentString (commonNamePrefix e) $ findBufferWith (bufkey $ coreWin w) e
ml = askBuffer (coreWin w) (findBufferWith (bufkey $ coreWin w) e) $ getModeLine (commonNamePrefix e)
update (textview w) widgetIsFocus True
update (modeline w) labelText ml
update (uiWindow ui) windowTitle $ bufferName ++ " - Yi"
update (uiNotebook ui) (notebookChildTabLabel (page t)) (tabAbbrevTitle bufferName)
removeTab :: UI -> TabInfo -> IO ()
removeTab ui t = do
p <- notebookPageNum (uiNotebook ui) (page t)
case p of
Just n -> notebookRemovePage (uiNotebook ui) n
Nothing -> return ()
removeWindow :: UI -> TabInfo -> WinInfo -> IO ()
removeWindow _ tab win = do
containerRemove (page tab) (widget win)
getWinInfo :: WindowRef -> [TabInfo] -> (Int, Int, WinInfo)
getWinInfo ref tabInfos =
head [ (tabIx, winIx, winInfo)
| (tabIx, tabInfo) <- zip [0..] tabInfos
, (winIx, winInfo) <- zip [0..] (windowCache tabInfo)
, ref == (wkey . coreWin) winInfo
]
handleClick :: UI -> WindowRef -> Gdk.Events.Event -> IO Bool
handleClick ui ref event = do
(_tabIdx,winIdx,w) <- getWinInfo ref <$> readIORef (tabCache ui)
logPutStrLn $ "Click: " ++ show (Gdk.Events.eventX event,
Gdk.Events.eventY event,
Gdk.Events.eventClick event)
(_,layoutIndex,_) <- io $ layoutXYToIndex (winLayout w) (Gdk.Events.eventX event) (Gdk.Events.eventY event)
tos <- readRef (shownTos w)
let p1 = tos + fromIntegral layoutIndex
logPutStrLn $ "Clicked inside window: " ++ show w
let focusWindow = do
modA windowsA (fromJust . PL.move winIdx)
case (Gdk.Events.eventClick event, Gdk.Events.eventButton event) of
(Gdk.Events.SingleClick, Gdk.Events.LeftButton) -> do
writeRef (winMotionSignal w) =<< Just <$> onMotionNotify (textview w) False (handleMove ui w p1)
_ -> do maybe (return ()) signalDisconnect =<< readRef (winMotionSignal w)
writeRef (winMotionSignal w) Nothing
let runAction = uiActionCh ui . makeAction
case (Gdk.Events.eventClick event, Gdk.Events.eventButton event) of
(Gdk.Events.SingleClick, Gdk.Events.LeftButton) -> runAction $ do
b <- gets $ (bkey . findBufferWith (bufkey $ coreWin w))
focusWindow
withGivenBufferAndWindow0 (coreWin w) b $ do
moveTo p1
setVisibleSelection False
(Gdk.Events.SingleClick, _) -> runAction focusWindow
(Gdk.Events.ReleaseClick, Gdk.Events.MiddleButton) -> do
disp <- widgetGetDisplay (textview w)
cb <- clipboardGetForDisplay disp selectionPrimary
let cbHandler Nothing = return ()
cbHandler (Just txt) = runAction $ do
b <- gets $ (bkey . findBufferWith (bufkey $ coreWin w))
withGivenBufferAndWindow0 (coreWin w) b $ do
pointB >>= setSelectionMarkPointB
moveTo p1
insertN txt
clipboardRequestText cb cbHandler
_ -> return ()
return True
handleScroll :: UI -> WindowRef -> Gdk.Events.Event -> IO Bool
handleScroll ui _ event = do
let editorAction = do
withBuffer0 $ vimScrollB $ case Gdk.Events.eventDirection event of
Gdk.Events.ScrollUp -> (1)
Gdk.Events.ScrollDown -> 1
_ -> 0
uiActionCh ui (makeAction editorAction)
return True
handleMove :: UI -> WinInfo -> Point -> Gdk.Events.Event -> IO Bool
handleMove ui w p0 event = do
logPutStrLn $ "Motion: " ++ show (Gdk.Events.eventX event, Gdk.Events.eventY event)
(_,layoutIndex,_) <- layoutXYToIndex (winLayout w) (Gdk.Events.eventX event) (Gdk.Events.eventY event)
tos <- readRef (shownTos w)
let p1 = tos + fromIntegral layoutIndex
let editorAction = do
txt <- withBuffer0 $ do
if p0 /= p1
then Just <$> do
m <- selMark <$> askMarks
setMarkPointB m p0
moveTo p1
setVisibleSelection True
readRegionB =<< getSelectRegionB
else return Nothing
maybe (return ()) setRegE txt
uiActionCh ui (makeAction editorAction)
selection <- newIORef ""
let yiAction = do
txt <- withEditor (withBuffer0 (readRegionB =<< getSelectRegionB))
:: YiM String
liftIO $ writeIORef selection txt
uiActionCh ui (makeAction yiAction)
txt <- readIORef selection
disp <- widgetGetDisplay (textview w)
cb <- clipboardGetForDisplay disp selectionPrimary
clipboardSetWithData cb [(targetString,0)]
(\0 -> selectionDataSetText txt >> return ()) (return ())
return True
handleConfigure :: UI -> WindowRef -> Gdk.Events.Event -> IO Bool
handleConfigure ui _ref _ev = do
postGUIAsync $ uiActionCh ui (makeAction (return () :: EditorM ()))
return False
newTab :: Editor -> UI -> VBox -> PL.PointedList Window -> IO TabInfo
newTab e ui vb ws = do
let t' = TabInfo { coreTab = ws
, page = vb
, windowCache = []
}
cache <- syncWindows e ui t' (toList $ PL.withFocus ws) []
return t' { windowCache = cache }
newWindow :: Editor -> UI -> Window -> FBuffer -> IO WinInfo
newWindow e ui w b = mdo
f <- readIORef (uiFont ui)
ml <- labelNew Nothing
widgetModifyFont ml (Just f)
set ml [ miscXalign := 0.01 ]
v <- drawingAreaNew
widgetModifyFont v (Just f)
widgetAddEvents v [Button1MotionMask]
widgetModifyBg v StateNormal $ mkCol False $ Yi.Style.background $ baseAttributes $ configStyle $ uiConfig ui
sw <- scrolledWindowNew Nothing Nothing
scrolledWindowAddWithViewport sw v
scrolledWindowSetPolicy sw PolicyAutomatic PolicyNever
box <- if isMini w
then do
prompt <- labelNew (Just $ miniIdentString b)
widgetModifyFont prompt (Just f)
hb <- hBoxNew False 1
set hb [ containerChild := prompt,
containerChild := sw,
boxChildPacking prompt := PackNatural,
boxChildPacking sw := PackGrow]
return (castToBox hb)
else do
vb <- vBoxNew False 1
set vb [ containerChild := sw,
containerChild := ml,
boxChildPacking ml := PackNatural]
return (castToBox vb)
sig <- newIORef =<< (v `onExpose` render e ui b (wkey w))
tosRef <- newIORef (askBuffer w b (getMarkPointB =<< fromMark <$> askMarks))
context <- widgetCreatePangoContext v
layout <- layoutEmpty context
language <- contextGetLanguage context
metrics <- contextGetMetrics context f language
motionSig <- newIORef Nothing
layoutSetFontDescription layout (Just f)
layoutSetText layout ""
let win = WinInfo { coreWin = w
, winLayout = layout
, winMetrics = metrics
, winMotionSignal = motionSig
, textview = v
, modeline = ml
, widget = box
, renderer = sig
, shownTos = tosRef
}
return win
insertTabBefore :: Editor -> UI -> PL.PointedList Window -> TabInfo -> IO TabInfo
insertTabBefore e ui ws c = do
Just p <- notebookPageNum (uiNotebook ui) (page c)
vb <- vBoxNew False 1
notebookInsertPage (uiNotebook ui) vb "" p
widgetShowAll $ vb
t <- newTab e ui vb ws
return t
insertTab :: Editor -> UI -> PL.PointedList Window -> IO TabInfo
insertTab e ui ws = do
vb <- vBoxNew False 1
notebookAppendPage (uiNotebook ui) vb ""
widgetShowAll $ vb
t <- newTab e ui vb ws
return t
insertWindowBefore :: Editor -> UI -> TabInfo -> Window -> WinInfo -> IO WinInfo
insertWindowBefore e ui tab w _c = insertWindow e ui tab w
insertWindowAtEnd :: Editor -> UI -> TabInfo -> Window -> IO WinInfo
insertWindowAtEnd e ui tab w = insertWindow e ui tab w
insertWindow :: Editor -> UI -> TabInfo -> Window -> IO WinInfo
insertWindow e ui tab win = do
let buf = findBufferWith (bufkey win) e
liftIO $ do w <- newWindow e ui win buf
set (page tab) $
[ containerChild := widget w
, boxChildPacking (widget w) :=
if isMini (coreWin w)
then PackNatural
else PackGrow
]
let ref = (wkey . coreWin) w
textview w `onButtonRelease` handleClick ui ref
textview w `onButtonPress` handleClick ui ref
textview w `onScroll` handleScroll ui ref
textview w `onConfigure` handleConfigure ui ref
widgetShowAll (widget w)
return w
updateCache :: UI -> Editor -> IO ()
updateCache ui e = do
let tabs = e ^. tabsA
cache <- readRef $ tabCache ui
cache' <- syncTabs e ui (toList $ PL.withFocus tabs) cache
writeRef (tabCache ui) cache'
refresh :: UI -> Editor -> IO ()
refresh ui e = do
contextId <- statusbarGetContextId (uiStatusbar ui) "global"
statusbarPop (uiStatusbar ui) contextId
statusbarPush (uiStatusbar ui) contextId $ intercalate " " $ statusLine e
updateCache ui e
cache <- readRef $ tabCache ui
forM_ cache $ \t -> do
forM_ (windowCache t) $ \w -> do
let b = findBufferWith (bufkey (coreWin w)) e
do
sig <- readIORef (renderer w)
signalDisconnect sig
writeRef (renderer w) =<< (textview w `onExpose` render e ui b (wkey (coreWin w)))
widgetQueueDraw (textview w)
render :: Editor -> UI -> FBuffer -> WindowRef -> t -> IO Bool
render e ui b ref _ev = do
(_,_,w) <- getWinInfo ref <$> readIORef (tabCache ui)
let win = coreWin w
let tos = max 0 (regionStart (winRegion win))
let bos = regionEnd (winRegion win)
let (cur, _) = runBuffer win b pointB
writeRef (shownTos w) tos
drawWindow <- widgetGetDrawWindow $ textview w
let picture = askBuffer (coreWin w) b $ attributesPictureAndSelB sty (currentRegex e) (mkRegion tos bos)
sty = extractValue $ configTheme (uiConfig ui)
strokes = [(start',s,end') | ((start', s), end') <- zip picture (drop 1 (map fst picture) ++ [bos]),
s /= emptyAttributes]
rel p = fromIntegral (p tos)
allAttrs = concat $ do
(p1, Attributes fg bg _rv bd itlc udrl, p2) <- strokes
return $ [ AttrForeground (rel p1) (rel p2) (mkCol True fg)
, AttrBackground (rel p1) (rel p2) (mkCol False bg)
, AttrStyle (rel p1) (rel p2) (if itlc then StyleItalic else StyleNormal)
, AttrUnderline (rel p1) (rel p2) (if udrl then UnderlineSingle else UnderlineNone)
, AttrWeight (rel p1) (rel p2) (if bd then WeightBold else WeightNormal)
]
layout = winLayout w
layoutSetAttributes layout allAttrs
(PangoRectangle curx cury curw curh, _) <- layoutGetCursorPos layout (rel cur)
PangoRectangle chx chy chw chh <- layoutIndexToPos layout (rel cur)
gc <- gcNew drawWindow
drawLayout drawWindow gc 0 0 layout
gcSetValues gc (newGCValues { Gtk.foreground = mkCol True $ Yi.Style.foreground $ baseAttributes $ configStyle $ uiConfig ui })
if askBuffer (coreWin w) b $ getA insertingA
then do drawLine drawWindow gc (round curx, round cury) (round $ curx + curw, round $ cury + curh)
else do drawRectangle drawWindow gc False (round chx) (round chy) (if chw > 0 then round chw else 8) (round chh)
return True
doLayout :: UI -> Editor -> IO Editor
doLayout ui e = do
updateCache ui e
tabs <- readRef $ tabCache ui
f <- readRef (uiFont ui)
heights <- concat <$> mapM (getHeightsInTab ui f e) tabs
let e' = (tabsA ^: fmap (fmap updateWin)) e
updateWin w = case find (\(ref,_,_) -> (wkey w == ref)) heights of
Nothing -> w
Just (_,h,rgn) -> w { height = h, winRegion = rgn }
let forceWin x w = height w `seq` winRegion w `seq` x
return $ (foldl . foldl) forceWin e' (e' ^. tabsA)
getHeightsInTab :: UI -> FontDescription -> Editor -> TabInfo -> IO [(WindowRef,Int,Region)]
getHeightsInTab ui f e tab = do
forM (windowCache tab) $ \wi -> do
(_, h) <- widgetGetSize $ textview wi
let metrics = winMetrics wi
lineHeight = ascent metrics + descent metrics
let b0 = findBufferWith (bufkey (coreWin wi)) e
rgn <- shownRegion ui f wi b0
let ret= (wkey (coreWin wi), round $ fromIntegral h / lineHeight, rgn)
return ret
shownRegion :: UI -> FontDescription -> WinInfo -> FBuffer -> IO Region
shownRegion ui f w b = do
(tos, _, bos) <- updatePango ui f w b layout
return $ mkRegion tos bos
where layout = winLayout w
updatePango :: UI -> FontDescription -> WinInfo -> FBuffer -> PangoLayout -> IO (Point, Point, Point)
updatePango ui font w b layout = do
(width', height') <- widgetGetSize $ textview w
oldFont <- layoutGetFontDescription layout
oldFontStr <- maybe (return Nothing) (fmap Just . fontDescriptionToString) oldFont
newFontStr <- Just <$> fontDescriptionToString font
when (oldFontStr /= newFontStr) (layoutSetFontDescription layout (Just font))
let win = coreWin w
[width'', height''] = map fromIntegral [width', height']
metrics = winMetrics w
lineHeight = ascent metrics + descent metrics
winh = max 1 $ floor (height'' / lineHeight)
(tos, point, text) = askBuffer win b $ do
from <- getMarkPointB =<< fromMark <$> askMarks
rope <- streamB Forward from
p <- pointB
let content = fst $ Rope.splitAtLine winh rope
let addNL = if Rope.countNewLines content == winh
then id
else (++"\n")
return (from, p, addNL $ Rope.toString content)
if configLineWrap $ uiConfig ui
then do oldWidth <- layoutGetWidth layout
when (oldWidth /= Just width'') (layoutSetWidth layout $ Just width'')
else do (Rectangle px _py pwidth _pheight, _) <- layoutGetPixelExtents layout
widgetSetSizeRequest (textview w) (px+pwidth) (1)
oldText <- layoutGetText layout
when (oldText /= text) (layoutSetText layout text)
(_, bosOffset, _) <- layoutXYToIndex layout width'' (fromIntegral winh * lineHeight 1)
return (tos, point, tos + fromIntegral bosOffset + 1)
reloadProject :: UI -> FilePath -> IO ()
reloadProject _ _ = return ()
mkCol :: Bool
-> Yi.Style.Color -> Gtk.Color
mkCol True Default = Color 0 0 0
mkCol False Default = Color maxBound maxBound maxBound
mkCol _ (RGB x y z) = Color (fromIntegral x * 256)
(fromIntegral y * 256)
(fromIntegral z * 256)