Grafik & Görüntü
TEKNİK REHBER GRAFİK & GÖRÜNTÜ WAYLAND / WESTON 2026

Wayland / Weston —
embedded compositor.

X11'in karmaşıklığı olmadan modern display server mimarisi. Weston kiosk shell ile tek-uygulama HMI, EGL/GLES2 ile donanım hızlandırmalı render ve Yocto entegrasyonu.

00 Wayland mimarisi

Wayland, display server protokolünü yeniden tasarlar. X11'in "server aracılar" mimarisinin aksine, compositor doğrudan display donanımıyla konuşur; client'lar compositing sonucunu görmez.

X11 vs Wayland

X11 modeli
Client → X Server → Compositor (WM). İki soyutlama katmanı, gereksiz kopyalama ve gecikme. Network transparent ama modern grafik için fazla yük.
Wayland modeli
Client → Compositor (= display server). Compositor hem pencere yöneticisi hem display server. Daha az gecikme, doğal GPU entegrasyonu.
Protocol
Unix domain socket üzerinden ikili mesajlar. wayland.xml interface tanımları, wayland-scanner ile C stub'larına derlenir.

Temel protokol nesneleri

  wl_display        — sunucu bağlantısı, event loop
       ↓
  wl_registry       — global nesne listesi (compositor, shm, seat...)
       ↓
  wl_compositor     — wl_surface oluşturur
       ↓
  wl_surface        — ham piksel tamponu (boyutsuz)
       ↓
  xdg_surface       — pencere soyutlaması (xdg-shell.xml)
       ↓
  xdg_toplevel      — maximize, minimize, resize, title
    

xdg-shell protokolü

Standart pencere yönetimi için xdg-shell.xml kullanılır. Kiosk shell ise xdg-shell yerine weston-kiosk-shell kullanır ve fullscreen zorunlu kılar.

01 Weston kurulumu ve yapılandırması

Weston, referans Wayland compositor'ıdır. Embedded kullanımda drm backend ile DRM/KMS üzerinde doğrudan çalışır; X11 gerektirmez.

weston.ini temel yapılandırması

/etc/xdg/weston/weston.ini
[core]
backend=drm-backend.so    # KMS/DRM backend
shell=kiosk-shell.so      # kiosk: tek uygulama
idle-time=0               # screensaver kapalı
require-input=false       # input cihazı olmasa da başla

[output]
name=HDMI-A-1
mode=1920x1080@60         # veya "preferred", "current"
transform=normal          # rotate-90, rotate-180, flipped
scale=1

[output]
name=DSI-1
mode=1024x600@60

[keyboard]
keymap_rules=evdev
keymap_layout=tr          # Türkçe klavye düzeni

[input-method]
path=/usr/libexec/weston-keyboard   # ekran klavyesi

Backend seçimi

drm-backend.so
Production seçimi. DRM/KMS üzerinde çalışır, libseat ile VT geçişi yönetir. Donanım hızlandırmayı tam destekler.
fbdev-backend.so
Legacy framebuffer backend. GPU hızlandırması yok, performansı düşük ama çok eski donanımda çalışır.
headless-backend.so
Ekran olmadan test için. CI ortamlarında veya offscreen render senaryosunda kullanılır.
libseat
logind'e gerek kalmadan DRM master izni alır. Yocto'da seatd ile birlikte kullanılır.

Weston başlatma

bash
# Doğrudan başlat (TTY1'de, root olarak)
weston --backend=drm-backend.so \
       --shell=kiosk-shell.so \
       --config=/etc/xdg/weston/weston.ini

# WAYLAND_DISPLAY soket adını belirt
WAYLAND_DISPLAY=wayland-1 weston &

# Compositor logunu ayrıntılı göster
weston --log=/var/log/weston.log --verbose

02 Kiosk shell — fullscreen tek uygulama HMI

