Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Mosaic

A lightweight Linux system shell built with Qt 6 and Qt Quick for industrial
A lightweight Linux system shell built with Qt 6 Widgets for industrial
embedded devices.

## Documentation
Expand Down
1 change: 1 addition & 0 deletions apps/dummy-application/mosaic-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"license": "GPL-3.0-only",
"license_file": "LICENSE",
"app": {
"ui_toolkit": "quick",
"source_dir": "app",
"executable": "dummy-application",
"arguments": []
Expand Down
16 changes: 9 additions & 7 deletions docs/app-generation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ rendering invoke neither Cargo nor the network. Yocto fetches the crates, and
Cargo retains its standard frozen build and executable installation tasks.

Manifest build and runtime dependencies extend ``DEPENDS`` and ``RDEPENDS:${PN}``,
with duplicate names removed and lists sorted. Application build dependencies
include ``qtbase``, ``qtdeclarative``, and ``qtdeclarative-native``. Runtime
dependencies include ``qtbase-plugins``, ``qtdeclarative-qmlplugins``, and
``qtwayland-plugins``. Services receive no automatic Qt dependencies.
with duplicate names removed and lists sorted. ``app.ui_toolkit`` selects the
automatic Qt dependencies listed in :doc:`applications`. Widgets applications
do not automatically depend on Qt Quick. Services receive no automatic Qt
dependencies. Display-platform packages are supplied by the product session.
Linked libraries and upstream package recommendations also contribute to the
final image.

Each application package depends on its required service packages, named
``mosaic-service-<app-id>-<service-id>``. Service recipes append installation of
Expand All @@ -59,9 +61,9 @@ Each application recipe generates Qt Application Manager (QAM) metadata during
installation at ``${datadir}/mosaic/apps/<id>/info.yaml`` (normally
``/usr/share/mosaic/apps/<id>/info.yaml``). The application package includes this
directory. See :doc:`applications` for the application authoring contract.
Shell integration must configure QAM discovery of this directory and display
installed application names. Native QAM applications require Wayland support
in the distro and compositor integration in the shell.
Generating this metadata does not install or start QAM. Multi-process display
integration requires a compositor, its client platform plugins, and a launcher
that discovers the installed application metadata.

Tool files
----------
Expand Down
82 changes: 70 additions & 12 deletions docs/applications.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ To add an application:
1. Create ``apps/<your-app>/`` with a ``mosaic-app.json`` manifest, Qt/CMake
application code, and a license file. Use ``apps/dummy-application/`` as an
example.
2. Set the manifest's application ID, display name, source directory, and
executable name. Add any dependencies and optional services.
2. Set the manifest's application ID, display name, source directory, executable
name and UI toolkit. Add any dependencies and optional services.
3. Add the CMake installation rules shown below, then build your chosen image
using the commands in :doc:`building`.

Expand All @@ -16,8 +16,27 @@ Build selection uses the manifest:
- ``enabled``: enable or exclude the bundle in both profiles.
- ``include_in_prod``: also include the enabled bundle in PROD.

Selection includes the bundle's required services. Applications start when
selected from the launcher after unlock. Services run independently.
Selection includes the bundle's required services. Services run independently.

UI toolkit and display compatibility
------------------------------------

Mosaic's shell uses Qt Widgets. Application bundles may use Qt Widgets or
Qt Quick, selected by the required ``app.ui_toolkit`` field. This controls build
and runtime dependencies, not application launch or display ownership.

The QEMU session uses a direct CPU-rendered display owned by the shell. Running
independent graphical applications alongside it requires a compositor and
launcher integration. The current session does not provide application
composition or guaranteed notification stacking. A future Wayland session must
supply its platform plugins and define the application window protocol.

Applications must work without a GPU. For Qt Quick, use
``QT_QUICK_BACKEND=software`` and avoid shader and particle effects. Widgets
applications must also avoid GPU-dependent components such as ``QOpenGLWidget``.
Test scrolling, animation, scaling and responsiveness on the intended device.
See `Qt's software renderer limitations
<https://doc.qt.io/qt-6.11/qtquick-visualcanvas-adaptations-software.html>`_.

Manifest fields
---------------
Expand All @@ -40,9 +59,11 @@ The following top-level fields are required:
- ``license_file`` points to the bundle's license file.
- ``app`` describes the application build and executable.

``app`` requires ``source_dir`` and ``executable``. The source directory contains
``CMakeLists.txt``. The executable is an installed program name, starting with
a letter or digit and containing only letters, digits, dots, underscores,
``app`` requires ``source_dir``, ``executable``, and ``ui_toolkit``.
``ui_toolkit`` accepts exactly ``"widgets"`` or ``"quick"``, with no default.
Missing or invalid values are errors, including in disabled bundles. The source
directory contains ``CMakeLists.txt``. The executable is an installed program
name, starting with a letter or digit and containing only letters, digits, dots, underscores,
hyphens, or ``+``. Arguments are supplied separately in the optional
``arguments`` string list, which defaults to ``[]``. Empty argument strings are
allowed, while NUL characters are rejected.
Expand All @@ -52,9 +73,27 @@ Both ``app`` and each service accept optional ``build_dependencies`` and
names and runtime package names respectively. Names start with a lowercase letter
or digit and contain lowercase letters, digits, dots, hyphens, or ``+``. Entries
are plain names without version constraints or BitBake expressions and must be
available in your configured Yocto layers. The application's standard Qt Quick
dependencies are already included. Declare any additional libraries, and declare
service dependencies separately.
available in your configured Yocto layers. The toolkit's standard dependencies
are included:

.. list-table:: Automatic application dependencies
:header-rows: 1

* - ``ui_toolkit``
- Build recipes
- Explicit runtime packages
* - ``widgets``
- ``qtbase``
- ``qtbase-plugins``
* - ``quick``
- ``qtbase``, ``qtdeclarative``, ``qtdeclarative-native``
- ``qtbase-plugins``, ``qtdeclarative-qmlplugins``

Yocto also resolves linked-library dependencies and upstream package
recommendations. These are aggregate Qt packages, not individual plugin
selections. Additional dependencies extend this baseline and are deduplicated.
Declare service dependencies separately. Display-platform dependencies belong
to the product session.

``services`` is an optional list, defaulting to ``[]``. Each service requires:

Expand Down Expand Up @@ -90,17 +129,36 @@ sets it from ``app.executable``. Install the target into
cmake_minimum_required(VERSION 3.16)
project(example LANGUAGES CXX)
include(GNUInstallDirs)
find_package(Qt6 REQUIRED COMPONENTS Quick)
find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets)
set(MOSAIC_APP_EXECUTABLE "example" CACHE STRING "Installed application name")
qt_add_executable(example_target main.cpp)
target_link_libraries(example_target PRIVATE Qt6::Quick)
target_link_libraries(example_target PRIVATE Qt6::Widgets)
set_target_properties(example_target PROPERTIES
OUTPUT_NAME "${MOSAIC_APP_EXECUTABLE}")
install(TARGETS example_target RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
install(DIRECTORY resources/
DESTINATION "${CMAKE_INSTALL_DATADIR}/mosaic/apps/example")
This example uses ``ui_toolkit: "widgets"``. Its ``main.cpp`` creates a
``QApplication`` and shows a widget. For ``ui_toolkit: "quick"``, replace the Qt
discovery and executable setup above with:

.. code-block:: cmake
find_package(Qt6 6.5 REQUIRED COMPONENTS Quick)
qt_standard_project_setup(REQUIRES 6.5)
qt_add_executable(example_target main.cpp)
qt_add_qml_module(example_target
URI Example
QML_FILES Main.qml)
target_link_libraries(example_target PRIVATE Qt6::Quick)
Keep the same output-name and installation rules. The Quick entry point creates
a ``QGuiApplication`` and ``QQmlApplicationEngine``, then loads ``Example.Main``
with ``engine.loadFromModule("Example", "Main")``. Declare additional Qt modules
in CMake when used.

The installed executable must match ``app.executable`` or the build fails.

Example manifest
Expand Down
95 changes: 64 additions & 31 deletions docs/building.rst
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ QEMU startup

QEMU images boot to ``graphical.target``, which starts ``mosaic-shell.service``.
The service runs ``/usr/bin/mosaic-shell`` on VT1 as the locked,
non-login ``mosaic`` account with ``video``, ``input``, and ``render`` group membership. It waits
non-login ``mosaic`` account with ``video`` and ``input`` group membership. It waits
for DRM card0, VT1, and systemd user-session initialization, and conflicts with
``getty@tty1.service``. If VT1 already has a controlling process, startup fails
instead of waiting for that process to release it. Use the serial console for
Expand All @@ -175,33 +175,67 @@ The product layer installs ``/etc/mosaic/session.env`` with:

.. code-block:: text
QT_QPA_PLATFORM=eglfs
QT_QPA_EGLFS_INTEGRATION=eglfs_kms
QT_QPA_EGLFS_KMS_CONFIG=/etc/mosaic/eglfs_kms.json
QT_QPA_EGLFS_HIDECURSOR=1
QSG_RHI_BACKEND=opengl
QT_SHADER_CACHE_PATH=/var/cache/mosaic
These settings select EGLFS/KMS and Qt Quick's OpenGL renderer. The installed
``/etc/mosaic/eglfs_kms.json`` selects ``/dev/dri/card0`` and disables the hardware
cursor. The session hides the pointer and stores shader cache files in its
persistent cache directory. Udev rules grant the ``video`` group access to card0
and the ``render`` group access to DRM render nodes.

The QEMU distro override enables ``opengl``. The pinned Qt recipe then enables
``gbm``, ``gles2``, and ``eglfs`` alongside ``kms``, ``libinput``, and ``udev``;
Mesa provides EGL, GLES, GBM, and runtime drivers. The session includes Mesa's
runtime drivers explicitly because they are loaded dynamically, along with Qt
platform/QML plugins and DejaVu Sans. The standard virtio GPU uses Mesa's
software rendering fallback, so host GPU acceleration is not required.
This uses the OpenGL rendering pipeline rather than Qt Quick's software backend.
STM32 graphics configuration is unchanged.
QT_QPA_PLATFORM=linuxfb
QT_QPA_FB_DRM=1
QT_QUICK_BACKEND=software
The shell uses Qt Widgets raster painting. LinuxFB uses DRM dumb buffers and
page flipping instead of EGL/OpenGL. This requires DRM dumb-buffer support from
the display driver. Qt Quick applications use their dedicated software renderer.
The session owns the display directly and is not a multi-process compositor.

The QEMU distro no longer enables OpenGL. The pinned Qt recipe supplies
``no-opengl``, ``linuxfb``, ``kms``, ``libinput`` and ``udev`` support. The session
selects Qt platform plugins and DejaVu Sans, without explicitly selecting Mesa
drivers or QML packages. Upstream Qt plugin packages remain aggregates with
their normal dependencies and recommendations. STM32 startup configuration is
unchanged.

Product graphics policy belongs in ``meta-mosaic-product``, including the service,
environment file, KMS configuration, and udev rules under
``recipes-shell/mosaic-session/``. To change the display setup on a running image,
edit ``/etc/mosaic/session.env`` or ``/etc/mosaic/eglfs_kms.json`` and restart the service.
A missing environment file or failed display/QML startup fails the service.
environment file and udev rules under ``recipes-shell/mosaic-session/``. To change
the display setup, edit ``/etc/mosaic/session.env`` and restart the service.
A missing environment file or failed display startup fails the service.

Native shell development
------------------------

With Qt 6.5 or newer, CMake, a C++17 compiler and PAM development files installed,
build the shell independently of Yocto from the repository root:

.. code-block:: bash
qt-cmake -S shell -B shell/build -DBUILD_TESTING=ON
cmake --build shell/build
ctest --test-dir shell/build --output-on-failure
./shell/build/app
Use the native desktop platform on macOS, without the Linux session environment.
The tests use a fake authentication backend. The application uses the real PAM
backend and does not provide a development password or bypass. Native UI checks
do not require changing the host's PAM configuration and do not establish target
authentication or graphics behavior.

Target resource verification
----------------------------

On a Linux builder, inspect the production image package manifest with all
application bundles disabled. Confirm the shell-only dependency path does not
install Qt Quick/QML or Mesa. Repeat with a Widgets application and a Quick
application selected, checking that each includes its required services and
runtime. Investigate transitive dependencies and recommendations rather than
assuming that removing an explicit package removes it from the image.

On the reference device, verify boot, display, keyboard/touch input and PAM.
Measure platform memory against the below-100 MiB normal-use and below-150 MiB
scenario limits. Include the kernel, required platform services, Qt and any
compositor, excluding product-specific applications and services. Report total
device memory separately, with consistent shared-memory and cache accounting.
Record image revision, resolution, workload, startup time and idle CPU usage.
When compositor and notification integration is available, verify critical-event
receipt-to-visible latency below one second while an application is active.

Native builds cannot verify the final package closure, Linux display startup,
embedded memory savings or notification latency.

Lock-screen authentication
--------------------------
Expand Down Expand Up @@ -236,8 +270,7 @@ Distribution policy
dev and prod policy. Mosaic uses Poky with glibc, systemd for init and device
management, and journald for logging.

``MOSAIC_APP_LIFECYCLE`` selects the application lifecycle policy:
``terminate`` (default) for running only one application at a time, or
``background`` for keeping applications running in the background after closing
or switching away from them. Override it in ``build/conf/local.conf``. This changes
reponsiveness and resource use of the system based on needs.
``MOSAIC_APP_LIFECYCLE`` reserves the lifecycle policy values ``terminate``
(default) and ``background``. The shell recipe validates this setting, but it
does not implement launching, switching or retaining applications. The intended
normal policy is to terminate the active application before starting another.
9 changes: 5 additions & 4 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
Mosaic documentation
====================

Mosaic is a lightweight Linux system shell built with Yocto Project, Qt 6 and
Qt Quick for industrial embedded devices. It provides a lock screen, application
launcher, status bar and shared notifications. Developers add their own Qt
applications and background services without changing the shell.
Mosaic is a lightweight Linux system shell built with Yocto Project and Qt 6
Widgets for industrial embedded devices. Its architecture separates the shell,
user applications and background services. Developers package Qt Widgets or
software-rendered Qt Quick applications without changing shared shell source.
See :doc:`applications` for the toolkit and display integration contract.

.. toctree::
:maxdepth: 2
Expand Down
11 changes: 5 additions & 6 deletions docs/mosiac-structure.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
Mosaic structure
================

Mosaic is a lightweight Linux system shell built with Qt 6 and Qt Quick for
Mosaic is a lightweight Linux system shell built with Qt 6 Widgets for
industrial embedded devices.

Project layout
--------------

``shell/``
The system shell, including the lock screen, home screen, application
launcher, and notification interface.
The Widgets system shell and its authentication/session logic.

``apps/``
User applications, each in its own folder. Applications must use Qt and
Expand All @@ -24,6 +23,6 @@ Project layout
``docs/``
Project documentation.

Applications appear in the launcher after unlock and start when selected.
Systemd manages background service startup and operation independently of the
active application.
Application packaging supports Widgets and Quick. The direct display session
owns the screen, while independent application surfaces require compositor and
launcher integration. Systemd manages background services independently.
50 changes: 22 additions & 28 deletions shell/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,43 +1,37 @@
cmake_minimum_required(VERSION 3.16)

project(Mosaic LANGUAGES CXX)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h)
find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h)
find_library(PAM_LIBRARY NAMES pam)
if(NOT PAM_INCLUDE_DIR OR NOT PAM_LIBRARY)
message(FATAL_ERROR "PAM development files not found. Install libpam0g-dev on Ubuntu, or provide PAM in the target sysroot.")
message(FATAL_ERROR "PAM development files not found")
endif()

