Initial commit

This commit is contained in:
2026-07-15 11:38:06 +01:00
commit 1d5acfdc94
20 changed files with 2902 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
module WM.Core where
import Control.Concurrent (threadDelay)
import Foreign (Ptr)
import System.Environment (getArgs)
import WM.Mruby
import WM.XKutu
data State = State
{ mruby_pointer :: Ptr MrbState,
running :: Bool
}
deriving (Show)
run :: IO ()
run = do
is_started <- deploy
print is_started
add_keybind 24 0
w_id <- draw_rectangle 10 100 10 10 0x500070
geom <- get_geometry w_id
print geom
screen_info <- get_screen
print screen_info
pointer_info <- get_pointer
print pointer_info
mrb <- mrb_open
runRuby mrb runtimeRb
handle_args mrb
loop $ State mrb True
mrb_close mrb
handle_args :: Ptr MrbState -> IO ()
handle_args mrb = do
args <- getArgs
case args of
[] -> pure ()
["--script", file] -> do
script <- readFile file
runRuby mrb script
_ -> putStrLn "unknown arguments"
handle_event :: Maybe Event -> State -> IO State
handle_event Nothing state = pure state
handle_event (Just ev) state
| event_btn ev == 24 = pure state {running = False}
| otherwise = do
print ev
pure state
loop :: State -> IO ()
loop state
| not (running state) = pure ()
| otherwise = do
flush
threadDelay 10000
ev <- next_event
handle_event ev state
>>= loop
+32
View File
@@ -0,0 +1,32 @@
{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE TemplateHaskell #-}
module WM.Mruby where
import Data.FileEmbed (embedFile)
import Data.Text qualified as T
import Data.Text.Encoding qualified as TE
import Data.Text.Encoding.Error qualified as TEE
import Foreign.C (CString, withCString)
import Foreign.Ptr
runtimeRb :: String
runtimeRb =
T.unpack (TE.decodeUtf8With TEE.lenientDecode $(embedFile "./app/ruby/lib.rb"))
data MrbState
foreign import capi "mruby.h mrb_open"
mrb_open :: IO (Ptr MrbState)
foreign import capi "mruby.h mrb_close"
mrb_close :: Ptr MrbState -> IO ()
foreign import capi "mruby/compile.h mrb_load_string"
c_mrb_load_string :: Ptr MrbState -> CString -> IO ()
runRuby :: Ptr MrbState -> String -> IO ()
runRuby mrb code =
withCString code $ \cstr ->
c_mrb_load_string mrb cstr
+219
View File
@@ -0,0 +1,219 @@
{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module WM.XKutu where
import Data.Int (Int16, Int32, Int8)
import Data.Word (Word16, Word32)
import Foreign (Ptr, Storable, alignment, alloca, peek, peekByteOff, poke, pokeByteOff, sizeOf)
import Foreign.C.Types (CInt (CInt))
newtype XcbWindow = XcbWindow Word32
deriving (Show, Storable)
data Geometry = Geometry
{ geom_x :: Int16,
geom_y :: Int16,
geom_w :: Word16,
geom_h :: Word16
}
deriving (Show)
instance Storable Geometry where
sizeOf _ = 8
alignment _ = alignment (undefined :: Word32)
peek ptr = do
x <- peekByteOff ptr 0
y <- peekByteOff ptr 2
w <- peekByteOff ptr 4
h <- peekByteOff ptr 6
pure $ Geometry x y w h
poke ptr (Geometry x y w h) = do
pokeByteOff ptr 0 x
pokeByteOff ptr 2 y
pokeByteOff ptr 4 w
pokeByteOff ptr 6 h
data PointerInfo = PointerInfo
{ pointer_info_x :: Int16,
pointer_info_y :: Int16,
pointer_info_w :: XcbWindow
}
deriving (Show)
instance Storable PointerInfo where
sizeOf _ = 8
alignment _ = alignment (undefined :: Word32)
peek ptr = do
x <- peekByteOff ptr 0
y <- peekByteOff ptr 2
w <- peekByteOff ptr 4
pure $ PointerInfo x y w
poke ptr (PointerInfo x y w) = do
pokeByteOff ptr 0 x
pokeByteOff ptr 2 y
pokeByteOff ptr 4 w
data Event = Event
{ event_type :: Int32,
event_window :: XcbWindow,
event_override_redirect :: Int8,
event_btn :: Word32,
event_x :: Int32,
event_y :: Int32,
event_height :: Word32,
event_width :: Word32,
event_state :: Word32,
event_is_root :: Int8
}
deriving (Show)
instance Storable Event where
sizeOf _ = 40
alignment _ = alignment (undefined :: Word32)
peek ptr = do
eventType <- peekByteOff ptr 0
window <- peekByteOff ptr 4
overrideRedirect <- peekByteOff ptr 8
btn <- peekByteOff ptr 12
x <- peekByteOff ptr 16
y <- peekByteOff ptr 20
height <- peekByteOff ptr 24
width <- peekByteOff ptr 28
state <- peekByteOff ptr 32
isRoot <- peekByteOff ptr 36
pure $
Event
eventType
(XcbWindow window)
overrideRedirect
btn
x
y
height
width
state
isRoot
poke ptr (Event eventType (XcbWindow window) overrideRedirect btn x y height width state isRoot) = do
pokeByteOff ptr 0 eventType
pokeByteOff ptr 4 window
pokeByteOff ptr 8 overrideRedirect
pokeByteOff ptr 12 btn
pokeByteOff ptr 16 x
pokeByteOff ptr 20 y
pokeByteOff ptr 24 height
pokeByteOff ptr 28 width
pokeByteOff ptr 32 state
pokeByteOff ptr 36 isRoot
foreign import capi "x_kutu.h value MOD"
mOD_MASK :: CInt
foreign import capi "x_kutu.h value XCB_MOD_MASK_LOCK"
cLK_MASK :: CInt
foreign import capi "x_kutu.h value XCB_MOD_MASK_2"
nUM_MASK :: CInt
foreign import capi "x_kutu.h deploy"
c_deploy :: IO CInt
deploy :: IO Bool
deploy = (== 0) <$> c_deploy
foreign import capi "x_kutu.h flush"
flush :: IO ()
foreign import capi "x_kutu.h cleanup"
cleanup :: IO ()
foreign import capi "x_kutu.h add_keybind"
add_keybind :: CInt -> CInt -> IO ()
foreign import capi "x_kutu.h add_mousebind"
add_mousebind :: CInt -> CInt -> IO ()
foreign import capi "x_kutu.h get_focus"
c_get_focus :: IO Word32
get_focus :: IO XcbWindow
get_focus = XcbWindow <$> c_get_focus
foreign import capi "x_kutu.h get_root"
c_get_root :: IO Word32
get_root :: IO XcbWindow
get_root = XcbWindow <$> c_get_root
foreign import capi "x_kutu.h focus"
focus :: XcbWindow -> IO ()
foreign import capi "x_kutu.h subscribe"
subscribe :: XcbWindow -> IO ()
foreign import capi "x_kutu.h kill"
kill :: XcbWindow -> IO ()
foreign import capi "x_kutu.h destroy"
destroy :: XcbWindow -> IO ()
foreign import capi "x_kutu.h show"
show :: XcbWindow -> IO ()
foreign import capi "x_kutu.h hide"
hide :: XcbWindow -> IO ()
foreign import capi "x_kutu.h send_to_top"
send_to_top :: XcbWindow -> IO ()
foreign import capi "x_kutu.h draw_rectangle"
c_draw_rectangle :: CInt -> CInt -> CInt -> CInt -> Word32 -> IO Word32
draw_rectangle :: CInt -> CInt -> CInt -> CInt -> Word32 -> IO XcbWindow
draw_rectangle x y w h c =
XcbWindow <$> c_draw_rectangle x y w h c
foreign import capi "x_kutu.h get_geometry"
c_get_geometry :: XcbWindow -> Ptr Geometry -> IO ()
get_geometry :: XcbWindow -> IO Geometry
get_geometry win =
alloca $ \ptr -> do
c_get_geometry win ptr
peek ptr
foreign import capi "x_kutu.h get_screen"
c_get_screen :: Ptr Geometry -> IO ()
get_screen :: IO Geometry
get_screen =
alloca $ \ptr -> do
c_get_screen ptr
peek ptr
foreign import capi "x_kutu.h get_pointer"
c_get_pointer :: Ptr PointerInfo -> IO ()
get_pointer :: IO PointerInfo
get_pointer =
alloca $ \ptr -> do
c_get_pointer ptr
peek ptr
foreign import capi "x_kutu.h next_event"
c_next_event :: Ptr Event -> IO CInt
next_event :: IO (Maybe Event)
next_event =
alloca $ \ptr -> do
ok <- c_next_event ptr
if ok == 0
then pure Nothing
else Just <$> peek ptr