Weston kiosk shell, embedded HMI ürünleri için tasarlanmıştır: tek uygulama, fullscreen, pencere çerçevesi yok, görev çubuğu yok.

Kiosk shell özellikleri

Fullscreen zorlama
Her uygulama otomatik olarak tam ekran yapılır. Kullanıcı boyut değiştiremez.
Input method entegrasyonu
weston-keyboard sanal klavyesi, text-input-v1 protokolü üzerinden etkinleştirilir.
Uygulama değişimi yok
Alt+Tab veya pencere listesi yoktur. Birden fazla uygulama başlatılırsa üst üste kapanır.

kiosk.ini — uygulama başlatma

weston.ini — kiosk launcher
[kiosk-shell]
# Weston başladığında otomatik uygulamayı başlat
launcher=/usr/bin/my-hmi-app

# Alternatif: systemd servisi ile (önerilen)
# [core] bölümünde: modules=systemd-notify.so

Dokunmatik ekran kiosk — tam senaryo

bash — kiosk kurulumu
# Weston kiosk servisini kur
cat > /etc/systemd/system/weston.service <<'EOF'
[Unit]
Description=Weston Wayland Compositor
After=systemd-udevd.service

[Service]
Type=notify
User=weston
Environment=WAYLAND_DISPLAY=wayland-0
Environment=XDG_RUNTIME_DIR=/run/user/1000
ExecStart=/usr/bin/weston \
    --backend=drm-backend.so \
    --shell=kiosk-shell.so
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

systemctl enable weston
systemctl start weston

# HMI uygulamasını kiosk içinde başlat
cat > /etc/systemd/system/hmi-app.service <<'EOF'
[Unit]
After=weston.service
Requires=weston.service

[Service]
Environment=WAYLAND_DISPLAY=wayland-0
Environment=XDG_RUNTIME_DIR=/run/user/1000
ExecStart=/usr/bin/my-hmi-app
Restart=always
EOF

03 Wayland client yazma

Wayland client'ı yazmak X11'e göre daha kurallıdır: her şey protokol üzerinden nesne oluşturma ve event işleme döngüsüne dayanır.

Temel bağlantı ve registry

C — minimal Wayland client
#include <wayland-client.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct wl_compositor *compositor = NULL;
struct wl_shm        *shm        = NULL;

/* registry'de yeni global nesne bildirildiğinde çağrılır */
static void registry_handler(void *data,
    struct wl_registry *registry,
    uint32_t id, const char *interface, uint32_t version)
{
    if (strcmp(interface, "wl_compositor") == 0) {
        compositor = wl_registry_bind(registry, id,
            &wl_compositor_interface, 4);
    } else if (strcmp(interface, "wl_shm") == 0) {
        shm = wl_registry_bind(registry, id,
            &wl_shm_interface, 1);
    }
}

static void registry_remover(void *data,
    struct wl_registry *registry, uint32_t id) {}

static const struct wl_registry_listener registry_listener = {
    .global        = registry_handler,
    .global_remove = registry_remover,
};

int main(void)
{
    struct wl_display  *display  = wl_display_connect(NULL);
    struct wl_registry *registry = wl_display_get_registry(display);

    wl_registry_add_listener(registry, &registry_listener, NULL);
    wl_display_roundtrip(display);  /* registry olaylarını işle */

    struct wl_surface *surface = wl_compositor_create_surface(compositor);

    /* shared memory buffer oluştur ve içeriği yaz... */

    wl_surface_commit(surface);

    while (wl_display_dispatch(display) != -1)
        ;   /* event döngüsü */

    wl_display_disconnect(display);
    return 0;
}

wl_shm ile shared memory buffer

C — SHM buffer
#include <sys/mman.h>
#include <fcntl.h>

int create_shm_buffer(struct wl_shm *shm, int width, int height,
                      struct wl_buffer **out_buf, void **out_data)
{
    int stride = width * 4;           /* ARGB8888: 4 byte/piksel */
    int size   = stride * height;

