diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs
index d0cc436d2304dc6ad96897d990cc48d40e262ecc..ec6babd4086badfdf07745eb817b99a8a67d54c7 100644
--- a/src/handlers/xdg_shell.rs
+++ b/src/handlers/xdg_shell.rs
@@ -1,12 +1,14 @@
 use smithay::{
     delegate_xdg_shell,
-    desktop::{find_popup_root_surface, get_popup_toplevel_coords, PopupKind, PopupManager, Space, Window},
+    desktop::{
+        find_popup_root_surface, get_popup_toplevel_coords, PopupKind, PopupManager, Space, Window,
+    },
     input::{
         pointer::{Focus, GrabStartData as PointerGrabStartData},
         Seat,
     },
     reexports::{
-        wayland_protocols::xdg::shell::server::xdg_toplevel,
+        wayland_protocols::xdg::shell::server::xdg_toplevel::{self},
         wayland_server::{
             protocol::{wl_seat, wl_surface::WlSurface},
             Resource,
@@ -27,6 +29,13 @@ use crate::{
     Smallvil,
 };
 
+fn is_maximized(surface: &ToplevelSurface) -> bool {
+    surface
+        .current_state()
+        .states
+        .contains(xdg_toplevel::State::Maximized)
+}
+
 impl XdgShellHandler for Smallvil {
     fn xdg_shell_state(&mut self) -> &mut XdgShellState {
         &mut self.xdg_shell_state
@@ -42,7 +51,12 @@ impl XdgShellHandler for Smallvil {
         let _ = self.popups.track_popup(PopupKind::Xdg(surface));
     }
 
-    fn reposition_request(&mut self, surface: PopupSurface, positioner: PositionerState, token: u32) {
+    fn reposition_request(
+        &mut self,
+        surface: PopupSurface,
+        positioner: PositionerState,
+        token: u32,
+    ) {
         surface.with_pending_state(|state| {
             let geometry = positioner.get_geometry();
             state.geometry = geometry;
@@ -52,7 +66,15 @@ impl XdgShellHandler for Smallvil {
         surface.send_repositioned(token);
     }
 
+    fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
+        // TODO popup grabs
+    }
+
     fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) {
+        if is_maximized(&surface) {
+            return;
+        }
+
         let seat = Seat::from_resource(&seat).unwrap();
 
         let wl_surface = surface.wl_surface();
@@ -85,6 +107,10 @@ impl XdgShellHandler for Smallvil {
         serial: Serial,
         edges: xdg_toplevel::ResizeEdge,
     ) {
+        if is_maximized(&surface) {
+            return;
+        }
+
         let seat = Seat::from_resource(&seat).unwrap();
 
         let wl_surface = surface.wl_surface();
@@ -118,8 +144,56 @@ impl XdgShellHandler for Smallvil {
         }
     }
 
-    fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
-        // TODO popup grabs
+    fn maximize_request(&mut self, surface: ToplevelSurface) {
+        if surface
+            .current_state()
+            .capabilities
+            .contains(xdg_toplevel::WmCapabilities::Maximize)
+        {
+            let wl_surface = surface.wl_surface();
+            let window = self
+                .space
+                .elements()
+                .find(|w| w.toplevel().wl_surface() == wl_surface)
+                .unwrap()
+                .clone();
+            let window_outputs = self.space.outputs_for_element(&window);
+            let output = window_outputs
+                .first()
+                .or_else(|| self.space.outputs().next())
+                .expect("No outputs?");
+
+            let geometry = self.space.output_geometry(output).unwrap();
+
+            surface.with_pending_state(|state| {
+                state.states.set(xdg_toplevel::State::Maximized);
+                state.size = Some(geometry.size);
+            });
+            self.space.map_element(window, geometry.loc, true);
+        }
+
+        surface.send_configure();
+    }
+
+    fn unmaximize_request(&mut self, surface: ToplevelSurface) {
+        if is_maximized(&surface) {
+            surface.with_pending_state(|state| {
+                state.states.unset(xdg_toplevel::State::Maximized);
+                state.size = None; // TODO: we should revert to the previous size and locationb
+            });
+            surface.send_pending_configure();
+        }
+    }
+
+    fn minimize_request(&mut self, surface: ToplevelSurface) {
+        let wl_surface = surface.wl_surface();
+        let window = self
+            .space
+            .elements()
+            .find(|w| w.toplevel().wl_surface() == wl_surface)
+            .unwrap()
+            .clone();
+        self.space.unmap_elem(&window); // TODO: this is unsafe as we have no way to remap it currently
     }
 }
 
@@ -202,7 +276,11 @@ impl Smallvil {
         let Ok(root) = find_popup_root_surface(&PopupKind::Xdg(popup.clone())) else {
             return;
         };
-        let Some(window) = self.space.elements().find(|w| w.toplevel().wl_surface() == &root) else {
+        let Some(window) = self
+            .space
+            .elements()
+            .find(|w| w.toplevel().wl_surface() == &root)
+        else {
             return;
         };