Skip to content

Commit 652d255

Browse files
sloukenmadebr
authored andcommitted
Add qt6 example
[sdl-ci-filter *]
1 parent ff5e589 commit 652d255

9 files changed

Lines changed: 362 additions & 9 deletions

File tree

.github/workflows/create-test-plan.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
378378
job.clang_tidy = False
379379
case _:
380380
raise ValueError(f"Invalid intel={spec.intel}")
381+
job.apt_packages.append("intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic")
381382
job.source_cmd = f"source /opt/intel/oneapi/setvars.sh;"
382383
job.intel = True
383384
job.shell = "bash"
@@ -494,6 +495,8 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
494495
"libavutil-dev",
495496
"libswresample-dev",
496497
"libswscale-dev",
498+
# testqt6
499+
"qt6-base-dev",
497500
))
498501
match = re.match(r"ubuntu-(?P<year>[0-9]+)\.(?P<month>[0-9]+|latest).*", spec.os.value)
499502
ubuntu_ge_24 = True
@@ -790,6 +793,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
790793
if spec.msys2_platform not in (Msys2Platform.Mingw32, ):
791794
job.msys2_packages.append(f"{msys2_env}-perl")
792795
job.msys2_packages.append(f"{msys2_env}-clang-tools-extra")
796+
job.msys2_packages.append(f"{msys2_env}-qt6")
793797
if job.ccache:
794798
job.msys2_packages.append(f"{msys2_env}-ccache")
795799
job.microsoft_gameinput = True

.github/workflows/generic.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,6 @@ jobs:
119119
120120
# Add signed entry to apt sources and configure the APT client to use Intel repository:
121121
echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list
122-
123-
# Update package list
124-
sudo apt-get update -y
125-
126-
# Install oneAPI
127-
sudo apt-get install -y intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic
128122
- name: 'Install apk packages'
129123
if: ${{ matrix.platform.apk-packages != '' }}
130124
run: |
@@ -133,7 +127,7 @@ jobs:
133127
- name: 'Install apt packages'
134128
if: ${{ matrix.platform.apt-packages != '' }}
135129
run: |
136-
${{ matrix.platform.sudo }} apt-get update
130+
${{ matrix.platform.sudo }} apt-get update -y
137131
${{ matrix.platform.sudo }} apt-get install -y ${{ matrix.platform.apt-packages }}
138132
- name: 'Install brew packages'
139133
if: ${{ matrix.platform.brew-packages != '' }}