    /* anonim geçici dosya */
    int fd = memfd_create("wl_shm_buf", MFD_CLOEXEC);
    ftruncate(fd, size);

    void *data = mmap(NULL, size,
        PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
    struct wl_buffer *buf = wl_shm_pool_create_buffer(pool,
        0, width, height, stride, WL_SHM_FORMAT_ARGB8888);
    wl_shm_pool_destroy(pool);
    close(fd);

    *out_buf  = buf;
    *out_data = data;
    return 0;
}

04 EGL ve OpenGL ES — donanım hızlandırmalı render

Yüksek performanslı render için wl_shm yerine EGL window surface kullanılır. Bu yaklaşımda GPU doğrudan Wayland surface'ine render eder.

EGL başlatma

C — EGL init
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <wayland-egl.h>

/* EGL display al — Wayland platform */
EGLDisplay egl_display =
    eglGetDisplay((EGLNativeDisplayType)wl_display);

eglInitialize(egl_display, NULL, NULL);
eglBindAPI(EGL_OPENGL_ES_API);

/* EGL config seç */
EGLint attribs[] = {
    EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    EGL_RED_SIZE,   8,
    EGL_GREEN_SIZE, 8,
    EGL_BLUE_SIZE,  8,
    EGL_ALPHA_SIZE, 8,
    EGL_NONE
};
EGLConfig config;
EGLint num_configs;
eglChooseConfig(egl_display, attribs, &config, 1, &num_configs);

/* GLES2 context */
EGLint ctx_attribs[] = {
    EGL_CONTEXT_CLIENT_VERSION, 2,
    EGL_NONE
};
EGLContext context = eglCreateContext(egl_display,
    config, EGL_NO_CONTEXT, ctx_attribs);

Wayland EGL window surface

C — EGL window surface
/* wl_egl_window: Wayland surface'ini EGL'e bağlar */
struct wl_egl_window *egl_window =
    wl_egl_window_create(wl_surface, width, height);

EGLSurface egl_surface =
    eglCreateWindowSurface(egl_display, config,
        (EGLNativeWindowType)egl_window, NULL);

eglMakeCurrent(egl_display, egl_surface, egl_surface, context);

/* GLES2 ile kırmızı ekran */
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

/* swap — compositor'a teslim et */
eglSwapBuffers(egl_display, egl_surface);
wl_display_flush(wl_display);

GLES2 üçgen örneği

GLSL + C — üçgen
const char *vert_src =
    "attribute vec2 pos;\n"
    "void main() { gl_Position = vec4(pos, 0.0, 1.0); }\n";

const char *frag_src =
    "precision mediump float;\n"
    "void main() { gl_FragColor = vec4(0.0, 0.8, 0.2, 1.0); }\n";

/* shader derleme (compile_shader yardımcı fonksiyonu varsayılıyor) */
GLuint prog = link_program(vert_src, frag_src);
glUseProgram(prog);

GLfloat verts[] = { 0.0f, 0.5f,  -0.5f, -0.5f,  0.5f, -0.5f };
GLint pos_loc = glGetAttribLocation(prog, "pos");
glVertexAttribPointer(pos_loc, 2, GL_FLOAT, GL_FALSE, 0, verts);
glEnableVertexAttribArray(pos_loc);

glDrawArrays(GL_TRIANGLES, 0, 3);
eglSwapBuffers(egl_display, egl_surface);

05 Input handling — pointer, keyboard, touch

wl_seat nesnesi tüm input cihazlarını (fare, klavye, dokunmatik ekran) kapsayan soyutlamadır. Her bir cihaz için ayrı listener kaydedilir.

wl_seat ve input nesneleri

C — seat ve touch listener
/* wl_seat'i registry'den al */
struct wl_seat *seat = wl_registry_bind(registry, id,
    &wl_seat_interface, 5);

/* wl_touch al */
struct wl_touch *touch = wl_seat_get_touch(seat);

/* touch event listener */
static void touch_down(void *data, struct wl_touch *wl_touch,
    uint32_t serial, uint32_t time,
    struct wl_surface *surface,
    int32_t id, wl_fixed_t x, wl_fixed_t y)
{
    double fx = wl_fixed_to_double(x);
    double fy = wl_fixed_to_double(y);
    printf("Touch DOWN: id=%d, x=%.1f, y=%.1f\n", id, fx, fy);
}

static void touch_up(void *data, struct wl_touch *wl_touch,
    uint32_t serial, uint32_t time, int32_t id)
{
    printf("Touch UP: id=%d\n", id);
}

static void touch_motion(void *data, struct wl_touch *wl_touch,
    uint32_t time, int32_t id,
    wl_fixed_t x, wl_fixed_t y) {}
static void touch_frame(void *data, struct wl_touch *wl_touch) {}
static void touch_cancel(void *data, struct wl_touch *wl_touch) {}

static const struct wl_touch_listener touch_listener = {
    .down   = touch_down,
    .up     = touch_up,
    .motion = touch_motion,
    .frame  = touch_frame,
    .cancel = touch_cancel,
};

wl_touch_add_listener(touch, &touch_listener, NULL);

Dokunmatik ekran kalibrasyonu

bash — xinput kalibrasyon (xwayland ile)
# libinput ile dokunma noktalarını izle
libinput debug-events --device /dev/input/event1

# Weston için evdev kalibrasyon matrisi
# weston.ini [libinput] bölümüne:
# calibration_matrix=1.0 0.0 0.0  0.0 1.0 0.0
# Döndürülmüş panel için (90 derece CCW):
# calibration_matrix=0.0 -1.0 1.0  1.0 0.0 0.0

# Yanlış yönlendirme için transform:
weston-calibrator   # interaktif kalibrasyon aracı
MULTI-TOUCH

Wayland, çoklu dokunma noktasını id parametresi ile takip eder. Her parmak farklı bir id alır; touch_down ile başlar, touch_up ile biter. Gesture kütüphaneleri bu ham olayları jestlere dönüştürür.

06 Yocto Wayland image — meta-weston, systemd servisi

Yocto ile Wayland/Weston içeren minimal embedded image oluşturmak için DISTRO_FEATURES ve IMAGE_INSTALL yapılandırması gerekir.

local.conf yapılandırması

conf/local.conf
# Wayland desteğini etkinleştir, X11'i çıkar
DISTRO_FEATURES:append = " wayland opengl"
DISTRO_FEATURES:remove = " x11"

# systemd kullan (önerilen Weston ile)
DISTRO_FEATURES:append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"

# GPU desteği — RPi4 için
MACHINE_FEATURES:append = " vc4graphics"

IMAGE_INSTALL ve bağımlılıklar

recipes-core/images/weston-hmi-image.bb
require recipes-graphics/images/core-image-weston.bb

IMAGE_INSTALL:append = " \
    weston             \
    weston-init        \
    weston-examples    \
    libdrm             \
    mesa               \
    mesa-megadriver    \
    wayland-utils      \
    libinput           \
    seatd              \
    my-hmi-application \
"

# weston.ini'yi image'a dahil et
ROOTFS_POSTPROCESS_COMMAND:append = " install_weston_config; "

install_weston_config() {
    install -d ${IMAGE_ROOTFS}/etc/xdg/weston
    install -m 0644 ${THISDIR}/files/weston.ini \
        ${IMAGE_ROOTFS}/etc/xdg/weston/weston.ini
}

meta-weston systemd servisi

bash — Yocto build
# meta-weston katmanını ekle
bitbake-layers add-layer ../sources/meta-openembedded/meta-oe
bitbake-layers add-layer ../sources/meta-raspberrypi  # RPi için

# image derle
bitbake weston-hmi-image

# Weston init servisi otomatik gelir:
# /lib/systemd/system/weston.service
# /lib/systemd/system/weston@.service (multi-user)

07 XWayland — X11 uygulama uyumluluğu

XWayland, Wayland compositor içinde tam X11 server emülasyonu sunar. Native Wayland desteği olmayan Qt5, GTK3 veya SDL2 uygulamaları XWayland üzerinden çalışır.

XWayland nasıl çalışır

