Put regions on a screen

This guide puts two programs from two namespaces on one monitor, each in its own rectangle. A notice board is on the left seven tenths of a lobby screen, and a parking-lot camera is in the upper right. You need the operator installed and the claim guide read, because each program gets to the screen the way that guide shows.

A screen with no Layout shows every window fullscreen, with the newest on top. A Layout divides the screen into regions, and each region shows the window of a pod whose labels match the region’s selector. A pod never receives its position on the screen. The Layout is the only resource that stores the arrangement, and one Layout can apply to any number of screens.

1. Write the Layout

apiVersion: display.liken.sh/v1alpha1
kind: Layout
metadata:
  name: front-desk
spec:
  regions:
    - name: notices
      rect: {left: 0, top: 0, width: 0.7, height: 1}
      selector:
        matchLabels: {panel: notices}
    - name: lot
      rect: {left: 0.7, top: 0, width: 0.3, height: 0.6}
      selector:
        matchLabels: {panel: parking-lot}
      transition:
        enter: {kind: fade, milliseconds: 300}
        exit: {kind: fade, milliseconds: 300}

A Layout is cluster-scoped, like a DeviceClass, and nothing in it names a namespace or a monitor. Each rectangle is four fractions of the screen, so the same Layout fits a 1080p panel and a 4K one. The order of the list is the stacking order: a region written after another draws over it where they overlap, which is how a small picture appears in the corner of a large one.

The selector matches labels the way a Service does, and any label counts. The candidates are only pods that hold a claim on this screen, so a panel label on a pod elsewhere in the cluster matches nothing here. A region shows one program: the first claim to arrive from a matching pod. Every window of that claim is drawn in the region, newest on top, which is how a player keeps its overlay over its video. A second matching claim stays off the screen and is reported, and a region with no matching pod is empty and is reported.

A transition has two halves, and each is fade over the stated milliseconds or none. The enter half runs when a window enters its region, and a fade brings the window from transparent to opaque. The exit half runs when a window that is still drawing stops matching the region, which is what a label a controller removes does. A fade then brings the window back to transparent while the program keeps drawing. A program that exits takes its window with it, and nothing fades a window the compositor no longer holds.

2. Name it on the Display

kubectl patch display boe-1080-display --type merge \
  -p '{"spec": {"layout": "front-desk"}}'

spec.layout is the one field a screen has for this. Change the name to change the arrangement, and delete it to return to fullscreen windows. A name that matches no Layout shows the fullscreen arrangement and reports the name under a LayoutResolved condition.

3. Label the pods

Each program claims the screen from its own namespace, exactly as the claim guide shows. The one addition is the label the region’s selector reads. The camera, in facilities:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: parking-lot-view
  namespace: facilities
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: parking-lot-view
  template:
    metadata:
      labels:
        app: parking-lot-view
        panel: parking-lot
    spec:
      resourceClaims:
        - name: screen
          resourceClaimName: front-desk-screen
      containers:
        - name: player
          image: <your mpv image>
          args: ["rtsp://cameras/parking-lot"]
          resources:
            claims:
              - name: screen

The notice board, in frontdesk, is the same shape with its own claim on the same connector and the label panel: notices. Two namespaces each hold a claim on one screen, because the screen’s draw device allows many claims at once.

When a window lands in a region, the compositor tells the program the region’s size. The program redraws at that size, the way it would if you dragged a window’s edge. A page reflows, and a video player letterboxes inside its rectangle. mpv needs --keepaspect-window=no to do that. Its default keeps the window at the film’s own aspect, and the compositor then scales that smaller buffer up to the region. A program that does not redraw at that size is scaled to fit.

4. Read what the screen shows

kubectl get display boe-1080-display -o yaml

status:
  layout:
    name: front-desk
    regions:
      - name: notices
        surface: 7c1e-1
      - name: lot
        surface: a940-1
  surfaces:
    - id: 7c1e-1
      claim: frontdesk/front-desk-screen
      pods: [frontdesk/notices-6b9d7-q2xw]
      labels: {app: notices, panel: notices}
      size: {width: 1344, height: 1080}
      region: notices
    - id: a940-1
      claim: facilities/front-desk-screen
      pods: [facilities/parking-lot-view-7d9f-x2k1]
      labels: {app: parking-lot-view, panel: parking-lot}
      size: {width: 576, height: 648}
      region: lot

Every window the compositor holds is in surfaces. Each entry has the claim the window arrived through, the pods that hold that claim, their labels, the window’s current size, and the region it is in. A window that matches no region is listed with no region, which is the first thing to read when a program is running and not on the screen. A region that shows nothing reads surface: empty.

A window for a while

A camera that should show for fifteen seconds is a Job whose image streams for fifteen seconds, with the claim and the label above. Its window appears in its region when the pod starts and leaves when the pod ends, and the region reads empty again. Nothing in the Layout reads a clock. The program controls how long its window stays.

What a Layout does not do

It does not start anything. A region with a selector and no matching pod stays empty until something creates a pod with that label and a claim on the screen. A Deployment, a Job, or an operator that makes pods fills a screen, and the Layout only says where.

It does not fill a region with two windows. A second window from a matching pod stays off the screen until the first is gone.

It does not move a window a program is drawing. A program never receives its rectangle, and it cannot ask for another one. Move the region in the Layout and every screen that names it follows.