Files
mesh-catalog/modules/xorg/cmd/xorg/xwd.go
T
jochen b2c170acda xorg: the display server holds node-display-server and writes the session's start (hq ADR 0208)
The X server, its start and its tools as one module. It provides x11-display with
the machine's reach, gated by the host's seat capability. It writes a block at the
start of ~/.xinitrc: the account's environment, an explicit import into the user
manager, the mesh's X resources merged without cpp, autorandr, the xinitrc slots,
~/.xinitrc.local, and the session's exec from the last slot.

Its Go tools serve the seat's displays and layout (autorandr profiles keyed by
EDID), and set-mode, primary, dpi, input devices and settings, keyboard, a
screenshot (xwd decoded in Go) and the server's log. internal/desktop is how
every desktop tool finds the operator's session from the runtime, which has none:
from the session's own processes, reading only its words, confirmed with logind.
2026-10-04 13:21:47 +02:00

84 lines
2.6 KiB
Go

package main
import (
"encoding/binary"
"errors"
"fmt"
"image"
"image/color"
"math/bits"
)
// DecodeXWD reads an X window dump as `xwd` writes it (XWDFile.h, file version 7): the screenshot
// tool's one dependency is xwd, from the X apps the display server already ships, and the PNG is made
// here — no screenshot program, no image library.
//
// Only what an X server dumps in practice is read: a ZPixmap of 24 or 32 bits per pixel with a
// TrueColor or DirectColor visual, in either byte order. Anything else is refused by name.
func DecodeXWD(b []byte) (*image.RGBA, error) {
const headerWords = 25
if len(b) < headerWords*4 {
return nil, errors.New("too short for an xwd header")
}
h := make([]uint32, headerWords)
for i := range h {
h[i] = binary.BigEndian.Uint32(b[i*4:])
}
headerSize, version, format := h[0], h[1], h[2]
width, height := int(h[4]), int(h[5])
byteOrder, bpp, bytesPerLine, class := h[7], int(h[11]), int(h[12]), h[13]
red, green, blue := h[14], h[15], h[16]
ncolors := int(h[19])
switch {
case version != 7:
return nil, fmt.Errorf("xwd file version %d; 7 is read", version)
case format != 2:
return nil, fmt.Errorf("pixmap format %d; ZPixmap (2) is read", format)
case bpp != 24 && bpp != 32:
return nil, fmt.Errorf("%d bits per pixel; 24 and 32 are read", bpp)
case class != 4 && class != 5:
return nil, fmt.Errorf("visual class %d; TrueColor and DirectColor are read", class)
case width <= 0 || height <= 0 || width > 32768 || height > 32768:
return nil, fmt.Errorf("a %dx%d image", width, height)
case bytesPerLine < width*bpp/8:
return nil, fmt.Errorf("%d bytes per line for %d pixels", bytesPerLine, width)
}
start := int(headerSize) + ncolors*12
if start < 0 || len(b) < start+bytesPerLine*height {
return nil, fmt.Errorf("the image data is cut short: %d bytes, %d needed", len(b), start+bytesPerLine*height)
}
pixels := b[start:]
step := bpp / 8
channel := func(p, mask uint32) uint8 {
if mask == 0 {
return 0
}
shift := bits.TrailingZeros32(mask)
width := bits.OnesCount32(mask)
v := (p & mask) >> shift
if width >= 8 {
return uint8(v >> (width - 8))
}
return uint8(v << (8 - width))
}
img := image.NewRGBA(image.Rect(0, 0, width, height))
for y := 0; y < height; y++ {
row := pixels[y*bytesPerLine:]
for x := 0; x < width; x++ {
px := row[x*step : x*step+step]
var p uint32
if byteOrder == 0 { // LSBFirst
for i := step - 1; i >= 0; i-- {
p = p<<8 | uint32(px[i])
}
} else {
for i := 0; i < step; i++ {
p = p<<8 | uint32(px[i])
}
}
img.SetRGBA(x, y, color.RGBA{channel(p, red), channel(p, green), channel(p, blue), 0xff})
}
}
return img, nil
}