The mjdev compositor (mjdevc) draws GNOME-style window frames itself, in the
C shim (native/shim.c), instead of letting each app draw its own titlebar (CSD).
The frame is rendered with cairo into an image surface that is wrapped in a
custom wlr_buffer and shown as a wlr_scene_buffer node, so it is GPU-composited
like any other surface and moves/raises/minimizes together with its window.
Policy (colors, what counts as an app window) is driven from the Kotlin side; the pixel-pushing lives in C. This follows the project's kotlin-first / compositor-only split: surface layering and frame rendering are genuine compositor concerns.
mjc_view_set_minimized)mjc_view_set_maximized)mjc_view_close)n/s/e/w/ne/nw/se/sw-resize),pointer (hand),default arrow.MJC_FOCUS_FOLLOWS_MOUSE in shim.c.A window is decorated when it is a normal app window. It is not decorated when:
Policy marks these via mjc_view_set_chrome(view, true)),mjc_view_set_decorated(view, false) / IPC set-decorated), or the client itself
demanded client-side decorations through the xdg-decoration protocol (we honor the
client and leave it frameless),Decoration is decoupled from the stacking layer, so always-on-top and always-on-bottom app windows keep their frame.
The frame colors come from the shell's live background palette, sent over the IPC socket. Until the shell sends a palette, a dark-slate default is used.
The desktop shell pushes them automatically: ThemeManagerLinux.createFromPalette()
(invoked whenever the wallpaper/palette changes) calls
CompositorControl.setDecorationTheme(backgroundColor, textColor)
(shared/.../helpers/compositor/CompositorControl.kt), a one-shot IPC sender that
no-ops when not running inside mjdevc. So changing the wallpaper re-colorizes every
window frame.
// rgb channels 0..255; fg is optional and defaults to a contrasting
// black/white chosen from the bg luminance
{"cmd":"set-decoration-theme","bg_r":30,"bg_g":34,"bg_b":42}
{"cmd":"set-decoration-theme","bg_r":30,"bg_g":34,"bg_b":42,"fg_r":240,"fg_g":240,"fg_b":240}
Other decoration-related IPC commands:
{"cmd":"set-decorated","id":3,"decorated":false} // chromeless / frameless
{"cmd":"always-on-top","id":3,"on":true}
{"cmd":"always-on-bottom","id":3,"on":true}
native/shim.h)void mjc_view_set_chrome(mjc_view *view, bool chrome); // shell surface, no frame
void mjc_view_set_decorated(mjc_view *view, bool decorated); // chromeless toggle
void mjc_set_decoration_theme(mjc_server *server,
float bg_r, float bg_g, float bg_b,
float fg_r, float fg_g, float fg_b); // colors in 0..1
compositor.gradle.kts adds cairo to the shim's pkg-config cflags and links it
into mjdevc.libcairo2 is declared in gradle/libs.versions.toml
(app-compositor-runtime-deps), the single source of truth that feeds the deb
Depends, the installDesktop apt step and make-iso.sh. mjdevc (the binary
that links cairo) ships via the deb and the ISO, so those are the package formats
that need the dependency.shim.c)#define MJC_DECO_TITLEBAR_H 36 // titlebar height
#define MJC_DECO_RADIUS 10 // top-corner radius
#define MJC_DECO_BORDER 3 // side/bottom border width
#define MJC_DECO_BTN_R 9 // button circle radius
#define MJC_DECO_BTN_GAP 8 // gap between buttons
#define MJC_DECO_PAD 14 // title-left / buttons-right padding
#define MJC_DECO_RESIZE 6 // edge/corner resize hotzone half-width