test/CMakeLists.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ include(sdlcompilers)
2121
check_language(CXX)
2222
if(CMAKE_CXX_COMPILER)
2323
enable_language(CXX)
24+
25+
find_package(Qt6 QUIET COMPONENTS Widgets)
26+
if(Qt6_FOUND)
27+
28+
if(cxx_std_17 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
29+
add_subdirectory(qt6)
30+
endif()
31+
endif()
2432
endif()
2533

2634
find_package(Python3 COMPONENTS Interpreter)
@@ -710,8 +718,8 @@ function(add_sdl_test TEST TARGET)
710718
set(installedtestsdir "${CMAKE_INSTALL_FULL_LIBEXECDIR}/installed-tests/SDL3")
711719
configure_file(template.test.in "${exe}.test" @ONLY)
712720
install(
713-
FILES "${CMAKE_CURRENT_BINARY_DIR}/${exe}.test"
714-
DESTINATION ${CMAKE_INSTALL_DATADIR}/installed-tests/SDL3
721+
FILES "${CMAKE_CURRENT_BINARY_DIR}/${exe}.test"
722+
DESTINATION ${CMAKE_INSTALL_DATADIR}/installed-tests/SDL3
715723
)
716724
endif()
717725
if(TARGET pretest AND NOT "${TARGET}" MATCHES "pretest")

test/qt6/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
qt_add_executable(testqt6 WIN32
2+
main.cpp
3+
mainwindow.h
4+
mainwindow.cpp
5+
QSDLCanvas.h
6+
QSDLCanvas.cpp
7+
)
8+
target_compile_features(testqt6 PRIVATE cxx_std_17)
9+
target_link_libraries(testqt6 PRIVATE Qt6::Widgets SDL3::SDL3)
10+
set_property(TARGET testqt6 PROPERTY AUTOMOC TRUE)

test/qt6/QSDLCanvas.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
3+
4+
This software is provided 'as-is', without any express or implied
5+
warranty. In no event will the authors be held liable for any damages
6+
arising from the use of this software.
7+
8+
Permission is granted to anyone to use this software for any purpose,
9+
including commercial applications, and to alter it and redistribute it
10+
freely.
11+
*/
12+
#include "QSDLCanvas.h"
13+
14+
15+
QSDLCanvas::QSDLCanvas(QWidget *parent) : QWidget(parent), m_drawTimer(this)
16+
{
17+
// Allow SDL to draw to the window
18+
setAttribute(Qt::WA_PaintOnScreen);
19+
setAttribute(Qt::WA_OpaquePaintEvent);
20+
setAttribute(Qt::WA_NoSystemBackground);
21+
}
22+
23+
QSDLCanvas::~QSDLCanvas()
24+
{
25+
if (m_renderer) {
26+
SDL_DestroyRenderer(m_renderer);
27+
}
28+
if (m_window) {
29+
SDL_DestroyWindow(m_window);
30+
}
31+
}
32+
33+
void QSDLCanvas::onUpdate()
34+
{
35+
SDL_Event event;
36+
while (SDL_PollEvent(&event)) {
37+
onEvent(&event);
38+
}
39+
40+
onDraw();
41+
}
42+
43+
void QSDLCanvas::changeRenderer(const QString &name)
44+
{
45+
m_rendererName = name;
46+
m_rendererNameChanged = true;
47+
48+
recreateRenderer();
49+
}
50+
51+
void QSDLCanvas::onDraw()
52+
{
53+
SDL_SetRenderDrawColor(m_renderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
54+
SDL_RenderClear(m_renderer);
55+
56+
// Do your drawing here
57+
58+
SDL_RenderPresent(m_renderer);
59+
}
60+
61+
void QSDLCanvas::onEvent(SDL_Event *event)
62+
{
63+
64+
}
65+
66+
void QSDLCanvas::showEvent(QShowEvent *event)
67+
{
68+
QWidget::showEvent(event);
69+
70+
if (!m_window) {
71+
SDL_PropertiesID props = SDL_CreateProperties();
72+
SDL_SetPointerProperty(props, "sdl2-compat.external_window", (void *)winId());
73+
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN, false);
74+
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true);
75+
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, true);
76+
m_window = SDL_CreateWindowWithProperties(props);
77+
SDL_DestroyProperties(props);
78+
}
79+
if (!m_window) {
80+
SDL_Log("Couldn't create SDL window: %s", SDL_GetError());
81+
return;
82+
}
83+
84+
recreateRenderer();
85+
86+
int ms = (int)(1000.0f / framerate());
87+
connect(&m_drawTimer, SIGNAL(timeout()), this, SLOT(onUpdate()));
88+
m_drawTimer.start(ms);
89+
}
90+
91+
void QSDLCanvas::recreateRenderer()
92+
{
93+
if (m_rendererNameChanged) {
94+
m_rendererNameChanged = false;
95+
QByteArray name_byte_array = m_rendererName.toUtf8();
96+
const char *name_bytes = name_byte_array.data();
97+
if (m_rendererName.length() == 0) {
98+
name_bytes = NULL;
99+
}
100+
if (m_renderer) {
101+
SDL_DestroyRenderer(m_renderer);
102+
m_renderer = nullptr;
103+
}
104+
m_renderer = SDL_CreateRenderer(m_window, name_bytes);
105+
if (!m_renderer) {
106+
SDL_Log("Couldn't create SDL renderer(%s): %s", name_bytes, SDL_GetError());
107+
return;
108+
}
109+
SDL_Log("Render driver changed to %s", name_bytes);
110+
}
111+
}

