Docker Template Stack X11-GUI

A DTS container for Desktop GUI Projects

View project on GitHub

X11 General GUI Development - Docker Setup & Usage Guide

MIT License Commercial Services Available


Introduction

This Docker Linux (Ubuntu 24.04) container is designed for use on a Windows host running Docker Desktop. Its purpose is to enable the development of GUI applications (such as those written in C++, .NET, Rust, and others1) within a Linux Docker container, and to display the running application on the Windows host

Since Docker containers are headless by default (i.e., they lack native GUI support), we need a way to display GUI application output—whether during debugging or when running the application in release mode—on the Windows system. This is achieved by combining Docker Desktop with a WSL2 environment and the VcXsrv server (via XLaunch).

This container stack consists of one required base container and one or more optional sub-containers. The sub-containers are built on top of the base container. The base container provides the core environment, while the sub-containers add programming languages, frameworks, and libraries, such as .NET, Avalonia, or C++ toolkits.

Docker with WSL Architecture & Requirements

To better understand the Docker container and WSL setup, and to verify the system requirements, please refer to the following document: WSL architecture and requirements

Security Considerations and Network Configuration

Security Considerations and Network Configuration
For personal use on a developer’s PC or laptop, security risks are generally lower than in a production environment. However, it’s still a good idea to follow some basic security practices. When running an X server (like vcxsrv) on your Windows host, configure vcxsrv to only accept connections from the local machine. This minimizes exposure and ensures that only applications on your PC can access the server. Additionally, keep your firewall enabled and set to block unsolicited incoming connections. While this setup is for development purposes and may not require strict security measures, these simple steps can help protect your system against unexpected threats, especially when connected to less secure networks.

In practice, this means that as a developer, you should leave the XLaunch ‘Extra option’ -> Disable access control unchecked


Create the Base Container

This chapter covers the setup of the Base Container Service (folder: Base-Container) and all the components required to run a GUI application on a Windows host. At the end of this setup, we will verify that everything works as expected by launching a sample X application (xeyes) from within the Base Container.

The Basic Container Setup

Before executing the Docker Compose file, ensure that VcXsrv XLaunch is installed on the Windows host, this make sure we receive graphical output from the docker application. follow these instructions:

  • Download and Install the VcXsrv software.
  • After installation start XLaunch
    • Select Multiple Windows and click Next
    • Select Start no client and click Next
    • Ensure that Clipboard and Native opengl are enabled
    • Ensure that Disable access control is not enabled ( this is more secure; only enable it if you encounter issues) click Next, then Finish

Create the Basic Docker Container

Now we can create and start the base container.

  • Open the service subfolder: .\Base-Container\X11-Gui-Base-Service\ in a CMD window.
  • Decide whether you want to use an internal or an external network. External networks are recommended, as they allow multiple Docker service containers to communicate within the same network, giving you the flexibility to extend your environment. This setting is configured in the Compose file of the base-container.For more generic and detailed information, refer to:
    WSL architecture and requirements

  • In any case, we use a fixed IP address in the Compose file to simplify communication with services such as an SSH server (though SSH is not used in this setup). While not strictly necessary, it helps avoid name resolution issues
  • The Subnet and IP address can be found in the .env file:
     FIXED_SUBNET  # Default: 172.20.0.0/16            FIXED_IP      # Default: 172.20.0.15
  • Next install the Container. Execute this command in the service sub folder
     docker-compose -f compose_x11_gui_base.yml up -d --build --force-recreate  --remove-orphans 

Recreate Docker Container

When recreating the same container(service name) avoid subtle/annoying caching issues, When needed make sure to:

  • delete the container
  • delete the volume and
  • use the Docker prune command, like: docker system prune -a –volumes



Verify the Setup

  • After running the commands you can open Docker Desktop and in the container section a new container is created under the name: x11-gui-basic-service/x11-gui-basic-service-1. Open a terminal session in this container

  • Under the Images tab of Docker Desktop you should see the image eelhart/x11-gui-base:latest This is the image that will be used by the Sub Containers that you may create (see next paragraph) to add different development environments to develop GUI applications.

Docker Image

