package oxbow

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

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
;;