git.lucas.co / cce-compositor
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