git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

scripts/startcce (8.1K)

  1 #!/bin/sh
  2 # Launch cce-server with cce-client
  3 # Usage: startcce [--logging] [--verbose] [--debug]
  4 #
  5 #   --logging  announce the log paths. The compositor's stderr goes to
  6 #              $CCE_RUN/cce.log regardless; this flag no longer raises the
  7 #              log level. The session entry in /usr/share/wayland-sessions
  8 #              passes it, and it used to mean `--log-level debug`: every
  9 #              transaction, key press and xwm property became a formatted
 10 #              line on the compositor's main thread, tens of MB per session,
 11 #              for a log that is only read when something goes wrong. Start
 12 #              with --verbose when it is.
 13 #   --verbose  compositor at `--log-level debug` (what --logging did before
 14 #              2026-09-11).
 15 #   --debug    --verbose plus WAYLAND_DEBUG=1 on the client.
 16 
 17 LOGGING=false
 18 VERBOSE=false
 19 DEBUG=false
 20 for arg in "$@"; do
 21     case "$arg" in
 22         --logging) LOGGING=true ;;
 23         --verbose) VERBOSE=true ;;
 24         --debug) DEBUG=true ;;
 25     esac
 26 done
 27 
 28 export XDG_RUNTIME_DIR=/run/user/$(id -u)
 29 export XDG_CURRENT_DESKTOP=cce
 30 export XDG_SESSION_TYPE=wayland
 31 export PATH="$HOME/.local/bin:$PATH"
 32 export XCURSOR_THEME="cce"
 33 # No XCURSOR_SIZE. Wayland toolkits already default to 24 logical pixels (sctk,
 34 # GTK and Chromium all do), and the two worlds want DIFFERENT numbers: under
 35 # `xwayland_hidpi` X11 is a physical-pixel world, so an X11 cursor has to be
 36 # 24*scale, which the compositor publishes as the `Xcursor.size` X resource
 37 # when Xwayland comes up (server.rs). XCURSOR_SIZE beats that resource in both
 38 # libXcursor and libxcb-cursor, so exporting it here made every X11 app ask for
 39 # a 24-pixel cursor and get one drawn at half size.
 40 export XCURSOR_PATH="${XDG_DATA_HOME:-$HOME/.local/share}/icons:$HOME/.icons:/usr/share/icons"
 41 # KEEP THIS. Removed on 2026-09-11 (cce-compositor@43a9108) on the theory
 42 # that the Intel cursor plane would work now that the session is pinned to
 43 # card1 and wlroots falls back to software on failure; the very next login
 44 # had NO visible cursor at all — the plane accepted the image and showed
 45 # nothing, so there was no failure to fall back from. A software cursor
 46 # costs a scene composite per pointer motion; an invisible one costs the
 47 # session. Do not remove again without verifying on the real display.
 48 export WLR_NO_HARDWARE_CURSORS=1
 49 export WLR_DRM_DEVICES=/dev/dri/card1
 50 # The session renders on the Intel GPU, so Vulkan is pinned to the Intel ICD.
 51 # This is not only a compositor concern: an app that also loads the NVIDIA
 52 # Vulkan ICD (libGLX_nvidia.so.0) and then asks NVIDIA OpenCL about its
 53 # Mesa GL context segfaults inside the NVIDIA driver — Houdini did, six
 54 # seconds after launch, every time it came from the launcher. It is
 55 # imported into the systemd user manager below for that reason: apps spawned
 56 # by cce-cloud (a user service) inherit the manager's environment, not this
 57 # script's, and without the import they ran under a different GPU setup than
 58 # anything the compositor spawned directly.
 59 export VK_DRIVER_FILES=/usr/share/vulkan/icd.d/intel_icd.json
 60 # Mesa's rusticl exposes NO OpenCL devices unless enabled per driver — without
 61 # this the session has zero OpenCL platforms from Mesa, and every OpenCL
 62 # consumer that wants the Intel GPU fails quietly: the designer's
 63 # kernel-generated geometry (the default project's sphere) simply never
 64 # appears. (The NVIDIA ICD is live too and serves the discrete GPU.)
 65 export RUSTICL_ENABLE=iris
 66 
 67 # The compositor binary this session runs, resolved once so the server launch
 68 # and the generated client launcher cannot disagree. Absolute rather than
 69 # leaning on the PATH set above: this is the one process a session cannot
 70 # recover from failing to find. $HOME is guaranteed — cce-display-manager sets
 71 # it explicitly when it execs this script, and the config read below already
 72 # depends on it.
 73 CCE_BIN="$HOME/.local/bin/cce"
 74 
 75 # Session runtime files: per-user, not /tmp. In /tmp these were a single path
 76 # every user of the machine raced for — whoever logged in first owned
 77 # /tmp/cce.log, and /tmp's sticky bit then denied everyone else, so a second
 78 # user's session logged nothing (or failed to write its launcher at all).
 79 # XDG_RUNTIME_DIR is /run/user/UID, 0700, exported just above. It is cleared
 80 # at logout, which costs nothing here: the compositor log is truncated on
 81 # every login anyway, and a start failure is tailed to the console below
 82 # while the directory still exists.
 83 CCE_RUN="$XDG_RUNTIME_DIR/cce"
 84 if ! mkdir -p "$CCE_RUN" 2>/dev/null; then
 85     # Gate on the failure itself, not on a guess about when it can happen:
 86     # pam_systemd normally creates /run/user/UID before this script runs, but
 87     # if it is missing or unwritable the launcher below cannot be written and
 88     # the session comes up with no client at all. /tmp is worse but it works.
 89     echo "startcce: $CCE_RUN unusable, falling back to /tmp" >&2
 90     CCE_RUN=/tmp
 91 fi
 92 CCE_LOG="$CCE_RUN/cce.log"
 93 CCE_CLIENT_LOG="$CCE_RUN/cce-client.log"
 94 CCE_LAUNCH="$CCE_RUN/launch.sh"
 95 
 96 # The DE's default terminal (config.kdl `default_terminal`, set from the
 97 # settings app) becomes $TERMINAL for the whole session, so non-cce tools
 98 # that honor the convention agree with the launcher. A crude KDL read, but
 99 # the key is a single top-level string node.