test/qt6/QSDLCanvas.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
3+
4+
This software is provided 'as-is', without any express or implied
5+
warranty. In no event will the authors be held liable for any damages
6+
arising from the use of this software.
7+
8+
Permission is granted to anyone to use this software for any purpose,
9+
including commercial applications, and to alter it and redistribute it
10+
freely.
11+
*/
12+
#include <QTimer>
13+
#include <QWidget>
14+
15+
#include <SDL3/SDL.h>
16+
17+
class QSDLCanvas : public QWidget
18+
{
19+
Q_OBJECT
20+
21+
public:
22+
QSDLCanvas(QWidget *parent = nullptr);
23+
virtual ~QSDLCanvas();
24+
25+
public Q_SLOTS:
26+
void changeRenderer(const QString &name);
27+
28+
protected Q_SLOTS:
29+
virtual void onUpdate();
30+
31+
protected:
32+
33+
void recreateRenderer();
34+
35+
// Override this to set the update framerate, this is called when the widget is shown
36+
virtual float framerate() { return 60.0f; }
37+
38+
// Override this to draw to the canvas
39+
virtual void onDraw();
40+
41+
// Override this to handle SDL events
42+
// This example assumes there is only one canvas and it should process all SDL events
43+
virtual void onEvent(SDL_Event *event);
44+
45+
// Handle the Qt show event
46+
virtual void showEvent(QShowEvent *event) override;
47+
48+
// Let Qt know that we're going to do our own drawing
49+
virtual QPaintEngine *paintEngine() const override { return nullptr; }
50+
51+
protected:
52+
SDL_Window *m_window = nullptr;
53+
SDL_Renderer *m_renderer = nullptr;
54+
QTimer m_drawTimer;
55+
56+
QString m_rendererName;
57+
bool m_rendererNameChanged = true;
58+
};

test/qt6/main.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
3+
4+
This software is provided 'as-is', without any express or implied
5+
warranty. In no event will the authors be held liable for any damages
6+
arising from the use of this software.
7+
8+
Permission is granted to anyone to use this software for any purpose,
9+
including commercial applications, and to alter it and redistribute it
10+
freely.
11+
*/
12+
13+
#include "mainwindow.h"
14+
15+
#include <QApplication>
16+
#include <QCommandLineParser>
17+
#include <QErrorMessage>
18+
19+
#include <SDL3/SDL.h>
20+
21+
int main(int argc, char *argv[])
22+
{
23+
QApplication app(argc, argv);
24+
QCoreApplication::setApplicationName("sdl3-qt-demp");
25+
QCoreApplication::setApplicationVersion(SDL_GetRevision());
26+
27+
QCommandLineParser parser;
28+
parser.setApplicationDescription("SDL3 example demonstrating Qt6 integration");
29+
parser.addHelpOption();
30+
parser.addVersionOption();
31+
32+
parser.process(app);
33+
34+
QString app_platform_name = app.platformName();
35+
36+
if (app_platform_name == "xcb") {
37+
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "x11", SDL_HINT_OVERRIDE);
38+
#if defined(QT_FEATURE_wayland) && QT_CONFIG(wayland) == 1
39+
} else if (app_platform_name == "wayland" || app_platform_name == "wayland-egl") {
40+
/* Get the wl_display object from Qt */
41+
QNativeInterface::QWaylandApplication *qtWlApp = app.nativeInterface<QNativeInterface::QWaylandApplication>();
42+
SDL_SetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, qtWlApp->display());
43+
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
44+
#endif
45+
} else if (app_platform_name == "windows") {
46+
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "windows", SDL_HINT_OVERRIDE);
47+
} else if (app_platform_name == "qnx") {
48+
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "qnx", SDL_HINT_OVERRIDE);
49+
} else if (app_platform_name == "cocoa") {
50+
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "cocoa", SDL_HINT_OVERRIDE);
51+
} else if (app_platform_name == "ios") {
52+
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "ios", SDL_HINT_OVERRIDE);
53+
} else {
54+
QErrorMessage error_message;
55+
error_message.showMessage(QString("Don't know SDL platform equivalent for qt platform: ") + app.platformName());
56+
app.exec();
57+
return 1;
58+
}
59+
60+
MainWindow window;
61+
window.show();
62+
63+
const int ret = app.exec();
64+
SDL_Quit();
65+
return ret;
66+
}