  X11 Uygulama
       ↓  DISPLAY=:0 bağlantısı
  XWayland Server
       ↓  Wayland client olarak
  Weston Compositor
       ↓  DRM/KMS
  Fiziksel Ekran
    

Weston'da XWayland etkinleştirme

weston.ini + bash
# weston.ini [core] bölümüne ekle:
[core]
xwayland=true

# Yocto'da xwayland paketi ekle:
IMAGE_INSTALL:append = " xwayland"

# X11 uygulamasını Wayland ortamında başlat
export DISPLAY=:0
export WAYLAND_DISPLAY=wayland-0

# XWayland otomatik olarak başlar, DISPLAY değişkeni ayarlanır
# Mevcut uygulamaları sınama:
DISPLAY=:0 xterm
DISPLAY=:0 glxinfo | grep "OpenGL renderer"
PERFORMANS NOTU

XWayland üzerinden çalışan uygulamalar bir Wayland buffer kopyasından geçer — native Wayland client'lara göre biraz daha yavaştır. Mümkünse uygulamaları native Wayland desteğine taşı: Qt5 için -platform wayland, SDL2 için SDL_VIDEODRIVER=wayland.

08 Pratik

Üç gerçek senaryo: i.MX8 Weston kiosk HMI, Raspberry Pi CM4 dual display ve weston-terminal ile serial konsol.

i.MX8 Weston kiosk HMI — Python + GTK4/Wayland

Python — GTK4 Wayland kiosk penceresi
import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, GLib

class KioskWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        super().__init__(application=app, title="HMI")
        # Fullscreen — kiosk shell zaten zorlar ama güvenlik için
        self.fullscreen()
        self.set_decorated(False)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
        box.set_margin_top(40)

