]> git.plutz.net Git - x11sh/commitdiff
legacy x11 client and interface file master
authorPaul Hänsch <paul@plutz.net>
Wed, 7 Jan 2026 12:13:49 +0000 (13:13 +0100)
committerPaul Hänsch <paul@plutz.net>
Wed, 7 Jan 2026 12:13:49 +0000 (13:13 +0100)
interface [new file with mode: 0644]
x11client.sh [new file with mode: 0755]

diff --git a/interface b/interface
new file mode 100644 (file)
index 0000000..9d51125
--- /dev/null
+++ b/interface
@@ -0,0 +1,21 @@
+win 300
+  direction SE
+  button
+    text OK
+  button
+    color 00008800
+    text <<Button>>
+    stroke 3
+    line 10 13 70 13
+    color 00000088
+  button
+    text 3rd one
+  dir NW
+  button
+    text Menu1
+  button
+    text Menu2
+  dir SW
+  button
+    text foo
+
diff --git a/x11client.sh b/x11client.sh
new file mode 100755 (executable)
index 0000000..e74798c
--- /dev/null
@@ -0,0 +1,482 @@
+#!/bin/busybox ash
+
+padding=10
+
+oct8() {
+  [ $1 -lt 0 ] && int=$(($1 + 256)) || int=$1
+  printf '\\0%s' "$(($1 / 64))$(($1 % 64 / 8))$(($1 % 8))"
+}
+oct16(){
+  [ $1 -lt 0 ] && int=$(($1 + 65536)) || int=$1
+  printf '%s%s' "$(oct8 $(($int / 256)))" \
+               "$(oct8 $(($int % 256)))"
+}
+oct32(){
+  [ $1 -lt 0 ] && int=$(($1 + 4294967296)) || int=$1
+  printf '%s%s' "$(oct16 $(($int / 65536)))" \
+               "$(oct16 $(($int % 65536)))"
+}
+
+if info=$(mktemp -td x11sh.XXXXXX); then
+  info="$info/conninfo"
+  fifo="${info%/*}/fifo"
+  mkfifo "$fifo"
+  trap \
+    '[ -f "$info" -a -p "$fifo" ] && rm "$info" "$fifo" && rmdir "${info%/*}"' \
+    EXIT INT TERM KILL
+else
+  printf 'failed to set up temp dir\n' >&2
+  exit 1
+fi
+
+format_info(){
+  fmt="$1"
+  while [ -n "$fmt" ]; do
+    printf 'Format: depth=%2s bpp=%2s slp=%s\n' \
+       $(( 0x${fmt:0:2} )) $(( 0x${fmt:2:2} )) $(( 0x${fmt:4:2} ))
+    fmt="${fmt:16}"
+  done
+}
+
+screen_info(){
+  scrns="$1"
+  scr="$2"
+
+  while [ $scrns -gt 0 ]; do
+    printf 'Screen: root=%s %sx%s:%s\n' \
+       0x${scr:0:8} $((0x${scr:40:4})) $((0x${scr:44:4})) $((0x${scr:76:2}))
+    printf 'ROOT=%s\n'  $((0x${scr:0:8}))  >>"$info"
+    printf 'DEPTH=%s\n' $((0x${scr:76:2})) >>"$info"
+
+    dpths=$((0x${scr:78:2}))
+    dptho=80
+    while [ $dpths -gt 0 ]; do
+      #printf '  depth=%s\n' $((0x${scr:${dptho}:2}))
+      dpthl=$(( 16 + 48 * 0x${scr:$(($dptho + 4)):4} ))
+      dptho=$(($dptho + $dpthl))
+      dpths=$(($dpths - 1))
+    done
+    scrns=$(($scrns - 1))
+    scr="${scr:${dptho}}"
+  done
+}
+
+server_info(){
+  msg="$1"
+
+  vl=$(( 0x${msg:48:4} * 2))
+  vp=$(( (8 - $vl % 8) % 8))
+
+  fmto=$(( $vl + $vp + 80 ))
+  fmtl=$(( 0x${msg:58:2} * 16 ))
+
+  scro=$(( $fmto + $fmtl ))
+
+  printf 'X-Protocol: %s.%s r%s\n' \
+    $(( 0x${msg:4:4} )) $(( 0x${msg:8:4} )) $(( 0x${msg:16:8} ))
+  printf 'RessourceID: 0x%s / 0x%s\n' ${msg:24:8} ${msg:32:8}
+  printf "Vendor: $(printf %s "${msg:80:$vl}" |sed 's;..;\\x&;g')\n"
+  printf "RID=%s\n" $((0x${msg:24:8})) >"$info"
+  format_info "${msg:${fmto}:${fmtl}}"
+  screen_info $((0x${msg:56:2})) "${msg:${scro}}"
+}
+
+event_id(){
+  case $1 in
+     2) printf KeyPress ;;
+     3) printf KeyRelease ;;
+     4) printf ButtonPress ;;
+     5) printf ButtonRelease ;;
+     6) printf MotionNotify ;;
+     7) printf EnterNotify ;;
+     8) printf LeaveNotify ;;
+     9) printf FocusIn ;;
+    10) printf FocusOut ;;
+    11) printf KeymapNotify ;;
+    12) printf Expose ;;
+    15) printf VisibilityNotify ;;
+    19) printf MapNotify ;;
+    28) printf PropertyNotify ;;
+     *) printf ' %s ' "$1" ;;
+  esac
+}
+
+op_id(){
+  case $1 in
+     1) printf CreateWindow ;;
+     2) printf ChangeWindow ;;
+     8) printf MapWindow ;;
+    55) printf CreateGC ;;
+    56) printf ChangeGC ;;
+     *) printf ' %s ' "$1" ;;
+  esac
+}
+
+error_id(){
+  case $1 in
+     1) printf Request ;;
+     2) printf Value ;;
+     3) printf Window ;;
+     4) printf Pixmap ;;
+     5) printf Atom ;;
+     6) printf Cursor ;;
+     7) printf Font ;;
+     8) printf Match ;;
+     9) printf Drawable ;;
+    10) printf Access ;;
+    11) printf Alloc ;;
+    12) printf ColorMap ;;
+    13) printf GContext ;;
+    14) printf IDChoice ;;
+    15) printf Name ;;
+    16) printf Length ;;
+    17) printf Implementation ;;
+     *) printf ' %s ' "$1" ;;
+  esac
+}
+
+text_render(){
+  widget="$1" gc="$2"
+  x="$3"; y="$4"; text="$5"
+  l=${#text}
+  while [ $(( ${#text} % 4 )) -gt 0 ]; do
+    text="${text}~"
+  done
+
+  printf '\\0114%s%s%s%s%s%s%s' \
+    $(oct8 $l) $(oct16 $((4 + ${#text} / 4))) \
+    "$widget" "$gc" $(oct16 $x) $(oct16 $y) \
+    "${text}"
+}
+
+case $DISPLAY in
+  '')
+    printf 'No DISPLAY variable, no server to connect to\n' >&2
+    exit 1;;
+  :[0-9]*)
+    unix=${DISPLAY#:}
+    unix="/tmp/.X11-unix/X${unix%.*}"
+    [ -S "$unix" ] || {
+      printf 'could not connect to server at %s\n' "$unix" >&2
+      exit 1
+    };;
+  *:[0-9]*)
+    host=${DISPLAY%:*}
+    port=${DISPLAY#*:}
+    port=$((6000 + ${port%.*}))
+    ;;
+esac
+
+widget(){
+  def="${1##* }"
+  printf '%s' "$(oct32 ${def%%:*})"
+}
+
+set_direction(){
+  stack="${1%:*}"
+  direction="$2"
+  printf '%s:%s%s' "$stack" "$direction" "$padding"
+}
+
+resize(){
+  cstack="$1"; widget="$2"; nw="$3"; nh="$4";
+  if [ "$(widget "$widget")" = "$(widget "$cstack")" ]; then
+    # never resize containers
+    printf '%s %s' "$widget" "$cstack"
+  else
+    dim="${widget#*:}"; dim="${dim%:*}"
+    cw="${dim%x*}"; ch="${dim#*x}"
+    case $nw in
+      +*) nw=$(($cw + ${nw#+}));;
+      -*) nw=$(($cw - ${nw#-}));;
+    esac
+    case $nh in
+      +*) nh=$(($ch + ${nh#+}));;
+      -*) nh=$(($ch - ${nh#-}));;
+    esac
+
+    offset=${cstack##*[NEMWS]}
+    case ${cstack##* } in
+       *[WE]*) no=$(($offset - $cw - $padding));;
+       *[NS]*) no=$(($offset - $ch - $padding));;
+       *M*) no=${padding};;
+    esac
+
+    read nx ny g cstack <<-EOF
+       $(position $nw $nh ${cstack%$offset}${no})
+       EOF
+
+    mapmsg="\\0014~\\0000\\0007$(oct32 ${widget%%:*})\\0000\\0017\\0000\\0000$(oct32 $nx)$(oct32 $ny)$(oct32 $nw)$(oct32 $nh)"
+    printf '%s %s:%sx%s:%s %s' "$mapmsg" \
+      ${widget%%:*} $nw $nh ${widget##*:} "$cstack"
+  fi
+}
+
+position(){
+  w="$1" h="$2"
+  def="${3##* }"
+  stack="${3% *}"
+  container="${def%%:*}"
+  meta="${def#*:}"
+  dir="${meta#*:}"; dir="${dir%%[0-9]*}"
+  offset="${meta##*[NEMWS]}"
+  dim="${meta%:*}"
+  cw="${dim%x*}"; ch="${dim#*x}"
+
+  [ "$stack" = "$def" ] && stack='' || stack="$stack "
+
+  case $def in
+    *NW*)
+       y="$padding"
+       x="$offset"
+       offset="$(( $offset + $w + $padding))"
+       grav=1
+       ;;
+    *NE*)
+       y="$padding"
+       x="$(( $cw - $offset - $w))"
+       offset="$(( $offset + $w + $padding))"
+       grav=3
+       ;;
+    *N*)
+       y="$offset"
+       x="$(( $cw / 2 - $w / 2))"
+       offset="$(( $offset + $h + $padding))"
+       grav=2
+       ;;
+    *SW*)
+       y="$(( $ch - $padding - $h ))"
+       x="$offset"
+       offset="$(( $offset + $w + $padding))"
+       grav=7
+       ;;
+    *SE*)
+       y="$(( $ch - $padding - $h ))"
+       x="$(( $cw - $offset - $w ))"
+       offset="$(( $offset + $w + $padding))"
+       grav=9
+       ;;
+    *S*)
+       y="$(( $ch - $offset - $h ))"
+       x="$(( $cw / 2 - $w / 2 ))"
+       offset="$(( $offset + $h + $padding))"
+       grav=8
+       ;;
+    *W*)
+       y=$(( $ch / 2 - $h / 2))
+       x="$offset"
+       offset="$(( $offset + $w + $padding))"
+       grav=4
+       ;;
+    *M*)
+       y=$(( $ch / 2 - $h / 2))
+       x="$(( $cw / 2 - $w / 2 ))"
+       grav=5
+       ;;
+    *E*)
+       y=$(( $ch / 2 - $h / 2))
+       x="$(( $cw - $offset - $w ))"
+       offset="$(( $offset + $w + $padding))"
+       grav=6
+       ;;
+  esac
+
+  printf '%s %s %s %s%s:%sx%s:%s%s' "$x" "$y" "$grav" \
+       "$stack" "$container" "$cw" "$ch" "$dir" "$offset"
+}
+
+rid=0
+replay=''
+msg=''
+
+{ printf 'B~\000\013\000\000\000\000\000\000~~'
+  while ! grep -q DEPTH "$info"; do
+    sleep 1
+  done
+  . "$info"
+  ROOT=$(oct32 $ROOT)
+  
+  rid=$(($rid + 1))
+  gc="$(oct32 $(( $RID + $rid)))"
+  printf '\067~\000\004%b%b\000\000\000\000' ${gc} ${ROOT}
+  replay="\070~\000\005${gc}\000\000\000\014"
+  replay="${replay}\000\000\000\000"
+  replay="${replay}\000\377\377\377"
+  printf %b "$replay"
+
+  while read -r cmd p1 p2 p3 p4; do
+    case "$cmd" in
+      env)
+       env >&2;;
+      dir*)
+       cstack=$(set_direction "$cstack" "$p1")
+       ;;
+      win*)
+       rid=$(($rid + 1))
+       win="$(( $RID + $rid))"
+       w=${p1:-100}
+       h=${p2:-100}
+
+       printf '\001\000\000\013%b%b%b%b%b%b\000\000\000\001\000\000\000\000\000\000\010\102%b%b%b' \
+               $(oct32 ${win}) ${ROOT} \
+               $(oct16 10) $(oct16 10) \
+               $(oct16 ${w}) $(oct16 ${h}) \
+               '\x00\xff\xff\xff' '\000\000\000\002' \
+               '\000\000\200\000'
+       mapmsg="${mapmsg}\\0010~\\0000\\0002$(oct32 ${win})"
+       widget="${win}:${w}x${h}:NW10"
+       cstack="${cstack:+$cstack }${widget}"
+       ;;
+      btn*|button*)
+       rid=$(($rid + 1))
+       btn="$(( $RID + $rid))"
+        text="${p1}${p2:+ $p2}${p3:+ $p3}${p4:+ $p4}"
+       w=$((2 * $padding)); h=16
+       read x y grav cstack <<-EOF
+       $(position "$w" "$h" "$cstack")
+       EOF
+       printf '\001\000\000\015%b%b%b%b%b%b\000\001\000\001\000\000\000\000\000\000\010\152%b%b%b%b%b' \
+               $(oct32 ${btn}) $(widget "${cstack}") \
+               $(oct16 $x) $(oct16 $y) \
+               $(oct16 $w) $(oct16 $h) \
+               '\000\377\377\377' '\000\000\000\000' \
+               $(oct32 $grav) '\000\000\000\002' \
+               '\000\000\000\014'
+       mapmsg="${mapmsg}\\0010~\\0000\\0002$(oct32 ${btn})"
+       widget="${btn}:${w}x${h}:W10"
+       ;;
+      col*)
+       msg="\070~\000\004${gc}\000\000\000\004$(oct32 $((0x${p1})))"
+       ;;
+      bg*|back*)
+       msg="\070~\000\004${gc}\000\000\000\010$(oct32 $((0x${p1})))"
+       ;;
+      text*)
+       text="${p1}${p2:+ $p2}${p3:+ $p3}${p4:+ $p4}"
+       read x y grav widget <<-EOF
+       $(position $((6 * ${#text})) 8 "$widget")
+       EOF
+
+       read -r mm widget cstack <<-EOF
+       $(resize "$cstack" "$widget" +$((6 * ${#text})) +0)
+       EOF
+
+       mapmsg="${mm}${mapmsg}"
+
+       msg="$(
+         text_render "$(widget $widget)" "$gc" \
+           $x $(($y + 8)) "$text"
+       )"
+       ;;
+      font*)
+       spec="$p1"
+       l=${#spec}
+       while [ $(( ${#spec} % 4 )) -gt 0 ]; do spec="${spec}~"; done
+       rid=$(($rid + 1))
+       font="$(oct32 $(( $RID + $rid)))"
+       printf '\055~%b%b%b~~%b' $(oct16 $((3 + ${#spec} / 4))) \
+               ${font} $(oct16 $l) "$spec"
+       msg="\070~\000\004${gc}\000\000\040\000${font}"
+       ;;
+      stroke*)
+       msg="\070~\000\006${gc}\000\000\000\160"
+       msg="${msg}$(oct32 ${p1:-1})$(oct32 ${p2:-0})$(oct32 ${p3:-2})"
+       ;;
+      line*)
+       d="$(widget $widget)"
+       msg="\101\000\000\005${d}${gc}"
+       msg="${msg}$(oct16 ${p1:-0})$(oct16 ${p2:-0})"
+       msg="${msg}$(oct16 ${p3:-0})$(oct16 ${p4:-0})"
+       ;;
+      *) printf %b "$cmd"
+       ;;
+    esac
+    if [ -n "${msg}" ]; then
+      printf %b "$msg"
+      replay="${replay}${msg}"
+      msg=''
+    fi
+  done
+
+  printf %b "$mapmsg"
+
+  while true; do
+    read -r cmd <"$fifo"
+    case $cmd in
+      redraw*)
+       printf %b "${replay}"
+       ;;
+      quit*) exit 0
+       ;;
+      *) printf %b "$cmd"
+       ;;
+    esac
+  done
+} \
+| if [ -n "$unix" ]; then
+  stdbuf -o0 ncat -U "$unix" || {
+    printf 'Cannot connect to %s\n' "$unix" >&2
+    exit 1
+  }
+else 
+  stdbuf -o0 ncat "$host" "$port" || {
+    printf 'Cannot connect to %s:%s\n' "$host" "$port" >&2
+    exit 1
+  }
+fi \
+| stdbuf -o0 hexdump -ve '/1 "%02x"' \
+| while read -n16 msg; do
+  case "$msg" in
+    0[2-9a-f]*|[1-9a-f]*) # Events, will be the most frequent match, handled below
+       read -n48 app
+       msg="${msg}${app}"
+       ;;
+    00*) # Error messages
+       read -n48 app
+       msg="${msg}${app}"
+       printf 'Error (%s): seq=%s op=%s( %s ) info=%s\n' \
+       $(error_id $((0x${msg:2:2})) ) $((0x${msg:4:4})) \
+       $(op_id $((0x${msg:20:2})) ) $((0x${msg:16:4})) \
+       $((0x${msg:8:8}))
+      ;;
+    01??000b0000*) # Connection initialisation, very first server reply
+       l=$((0x${msg:12:4} * 8))
+       read -n$l app
+       msg="${msg}${app}"
+       server_info "$msg"
+       . "$info"
+       ;;
+    01*) # Reply to request
+       l=$((48 + 0x${msg:8} * 8))
+       read -n$l app
+       msg="${msg}${app}"
+       printf 'Reply:\n%s\n' "$msg"
+       printf "[$(printf "$msg" |sed 's;..;\\x&;g')]\n"
+       ;;
+  esac
+  case "$msg" in
+    0[2-8]*)
+       printf 'Event (%s): %s %sx%s widget=%s\n' \
+       $(event_id $((0x${msg:0:2}))) $((0x${msg:2:2})) \
+       $((0x${msg:48:4})) $((0x${msg:52:4})) \
+       $((0x${msg:24:8} - $RID))
+       ;;
+    0[9a]*)
+       printf 'Event (%s)\n' $(event_id $((0x${msg:0:2})))
+       ;;
+    0b*)
+       printf 'Event (%s):' $(event_id $((0x${msg:0:2})))
+       for n in $(seq 2 2 62); do
+         printf ' %s' $(( 0x${msg:$n:2} ))
+       done
+       printf '\n'
+       ;;
+    0c*) # Expose Event
+       [ "${msg:32:4}" = 0000 ] \
+       && printf 'redraw\n' >"$fifo"
+       ;;
+    0[2-9a-f]*|[1-9a-f]*)
+       printf 'Event (%s):\n%s\n' $(event_id $((0x${msg:0:2}))) "$msg"
+       ;;
+  esac
+done