package oxbow

  1. Overview
  2. Docs
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/targets.ml.html

Source file targets.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
open! Oxbow_core
open! Oxbow_state
open! Result.Syntax

let outputs_by_recency (wm : Wm.t) (seat : Seat.t) =
  match seat.output with
  | None -> wm.outputs
  | Some o -> o :: List.filter (( != ) o) wm.outputs
;;

let windows_by_recency wm seat =
  let stacks = outputs_by_recency wm seat in
  let ordered = List.(map (fun (o : Output.t) -> o.focus_stack) stacks |> flatten) in
  let orphans = List.filter (fun (w : Window.t) -> Option.is_none w.output) wm.windows in
  ordered @ orphans
;;

let no_match kind to_string m =
  Error (Printf.sprintf "no %s matching %s" kind (to_string m))
;;

let matched_windows wm seat (wmatch : Window_match.t) =
  let* matches = Window_pattern.compile wmatch.pattern in
  let+ windows = Window_scope.filter wm seat wmatch.scope in
  let hits =
    windows_by_recency wm seat
    |> List.filter (fun w -> List.memq w windows && Window_scope.holds matches w)
  in
  matches, windows, hits
;;

let matched_outputs wm seat (omatch : Output_match.t) =
  let+ matches = Output_match.compile omatch in
  let hits =
    outputs_by_recency wm seat
    |> List.filter (fun (o : Output.t) -> matches ~name:o.name ~labels:o.labels)
  in
  matches, hits
;;

let return_window_cycle seat matches scoped ~best =
  let stable = List.find_all (Window_scope.holds matches) scoped in
  let next =
    match Seat.focused_window seat with
    | Some w when List.memq w stable -> Ring.next_or_first w stable
    | _ -> List.nth_opt stable 0
  in
  Ok (Option.value next ~default:best)
;;

let return_output_cycle (seat : Seat.t) hits ~best =
  let next =
    match seat.output with
    | Some o when List.memq o hits -> Ring.next_or_first o hits
    | _ -> List.nth_opt hits 0
  in
  Ok (Option.value next ~default:best)
;;

let resolve_one_window wm seat (target : Target.Window.One.t) =
  match target with
  | Focused -> With.focused_window seat @@ fun _o w -> Ok w
  | Matching { wmatch; select } ->
    let* matches, windows, hits = matched_windows wm seat wmatch in
    (match hits with
     | [] -> no_match "window" Window_match.to_string wmatch
     | best :: _ ->
       if select = Best then Ok best else return_window_cycle seat matches windows ~best)
;;

let resolve_one_output wm seat (target : Target.Output.One.t) =
  match target with
  | Focused -> With.focused_output seat @@ fun o -> Ok o
  | Matching { omatch; select } ->
    let* _, hits = matched_outputs wm seat omatch in
    (match hits with
     | [] -> no_match "output" Output_match.to_string omatch
     | best :: _ -> if select = Best then Ok best else return_output_cycle seat hits ~best)
;;

let resolve_all_windows wm seat (target : Target.Window.Any.t) =
  match target with
  | One o ->
    let+ w = resolve_one_window wm seat o in
    [ w ]
  | All { wmatch } ->
    let* _, _, hits = matched_windows wm seat wmatch in
    (match hits with
     | [] -> no_match "window" Window_match.to_string wmatch
     | _ -> Ok hits)
;;

let resolve_all_outputs wm seat (target : Target.Output.Any.t) =
  match target with
  | One o ->
    let+ o = resolve_one_output wm seat o in
    [ o ]
  | All { omatch } ->
    let* _, hits = matched_outputs wm seat omatch in
    (match hits with
     | [] -> no_match "output" Output_match.to_string omatch
     | _ -> Ok hits)
;;

let transact windows plan =
  let+ rev_actions =
    List.fold_left
      (fun acc w ->
         Result.bind acc
         @@ fun actions -> Result.bind (plan w) @@ fun action -> Ok (action :: actions))
      (Ok [])
      windows
  in
  (* The actions run reversed of the window order. This is depended on by
     [Stacking.push], which puts the first window on top. If the actions were in
     window order, the last window would be the head. *)
  List.iter (fun action -> action ()) rev_actions;
  windows
;;

let transact_all_windows wm seat target ~plan =
  let* windows = resolve_all_windows wm seat target in
  transact windows plan
;;

let transact_all_outputs wm seat target ~plan =
  let* outputs = resolve_all_outputs wm seat target in
  transact outputs plan
;;

let apply_one bind wm seat target plan =
  let* x = bind wm seat target in
  let+ action = plan x in
  action ();
  x
;;

let transact_one_window wm seat target ~plan =
  apply_one resolve_one_window wm seat target plan
;;

let transact_one_output wm seat target ~plan =
  apply_one resolve_one_output wm seat target plan
;;