[Solved] Ogre-next passing events to parentWindowHandle on Windows

Problems building or running the engine, queries about how to use features etc.
knn217
Halfling
Posts: 82
Joined: Wed Jan 25, 2023 9:04 am
x 5

[Solved] Ogre-next passing events to parentWindowHandle on Windows

Post by knn217 »

Ogre Version: :?: ogre-next v3-0
Operating System: Windows / Linux(Ubuntu 24.04)
Render System: Vulkan

I'm currently using Ogre alongside SDL3 and ImGui in my app, I needed SDL3 since they have the new multikeyboard feature that doesn't exist in SDL2. This leads to some problems with telling ogre-next V3 to render to my SDL3 window.

After experimenting with the NameValuePairList, I found using parentWindowHandle instead of externalWindowHandle to work better.

Ogre-next renders correctly, and my app (parent window) was able to get events correctly on Linux (Ubuntu 24.04). But on Windows, my app could not get some events, like mouse movement. Is there a setting in NameValuePairList that I can pass in to make the ogre window pass events to the parent?

Thank you!

Code: Select all

void GraphSys3D::init(const std::string& device_choice, const std::string& win_handle,
    void* xdisplay, const unsigned long &xwindow,
    const std::uint32_t& wdth, const std::uint32_t& hght, const bool& fullscreen)
{
    Ogre::NameValuePairList config_params;

// params.insert(std::make_pair("external_device", win_handle)); // for vulkan on linux
config_params.insert(std::make_pair("title", "window title"));
config_params.insert(std::make_pair("gamma", "Yes"));
config_params.insert(std::make_pair("vsync", "No"));
config_params.insert(std::make_pair("vsync_interval", "1"));
config_params.insert(std::make_pair("vsync_method", "Render Ahead / FIFO"));
config_params.insert(std::make_pair("FSAA", "1"));
config_params.insert(std::make_pair("reverse_depth", "Yes"));
// config_params.insert(std::make_pair("externalGLContext", std::to_string(winGlContext)));
// config_params.insert(std::make_pair("externalGLControl", "True"));

#if defined(_WIN32) || defined(_WIN64)
#elif defined(__linux__)
    struct
    {
        Display *display;           /**< The X11 display */
        Window window;              /**< The X11 window */
    } x11;
    x11.display = (Display *)xdisplay;
    x11.window = (Window)xwindow;
    config_params.insert(std::make_pair("SDL2x11", Ogre::StringConverter::toString((uintptr_t)&x11)));
#endif
    // config_params.insert(std::make_pair("externalWindowHandle", win_handle));
    config_params.insert(std::make_pair("parentWindowHandle", win_handle));

this->initMiscParamsListener(config_params);

this->mRenderWindow = this->mRoot->createRenderWindow(
    "Main RenderWindow",
    wdth, hght,
    false, &config_params);

this->mOverlaySystem = OGRE_NEW Ogre::v1::OverlaySystem();

this->setupResources();
this->loadResources();       // registerHlms is called in this function
this->chooseSceneManager();
this->createCamera();
this->mWorkspace = this->setupCompositor();
return;
}
Last edited by knn217 on Fri May 02, 2025 4:35 am, edited 3 times in total.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5496
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1365

Re: Ogre-next passing events to parentWindowHandle on Windows

Post by dark_sylinc »

Linux + Vulkan ignores "parentWindowHandle" and what's making it work must be SDL2x11.

After experimenting with the NameValuePairList, I found using parentWindowHandle instead of externalWindowHandle to work better.

What are the problems when using "externalWindowHandle" ?

When using a parent handle, since OgreNext owns the child HWND, it registers WindowEventUtilities::_WndProc to process event messages. This doesn't happen with "externalWindowHandle", so that SDL3 gets to process them instead.

Cheers

knn217
Halfling
Posts: 82
Joined: Wed Jan 25, 2023 9:04 am
x 5

Re: Ogre-next passing events to parentWindowHandle on Windows

Post by knn217 »

dark_sylinc wrote: Tue Apr 22, 2025 2:36 am

What are the problems when using "externalWindowHandle" ?

On Windows, when using "externalWindowHandle", Ogre won't render to my app, leaving the app to have the last rendered Gui from IMGUI after I clicked to choose rendering device (I wanted to use ImGUI to pick device instead of the Ogre's window).
Image

Here's my code for graphics system class:

Code: Select all

#include "graph_sys_3d.h"

#include "GameState.h"
#include "GameEntity.h"

#include "OgreAbiUtils.h"
#include "OgreConfigFile.h"
#include "OgreException.h"
#include "OgreRoot.h"

#include "OgreCamera.h"
#include "OgreItem.h"

#include "OgreArchiveManager.h"
#include "OgreHlmsManager.h"
#include "OgreHlmsPbs.h"
#include "OgreHlmsUnlit.h"

#include "Compositor/OgreCompositorManager2.h"

#include "OgreOverlayManager.h"
#include "OgreOverlaySystem.h"

#include "OgreTextureGpuManager.h"

#include "OgreWindow.h"
#include "OgreWindowEventUtilities.h"

#include "OgreFileSystemLayer.h"

#include "OgreGpuProgramManager.h"
#include "OgreHlmsDiskCache.h"

#include "OgreLogManager.h"

#include "OgrePlatformInformation.h"

#include "System/Android/AndroidSystems.h"