test/qt6/mainwindow.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
3+
4+
This software is provided 'as-is', without any express or implied
5+
warranty. In no event will the authors be held liable for any damages
6+
arising from the use of this software.
7+
8+
Permission is granted to anyone to use this software for any purpose,
9+
including commercial applications, and to alter it and redistribute it
10+
freely.
11+
*/
12+
#include <QtWidgets>
13+
14+
#include "mainwindow.h"
15+
#include "QSDLCanvas.h"
16+
17+
MainWindow::MainWindow()
18+
{
19+
QWidget *widget = new QWidget;
20+
setCentralWidget(widget);
21+
22+
m_canvas = new QSDLCanvas(this);
23+
24+
QVBoxLayout *layout = new QVBoxLayout;
25+
layout->setContentsMargins(0, 4, 0, 0);
26+
layout->addWidget(m_canvas);
27+
widget->setLayout(layout);
28+
29+
createMenus();
30+
31+
setWindowTitle(tr("SDL Qt Demo"));
32+
setMinimumSize(160, 160);
33+
resize(640, 480);
34+
}
35+
36+
void MainWindow::createMenus()
37+
{
38+
// Create "File" menu
39+
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
40+
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
41+
QAction *exitAct = new QAction(QIcon::fromTheme(QIcon::ThemeIcon::ApplicationExit), tr("E&xit"), this);
42+
#else
43+
QAction *exitAct = new QAction(tr("E&xit"), this);
44+
#endif
45+
exitAct->setShortcuts(QKeySequence::Quit);
46+
connect(exitAct, &QAction::triggered, this, &QWidget::close);
47+
fileMenu->addAction(exitAct);
48+
49+
// Create Renderer menu
50+
QMenu *rendererMenu = menuBar()->addMenu(tr("&Renderer"));
51+
QActionGroup* renderGroup = new QActionGroup(this);
52+
renderGroup->setExclusive(true);
53+
{
54+
QAction *defaultDriverAction = new QAction(tr("(default)"), this);
55+
defaultDriverAction->setCheckable(true);
56+
defaultDriverAction->setChecked(true);
57+
renderGroup->addAction(defaultDriverAction);
58+
rendererMenu->addAction(defaultDriverAction);
59+
connect(defaultDriverAction, &QAction::triggered, [this] { m_canvas->changeRenderer(""); });
60+
}
61+
int count_render_drivers = SDL_GetNumRenderDrivers();
62+
for (int i = 0; i < count_render_drivers; i++) {
63+
QString driver_name = SDL_GetRenderDriver(i);
64+
QAction *driverAction = new QAction(driver_name, this);
65+
driverAction->setCheckable(true);
66+
driverAction->setChecked(false);
67+
renderGroup->addAction(driverAction);
68+
rendererMenu->addAction(driverAction);
69+
connect(driverAction, &QAction::triggered, [this, driver_name] { m_canvas->changeRenderer(driver_name); });
70+
}
71+
72+
// Create Help menu
73+
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
74+
QAction *aboutQtAction = new QAction(tr("About &Qt"), this);
75+
connect(aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
76+
helpMenu->addAction(aboutQtAction);
77+
}

0 commit comments

Comments
 (0)