Files
MobileGL/MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h
T
gold 1ae7a8eaa4 [Feature] (disaggregated): WindowKind::HostFrame - the host-framed surface crosses the wire
The last thing between the host-framed surface and a run: the sub-byte on the wire that names it.
ServerOwned had one (7) and the host frame had none, so the encoder refused the op with
UnknownWindowKind - a correct refusal, and the reason the first run of MOBILEGL_IPC_SURFACE=host
failed in the codec rather than anywhere near the pixels.

 - protocol.fbs: WindowKind::HostFrame = 8, appended, legal on CreateWindowSurface only and always
   with nativeToken 0 - the same rules ServerOwned carries, for the same reason: no pointer of any
   process's window system crosses (Rule G/H).
 - SurfaceRefusal: HostFrameOnSetWindowHandle = 5, its own code rather than reusing the
   server-owned one: a refusal that names the wrong owner sends the next reader to the wrong place.
 - SurfaceOpCodec: the encode and decode arms, the two wire errors that go with them
   (HostFrameTokenNotZero latches as corruption, HostFrameOnSetWindowHandle is a named refusal), and
   the range bound that had ServerOwned as its upper limit now ends at HostFrame.
 - protocol_generated.h regenerated with the pinned submodule's flatc (scripts/gen_protocol.py),
   diff being exactly the two appended enumerators.

Both ends must be rebuilt together, as any protocol revision here: the container's client and the
device's server went out with this on either side.
2026-10-01 01:26:02 +08:00

111 lines
7.0 KiB
C++