#include "OgreAtmosphereComponent.h"
#ifdef OGRE_BUILD_COMPONENT_ATMOSPHERE
#    include "OgreAtmosphereNpr.h"
#endif

#include <fstream>

#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
#    include "OSX/macUtils.h"
#    if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
#        include "System/iOS/iOSUtils.h"
#    else
#        include "System/OSX/OSXUtils.h"
#    endif
#endif

#include <cstdint>

#include <SDL3/SDL.h>

#if defined(__linux__)
#include <X11/Xlib.h>
#endif
//using namespace Demo;

GraphSys3D::GraphSys3D(
        const Ogre::String& resourcePath,
        const Ogre::String& writeAccessFolder,
        Ogre::ColourValue backgroundColour ) :
    GraphSys(),
    mRoot( 0 ),
    mRenderWindow( 0 ),
    mSceneManager( 0 ),
    mCamera( 0 ),
    mWorkspace( 0 ),
    mPluginsFolder( "" ),
    mResourcePath( resourcePath ),
    mWriteAccessFolder( writeAccessFolder ),
    mOverlaySystem( 0 ),
    mAccumTimeSinceLastLogicFrame( 0 ),
    mCurrentTransformIdx( 0 ),
    mThreadWeight( 0 ),
    mAlwaysAskForConfig( true ),
    mUseHlmsDiskCache( true ),
    mUseMicrocodeCache( true ),
    mBackgroundColour( backgroundColour )
{}
//-----------------------------------------------------------------------------------
GraphSys3D::~GraphSys3D()
{
    if( mRoot )
    {
        Ogre::LogManager::getSingleton().logMessage(
            "WARNING: GraphSys3D::deinitialize() not called!!!", Ogre::LML_CRITICAL );
    }
}
//-----------------------------------------------------------------------------------
bool GraphSys3D::isWriteAccessFolder( const Ogre::String &folderPath,
                                          const Ogre::String &fileToSave )
{
    if( !Ogre::FileSystemLayer::createDirectory( folderPath ) )
        return false;

std::ofstream of( ( folderPath + fileToSave ).c_str(),
                  std::ios::out | std::ios::binary | std::ios::app );
if( !of )
    return false;

return true;
}
//---------------------------------------------------------------------------------------
std::vector<std::string> GraphSys3D::selectGraphics(const std::string& pluginsFile)
{
    const Ogre::String windowTitle = "Window's Title";

const Ogre::AbiCookie abiCookie = Ogre::generateAbiCookie();
this->mRoot = OGRE_NEW Ogre::Root(&abiCookie,
    this->mPluginsFolder + pluginsFile,
    this->mWriteAccessFolder + "ogre.cfg",
    this->mWriteAccessFolder + "Ogre.log",
    windowTitle);

this->mStaticPluginLoader.install(this->mRoot);

// std::string renderSys_name = "OpenGL 3+ Rendering Subsystem";
std::string renderSys_name = "Vulkan Rendering Subsystem";
Ogre::RenderSystem* rs = this->mRoot->getRenderSystemByName(renderSys_name); // get the renderer by hard name, we will only use Vulkan
Ogre::String error = rs->validateConfigOptions(); // validate render system
if (error.length() > 0)
{
    this->mQuit = true;
    return std::vector<std::string>();
}
this->mRoot->setRenderSystem(rs);
// enable sRGB Gamma Conversion
rs->setConfigOption("sRGB Gamma Conversion", "Yes");


this->mRoot->initialise(false);

Ogre::ConfigOptionMap& config_options = rs->getConfigOptions();
Ogre::StringVector possible_devices = config_options["Device"].possibleValues;
return possible_devices;
}
//---------------------------------------------------------------------------------------
void GraphSys3D::init(const std::string& device_choice, const std::string& win_handle,
    void* xdisplay, const unsigned long &xwindow,
    const std::uint32_t& wdth, const std::uint32_t& hght, const bool& fullscreen)
{
    Ogre::NameValuePairList config_params;

// params.insert(std::make_pair("external_device", win_handle)); // for vulkan on linux
config_params.insert(std::make_pair("title", "window title"));
config_params.insert(std::make_pair("gamma", "Yes"));
config_params.insert(std::make_pair("vsync", "No"));
config_params.insert(std::make_pair("vsync_interval", "1"));
config_params.insert(std::make_pair("vsync_method", "Render Ahead / FIFO"));
config_params.insert(std::make_pair("FSAA", "1"));
config_params.insert(std::make_pair("reverse_depth", "Yes"));
// config_params.insert(std::make_pair("externalGLContext", std::to_string(winGlContext)));
// config_params.insert(std::make_pair("externalGLControl", "True"));

#if defined(_WIN32) || defined(_WIN64)
#elif defined(__linux__)
    struct
    {
        Display *display;           /**< The X11 display */
        Window window;              /**< The X11 window */
    } x11;
    x11.display = (Display *)xdisplay;
    x11.window = (Window)xwindow;
    config_params.insert(std::make_pair("SDL2x11", Ogre::StringConverter::toString((uintptr_t)&x11)));
#endif
    // config_params.insert(std::make_pair("externalWindowHandle", win_handle));
    config_params.insert(std::make_pair("parentWindowHandle", win_handle));

this->initMiscParamsListener(config_params);

this->mRenderWindow = this->mRoot->createRenderWindow(
    "Main RenderWindow",
    wdth, hght,
    false, &config_params);

this->mOverlaySystem = OGRE_NEW Ogre::v1::OverlaySystem();

this->setupResources();
this->loadResources();       // registerHlms is called in this function
this->chooseSceneManager();
this->createCamera();
this->mWorkspace = this->setupCompositor();
return;
}
//-----------------------------------------------------------------------------------
void GraphSys3D::deinitialize()
{
    this->saveTextureCache();
    this->saveHlmsDiskCache();

if( mSceneManager )
{
    Ogre::AtmosphereComponent *atmosphere = mSceneManager->getAtmosphereRaw();
    OGRE_DELETE atmosphere;

    mSceneManager->removeRenderQueueListener( mOverlaySystem );
}

OGRE_DELETE mOverlaySystem;
mOverlaySystem = 0;

OGRE_DELETE mRoot;
mRoot = 0;

}
//-----------------------------------------------------------------------------------
void GraphSys3D::initImgui()
{
    // Register imgui for rendering
    this->mRoot->addFrameListener(new ImguiFrameListener);
    //initialise with your target workspace.
    ImguiManager::getSingleton().init(this->getCompositorWorkspace());
    return;
}
//-----------------------------------------------------------------------------------
void GraphSys3D::deinitImgui()
{
    ImguiManager::getSingleton().shutdown();
    return;
}
//-----------------------------------------------------------------------------------
void GraphSys3D::updateImgui()
{
    //Calculate the frame delta time using SDL's helper functions.
    static std::uint64_t g_Time = 0;
    static std::uint64_t frequency = SDL_GetPerformanceFrequency();
    std::uint64_t current_time = SDL_GetPerformanceCounter();
    float deltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f);
    g_Time = current_time;

ImguiManager::getSingletonPtr()->newFrame(deltaTime);
return;
}
//-----------------------------------------------------------------------------------
void GraphSys3D::update( float timeSinceLast )
{
    this->mRoot->renderOneFrame();
    this->update3D(timeSinceLast);
    return;
}
//-----------------------------------------------------------------------------------
void GraphSys3D::addResourceLocation( const Ogre::String &archName, const Ogre::String &typeName,
                                          const Ogre::String &secName )
{
#if( OGRE_PLATFORM == OGRE_PLATFORM_APPLE ) || ( OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS )
    // OS X does not set the working directory relative to the app,
    // In order to make things portable on OS X we need to provide
    // the loading with it's own bundle path location
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
        Ogre::String( Ogre::macBundlePath() + "/" + archName ), typeName, secName );
#else
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation( archName, typeName, secName );
#endif
}
//-----------------------------------------------------------------------------------
void GraphSys3D::loadTextureCache()
{
#if !OGRE_NO_JSON
    Ogre::ArchiveManager &archiveManager = Ogre::ArchiveManager::getSingleton();
    Ogre::Archive *rwAccessFolderArchive =
        archiveManager.load( mWriteAccessFolder, "FileSystem", true );
    try
    {
        const Ogre::String filename = "textureMetadataCache.json";
        if( rwAccessFolderArchive->exists( filename ) )
        {
            Ogre::DataStreamPtr stream = rwAccessFolderArchive->open( filename );
            std::vector<char> fileData;
            fileData.resize( stream->size() + 1 );
            if( !fileData.empty() )
            {
                stream->read( &fileData[0], stream->size() );
                // Add null terminator just in case (to prevent bad input)
                fileData.back() = '\0';
                Ogre::TextureGpuManager *textureManager =
                    mRoot->getRenderSystem()->getTextureGpuManager();
                textureManager->importTextureMetadataCache( stream->getName(), &fileData[0], false );
            }
        }
        else
        {
            Ogre::LogManager::getSingleton().logMessage( "[INFO] Texture cache not found at " +
                                                         mWriteAccessFolder +
                                                         "/textureMetadataCache.json" );
        }
    }
    catch( Ogre::Exception &e )
    {
        Ogre::LogManager::getSingleton().logMessage( e.getFullDescription() );
    }

archiveManager.unload( rwAccessFolderArchive );
#endif
}
//-----------------------------------------------------------------------------------
void GraphSys3D::saveTextureCache()
{
    if( mRoot->getRenderSystem() )
    {
        Ogre::TextureGpuManager *textureManager = mRoot->getRenderSystem()->getTextureGpuManager();
        if( textureManager )
        {
            Ogre::String jsonString;
            textureManager->exportTextureMetadataCache( jsonString );
            const Ogre::String path = mWriteAccessFolder + "/textureMetadataCache.json";
            std::ofstream file( path.c_str(), std::ios::binary | std::ios::out );
            if( file.is_open() )
                file.write( jsonString.c_str(), static_cast<std::streamsize>( jsonString.size() ) );
            file.close();
        }
    }
}
//-----------------------------------------------------------------------------------
void GraphSys3D::loadHlmsDiskCache()
{
    if( !mUseMicrocodeCache && !mUseHlmsDiskCache )
        return;

Ogre::HlmsManager *hlmsManager = mRoot->getHlmsManager();
Ogre::HlmsDiskCache diskCache( hlmsManager );

Ogre::ArchiveManager &archiveManager = Ogre::ArchiveManager::getSingleton();

Ogre::Archive *rwAccessFolderArchive =
    archiveManager.load( mWriteAccessFolder, "FileSystem", true );

if( mUseMicrocodeCache )
{
    // Make sure the microcode cache is enabled.
    Ogre::GpuProgramManager::getSingleton().setSaveMicrocodesToCache( true );
    const Ogre::String filename = "microcodeCodeCache.cache";
    if( rwAccessFolderArchive->exists( filename ) )
    {
        Ogre::DataStreamPtr shaderCacheFile = rwAccessFolderArchive->open( filename );
        Ogre::GpuProgramManager::getSingleton().loadMicrocodeCache( shaderCacheFile );
    }
}

if( mUseHlmsDiskCache )
{
    for( size_t i = Ogre::HLMS_LOW_LEVEL + 1u; i < Ogre::HLMS_MAX; ++i )
    {
        Ogre::Hlms *hlms = hlmsManager->getHlms( static_cast<Ogre::HlmsTypes>( i ) );
        if( hlms )
        {
            Ogre::String filename =
                "hlmsDiskCache" + Ogre::StringConverter::toString( i ) + ".bin";

            try
            {
                if( rwAccessFolderArchive->exists( filename ) )
                {
                    Ogre::DataStreamPtr diskCacheFile = rwAccessFolderArchive->open( filename );
                    diskCache.loadFrom( diskCacheFile );
                    diskCache.applyTo( hlms );
                }
            }
            catch( Ogre::Exception & )
            {
                Ogre::LogManager::getSingleton().logMessage(
                    "Error loading cache from " + mWriteAccessFolder + "/" + filename +
                    "! If you have issues, try deleting the file "
                    "and restarting the app" );
            }
        }
    }
}

archiveManager.unload( mWriteAccessFolder );
}
//-----------------------------------------------------------------------------------
void GraphSys3D::saveHlmsDiskCache()
{
    if( mRoot->getRenderSystem() && Ogre::GpuProgramManager::getSingletonPtr() &&
        ( mUseMicrocodeCache || mUseHlmsDiskCache ) )
    {
        Ogre::HlmsManager *hlmsManager = mRoot->getHlmsManager();
        Ogre::HlmsDiskCache diskCache( hlmsManager );

    Ogre::ArchiveManager &archiveManager = Ogre::ArchiveManager::getSingleton();

    Ogre::Archive *rwAccessFolderArchive =
        archiveManager.load( mWriteAccessFolder, "FileSystem", false );

    if( mUseHlmsDiskCache )
    {
        for( size_t i = Ogre::HLMS_LOW_LEVEL + 1u; i < Ogre::HLMS_MAX; ++i )
        {
            Ogre::Hlms *hlms = hlmsManager->getHlms( static_cast<Ogre::HlmsTypes>( i ) );
            if( hlms )
            {
                diskCache.copyFrom( hlms );

                Ogre::DataStreamPtr diskCacheFile = rwAccessFolderArchive->create(
                    "hlmsDiskCache" + Ogre::StringConverter::toString( i ) + ".bin" );
                diskCache.saveTo( diskCacheFile );
            }
        }
    }

    if( Ogre::GpuProgramManager::getSingleton().isCacheDirty() && mUseMicrocodeCache )
    {
        const Ogre::String filename = "microcodeCodeCache.cache";
        Ogre::DataStreamPtr shaderCacheFile = rwAccessFolderArchive->create( filename );
        Ogre::GpuProgramManager::getSingleton().saveMicrocodeCache( shaderCacheFile );
    }

    archiveManager.unload( mWriteAccessFolder );
}
}
//-----------------------------------------------------------------------------------
void GraphSys3D::setupResources()
{
    // Load resource paths from config file
    Ogre::ConfigFile cf;
    cf.load( AndroidSystems::openFile( mResourcePath + "resources2.cfg" ) );

// Go through all sections & settings in the file
Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

Ogre::String secName, typeName, archName;
while( seci.hasMoreElements() )
{
    secName = seci.peekNextKey();
    Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();

    if( secName != "Hlms" )
    {
        Ogre::ConfigFile::SettingsMultiMap::iterator i;
        for( i = settings->begin(); i != settings->end(); ++i )
        {
            typeName = i->first;
            archName = i->second;
            addResourceLocation( archName, typeName, secName );
        }
    }
}
}
//-----------------------------------------------------------------------------------
void GraphSys3D::registerHlms()
{
    Ogre::ConfigFile cf;
    cf.load( AndroidSystems::openFile( mResourcePath + "resources2.cfg" ) );

#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
    Ogre::String rootHlmsFolder =
        Ogre::macBundlePath() + '/' + cf.getSetting( "DoNotUseAsResource", "Hlms", "" );
#else
    Ogre::String rootHlmsFolder = mResourcePath + cf.getSetting( "DoNotUseAsResource", "Hlms", "" );
#endif

if( rootHlmsFolder.empty() )
    rootHlmsFolder = AndroidSystems::isAndroid() ? "/" : "./";
else if( *( rootHlmsFolder.end() - 1 ) != '/' )
    rootHlmsFolder += "/";

// At this point rootHlmsFolder should be a valid path to the Hlms data folder

Ogre::HlmsUnlit *hlmsUnlit = 0;
Ogre::HlmsPbs *hlmsPbs = 0;

// For retrieval of the paths to the different folders needed
Ogre::String mainFolderPath;
Ogre::StringVector libraryFoldersPaths;
Ogre::StringVector::const_iterator libraryFolderPathIt;
Ogre::StringVector::const_iterator libraryFolderPathEn;

Ogre::ArchiveManager &archiveManager = Ogre::ArchiveManager::getSingleton();

const Ogre::String &archiveType = getMediaReadArchiveType();

{
    // Create & Register HlmsUnlit
    // Get the path to all the subdirectories used by HlmsUnlit
    Ogre::HlmsUnlit::getDefaultPaths( mainFolderPath, libraryFoldersPaths );
    Ogre::Archive *archiveUnlit =
        archiveManager.load( rootHlmsFolder + mainFolderPath, archiveType, true );
    Ogre::ArchiveVec archiveUnlitLibraryFolders;
    libraryFolderPathIt = libraryFoldersPaths.begin();
    libraryFolderPathEn = libraryFoldersPaths.end();
    while( libraryFolderPathIt != libraryFolderPathEn )
    {
        Ogre::Archive *archiveLibrary =
            archiveManager.load( rootHlmsFolder + *libraryFolderPathIt, archiveType, true );
        archiveUnlitLibraryFolders.push_back( archiveLibrary );
        ++libraryFolderPathIt;
    }

    // Create and register the unlit Hlms
    hlmsUnlit = OGRE_NEW Ogre::HlmsUnlit( archiveUnlit, &archiveUnlitLibraryFolders );
    Ogre::Root::getSingleton().getHlmsManager()->registerHlms( hlmsUnlit );
}

{
    // Create & Register HlmsPbs
    // Do the same for HlmsPbs:
    Ogre::HlmsPbs::getDefaultPaths( mainFolderPath, libraryFoldersPaths );
    Ogre::Archive *archivePbs =
        archiveManager.load( rootHlmsFolder + mainFolderPath, archiveType, true );

    // Get the library archive(s)
    Ogre::ArchiveVec archivePbsLibraryFolders;
    libraryFolderPathIt = libraryFoldersPaths.begin();
    libraryFolderPathEn = libraryFoldersPaths.end();
    while( libraryFolderPathIt != libraryFolderPathEn )
    {
        Ogre::Archive *archiveLibrary =
            archiveManager.load( rootHlmsFolder + *libraryFolderPathIt, archiveType, true );
        archivePbsLibraryFolders.push_back( archiveLibrary );
        ++libraryFolderPathIt;
    }

    // Create and register
    hlmsPbs = OGRE_NEW Ogre::HlmsPbs( archivePbs, &archivePbsLibraryFolders );
    Ogre::Root::getSingleton().getHlmsManager()->registerHlms( hlmsPbs );
}

Ogre::RenderSystem *renderSystem = mRoot->getRenderSystem();
if( renderSystem->getName() == "Direct3D11 Rendering Subsystem" )
{
    // Set lower limits 512kb instead of the default 4MB per Hlms in D3D 11.0
    // and below to avoid saturating AMD's discard limit (8MB) or
    // saturate the PCIE bus in some low end machines.
    bool supportsNoOverwriteOnTextureBuffers;
    renderSystem->getCustomAttribute( "MapNoOverwriteOnDynamicBufferSRV",
                                      &supportsNoOverwriteOnTextureBuffers );

    if( !supportsNoOverwriteOnTextureBuffers )
    {
        hlmsPbs->setTextureBufferDefaultSize( 512 * 1024 );
        hlmsUnlit->setTextureBufferDefaultSize( 512 * 1024 );
    }
}
}
//-----------------------------------------------------------------------------------
void GraphSys3D::loadResources()
{
    registerHlms();

loadTextureCache();
loadHlmsDiskCache();

// Initialise, parse scripts etc
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups( true );

// Initialize resources for LTC area lights and accurate specular reflections (IBL)
Ogre::Hlms *hlms = mRoot->getHlmsManager()->getHlms( Ogre::HLMS_PBS );
OGRE_ASSERT_HIGH( dynamic_cast<Ogre::HlmsPbs *>( hlms ) );
Ogre::HlmsPbs *hlmsPbs = static_cast<Ogre::HlmsPbs *>( hlms );
try
{
    hlmsPbs->loadLtcMatrix();
}
catch( Ogre::FileNotFoundException &e )
{
    Ogre::LogManager::getSingleton().logMessage( e.getFullDescription(), Ogre::LML_CRITICAL );
    Ogre::LogManager::getSingleton().logMessage(
        "WARNING: LTC matrix textures could not be loaded. Accurate specular IBL reflections "
        "and LTC area lights won't be available or may not function properly!",
        Ogre::LML_CRITICAL );
}
}
//-----------------------------------------------------------------------------------
void GraphSys3D::chooseSceneManager()
{
#if OGRE_DEBUG_MODE >= OGRE_DEBUG_HIGH
    // Debugging multithreaded code is a PITA, disable it.
    const size_t numThreads = 1;
#else
    // getNumLogicalCores() may return 0 if couldn't detect
    const size_t numThreads = std::max<size_t>( 1, Ogre::PlatformInformation::getNumLogicalCores() );
#endif
    // Create the SceneManager, in this case a generic one
    mSceneManager = mRoot->createSceneManager( Ogre::ST_GENERIC, numThreads, "ExampleSMInstance" );

mSceneManager->addRenderQueueListener( mOverlaySystem );
mSceneManager->getRenderQueue()->setSortRenderQueue(
    Ogre::v1::OverlayManager::getSingleton().mDefaultRenderQueueId,
    Ogre::RenderQueue::StableSort );

// Set sane defaults for proper shadow mapping
mSceneManager->setShadowDirectionalLightExtrusionDistance( 500.0f );
mSceneManager->setShadowFarDistance( 500.0f );
}
//-----------------------------------------------------------------------------------
void GraphSys3D::createCamera()
{
    mCamera = mSceneManager->createCamera( "Main Camera" );

// Position it at 500 in Z direction
mCamera->setPosition( Ogre::Vector3( 0, 5, 15 ) );
// Look back along -Z
mCamera->lookAt( Ogre::Vector3( 0, 0, 0 ) );
mCamera->setNearClipDistance( 0.2f );
mCamera->setFarClipDistance( 1000.0f );
mCamera->setAutoAspectRatio( true );
}
//-----------------------------------------------------------------------------------
Ogre::CompositorWorkspace *GraphSys3D::setupCompositor()
{
    Ogre::CompositorManager2 *compositorManager = mRoot->getCompositorManager2();

const Ogre::String workspaceName( "Demo Workspace" );
if( !compositorManager->hasWorkspaceDefinition( workspaceName ) )
{
    compositorManager->createBasicWorkspaceDef( workspaceName, mBackgroundColour,
                                                Ogre::IdString() );
}

return compositorManager->addWorkspace( mSceneManager, mRenderWindow->getTexture(), mCamera,
                                        workspaceName, true );
}
//-----------------------------------------------------------------------------------
void GraphSys3D::initMiscParamsListener( Ogre::NameValuePairList &params ) {}
//-----------------------------------------------------------------------------------
void GraphSys3D::createAtmosphere( Ogre::Light *sunLight )
{
#ifdef OGRE_BUILD_COMPONENT_ATMOSPHERE
    {
        Ogre::AtmosphereComponent *atmosphere = mSceneManager->getAtmosphereRaw();
        OGRE_DELETE atmosphere;
    }

Ogre::AtmosphereNpr *atmosphere =
    OGRE_NEW Ogre::AtmosphereNpr( mRoot->getRenderSystem()->getVaoManager() );

{
    // Preserve the Power Scale explicitly set by the sample
    Ogre::AtmosphereNpr::Preset preset = atmosphere->getPreset();
    preset.linkedLightPower = sunLight->getPowerScale();
    atmosphere->setPreset( preset );
}

atmosphere->setSunDir(
    sunLight->getDirection(),
    std::asin( Ogre::Math::Clamp( -sunLight->getDirection().y, -1.0f, 1.0f ) ) /
        Ogre::Math::PI );
atmosphere->setLight( sunLight );
atmosphere->setSky( mSceneManager, true );
#endif
}
//-----------------------------------------------------------------------------------
void GraphSys3D::setAlwaysAskForConfig( bool alwaysAskForConfig )
{
    mAlwaysAskForConfig = alwaysAskForConfig;
}
//-----------------------------------------------------------------------------------
const char *GraphSys3D::getMediaReadArchiveType() const
{
#if OGRE_PLATFORM != OGRE_PLATFORM_ANDROID
    return "FileSystem";
#else
    return "APKFileSystem";
#endif
}
//-----------------------------------------------------------------------------------
void GraphSys3D::stopCompositor()
{
    if( mWorkspace )
    {
        Ogre::CompositorManager2 *compositorManager = mRoot->getCompositorManager2();
        compositorManager->removeWorkspace( mWorkspace );
        mWorkspace = 0;
    }
}
//-----------------------------------------------------------------------------------
void GraphSys3D::restartCompositor()
{
    stopCompositor();
    mWorkspace = setupCompositor();
}
//-----------------------------------------------------------------------------------
void GraphSys3D::loadScene(std::uint16_t index)
{
    Ogre::Item *item = mSceneManager->createItem(
        "Cube_d.mesh", Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME,
        Ogre::SCENE_DYNAMIC );
    mSceneNode = mSceneManager->getRootSceneNode( Ogre::SCENE_DYNAMIC )
                     ->createChildSceneNode( Ogre::SCENE_DYNAMIC );
    mSceneNode->attachObject( item );
}
//-----------------------------------------------------------------------------------
void GraphSys3D::update3D( float timeSinceLast )
{
    static float mDisplacement;
    const Ogre::Vector3 origin( -5.0f, 0.0f, 0.0f );
    mDisplacement += timeSinceLast * 4.0f;
    mDisplacement = fmodf( mDisplacement, 10.0f );
    mSceneNode->setPosition( origin + Ogre::Vector3::UNIT_X * mDisplacement );
}
//-----------------------------------------------------------------------------------
void GraphSys3D::requestResize(const std::uint32_t& wdth, const std::uint32_t& hght, const bool& fullscreen)
{
    if (this->ready()){
        this->mResizedOrMoved = true;
        this->mRequestedWdth = wdth;
        this->mRequestedHght = hght;
    }
}
//-----------------------------------------------------------------------------------
void GraphSys3D::checkResize()
{
    if (this->ready() && this->mResizedOrMoved){
        this->mRenderWindow->requestResolution(this->mRequestedWdth, this->mRequestedHght);
        this->mResizedOrMoved = false;
    }
}

And here's how my app uses it:

Code: Select all

#include "Threading/YieldTimer.h"
#include "OgreTimer.h"
#include <iostream>
#include "app.h"
#if defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
#elif defined(__linux__)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <unistd.h>
#endif

#include <stdexcept>

std::string SDL_getWinHandle(SDL_Window* window, void* &xdisplay, unsigned long &xwindow)
{
    std::string win_handle{"Default Handle"};
#if defined(_WIN32) || defined(_WIN64)
    HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
    if (hwnd) {
        win_handle = std::to_string((unsigned long long)hwnd);
    }
#elif defined(__linux__)
    if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
        xdisplay = (void *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
        xwindow = (unsigned long)SDL_GetNumberProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
        win_handle = std::to_string(xwindow);
    } else if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0) {
        struct wl_display *display = (struct wl_display *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, NULL);
        struct wl_surface *surface = (struct wl_surface *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL);
        // Wayland not implemented
        throw std::logic_error( "wayland not implemented!" );
    }
#endif

return win_handle;
}

// ------------------------------------
bool App::init()
{
    this->mDone = false;
    this->mFullScreen = false;
    // Init SDL window
    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS |
        SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD))
        return false;

//SDL_SetHint(SDL_HINT_WINDOWS_GAMEINPUT, "1");
#if defined(_WIN32) || defined(_WIN64)
    SDL_SetHint(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1");
#elif defined(__linux__)
#define USE_XINPUT2_KEYBOARD
#define SDL_VIDEO_DRIVER_X11_XINPUT2
#define SDL_VIDEO_DRIVER_X11
    //SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "wayland");
    SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11");
#endif

this->mWindow = SDL_CreateWindow("SDL3 window", this->getWdth(), this->getHght(), 
    SDL_WINDOW_OCCLUDED |
    SDL_WINDOW_KEYBOARD_GRABBED |
//        SDL_WINDOW_BORDERLESS |
        SDL_WINDOW_VULKAN
    );
    SDL_SetWindowPosition(this->mWindow, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
    this->mRenderer = SDL_CreateRenderer(mWindow, NULL);

if (!this->mWindow || !this->mRenderer)
    return false;

this->mWinHandle = SDL_getWinHandle(this->mWindow, this->mXdisplay, this->mXwindow);

// Show SDL window
SDL_ShowWindow(this->mWindow);

this->mGui = new Gui();
this->mGui->init(this->mWindow, this->mRenderer, this->mFullScreen);
this->mGui->setDebug(this->mWinHandle);

return true;
}

// ------------------------------------
void App::keyPress(SDL_Keycode code, SDL_KeyboardID id)
{
    switch (code)
    {
    default:
        break;
    }
}

// ------------------------------------
void App::keyRelease(SDL_Keycode code, SDL_KeyboardID id)
{
    switch (code)
    {
    case SDLK_ESCAPE:
        this->done();
        break;
    case SDLK_F4:
        this->togFullScreen();
        break;
    default:
        break;
    }
}

// ------------------------------------
void App::togFullScreen()
{
    this->mFullScreen ^= true; // XOR for toggling between true and false
    SDL_SetWindowSize(this->mWindow, this->getWdth(), this->getHght());
    SDL_SetWindowPosition(this->mWindow, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
    if (nullptr != this->mGraphSys) {
        this->mGraphSys->requestResize(this->getWdth(), this->getHght(), this->mFullScreen);
    }
    this->mGui->togFullScreen();
}

// ------------------------------------
void App::event(SDL_Event &event)
{
    int count;
    switch (event.type)
    {
    case SDL_EVENT_QUIT:
        this->done();  // end the program
        break;
    case SDL_EVENT_KEY_DOWN:
        keyPress(event.key.key, event.key.which);
        SDL_GetKeyboards(&count);
        mMessage = SDL_GetKeyName(event.key.key) + std::string(" ") + std::to_string(count) + ":" + std::to_string(event.key.which);
        break;
    case SDL_EVENT_KEY_UP:
        keyRelease(event.key.key, event.key.which);
        SDL_GetKeyboards(&count);
        mMessage = SDL_GetKeyName(event.key.key) + std::string(" ") + std::to_string(count) + ":" + std::to_string(event.key.which);
        break;
    default:
        break;
    }
}

// ------------------------------------
void App::loop()
{
    while (!this->mDone.load()){
        SDL_Event event;
        while (SDL_PollEvent(&event)) {  // poll until all events are handled!
            ImGui_ImplSDL3_ProcessEvent(&event);
            // decide what to do with this event.
            this->event(event);
        }

    this->changeGui();
}
}

// ------------------------------------
void App::deInit()
{
    this->mThreadMgr->joinThreads();
    this->mGui->deInit();

// Deinit SDL window
SDL_DestroyRenderer(mRenderer);
SDL_DestroyWindow(mWindow);
SDL_Quit();

delete this->mThreadMgr;
delete this->mGui;
}

// ------------------------------------
void App::changeGui()
{
    switch(mGui->getCurrent())
    {
    case e_render_config:
        this->mGui->update(this->mRenderer); // update since this gui is still in main thread
        break;
    case e_start_2d:
        break;
    case e_config_3d:
        if (this->mGui->getEdge()){ this->initRender3D(); }
        this->mGui->update(this->mRenderer); // update since this gui is still in main thread
        break;
    case e_start_3d:
        if (this->mGui->getEdge()){
            this->mGui->update(this->mRenderer); // update to reset Gui's edge
            this->mThreadMgr = new ThreadManager();
            this->mThreadMgr->poolThreads();
            this->mThreadMgr->mPause = false;
            this->mThreadMgr->submit(&App::threadRender3D, this);
        }
        break;
    case e_exit:
        this->done();
        break;
    default:
        break;
    }
    // this->mGui->update(mRenderer);
}

// ------------------------------------
void App::initRender2D()
{
    
} // ------------------------------------ void App::initRender3D() { //========================================================================================== const Ogre::String pluginsFolder = "./"; const Ogre::String writeAccessFolder = "./"; Ogre::String resourcePath = ""; #if OGRE_DEBUG_MODE && !( ( OGRE_PLATFORM == OGRE_PLATFORM_APPLE ) || ( OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS ) ) const char* pluginsFile = "plugins_d.cfg"; #else const char* pluginsFile = "plugins.cfg"; #endif this->mGui->setDebug("3d"); this->mGraphSys = new GraphSys3D(); // this->mGraphSys->initialize("win title", this->mWinHandle); this->mDevices = this->mGraphSys->selectGraphics(pluginsFile); this->mGui->setDevices(&this->mDevices); } // ------------------------------------ void App::threadRender3D() { // this->setDebug("Hello from thread 3d"); Ogre::Timer timer; Ogre::uint64 startTime{ timer.getMicroseconds() }; Demo::YieldTimer yieldTimer(&timer); // Create yield timer for fixed frame rate std::size_t render_count{ 0 }; std::uint8_t FPS{ 60 }; std::double_t render_FrameTime{ 1.0 / FPS }; std::string device_choice = this->mGui->getDeviceChoice(); this->mGraphSys->init(device_choice, this->mWinHandle, this->mXdisplay, this->mXwindow, this->getWdth(), this->getHght(), this->mFullScreen); this->mGraphSys->initImgui(); this->mGraphSys->loadScene(1); while (!this->mDone.load()) { this->mGraphSys->updateImgui(); this->mGui->update(this->mRenderer, true); this->mGraphSys->update(render_FrameTime); //std::cout << "Rendering 3D\n"; startTime = yieldTimer.yield(render_FrameTime, startTime); this->setDebug(mMessage); this->mGraphSys->checkResize(); } this->mGraphSys->deinitialize(); delete this->mGraphSys; }
Last edited by knn217 on Sat Apr 26, 2025 2:39 am, edited 2 times in total.
knn217
Halfling
Posts: 82
Joined: Wed Jan 25, 2023 9:04 am
x 5

Re: Ogre-next passing events to parentWindowHandle on Windows

Post by knn217 »

Also, here's how my App init SDL3:

Code: Select all

// ------------------------------------
bool App::init()
{
    this->mDone = false;
    this->mFullScreen = false;
    // Init SDL window
    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS |
        SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD))
        return false;

//SDL_SetHint(SDL_HINT_WINDOWS_GAMEINPUT, "1");
#if defined(_WIN32) || defined(_WIN64)
    SDL_SetHint(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1");
#elif defined(__linux__)
#define USE_XINPUT2_KEYBOARD
#define SDL_VIDEO_DRIVER_X11_XINPUT2
#define SDL_VIDEO_DRIVER_X11
    //SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "wayland");
    SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11");
#endif

this->mWindow = SDL_CreateWindow("SDL3 window", this->getWdth(), this->getHght(), 
    SDL_WINDOW_OCCLUDED |
    SDL_WINDOW_KEYBOARD_GRABBED |
//        SDL_WINDOW_BORDERLESS |
        SDL_WINDOW_VULKAN
    );
    SDL_SetWindowPosition(this->mWindow, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
    this->mRenderer = SDL_CreateRenderer(mWindow, NULL);

if (!this->mWindow || !this->mRenderer)
    return false;

this->mWinHandle = SDL_getWinHandle(this->mWindow, this->mXdisplay, this->mXwindow);

// Show SDL window
SDL_ShowWindow(this->mWindow);

this->mGui = new Gui();
this->mGui->init(this->mWindow, this->mRenderer, this->mFullScreen);
this->mGui->setDebug(this->mWinHandle);

return true;
}

And here's how to get SDL3 window handler for Windows/Linux

Code: Select all

std::string SDL_getWinHandle(SDL_Window* window, void* &xdisplay, unsigned long &xwindow)
{
    std::string win_handle{"Default Handle"};
#if defined(_WIN32) || defined(_WIN64)
    HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
    if (hwnd) {
        win_handle = std::to_string((unsigned long long)hwnd);
    }
#elif defined(__linux__)
    if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
        xdisplay = (void *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
        xwindow = (unsigned long)SDL_GetNumberProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
        win_handle = std::to_string(xwindow);
    } else if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0) {
        struct wl_display *display = (struct wl_display *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, NULL);
        struct wl_surface *surface = (struct wl_surface *)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL);
        // Wayland not implemented
        throw std::logic_error( "wayland not implemented!" );
    }
#endif

return win_handle;
}
knn217
Halfling
Posts: 82
Joined: Wed Jan 25, 2023 9:04 am
x 5

Re: Ogre-next passing events to parentWindowHandle on Windows

Post by knn217 »

dark_sylinc wrote: Tue Apr 22, 2025 2:36 am

What are the problems when using "externalWindowHandle" ?

Sorry about this, I found that "externalWindowHandle" is not the problem here.
I have retried the implementation on a new project and found that SDL3 + Ogre-next(Vulkan)'s "externalWindowHandle" works correctly.

Thank you!