diff cursors/92h.sh @ 0:f0372d3356b4 default tip

initial import
author Yasuri <linuxoverwindows@charliesmp.net>
date Sun, 30 Aug 2026 12:47:10 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cursors/92h.sh	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,224 @@
+#!/bin/sh
+# convert plan 9 cursor definitions in a C file to packed argb cursor_data[] + cursor_metadata[] for swc
+# asage:
+#   ./92h.sh 9cursors.c > out.c
+#   ./92h.sh 9cursors.c tl t tr l r bl b br > out.c   # explicit order/subset
+
+infile=${1:-/dev/stdin}
+shift 2>/dev/null || true
+
+# remaining args are optional, desired cursor names in output order.
+order="$*"
+
+awk -v ORDER="$order" '
+function hexval(c) {
+  c = toupper(c)
+  if (c >= "0" && c <= "9") return c + 0
+  return 10 + index("ABCDEF", c) - 1
+}
+function hex2dec(s,    i,n) {
+  sub(/^0[xX]/, "", s)
+  n = 0
+  for (i = 1; i <= length(s); i++)
+    n = n * 16 + hexval(substr(s, i, 1))
+  return n
+}
+function pow2(n,    i,p) { p=1; for(i=0;i<n;i++) p*=2; return p }
+function bit16msb(v, col,    shift) {
+  shift = 15 - col
+  return int(v / P2[shift]) % 2
+}
+function pix(setb, clrb) {
+  #   clr=0,set=0 => transparent
+  #   clr=1,set=0 => black
+  #   clr=0,set=1 => white
+  #   clr=1,set=1 => invert (approximate as white for ARGB cursors)
+  if (!clrb && !setb) return "0x00000000"
+  if (clrb && !setb) return "0xff000000"
+  if (!clrb && setb) return "0xffffffff"
+  return "0xffffffff"
+}
+function min(a,b){ return (a<b)?a:b }
+function max(a,b){ return (a>b)?a:b }
+
+BEGIN {
+  # precompute powers of two up to 15
+  for (i=0;i<16;i++) P2[i]=pow2(i)
+
+  want_cnt = 0
+  if (ORDER != "") {
+    n = split(ORDER, want, /[[:space:]]+/)
+    for (i=1;i<=n;i++) if (want[i] != "") { want_cnt++; wantlist[want[i]]=i }
+  }
+
+  in_cursor = 0
+  arr = 0
+  c1 = c2 = 0
+  name_cnt = 0
+}
+
+# start of a cursor block: cursor foo = {
+match($0, /^[[:space:]]*Cursor[[:space:]]+[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/) {
+  # extract the name
+  line = $0
+  sub(/^[[:space:]]*Cursor[[:space:]]+/, "", line)
+  cur = line
+  sub(/[[:space:]]*=.*/, "", cur)
+
+  in_cursor = 1
+  got_hot = 0
+  arr = 0
+  c1 = 0
+  c2 = 0
+  next
+}
+
+# inside cursor: hotspot like {-7, -7}, compute it properly so it offsets,
+in_cursor && !got_hot && match($0, /\{[[:space:]]*-?[0-9]+[[:space:]]*,[[:space:]]*-?[0-9]+[[:space:]]*\}/) {
+  s = substr($0, RSTART+1, RLENGTH-2)
+  gsub(/[[:space:]]*/, "", s)
+  split(s, a, ",")
+  hx[cur] = a[1] + 0
+  hy[cur] = a[2] + 0
+  got_hot = 1
+}
+
+in_cursor {
+  while (match($0, /0[xX][0-9A-Fa-f]+/)) {
+    tok = substr($0, RSTART, RLENGTH)
+    val = hex2dec(tok)
+
+    if (arr == 0) arr = 1
+    if (arr == 1) {
+      clr[cur, c1++] = val
+      if (c1 >= 32) arr = 2
+    } else {
+      set[cur, c2++] = val
+    }
+
+    $0 = substr($0, RSTART + RLENGTH)
+  }
+}
+
+# end of cursor block
+in_cursor && /};/ {
+  in_cursor = 0
+
+  if (!(seen[cur]++)) {
+    name_cnt++
+    names[name_cnt] = cur
+  }
+
+  if (c1 < 32 || c2 < 32) {
+    warn[cur] = 1
+  }
+}
+
+END {
+  # decide output order
+  outn = 0
+  if (want_cnt > 0) {
+    for (i=1; i<=want_cnt; i++) out[++outn] = want[i]
+  } else {
+    for (i=1; i<=name_cnt; i++) out[++outn] = names[i]
+  }
+
+  print "/* generated from plan 9 cursor data */"
+  print "#include <stdint.h>"
+  print "#include <stddef.h>"
+  print ""
+  print "enum {"
+  for (oi=1; oi<=outn; oi++) {
+    n = out[oi]
+    if (n == "") continue
+    name = toupper(n)
+    gsub(/[^A-Z0-9]+/, "_", name)
+    printf "\tNEIN_CURSOR_%s = %d,\n", name, oi-1
+  }
+  print "};"
+  print ""
+  print "static const uint32_t nein_cursor_data[] = {"
+
+  off = 0
+  perline = 0
+
+  for (oi=1; oi<=outn; oi++) {
+    n = out[oi]
+    if (n == "") continue
+    if (!(seen[n])) {
+      printf "/* WARNING: cursor %s not found */\n", n
+      continue
+    }
+    if (warn[n]) {
+      printf "/* WARNING: cursor %s looked incomplete (expected 32 clr + 32 set bytes) */\n", n
+    }
+
+    # build full 16x16 pixels and compute tight bbox
+    minx=16; miny=16; maxx=-1; maxy=-1
+    for (y=0; y<16; y++) {
+      rowc = clr[n,2*y]*256 + clr[n,2*y+1]
+      rows = set[n,2*y]*256 + set[n,2*y+1]
+      for (x=0; x<16; x++) {
+        pb = pix(bit16msb(rows,x), bit16msb(rowc,x))
+        full[n,y,x] = pb
+        if (pb != "0x00000000") {
+          minx = min(minx, x); maxx = max(maxx, x)
+          miny = min(miny, y); maxy = max(maxy, y)
+        }
+      }
+    }
+
+    # if entirely transparent, fall back to 1x1
+    if (maxx < 0) { minx=0; maxx=0; miny=0; maxy=0 }
+
+    w = maxx - minx + 1
+    h = maxy - miny + 1
+
+    #offset hostspot
+    xhot = -hx[n]
+    yhot = -hy[n]
+
+    hx2 = xhot - minx
+    hy2 = yhot - miny
+
+    meta_name[oi] = n
+    meta_w[oi] = w
+    meta_h[oi] = h
+    meta_hx[oi] = hx2
+    meta_hy[oi] = hy2
+    meta_off[oi] = off
+
+    for (y=miny; y<=maxy; y++) {
+      for (x=minx; x<=maxx; x++) {
+        v = full[n,y,x]
+        if (perline == 0) printf "\t"
+        printf "%s,", v
+        perline++
+        off++
+        if (perline >= 6) { printf "\n"; perline = 0 }
+        else printf " "
+      }
+    }
+    if (perline != 0) { printf "\n"; perline = 0 }
+  }
+
+  print "};"
+  print ""
+  print "struct nein_cursor_meta {"
+  print "\tuint32_t width, height;"
+  print "\tint32_t hotspot_x, hotspot_y;"
+  print "\tsize_t offset;"
+  print "};"
+  print ""
+  print "static const struct nein_cursor_meta nein_cursor_metadata[] = {"
+
+  for (oi=1; oi<=outn; oi++) {
+    n = meta_name[oi]
+    if (n == "") continue
+    printf "\t{ %d, %d, %d, %d, %d }, /* %s */\n",
+      meta_w[oi], meta_h[oi], meta_hx[oi], meta_hy[oi], meta_off[oi], n
+  }
+
+  print "};"
+}
+' "$infile"