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 current_mode=$("$CCE_CTL" windows --json 2>/dev/null | python3 -c '
 56 import sys, json
 57 want = sys.argv[1]
 58 for line in sys.stdin:
 59     line = line.strip()
 60     if not line:
 61         continue
 62     try:
 63         w = json.loads(line)
 64     except ValueError:
 65         continue
 66     if str(w.get("id")) == want:
 67         print(str(w.get("mode", "")).lower())
 68         break
 69 ' "$window_index")
 70 
 71 # Build the layout in python so the app_id (client-chosen text) is escaped
 72 # for JSON rather than spliced into it.
 73 json_layout=$(python3 - "$app_id" "$current_mode" <<'PY'
 74 import sys, json
 75 app_id, current = sys.argv[1], sys.argv[2]
 76 def mode(text, ident):
 77     mark = "● " if ident == current else "   "
 78     return {"type": "button", "text": mark + text, "id": ident}
 79 layout = {"pages": [
 80     {"title": app_id, "justify": "left", "widgets": [
 81         {"type": "button", "text": "Window Mode >", "id": "mode_page", "target_page": 1},
 82         {"type": "button", "text": "Minimize", "id": "minimize"},
 83         {"type": "button", "text": "Close Window", "id": "close"},
 84     ]},
 85     {"title": "Window Mode", "justify": "left", "widgets": [
 86         mode("Floating", "floating"),
 87         mode("Tiled", "tiled"),
 88         mode("Fullscreen", "fullscreen"),
 89         {"type": "button", "text": "< Back", "id": "back", "target_page": 0},
 90     ]},
 91 ]}
 92 print(json.dumps(layout))
 93 PY
 94 )
 95 
 96 # Spawn cce-cloud with the json layout and coordinates
 97 selected=$(echo "$json_layout" | $CLEAR_CLOUD --json $x_arg $y_arg 2>/dev/null)
 98 
 99 [[ -z "$selected" ]] && exit 0
100 
101 # Parse button using python for reliability and robustness
102 btn=$(echo "$selected" | python3 -c "import sys, json; print(json.load(sys.stdin).get('button', ''))")
103 
104 [[ -z "$btn" ]] && exit 0
105 
106 case "$btn" in
107     "floating"|"tiled"|"fullscreen")
108         $CCE_CTL set-mode "$btn" "$window_index"
109         ;;
110     "minimize")
111         # minimize/close act on the focused window: focus this one first.
112         $CCE_CTL focus-window "$window_index"
113         $CCE_CTL minimize
114         ;;
115     "close")
116         $CCE_CTL focus-window "$window_index"
117         $CCE_CTL close
118         ;;
119 esac