Using the Linux FrameBuffer and libinput

Uno Platform supports the Linux FrameBuffer and libinput as a target, in the case where your target device does not provide a Window Manager.

There are some restrictions for the support for the FrameBuffer:

  • The mouse is supported through pointer events, but Uno Platform does not show the pointer for your app. You'll need to display one using the pointer events provided by Uno Platform (also in the CoreWindow class).
  • It is only supported on Linux where /dev/fbXX is available.

Get started with the FrameBuffer

Create a new app using:

dotnet new unoapp -o MyApp

In the Platforms/Desktop/Program.cs file, the UnoPlatformHostBuilder can be configured to use the Framebuffer support, in case X11 support is detected first:

var host = UnoPlatformHostBuilder.Create()
    .App(() => new App())
    .UseX11()
    .UseLinuxFrameBuffer()
    .UseMacOS()
    .UseWindows()
    .Build();

Each platform support is evaluated in order for availability and definition in builder. X11 is chosen when the DISPLAY variable is present.

Running the app

You can build and run this app by navigating to the MyApp and type the following:

dotnet run -f net10.0-desktop

The app will start and display on the first available framebuffer device. To change the active framebuffer, set the device name in the FRAMEBUFFER environment variable.

By default, the Debug configuration is used, which will show logging information in the current terminal and may overwrite the UI content.

To read the logging information, either:

  • Launch the application from a different terminal (through SSH, for instance)
  • Launch the app using dotnet run -f net10.0-desktop > logging.txt 2>&1, then launch tail -f logging.txt in another terminal.

Once the application is running, you can exit the application with:

  • Ctrl+C
  • F12, a key configuration found in the Program.cs file of your project which invokes Application.Current.Exit()

Creating a standalone app

You can create a standalone publication folder using the following:

dotnet publish -c Release -f net10.0-desktop -r linux-x64 --self-contained true
Note

When using the Release configuration, logging is disabled for performance considerations. You can restore logging in the App.xaml.cs file.

Documentation on other hardware targets is available here.

Configuration Options

The UseLinuxFrameBuffer method accepts an optional configuration callback that provides access to the FramebufferHostBuilder. This builder exposes options for rendering, input, display orientation, and keyboard layout.

var host = UnoPlatformHostBuilder.Create()
    .App(() => new App())
    .UseLinuxFrameBuffer(fb => fb
        .EnableMouseCursor(5, Color.FromArgb(255, 255, 255, 255))
        .Orientation(DisplayOrientations.Portrait)
        .UseKMSDRM()
        .XkbKeymap(new FramebufferHostBuilder.XKBKeymapParams(layout: "us"))
        .ReverseMouseWheel()
    )
    .Build();

Mouse Cursor

By default, the mouse cursor is shown only after the first mouse event is received from libinput. If only touch events are received (e.g. on a touchscreen), no cursor is displayed.

You can override this behavior explicitly:

  • EnableMouseCursor(float radius, Color color) — Always show the cursor, rendered as a small filled circle with the specified radius (in pixels) and color.
  • DisableMouseCursor() — Never show the cursor, even when mouse events are received.
// Show a white cursor circle with radius 5
fb.EnableMouseCursor(5, Color.FromArgb(255, 255, 255, 255));

// Or hide the cursor entirely
fb.DisableMouseCursor();

Mouse Wheel Direction

By default, the framebuffer host inverts the raw libinput scroll values so that scrolling matches the conventional direction used by desktop environments.

  • ReverseMouseWheel(bool reverse = true) — Disables the default inversion and uses the raw libinput scroll values directly. This is useful on devices where the natural scrolling direction from libinput already matches the expected behavior.
fb.ReverseMouseWheel();

Display Orientation

  • Orientation(DisplayOrientations orientation) — Sets the display orientation. The default is DisplayOrientations.Landscape. This rotates how pointer coordinates and rendering are mapped to the framebuffer.

Available values: Landscape, Portrait, LandscapeFlipped, PortraitFlipped.

fb.Orientation(DisplayOrientations.Portrait);

Hardware-Accelerated Rendering (KMS/DRM)

By default, the framebuffer host attempts to create an OpenGL ES context via KMS/DRM for hardware-accelerated rendering. If that fails, it falls back to software rendering automatically.

  • UseKMSDRM(string? cardPath, DRMFourCCColorFormat? gbmSurfaceColorFormat, DRMConnectorChooserDelegate? connectorChooser) — Explicitly enables hardware-accelerated rendering via KMS/DRM.

    • cardPath — Path to the DRM device file (e.g. /dev/dri/card0). If null, the first available /dev/dri/cardX device is used.
    • gbmSurfaceColorFormat — The FourCC color format for the GBM surface. Defaults to ARGB8888. See the DRM FourCC header for valid values.
    • connectorChooser — A delegate that receives the list of available DRM connectors and returns the index of the one to use, or -1. If not provided, the first connector is used.
  • DisableKMSDRM() — Forces software rendering, skipping any attempt to use KMS/DRM.

// Explicitly enable DRM with a specific card
fb.UseKMSDRM(cardPath: "/dev/dri/card1");

// Or force software rendering
fb.DisableKMSDRM();

// Choose a specific connector
fb.UseKMSDRM(connectorChooser: connectors =>
{
    for (int i = 0; i < connectors.Count; i++)
    {
        if (connectors[i].connectorStringRepresentation.Contains("HDMI"))
            return i;
    }
    return 0;
});

Keyboard Layout (XKB Keymap)

  • XkbKeymap(XKBKeymapParams keymapParams) — Configures the RMLVO parameters passed to libxkbcommon for keyboard keymap creation. If not called, the system default keymap is used.

The XKBKeymapParams record accepts the following optional parameters: rules, model, layout, variant, and options. For details on RMLVO, see the libxkbcommon documentation.

// Use a French AZERTY layout
fb.XkbKeymap(new FramebufferHostBuilder.XKBKeymapParams(layout: "fr"));

// Use a US layout with the Dvorak variant
fb.XkbKeymap(new FramebufferHostBuilder.XKBKeymapParams(layout: "us", variant: "dvorak"));

DPI Scaling Support

Whenever possible, the FrameBufferHost will try to detect the actual DPI scale to use when rendering the UI, based on the physical information provided by the FrameBuffer driver. If the value cannot be determined, a scale of 1.0 is used.

The automatic scaling can be overridden in two ways:

  • Set a value using FrameBufferHost.DisplayScale.
  • Set a value through the UNO_DISPLAY_SCALE_OVERRIDE environment variable. This value has precedence over the value specified in FrameBufferHost.DisplayScale.

Additional Dependencies

If your device is significantly trimmed down for installed packages, you'll need to install:

  • libfontconfig
  • libfreetype
  • libinput