// MobileGL - MobileGL/MG_Remote/Protocol/SurfaceOpCodec.h
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
// P5f, package fc: the codec between the inproc SurfaceControlFrame and the wire's
// ::MobileGL::Wire::SurfaceOp / ::MobileGL::Wire::SurfaceReply (protocol.fbs).
//
// The frame is the inproc payload (Server/SurfaceControlFrame.h); this is the ONLY place the
// frame meets the schema. Nothing in the inproc hot path encodes - the frame crosses the one-slot
// channel by value - but the codec is what makes "the same struct encodes under spawn" a tested
// claim rather than a plan, and it owns the two schema seams that are NOT integer translations:
//
// * SurfaceControlOp <-> ::MobileGL::Wire::SurfaceOpKind: numerically aligned for the ten framed ops and
// PINNED by static_asserts in the .cpp, but mapped through an explicit table. The four
// inproc-only kinds have no wire kind and refuse to encode by name.
// * MG_Backend::WindowBackend <-> ::MobileGL::Wire::WindowKind: the two enums do not agree (WindowKind's
// Surfaceless/Pbuffer are surface shapes, not window backends; WindowBackend's MetalLayer had
// no wire enumerator until fc added it), so the mapping is a table with named failures, never
// a cast (f0-egl census, schema gap 3).
//
// ERROR SEMANTICS. Decode is pure: it answers a SurfaceWireError. The FATALS live at the server
// entry point, ServerApplyWireSurfaceOp, because "a wire op the server cannot honour" is a
// contract violation, not a data problem: AndroidNativeWindowArrived dies
// Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"} (the ANativeWindow* means nothing in the
// server's process; real window arrival is P12) and everything else dies
// Fatal{ProtocolCorruption, "SurfaceOp"}. P6's control pump is the intended caller; P5f's unit
// tests drive it directly. MetalLayerArrived likewise dies
// Fatal{UnmigratedSurface, "MetalLayer@P12"}: CAMetalLayer* is also process-local.
//
// P12 (on-screen server window). WindowKind::ServerOwned is the one window kind whose window is
// NOT the client's: the server substitutes its own (ServerLoop.cpp's ServerOwned arm). The codec
// maps it onto the frame-local Server::kServerOwnedWindowBackend and writes/reads nativeToken 0,
// always - so the client's window value never reaches the wire. A ServerOwned op with a non-zero
// token is a peer's corrupt bytes (Fatal{ProtocolCorruption, "SurfaceOp.nativeToken"}, latched);
// SetWindowHandle naming it is a named refusal (SurfaceRefusal::ServerOwnedOnSetWindowHandle),
// answered and not latched.
#pragma once
#include <Includes.h>
// The generated schema header (not forward declarations): flatbuffers 25 makes
// FlatBufferBuilder an alias template instantiation, which cannot be opaque-declared, and the
// Wire enums' underlying types are the schema's own - so this header is the dependency, kept
// honest by being the ONLY schema include the codec needs.
#include <MG_Remote/Protocol/generated/protocol_generated.h>
#include <MG_Remote/Protocol/mg_protocol_base.h>
#include <MG_Remote/Server/SurfaceControlFrame.h>
namespace MobileGL::MG_Backend {
enum class WindowBackend; // BackendObject.h; opaque-declared so this header stays light
} // namespace MobileGL::MG_Backend
namespace MobileGL::MG_Remote {
enum class SurfaceWireError {
None = 0,
UnknownOpKind, // no SurfaceControlOp for the wire kind (or None on the wire)
InprocOnlyOpOnTheWire, // a kind that may ride the inproc channel but never the wire
WindowKindNamesNoBackend,// Surfaceless/Pbuffer where a window backend is required
UnknownWindowKind, // a WindowKind the mapping table does not know
MetalLayerArrived, // -> Fatal{UnmigratedSurface, "MetalLayer@P12"}
AndroidNativeWindowArrived, // -> Fatal{UnmigratedSurface, "AndroidNativeWindow@P12"}
// P12 (on-screen server window), D2. WindowKind::ServerOwned is legal on
// CreateWindowSurface only, and only with nativeToken 0:
ServerOwnedTokenNotZero, // -> Fatal{ProtocolCorruption, "SurfaceOp.nativeToken"}, latched
ServerOwnedOnSetWindowHandle, // -> a NAMED REFUSAL (reply ok=false), not a latch
// MOBILEGL_IPC_SURFACE=host, the same two rules for the other on-screen shape:
HostFrameTokenNotZero, // -> Fatal{ProtocolCorruption, "SurfaceOp.nativeToken"}, latched
HostFrameOnSetWindowHandle, // -> a NAMED REFUSAL (reply ok=false), not a latch
};
const char* SurfaceWireErrorName(SurfaceWireError error);
// frame.kind <-> wire kind. Both answer false (and leave *out untouched) for None, the four
// inproc-only kinds, and any wire value the schema does not define.
bool WireKindForSurfaceControlOp(Server::SurfaceControlOp op, ::MobileGL::Wire::SurfaceOpKind* out);
bool SurfaceControlOpForWireKind(::MobileGL::Wire::SurfaceOpKind kind, Server::SurfaceControlOp* out);
// The explicit WindowBackend <-> WindowKind table. WireWindowKindForWindowBackend also maps
// WindowBackend::Unknown to WindowKind::None; WindowBackendForWireWindowKind maps
// WindowKind::None back to Unknown and answers FALSE for Surfaceless/Pbuffer (surface shapes,
// not window backends).
bool WireWindowKindForWindowBackend(MG_Backend::WindowBackend backend, ::MobileGL::Wire::WindowKind* out);
bool WindowBackendForWireWindowKind(::MobileGL::Wire::WindowKind kind, MG_Backend::WindowBackend* out);
// Encode one frame as a complete CtrlEnvelope{SurfaceOp} buffer (the builder is left
// Finished). Fails BEFORE touching the builder for a kind that may not cross.
SurfaceWireError EncodeSurfaceOpFrame(const Server::SurfaceControlFrame& frame,
flatbuffers::FlatBufferBuilder* builder);
// Validate + decode a wire SurfaceOp into a frame. Pure - it never aborts; the Fatal
// decisions belong to ServerApplyWireSurfaceOp. The reply half of the frame is reset.
SurfaceWireError DecodeWireSurfaceOp(const ::MobileGL::Wire::SurfaceOp& op, Server::SurfaceControlFrame* frame);
// The reply direction, same shape (CtrlEnvelope{SurfaceReply}).
void EncodeSurfaceReplyFrame(const Server::SurfaceControlFrame& frame,
flatbuffers::FlatBufferBuilder* builder);
void DecodeWireSurfaceReply(const ::MobileGL::Wire::SurfaceReply& reply, Server::SurfaceControlFrame* frame);
// The server-side wire entry: decode, then post the frame through ServerLoop's channel and
// hand the reply half back. A decode error is a NAMED Fatal (see the header block), never a
// dropped message. No transport calls this yet - P6's control pump will.
MobileGLResult ServerApplyWireSurfaceOp(const ::MobileGL::Wire::SurfaceOp& op,
Server::SurfaceControlFrame* replyOut);
} // namespace MobileGL::MG_Remote