aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2026-01-05 01:11:45 -0700
committerJosh Rahm <joshuarahm@gmail.com>2026-01-05 01:11:45 -0700
commit34e0354bb2d07ce0ad8a6e83e226370cfb9904da (patch)
treea8b9638c6d74cc2454ca2d7354a8ecea7dbd4cba
parent9daffd37236469e8089e3c12207c449b4db09e92 (diff)
downloadmontis-34e0354bb2d07ce0ad8a6e83e226370cfb9904da.tar.gz
montis-34e0354bb2d07ce0ad8a6e83e226370cfb9904da.tar.bz2
montis-34e0354bb2d07ce0ad8a6e83e226370cfb9904da.zip
[feat] right-click drag to resize windows.
-rw-r--r--plug/src/Config.hs7
-rw-r--r--plug/src/Montis/Base/Foreign/Runtime.hs10
-rw-r--r--plug/src/Montis/Standard/Drag.hs82
-rw-r--r--rt/include/util.h5
-rw-r--r--rt/src/util.c35
5 files changed, 123 insertions, 16 deletions
diff --git a/plug/src/Config.hs b/plug/src/Config.hs
index fd337eb..c76898e 100644
--- a/plug/src/Config.hs
+++ b/plug/src/Config.hs
@@ -4,9 +4,10 @@ import Control.Monad.IO.Class (liftIO)
import Data.Bits (shiftL, (.&.))
import Data.Word (Word32)
import Montis.Core
-import Montis.Standard.Drag (DragConfig (DragConfig))
-import Montis.Standard.Mouse (MouseConfig (MouseConfig))
+import Montis.Base.Foreign.Runtime
+import Montis.Standard.Drag (DragConfig (DragConfig), unwrapSelf)
import Montis.Standard.Keys (KeysConfig (KeysConfig), subkeys)
+import Montis.Standard.Mouse (MouseConfig (MouseConfig))
foreign export ccall "plugin_cold_start"
coldStart :: MontisColdStart
@@ -29,6 +30,8 @@ keys ev
subkeys $ \ev -> case keyEvent_codepoint ev of
'k' -> do
liftIO (putStrLn "k was pressed after j!")
+ self <- getSelfPtr
+ liftIO $ foreign_warpCursor (unwrapSelf self) 0 0
return True
_ -> return False
_ -> return False
diff --git a/plug/src/Montis/Base/Foreign/Runtime.hs b/plug/src/Montis/Base/Foreign/Runtime.hs
index 65ae53f..427545a 100644
--- a/plug/src/Montis/Base/Foreign/Runtime.hs
+++ b/plug/src/Montis/Base/Foreign/Runtime.hs
@@ -23,5 +23,15 @@ foreign import ccall "montis_plugin_get_toplevel_position"
foreign import ccall "montis_plugin_set_toplevel_position"
foreign_setToplevelPosition :: Ptr ForeignMontisToplevel -> CDouble -> CDouble -> IO ()
+foreign import ccall "montis_plugin_get_toplevel_geometry"
+ foreign_getToplevelGeometry ::
+ Ptr ForeignMontisToplevel -> Ptr CDouble -> Ptr CDouble -> Ptr CDouble -> Ptr CDouble -> IO ()
+
+foreign import ccall "montis_plugin_set_toplevel_geometry"
+ foreign_setToplevelGeometry :: Ptr ForeignMontisToplevel -> CDouble -> CDouble -> CDouble -> CDouble -> IO ()
+
foreign import ccall "montis_plugin_focus_toplevel"
foreign_focusToplevel :: Ptr ForeignMontisToplevel -> IO ()
+
+foreign import ccall "montis_plugin_warp_cursor"
+ foreign_warpCursor :: Ptr Void -> CDouble -> CDouble -> IO ()
diff --git a/plug/src/Montis/Standard/Drag.hs b/plug/src/Montis/Standard/Drag.hs
index 192ade8..720398d 100644
--- a/plug/src/Montis/Standard/Drag.hs
+++ b/plug/src/Montis/Standard/Drag.hs
@@ -46,7 +46,23 @@ data DragState = DragState
}
deriving (Typeable)
-newtype Dragging = Dragging (Maybe DragState)
+data ResizeState = ResizeState
+ { resizeToplevel :: Ptr ForeignMontisToplevel,
+ resizeStartX :: Double,
+ resizeStartY :: Double,
+ resizeStartW :: Double,
+ resizeStartH :: Double,
+ resizeStartCursorX :: Double,
+ resizeStartCursorY :: Double
+ }
+ deriving (Typeable)
+
+data DragAction
+ = DragMove DragState
+ | DragResize ResizeState
+ deriving (Typeable)
+
+newtype Dragging = Dragging (Maybe DragAction)
deriving (Typeable)
instance StateExtension Dragging where
@@ -57,24 +73,45 @@ instance StateExtension Dragging where
leftButton :: Word32
leftButton = 272 -- BTN_LEFT
+rightButton :: Word32
+rightButton = 273 -- BTN_RIGHT
+
onButton :: Word32 -> ButtonEvent -> Montis ()
onButton modMask ev
- | buttonEvent_button ev /= leftButton = return ()
+ | buttonEvent_button ev /= leftButton && buttonEvent_button ev /= rightButton = return ()
| buttonEvent_state ev == ButtonPressed = do
if buttonEvent_modifiers ev .&. modMask == 0
then return ()
else do
self <- getSelfPtr
CursorPosition (x, y) <- xStateGet
- newDrag <- liftIO $ do
+ (newDrag, warpState) <- liftIO $ do
tl <- foreign_toplevelAt (unwrapSelf self) (realToFrac x) (realToFrac y)
if tl == nullPtr
- then return (Dragging Nothing)
+ then return (Dragging Nothing, Nothing)
else do
- (tx, ty) <- getToplevelPosition tl
- return $
- Dragging
- (Just (DragState tl (x - tx) (y - ty)))
+ (tx, ty, tw, th) <- getToplevelGeometry tl
+ if buttonEvent_button ev == rightButton
+ then do
+ let warpX = tx + tw
+ warpY = ty + th
+ foreign_warpCursor (unwrapSelf self) (realToFrac warpX) (realToFrac warpY)
+ return
+ ( Dragging
+ ( Just
+ ( DragResize
+ (ResizeState tl tx ty tw th warpX warpY)
+ )
+ ),
+ Just (CursorPosition (warpX, warpY))
+ )
+ else
+ return
+ ( Dragging
+ (Just (DragMove (DragState tl (x - tx) (y - ty)))),
+ Nothing
+ )
+ mapM_ xStatePut warpState
xStatePut newDrag
| buttonEvent_state ev == ButtonReleased =
xStatePut (Dragging Nothing)
@@ -87,20 +124,37 @@ onMotion ev = do
Dragging mdrag <- xStateGet
case mdrag of
Nothing -> return ()
- Just (DragState tl dx dy) -> do
+ Just (DragMove (DragState tl dx dy)) -> do
liftIO $
foreign_setToplevelPosition
tl
(realToFrac (x - dx))
(realToFrac (y - dy))
+ Just (DragResize rs) -> do
+ let newW = max 1 (resizeStartW rs + (x - resizeStartCursorX rs))
+ newH = max 1 (resizeStartH rs + (y - resizeStartCursorY rs))
+ liftIO $
+ foreign_setToplevelGeometry
+ (resizeToplevel rs)
+ (realToFrac (resizeStartX rs))
+ (realToFrac (resizeStartY rs))
+ (realToFrac newW)
+ (realToFrac newH)
unwrapSelf :: SelfPtr -> Ptr Void
unwrapSelf (SelfPtr p) = p
-getToplevelPosition :: Ptr ForeignMontisToplevel -> IO (Double, Double)
-getToplevelPosition tl =
- alloca $ \xPtr -> alloca $ \yPtr -> do
- foreign_getToplevelPosition tl xPtr yPtr
+getToplevelGeometry :: Ptr ForeignMontisToplevel -> IO (Double, Double, Double, Double)
+getToplevelGeometry tl =
+ alloca $ \xPtr -> alloca $ \yPtr -> alloca $ \wPtr -> alloca $ \hPtr -> do
+ foreign_getToplevelGeometry tl xPtr yPtr wPtr hPtr
x <- peek xPtr
y <- peek yPtr
- return (realToFrac (x :: CDouble), realToFrac (y :: CDouble))
+ w <- peek wPtr
+ h <- peek hPtr
+ return
+ ( realToFrac (x :: CDouble),
+ realToFrac (y :: CDouble),
+ realToFrac (w :: CDouble),
+ realToFrac (h :: CDouble)
+ )
diff --git a/rt/include/util.h b/rt/include/util.h
index a9c290d..2ed2f70 100644
--- a/rt/include/util.h
+++ b/rt/include/util.h
@@ -9,6 +9,11 @@
void *montis_plugin_toplevel_at(void *ctx, double lx, double ly);
void montis_plugin_get_toplevel_position(void *toplevel, double *x, double *y);
void montis_plugin_set_toplevel_position(void *toplevel, double x, double y);
+void montis_plugin_get_toplevel_geometry(void *toplevel, double *x, double *y,
+ double *w, double *h);
+void montis_plugin_set_toplevel_geometry(void *toplevel, double x, double y,
+ double w, double h);
void montis_plugin_focus_toplevel(void *toplevel);
+void montis_plugin_warp_cursor(void *ctx, double lx, double ly);
#endif /* MONTIS_UTIL_H */
diff --git a/rt/src/util.c b/rt/src/util.c
index 66a2b20..e09cff9 100644
--- a/rt/src/util.c
+++ b/rt/src/util.c
@@ -63,6 +63,41 @@ void montis_plugin_set_toplevel_position(void *toplevel, double x, double y)
wlr_scene_node_set_position(&tl->scene_tree->node, (int)x, (int)y);
}
+void montis_plugin_get_toplevel_geometry(void *toplevel, double *x, double *y,
+ double *w, double *h)
+{
+ if (!toplevel || !x || !y || !w || !h) {
+ return;
+ }
+ struct montis_toplevel *tl = toplevel;
+ struct wlr_box geo_box;
+ wlr_xdg_surface_get_geometry(tl->xdg_toplevel->base, &geo_box);
+ *x = tl->scene_tree->node.x;
+ *y = tl->scene_tree->node.y;
+ *w = geo_box.width;
+ *h = geo_box.height;
+}
+
+void montis_plugin_set_toplevel_geometry(void *toplevel, double x, double y,
+ double w, double h)
+{
+ if (!toplevel) {
+ return;
+ }
+ struct montis_toplevel *tl = toplevel;
+ wlr_scene_node_set_position(&tl->scene_tree->node, (int)x, (int)y);
+ wlr_xdg_toplevel_set_size(tl->xdg_toplevel, (int)w, (int)h);
+}
+
+void montis_plugin_warp_cursor(void *ctx, double lx, double ly)
+{
+ if (!ctx) {
+ return;
+ }
+ struct montis_server *server = server_from_ctx(ctx);
+ wlr_cursor_warp(server->cursor, NULL, lx, ly);
+}
+
void montis_plugin_focus_toplevel(void *toplevel)
{
if (!toplevel) {