Skip to content
Draft
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
4 changes: 4 additions & 0 deletions .github/workflows/create-test-plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
job.clang_tidy = False
case _:
raise ValueError(f"Invalid intel={spec.intel}")
job.apt_packages.append("intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic")
job.source_cmd = f"source /opt/intel/oneapi/setvars.sh;"
job.intel = True
job.shell = "bash"
Expand Down Expand Up @@ -494,6 +495,8 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
"libavutil-dev",
"libswresample-dev",
"libswscale-dev",
# testqt6
"qt6-base-dev",
))
match = re.match(r"ubuntu-(?P<year>[0-9]+)\.(?P<month>[0-9]+|latest).*", spec.os.value)
ubuntu_ge_24 = True
Expand Down Expand Up @@ -790,6 +793,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
if spec.msys2_platform not in (Msys2Platform.Mingw32, ):
job.msys2_packages.append(f"{msys2_env}-perl")
job.msys2_packages.append(f"{msys2_env}-clang-tools-extra")
job.msys2_packages.append(f"{msys2_env}-qt6")
if job.ccache:
job.msys2_packages.append(f"{msys2_env}-ccache")
job.microsoft_gameinput = True
Expand Down
8 changes: 1 addition & 7 deletions .github/workflows/generic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,6 @@ jobs:

# Add signed entry to apt sources and configure the APT client to use Intel repository:
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

# Update package list
sudo apt-get update -y

# Install oneAPI
sudo apt-get install -y intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic
- name: 'Install apk packages'
if: ${{ matrix.platform.apk-packages != '' }}
run: |
Expand All @@ -133,7 +127,7 @@ jobs:
- name: 'Install apt packages'
if: ${{ matrix.platform.apt-packages != '' }}
run: |
${{ matrix.platform.sudo }} apt-get update
${{ matrix.platform.sudo }} apt-get update -y
${{ matrix.platform.sudo }} apt-get install -y ${{ matrix.platform.apt-packages }}
- name: 'Install brew packages'
if: ${{ matrix.platform.brew-packages != '' }}
Expand Down
12 changes: 10 additions & 2 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ include(sdlcompilers)
check_language(CXX)
if(CMAKE_CXX_COMPILER)
enable_language(CXX)

find_package(Qt6 QUIET COMPONENTS Widgets)
if(Qt6_FOUND)

if(cxx_std_17 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
add_subdirectory(qt6)
endif()
endif()
endif()

find_package(Python3 COMPONENTS Interpreter)
Expand Down Expand Up @@ -710,8 +718,8 @@ function(add_sdl_test TEST TARGET)
set(installedtestsdir "${CMAKE_INSTALL_FULL_LIBEXECDIR}/installed-tests/SDL3")
configure_file(template.test.in "${exe}.test" @ONLY)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/${exe}.test"
DESTINATION ${CMAKE_INSTALL_DATADIR}/installed-tests/SDL3
FILES "${CMAKE_CURRENT_BINARY_DIR}/${exe}.test"
DESTINATION ${CMAKE_INSTALL_DATADIR}/installed-tests/SDL3
)
endif()
if(TARGET pretest AND NOT "${TARGET}" MATCHES "pretest")
Expand Down
10 changes: 10 additions & 0 deletions test/qt6/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
qt_add_executable(testqt6 WIN32
main.cpp
mainwindow.h
mainwindow.cpp
QSDLCanvas.h
QSDLCanvas.cpp
)
target_compile_features(testqt6 PRIVATE cxx_std_17)
target_link_libraries(testqt6 PRIVATE Qt6::Widgets SDL3::SDL3)
set_property(TARGET testqt6 PROPERTY AUTOMOC TRUE)
158 changes: 158 additions & 0 deletions test/qt6/QSDLCanvas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/
#include "QSDLCanvas.h"

#include <QMessageBox>

QSDLCanvas::QSDLCanvas(QWidget *parent, bool updateCanvasSize) : QWidget(parent), m_drawTimer(this)
{
// Allow SDL to draw to the window
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_OpaquePaintEvent);
setAttribute(Qt::WA_NoSystemBackground);

m_updateCanvasSize = updateCanvasSize;
}

QSDLCanvas::~QSDLCanvas()
{
if (m_renderer) {
SDL_DestroyRenderer(m_renderer);
}
if (m_window) {
SDL_DestroyWindow(m_window);
}
}

void QSDLCanvas::onUpdate()
{
SDL_Event event;
while (SDL_PollEvent(&event)) {
onEvent(&event);
}

onDraw();
}

void QSDLCanvas::changeRenderer(const QString &name)
{
m_rendererName = name;
m_rendererNameChanged = true;

recreateRenderer();
}

void QSDLCanvas::drawRendererName(bool draw)
{
m_draw_renderer_name = draw;
}

