package oxbow
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Dynamic window manager for the river Wayland compositor
Install
dune-project
Dependency
Authors
Maintainers
Sources
v0.1.0.tar.gz
md5=a637acaa0e19046cd65fff733874eb97
sha512=76230cbecd7510de7a05b5d1f335915ef7b6c4aa557d8dbfd23591606618d2cfa25082d17b93f2f4b53a118d1cbf6a80e4ad51cf0f099b4921fb83d6df0c9801
doc/src/oxbow.ops/placement.ml.html
Source file placement.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361open! Oxbow_core open! Oxbow_state open! Oxbow_ipc open! Result.Syntax let zoom ?warp wm seat target = let* w = Targets.resolve_one_window wm seat target in With.output w @@ fun o -> match Output.current_layout o with | _ when o.overview.enabled -> Error "cannot zoom from overview" | Floating -> Error "cannot zoom in the floating layout" | Scrolling -> Column.zoom ?warp wm seat w | Tiling -> Tiling.zoom ?warp wm seat w ;; let move_window ?(policy = Tag.Policy.Keep) window (target : Output.t) = let take () = (match policy with | Keep -> () | Take -> Tag.Set.first_index target.tags.selected |> Option.fold ~none:window.tags ~some:Tag.Set.singleton |> Window.set_tags window); Window.set_output window @@ Some target; Stacking.push [ window ] target in match window.output with | Some o when o == target -> () | None -> take () | Some _ -> Option.iter (Stacking.remove_window ~window) window.output; take () ;; let send_to ~(dst : Output.t) window policy = move_window ~policy window dst; (match window.presentation with | Tiled when not @@ Window.floats window dst -> () | Tiled | Floating -> Window.restore_float window | Maximized { restore } -> Window.maximize ~restore window | Fullscreen _ -> Window.fullscreen ~force:true window); Schedule.manage () ;; let send_result ~src window policy ~err = function | Some o when o != src -> Ok (fun () -> send_to ~dst:o window policy) | _ -> Error err ;; let run_plan plan = let+ commit = plan in commit (); None ;; let plan_send_window_to_logical (wm : Wm.t) window dir policy = With.output window @@ fun current -> Output.resolve_output_logical ~dir current wm.outputs |> send_result ~src:current window policy ~err:Messages.no_other_output ;; let send_window_to_logical wm window dir policy = plan_send_window_to_logical wm window dir policy |> run_plan ;; let plan_send_window_to_spatial (wm : Wm.t) window dir policy = With.output window @@ fun current -> let from = Output.to_vector current in Output.resolve_output_spatial ~from ~dir current wm.outputs |> send_result ~src:current window policy ~err:(Printf.sprintf "no output %s" (Direction.Spatial.to_string dir)) ;; let send_window_to_spatial wm window dir policy = plan_send_window_to_spatial wm window dir policy |> run_plan ;; let plan_send_window_to_name (wm : Wm.t) window name policy = With.output window @@ fun current -> if Output.matches_name name current then Ok ignore else Output.resolve_output_name name wm.outputs |> send_result ~src:current window policy ~err:(Printf.sprintf "no output named %S" name) ;; let send_window_to_name wm window name policy = plan_send_window_to_name wm window name policy |> run_plan ;; let follow_focus wm seat ~follow ~send = let+ l = send () in match l with | w :: _ when follow -> Focus.focus_window ~force:true ~warp:Seat.Warp_request.Follow_config wm seat w; None | _ -> None ;; let send_to_logical wm seat target dir policy ~follow = follow_focus wm seat ~follow ~send:(fun () -> Targets.transact_all_windows wm seat target ~plan:(fun w -> plan_send_window_to_logical wm w dir policy)) ;; let send_to_spatial wm seat target dir policy ~follow = follow_focus wm seat ~follow ~send:(fun () -> Targets.transact_all_windows wm seat target ~plan:(fun w -> plan_send_window_to_spatial wm w dir policy)) ;; let send_to_name wm seat target name policy ~follow = follow_focus wm seat ~follow ~send:(fun () -> Targets.transact_all_windows wm seat target ~plan:(fun w -> plan_send_window_to_name wm w name policy)) ;; let toggle_floating wm seat target = let+ _ = Targets.transact_all_windows wm seat target ~plan:(fun w -> match w.output with | None -> Error Messages.window_missing_output | Some o -> if Output.current_layout o = Floating then Error "cannot toggle floating from the floating layout" else if o.overview.enabled then Error "cannot toggle floating from overview" else ( match w.presentation with | Fullscreen _ -> Error "cannot toggle float while window is fullscreen" | Maximized _ -> Error "cannot toggle float while window is maximized" | Floating when w.is_fixed -> Error "cannot tile a fixed window" | Tiled -> Ok (fun () -> Window.float w; Schedule.manage ()) | Floating -> Ok (fun () -> Window.tile w; Schedule.manage ()))) in None ;; let maximize (wm : Wm.t) window = List.iter Seat.clear_op wm.seats; Window.maximize window; Schedule.manage () ;; let unmaximize window = Window.unmaximize window; Schedule.manage () ;; let fullscreen (wm : Wm.t) output (window : Window.t) cb = let enter () = match output, window.output with | None, None -> () | Some o, _ | None, Some o -> List.iter (fun w -> if Window.tag_visible w && Window.is_fullscreen w then cb wm w Window.Request.Exit_fullscreen) o.focus_stack; List.iter Seat.clear_op wm.seats; move_window window o; Window.fullscreen window; Schedule.manage () in match window.presentation with | Tiled | Floating | Maximized _ -> enter () | Fullscreen _ -> (match output, window.output with | Some o1, Some o2 when o1 != o2 -> move_window window o1; Window.fullscreen ~force:true window; Schedule.manage () | _, _ -> ()) ;; let exit_fullscreen (window : Window.t) = match window.presentation with | Tiled | Floating | Maximized _ -> () | Fullscreen _ -> Window.exit_fullscreen window; Schedule.manage () ;; let close wm seat target = let+ _ = Targets.transact_all_windows wm seat target ~plan:(fun w -> Ok (fun () -> Window.set_close_pending w true)) in None ;; let unless_fullscreen ~verb w f = if Window.is_fullscreen w then Error (Printf.sprintf "cannot %s a fullscreen window" verb) else Ok (fun () -> f (); Schedule.manage ()) ;; let plan_move_window_to ~x ~y w = unless_fullscreen ~verb:"move" w @@ fun () -> Window.move_to w ~x ~y ;; let move_window_to ~x ~y w = plan_move_window_to ~x ~y w |> run_plan let move_to ~x ~y wm seat target = let+ _ = Targets.transact_all_windows wm seat target ~plan:(fun w -> plan_move_window_to ~x ~y w) in None ;; let move_spatial wm seat target dir by = let+ _ = Targets.transact_all_windows wm seat target ~plan:(fun w -> unless_fullscreen ~verb:"move" w @@ fun () -> Window.move_spatial w dir by) in None ;; let plan_resize_window_to ~width ~height window = unless_fullscreen ~verb:"resize" window @@ fun () -> Window.resize_to window ~width ~height ;; let resize_window_to ~width ~height window = plan_resize_window_to ~width ~height window |> run_plan ;; let resize_to ~width ~height wm seat target = let+ _ = Targets.transact_all_windows wm seat target ~plan:(fun w -> plan_resize_window_to ~width ~height w) in None ;; let resize_spatial wm seat target dir by = let+ _ = Targets.transact_all_windows wm seat target ~plan:(fun w -> unless_fullscreen ~verb:"resize" w @@ fun () -> Window.resize_spatial w dir by) in None ;; let swap_outputs (wm : Wm.t) seat ~(target : Command.Output.Swap.Target.t) ~policy ~follow scope = With.focused_output seat @@ fun current -> let with_named_output name f = match List.find_opt (fun (o : Output.t) -> Output.matches_name name o) wm.outputs with | None -> Error (Printf.sprintf "no output name matching %S" name) | Some o -> f o in let* a, b = match target with | Pair { first; second } -> (match first, second with | None, None -> (match wm.outputs with | [ a; b ] -> if a == current then Ok (a, b) else Ok (b, a) | os -> Error (Printf.sprintf "needs exactly two outputs, have %d" (List.length os))) | Some n, None -> with_named_output n @@ fun a -> Ok (current, a) | Some n, Some n' -> with_named_output n @@ fun a -> with_named_output n' @@ fun b -> Ok (a, b) | None, Some _ -> Error "swap needs a first output name before a second") | Ring { members; rev } -> let resolve name = List.find_opt (Output.matches_name name) wm.outputs in let live = List.filter_map resolve members in if List.length live < 2 then Error "the ring needs two connect outputs" else if not @@ List.memq current live then Error "the focused output is not in the ring" else ( let dest = Option.get @@ if rev then Ring.prev_or_last current live else Ring.next_or_first current live in Ok (current, dest)) in if a == b then Error "cannot swap an output with itself" else ( let in_scope = match scope with | `Tags -> fun (o : Output.t) -> if o == a then Output.visible_windows a else Output.windows_on_tags b ~tags:a.tags.selected | `All -> fun (o : Output.t) -> o.wm_stack | `Visible -> Output.visible_windows in let a_focus = a.focus_stack and b_focus = b.focus_stack and a_ws = in_scope a |> List.rev and b_ws = in_scope b |> List.rev in List.iter (fun w -> send_to ~dst:b w policy) a_ws; List.iter (fun w -> send_to ~dst:a w policy) b_ws; Stacking.restore_focus_order ~like:a_focus b; Stacking.restore_focus_order ~like:b_focus a; if follow then ( let arrived w = List.memq w a_focus in match List.find_opt arrived b.focus_stack with | Some w -> Focus.focus_window ~force:true ~warp:Seat.Warp_request.Follow_config wm seat w | None -> Focus.focus_output wm seat b); Ok None) ;; let set_sticky wm seat target scope = let+ _ = Targets.transact_all_windows wm seat target ~plan:(fun w -> Ok (fun () -> Window.set_sticky w scope)) in None ;; let toggle_sticky wm seat target toggle = let+ _ = Targets.transact_all_windows wm seat target ~plan:(fun w -> let scope = match w.sticky with | Off -> Sticky.of_toggle toggle | Occupied | All -> Off in Ok (fun () -> Window.set_sticky w scope)) in None ;;
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>