| 1 | #! /bin/bash |
| 2 | ### |
| 3 | ### X startup script |
| 4 | |
| 5 | ###-------------------------------------------------------------------------- |
| 6 | ### Utility functions. |
| 7 | |
| 8 | ## Progress indicators. |
| 9 | info=yes |
| 10 | info () { |
| 11 | case $info in yes) echo "- $*" >&2 ;; esac |
| 12 | } |
| 13 | |
| 14 | run=yes |
| 15 | run () { |
| 16 | local what=$1; shift |
| 17 | local bg=no |
| 18 | |
| 19 | case $what in bg*) bg=yes what=${what#bg} ;; esac |
| 20 | info "run $what: $*" |
| 21 | |
| 22 | case "$run,$bg" in |
| 23 | yes,no) "$@" ;; |
| 24 | yes,yes) "$@" & ;; |
| 25 | esac |
| 26 | } |
| 27 | |
| 28 | manage () { |
| 29 | local when=$(date +%s) now |
| 30 | local fail=0 rc report |
| 31 | |
| 32 | while :; do |
| 33 | "$@"; rc=$? |
| 34 | case $rc in |
| 35 | 0) info "manage $1: successful exit"; break ;; |
| 36 | 143) info "manage $1: terminated"; break ;; |
| 37 | esac |
| 38 | now=$(date +%s) |
| 39 | report="rc = $rc" |
| 40 | if (( $now - $when > 5 )); then |
| 41 | fail=0 |
| 42 | else |
| 43 | report="$report, early failure" |
| 44 | fail=$(( $fail + 1 )) |
| 45 | if (( $fail >= 5 )); then |
| 46 | info "manage $1: exit ($report), giving up after $fail failures" |
| 47 | break |
| 48 | fi |
| 49 | fi |
| 50 | info "manage $1: exit ($report), restarting" |
| 51 | when=$now |
| 52 | done |
| 53 | } |
| 54 | |
| 55 | ## Program choice |
| 56 | pick_program () { |
| 57 | local what=$1; shift |
| 58 | local choice=false |
| 59 | for i in "$@"; do |
| 60 | if type -t >/dev/null "$i"; then choice=$i; break; fi |
| 61 | done |
| 62 | info "pick $what = $choice" |
| 63 | echo "$choice" |
| 64 | } |
| 65 | |
| 66 | ###-------------------------------------------------------------------------- |
| 67 | ### Parse arguments. |
| 68 | |
| 69 | vnc=no |
| 70 | atomtag= |
| 71 | start=yes |
| 72 | wait=yes |
| 73 | |
| 74 | for opt; do |
| 75 | case "$opt" in |
| 76 | help) |
| 77 | cat <<EOF |
| 78 | Options: |
| 79 | tag=TAG |
| 80 | [no]trace |
| 81 | [no]info |
| 82 | [no]run |
| 83 | [no]start |
| 84 | [no]wait |
| 85 | [no]vnc |
| 86 | EOF |
| 87 | exit |
| 88 | ;; |
| 89 | |
| 90 | tag=*) atomtag=/${opt#tag=} ;; |
| 91 | trace) set -x ;; |
| 92 | notrace) set +x ;; |
| 93 | info | run | start | wait | vnc) eval "$opt=yes" ;; |
| 94 | noinfo | norun | nostart | nowait | novnc) eval "${opt#no}=no" ;; |
| 95 | |
| 96 | *) echo "unknown option $opt" >&2; exit 1 ;; |
| 97 | esac |
| 98 | done |
| 99 | |
| 100 | ###-------------------------------------------------------------------------- |
| 101 | ### Preliminary hook. |
| 102 | |
| 103 | if [ -r $HOME/.xinitrc-prehook ]; then |
| 104 | . $HOME/.xinitrc-prehook |
| 105 | fi |
| 106 | |
| 107 | ###-------------------------------------------------------------------------- |
| 108 | ### Iniitial settings. |
| 109 | |
| 110 | ## Assume X sessions are secure. |
| 111 | export __mdw_sechost="$(hostname)" |
| 112 | |
| 113 | ## Tell things that XFCE is in charge. This is the most desktoppy thing that |
| 114 | ## I run, so it's not entirely wrong. |
| 115 | : ${XDG_CURRENT_DESKTOP=XFCE}; export XDG_CURRENT_DESKTOP |
| 116 | |
| 117 | ## Obtain the screen dimensions. |
| 118 | case ",$XWIDTH,$XHEIGHT," in |
| 119 | *,,*) eval $(xscsize -bx; xscsize -bmx) ;; |
| 120 | esac |
| 121 | case ",$XNSCR," in |
| 122 | ,,) |
| 123 | XNSCR=1 XSCR0_X=0 XSCR0_Y=0 XSCR0_WIDTH=$XWIDTH XSCR0_HEIGHT=$XHEIGHT |
| 124 | ;; |
| 125 | esac |
| 126 | i=0; while (( i < XNSCR )); do |
| 127 | eval "x=\$XSCR${i}_X y=\$XSCR${i}_Y |
| 128 | wd=\$XSCR${i}_WIDTH ht=\$XSCR${i}_HEIGHT" |
| 129 | info "screen #$i = ${wd}x${ht}+${x}+${y}" |
| 130 | i=$(( i + 1 )) |
| 131 | done |
| 132 | |
| 133 | initialize () { |
| 134 | ## Load the X resource database. |
| 135 | run init xrdb -override $HOME/.Xdefaults |
| 136 | |
| 137 | ## Random xsettery. |
| 138 | run init xset b 5 2000 50 |
| 139 | run init xset r rate 500 50 |
| 140 | run init xset m 2 1 |
| 141 | |
| 142 | ## Unclutter. |
| 143 | if type -t >/dev/null unclutter; then run bgclients unclutter -root -idle 7; fi |
| 144 | |
| 145 | ## Key mappings. |
| 146 | xmodmap $HOME/.xmodmap |
| 147 | if [ -r $HOME/.xmodmap-local ]; then |
| 148 | xmodmap $HOME/.xmodmap-local |
| 149 | fi |
| 150 | } |
| 151 | |
| 152 | ###-------------------------------------------------------------------------- |
| 153 | ### Start a window manager. |
| 154 | |
| 155 | wm=$(pick_program window-manager e16 compiz enlightenment e17 twm) |
| 156 | wmopts="" |
| 157 | case "$wm,$vnc" in |
| 158 | enlightenment,yes | e16,yes) |
| 159 | wmopts="$eopts -econfdir $HOME/.enlightenment-vnc" |
| 160 | ;; |
| 161 | esac |
| 162 | |
| 163 | start-e16 () { |
| 164 | run bginit manage $wm $wmopts |
| 165 | win=nil |
| 166 | for i in $(seq 10); do |
| 167 | sleep 1 |
| 168 | if eesh version >/dev/null 2>&1; then |
| 169 | win=t |
| 170 | break |
| 171 | fi |
| 172 | done |
| 173 | case $win in |
| 174 | t) |
| 175 | info "$wm started ok" |
| 176 | run init xsetroot -cursor_name left_ptr |
| 177 | ;; |
| 178 | nil) |
| 179 | info "$wm failed to start!" |
| 180 | ;; |
| 181 | esac |
| 182 | } |
| 183 | |
| 184 | start-window-manager () { |
| 185 | case $(type -t start-$wm || echo "not-found") in |
| 186 | function) |
| 187 | start-$wm $wmopts |
| 188 | ;; |
| 189 | *) |
| 190 | run bginit manage $wm $wmopts |
| 191 | ;; |
| 192 | esac |
| 193 | } |
| 194 | |
| 195 | ###-------------------------------------------------------------------------- |
| 196 | ### Random useful clients. |
| 197 | |
| 198 | start-clients-local () { :; } |
| 199 | |
| 200 | start-clients () { |
| 201 | |
| 202 | ## Gnome session. |
| 203 | case "$vnc,$(xfce4-session --version 2>&1),$(gnome-session --version 2>&1)" |
| 204 | in |
| 205 | no,xfce4-session*) |
| 206 | run bginit xfce4-session |
| 207 | ;; |
| 208 | no,*,gnome-session\ 2.3[2-9].* | \ |
| 209 | no,*,gnome-session\ 2.4[0-9].* | \ |
| 210 | no,*,gnome-session\ 2.[1-9][0-9][0-9]*) |
| 211 | run bginit gnome-session --session mdw |
| 212 | ;; |
| 213 | no,*,gnome-session*) |
| 214 | run bginit gnome-session |
| 215 | ;; |
| 216 | esac |
| 217 | |
| 218 | ## Local clients. |
| 219 | start-clients-local |
| 220 | } |
| 221 | |
| 222 | ###-------------------------------------------------------------------------- |
| 223 | ### Main screen layout. |
| 224 | |
| 225 | ## Choose appropriate clients. |
| 226 | emacs=$(pick_program emacs \ |
| 227 | emacs24-lucid emacs23-lucid emacs24 emacs23 emacs22 emacs21 \ |
| 228 | emacs-lucid emacs) |
| 229 | term=$(pick_program terminal pterm Eterm xterm) |
| 230 | |
| 231 | ## If we fell back to an unversioned Emacs binary, then figure out what |
| 232 | ## version it actually is. |
| 233 | case $emacs in |
| 234 | emacs | emacs-lucid) |
| 235 | set -- $($emacs --version | head -n1) |
| 236 | e_ver=emacs${3%%.*}${emacs#emacs} |
| 237 | ;; |
| 238 | *) |
| 239 | e_ver=$emacs |
| 240 | ;; |
| 241 | esac |
| 242 | info emacs-version = $e_ver |
| 243 | |
| 244 | ## Emacs window measurements. |
| 245 | ## |
| 246 | ## e_colsz = width of a column in characters (from `emacs-width' metaconfig) |
| 247 | ## e_charwd = width of a character in pixels (assume `6x13') |
| 248 | ## e_colextra = additional per-column overhead in pixels |
| 249 | ## e_colextrachars = additional per-column overhead in character units |
| 250 | ## e_colwd = basic width of a column in pixels |
| 251 | ## e_hextra = extra horizontal width in pixels |
| 252 | ## Width of an N-column Emacs frame in pixels will be |
| 253 | ## N*e_colwd + e_hextra |
| 254 | ## e_colchars = width of a column in Emacs `-geometry' units |
| 255 | ## e_cextra = extra horizontal width in Emacs `-geometry' units |
| 256 | ## So an N-column frame should be reported to Emacs as being |
| 257 | ## N*e_colchars + e_cextra geometry units wide |
| 258 | ## e_lineht = height of a character line in pixels |
| 259 | ## e_vextra = number of additional vertical cruft pixels |
| 260 | ## So an N-line Emacs frame takes N*e_lineht + e_vextra pixels |
| 261 | e_colsz=$(mdw-conf emacs-width 77) e_charwd=6 |
| 262 | e_colextra=30 e_colextrachars=5 e_lineht=13 |
| 263 | case "$e_ver" in |
| 264 | emacs21 | emacs) e_hextra=34 e_cextra=-2 e_vextra=52 ;; |
| 265 | emacs22 | emacs23) e_hextra=8 e_cextra=-6 e_vextra=46 ;; |
| 266 | emacs24) e_hextra=5 e_cextra=-6 e_vextra=42 ;; |
| 267 | emacs23-lucid) e_hextra=7 e_cextra=-6 e_vextra=48 ;; |
| 268 | emacs24-lucid) e_hextra=7 e_cextra=-5 e_vextra=48 ;; |
| 269 | emacs26-lucid) e_hextra=5 e_cextra=-6 e_vextra=51 ;; |
| 270 | esac |
| 271 | e_colwd=$(( e_colsz*e_charwd + e_colextra )) |
| 272 | e_colchars=$(( e_colsz + e_colextrachars )) |
| 273 | |
| 274 | ## Terminal window measurements. |
| 275 | ## |
| 276 | ## t_wd = the window width, in pixels |
| 277 | ## t_lineht, t_vextra = height parameters: if the window is N lines high, |
| 278 | ## then it will be N*t_lineht + t_vextra pixels high |
| 279 | case "$term" in |
| 280 | pterm) |
| 281 | ## The pterm width differs according to whether it's linked against Gtk 2 |
| 282 | ## or 3. Let's find out... |
| 283 | case $(ldd $(command -v pterm) | grep libgtk) in |
| 284 | *libgtk-2* | *libgtk-x11-2*) t_wd=504 ;; |
| 285 | *) t_wd=503 ;; |
| 286 | esac |
| 287 | t_lineht=13 t_vextra=23 geom=-geometry;; |
| 288 | Eterm) t_wd=504 t_lineht=13 t_vextra=23 geom=-g;; |
| 289 | xterm) t_wd=507 t_lineht=13 t_vextra=27 geom=-geometry;; |
| 290 | esac |
| 291 | |
| 292 | ## GNOME stuff measurements. |
| 293 | declare -i panelwd=64 xbound=$(( XWIDTH - panelwd )) |
| 294 | |
| 295 | ## Choose a width for Emacs. |
| 296 | ## |
| 297 | ## We're going to make a single frame of Emacs, which will be automatically |
| 298 | ## divvied into columns; the rest of the space will be tiled with xterms. If |
| 299 | ## we have multiple screens, then Emacs can have the first screen and we'll |
| 300 | ## fill the rest with terminals. Otherwise, we'll try to have N columns of |
| 301 | ## Emacs, and N - 1 columns of xterms; if that doesn't work, we'll make do |
| 302 | ## with N - 2 columns of xterms. |
| 303 | ## |
| 304 | ## So, let W be the available width; let E and T be the widths of an Emacs |
| 305 | ## column (e_colwd) and terminal (t_wd), respectively, and let E0 be the |
| 306 | ## additive width of Emacs's existence (e_hextra). If we have more than one |
| 307 | ## screen, then instead, let T be zero. To start out, then, we let N = 1 + |
| 308 | ## floor((W - E - E0)/(E + T)). If W - E0 - (N + 1) E - (N - 1)*T >= 0, then |
| 309 | ## we increase N by one. |
| 310 | declare -i lim=XSCR0_WIDTH |
| 311 | if (( lim > xbound )); then lim=xbound; fi |
| 312 | declare -i twd=t_wd; if (( XNSCR > 1 )); then twd=0; fi |
| 313 | declare -i ecols=$(( (lim - e_colwd - e_hextra)/(e_colwd + twd) + 1 )) |
| 314 | if (( lim - e_hextra - (ecols + 1)*e_colwd - (ecols - 1)*twd >= 0 )); then |
| 315 | ecols=$(( ecols + 1 )) |
| 316 | fi |
| 317 | |
| 318 | declare -i \ |
| 319 | emacsx=$(( ecols*e_colchars + e_cextra )) \ |
| 320 | emacsy=$(( (XHEIGHT - e_vextra)/e_lineht )) |
| 321 | |
| 322 | start-emacs () { |
| 323 | GDK_NATIVE_WINDOWS=1 run bgclients noip \ |
| 324 | $emacs -bg black -geometry ${emacsx}x${emacsy}+${XSCR0_X}+${XSCR0_Y} \ |
| 325 | --mdw-splashy-startup |
| 326 | } |
| 327 | |
| 328 | ## Now place some xterms. |
| 329 | ## |
| 330 | ## A few smaller xterms are in general better than one great big one. 35 |
| 331 | ## lines is a good height for most terminals. 25 lines is a minimum. The |
| 332 | ## strategy for doling out xterms into a column is to make as many 35-liners |
| 333 | ## as we can, until the remaining space would be too small for a 25-liner. |
| 334 | ## If we can get two 25s out of that then we do (largest first); otherwise |
| 335 | ## just make one big one. We stop at the end of a page, once we've made |
| 336 | ## three xterms. |
| 337 | |
| 338 | start-xterms () { |
| 339 | |
| 340 | ## Initialize some parameters. |
| 341 | declare -i x=$(( ecols*e_colwd + e_hextra + XSCR0_X )) xb=xbound |
| 342 | declare -i n=0 pgx=0 l h y ht scr=0 ll=lim |
| 343 | declare -i hstd=$(( 35*t_lineht + t_vextra )) |
| 344 | declare -i hmin=$(( 25*t_lineht + t_vextra )) |
| 345 | declare -i scrx scry scrwd scrht |
| 346 | |
| 347 | eval "scrx=\$XSCR${scr}_X scry=\$XSCR${scr}_Y |
| 348 | scrwd=\$XSCR${scr}_WIDTH scrht=\$XSCR${scr}_HEIGHT" |
| 349 | |
| 350 | ## Do the placement. |
| 351 | while :; do |
| 352 | |
| 353 | ## Start a new iteration. |
| 354 | if (( x + t_wd > ll )); then |
| 355 | scr=$(( scr + 1 )) |
| 356 | if (( scr >= XNSCR )); then |
| 357 | if (( n >= 3 )); then break; fi |
| 358 | pgx=$(( pgx + XWIDTH )) xb=$(( xb + XWIDTH )) |
| 359 | scr=0 |
| 360 | fi |
| 361 | eval "scrx=\$XSCR${scr}_X scry=\$XSCR${scr}_Y |
| 362 | scrwd=\$XSCR${scr}_WIDTH scrht=\$XSCR${scr}_HEIGHT" |
| 363 | x=$(( pgx + scrx )) |
| 364 | ll=$(( x + scrwd )) |
| 365 | if (( ll > xb )); then ll=xb; fi |
| 366 | fi |
| 367 | |
| 368 | ## Make large xterms. |
| 369 | y=scry ht=scrht |
| 370 | while (( ht - hstd >= hmin )); do |
| 371 | run bgclients $term $geom 80x35+$x+$y |
| 372 | y=$(( y + hstd )) ht=$(( ht - hstd )) n=$(( n + 1 )) |
| 373 | done |
| 374 | |
| 375 | ## Fill the remaining space. |
| 376 | if (( ht >= 2*hmin )); then h=$(( ht - hmin )); else h=ht; fi |
| 377 | l=$(( (h - t_vextra)/t_lineht )) h=$(( l*t_lineht + t_vextra )) |
| 378 | run bgclients $term $geom 80x$l+$x+$y |
| 379 | y=$(( y + h )) ht=$(( ht - h )) n=$(( n + 1 )) |
| 380 | if ((ht >= hmin)); then |
| 381 | run bgclients $term $geom 80x25+$x+$y |
| 382 | n=$(( n + 1 )) |
| 383 | fi |
| 384 | x=$(( x + t_wd )) |
| 385 | done |
| 386 | } |
| 387 | |
| 388 | ###-------------------------------------------------------------------------- |
| 389 | ### Requesters. |
| 390 | |
| 391 | req () { |
| 392 | declare title=$1 hist=$2; shift 2 |
| 393 | cmd=$(xgetline -t "$title" -p "_Command:" -Hl "$HOME/$hist") && |
| 394 | exec "$@" "$cmd" |
| 395 | } |
| 396 | |
| 397 | ###-------------------------------------------------------------------------- |
| 398 | ### Final waiting. |
| 399 | |
| 400 | atom=XINIT_COMMAND$atomtag |
| 401 | |
| 402 | xwait () { |
| 403 | while :; do |
| 404 | xatom delete $atom |
| 405 | info "waiting on $atom" |
| 406 | line=$(xatom wait $atom) |
| 407 | info "xatom: $line" |
| 408 | |
| 409 | case "$line" in |
| 410 | :help) |
| 411 | xmsg -I -t "xinitrc help" -d "xinitrc commands" - <<EOF & |
| 412 | :help |
| 413 | :emacs :xterms :window-manager :clients |
| 414 | :ask-run :ask-command |
| 415 | :init |
| 416 | :terminal |
| 417 | ! SHELL-COMMAND |
| 418 | CLIENT |
| 419 | EOF |
| 420 | ;; |
| 421 | :emacs | :xterms | :window-manager | :clients) |
| 422 | start-${line#:} |
| 423 | ;; |
| 424 | :terminal) |
| 425 | run bgclients $term |
| 426 | ;; |
| 427 | :init) |
| 428 | initialize |
| 429 | ;; |
| 430 | :exec) |
| 431 | info "restarting xinitrc" |
| 432 | exec "$0" wait nostart |
| 433 | ;; |
| 434 | :ask-run) |
| 435 | req "Shell command" .cmd.hist xcatch -F"Fixed 13" -- sh -c& |
| 436 | ;; |
| 437 | :ask-command) |
| 438 | req "xinit command" .xinit.hist xatom set XINIT_COMMAND$atomtag& |
| 439 | ;; |
| 440 | :*) |
| 441 | xmsg -E -t "xinitrc error" "Unknown command \`$line'" & |
| 442 | ;; |
| 443 | !*) |
| 444 | eval "${line#!}" |
| 445 | ;; |
| 446 | *) |
| 447 | eval "set -- $line" |
| 448 | run bgclients "$@" |
| 449 | ;; |
| 450 | esac |
| 451 | done |
| 452 | } |
| 453 | |
| 454 | ###-------------------------------------------------------------------------- |
| 455 | ### Gnome session care and feeding. |
| 456 | |
| 457 | session-running-p () { |
| 458 | dbus-send --session --print-reply --dest=org.freedesktop.DBus / \ |
| 459 | org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager \ |
| 460 | >/dev/null 2>&1 |
| 461 | } |
| 462 | |
| 463 | dbus-service-running-p () { |
| 464 | dbus-send >/dev/null 2>&1 --session --print-reply \ |
| 465 | --dest=org.freedesktop.DBus / \ |
| 466 | org.freedesktop.DBus.GetNameOwner string:$1 |
| 467 | } |
| 468 | |
| 469 | kill-gnome-session () { |
| 470 | win=nil |
| 471 | while read service object logout; do |
| 472 | if dbus-service-running-p $service; then win=t; break; fi |
| 473 | done <<EOF |
| 474 | org.xfce.SessionManager /org/xfce/SessionManager org.xfce.Session.Manager.Shutdown uint32:1 boolean:false |
| 475 | org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.Logout uint32:2 |
| 476 | EOF |
| 477 | case $win in nil) return ;; esac |
| 478 | info "killing session manager" |
| 479 | dbus-send --session --dest=$service $object $logout |
| 480 | for i in 1 2 3 4 5; do |
| 481 | sleep 1 |
| 482 | if ! dbus-service-running-p $service; then break; fi |
| 483 | done |
| 484 | } |
| 485 | |
| 486 | ###-------------------------------------------------------------------------- |
| 487 | ### Actually start things up. |
| 488 | |
| 489 | if [ -f $HOME/.xinitrc-local ]; then |
| 490 | . $HOME/.xinitrc-local |
| 491 | fi |
| 492 | |
| 493 | case "$start" in |
| 494 | yes) |
| 495 | info "starting standard clients" |
| 496 | initialize |
| 497 | start-window-manager |
| 498 | start-clients |
| 499 | start-emacs |
| 500 | start-xterms |
| 501 | ;; |
| 502 | no) |
| 503 | info "not starting standard clients" |
| 504 | ;; |
| 505 | esac |
| 506 | |
| 507 | case "$wait" in |
| 508 | yes) |
| 509 | xwait |
| 510 | kill-gnome-session |
| 511 | ;; |
| 512 | no) |
| 513 | info "not waiting before exit" |
| 514 | ;; |
| 515 | esac |
| 516 | |
| 517 | ###----- That's all, folks -------------------------------------------------- |