For the usage of the Sub Containers you may remove the container ‘x11-gui-basic-service/x11-gui-basic-service-1’ but you will need to hold on to the image eelhart/x11-gui-base:latest

  • Make sure the X-server(XLaunch) is started (see 2.1.4)
  • Enter the following command in the Docker Terminal shell:
     xeyes      # This should display a window with eyes on your Windows desktop

    When successful a Window with a pair of eyes should be displayed

Docker call syntax (Skip this if you know Docker basics)

Docker calling context
Because we use Docker files (Dockerfile and compose) with descriptive names, such as Dockerfile_Nodejs_React_Cont instead of just Dockerfile, this affects how Docker commands are executed. For example, with a standard Dockerfile, we would use this command to reference the Docker file in the Docker Compose file:

  context: .
  dockerfile: Dockefile
  

In our case, we cannot use the default name but have to specify the name we gave, thus:

  build: 	    
  context: .
  dockerfile: Dockerfile_Nodejs_React_Cont
  

The same applies for using the build command. With the default Dockerfile, you can use this:

  docker build 
  # This will assume a file: Dockerfile is available
  

With the named file, we have to use

docker build -f MyDockerFileNameHere 


The same applies for running the Compose file (use -f option)

─── ✦ ───


3. Creating the Sub Containers

This section describes several combinations of Dockerfiles and Compose files that can be used to create different sub-containers. Before continuing, make sure you have already created the Base Container, as described in section 2.

In the Sub-Containers folder, each sub-container is stored in its own subfolder. The following sub-sections provide instructions for installing one or more sub-containers.


3.1 Creating the .NET Sub Container (afx-x11-forward-net-service)

This will create a basic .NET command-line application that returns—guess what? Yes! “Hello World.”
This program does not display its output in an X11 window on the host; instead, it is meant to demonstrate that the Base Container can be extended, and to show how this can be done.

In addition it will create a project, based on a template, and demonstrate how to use Visual Studio Code (VSC) to Debug and run the application on the Windows host. See paragraph 4 for general VSC instructions.

Additionally, these steps create a project from a template and show how to build the application using the CLI prompt.

Prove that GUI output works

We already have proven that that GUI output from the Linux application is displayed in our XLaunch Window on the Windows Host, if you did not see this you can open a terminal in the Docker container and enter the command: xeyes


Create the Application project:

1. Open a CMD in the folder .\Sub-Containers\Afx-X11-Forward-NET-Service\
2. Define the Network: an External network configuration is used by default

Defaults checklist:

  1. In the file .env of the sub container make sure the FIXED_SUBNET is set to the same value, of the .env file in the base container
  2. In the file .env of the sub container make sure that a free IP address variable is used (i.e. FIXED_IP2), this can be taken from the .envM file in the base container
  3. In the compose file make sure that the same network (name) as in the base container compose file is used,
  4. In the compose file(sub container) make sure that the IP variable, from step 2 is used.

📍Alternatively you can use an internal network, these are commented around the same location in the compose file.

