android

package module
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2026 License: BSD-3-Clause Imports: 13 Imported by: 0

README

go-widgets/android

ci Go Reference Go Report Card

A pure-Go, CGO-free Android back-end for the go-widgets toolkit: a real, installable Android app whose entire user interface is laid out and painted by a Go process built with CGO_ENABLED=0.

a go-widgets tree running as an Android app

Why it is split in two

Android hands no drawable surface to a process that is not the app. Every path to one — ANativeWindow, Surface, NativeActivity — is behind JNI, and JNI needs cgo. purego does not rescue it either: its dlfcn_android.go routes Dlopen through internal/cgo, unlike its darwin and non-cgo linux paths. And there is no wire-protocol back door the way X11 and Wayland have one: SurfaceFlinger sits behind Binder, and a Surface only ever comes from the WindowManager against an Activity token.

So the app is two processes:

Java host (host/, ~360 lines) owns the Activity, the SurfaceView, touch, keys and the lifecycle. Blits pixels. Knows nothing about widgets.
Go application (cmd/gwapp) an ordinary CGO_ENABLED=0 GOOS=android executable. Owns layout, widgets, theme, hit-testing and focus — unchanged from every other back-end.

This is the split the Linux back-ends already live with — a socket protocol plus a shared pixel buffer — with the Java host standing exactly where the X server or the Wayland compositor stands.

  MotionEvent ─► LocalSocket ─► android.Client ─► toolkit widget tree
                                      │
  Surface ◄── Bitmap ◄── mmap'd file ◄─┘  painter.PixelPainter
                    ▲
                    └── MsgFrame{x,y,w,h}: which rectangle changed