find_package(Qt6 6.5 REQUIRED COMPONENTS Quick QuickControls2 Concurrent)
find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Concurrent)
qt_standard_project_setup(REQUIRES 6.5)

qt_add_executable(app src/main.cpp)

set_source_files_properties(qml/Main.qml PROPERTIES QT_RESOURCE_ALIAS Main.qml)

qt_add_qml_module(app
URI Mosaic
QML_FILES
qml/Main.qml
qml/Shell.qml
qml/LockScreen.qml
qml/HomeScreen.qml
SOURCES
src/session/sessionController.h
src/session/sessionController.cpp

src/auth/AuthenticationBackend.h
src/auth/PamAuthenticationBackend.h
src/auth/PamAuthenticationBackend.cpp
)

target_include_directories(app PRIVATE src/session src/auth ${PAM_INCLUDE_DIR})

target_link_libraries(app PRIVATE Qt6::Quick Qt6::QuickControls2 Qt6::Concurrent ${PAM_LIBRARY})
qt_add_library(shell-ui STATIC
src/ShellWindow.cpp src/ShellWindow.h
src/session/sessionController.cpp src/session/sessionController.h
src/auth/AuthenticationBackend.h)
target_include_directories(shell-ui PUBLIC src src/session src/auth)
target_link_libraries(shell-ui PUBLIC Qt6::Widgets)
target_compile_options(shell-ui PRIVATE -Wall -Wextra)

qt_add_executable(app src/main.cpp src/auth/PamAuthenticationBackend.cpp src/auth/PamAuthenticationBackend.h)
target_include_directories(app PRIVATE ${PAM_INCLUDE_DIR})
target_link_libraries(app PRIVATE shell-ui Qt6::Concurrent ${PAM_LIBRARY})
target_compile_options(app PRIVATE -Wall -Wextra)

include(CTest)
if(BUILD_TESTING)
find_package(Qt6 6.5 REQUIRED COMPONENTS Test)
qt_add_executable(session-test tests/sessionTest.cpp)
target_link_libraries(session-test PRIVATE shell-ui Qt6::Test)
add_test(NAME session COMMAND session-test -platform offscreen)
endif()
Loading