void QSDLCanvas::onDraw()
{
char text_buffer[256];
SDL_SetRenderDrawColor(m_renderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(m_renderer);

// Do your drawing here

if (m_draw_renderer_name) {
const char *renderer_name = SDL_GetRendererName(m_renderer);
if (renderer_name == NULL) {
SDL_snprintf(text_buffer, sizeof(text_buffer), "(null) [%s]", SDL_GetError());
renderer_name = text_buffer;
}
SDL_Point render_size;
SDL_GetRenderOutputSize(m_renderer, &render_size.x, &render_size.y);
SDL_SetRenderDrawColor(m_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderDebugText(m_renderer, render_size.x - SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE * SDL_strlen(renderer_name), render_size.y - 1 * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE, renderer_name);
}

SDL_RenderPresent(m_renderer);
}

void QSDLCanvas::onEvent(SDL_Event *event)
{

}

void QSDLCanvas::resizeEvent(QResizeEvent *event)
{
if (m_updateCanvasSize && m_window) {
const QSize canvasSize = event->size();
SDL_SetWindowSize(m_window, canvasSize.width(), canvasSize.height());
}
}

void QSDLCanvas::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);

if (!m_window) {
SDL_PropertiesID props = SDL_CreateProperties();
QString video_driver = SDL_GetCurrentVideoDriver();
if (video_driver == "cocoa") {
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_COCOA_WINDOW_POINTER, (void *)winId());
} else if (video_driver == "x11") {
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER, (int)winId());
} else if (video_driver == "wayland") {
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER, (void *)winId());
} else if (video_driver == "windows") {
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER, (void *)winId());
} else {
SDL_Log("QSDLCanvas does not support this video driver: %s", SDL_GetCurrentVideoDriver());
QMessageBox::critical(nullptr, "Unknown SDL video driver", QString{"Don't know how to create SDL_Window from native handle for '%1' SDL video driver"}.arg(SDL_GetCurrentVideoDriver()));
return;
}
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN, false);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, true);
if (m_updateCanvasSize) {
QSize canvasSize = size();
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, canvasSize.width());
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, canvasSize.height());
}
m_window = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
}
if (!m_window) {
SDL_Log("Failed to create SDL window (%s)", SDL_GetError());
return;
}

recreateRenderer();

int ms = (int)(1000.0f / framerate());
connect(&m_drawTimer, SIGNAL(timeout()), this, SLOT(onUpdate()));
m_drawTimer.start(ms);
}

void QSDLCanvas::recreateRenderer()
{
if (m_rendererNameChanged) {
m_rendererNameChanged = false;
QByteArray name_byte_array = m_rendererName.toUtf8();
const char *name_bytes = name_byte_array.data();
if (m_rendererName.length() == 0) {
name_bytes = nullptr;
}
if (m_renderer) {
SDL_DestroyRenderer(m_renderer);
m_renderer = nullptr;
}
m_renderer = SDL_CreateRenderer(m_window, name_bytes);
if (!m_renderer) {
SDL_Log("Couldn't create SDL renderer(%s): %s", name_bytes, SDL_GetError());
return;
}
SDL_Log("Render driver changed to %s", name_bytes);
}
}
64 changes: 64 additions & 0 deletions test/qt6/QSDLCanvas.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/
#include <QTimer>
#include <QWidget>
#include <QResizeEvent>

#include <SDL3/SDL.h>

class QSDLCanvas : public QWidget
{
Q_OBJECT

public:
QSDLCanvas(QWidget *parent = nullptr, bool updateCanvasSize = false);
virtual ~QSDLCanvas();

public Q_SLOTS:
void changeRenderer(const QString &name);
void drawRendererName(bool draw);

protected Q_SLOTS:
virtual void onUpdate();

protected:

void recreateRenderer();

// Override this to set the update framerate, this is called when the widget is shown
virtual float framerate() { return 60.0f; }

// Override this to draw to the canvas
virtual void onDraw();

// Override this to handle SDL events
// This example assumes there is only one canvas and it should process all SDL events
virtual void onEvent(SDL_Event *event);

virtual void resizeEvent(QResizeEvent *event) override;

// Handle the Qt show event
virtual void showEvent(QShowEvent *event) override;

// Let Qt know that we're going to do our own drawing
virtual QPaintEngine *paintEngine() const override { return nullptr; }

protected:
SDL_Window *m_window = nullptr;
SDL_Renderer *m_renderer = nullptr;
QTimer m_drawTimer;

bool m_updateCanvasSize = false;
bool m_draw_renderer_name = true;
QString m_rendererName;
bool m_rendererNameChanged = true;
};
74 changes: 74 additions & 0 deletions test/qt6/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/

#include "mainwindow.h"

#include <QApplication>
#include <QCommandLineParser>
#include <QErrorMessage>

#include <SDL3/SDL.h>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QCoreApplication::setApplicationName("sdl3-qt-demo");
QCoreApplication::setApplicationVersion(SDL_GetRevision());

QCommandLineParser parser;
parser.setApplicationDescription("SDL3 example demonstrating Qt6 integration");
parser.addHelpOption();
parser.addVersionOption();

parser.process(app);

QString app_platform_name = app.platformName();
bool updateCanvasSize = false;

if (app_platform_name == "xcb") {
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "x11", SDL_HINT_OVERRIDE);
#if defined(QT_FEATURE_wayland) && QT_CONFIG(wayland) == 1
} else if (app_platform_name == "wayland" || app_platform_name == "wayland-egl") {
/* Get the wl_display object from Qt */
QNativeInterface::QWaylandApplication *qtWlApp = app.nativeInterface<QNativeInterface::QWaylandApplication>();
SDL_SetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, qtWlApp->display());
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);

// Wayland requires explicitly setting and updating the canvas size.
updateCanvasSize = true;
#endif
} else if (app_platform_name == "windows") {
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "windows", SDL_HINT_OVERRIDE);
} else if (app_platform_name == "qnx") {
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "qnx", SDL_HINT_OVERRIDE);
} else if (app_platform_name == "cocoa") {
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "cocoa", SDL_HINT_OVERRIDE);
} else if (app_platform_name == "ios") {
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "ios", SDL_HINT_OVERRIDE);
} else {
QErrorMessage error_message;
error_message.showMessage(QString("Don't know SDL platform equivalent for qt platform: ") + app.platformName());
app.exec();
return 1;
}
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("SDL_Init failed (%s)", SDL_GetError());
return 1;
}

MainWindow window(updateCanvasSize);
window.show();

const int ret = app.exec();
SDL_Quit();
return ret;
}
Loading
Loading