        label = Gtk.Label(label="Üretim Hattı Monitörü")
        label.add_css_class("title-1")
        box.append(label)

        btn = Gtk.Button(label="Başlat")
        btn.connect("clicked", self.on_start)
        box.append(btn)

        self.set_child(box)

    def on_start(self, btn):
        print("Üretim başlatıldı")

class App(Gtk.Application):
    def __init__(self):
        super().__init__(application_id="com.example.hmi")
        self.connect("activate", self.on_activate)

    def on_activate(self, app):
        win = KioskWindow(app)
        win.present()

if __name__ == "__main__":
    import sys
    App().run(sys.argv)
bash — çalıştırma
export WAYLAND_DISPLAY=wayland-0
export XDG_RUNTIME_DIR=/run/user/1000
GDK_BACKEND=wayland python3 hmi_app.py

Raspberry Pi CM4 — dual display konfigürasyonu

weston.ini — dual output
[output]
name=HDMI-A-1
mode=1920x1080@60
transform=normal

[output]
name=HDMI-A-2
mode=1280x720@60
x=1920          # ikinci ekran yatayda yanına
y=0

weston-terminal ile serial konsol

bash
# Weston compositor çalışırken terminal başlat
WAYLAND_DISPLAY=wayland-0 weston-terminal

# SSH üzerinden uzak terminal (headless debug)
WAYLAND_DISPLAY=wayland-0 \
    DISPLAY= \
    weston-terminal --shell=/bin/bash

# Üretim sisteminde debug: kiosk'u bypass etmeden
# weston-terminal'i gizli touch kombinasyonu ile aç
# (4 köşeye aynı anda 3 saniye basma gibi)