100 TERMINAL_CFG=$(sed -n 's/^[[:space:]]*default_terminal[[:space:]]*"\([^"]*\)".*/\1/p' "$HOME/.config/cce/config.kdl" 2>/dev/null | head -n 1)
101 if [ -n "$TERMINAL_CFG" ] && command -v "$TERMINAL_CFG" >/dev/null 2>&1; then
102     export TERMINAL="$TERMINAL_CFG"
103 fi
104 
105 # Kill any stale processes to ensure DRM master and sockets are released.
106 # killall matches on comm (argv[0]'s basename), which reflects how a process
107 # was started, not which binary it is: started through the `cce` symlink comm
108 # is "cce", but a compositor left over from `make run` / `cargo run --bin
109 # cce-fx` has comm "cce-fx" and would survive this sweep still holding DRM
110 # master. So name both. ("cce-client" sat here for years and never matched
111 # anything: there is no such binary — the client is the subcommand
112 # `cce client`, whose comm is also "cce", already covered.)
113 killall -q cce cce-fx 2>/dev/null
114 # pkill -9 -f antigravity 2>/dev/null
115 # pkill -9 -f language_server 2>/dev/null
116 
117 
118 # Create the cce-client launch script
119 DEBUG_FLAG=""
120 if [ "$DEBUG" = true ]; then
121     DEBUG_FLAG="WAYLAND_DEBUG=1 "
122 fi
123 
124 # Unquoted delimiter on purpose: $DEBUG_FLAG, $TERMINAL, $CCE_BIN and
125 # $CCE_CLIENT_LOG expand HERE, so the generated script carries concrete values
126 # and depends on none of this environment. Quoting LAUNCH_EOF would ship the
127 # literal '$CCE_BIN' and the client would never start.
128 cat > "$CCE_LAUNCH" << LAUNCH_EOF
129 #!/bin/sh
130 systemctl --user import-environment DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_TYPE XDG_SESSION_ID XDG_RUNTIME_DIR GNOME_KEYRING_CONTROL SSH_AUTH_SOCK RUSTICL_ENABLE VK_DRIVER_FILES${TERMINAL:+ TERMINAL}
131 systemctl --user start cce-session.target
132 ${DEBUG_FLAG}exec "$CCE_BIN" client 2>"$CCE_CLIENT_LOG"
133 LAUNCH_EOF
134 chmod +x "$CCE_LAUNCH"
135 
136 LOG_LEVEL_FLAG=""
137 if [ "$VERBOSE" = true ] || [ "$DEBUG" = true ]; then
138     LOG_LEVEL_FLAG="--log-level debug"
139 fi
140 if [ "$LOGGING" = true ] || [ "$VERBOSE" = true ] || [ "$DEBUG" = true ]; then
141     echo "Starting cce-server with cce..."
142     echo "  Logs: $CCE_LOG + $CCE_CLIENT_LOG (compositor level: ${LOG_LEVEL_FLAG:---log-level info})"
143     if [ "$DEBUG" = true ]; then
144         echo "  Wayland debug logging enabled (WAYLAND_DEBUG=1)"
145     fi
146 fi
147 
148 if [ "$DEBUG" = true ]; then
149     export WAYLAND_DEBUG=1
150 fi
151 
152 # Clear out any old log file so we only see errors from this run
153 > "$CCE_LOG"
154 
155 # Run CCE in the background so we can check if it exits immediately
156 "$CCE_BIN" ${LOG_LEVEL_FLAG} -c "$CCE_LAUNCH" 2>>"$CCE_LOG" &
157 CCE_PID=$!
158 
159 # Wait briefly to verify it launched successfully
160 sleep 1.2
161 if ! kill -0 $CCE_PID 2>/dev/null; then
162     wait $CCE_PID
163     EXIT_CODE=$?
164     echo ""
165     echo "Error: cce failed to start (exited with code $EXIT_CODE)."
166     echo "Showing last 15 lines of $CCE_LOG:"
167     echo "----------------------------------------"
168     tail -n 15 "$CCE_LOG"
169     echo "----------------------------------------"
170     exit $EXIT_CODE
171 fi
172 
173 # If it runs fine, wait on it to finish
174 wait $CCE_PID
175 systemctl --user stop cce-session.target
176