Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scripts/cce-app-menu (4K)
1 #!/usr/bin/env bash
2 # cce-app-menu — Context menu for CCE application windows
3 #
4 # Opened by the compositor (cursor.rs, the right-click border path): from a
5 # right-click on a window's handle disc in adjust mode, and from a right-click
6 # anywhere on a window in overview. It is a `cce-cloud --json` popup, the same
7 # mechanism as cce-desktop-menu and cce-grid's desktop-item menu, so it looks
8 # and dismisses like every other menu in the DE. The first page holds the
9 # window verbs; "Window Mode" is a second page (a button with `target_page`
10 # switches pages without closing the popup) listing the modes `ccectl
11 # set-mode` accepts, the current one marked.
12 #
13 # The mode is set with `set-mode <mode> <id>` — one window, by id — NOT
14 # `mode <mode> <app_id>`, which appends a persistent rule that also
15 # re-classes every later window of that app.
16
17 CCE_CTL="$HOME/.local/bin/ccectl"
18 CLEAR_CLOUD="$HOME/.local/bin/cce-cloud"
19
20 # Default to empty/none
21 x_arg=""
22 y_arg=""
23 window_index=""
24 app_id=""
25
26 while [[ $# -gt 0 ]]; do
27 case "$1" in
28 -x|--x-pos)
29 # A flag with no value must still consume itself: bare `shift 2`
30 # is a no-op when one arg remains, and the loop spins forever.
31 if [[ $# -ge 2 ]]; then x_arg="-x $2"; shift 2; else shift; fi
32 ;;
33 -y|--y-pos)
34 if [[ $# -ge 2 ]]; then y_arg="-y $2"; shift 2; else shift; fi
35 ;;
36 -i|--window-id)
37 if [[ $# -ge 2 ]]; then window_index="$2"; shift 2; else shift; fi
38 ;;
39 -a|--app-id)
40 if [[ $# -ge 2 ]]; then app_id="$2"; shift 2; else shift; fi
41 ;;
42 *)
43 shift
44 ;;
45 esac
46 done
47
48 if [[ -z "$window_index" || -z "$app_id" ]]; then
49 echo "Usage: cce-app-menu -i <window_index> -a <app_id> [-x <x>] [-y <y>]" >&2
50 exit 1
51 fi
52
53 # The window's current mode, so the submenu can mark it. `windows --json` is
54 # one object per line; a window that has gone since the click yields "".
55 # jq, not python, here and below: the popup waits on everything before it,
56 # and each python start costs 100-200 ms (more under load) where jq costs a
57 # few. cce-desktop-menu builds its menu the same way.
58 current_mode=$("$CCE_CTL" windows --json 2>/dev/null | jq -Rrn --arg want "$window_index" '
59 first(inputs | fromjson? | select((.id | tostring) == $want)
60 | .mode // "" | tostring | ascii_downcase)' 2>/dev/null)
61
62 # Build the layout in jq so the app_id (client-chosen text) is escaped
63 # for JSON rather than spliced into it.
64 json_layout=$(jq -nc --arg app_id "$app_id" --arg current "$current_mode" '
65 def btn($text; $id): {type: "button", text: $text, id: $id};
66 # The mode page is a radio group: cce-cloud draws the toolkit leading
67 # marks ("● " on, "○ " off) as the circle glyphs, and a `target_page` button
68 # as a page row (chevron-right) or, turning back to an earlier page, a back
69 # row (chevron-left) -- so no label spells a mark or an arrow of its own.
70 def mode($text; $id): btn((if $id == $current then "● " else "○ " end) + $text; $id);
71 {pages: [
72 {title: $app_id, justify: "left", widgets: [
73 btn("Window Mode"; "mode_page") + {target_page: 1},
74 btn("Minimize"; "minimize"),
75 btn("Close Window"; "close")
76 ]},
77 {title: "Window Mode", justify: "left", widgets: [
78 mode("Floating"; "floating"),
79 mode("Tiled"; "tiled"),
80 mode("Fullscreen"; "fullscreen"),
81 btn("Back"; "back") + {target_page: 0}
82 ]}
83 ]}')
84
85 # Spawn cce-cloud with the json layout and coordinates
86 selected=$(echo "$json_layout" | $CLEAR_CLOUD --json $x_arg $y_arg 2>/dev/null)
87
88 [[ -z "$selected" ]] && exit 0
89
90 btn=$(jq -r '.button // ""' <<<"$selected" 2>/dev/null)
91
92 [[ -z "$btn" ]] && exit 0
93
94 case "$btn" in
95 "floating"|"tiled"|"fullscreen")
96 $CCE_CTL set-mode "$btn" "$window_index"
97 ;;
98 "minimize")
99 # minimize/close act on the focused window: focus this one first.
100 $CCE_CTL focus-window "$window_index"
101 $CCE_CTL minimize
102 ;;
103 "close")
104 $CCE_CTL focus-window "$window_index"
105 $CCE_CTL close
106 ;;
107 esac