3. In the .env file, set the variable PRJ_NAME_ARG to the desired project name.

  • Alternatively, you can also set this environment variable from the command line.This value will be used for both the project name and the project directory. If you omit this step, a default will be used (see the PRJ_NAME_ARG variable in the .env file:
$env:PRJ_NAME_ARG="my-project"		# From Command line 

4. Create the external network, if does not exists

 
docker network ls   # check if already exists (don't include in copy)
docker network create --subnet=192.168.52.0/28 network_common_X11_gui

5. Then execute the docker command, to create and builds the project

 docker  compose -f compose_net_x11_project.yml up -d  --remove-orphans --build --force-recreate 

6. Test checklist

Test steps:

  • Open Docker Desktop, in the container section a new container should be created with the name:
    afx-x11-forward-net-Service/afx-dotnet-container-1
  • Open a terminal session in this container, navigate to directory: /projects/net-gtk-sharp/project_name2
  • Enter the following command in the terminal session:
    dotnet run

    This should create your project and display ‘Hello world’


3.2 Creating an Avalonia .NET Sub-Container (afx-x11-forward-avalonia-service)

This sub-container sets up a GUI project using the Avalonia UI framework for .NET. For more information about Avalonia, visit the official Avalonia site.

Avalonia supports creating multiple types of projects using its built-in project templates. In this setup, a custom sample project—based on one of these templates—is included and installed by default.

Steps to Create an Avalonia Base Application Project:
1. Open a Command Prompt in the folder: .\Sub-Containers\X11-Gui-Avalonia-Service\
2. Define the Network: an External network configuration is used by default

Defaults checklist:

  1. In the file .env of the sub container make sure the FIXED_SUBNET is set to the same value, of the .env file in the base container
  2. In the file .env of the sub container make sure that a free IP address variable is used (i.e. FIXED_IP2), this can be taken from the .env file in the base container
  3. In the compose file make sure that the same network (name) as in the base container compose file is used,
  4. In the compose file(sub container) make sure that the IP variable, from step 2 is used.

📍Alternatively you can use an internal network, these are commented around the same location in the compose file.

3. Open the .env file to adjust the necessary settings:

  • Project Type By default PRJ_TYPE_USE_CUSTOM_APP is set to create a basic application based on our custom template. You can disable this to choose one of the Avalonia-provided templates by setting the variable: PRJ_TYPE_ARG.
  • Project Name: Set the variable PRJ_NAME_ARG to your desired project name. This will be used for both the project name and the project directory.If omitted, the default value from PRJ_NAME_ARG in the .env file will be used.

4. Create the external network if does not exists

docker network ls   # check if already exists (don't include in copy)
docker network create --subnet=192.168.52.0/28 network_common_X11_gui

5. Execute the Docker command to create the project:

 docker  compose -f compose_avalonia_x11_project.yml up -d  --remove-orphans --build --force-recreate 

Note that this compose creates and builds the project.

6. Test checklist

Test steps:

  • Open Docker Desktop, in the container section a new container should be created with the name:
    x11-gui-avalonia-service/x11-gui-avalonia-dotnet-1
  • Open a terminal session in this container, navigate to directory: /projects/ava/project_name
  • Make sure XLaunch is started on the host
  • Enter the following command in the terminal session:
    dotnet run

    This should display Application GUI on your host via the XLaunch program

When all this works you should be able to open, build, and debug the project in Visual Studio code, See paragraph 4 for details

3.2.1 Visual Studio Code Specifics

This section outlines the required Visual Studio Code (VSC) plugins and configurations. These should already be installed and configured if you are using our custom application template (see PRJ_TYPE_USE_CUSTOM_APP). In this case, the project file and .vscode directory will already contain the necessary settings. For other project types (e.g., PRJ_TYPE_ARG), you will need to configure these manually. If something doesn’t work as expected, it’s a good idea to verify your settings. (Click on Side note below to expand the section)

Available tasks

In the Terminal → Run Task… menu, you can find the build tasks for the Avalonia project, which are defined in the workspace settings file (see side note above). The following tasks are defined for our project:

  • Clean: All Build Output
  • Debug: Build Configuration
  • Debug: Restores NuGet Packages
    Almost all ways called automatically
  • Release: Build Configuration
  • Release: Restore NuGet Packages
    Almost all ways called automatically
  • Watch XAML Build
    This makes sure you can see the ‘live output’ of the application while your updating the XAML file(s) For other sub-container other task might be available, but most of them should be self explaining.

Potential required bugfix

As of Sept. 2024, you might need to change one thing. Be sure to install version 0.29 of the Avalonia for Visual Studio Code plugin. There is a persistent parsing error in versions up to 0.31 that pops up whenever you type. See more details here

Visual Code, Required Plugins for the Container

  1. General plugins
    • C# Dev Kit
    • Avalonia for vscode (by Avalonia team)
      This feature for XAML development and auto complete features (Requires .NET Core 8.0) WARNING Make sure to install version 0.29, untill version 0.31 there is a super annoying parse error which pops-up when every you type something, see here. When a higher version then 0.31 is released you can test that version to check if it still there.
  2. Kir-AntipovHot Reload plugin should already be part of this project, this is how it defined:
    • Project file should contain (Copy this if not)

       <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
       <DefineConstants>$(DefineConstants);ENABLE_XAML_HOT_RELOAD</DefineConstants>
       </PropertyGroup>
       <ItemGroup>
       <PackageReference Condition="$(DefineConstants.Contains(ENABLE_XAML_HOT_RELOAD))" Include="Avalonia.Markup.Xaml.Loader" Version="11.1.0" />
       <PackageReference Condition="$(DefineConstants.Contains(ENABLE_XAML_HOT_RELOAD))" Include="HotAvalonia" Version="1.1.1" />
       <PackageReference Include="HotAvalonia.Extensions" Version="1.1.1" PrivateAssets="All" />
       </ItemGroup>
      
  • In project ReactiveUI Package should be present, check if it is
    PackageReference Include=”Avalonia.ReactiveUI” Version=”your_version_here” # alternatively added it with: dotnet add package Avalonia.ReactiveUI

  • Next initialize HotAvalonia in the App.axaml.cs (This should already be done, but for your reference)
    - Using directive using HotAvalonia;
    - In the method public override void Initialize() add:
    this.EnableHotReload(); // HotAvalonia Ensure this line precedes AvaloniaXamlLoader.Load(this);

    • Make sure the .UseReactiveUI(); is called in AppBuilder.Configure() see program.cs. This also requires: using Avalonia.ReactiveUI;
    • Run task -> Watch run Avalonia Change an *.axaml and the program output should be adapted

WARNING: When Visual Code complains about multiple project and refuses to start, it is likely due to the fact that in the same folder a solution file automatically created for you :( removing/relocating this file might solve the issue, to prevent it from creating:

  • Look for installed plugin ‘C# Dev Kit’
  • press Extensions settings
  • Uncheck ‘Automatically Create Solution In Workspace’
  • Reload your project.

*Thanks: to Kir_Antipov Github for providing the required tools to make this work!


3.3 Creating a .NET GTK Sub-Container (Afx-X11-Forward-NET-Service-GTK#)

These instructions describes, how to install the .NET GTK# Sub-Container, which is based on GTK 3.24.24.

Steps to Create a .NET GTK# Sub-Container Application Project:

  1. Open a Command Prompt in the following folder: .\Sub-Containers\X11-Gui-NET-Service-GTK#\
  2. Define the Network, an External network configuration is used by default

Defaults checklist:

  1. In the file .env of the sub container make sure the FIXED_SUBNET is set to the same value, of the .env file in the base container
  2. In the file .env of the sub container make sure that a free IP address variable is used (i.e. FIXED_IP2), this can be taken from the .env file in the base container
  3. In the compose file make sure that the same network (name) as in the base container compose file is used,
  4. In the compose file(sub container) make sure that the IP variable, from step 2 is used.

📍Alternatively you can use an internal network, these are commented around the same location in the compose file.

3. Create the external network if does not exists

docker network ls   # check if already exists (don't include in copy)
docker network create --subnet=192.168.52.0/28 network_common_X11_gui 

5. Execute the Docker command to create the project (folder: ‘X11-Gui-NET-Service-GTK#’)

docker-compose -f compose_net_x11_gtksharp_project.yml up -d --build --force-recreate --remove-orphans

6. Test checklist

Test steps:

  • Open Docker Desktop, in the container section a new container should be created with the name:
    x11-gui-avalonia-service/x11-gui-avalonia-dotnet-1
  • Open a terminal session in this container, navigate to directory: /projects/net-gtk/project_name
  • Make sure XLaunch is started on the host
  • Enter the following command in the terminal session:
    dotnet run -f net8.0-windows

    This should display Application GUI on your host via the XLaunch program

When everything is set up correctly, you should be able to open, build, and debug the project in Visual Studio Code. See paragraph 4 for details.

7. Specific for Visual Studio Code

  • Visual Studio Code requires you to install the following plugins for this container:
    • Start the new container in VSC
    • Open the directory: projects/net-gtk/project_name
    • Within a new terminal session in VSC Install the following extension(s) in the container:
      code --install-extension ms-dotnettools.csharp
      code --install-extension ms-dotnettools.csdevkit
      code --install-extensionms-dotnettools.vscode-dotnet-runtime
      

3.3.1 The project Template and specific tasks in VSC

This sections explains how to use the GTL project template in Visual Studio Code.

Project structure
When you open your GTK3 .NET project in VSC, you’ll find the following ‘src’ folder structure:

📁 src                          → root source folder
├── 📁 backend                  → .NET class libraries (See VSC task: '2.1 AFX ...' below)
│   ├── 📁 cl_example           → simple sample class library
│   └── 📁 cl_example_rest      → other sample class library, with simple Rest example
└── 📁 frontend                 → GTK3 csharp code
    └── 📄 Program.cs           → main program using the sample libraries

VSC Tasks
The following tasks are available in Visual Studio Code:

Task name Description 1.1 AFX Build GTK App (Debug Windows) Debug Build all for the Windows platform 1.2 AFX Build Release GTK App (Windows & Linux) Release build ofr Linux and Windows Platforms 2.1 AFX CREATE: Class Source Library Creates a class library in the directory: src/backend/ see Remark 1 3.1 AFX Run GTKApp (Windows Release) Run the app in release mode 4.1 AFX Clean a specific Library Cleans a specific class library 4.2 AFX Clean the application Cleans the frontend application 4.3 AFX Clean ALL Clean all libraries and frontend application

Prefix library names

Make sure the class library start with: ‘cl_‘ followed by a descriptive name. This makes sure:

  • That the class libraries are created in a folder with that name on src\backend\cl_yourname
  • During the build process the cl_ is replaced with with ‘lib_‘ resulting into library: lib_yourname
  • The clean tasks 4.x wil work properly, because they act on these names!


3.4 - A PHP/Python/Rust Sub container

This is a general-purpose container for PHP, Python, and Rust development. It includes a default project template that integrates all three languages. Below are the instructions to install this sub-container:

Steps to Create an PHP, Python, Rust based Application Project:

  1. Open a Command Prompt in the folder: .\Sub-Containers\X11-Gui-PyPHP-Service\
  2. Define the Network, an External network configuration is used by default

Defaults checklist:

  1. In the file .env of the sub container make sure the FIXED_SUBNET is set to the same value, of the .env file in the base container
  2. In the file .env of the sub container make sure that a free IP address variable is used (i.e. FIXED_IP5), this can be taken from the .env file in the base container
  3. In the compose file make sure that the same network (name) as in the base container compose file is used,
  4. In the compose file(sub container) make sure that the IP variable, from step 2 is used.

📍Alternatively you can use an internal network, these are commented around the same location in the compose file.

3. Create the external network if does not exists

docker network ls   # check if already exists (don't include in copy)
docker network create --subnet=192.168.52.0/28 network_common_X11_gui

4. Execute the Docker command to create the project (folder: ‘X11-Gui-PyPHP-Service’)

docker compose -f compose_Python-Apache-php.yml up -d --build --force-recreate --remove-orphans

6. Test checklist

Test steps:

  • Open Docker Desktop, in the container section a new container should be created with the name:
    x11-gui-pyphp-service/x11-gui-python-apache-php-1
  • The following wen applications should be reachable:
  • Open a terminal session in this container, navigate to directory: /projects/pyphp/project
  • You should note the atleast the following files and directories
    • File: .project.code-workspace to open all projects in VSC
    • Dir: ./GUI-app/src_frontend a GUI Python application
    • Dir: ./GUI-app/src_backend The location for the Rust libraries that can be called from Python, can be generated from a VSC Task
    • Dir: ./web-app The PHP home webpage (http://localhost:8071/web-app/home/index.php) Application
    • Dir: ./web-rest A simple REST PHP application
    • Dir: ./rust_lib_demo A Simple Rust library to check the creation of a Rust library

      Please note that the ./rust_lib_demo library is just a simple sample, unrelated to the GUI application, to demonstrate a Rust function exposed to Python using PyO3. Similar Rust libraries can be generated in the ./GUI-app/src_backend directory via a Visual Studio Code task (see section Visual Studio Code Specifics just after this section).
  • Lets test if Python can be found:
    python --version 
    • Let’s test if we can build the ./rust_lib_demo with maturin
    • Change directory to rust_lib_demo in the CLI and execute:
      maturin build --release
      pip install target/wheels/*.whl  # install the wheel
      python -c "import rust_lib; print(rust_lib.add(3, 7));" # Sample calls the Rust Function in Python

      Maturin creates a Python module from our Rust library. It first creates a shared library and then it Packages everything into a wheel that can be installed with pip. Note that in the VSC Task that builds the rust library in (./GUI-app/src_backend we use cargo to build the shared and statical library.

When the above links work and the rust generates a library and python is install, the installation was successful. You can Continue with the Visual Studio Code specifics section, to explorer the projects and tools.

3.4.1 - Visual Studio Code Specifics

This section explains how to use the project template in Visual Studio Code.

Project structure
This project template consists of the following components and tools:

📁 project                                    → root project folder
├── 📄 .project.code-workspace               → VSCode workspace configuration
├── 📁 GUI-app                               → Python GUI application
│   ├── 📁 src_frontend                      → Python frontend code (PySide/Qt)
│   └── 📁 src_backend                       → Rust libraries for Python
│       └── 📁 api_interface_rust            → Rust library location (PyO3-enabled)
├── 📁 web-app                               → PHP web application
│   └── 📁 home                              → Main web pages
├── 📁 web-rest                              → PHP REST API application
│   └── 📁 test-client                       → REST API test client
└── 📁 rust_lib_demo                         → Sample Rust library (Maturin demo)
    └── 📁 target/wheels                     → Built Python wheels

Technologies:

  • PHP - Two web applications (web-app and web-rest)
  • Python - GUI application using PySide (Qt bindings)
  • Rust - Library files callable from Python via PyO3
  • Maturin - Creates Python modules from Rust libraries (pip installable)
  • Make - Tool to generate Rust source libraries (invoked via VSC tasks)

Because multiple project definitions are used, a project workspace is defined to open all these (sub)projects: .project.code-workspace.

To open the project workspace
  • First open the folder in VSC: ‘File -> Open folder’ and choos /projects/pyphp/project/
  • Then open the project workspace: ‘File-> open workspace from file’ choose: .project.code-workspace
  • This result in:

Project workspace layout:
📦 Desktop App (PY, Main) ➜ This the Pyton GUI application
📦 Rust_lib_demo ➜ A simple sample Rust library
📦 Web-app (PHP) ➜ The main Web Application
📦 Web-rest (PHP) ➜ Another Web Application, demostarting a REST call
📝 About & info & tools ➜ Documentation and tools
🧾 Logs ➜ Log files from apache, php and others


3.4.1.1 - Desktop App (PY, Main)

In VSC basic actions and task can be run for the python application ➜ Project: Desktop App (PY, Main)

  1. Debug and Run
    • Make sure XLaunch is started in the Windows host.
    • In the project:Project:Desktop App (PY, main)<>/span>
    • Open the file: ./src_frontend/main.py and set a breakpoint
    • Make Sure that in the Run and Debug tab the AFX PY(Debugpy) is selected
    • Press F5, you should hit the breakpoint
    • Press F5 again, the program should run and you should see a simple program in the XLaunch Window.
  2. Create a Rust Source Library
    By default the directorie: ./src-backend/api_interface_rust is empty. This directorty is used for Rust libraries that we can generate it:
    • Terminal ➜ Run Task…
    • Choose AFX Create 4: business Library (Rust) and follow the instruction (Use the mentioned conventions!, i.e. name_lib) After this you have a Rust library in: src_backend/api_interface_rust/name_lib
  3. Build the Rust library
    The above created Rust library can be build:
    • Terminal ➜ Run Task…
    • Choose “AFX BUILD 1: Libraries (Rust,LINUX) This will create the library and dislays where it can be found, after this you can call it from Python using PyO3 (which is instaled see ).

Cross-compile Rust

While there is a tasks to cross-compile the Rust library for Windows (AFX BUILD 2: Libraries (Rust,WINDOWS)) This is not supported because it uses Python Windows bindings which at this time(2025) are not provide. The compile should probably work but linkage will result into errors

4. Optional. Client Rust Libary Test App
Optional you can create a simple ‘client test’ application for your Rust Library.

  • Terminal ➜ Run Task…
  • Choose AFX Create 5: Client Library Tester (Rust LINUX) and follow the instruction.
  • This will create in the directorie src_backend/api_interface_rust/tests a sub directory test[libname] with the test code.

5.Optional. Build the Test Client
The created Rust client Test can be build with:

  • Terminal ➜ Run Task…
  • Choose AFX BUILD 3: Client Library Tester (Rust LINUX, debug)
  • It ask for the test, which is named like test_[lib_name]
  • After that follow the the instructions on the scrreen

Can not find library…

If you get an error; ‘Can not find library…‘ make sure you have the following in your Cargo.toml of the library:
crate-type = [“rlib”, “cdylib”] instead of:
crate-type = [“cdylib”]
*rlib makes sure the library can be found statically

3.4.1.2 PHP application

In VSC basic actions and task can be run for the PHP web applications (Project: web-app (PHP) and Project: web-rest (PHP))

  1. Debug and Run
    • In one of the project files set a breakpoint
    • In the RUN AND DEBUG make sure you have selected: PHP(A): Xdebug AFX” for the PHP application and PHP(R): Xdebug AFX” for the rest version
    • Load the relevant page:
      • http://localhost:8071/web-app/home/index.php
      • http://localhost:8071/web-rest/wr-api.php?name=John
    • You should hit the breakpoint.
    • Press F5 again the program should run and you should see a simple program in the XLaunch Window

3.5 - Sub-Container: Slint/SDL2 C++ and Python

This is a general-purpose container for the development of Slint applications in both Python and C++, with additional support for SDL2 (C++). Since Slint is partly implemented in Rust, Rust is also included.

Steps to Create a Slint/SDL2 C++ and Python container and Slint Application Project:

  1. Open a Command prompt in the folder: .\Sub-Containers\X11-Gui-Slint-py-cpp-Service\
  2. Define the network: an External network configuration is used by default.

Defaults checklist:

  1. In the file .env of the sub container make sure the FIXED_SUBNET is set to the same value, of the .env file in the base container
  2. In the file .env of the sub container make sure that a free IP address variable is used (i.e. FIXED_IP6), this can be taken from the .env file in the base container
  3. In the compose file make sure that the same network (name) as in the base container compose file is used,
  4. In the compose file(sub container) make sure that the IP variable, from step 2 is used.

📍Alternatively you can use an internal network, these are commented around the same location in the compose file.

3. Create the external network if does not exists

docker network ls   # check if already exists (don't include in copy)
docker network create --subnet=192.168.52.0/28 network_common_X11_gui 

5. Execute the Docker command to create the project (folder: ‘X11-Gui-PyPHP-Service’)

docker compose -f compose_Slint-py-cpp.yml -d --build --force-recreate --remove-orphans

6. Setup Result

Test steps:

  • Open Docker Desktop, in the container section a new container should be created with the name:
    x11-gui-slint-py-cpp-service/x11-gui-slint-py-cpp-service-x11-gui-slint-python-cpp-1
  • Open a terminal session in this container, navigate to directory: /projects/slint/project/gui-app
    You should see at least the following file:
    • File: build.sh to build the program with CMake
    • Test if can build the project from here:
    ./build.sh 
    • Test if the created program works, in your CLI and exceute:
    
    # 1. Change to dir:
      cd build
    # 2. Make sure XLaunch is running on your host
    # 3. Execute:
      ./HelloWorld
    
    

📍SDL2
Another example includes a SDL2 Window, and can be found in the directory gui-app-sdl2. It can be build an run in the same way

When all this works, you should be able to open, build, and debug the project in Visual Studio Code. See paragraph 3.4.1 for details.


3.5.1 - Visual Studio Code Specifics

This section explains how to use the project template in Visual Studio Code.

Project structure
This project template consists of two Slint C++ sub-projects:

📁 project                                    → root project folder
├── 📄 .project.code-workspace               → VSCode workspace configuration
├── 📁 .vscode                               → Shared VSCode settings
├── 📁 gui-app                               → Basic Slint C++ sample
│   ├── 📄 CMakeLists.txt                    → CMake build configuration
│   ├── 📄 build.sh                          → Build script
│   ├── 📁 src                               → C++ source files
│   └── 📁 build                             → Build output directory
└── 📁 gui-app-sdl2                          → Slint C++ with SDL2 integration
    ├── 📄 CMakeLists.txt                    → CMake build configuration
    ├── 📄 build.sh                          → Build script
    ├── 📁 src                               → C++ source files
    └── 📁 build                             → Build output directory

Technologies:

  • Slint Modern GUI toolkit for C++ and Rust
  • SDL2 Cross-platform multimedia library for graphics and windowing (Simple DirectMedia Layer)
  • CMake Build system generator

Because multiple project definitions are used, a project workspace is defined to open all these projects: class=”nje-cmd-inline-sm”>.project.code-workspace</span>

To open the project workspace
  • First open the folder in VSC: ‘File -> Open folder’ and choos /projects/slint/project
  • (Then open the project workspace: ‘File-> open workspace from file’ choose: .project.code-workspace)
  • This result in:

Project workspace layout:
📦 Root ➜ This the root of all project, includes ./vscode(shared)
📦 gui-app ➜ A simple SDL C++ sample
📦 gui-app-sdl-2 ➜ A simple SDL C++ sample with SDL2 Window(threaded)

Required Extensions

These extensions should be installed automatically, or at least recommended by the Visual Studio Code interface when opening the project workspace. If not, please install them manually:

Slint.slint
ms-vscode.cpptools
ms-vscode.cmake-tools
ms-vscode.cpptools-extension-pack
ms-python.vscode-pylance
ms-python.python
ms-python.debugpy
3.5.1.1 - Task for the sub project

In VSC the following tasks can be run for the projects in the Workspace.

  1. Build Slint C++
    By default 2 task are available one to build the project in Debug mode and one to build it in Release mode. Both will give you the option two choose the project (gui-app or gui-app-sdl2) and follow the instructions
    • Terminal ➜ Run Task…
    • Choose SLINT Build (DEBUG) or SLINT Build (RELEASE)
  2. Debug and Run
    • Make sure XLaunch is started in the Windows host.
    • Make sure the project is build in DEBUG mode
    • Set a breakpoint in one of the project files
    • Make Sure that in the Run and Debug tab, the Debug C++ is selected
    • Press F5, you should hit the breakpoint.
    • Press F5 again, the program should run and you should see a simple program in the XLaunch Window.

Footnotes

1. Of course, any Linux GUI application can be displayed via Docker on the Windows host; it is not limited to development.

Appendix I - Develop with VSC in the host

📎 Develop with VSC in the host

To develop in Visual Studio Code we advice the following instructions. Note that this is a general instruction applicable for all sub containers, any specific issue will be described in the ‘README.md’ in the root directory of the sub container, this all includes specific VSC task and project structure.

1. Open an application from a container VSC (@host)

  • Press CTRL-SHIFT-P or F1 and select (start typing) Attach to running container…
  • Select fore example the afx-x11-forward-avalonia-service sub container, or any other sub container
  • Alternatively you might click on the Docker boot on the left toolbar and select the container from there, this opens a new Window with the container information

2. Open Folder and building your app.

  • Use the VSC Explorer and the Open Folder to open the remote container’s folder. Ensure you open the correct folder so that the .vscode directory settings are applied properly.
  • Select Open Folder and enter: /projects/ava/your_project_name (for Avalonia). This will ensure the project is loaded along with the settings configured in the .vscode folder. (Alternatively, you can obtain the path by opening a terminal inside the Docker container. The initial folder shown by the pwd command will give you the correct path.)

When opening the application container (i.e..NET container) and the project root folder in Visual Studio Code, a dedicated Visual Studio Code server will be installed within the container. This server provides a full Visual Studio Code environment with its own settings and extensions. Upon opening the folder for the first time, the system will detect any required extensions and prompt you to install them. Follow the instructions to complete the installation if prompted.

In case the intellisense indicates errors in one of the project files this is probably due to the missing of an extension.

3 VSC Build tasks

Most of the build an launch task are pre-created for you in th sub containers

Side note: VSC Settings files

VSC Settings files

Settings file

To change the build targets and related settings you will need to update the files in the .vscode directory of the project (this is the reason you have to open the right project directory, opening the right directory make sure the project directory is the root and VSC searches for a ‘.vscode’ directory in the root to apply settings to this project) the following settings files are available in ‘.vscode’:

  • .vscode/tasks.json: For build tasks.
  • .vscode/launch.json: For debugging configurations.
  • .vscode/settings.json: For general VS Code settings specific to your project (e.g., editor preferences, linting settings). In case you have to customize the build properties or other settings these files should be updated.


License
This file is part of: X11 GUI Development Template Stack Copyright (c) 2025-2026 Nico Jan Eelhart.This repository is MIT licensed and free to use. For optional commercial support, customization, training, or long-term maintenance, see COMMERCIAL.md.


─── ✦ ───

Home Sites
















Maintained by NicoJanE. Generated by GitHub Pages.