From 4d9c231bc08cfd78840c46b95239d408703d8ee4 Mon Sep 17 00:00:00 2001 From: "Eunki, Hong" Date: Wed, 18 Dec 2024 13:59:07 +0900 Subject: [PATCH] [NUI] Support RemoveIdle API for NUIApplication + Use unified idler callback Let we keep Idler callback list as membery of internal Application class, and allow to remove them during idler callback execute. Signed-off-by: Eunki, Hong --- .../src/internal/Application/Application.cs | 360 +++++++++++------- .../internal/Application/NUICoreBackend.cs | 12 +- .../src/public/Application/NUIApplication.cs | 12 +- .../Samples/FlushApplicationMessageSample.cs | 77 +++- 4 files changed, 327 insertions(+), 134 deletions(-) diff --git a/src/Tizen.NUI/src/internal/Application/Application.cs b/src/Tizen.NUI/src/internal/Application/Application.cs index 4d775bc9579..83545e7a8b1 100755 --- a/src/Tizen.NUI/src/internal/Application/Application.cs +++ b/src/Tizen.NUI/src/internal/Application/Application.cs @@ -1,5 +1,5 @@ /* - * Copyright(c) 2022 Samsung Electronics Co., Ltd. + * Copyright(c) 2024 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -363,6 +363,47 @@ internal override void OnParentResourcesChanged(IEnumerable idleCallbackMap = new Dictionary(); + + private void RootIdleCallback() + { + if (idleCallbackMap == null || IsDisposedOrQueued) + { + Tizen.Log.Error("NUI", $"[Error] Application disposed! Fail to execute idle callback!\n"); + return; + } + + Tizen.Log.Debug("NUI", $"Application RootIdleCallback comes\n"); + // Reset root idle callback as null now, since we could call AddIdle during delegate function invoke + rootIdleCallback = null; + + // Copy key list of idle callback map + // (Since idle callback could change the dictionary itself during iteration, we need to make a copy of keys first) + List delegateList = new List(); + foreach (var func in idleCallbackMap.Keys) + { + delegateList.Add(func); + } + + foreach (var func in delegateList) + { + // Remove delegate at map first, and then invoke it. + bool isValid = false; + if (idleCallbackMap?.Remove(func, out isValid) ?? false) + { + if (isValid) + { + func.DynamicInvoke(); + } + } + } + Tizen.Log.Debug("NUI", $"Application RootIdleCallback finished\n"); + } + internal Application(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn) { SetCurrentApplication(this); @@ -376,145 +417,150 @@ protected override void Dispose(DisposeTypes type) return; } - //Release your own unmanaged resources here. - //You should not access any managed member here except static instance. - //because the execution order of Finalizes is non-deterministic. - if (applicationInitEventCallbackDelegate != null) + if (type == DisposeTypes.Explicit) { - initSignal?.Disconnect(applicationInitEventCallbackDelegate); - initSignal?.Dispose(); - initSignal = null; - } + //Release your own unmanaged resources here. + //You should not access any managed member here except static instance. + //because the execution order of Finalizes is non-deterministic. + if (applicationInitEventCallbackDelegate != null) + { + initSignal?.Disconnect(applicationInitEventCallbackDelegate); + initSignal?.Dispose(); + initSignal = null; + } - if (applicationTerminateEventCallbackDelegate != null) - { - terminateSignal?.Disconnect(applicationTerminateEventCallbackDelegate); - terminateSignal?.Dispose(); - terminateSignal = null; - } + if (applicationTerminateEventCallbackDelegate != null) + { + terminateSignal?.Disconnect(applicationTerminateEventCallbackDelegate); + terminateSignal?.Dispose(); + terminateSignal = null; + } - if (applicationPauseEventCallbackDelegate != null) - { - pauseSignal?.Disconnect(applicationPauseEventCallbackDelegate); - pauseSignal?.Dispose(); - pauseSignal = null; - } + if (applicationPauseEventCallbackDelegate != null) + { + pauseSignal?.Disconnect(applicationPauseEventCallbackDelegate); + pauseSignal?.Dispose(); + pauseSignal = null; + } - if (applicationResumeEventCallbackDelegate != null) - { - resumeSignal?.Disconnect(applicationResumeEventCallbackDelegate); - resumeSignal?.Dispose(); - resumeSignal = null; - } + if (applicationResumeEventCallbackDelegate != null) + { + resumeSignal?.Disconnect(applicationResumeEventCallbackDelegate); + resumeSignal?.Dispose(); + resumeSignal = null; + } - if (applicationResetEventCallbackDelegate != null) - { - resetSignal?.Disconnect(applicationResetEventCallbackDelegate); - resetSignal?.Dispose(); - resetSignal = null; - } + if (applicationResetEventCallbackDelegate != null) + { + resetSignal?.Disconnect(applicationResetEventCallbackDelegate); + resetSignal?.Dispose(); + resetSignal = null; + } - if (applicationLanguageChangedEventCallbackDelegate != null) - { - languageChangedSignal?.Disconnect(applicationLanguageChangedEventCallbackDelegate); - languageChangedSignal?.Dispose(); - languageChangedSignal = null; - } + if (applicationLanguageChangedEventCallbackDelegate != null) + { + languageChangedSignal?.Disconnect(applicationLanguageChangedEventCallbackDelegate); + languageChangedSignal?.Dispose(); + languageChangedSignal = null; + } - if (applicationRegionChangedEventCallbackDelegate != null) - { - regionChangedSignal?.Disconnect(applicationRegionChangedEventCallbackDelegate); - regionChangedSignal?.Dispose(); - regionChangedSignal = null; - } + if (applicationRegionChangedEventCallbackDelegate != null) + { + regionChangedSignal?.Disconnect(applicationRegionChangedEventCallbackDelegate); + regionChangedSignal?.Dispose(); + regionChangedSignal = null; + } - if (applicationBatteryLowEventCallbackDelegate != null) - { - batteryLowSignal?.Disconnect(applicationBatteryLowEventCallbackDelegate); - batteryLowSignal?.Dispose(); - batteryLowSignal = null; - } + if (applicationBatteryLowEventCallbackDelegate != null) + { + batteryLowSignal?.Disconnect(applicationBatteryLowEventCallbackDelegate); + batteryLowSignal?.Dispose(); + batteryLowSignal = null; + } - if (applicationMemoryLowEventCallbackDelegate != null) - { - memoryLowSignal?.Disconnect(applicationMemoryLowEventCallbackDelegate); - memoryLowSignal?.Dispose(); - memoryLowSignal = null; - } + if (applicationMemoryLowEventCallbackDelegate != null) + { + memoryLowSignal?.Disconnect(applicationMemoryLowEventCallbackDelegate); + memoryLowSignal?.Dispose(); + memoryLowSignal = null; + } - if (applicationDeviceOrientationChangedEventCallback != null) - { - deviceOrientationChangedSignal?.Disconnect(applicationDeviceOrientationChangedEventCallback); - deviceOrientationChangedSignal?.Dispose(); - deviceOrientationChangedSignal = null; - } + if (applicationDeviceOrientationChangedEventCallback != null) + { + deviceOrientationChangedSignal?.Disconnect(applicationDeviceOrientationChangedEventCallback); + deviceOrientationChangedSignal?.Dispose(); + deviceOrientationChangedSignal = null; + } - if (applicationAppControlEventCallbackDelegate != null) - { - appControlSignal?.Disconnect(applicationAppControlEventCallbackDelegate); - appControlSignal?.Dispose(); - appControlSignal = null; - } + if (applicationAppControlEventCallbackDelegate != null) + { + appControlSignal?.Disconnect(applicationAppControlEventCallbackDelegate); + appControlSignal?.Dispose(); + appControlSignal = null; + } - //Task - if (applicationTaskInitEventCallbackDelegate != null) - { - taskInitSignal?.Disconnect(applicationTaskInitEventCallbackDelegate); - taskInitSignal?.Dispose(); - taskInitSignal = null; - } + //Task + if (applicationTaskInitEventCallbackDelegate != null) + { + taskInitSignal?.Disconnect(applicationTaskInitEventCallbackDelegate); + taskInitSignal?.Dispose(); + taskInitSignal = null; + } - if (applicationTaskTerminateEventCallbackDelegate != null) - { - taskTerminateSignal?.Disconnect(applicationTaskTerminateEventCallbackDelegate); - taskTerminateSignal?.Dispose(); - taskTerminateSignal = null; - } + if (applicationTaskTerminateEventCallbackDelegate != null) + { + taskTerminateSignal?.Disconnect(applicationTaskTerminateEventCallbackDelegate); + taskTerminateSignal?.Dispose(); + taskTerminateSignal = null; + } - if (applicationTaskLanguageChangedEventCallbackDelegate != null) - { - taskLanguageChangedSignal?.Disconnect(applicationTaskLanguageChangedEventCallbackDelegate); - taskLanguageChangedSignal?.Dispose(); - taskLanguageChangedSignal = null; - } + if (applicationTaskLanguageChangedEventCallbackDelegate != null) + { + taskLanguageChangedSignal?.Disconnect(applicationTaskLanguageChangedEventCallbackDelegate); + taskLanguageChangedSignal?.Dispose(); + taskLanguageChangedSignal = null; + } - if (applicationTaskRegionChangedEventCallbackDelegate != null) - { - taskRegionChangedSignal?.Disconnect(applicationTaskRegionChangedEventCallbackDelegate); - taskRegionChangedSignal?.Dispose(); - taskRegionChangedSignal = null; - } + if (applicationTaskRegionChangedEventCallbackDelegate != null) + { + taskRegionChangedSignal?.Disconnect(applicationTaskRegionChangedEventCallbackDelegate); + taskRegionChangedSignal?.Dispose(); + taskRegionChangedSignal = null; + } - if (applicationTaskBatteryLowEventCallbackDelegate != null) - { - taskBatteryLowSignal?.Disconnect(applicationTaskBatteryLowEventCallbackDelegate); - taskBatteryLowSignal?.Dispose(); - taskBatteryLowSignal = null; - } + if (applicationTaskBatteryLowEventCallbackDelegate != null) + { + taskBatteryLowSignal?.Disconnect(applicationTaskBatteryLowEventCallbackDelegate); + taskBatteryLowSignal?.Dispose(); + taskBatteryLowSignal = null; + } - if (applicationTaskMemoryLowEventCallbackDelegate != null) - { - taskMemoryLowSignal?.Disconnect(applicationTaskMemoryLowEventCallbackDelegate); - taskMemoryLowSignal?.Dispose(); - taskMemoryLowSignal = null; - } + if (applicationTaskMemoryLowEventCallbackDelegate != null) + { + taskMemoryLowSignal?.Disconnect(applicationTaskMemoryLowEventCallbackDelegate); + taskMemoryLowSignal?.Dispose(); + taskMemoryLowSignal = null; + } - if (applicationTaskDeviceOrientationChangedEventCallback != null) - { - taskDeviceOrientationChangedSignal?.Disconnect(applicationTaskDeviceOrientationChangedEventCallback); - taskDeviceOrientationChangedSignal?.Dispose(); - taskDeviceOrientationChangedSignal = null; - } + if (applicationTaskDeviceOrientationChangedEventCallback != null) + { + taskDeviceOrientationChangedSignal?.Disconnect(applicationTaskDeviceOrientationChangedEventCallback); + taskDeviceOrientationChangedSignal?.Dispose(); + taskDeviceOrientationChangedSignal = null; + } - if (applicationTaskAppControlEventCallbackDelegate != null) - { - taskAppControlSignal?.Disconnect(applicationTaskAppControlEventCallbackDelegate); - taskAppControlSignal?.Dispose(); - taskAppControlSignal = null; + if (applicationTaskAppControlEventCallbackDelegate != null) + { + taskAppControlSignal?.Disconnect(applicationTaskAppControlEventCallbackDelegate); + taskAppControlSignal?.Dispose(); + taskAppControlSignal = null; + } + + window?.Dispose(); + window = null; } - window?.Dispose(); - window = null; + idleCallbackMap = null; base.Dispose(type); } @@ -1721,13 +1767,72 @@ public static Application NewApplication(string[] args, string stylesheet, bool /// public bool AddIdle(System.Delegate func) { - System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(func); - System.IntPtr ip2 = Interop.Application.MakeCallback(new System.Runtime.InteropServices.HandleRef(this, ip)); + bool idleAdded = false; // default value is false + + if (idleCallbackMap == null || IsDisposedOrQueued) + { + Tizen.Log.Error("NUI", $"[Error] Application disposed! failed to add idle {func}\n"); + return false; + } - bool ret = Interop.Application.AddIdle(SwigCPtr, new System.Runtime.InteropServices.HandleRef(this, ip2)); + // Register root idle callback for Tizen.NUI.Application + if (rootIdleCallback == null) + { + rootIdleCallback = RootIdleCallback; + System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(rootIdleCallback); + System.IntPtr ip2 = Interop.Application.MakeCallback(new System.Runtime.InteropServices.HandleRef(this, ip)); - if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); - return ret; + bool ret = Interop.Application.AddIdle(SwigCPtr, new System.Runtime.InteropServices.HandleRef(this, ip2)); + NDalicPINVOKE.ThrowExceptionIfExists(); + if (!ret) + { + rootIdleCallback = null; + Tizen.Log.Error("NUI", $"[Error] failed to add idle {func}\n"); + return false; + } + } + + if (idleCallbackMap.TryGetValue(func, out bool isValid)) + { + idleAdded = true; // success case + if (!isValid) + { + idleCallbackMap[func] = true; + } + } + else if (idleCallbackMap.TryAdd(func, true)) + { + idleAdded = true; // success case + } + + if (!idleAdded) + { + Tizen.Log.Error("NUI", $"[Error] failed to add idle {func}\n"); + } + + return idleAdded; + } + + /// + /// Remove delegate what we added by AddIdle. + /// + /// The function to remove + [EditorBrowsable(EditorBrowsableState.Never)] + public void RemoveIdle(System.Delegate func) + { + if (idleCallbackMap == null || IsDisposedOrQueued) + { + Tizen.Log.Error("NUI", $"[Error] Application disposed! failed to remove idle {func}\n"); + return; + } + + if (idleCallbackMap.TryGetValue(func, out bool isValid)) + { + if (isValid) + { + idleCallbackMap[func] = false; + } + } } /** @@ -1925,13 +2030,6 @@ public void Quit() if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); } - internal bool AddIdle(SWIGTYPE_p_Dali__CallbackBase callback) - { - bool ret = Interop.Application.AddIdle(SwigCPtr, SWIGTYPE_p_Dali__CallbackBase.getCPtr(callback)); - if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - public Window GetWindow() { if (window != null) diff --git a/src/Tizen.NUI/src/internal/Application/NUICoreBackend.cs b/src/Tizen.NUI/src/internal/Application/NUICoreBackend.cs index e736eba1259..bc79963932b 100755 --- a/src/Tizen.NUI/src/internal/Application/NUICoreBackend.cs +++ b/src/Tizen.NUI/src/internal/Application/NUICoreBackend.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2024 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -158,6 +158,16 @@ public bool AddIdle(System.Delegate func) return application.AddIdle(func); } + /// + /// Remove delegate what we added by AddIdle. + /// + /// The function to remove + [EditorBrowsable(EditorBrowsableState.Never)] + public void RemoveIdle(System.Delegate func) + { + application.RemoveIdle(func); + } + /// /// The Run application. /// diff --git a/src/Tizen.NUI/src/public/Application/NUIApplication.cs b/src/Tizen.NUI/src/public/Application/NUIApplication.cs index ac178056c5f..b63fab53542 100755 --- a/src/Tizen.NUI/src/public/Application/NUIApplication.cs +++ b/src/Tizen.NUI/src/public/Application/NUIApplication.cs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2024 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -547,6 +547,16 @@ public bool AddIdle(System.Delegate func) return ((NUICoreBackend)this.Backend).AddIdle(func); } + /// + /// Remove delegate what we added by AddIdle. + /// + /// The function to remove + [EditorBrowsable(EditorBrowsableState.Never)] + public void RemoveIdle(System.Delegate func) + { + ((NUICoreBackend)this.Backend).RemoveIdle(func); + } + /// /// Flush render/update thread messages synchronously. /// diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/FlushApplicationMessageSample.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/FlushApplicationMessageSample.cs index 3b084621e78..7b6479f1dc8 100644 --- a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/FlushApplicationMessageSample.cs +++ b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/FlushApplicationMessageSample.cs @@ -1,6 +1,7 @@ using Tizen.NUI; using Tizen.NUI.BaseComponents; using Tizen.NUI.Components; +using System; using System.Threading; namespace Tizen.NUI.Samples @@ -23,6 +24,21 @@ public Tizen.NUI.NUIApplication GetCurrentApplication() return application; } + private delegate void FuncDelegate(); + private FuncDelegate FuncDelegater = null; + + private void Func() + { + Tizen.Log.Error("NUITEST", "Idle callback comes!\n"); + FuncDelegater = null; + } + private void Func2() + { + Tizen.Log.Error("NUITEST", "Idle callback comes! Register idle once again!\n"); + FuncDelegater = Func; + GetCurrentApplication()?.AddIdle(FuncDelegater); + } + public void Activate() { Window window = NUIApplication.GetDefaultWindow(); @@ -55,14 +71,73 @@ public void Activate() Tizen.Log.Error("NUITEST", "Sleep done\n"); textLabel.Text = "Sleep done!\n"; + + window.KeyEvent += WinKeyEvent; } public void Deactivate() { if (root != null) { - NUIApplication.GetDefaultWindow().Remove(root); + Window window = NUIApplication.GetDefaultWindow(); + window.Remove(root); + window.KeyEvent -= WinKeyEvent; root.Dispose(); + + if (FuncDelegater != null) + { + GetCurrentApplication()?.RemoveIdle(FuncDelegater); + } + } + } + + private void WinKeyEvent(object sender, Window.KeyEventArgs e) + { + if (e.Key.State == Key.StateType.Down) + { + if (e.Key.KeyPressedName == "1") + { + Tizen.Log.Error("NUITEST", "Add idle callback\n"); + if (FuncDelegater == null) + { + FuncDelegater = Func; + } + + GetCurrentApplication()?.AddIdle(FuncDelegater); + } + else if (e.Key.KeyPressedName == "2") + { + Tizen.Log.Error("NUITEST", "Add idle callback, and remove immediately\n"); + if (FuncDelegater == null) + { + FuncDelegater = Func; + } + + GetCurrentApplication()?.AddIdle(FuncDelegater); + GetCurrentApplication()?.RemoveIdle(FuncDelegater); + FuncDelegater = null; + } + else if (e.Key.KeyPressedName == "3") + { + Tizen.Log.Error("NUITEST", "Add idle callback during idle callback execute\n"); + if (FuncDelegater == null) + { + FuncDelegater = Func2; + } + + GetCurrentApplication()?.AddIdle(FuncDelegater); + } + else if (e.Key.KeyPressedName == "4") + { + Tizen.Log.Error("NUITEST", "Self dispose after add idle callback\n"); + if (FuncDelegater == null) + { + FuncDelegater = Func; + } + + GetCurrentApplication()?.AddIdle(FuncDelegater); + GetCurrentApplication()?.Dispose(); + } } } }