Pixels travel through a memfd the application creates and hands to the host as an ancillary descriptor on the socket, so they live in memory and never dirty page cache the kernel writes to storage. (A file in the app's own storage is the fallback where memfd_create is missing.) The Go side writes RGBA_8888, which is byte-for-byte what Android's ARGB_8888 Bitmap holds in memory, so the blit is a copy with no conversion — and only the damaged rectangle is copied, measured on an Android 15 arm64 device:

damage on a 1080×2400 surface whole-surface copy damage-only copy
400×300 (a widget) 883 µs 328 µs
full surface (a plain tree) 3335 µs 1957 µs

(median of 41 and 21 blits; the full-surface case gets faster too because the gathered tile is drawn with an offset blit rather than a src/dst rect one.)

The memfd is worth the same kind of measurement — /proc/meminfo Dirty, idle versus painting, same session and same taps:

framebuffer idle painting delta
file in app storage 192 kB 10188 kB +9996 kB
memfd 188 kB 140 kB −48 kB

Ten megabytes of dirty page cache per painting session, written out to flash half a minute later, for pixels that are pure scratch — gone.

arm64 only, and why

android/arm64 is the only Android target Go links CGO-free:

$ CGO_ENABLED=0 GOOS=android GOARCH=arm64 go build ./cmd/gwapp   # fine
$ CGO_ENABLED=0 GOOS=android GOARCH=arm   go build ./cmd/gwapp
android/arm requires external (cgo) linking, but cgo is not enabled

android/amd64 and android/386 answer the same. So the premise this back-end rests on — a sovereign application binary with no C tool chain — holds on 64-bit ARM alone, which is every Android phone and tablet shipped for years, but not the x86 emulator images. CI asserts both halves of that, so the day Go lifts the restriction is a red build rather than a silent one.

Usage

c, err := android.Dial("my app", nil) // nil theme = toolkit.DefaultDark()
if errors.Is(err, android.ErrUnsupported) {
    // Not running under a host: the module still builds and vets everywhere.
    return nil
}
defer c.Close()
return c.Run(myWidgetTree()) // blocks until the Activity goes away

Client satisfies go-widgets/window's Backend (Run/Close/Size/ String) and its Repainter, so an application moves between this back-end and X11, Wayland, Cocoa, Win32 or wasmbox without changing a line above the window.

Accessibility

The application paints pixels, so without help a screen reader sees the SurfaceView as one opaque rectangle. The host gives it a virtual view hierarchy instead: one node per accessible element of the widget tree, with the android.widget.* class name Android decides its announcements from, the text to read, screen bounds and an activation action.

The elements are pulled, never pushed. A provider method is only ever called when something is reading the tree, so an app with no accessibility service attached never builds one — which is also what keeps this from repeating go-widgets/window's macOS mistake of rebuilding the whole tree inside the paint loop and freezing the machine.

An activation comes back as an ordinary click at the element's centre, so an accessibility action goes through the very code a touch does, with no second path to drift from the first — the rule the AT-SPI bridge already follows.

Touch

Each pointer sample reaches the widget tree as two events: the touch event first, then a mouse event.

go-widgets models touch directly — EventTouchStart/Move/End carry a pointer id in Event.Code, and toolkit.GestureRecognizer turns them into taps, long presses and swipes. A back-end emitting only mouse events would leave every gesture-aware widget deaf on the one kind of device gestures are for. The compatibility mouse event follows because most widgets listen for EventClick; a browser does exactly this, for exactly this reason.

System bars

An Android window is edge-to-edge from API 35: the surface really is the whole screen, and the status bar, the navigation bar, a display cutout and the soft keyboard are painted on top of it rather than shrinking it. The tree is therefore laid out inside what they leave, so its first and last rows are not hidden; the margins are still painted in the theme background, so the bars sit on the app's own colour.

Client.Insets() reports those four edges, and Client.SetFullBleed(true) opts back out to the whole surface — for a root that means to reach under the bars (a photo, a map, a video) and takes responsibility for keeping anything readable out of the way.

Layout

protocol.go       the sovereign codec — wire messages, framing, and the
                  input→toolkit.Event mapping. No syscall, no net: it
                  builds, and is tested, on every GOOS.
client.go         the transport — dials the host, maps the framebuffer,
                  drives the widget tree. //go:build linux
client_other.go   the same surface reporting ErrUnsupported, so an
                  application still cross-builds off Android.
cmd/gwapp/        the demo application.
host/             the Java host, its manifest, and build.sh.

Building the APK

No Gradle and no Kotlin: the host is a handful of Java files and the application is a Go binary, so the SDK's own tools are the whole tool chain.

export ANDROID_HOME=... JAVA_HOME=...
sdkmanager --install "platforms;android-35" "build-tools;35.0.0"
host/build.sh                        # → host/out/gwhost.apk
adb install host/out/gwhost.apk

build.sh runs go build, javac, d8, aapt2 link, zipalign and apksigner, in that order, and picks the newest platform and build-tools the SDK has installed. APP=./cmd/myapp host/build.sh packages your own application instead of the demo. Two things it does that are worth knowing:

  • the Go executable ships as lib/<abi>/libgwapp.so with extractNativeLibs="true", because nativeLibraryDir is the one place an Android app may execute from. It is a plain PIE executable; nothing ever dlopens it;
  • the debug keystore lives beside the sources, never under the build output. A fresh key per build changes the signing certificate, and Android then refuses to update an installed app (INSTALL_FAILED_UPDATE_INCOMPATIBLE).

Testing

The transport is Linux, and Android is Linux: the abstract socket it dials and the shared mapping it paints into are ordinary Linux facilities. So the suite runs against a fake host over a real socket and a real mmap, not a mock — on the CI Linux runner under -race, and on the device itself:

go test -c -cover -coverpkg=. -o android.test .
adb push android.test /data/local/tmp/ && adb shell /data/local/tmp/android.test

100.0% statement coverage, gated in CI, covering every decode error, every framebuffer failure, the lifecycle pause and the damage-rectangle path. -race runs on the Linux lane only: the race detector needs cgo, which is the very thing an Android application binary must not have.

Proven on device

Android 15 / arm64:

  • the app installs and launches; the whole window is the go-widgets tree;
  • a touch reaches the widget — three taps on the button leave clicks: 3, and a pixel diff bounds the repaint to the button and its label alone;
  • a live rotation is survived in-process: the Activity keeps configChanges, the surface is remapped at 2400×1080, the tree is laid out again, and taps still land (landscape);
  • the surface geometry and display density cross the socket — the demo reports surface: 1080x2400 px, density 263, the panel's true 2.625×;
  • the accessibility tree is real: adb shell uiautomator dump, which reads through the same framework a screen reader does, sees a virtual node per element — android.widget.TextView for each label, android.widget.Button for each button — each with its text and screen bounds;
  • the touch-density floor works end to end. The demo's last row holds a deliberately tiny 20x20 button, because nothing visual can show this axis: toolkit.TouchTarget clamps a control's HIT rectangle up to the density minimum and centres it over UNCHANGED pixels. With the button at [40,2085][60,2105] and its hit rect at [28,2073][72,2117], a tap at (10,2095) — outside both — does nothing, and a tap at (30,2095) — outside the pixels, inside the hit rect — activates it. That is a fingertip landing beside a 20-pixel target and still hitting it;
  • the system bars do not hide anything: with the device reporting statusBars top=128 and navigationBars bottom=126, the tree's first text row moves from y=234 to y=337 and its last from y=2160 to y=2063 — the +103 and −97 a five-child box redistributed over the safe area gives, to the pixel (without insets vs with).

Known gaps

Deliberate, and none of them protocol-deep:

  • single touch — the protocol carries a pointer id, the host forwards one. Multi-touch, fling and inertial scroll are toolkit work, not host work;
  • no IME — a soft keyboard needs InputConnection on the host and a text model above;
The accessibility path, measured with a real client

adb shell uiautomator dump reports clickable="false" on the button. That attribute is an artefact of the dump, not a property of the node: asked the way a screen reader asks, the framework returns something else entirely.

host/probe/ is a real AccessibilityService — a test instrument, in its own package, never part of the shipped app — that queries the node and activates it. Against the live demo it reports:

PROBE found text=Click me class=android.widget.Button clickable=true
      actions=[ACTION_CLICK, ACTION_ACCESSIBILITY_FOCUS, ACTION_CLEAR_ACCESSIBILITY_FOCUS]
      bounds=[0,1418][1080,1844]
PROBE performAction(ACTION_CLICK) returned true
PROBE found text=Click me, pressed ...

So the node a screen reader sees IS clickable, it DOES carry ACTION_CLICK, and performing that action reaches the Go widget: two activations left the demo reading clicks: 2, and the button's own value changed to pressed between the two reads. What TalkBack decides "double-tap to activate" from is the action list, which is present.

Run it with:

host/probe/build.sh && adb install -r host/probe/out/gwprobe.apk
adb shell settings put secure enabled_accessibility_services \
    org.gowidgets.a11yprobe/org.gowidgets.a11yprobe.GwProbeService
adb shell settings put secure accessibility_enabled 1
adb logcat -s gw-a11y-probe
What is and is not proven about multi-touch

The application half is proven deterministically and on the device: a real toolkit.MultiTouchRecognizer, fed this back-end's own output for two contacts, engages and reports a pinch out and a pinch in.

The host half — forwarding every contact of a MotionEvent — is not proven on a device. adb shell input injects a single pointer, and kernel-level multi-touch injection through /dev/input does not reach the app on this emulator, because input uses Android's injection API rather than the input devices. A single contact is verified end to end; the second-contact path is reviewed code, not measured behaviour, until it runs on real hardware.

License

BSD-3-Clause. See LICENSE.

Documentation

Overview

This file is the transport that binds the sovereign codec (protocol.go) to a live Java host: it dials the host's abstract unix socket, creates and maps the shared framebuffer, hands its descriptor over, paints the go-widgets root into it and posts a frame per damaged rectangle. It is CGO-free — the whole Android side of the process is a socket and an mmap, both of which Go makes on its own — so the application binary stays exactly as sovereign as it is on X11 or Wayland.

Package android implements the application half of the go-widgets Android host protocol, so a go-widgets application runs inside a real Android app exactly as it runs on X11, Wayland, Cocoa or Win32.

Android hands out no drawable surface to a process that is not the app: the whole graphics API is behind JNI, and JNI needs cgo. So the app is split in two. A thin Java host owns the Activity, the SurfaceView and the input stream; the go-widgets application is an ordinary CGO-free executable the host spawns, which paints into a shared mapping and tells the host which rectangle changed. The split is the same one the Linux back-ends already live with — a socket protocol plus a shared pixel buffer — with the Java host standing where the X server or the Wayland compositor stands.

This file is the SOVEREIGN, transport-agnostic codec: the wire messages, the framing, and the input→toolkit.Event mapping, over plain Go values. It carries no syscall and no net dependency, so it builds — and is unit-tested to 100% — on every GOOS. The transport that dials the host socket, maps the buffer and drives a widget tree lives in client.go.

Index

Constants

View Source
const (
	// MsgConfig carries the surface geometry and the shared buffer path. The
	// host sends it once at start-up and again on every resize or rotation.
	MsgConfig uint8 = 0x01
	// MsgTouch carries one pointer sample in surface pixels.
	MsgTouch uint8 = 0x02
	// MsgKey carries one key event: an Android key code plus the unicode rune
	// the host's key-character map produced (0 when the key produces none).
	MsgKey uint8 = 0x03
	// MsgLifecycle carries an Activity transition: the app keeps its widget
	// tree across a pause, but stops painting until it resumes.
	MsgLifecycle uint8 = 0x04
	// MsgClose asks the application to end its Run loop.
	MsgClose uint8 = 0x05
	// MsgA11yRequest asks the application for its accessibility tree. The host
	// sends it only when something is actually reading one, so an app with no
	// screen reader attached never builds a tree at all.
	MsgA11yRequest uint8 = 0x07
	// MsgA11yAction carries the index of the element a screen reader activated.
	MsgA11yAction uint8 = 0x08
	// MsgInsets carries the area of the surface the system is drawing over.
	// It is its own message rather than a Config field because insets change
	// on their own schedule: the soft keyboard opening does not resize the
	// surface, and a bar auto-hiding does not either.
	MsgInsets uint8 = 0x06

	// MsgReady tells the host the shared buffer is mapped at the announced
	// size, so the host may map it in turn. Every MsgFrame that follows
	// refers to this mapping, until the next MsgReady replaces it.
	MsgReady uint8 = 0x81
	// MsgFrame tells the host which surface-local rectangle changed.
	MsgFrame uint8 = 0x82
	// MsgTitle updates the host's window title.
	MsgTitle uint8 = 0x83
	// MsgBye tells the host the application ended.
	MsgBye uint8 = 0x84
	// MsgA11yTree answers MsgA11yRequest with the accessibility elements.
	MsgA11yTree uint8 = 0x85
)

Message types. Host→app messages are below 0x80, app→host at or above it, so a misrouted message is a decode error rather than a plausible other message.

View Source
const (
	TouchDown uint8 = 0
	TouchUp   uint8 = 1
	TouchMove uint8 = 2
)

Touch actions, matching the three MotionEvent actions the host forwards.

View Source
const (
	KeyDown uint8 = 0
	KeyUp   uint8 = 1
)

Key actions.

View Source
const (
	LifecyclePause  uint8 = 0
	LifecycleResume uint8 = 1
)

Lifecycle states.

View Source
const EnvSocket = "GW_ANDROID_SOCKET"

EnvSocket names the environment variable the Java host sets to the abstract socket it is listening on. The host generates a fresh name per launch, so two instances of the app never collide.

View Source
const MaxPayload = 1 << 16

MaxPayload bounds one decoded message body. The largest message a host legitimately sends is a Config carrying a filesystem path, so a frame beyond this is a desynchronised stream — refused rather than allocated.

Variables

View Source
var ErrShortPayload = errors.New("android: truncated message payload")

ErrShortPayload reports a message whose body is too short for its type.

View Source
var ErrUnsupported = errors.New("android: no Android host on this platform")

ErrUnsupported reports an environment with no Android host: every GOOS but Linux, where the abstract socket and the shared mapping the host protocol needs do not exist. A cross-built application gets this from Dial and can report it and exit cleanly, exactly as go-widgets/window does off its supported back-ends.

Functions

func AndroidClass added in v0.5.0

func AndroidClass(r toolkit.Role) string

AndroidClass returns the Android class name for a toolkit role. Anything with no more specific mapping is a TextView, which is what Android itself uses for a piece of readable content.

func DecodeA11yAction added in v0.5.0

func DecodeA11yAction(b []byte) (int, error)

DecodeA11yAction parses a MsgA11yAction body: the index of the element a screen reader activated.

func DecodeReady

func DecodeReady(b []byte) (w, h int, err error)

DecodeReady parses a MsgReady body.

func EncodeA11yAction added in v0.5.0

func EncodeA11yAction(index int) []byte

EncodeA11yAction builds a MsgA11yAction body.

func EncodeA11yTree added in v0.5.0

func EncodeA11yTree(els []A11yElement) []byte

EncodeA11yTree builds a MsgA11yTree body: a count, then each element as three length-prefixed strings, four coordinates and a flag.

func EncodeConfig

func EncodeConfig(c Config) []byte

EncodeConfig builds a MsgConfig body.

func EncodeFrame

func EncodeFrame(r Rect) []byte

EncodeFrame builds a MsgFrame body naming the damaged rectangle.

func EncodeInsets added in v0.3.0

func EncodeInsets(i Insets) []byte

EncodeInsets builds a MsgInsets body.

func EncodeKey

func EncodeKey(k Key) []byte

EncodeKey builds a MsgKey body.

func EncodeReady

func EncodeReady(w, h int) []byte

EncodeReady builds a MsgReady body: the size the application actually mapped.

func EncodeTouch

func EncodeTouch(t Touch) []byte

EncodeTouch builds a MsgTouch body.

func FrameMessage added in v0.4.0

func FrameMessage(typ uint8, body []byte) []byte

FrameMessage returns one framed message: a 4-byte big-endian length covering the type byte and the body, then the type byte, then the body. Big-endian keeps the Java host on DataInputStream.readInt with no byte-swapping.

It exists as bytes rather than as writes because a message that carries an ancillary descriptor has to reach the host in ONE sendmsg: split across two writes, the host could attribute the descriptor to the wrong message.

func MapKey

func MapKey(k Key) []toolkit.Event

MapKey maps one Android key event to toolkit events, mirroring the wasmbox and X11 mappings: a named key is one EventKeyDown/EventKeyUp; a key that committed a character is an EventKeyDown followed by an EventChar on press, and an EventKeyUp on release. A key that is neither named nor printable reaches the tree as nothing.

func MapTouch

func MapTouch(t Touch, held, primary bool) []toolkit.Event

MapTouch maps one pointer sample to toolkit events.

A contact always yields its touch event — EventTouchStart/Move/End with the pointer id in Event.Code, which is what toolkit's GestureRecognizer and MultiTouchRecognizer key contacts by.

Only the PRIMARY contact also yields a compatibility mouse event. Most widgets, and every widget written before touch existed, listen for EventClick; but a second finger must not fire a second click, or a pinch would read as two taps to every widget in the tree. A browser draws the line in the same place, for the same reason.

The mouse half mirrors the wasmbox and X11 mappings: a press is a click, a move with a finger down is a drag. A touch screen has no hover, so a move with nothing down cannot occur and is mapped to a plain move rather than dropped, keeping a synthetic host (a test, a replay) honest.

func ReadMessage

func ReadMessage(r io.Reader) (typ uint8, body []byte, err error)

ReadMessage reads one framed message. It returns io.EOF when the stream ends cleanly between messages, so a caller can tell a closed host from a truncated one.

func WriteMessage

func WriteMessage(w io.Writer, typ uint8, body []byte) error

WriteMessage writes one framed message.

Types

type A11yElement added in v0.5.0

type A11yElement struct {
	// Class is the android.widget.* class name a screen reader expects for
	// this kind of element. Android has no notion of an ARIA role; it decides
	// almost everything from the class name of the node.
	Class string
	// Name is what a screen reader announces.
	Name string
	// Value is the element's current value, appended after the name for a
	// control that has one (a text field's content, a slider's reading).
	Value string
	// X, Y, W, H is the element's rectangle in surface pixels.
	X, Y, W, H int
	// Clickable reports whether activating the element does something, i.e.
	// whether the host should offer "double-tap to activate".
	Clickable bool
}

A11yElement is one element of the accessibility tree as the host sees it: a role it can turn into an Android class name, the text a screen reader reads, and the rectangle to focus, in surface pixels.

func A11yElements added in v0.5.0

func A11yElements(root toolkit.Widget) []A11yElement

A11yElements turns the widget tree into the flat element list the host serves. Elements with nothing to announce are dropped: an unnamed, valueless element would reach a screen reader as an anonymous stop the user has to swipe past, and a zero-area one cannot be focused at all.

func DecodeA11yTree added in v0.5.0

func DecodeA11yTree(b []byte) ([]A11yElement, error)

DecodeA11yTree parses a MsgA11yTree body. It exists for the round-trip tests and for any host written in Go; the shipped host is the Java one.

func (A11yElement) Center added in v0.5.0

func (e A11yElement) Center() (int, int)

Center returns the point to replay an activation at: the middle of the element. A screen reader's activation becomes an ordinary click there, so every behaviour a click has is had by an accessibility action, with no second code path to drift from the first — the rule the AT-SPI bridge already follows.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is an open Android host surface bound to a go-widgets scene. It satisfies window.Backend (Run/Close/Size/String), so a go-widgets app runs through Open→Run unchanged whether the backend is X11, Wayland, Cocoa, Win32, wasmbox or — here — a Java host Activity.

func Dial

func Dial(title string, theme *toolkit.Theme) (*Client, error)

Dial connects to the Java host named by $GW_ANDROID_SOCKET and blocks until the host has announced its surface geometry, so the returned Client is ready to paint. It is the Android-environment analogue of openX11/openWayland.

func (*Client) Close

func (c *Client) Close() error

Close ends the session: it tells the host the application is going away, unmaps the framebuffer and ends Run. Idempotent.

func (*Client) Density

func (c *Client) Density() int

Density returns the display density in hundredths, as the host read it from Android's DisplayMetrics (a 3x panel is 300). It is this back-end's spelling of the backing-scale factor.

func (*Client) Insets added in v0.3.0

func (c *Client) Insets() Insets

Insets returns the margin of the surface the system is drawing over: the status and navigation bars, a display cutout, the soft keyboard. By default the widget tree is laid out inside what they leave; see Client.SetFullBleed.

func (*Client) Repaint

func (c *Client) Repaint()

Repaint asks for a repaint from ANY goroutine, satisfying window.Repainter.

func (*Client) Run

func (c *Client) Run(root toolkit.Widget) error

Run binds root, paints the seed frame and blocks while host messages drive the widget tree, until the host closes the surface (or Close ends it). It is the Android analogue of the X11 and Wayland event loops.

func (*Client) SetFullBleed added in v0.3.0

func (c *Client) SetFullBleed(on bool)

SetFullBleed lays the widget tree out over the WHOLE surface, insets and all. It is for a root that means to reach under the system bars — a photo, a map, a video — and is then responsible for keeping anything readable out of the area Client.Insets reports.

func (*Client) SetTitle

func (c *Client) SetTitle(title string)

SetTitle updates the host's window title.

func (*Client) Size

func (c *Client) Size() (int, int)

Size returns the current surface size in physical pixels.

func (*Client) String

func (c *Client) String() string

String identifies the surface for debugging.

type Config

type Config struct {
	// W and H are the surface size in physical pixels.
	W, H int
	// Density is the display density in hundredths (Android's
	// DisplayMetrics.density × 100, so a 3.0x panel arrives as 300). It is the
	// Android spelling of the backing-scale factor the Cocoa back-end reads
	// from the screen.
	Density int
	// BufPath is the file the application maps as its framebuffer. The host
	// picks it inside the app's own storage, which both processes share.
	BufPath string
}

Config is the host's geometry announcement.

func DecodeConfig

func DecodeConfig(b []byte) (Config, error)

DecodeConfig parses a MsgConfig body.

type Insets added in v0.3.0

type Insets struct{ Left, Top, Right, Bottom int }

Insets is the margin of the surface the system draws over, in pixels.

An Android window is edge-to-edge from API 35: the surface really is the whole screen, and the status bar, the navigation bar, a display cutout and the soft keyboard are painted ON TOP of it rather than shrinking it. So a widget tree laid out to the full surface is correct in size and wrong in practice — its first and last rows are behind the bars. These are the four edges to keep clear.

func DecodeInsets added in v0.3.0

func DecodeInsets(b []byte) (Insets, error)

DecodeInsets parses a MsgInsets body.

func (Insets) Apply added in v0.3.0

func (i Insets) Apply(w, h int) Rect

Apply returns the part of a w×h surface that nothing is drawn over. It never returns a negative extent: insets wider than the surface (a phone folded to a sliver, a bad host) collapse the area to zero rather than inverting it.

func (Insets) Empty added in v0.3.0

func (i Insets) Empty() bool

Empty reports whether nothing is covering the surface.

type Key

type Key struct {
	Action uint8
	// Code is the Android KeyEvent key code.
	Code int
	// Rune is the character the key produced, or 0 for a key that produces
	// none (an arrow, a modifier, the back key).
	Rune rune
}

Key is one key event.

func DecodeKey

func DecodeKey(b []byte) (Key, error)

DecodeKey parses a MsgKey body.

type Rect

type Rect struct{ X, Y, W, H int }

Rect is a surface-local rectangle in pixels. It mirrors toolkit.Rect but is kept local so the codec stays a leaf with one toolkit dependency (the event model).

func ClampRect

func ClampRect(r Rect, w, h int) Rect

ClampRect clips r to a w×h surface, returning a zero-area rectangle when nothing of r is inside. The host trusts the rectangle it is given, so the application clamps before sending.

func DecodeFrame

func DecodeFrame(b []byte) (Rect, error)

DecodeFrame parses a MsgFrame body.

type Touch

type Touch struct {
	Action uint8
	X, Y   int
	// ID is the pointer index, so a later multi-touch host can be told apart
	// from this one without a protocol break. Single-touch hosts send 0.
	ID int
}

Touch is one pointer sample.

func DecodeTouch

func DecodeTouch(b []byte) (Touch, error)

DecodeTouch parses a MsgTouch body.

Directories

Path Synopsis
cmd
gwapp command
Command gwapp is the go-widgets application half of the Android host demo.
Command gwapp is the go-widgets application half of the Android host demo.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL