View Issue Details

IDProjectCategoryView StatusLast Update
0002492NoesisGUIC++ SDKpublic2023-01-19 10:10
Reporterjack.barkov Assigned Tosfernandez  
PrioritynormalSeveritycrashReproducibilityalways
Status resolvedResolutionfixed 
Product Version3.1.6 
Target Version3.1.7Fixed in Version3.1.7 
Summary0002492: Noesis::TextBox::GetRangeBounds() - Crash
DescriptionI have a hard to reproduce problem happening in my application.

My test app is a chat, where you press enter, a TextBox is displayed, the user types a text and presses enter to send.

Sometimes when the user does this, the crash happens on:
Noesis.dll!Noesis::TextBox::GetRangeBounds()

Then I tried to reproduce in the noesis examples until I managed to reproduce in the c++ sample.login example.

To reproduce basically apply the patch below and run the program that you will simulate what happens in my program. Here the crash happens in a few seconds after opening.

Stacktrace:

     Noesis.dll!Noesis::TextBox::GetRangeBounds() + 6 bytes Unknown
     [Frames below may be incorrect and/or missing, no symbols loaded for Noesis.dll]
> NoesisApp.dll!TsfTextStore::GetTextExt(unsigned long vcView=0, long acpStart=0, long acpEnd=0, tagRECT * prc=0x012fee14, int * pfClipped=0x012fedcc) Line 654 C++
     TextInputFramework.dll!CACPWrap::GetTextExt(unsigned long,struct IAnchor *,struct IAnchor *,struct tagRECT *,int *) Unknown
     TextInputFramework.dll!CContextView::GetTextExt(unsigned long,struct ITfRange *,struct tagRECT *,int *) Unknown
     TextInputFramework.dll!CInputContextAdapter::GetLayoutBounds(unsigned int,struct EditControlRange const &,struct TextRect *,struct TextRect *) Unknown
     TextInputFramework.dll!CTSF3LayoutQuery::Execute(struct IInputContextAdapter *) Unknown
     TextInputFramework.dll!CInputContextAdapter::_ExecuteOperation(class CTSF3Operation *,bool) Unknown
     TextInputFramework.dll!CInputContextAdapter::GetLayoutBounds(struct EditControlRange,struct TextRect *,struct TextRect *) Unknown
     TextInputFramework.dll!CTextInputClientOwnerAsync::GetLayoutBoundsAsync(struct EditControlRange) Unknown
     TextInputFramework.dll!TextInputClient::UpdateLayoutInfo(unsigned int,bool) Unknown
     TextInputFramework.dll!TextInputClient::EditControlLayoutChanged(unsigned int) Unknown
     TextInputFramework.dll!<lambda>(void)() Unknown
     TextInputFramework.dll!CTextInputClientFreeThread::RunOrDispatch(unsigned int,class std::function<long >,bool) Unknown
     TextInputFramework.dll!CTextInputClientFreeThread::EditControlLayoutChanged(unsigned int) Unknown
     TextInputFramework.dll!CInputContextAdapter::OnEditSessionEnded(unsigned long,struct ITfEditRecord *,bool) Unknown
     TextInputFramework.dll!CInputContext::_NotifyEndEdit(void) Unknown
     TextInputFramework.dll!CInputContext::_SynchAppChanges(unsigned long) Unknown
     TextInputFramework.dll!CInputContext::OnLockGranted(unsigned long) Unknown
     TextInputFramework.dll!CACPWrap::OnLockGranted(unsigned long) Unknown
     NoesisApp.dll!TsfTextStore::RequestLock(unsigned long dwLockFlags=2, HRESULT * phrSession=0x012ff1a4) Line 318 C++
     TextInputFramework.dll!CACPWrap::RequestLock(unsigned long,long *) Unknown
     TextInputFramework.dll!SafeRequestLock(struct ITextStoreAnchor *,unsigned long,long *) Unknown
     TextInputFramework.dll!CInputContext::OnLayoutChange(enum __MIDL___MIDL_itf_textstor_0000_0000_0002,unsigned long) Unknown
     TextInputFramework.dll!CACPWrap::OnLayoutChange(enum __MIDL___MIDL_itf_textstor_0000_0000_0002,unsigned long) Unknown
     NoesisApp.dll!TsfTextStore::OnLayoutChanged() Line 198 C++
     NoesisApp.dll!TsfTextStore::OnScrollChanged(Noesis::BaseComponent * __formal=0x1a77ab28, const Noesis::RoutedEventArgs & __formal={...}) Line 1035 C++
     NoesisApp.dll!Noesis::Delegate<void __cdecl(Noesis::BaseComponent *,Noesis::RoutedEventArgs const &)>::MemberFuncStub<TsfTextStore,void (__thiscall TsfTextStore::*)(Noesis::BaseComponent *,Noesis::RoutedEventArgs const &)>::Invoke(Noesis::BaseComponent * <args_0>=0x1a77ab28, const Noesis::RoutedEventArgs & <args_1>={...}, const void * __formal=0x012ff458, bool(*)(const void *) __formal=0x79599b20) Line 473 C++
     Noesis.dll!Noesis::UIElement::NotifyHandlers() + 140 bytes Unknown
     Noesis.dll!Noesis::UIElement::BubblingEvent() + 90 bytes Unknown
     Noesis.dll!Noesis::ScrollViewer::OnLayoutUpdated() + 956 bytes Unknown
     Noesis.dll!Noesis::UIElement::RaiseEvent() + 100 bytes Unknown
     Noesis.dll!Noesis::Viewbox::StaticGetClassType() + 1867 bytes Unknown
     Noesis.dll!Noesis::Viewbox::StaticGetClassType() + 3086 bytes Unknown
     Noesis.dll!Noesis::Viewbox::StaticGetClassType() + 2669 bytes Unknown
     Noesis.dll!Noesis::Visual::GetContentBoundsCore() + 5163 bytes Unknown
     NoesisApp.dll!NoesisApp::Window::Render(double time=0.94558699999999996) Line 213 C++
     NoesisApp.dll!NoesisApp::Application::Tick(double time=0.94558699999999996) Line 217 C++
     NoesisApp.dll!NoesisApp::ApplicationLauncher::OnTick(double time=0.94558699999999996) Line 116 C++
     NoesisApp.dll!NoesisApp::DisplayLauncher::Run::__l2::<lambda_1>::operator()(NoesisApp::Display * __formal=0x015d6c00) Line 42 C++
     NoesisApp.dll!Noesis::Delegate<void __cdecl(NoesisApp::Display *)>::FunctorStub<`NoesisApp::DisplayLauncher::Run'::`2'::<lambda_1>>::Invoke(NoesisApp::Display * <args_0>=0x015d6c00) Line 432 C++
     Samples.Login.exe!Noesis::Delegate<void __cdecl(NoesisApp::Display *)>::operator()(NoesisApp::Display * <args_0>=0x015d6c00) Line 172 C++
     Samples.Login.exe!Noesis::Delegate<void __cdecl(NoesisApp::Display *)>::MultiDelegate::Invoke(NoesisApp::Display * <args_0>=0x015d6c00) Line 576 C++
     NoesisApp.dll!Noesis::Delegate<void __cdecl(NoesisApp::Display *)>::operator()(NoesisApp::Display * <args_0>=0x015d6c00) Line 172 C++
     NoesisApp.dll!NoesisApp::Win32Display::EnterMessageLoop(bool runInBackground=false) Line 482 C++
     NoesisApp.dll!NoesisApp::DisplayLauncher::Run() Line 47 C++
     Samples.Login.exe!NsMain(int argc=1, char * * argv=0x015204c8) Line 71 C++
     Samples.Login.exe!WinMain(HINSTANCE__ * __formal=0x00ed0000, HINSTANCE__ * __formal=0x00000000, char * __formal=0x0151a01e, int __formal=10) Line 36 C++
     Samples.Login.exe!invoke_main() Line 107 C++
     Samples.Login.exe!__scrt_common_main_seh() Line 288 C++
     Samples.Login.exe!__scrt_common_main() Line 331 C++
     Samples.Login.exe!WinMainCRTStartup(void * __formal=0x010a1000) Line 17 C++
     [email protected]@12() Unknown
     [email protected]() Unknown
     [email protected]() Unknown

TagsC++
PlatformWindows

Activities

jack.barkov

jack.barkov

2023-01-13 19:38

reporter  

MainWindow.patch.zip (1,321 bytes)
sfernandez

sfernandez

2023-01-16 19:11

manager   ~0008212

Last edited: 2023-01-16 19:12

Could you try the following patch in TSF.cpp located in Src/Packages/App/Win32Display:

Index: TSF.cpp
===================================================================
--- TSF.cpp	(revision 11921)
+++ TSF.cpp	(working copy)
@@ -650,6 +650,12 @@
         NS_ASSERT(mTextBox != 0);
         mTextBox->UpdateLayout();
 
+        // UpdateLayout can release the focus from the TextBox, in that case we just return
+        if (mTextBox == 0)
+        {
+            return E_INVALIDARG;
+        }
+
         // Get bounding box of the selected text
         Rect bounds = mTextBox->GetRangeBounds(acpStart, acpEnd);
 

jack.barkov

jack.barkov

2023-01-16 22:31

reporter   ~0008214

Last edited: 2023-01-16 22:38

I can't explain exactly what happened, however, in Sample.Login.exe the error stopped happening.
But in my application even with the patch you sent me, the crash continues.

My application uses several views, and this view that the crash happens is precisely the view that has the TextBox.

Sometimes the crash doesn't happen, but the process goes into an infinite loop inside noesis when pause is always in the same stacktrace.


     Noesis.dll!Noesis::GUI::SetSoftwareKeyboardCallback() + 1883 bytes Unknown
     [Frames below may be incorrect and/or missing, no symbols loaded for Noesis.dll]
     Noesis.dll!Noesis::UIElement::RaiseEvent() + 30 bytes Unknown
     Noesis.dll!Noesis::Viewbox::StaticGetClassType() + 1867 bytes Unknown
     Noesis.dll!Noesis::Viewbox::StaticGetClassType() + 3086 bytes Unknown
     Noesis.dll!Noesis::Viewbox::StaticGetClassType() + 2669 bytes Unknown
     Noesis.dll!Noesis::Visual::GetContentBoundsCore() + 5163 bytes Unknown
 
I couldn't quite understand how this patch you sent me would work.

How could `mTextBox->UpdateLayout();` invalidate the `mTextBox` pointer by setting nullptr right after it was called? Doesn't seem to make much sense...

I'm trying to figure out how to best reproduce the problem, but I'm having trouble.

Would it be possible to make available a Noesis.dll in debug or the pdb of Noesis.dll release build 3.1.6.11756 x86 as jsantos sent me once so we can see the stacktrace in more detail?

Thanks
jack.barkov

jack.barkov

2023-01-16 22:47

reporter   ~0008215

Trying to reproduce the problem, a situation happened that the crash happens again on the same line.
I know this crash is due to the TextBox being without any style, however, the crash happens again on the same line. This is not the case with my application, as I am using the default Noesis theme.

To reproduce, in the examples use Sample.HelloWorld.exe, replace the MainWindow.xaml content with:

<Window x:Class="HelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HelloWorld"
        mc:Ignorable="d"
        FontFamily="./#Aero Matics"
        FontSize="36"
        Background="#FF124C7A"
        Title="NoesisGUI - Hello, World!">

    <Grid>
        <Viewbox>
            <Grid Width="325.8" Margin="24">
                <TextBox x:Name="ChatInput" Visibility="Visible" />
            </Grid>
        </Viewbox>
    </Grid>
</Window>


When opening the application click on the pink field and the crash will happen exactly on the same line again.

Could it be that the fact that the field has Visibility="Collapsed" when I press VK_RETURN in my application could be causing the TSF to have a problem with regard to style?

Thanks
helloworld-1.png (311,737 bytes)
jack.barkov

jack.barkov

2023-01-17 00:10

reporter   ~0008216

I tried to reproduce the problem using a very simple implementation with Sample.IntegrationGLUT.exe.
After many attempts, I managed to have the exact same crash that I have in my application.

The crash sometimes happens quickly, sometimes it takes time to happen.
I really have no idea what's going on...

Below is a file on google drive with everything compiled and prepared in debug.

I moved the TSF implementation into the integration glut, I made the TSF calls manually with the patch you sent me.

Inside the file below, you will find a dump that I took in debug in my VS along with the exe, and pdb of the moment of execution.
I hope I can help with something.

https://drive.google.com/file/d/1pVraAG-BTwhO6DKPVh5kw9vwDPCxgBhf/view?usp=sharing

I also attached a print of the stacktrace at the time of the error (which is the same as the attached dump /bin/Samples.IntegrationGLUT.dmp)

Thanks,
glut-1.png (257,064 bytes)   
glut-1.png (257,064 bytes)   
jack.barkov

jack.barkov

2023-01-17 00:33

reporter   ~0008217

From what I could understand, in the example above it happens when you start giving dozens of clicks on the text fields until suddenly the crash happens. I only left a test block in the video as you can see, but anyway I will send here the cpp I used in this video.

I made a video showing the crash happening (sorry for the fact that it's on a cell phone, but my resolution is very high, and this was the best way I imagined for the video to be readable)

Link:
https://youtu.be/hDG296REfV0

Thanks,
Main.cpp (21,572 bytes)   
////////////////////////////////////////////////////////////////////////////////////////////////////
// NoesisGUI - http://www.noesisengine.com
// Copyright (c) 2013 Noesis Technologies S.L. All Rights Reserved.
////////////////////////////////////////////////////////////////////////////////////////////////////


// This is a minimal integration example. For simplification purposes only basic input events are
// handled, no resource providers are used and the shutdown procedure is omitted. A more complete
// multiplatform integration sample with step by step comments can be found at 'Samples/Integration'


#ifdef _WIN32
#include "glut.h"
#pragma comment(linker,"/SUBSYSTEM:CONSOLE")
#endif

#ifdef __APPLE__
#include <GLUT/glut.h>
#endif

#ifdef __EMSCRIPTEN__
#include <GL/glut.h>
#include <GLES3/gl32.h>
#include <emscripten/html5.h>
#endif

#ifdef __linux__
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glut.h>
#endif

#ifdef _MSC_VER
#define UNUSED_ARGS(...) (void)(true ? (void)0 : ((void)(__VA_ARGS__)))
#else
#define UNUSED_ARGS(...)
#endif


#include <NsApp/ThemeProviders.h>
#include <NsRender/GLFactory.h>
#include <NsGui/FontProperties.h>
#include <NsGui/IntegrationAPI.h>
#include <NsGui/IRenderer.h>
#include <NsGui/IView.h>
#include <NsGui/Grid.h>
#include <NsCore/HighResTimer.h>
#include <NSGui/TextBox.h>

#include <NoesisPCH.h>

#include "TSF.h"

#include <string>
#include <list>

Noesis::Key ConvertCharKeyToNoesisKey(unsigned char key)
{
    if (key == 127) //GLUT treats delete key as char key, this is fixed keycode
    {
        return Noesis::Key::Key_Delete;
    }

    short scanKey = VkKeyScanA(key);

    Noesis::Key noesisKey = Noesis::Key_None;
    switch (scanKey)
    {
    case VK_BACK: noesisKey = Noesis::Key::Key_Back; break;
    case VK_TAB: noesisKey = Noesis::Key::Key_Tab; break;
    case VK_CLEAR: noesisKey = Noesis::Key::Key_Clear; break;
    case VK_RETURN: noesisKey = Noesis::Key::Key_Return; break;
    case VK_PAUSE: noesisKey = Noesis::Key::Key_Pause; break;
    case VK_SHIFT: noesisKey = Noesis::Key::Key_LeftShift; break;
    case VK_LSHIFT: noesisKey = Noesis::Key::Key_LeftShift; break;
    case VK_RSHIFT: noesisKey = Noesis::Key::Key_RightShift; break;
    case VK_CONTROL: noesisKey = Noesis::Key::Key_LeftCtrl; break;
    case VK_LCONTROL: noesisKey = Noesis::Key::Key_LeftCtrl; break;
    case VK_RCONTROL: noesisKey = Noesis::Key::Key_RightCtrl; break;
    case VK_MENU: noesisKey = Noesis::Key::Key_LeftAlt; break;
    case VK_LMENU: noesisKey = Noesis::Key::Key_LeftAlt; break;
    case VK_RMENU: noesisKey = Noesis::Key::Key_RightAlt; break;
    case VK_LWIN: noesisKey = Noesis::Key::Key_LWin; break;
    case VK_RWIN: noesisKey = Noesis::Key::Key_RWin; break;
    case VK_ESCAPE: noesisKey = Noesis::Key::Key_Escape; break;
    case VK_SPACE: noesisKey = Noesis::Key::Key_Space; break;
    case VK_PRIOR: noesisKey = Noesis::Key::Key_Prior; break;
    case VK_NEXT: noesisKey = Noesis::Key::Key_Next; break;
    case VK_END: noesisKey = Noesis::Key::Key_End; break;
    case VK_HOME: noesisKey = Noesis::Key::Key_Home; break;
    case VK_LEFT: noesisKey = Noesis::Key::Key_Left; break;
    case VK_UP: noesisKey = Noesis::Key::Key_Up; break;
    case VK_RIGHT: noesisKey = Noesis::Key::Key_Right; break;
    case VK_DOWN: noesisKey = Noesis::Key::Key_Down; break;
    case VK_SELECT: noesisKey = Noesis::Key::Key_Select; break;
    case VK_PRINT: noesisKey = Noesis::Key::Key_Print; break;
    case VK_EXECUTE: noesisKey = Noesis::Key::Key_Execute; break;
    case VK_SNAPSHOT: noesisKey = Noesis::Key::Key_Snapshot; break;
    case VK_INSERT: noesisKey = Noesis::Key::Key_Insert; break;
    case VK_DELETE: noesisKey = Noesis::Key::Key_Delete; break;
    case VK_HELP: noesisKey = Noesis::Key::Key_Help; break;
    case '0': noesisKey = Noesis::Key::Key_D0; break;
    case '1': noesisKey = Noesis::Key::Key_D1; break;
    case '2': noesisKey = Noesis::Key::Key_D2; break;
    case '3': noesisKey = Noesis::Key::Key_D3; break;
    case '4': noesisKey = Noesis::Key::Key_D4; break;
    case '5': noesisKey = Noesis::Key::Key_D5; break;
    case '6': noesisKey = Noesis::Key::Key_D6; break;
    case '7': noesisKey = Noesis::Key::Key_D7; break;
    case '8': noesisKey = Noesis::Key::Key_D8; break;
    case '9': noesisKey = Noesis::Key::Key_D9; break;
    case VK_NUMPAD0: noesisKey = Noesis::Key::Key_NumPad0; break;
    case VK_NUMPAD1: noesisKey = Noesis::Key::Key_NumPad1; break;
    case VK_NUMPAD2: noesisKey = Noesis::Key::Key_NumPad2; break;
    case VK_NUMPAD3: noesisKey = Noesis::Key::Key_NumPad3; break;
    case VK_NUMPAD4: noesisKey = Noesis::Key::Key_NumPad4; break;
    case VK_NUMPAD5: noesisKey = Noesis::Key::Key_NumPad5; break;
    case VK_NUMPAD6: noesisKey = Noesis::Key::Key_NumPad6; break;
    case VK_NUMPAD7: noesisKey = Noesis::Key::Key_NumPad7; break;
    case VK_NUMPAD8: noesisKey = Noesis::Key::Key_NumPad8; break;
    case VK_NUMPAD9: noesisKey = Noesis::Key::Key_NumPad9; break;
    case VK_MULTIPLY: noesisKey = Noesis::Key::Key_Multiply; break;
    case VK_ADD: noesisKey = Noesis::Key::Key_Add; break;
    case VK_SEPARATOR: noesisKey = Noesis::Key::Key_Separator; break;
    case VK_SUBTRACT: noesisKey = Noesis::Key::Key_Subtract; break;
    case VK_DECIMAL: noesisKey = Noesis::Key::Key_Decimal; break;
    case VK_DIVIDE: noesisKey = Noesis::Key::Key_Divide; break;
    case 'A': noesisKey = Noesis::Key::Key_A; break;
    case 'B': noesisKey = Noesis::Key::Key_B; break;
    case 'C': noesisKey = Noesis::Key::Key_C; break;
    case 'D': noesisKey = Noesis::Key::Key_D; break;
    case 'E': noesisKey = Noesis::Key::Key_E; break;
    case 'F': noesisKey = Noesis::Key::Key_F; break;
    case 'G': noesisKey = Noesis::Key::Key_G; break;
    case 'H': noesisKey = Noesis::Key::Key_H; break;
    case 'I': noesisKey = Noesis::Key::Key_I; break;
    case 'J': noesisKey = Noesis::Key::Key_J; break;
    case 'K': noesisKey = Noesis::Key::Key_K; break;
    case 'L': noesisKey = Noesis::Key::Key_L; break;
    case 'M': noesisKey = Noesis::Key::Key_M; break;
    case 'N': noesisKey = Noesis::Key::Key_N; break;
    case 'O': noesisKey = Noesis::Key::Key_O; break;
    case 'P': noesisKey = Noesis::Key::Key_P; break;
    case 'Q': noesisKey = Noesis::Key::Key_Q; break;
    case 'R': noesisKey = Noesis::Key::Key_R; break;
    case 'S': noesisKey = Noesis::Key::Key_S; break;
    case 'T': noesisKey = Noesis::Key::Key_T; break;
    case 'U': noesisKey = Noesis::Key::Key_U; break;
    case 'V': noesisKey = Noesis::Key::Key_V; break;
    case 'W': noesisKey = Noesis::Key::Key_W; break;
    case 'X': noesisKey = Noesis::Key::Key_X; break;
    case 'Y': noesisKey = Noesis::Key::Key_Y; break;
    case 'Z': noesisKey = Noesis::Key::Key_Z; break;
    case VK_F1: noesisKey = Noesis::Key::Key_F1; break;
    case VK_F2: noesisKey = Noesis::Key::Key_F2; break;
    case VK_F3: noesisKey = Noesis::Key::Key_F3; break;
    case VK_F4: noesisKey = Noesis::Key::Key_F4; break;
    case VK_F5: noesisKey = Noesis::Key::Key_F5; break;
    case VK_F6: noesisKey = Noesis::Key::Key_F6; break;
    case VK_F7: noesisKey = Noesis::Key::Key_F7; break;
    case VK_F8: noesisKey = Noesis::Key::Key_F8; break;
    case VK_F9: noesisKey = Noesis::Key::Key_F9; break;
    case VK_F10: noesisKey = Noesis::Key::Key_F10; break;
    case VK_F11: noesisKey = Noesis::Key::Key_F11; break;
    case VK_F12: noesisKey = Noesis::Key::Key_F12; break;
    case VK_F13: noesisKey = Noesis::Key::Key_F13; break;
    case VK_F14: noesisKey = Noesis::Key::Key_F14; break;
    case VK_F15: noesisKey = Noesis::Key::Key_F15; break;
    case VK_F16: noesisKey = Noesis::Key::Key_F16; break;
    case VK_F17: noesisKey = Noesis::Key::Key_F17; break;
    case VK_F18: noesisKey = Noesis::Key::Key_F18; break;
    case VK_F19: noesisKey = Noesis::Key::Key_F19; break;
    case VK_F20: noesisKey = Noesis::Key::Key_F20; break;
    case VK_F21: noesisKey = Noesis::Key::Key_F21; break;
    case VK_F22: noesisKey = Noesis::Key::Key_F22; break;
    case VK_F23: noesisKey = Noesis::Key::Key_F23; break;
    case VK_F24: noesisKey = Noesis::Key::Key_F24; break;
    case VK_NUMLOCK: noesisKey = Noesis::Key::Key_NumLock; break;
    case VK_SCROLL: noesisKey = Noesis::Key::Key_Scroll;
    }
    return noesisKey;
}

static Noesis::Ptr<Noesis::RenderDevice> _device;
static std::list<Noesis::IView*> _views;


///////////////////////////////////////////////////////////////////////////////////////////////////
void NoesisInit()
{
    // A logging handler is installed here. You can also install a custom error handler and memory
    // allocator. By default errors are redirected to the logging handler
    Noesis::GUI::SetLogHandler([](const char*, uint32_t, uint32_t level, const char*, const char* msg)
        {
            // [TRACE] [DEBUG] [INFO] [WARNING] [ERROR]
            const char* prefixes[] = { "T", "D", "I", "W", "E" };
    printf("[NOESIS/%s] %s\n", prefixes[level], msg);
        });

    // https://www.noesisengine.com/docs/Gui.Core.Licensing.html
    Noesis::GUI::SetLicense("test", "WOLw8pOp8PLHfabI2XuOBX26rFYjT94cw/+zrr0PBMP7Ve45");

    // Noesis initialization. This must be the first step before using any NoesisGUI functionality
    Noesis::GUI::Init();

    _device = NoesisApp::GLFactory::CreateDevice(false);

    // Setup theme
    NoesisApp::SetThemeProviders();
    Noesis::GUI::LoadApplicationResources("Theme/NoesisTheme.DarkBlue.xaml");
}

static void CreateBackground()
{
    // For simplicity purposes we are not using resource providers in this sample. ParseXaml() is
    // enough if there is no extra XAML dependencies
    Noesis::Ptr<Noesis::Grid> xaml(Noesis::GUI::ParseXaml<Noesis::Grid>(R"(<?xml version="1.0" encoding="UTF-8"?>
<Grid 
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
	xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
	xmlns:noesis="clr-namespace:NoesisGUIExtensions;assembly=Noesis.GUI.Extensions">

    <Grid Grid.Column="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="30"/>
            <ColumnDefinition Width="54"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="11"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <Button Grid.Column="1" Grid.Row="1" Content="Test" />
    </Grid>
</Grid>
    )"));

    // View creation to render and interact with the user interface
    // We transfer the ownership to a global pointer instead of a Ptr<> because there is no way
    // in GLUT to do shutdown and we don't want the Ptr<> to be released at global time
    Noesis::IView* view = Noesis::GUI::CreateView(xaml).GiveOwnership();
    view->SetFlags(Noesis::RenderFlags_PPAA | Noesis::RenderFlags_LCD);
    view->SetSize(glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));

    // Renderer initialization with an OpenGL device
    view->GetRenderer()->Init(_device);

    _views.push_back(view);
}

static void CreateChatFrame()
{
    // For simplicity purposes we are not using resource providers in this sample. ParseXaml() is
    // enough if there is no extra XAML dependencies
    Noesis::Ptr<Noesis::Grid> xaml(Noesis::GUI::ParseXaml<Noesis::Grid>(R"(<?xml version="1.0" encoding="UTF-8"?>
<Grid 
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
	xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
	xmlns:noesis="clr-namespace:NoesisGUIExtensions;assembly=Noesis.GUI.Extensions">

    <Grid Grid.Column="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        
        <TextBox Grid.Row="1" x:Name="ChatInput" Text="WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW" />
        <TextBox Grid.Row="2" x:Name="ChatSecond" Text="123123" />

    </Grid>
</Grid>
    )"));

    // View creation to render and interact with the user interface
    // We transfer the ownership to a global pointer instead of a Ptr<> because there is no way
    // in GLUT to do shutdown and we don't want the Ptr<> to be released at global time
    Noesis::IView* view = Noesis::GUI::CreateView(xaml).GiveOwnership();
    view->SetFlags(Noesis::RenderFlags_PPAA | Noesis::RenderFlags_LCD);
    view->SetSize(glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));

    // Renderer initialization with an OpenGL device
    view->GetRenderer()->Init(_device);

    _views.push_back(view);
}

static void CreateOtherView()
{
    // For simplicity purposes we are not using resource providers in this sample. ParseXaml() is
    // enough if there is no extra XAML dependencies
    Noesis::Ptr<Noesis::Grid> xaml(Noesis::GUI::ParseXaml<Noesis::Grid>(R"(<?xml version="1.0" encoding="UTF-8"?>
<Grid 
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
	xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
	xmlns:noesis="clr-namespace:NoesisGUIExtensions;assembly=Noesis.GUI.Extensions">

    <Grid Grid.Column="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        
        <TextBox Grid.Row="3" x:Name="OtherText" Text="8888888" />

    </Grid>
</Grid>
    )"));

    // View creation to render and interact with the user interface
    // We transfer the ownership to a global pointer instead of a Ptr<> because there is no way
    // in GLUT to do shutdown and we don't want the Ptr<> to be released at global time
    Noesis::IView* view = Noesis::GUI::CreateView(xaml).GiveOwnership();
    view->SetFlags(Noesis::RenderFlags_PPAA | Noesis::RenderFlags_LCD);
    view->SetSize(glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));

    // Renderer initialization with an OpenGL device
    view->GetRenderer()->Init(_device);

    _views.push_back(view);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
static void DisplayFunc(void)
{
    glViewport(0, 0, glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));
    glDisable(GL_SCISSOR_TEST);
    glClearColor(0.0f, 0.0f, 0.25f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    static unsigned long long loop{ 0 };
    loop++;

    for (auto& view : _views)
    {
        // Update view (layout, animations, ...)
        view->Update(glutGet(GLUT_ELAPSED_TIME) / 1000.0);

        // Offscreen rendering phase populates textures needed by the on-screen rendering
        view->GetRenderer()->UpdateRenderTree();
        view->GetRenderer()->RenderOffscreen();
    }

    for (auto& view : _views)
    {
        const std::string test_str = "test string";
        for (auto c : test_str)
        {
            view->Char(c);
        }
        view->Char(VK_RETURN);

        if (loop % 20 == 0)
        {
            Noesis::TextBox* tb = view->GetContent()->FindName<Noesis::TextBox>("ChatInput");
            if (tb != nullptr)
            {
                if (tb->GetIsVisible())
                {
                    tb->SetText("");
                    tb->SetVisibility(Noesis::Visibility_Collapsed);
                }
                else
                {
                    tb->SetVisibility(Noesis::Visibility_Visible);
                    tb->SetText("test-username-value");
                    tb->Focus();
                }
            }
        }

        /*if (loop % 30 == 0)
        {
            Noesis::TextBox* tb = view->GetContent()->FindName<Noesis::TextBox>("ChatSecond");
            if (tb != nullptr)
            {
                if (tb->GetIsVisible())
                {
                    tb->SetText("");
                    tb->SetVisibility(Noesis::Visibility_Collapsed);
                }
                else
                {
                    tb->SetVisibility(Noesis::Visibility_Visible);
                    tb->SetText("test-username-value");
                    tb->Focus();
                }
            }
        }

        if (loop % 40 == 0)
        {
            Noesis::TextBox* tb = view->GetContent()->FindName<Noesis::TextBox>("OtherText");
            if (tb != nullptr)
            {
                if (tb->GetIsVisible())
                {
                    tb->SetText("");
                    tb->SetVisibility(Noesis::Visibility_Collapsed);
                }
                else
                {
                    tb->SetVisibility(Noesis::Visibility_Visible);
                    tb->SetText("test-username-value");
                    tb->Focus();
                }
            }
        }*/
    }
    // My custom render, goes here...
    // My custom render, goes here...
    // My custom render, goes here...
    // My custom render, goes here...

    for (auto& view : _views)
    {
        // If you are going to render here with your own engine you need to restore the GPU state
        // because noesis changes it. In this case only framebuffer and viewport need to be restored
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
        glClearStencil(0);

        // Rendering is done in the active framebuffer
        view->GetRenderer()->Render();
    }

    glutSwapBuffers();
    glutPostRedisplay();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
static void ReshapeFunc(int width, int height)
{
    for (auto& view : _views)
    {
        view->SetSize(width, height);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
static void MouseMoveFunc(int x, int y)
{
    for (auto& view : _views)
    {
        view->MouseMove(x, y);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
static void MouseFunc(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON)
    {
        if (state == GLUT_DOWN)
        {
            for (auto& view : _views)
            {
                view->MouseButtonDown(x, y, Noesis::MouseButton_Left);
            }
        }
        else
        {
            for (auto& view : _views)
            {
                view->MouseButtonUp(x, y, Noesis::MouseButton_Left);
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL);
    glutInitWindowSize(1000, 600);

#ifdef __EMSCRIPTEN__
    double width, height;
    emscripten_get_element_css_size("#canvas", &width, &height);
    emscripten_set_canvas_element_size("#canvas", (uint32_t)width, (uint32_t)height);
    glutInitWindowSize((uint32_t)width, (uint32_t)height);
#endif

    glutCreateWindow("NoesisGUI - GLUT integration");
    NoesisInit();

    NoesisApp::TSF::Init();
    NoesisApp::TSF::ActivateWindow(GLUT::hWnd);

    Noesis::GUI::SetSoftwareKeyboardCallback(nullptr, [](void* _user, Noesis::UIElement* _focused, bool _open) {
        if (_open)
        {
            NoesisApp::TSF::ShowKeyboard(_focused);
        }
        else
        {
            NoesisApp::TSF::HideKeyboard();
        }
    });

    CreateBackground();
    CreateChatFrame();
    CreateOtherView();

    glutDisplayFunc(&DisplayFunc);
    glutReshapeFunc(&ReshapeFunc);
    glutPassiveMotionFunc(&MouseMoveFunc);
    glutMouseFunc(&MouseFunc);	

    glutMainLoop();

    return 0;
}
Main.cpp (21,572 bytes)   
sfernandez

sfernandez

2023-01-17 17:36

manager   ~0008218

Thanks a lot for your examples and feedback. I was able to find a bug in the processing of the LayoutUpdated event.
Please try the following patched library and let me know if your problems are solved: https://drive.google.com/file/d/1qouAu1fUyqM--el4kdFA6MTHjB0to5S2/view?usp=share_link
jack.barkov

jack.barkov

2023-01-17 21:45

reporter   ~0008220

I did the test with the example I prepared yesterday and it really seems to have solved it.

My application is x86 only, could you provide me with an x86 build to test better?

Thanks,
sfernandez

sfernandez

2023-01-18 19:49

manager   ~0008223

Of course, here you are: https://drive.google.com/file/d/1MF5r5eCZfFZ2R6qOX4yt2EMbKtd2SgZq/view?usp=share_link
jack.barkov

jack.barkov

2023-01-18 22:21

reporter   ~0008224

I did some tests here and it seems to have really solved it! No bugs happened again.

Is it safe to use this dll until the next version 3.1.7 comes out?

Thanks!
sfernandez

sfernandez

2023-01-19 10:09

manager   ~0008225

Yes, you can use this patched library in the meantime.

Issue History

Date Modified Username Field Change
2023-01-13 19:38 jack.barkov New Issue
2023-01-13 19:38 jack.barkov Tag Attached: C++
2023-01-13 19:38 jack.barkov File Added: MainWindow.patch.zip
2023-01-16 15:48 jsantos Assigned To => sfernandez
2023-01-16 15:48 jsantos Status new => assigned
2023-01-16 15:48 jsantos Target Version => 3.1.7
2023-01-16 19:11 sfernandez Status assigned => feedback
2023-01-16 19:11 sfernandez Note Added: 0008212
2023-01-16 19:12 sfernandez Note Edited: 0008212
2023-01-16 22:31 jack.barkov Note Added: 0008214
2023-01-16 22:31 jack.barkov Status feedback => assigned
2023-01-16 22:38 jack.barkov Note Edited: 0008214
2023-01-16 22:47 jack.barkov Note Added: 0008215
2023-01-16 22:47 jack.barkov File Added: helloworld-1.png
2023-01-17 00:10 jack.barkov Note Added: 0008216
2023-01-17 00:10 jack.barkov File Added: glut-1.png
2023-01-17 00:33 jack.barkov Note Added: 0008217
2023-01-17 00:33 jack.barkov File Added: Main.cpp
2023-01-17 17:36 sfernandez Status assigned => feedback
2023-01-17 17:36 sfernandez Note Added: 0008218
2023-01-17 21:45 jack.barkov Note Added: 0008220
2023-01-17 21:45 jack.barkov Status feedback => assigned
2023-01-18 19:49 sfernandez Status assigned => feedback
2023-01-18 19:49 sfernandez Note Added: 0008223
2023-01-18 22:21 jack.barkov Note Added: 0008224
2023-01-18 22:21 jack.barkov Status feedback => assigned
2023-01-19 10:09 sfernandez Note Added: 0008225
2023-01-19 10:10 sfernandez Status assigned => resolved
2023-01-19 10:10 sfernandez Resolution open => fixed
2023-01-19 10:10 sfernandez Fixed in Version => 3.1.7