[SDL3] Hooking into another gui library

I’ve been working on an app that uses wxWidgets, and am trying to use SDL to retrieve the inputs as events when I open up a dialog box. So far, I have managed to hook SDL and get events to pass to my main event loop. However, I came up with a behavior where the background of the window caused it to have visual artifacts when resizing and moving. Is anyone aware of the cause of this sort of thing? I’m using CreateWindowWithProperties and hooking with the HWND property. If not, I have a few options ahead of me.

  1. Accept the current behavior
  2. Create a custom SDL window that appears when I need the events to fire.
  3. Try to find out how to pump key and mouse events from the gui library to SDL.

For testing of this behavior, I ran it on Windows and found that even minimal window setup results in this behavior. I’ve tried using several properties, but the only one that “fixes” it is turning the Dialog into a modal or similar, and that disables event input.

I’ve had some success with embedding SDL2 in Qt, and handling keyboard/mouse events as you suggested in your third option using SDL_PushEvent… But there were some problems due to Qt threading. I haven’t worked with wxWidgets much, but I remember there are some similar issues.
Would you like to share a minimal example so I can take a look?

Here’s the minimal example that I could come up with. I place the GUI into another thread and wrapper class. Then I hook into it (the thread doesn’t matter, the bug occurs without a thread).

#include <SDL3/SDL.h>

#define SDL_MAIN_HANDLED
#include<iostream>
#include <thread>
#include <memory>
#include <string>

#ifdef _MSC_VER //Make you sane and never use WinMain
#    pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

//#include "FrameWrapper.h"
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif


class FrameWrapper
{
public:
    FrameWrapper() ;
    int start_thread( int argc, char** argv ) ;
private:
};

class gui : public wxApp
{
public:
    gui() ;
    virtual bool OnInit() ;
};

class MyFrame : public wxFrame
{
public:
    MyFrame( const wxString& title, const wxPoint& pos, const wxSize& size ) ;

private:
    void OnAbout( wxCommandEvent& event );
    void OnExit( wxCommandEvent& event );
};


int main( int argc, char** argv ) {
    {
        if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD ) < 0 )
        {
            std::cerr << "Failed to initialize SDL: " << SDL_GetError() << std::endl ;
            return 1 ;
        }

        std::shared_ptr<FrameWrapper> wrapper = std::make_shared<FrameWrapper>() ;

        std::thread ui( &FrameWrapper::start_thread, wrapper, argc, argv ) ;

        bool active = 1;
        SDL_Event event ;

        std::string test ;
        while( active )
        {
            if( SDL_WaitEvent( &event ) )
            {
                switch( event.type )
                {
                case SDL_EVENT_KEY_DOWN:
                    test = SDL_GetKeyFromScancode( event.key.keysym.scancode ) ;
                    break ;
                case SDL_EVENT_USER:
                    active = false ;
                    break ;
                }
            }
        }

        ui.join() ;
        /* Deinitialize SDL */
        SDL_Quit();
    }
    return 0;
}


FrameWrapper::FrameWrapper()
{

}

int FrameWrapper::start_thread( int argc, char** argv )
{
    wxApp::SetInstance( new gui() ) ;
    wxEntryStart( argc, argv ) ;
    wxTheApp->CallOnInit() ;


    wxFrame* mainFrame = dynamic_cast< wxFrame* >( wxTheApp->GetTopWindow() ) ;
    if( !mainFrame )
        return 1 ;


    SDL_PropertiesID props = SDL_CreateProperties() ;
    //SDL_SetBooleanProperty( props, SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN, true ) ;

    SDL_SetProperty( props, SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER, mainFrame->GetHandle() ) ;

    SDL_Window* sdlWindow = SDL_CreateWindowWithProperties( props )  ;

    if( !sdlWindow )
    {
        return 1 ;
    }

    wxTheApp->OnRun() ;

    SDL_DestroyWindow( sdlWindow ) ;
    wxEntryCleanup() ;


    uint32_t threadType = SDL_RegisterEvents( 1 ) ;
    if( threadType != ( ( uint32_t )-1 ) )
    {
        SDL_Event threadInterrupt ;
        SDL_zero( threadInterrupt ) ;
        threadInterrupt.type = threadType ;
        threadInterrupt.user.data1 = nullptr ;
        threadInterrupt.user.data2 = nullptr ;
        SDL_PushEvent( &threadInterrupt ) ;
    }
    return 0;
}

gui::gui()
{

}

bool gui::OnInit()
{
    MyFrame* frame = new MyFrame( "Title", wxDefaultPosition, wxDefaultSize ) ;
    frame->Show() ;
    return true;
}

wxIMPLEMENT_APP_NO_MAIN( gui ) ;

MyFrame::MyFrame( const wxString& title, const wxPoint& pos, const wxSize& size )
    : wxFrame( nullptr, wxID_ANY, title, pos, size )
{

    wxMenu* menuFile = new wxMenu;
    menuFile->AppendSeparator();
    menuFile->Append( wxID_EXIT );

    wxMenu* menuHelp = new wxMenu;
    menuHelp->Append( wxID_ABOUT );

    wxMenuBar* menuBar = new wxMenuBar;
    menuBar->Append( menuFile, "&File" );
    menuBar->Append( menuHelp, "&Help" );

    SetMenuBar( menuBar );

    CreateStatusBar();
    SetStatusText( "Welcome to wxWidgets!" );
    Bind( wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT );
    Bind( wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT );

}

void MyFrame::OnAbout( wxCommandEvent& event )
{
    wxMessageBox( "This is a wxWidgets Hello World example",
        "About Hello World", wxOK | wxICON_INFORMATION );
}

void MyFrame::OnExit( wxCommandEvent& event )
{
    Close( true );
}