Support UI Thread 64/272464/21
authorHwankyu Jhun <h.jhun@samsung.com>
Thu, 17 Mar 2022 06:46:56 +0000 (15:46 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Mon, 11 Apr 2022 04:21:22 +0000 (13:21 +0900)
This patch supports UI thread for ui-applications. There are two threads.
The main thread is for Service thread and the sub thread is for UI thread.
Service thread is recommended to contain the Model of MVC pattern. UI thread
is recommended to contain the View and Controller of MVC pattern.

Requires:
 - https://review.tizen.org/gerrit/#/c/platform/upstream/efl/+/270474/

Change-Id: I77b196dd37e6cd42c9c3b914bfe9ed89f3c61391
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
14 files changed:
packaging/app-core.spec
tizen-cpp/app-core-cpp/CMakeLists.txt
tizen-cpp/app-core-cpp/app_core_base.cc
tizen-cpp/app-core-cpp/interface_app_core_ui_event.hh [new file with mode: 0644]
tizen-cpp/app-core-efl-cpp/app_core_efl_base.cc
tizen-cpp/app-core-ui-cpp/api/app_core_ui_base.cc
tizen-cpp/app-core-ui-cpp/api/app_core_ui_base.h
tizen-cpp/app-core-ui-cpp/app_core_task_base.cc [new file with mode: 0644]
tizen-cpp/app-core-ui-cpp/app_core_task_base.hh [new file with mode: 0644]
tizen-cpp/app-core-ui-cpp/app_core_ui_base.cc
tizen-cpp/app-core-ui-cpp/app_core_ui_base.hh
tizen-cpp/common/ecore_handler.cc
tizen-cpp/common/glib_private.cc [new file with mode: 0644]
tizen-cpp/common/glib_private.hh [new file with mode: 0644]

index 7511a16..acef89c 100644 (file)
@@ -244,6 +244,7 @@ install -m 0644 app-core.zip %{buildroot}%{_datadir}/gcov/
 %{_includedir}/appcore_cpp/app_core_base.hh
 %{_includedir}/appcore_cpp/interface_app_core.hh
 %{_includedir}/appcore_cpp/interface_app_core_ui.hh
+%{_includedir}/appcore_cpp/interface_app_core_ui_event.hh
 %{_includedir}/appcore_cpp/interface_main_loop.hh
 %{_includedir}/appcore_cpp/interface_window.hh
 %{_libdir}/libapp-core-cpp.so
@@ -262,6 +263,7 @@ install -m 0644 app-core.zip %{buildroot}%{_datadir}/gcov/
 %{_libdir}/libappcore-ui.so
 %{_libdir}/pkgconfig/appcore-ui.pc
 
+%{_includedir}/appcore_cpp/app_core_task_base.hh
 %{_includedir}/appcore_cpp/app_core_ui_base.hh
 %{_includedir}/appcore_cpp/api/app_core_ui_base.h
 %{_libdir}/libapp-core-ui-cpp.so
index 9668ed4..08a41fb 100644 (file)
@@ -1,6 +1,9 @@
 AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_CORE_CPP_SRCS)
+SET(COMMON_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../common/glib_private.cc)
 
-ADD_LIBRARY(${TARGET_APP_CORE_CPP} SHARED ${APP_CORE_CPP_SRCS})
+ADD_LIBRARY(${TARGET_APP_CORE_CPP} SHARED
+  ${APP_CORE_CPP_SRCS}
+  ${COMMON_SRCS})
 
 TARGET_INCLUDE_DIRECTORIES(${TARGET_APP_CORE_CPP} PUBLIC
   ${CMAKE_CURRENT_SOURCE_DIR}
index 5adf51c..a2bb1b5 100644 (file)
@@ -43,6 +43,7 @@
 #include <sstream>
 
 #include "app-core-cpp/app_core_base.hh"
+#include "common/glib_private.hh"
 #include "common/log_private.hh"
 
 extern "C" void aul_finalize();
@@ -341,7 +342,7 @@ void AppCoreBase::Impl::ReceiveSuspendSignalCb(GDBusConnection*, const gchar*,
 void AppCoreBase::Impl::LanguageChangeCb(keynode_t* key, void* user_data) {
   AppCoreBase* base = reinterpret_cast<AppCoreBase*>(user_data);
   if (base->impl_->sid_) {
-    g_source_remove(base->impl_->sid_);
+    GLib::SourceRemove(base->impl_->sid_);
     base->impl_->sid_ = 0;
   }
 
@@ -518,7 +519,7 @@ void AppCoreBase::Impl::VerifyLanguage() {
 
   if (strcmp(env_lang, lang) != 0) {
     _I("LANG(%s), LANGSET(%s)", env_lang, lang);
-    sid_ = g_idle_add(InvokeLangChangeCb, parent_);
+    sid_ = GLib::IdleAdd(InvokeLangChangeCb, parent_);
   }
 }
 
@@ -580,10 +581,7 @@ int AppCoreBase::OnReceive(aul_type type, tizen_base::Bundle b) {
     if (!impl_->allowed_bg_)
       RemoveSuspendTimer();
 
-    if (impl_->loop_delegator_)
-      impl_->loop_delegator_->OnLoopExit();
-    else
-      OnLoopExit();
+    Exit();
     break;
   case AUL_TERMINATE_INST:
   case AUL_TERMINATE_BG_INST:
@@ -1103,7 +1101,7 @@ void AppCoreBase::Exit() {
 }
 
 void AppCoreBase::AddSuspendTimer() {
-  impl_->tid_ = g_timeout_add_seconds(5, [](gpointer data) -> gboolean {
+  impl_->tid_ = GLib::TimeoutAdd(5000, [](gpointer data) -> gboolean {
         AppCoreBase* base = reinterpret_cast<AppCoreBase*>(data);
         base->FlushMemory();
         return FALSE;
@@ -1112,7 +1110,7 @@ void AppCoreBase::AddSuspendTimer() {
 
 void AppCoreBase::RemoveSuspendTimer() {
   if (impl_->tid_ > 0) {
-    g_source_remove(impl_->tid_);
+    GLib::SourceRemove(impl_->tid_);
     impl_->tid_ = 0;
   }
 }
@@ -1159,7 +1157,7 @@ void AppCoreBase::Init(int argc, char** argv) {
   traceEnd(TTRACE_TAG_APPLICATION_MANAGER);
 
   if (impl_->feature_ & FEATURE_BACKGROUND_MANAGEMENT)
-    g_idle_add(Impl::InitSuspendCb, this);
+    GLib::IdleAdd(Impl::InitSuspendCb, this);
 
   traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:SET_SYSTEM_EVENT");
   if (!impl_->dirty_) {
@@ -1232,7 +1230,7 @@ void AppCoreBase::Dispose() {
 
   impl_->UnsetDefaultEvents();
   if (impl_->sid_) {
-    g_source_remove(impl_->sid_);
+    GLib::SourceRemove(impl_->sid_);
     impl_->sid_ = 0;
   }
 
diff --git a/tizen-cpp/app-core-cpp/interface_app_core_ui_event.hh b/tizen-cpp/app-core-cpp/interface_app_core_ui_event.hh
new file mode 100644 (file)
index 0000000..5c39c86
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TIZEN_CPP_APP_CORE_CPP_INTERFACE_APP_CORE_UI_EVENT_HH_
+#define TIZEN_CPP_APP_CORE_CPP_INTERFACE_APP_CORE_UI_EVENT_HH_
+
+#undef EXPORT_API
+#define EXPORT_API __attribute__((visibility("default")))
+
+namespace tizen_cpp {
+
+class EXPORT_API IAppCoreUiEvent {
+ public:
+  enum class UiState {
+    RESUMED,
+    PAUSED,
+  };
+
+  virtual ~IAppCoreUiEvent() = default;
+
+  virtual void Post(UiState state) = 0;
+  virtual void OnUiEvent(UiState state) = 0;
+};
+
+}  // namespace tizen_cpp
+
+#endif  // TIZEN_CPP_APP_CORE_CPP_INTERFACE_APP_CORE_UI_EVENT_HH_
index 6fa4a74..b9bc366 100644 (file)
 #include <glib.h>
 #include <stdlib.h>
 
+#include <condition_variable>
 #include <memory>
+#include <mutex>
 #include <string>
 #include <thread>
 
 #include "app-core-efl-cpp/app_core_efl_base.hh"
 #include "app-core-efl-cpp/voice_elm_private.hh"
 #include "common/log_private.hh"
+#include "common/glib_private.hh"
 
 namespace tizen_cpp {
 
@@ -44,8 +47,12 @@ class AppCoreEflBase::Impl {
   AppCoreEflBase* parent_;
 
   std::thread thread_;
+  bool thread_started_ = false;
+  std::condition_variable cond_;
   std::unique_ptr<VoiceElm> vc_elm_;
   guint source_id_ = 0;
+  std::mutex mutex_;
+  bool initialized_ = false;
 };
 
 AppCoreEflBase::Impl::Impl(AppCoreEflBase* parent) : parent_(parent) {}
@@ -55,10 +62,13 @@ AppCoreEflBase::Impl::~Impl() {
     thread_.join();
 
   if (source_id_ != 0)
-    g_source_remove(source_id_);
+    GLib::SourceRemove(source_id_);
 }
 
 void AppCoreEflBase::Impl::Init(int argc, char** argv) {
+  if (initialized_)
+    return;
+
   elm_init(argc, argv);
   unsigned int hint = parent_->GetHint();
   if ((hint & HINT_HW_ACC_CONTROL) && !getenv("AUL_HWACC")) {
@@ -77,14 +87,19 @@ void AppCoreEflBase::Impl::Init(int argc, char** argv) {
   }
 
   if (VoiceElm::IsVtAutoMode()) {
+    std::unique_lock<std::mutex> lock(mutex_);
     thread_ = std::thread([&] {
+        std::unique_lock<std::mutex> lock(mutex_);
+        thread_started_ = true;
+        cond_.notify_one();
+
         int retry_count = 3;
         do {
           if (vc_elm_.get() == nullptr)
               vc_elm_.reset(VoiceElm::Load());
 
           if (vc_elm_.get() != nullptr) {
-            source_id_ = g_idle_add([](gpointer user_data) {
+            source_id_ = GLib::IdleAdd([](gpointer user_data) {
                   auto* impl = static_cast<AppCoreEflBase::Impl*>(user_data);
                   if (impl->vc_elm_.get() != nullptr)
                     impl->vc_elm_->Init();
@@ -96,13 +111,26 @@ void AppCoreEflBase::Impl::Init(int argc, char** argv) {
           }
         } while (retry_count--);
     });
+    cond_.wait(lock, [&] { return thread_started_; });
   }
+
+  initialized_ = true;
 }
 
 void AppCoreEflBase::Impl::Finish() {
+  if (!initialized_)
+    return;
+
   if (thread_.joinable())
     thread_.join();
 
+  thread_started_ = false;
+
+  if (source_id_ != 0) {
+    GLib::SourceRemove(source_id_);
+    source_id_ = 0;
+  }
+
   elm_shutdown();
 
   // Check loader case
@@ -111,6 +139,8 @@ void AppCoreEflBase::Impl::Finish() {
     setenv("AUL_LOADER_INIT", "0", 1);
     elm_shutdown();
   }
+
+  initialized_ = false;
 }
 
 void AppCoreEflBase::Impl::LoopRun() {
index 1a0d337..56d0c14 100644 (file)
@@ -16,6 +16,8 @@
 
 #include "app-core-ui-cpp/api/app_core_ui_base.h"
 
+#include <stdlib.h>
+
 #include "app-core-ui-cpp/app_core_ui_base.hh"
 
 #undef API
@@ -256,3 +258,11 @@ API void app_core_ui_base_set_system_resource_reclaiming(app_core_ui_base_h h,
   auto* base = static_cast<AppCoreUiBase*>(h);
   base->SetSystemResourceReclaiming(enable);
 }
+
+API void* app_core_ui_base_get_tizen_glib_context(void) {
+  const char* env = getenv("TIZEN_GLIB_CONTEXT");
+  if (env)
+    return reinterpret_cast<void*>(strtoul(env, nullptr, 10));
+
+  return nullptr;
+}
index 8e255fc..f2752e2 100644 (file)
@@ -136,6 +136,8 @@ void app_core_ui_base_set_bg_state(app_core_ui_base_h h, bool bg_state);
 
 void app_core_ui_base_set_system_resource_reclaiming(app_core_ui_base_h h, bool enable);
 
+void *app_core_ui_base_get_tizen_glib_context(void);
+
 #ifdef __cplusplus
 }
 #endif  // __cplusplus
diff --git a/tizen-cpp/app-core-ui-cpp/app_core_task_base.cc b/tizen-cpp/app-core-ui-cpp/app_core_task_base.cc
new file mode 100644 (file)
index 0000000..f6e1906
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "app-core-ui-cpp/app_core_task_base.hh"
+
+#include <glib.h>
+#include <vconf-internal-keys.h>
+
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <vector>
+
+#include "common/glib_private.hh"
+
+namespace tizen_cpp {
+
+class SystemEvent : public AppCoreBase::EventBase {
+ public:
+  class IEvent {
+   public:
+    virtual ~IEvent() = default;
+    virtual void OnSystemEvent(Type type) = 0;
+  };
+
+  SystemEvent(IAppCore::IEvent::Type type, IEvent* listener);
+
+  void OnEvent(const std::string& value) override;
+  void OnEvent(int value) override;
+
+ private:
+  IEvent* listener_;
+};
+
+class AppCoreTaskBase::Impl : public SystemEvent::IEvent {
+ public:
+  Impl(AppCoreTaskBase* parent);
+
+  void LoopInit(int argc, char** argv);
+  void LoopRun();
+  void LoopExit();
+  void LoopFinish();
+  void SetSystemEvents();
+  void UnsetSystemEvents();
+  void Post(UiState state);
+  std::recursive_mutex& GetMutex() const;
+
+ private:
+  void OnSystemEvent(IAppCore::IEvent::Type type) override;
+  DeviceOrientationState ConvertRotationState(int value);
+  LowMemoryState ConvertLowMemoryState(int value);
+  LowBatteryState ConvertLowBatteryState(int value);
+
+ private:
+  friend class AppCoreTaskBase;
+  AppCoreTaskBase* parent_;
+  std::shared_ptr<SystemEvent> low_memory_;
+  std::shared_ptr<SystemEvent> low_battery_;
+  std::shared_ptr<SystemEvent> lang_change_;
+  std::shared_ptr<SystemEvent> device_orientation_changed_;
+  std::shared_ptr<SystemEvent> region_change_;
+  GMainLoop* loop_ = nullptr;
+  std::queue<UiState> queue_;
+  mutable std::recursive_mutex mutex_;
+};
+
+SystemEvent::SystemEvent(Type type, SystemEvent::IEvent* listener)
+    : EventBase(type), listener_(listener) {
+}
+
+void SystemEvent::OnEvent(const std::string& value) {
+  listener_->OnSystemEvent(GetType());
+}
+
+void SystemEvent::OnEvent(int value) {
+  listener_->OnSystemEvent(GetType());
+}
+
+AppCoreTaskBase::Impl::Impl(AppCoreTaskBase* parent) : parent_(parent) {
+  low_memory_ = std::make_shared<SystemEvent>(
+      IAppCore::IEvent::Type::LOW_MEMORY, this);
+  low_battery_ = std::make_shared<SystemEvent>(
+      IAppCore::IEvent::Type::LOW_BATTERY, this);
+  lang_change_ = std::make_shared<SystemEvent>(
+      IAppCore::IEvent::Type::LANG_CHANGE, this);
+  device_orientation_changed_ = std::make_shared<SystemEvent>(
+      IAppCore::IEvent::Type::DEVICE_ORIENTATION_CHANGED, this);
+  region_change_ = std::make_shared<SystemEvent>(
+      IAppCore::IEvent::Type::REGION_CHANGE, this);
+}
+
+void AppCoreTaskBase::Impl::LoopInit(int argc, char** argv) {
+  loop_ = g_main_loop_new(nullptr, FALSE);
+}
+
+void AppCoreTaskBase::Impl::LoopRun() {
+  g_main_loop_run(loop_);
+}
+
+void AppCoreTaskBase::Impl::LoopExit() {
+  g_main_loop_quit(loop_);
+}
+
+void AppCoreTaskBase::Impl::LoopFinish() {
+  g_main_loop_unref(loop_);
+}
+
+void AppCoreTaskBase::Impl::SetSystemEvents() {
+  parent_->AddEvent(low_memory_);
+  parent_->AddEvent(low_battery_);
+  parent_->AddEvent(lang_change_);
+  parent_->AddEvent(device_orientation_changed_);
+  parent_->AddEvent(region_change_);
+}
+
+void AppCoreTaskBase::Impl::UnsetSystemEvents() {
+  parent_->RemoveEvent(region_change_);
+  parent_->RemoveEvent(device_orientation_changed_);
+  parent_->RemoveEvent(lang_change_);
+  parent_->RemoveEvent(low_battery_);
+  parent_->RemoveEvent(low_memory_);
+}
+
+void AppCoreTaskBase::Impl::OnSystemEvent(IAppCore::IEvent::Type type) {
+  if (type == IAppCore::IEvent::Type::LOW_MEMORY) {
+    parent_->OnLowMemory(ConvertLowMemoryState(low_memory_->GetVal(0)));
+  } else if (type == IAppCore::IEvent::Type::LOW_BATTERY) {
+    parent_->OnLowBattery(ConvertLowBatteryState(low_battery_->GetVal(0)));
+  } else if (type == IAppCore::IEvent::Type::LANG_CHANGE) {
+    parent_->OnLangChanged(lang_change_->GetVal(""));
+  } else if (type == IAppCore::IEvent::Type::DEVICE_ORIENTATION_CHANGED) {
+    parent_->OnDeviceOrientationChanged(
+        ConvertRotationState(device_orientation_changed_->GetVal(0)));
+  } else if (type == IAppCore::IEvent::Type::REGION_CHANGE) {
+    parent_->OnRegionChanged(region_change_->GetVal(""));
+  }
+}
+
+void AppCoreTaskBase::Impl::Post(UiState state) {
+  std::lock_guard<std::recursive_mutex> lock(mutex_);
+  queue_.push(state);
+
+  GLib::IdleAdd(g_main_context_default(), [](gpointer user_data) {
+        auto* impl = static_cast<AppCoreTaskBase::Impl*>(user_data);
+        UiState state;
+        {
+          std::lock_guard<std::recursive_mutex> lock(impl->GetMutex());
+          state = impl->queue_.front();
+          impl->queue_.pop();
+        }
+        impl->parent_->OnUiEvent(state);
+        return G_SOURCE_REMOVE;
+      }, this);
+}
+
+std::recursive_mutex& AppCoreTaskBase::Impl::GetMutex() const {
+  return mutex_;
+}
+
+AppCoreTaskBase::DeviceOrientationState
+AppCoreTaskBase::Impl::ConvertRotationState(int value) {
+  AppCoreTaskBase::DeviceOrientationState state;
+
+  switch (value) {
+    case AppCoreBase::ROTATION_PORTRAIT_NORMAL:
+      state = AppCoreTaskBase::DEVICE_ORIENTATION_STATE_0;
+      break;
+    case AppCoreBase::ROTATION_PORTRAIT_REVERSE:
+      state = AppCoreTaskBase::DEVICE_ORIENTATION_STATE_180;
+      break;
+    case AppCoreBase::ROTATION_LANDSCAPE_NORMAL:
+      state = AppCoreTaskBase::DEVICE_ORIENTATION_STATE_270;
+      break;
+    case AppCoreBase::ROTATION_LANDSCAPE_REVERSE:
+      state = AppCoreTaskBase::DEVICE_ORIENTATION_STATE_90;
+      break;
+    default:
+      state = AppCoreTaskBase::DEVICE_ORIENTATION_STATE_UNKNOWN;
+      break;
+  }
+
+  return state;
+}
+
+AppCoreTaskBase::LowMemoryState AppCoreTaskBase::Impl::ConvertLowMemoryState(
+    int value) {
+  AppCoreTaskBase::LowMemoryState state;
+
+  switch (value) {
+    case VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL:
+      state = AppCoreTaskBase::LOW_MEMORY_STATE_NORMAL;
+      break;
+    case VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING:
+      state = AppCoreTaskBase::LOW_MEMORY_STATE_SOFT_WARNING;
+      break;
+    case VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING:
+      state = AppCoreTaskBase::LOW_MEMORY_STATE_HARD_WARNING;
+      break;
+    default:
+      state = AppCoreTaskBase::LOW_MEMORY_STATE_UNKNOWN;
+      break;
+  }
+
+  return state;
+}
+
+AppCoreTaskBase::LowBatteryState AppCoreTaskBase::Impl::ConvertLowBatteryState(
+    int value) {
+  AppCoreTaskBase::LowBatteryState state;
+
+  switch (value) {
+    case VCONFKEY_SYSMAN_BAT_POWER_OFF:
+      state = AppCoreTaskBase::LOW_BATTERY_STATE_POWER_OFF;
+      break;
+    case VCONFKEY_SYSMAN_BAT_CRITICAL_LOW:
+      state = AppCoreTaskBase::LOW_BATTERY_STATE_CRITICAL_LOW;
+      break;
+    default:
+      state = AppCoreTaskBase::LOW_BATTERY_STATE_UNKNOWN;
+      break;
+  }
+
+  return state;
+}
+
+AppCoreTaskBase::AppCoreTaskBase() : impl_(new Impl(this)) {}
+
+AppCoreTaskBase::~AppCoreTaskBase() = default;
+
+int AppCoreTaskBase::OnReceive(aul_type type, tizen_base::Bundle b) {
+  return AppCoreBase::OnReceive(type, std::move(b));
+}
+
+int AppCoreTaskBase::OnCreate() {
+  return 0;
+}
+
+int AppCoreTaskBase::OnTerminate() {
+  return AppCoreBase::OnTerminate();
+}
+
+int AppCoreTaskBase::OnControl(tizen_base::Bundle b) {
+  return AppCoreBase::OnControl(std::move(b));
+}
+
+int AppCoreTaskBase::OnTrimMemory() {
+  return AppCoreBase::OnTrimMemory();
+}
+
+void AppCoreTaskBase::OnLoopInit(int argc, char** argv) {
+  impl_->LoopInit(argc, argv);
+}
+
+void AppCoreTaskBase::OnLoopRun() {
+  impl_->LoopRun();
+}
+
+void AppCoreTaskBase::OnLoopExit() {
+  impl_->LoopExit();
+}
+
+void AppCoreTaskBase::OnLoopFinish() {
+  impl_->LoopFinish();
+}
+
+void AppCoreTaskBase::OnLowMemory(LowMemoryState state) {}
+
+void AppCoreTaskBase::OnLowBattery(LowBatteryState state) {}
+
+void AppCoreTaskBase::OnLangChanged(const std::string& lang) {}
+
+void AppCoreTaskBase::OnDeviceOrientationChanged(DeviceOrientationState state) {}
+
+void AppCoreTaskBase::OnRegionChanged(const std::string& region) {}
+
+void AppCoreTaskBase::Run(int argc, char** argv) {
+  impl_->SetSystemEvents();
+  AppCoreBase::Run(argc, argv);
+  impl_->UnsetSystemEvents();
+}
+
+void AppCoreTaskBase::OnUiEvent(UiState state) {}
+
+void AppCoreTaskBase::Post(UiState state) {
+  impl_->Post(state);
+}
+
+}  // namespace tizen_cpp
diff --git a/tizen-cpp/app-core-ui-cpp/app_core_task_base.hh b/tizen-cpp/app-core-ui-cpp/app_core_task_base.hh
new file mode 100644 (file)
index 0000000..91f089d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TIZEN_CPP_APP_CORE_UI_CPP_APP_CORE_TASK_BASE_HH_
+#define TIZEN_CPP_APP_CORE_UI_CPP_APP_CORE_TASK_BASE_HH_
+
+#include <memory>
+
+#include <app_core_base.hh>
+#include <interface_app_core_ui_event.hh>
+
+#undef EXPORT_API
+#define EXPORT_API __attribute__((visibility("default")))
+
+namespace tizen_cpp {
+
+class EXPORT_API AppCoreTaskBase : public AppCoreBase,
+                                   public IAppCoreUiEvent {
+ public:
+  enum LowMemoryState {
+    LOW_MEMORY_STATE_UNKNOWN,
+    LOW_MEMORY_STATE_NORMAL,
+    LOW_MEMORY_STATE_SOFT_WARNING,
+    LOW_MEMORY_STATE_HARD_WARNING,
+  };
+
+  enum LowBatteryState {
+    LOW_BATTERY_STATE_UNKNOWN,
+    LOW_BATTERY_STATE_POWER_OFF,
+    LOW_BATTERY_STATE_CRITICAL_LOW,
+  };
+
+  enum DeviceOrientationState {
+    DEVICE_ORIENTATION_STATE_UNKNOWN,
+    DEVICE_ORIENTATION_STATE_0,
+    DEVICE_ORIENTATION_STATE_180,
+    DEVICE_ORIENTATION_STATE_270,
+    DEVICE_ORIENTATION_STATE_90,
+  };
+
+  AppCoreTaskBase();
+  virtual ~AppCoreTaskBase();
+
+  int OnReceive(aul_type type, tizen_base::Bundle b) override;
+  int OnCreate() override;
+  int OnTerminate() override;
+  int OnControl(tizen_base::Bundle b) override;
+
+  int OnTrimMemory() override;
+  void OnLoopInit(int argc, char** argv) override;
+  void OnLoopRun() override;
+  void OnLoopExit() override;
+  void OnLoopFinish() override;
+
+  virtual void OnLowMemory(LowMemoryState state);
+  virtual void OnLowBattery(LowBatteryState state);
+  virtual void OnLangChanged(const std::string& lang);
+  virtual void OnDeviceOrientationChanged(DeviceOrientationState state);
+  virtual void OnRegionChanged(const std::string& region);
+
+  void Run(int argc, char** argv) override;
+
+  void OnUiEvent(UiState state) override;
+  void Post(UiState state) override;
+
+ private:
+  class Impl;
+  std::unique_ptr<Impl> impl_;
+};
+
+}  // namespace tizen_cpp
+
+#endif  // TIZEN_CPP_APP_CORE_UI_CPP_APP_CORE_TASK_BASE_HH_
index a3c67ca..8666ad3 100644 (file)
 #include <list>
 #include <memory>
 #include <string>
+#include <thread>
 
 #include "app-core-cpp/app_core_base.hh"
 #include "app-core-ui-cpp/api/app_core_ui_base.h"
+#include "app-core-ui-cpp/app_core_task_base.hh"
 #include "app-core-ui-cpp/app_core_ui_delegator_private.hh"
 #include "app-core-ui-cpp/app_core_ui_plugin_private.hh"
 #include "app-core-ui-cpp/wayland_handler_private.hh"
 #include "common/ecore_handler.hh"
+#include "common/glib_private.hh"
 #include "common/log_private.hh"
 
 namespace tizen_cpp {
@@ -100,6 +103,8 @@ class AppCoreUiBase::Impl {
   int FiniWl();
   void PluginInit(int argc, char** argv);
   void PluginFini();
+  void Run(int argc, char** argv);
+  void Exit();
 
   std::list<std::shared_ptr<WinNode>> winnode_list_;
   unsigned int hint_;
@@ -115,6 +120,9 @@ class AppCoreUiBase::Impl {
   IAppCoreUi* core_ui_delegator_ = nullptr;
   std::unique_ptr<AppCoreUiDelegator> plugin_delegator_;
   std::unique_ptr<AppCoreUiPlugin> plugin_;
+  std::unique_ptr<AppCoreTaskBase> service_;
+  GMainContext* context_ = nullptr;
+  std::thread thread_;
 };
 
 AppCoreUiBase::AppCoreUiBase(unsigned int hint)
@@ -367,7 +375,7 @@ void AppCoreUiBase::Impl::PluginFini() {
   plugin_->Fini(parent_);
 }
 
-void AppCoreUiBase::Run(int argc, char** argv) {
+void AppCoreUiBase::DoRun(int argc, char** argv) {
   SetCoreDelegator(nullptr);
   SetLoopDelegator(nullptr);
   SetCoreUiDelegator(nullptr);
@@ -400,6 +408,61 @@ void AppCoreUiBase::Run(int argc, char** argv) {
   AppCoreBase::Run(argc, argv);
 }
 
+void AppCoreUiBase::DoExit() {
+  AppCoreBase::Exit();
+}
+
+void AppCoreUiBase::Impl::Run(int argc, char** argv) {
+  if (hint_ & HINT_DUAL_THREAD) {
+    // For the loader case
+    while (ecore_shutdown() != 0);
+
+    service_ = parent_->CreateTask();
+    context_ = g_main_context_new();
+    std::string env = std::to_string(reinterpret_cast<unsigned int>(context_));
+    setenv("TIZEN_GLIB_CONTEXT", env.c_str(), 1);
+
+    thread_ = std::thread([&] {
+          parent_->DoRun(argc, argv);
+        });
+
+    service_->Run(argc, argv);
+
+    if (thread_.joinable())
+      thread_.join();
+
+    setenv("TIZEN_GLIB_CONTEXT", "", 1);
+    g_main_context_unref(context_);
+    context_ = nullptr;
+    return;
+  }
+
+  parent_->DoRun(argc, argv);
+}
+
+void AppCoreUiBase::Impl::Exit() {
+  if (hint_ & HINT_DUAL_THREAD) {
+    GLib::IdleAdd(context_, [](gpointer user_data) {
+          auto* impl = static_cast<AppCoreUiBase::Impl*>(user_data);
+          impl->parent_->DoExit();
+          return G_SOURCE_REMOVE;
+        }, this);
+
+    service_->Exit();
+    return;
+  }
+
+  parent_->DoExit();
+}
+
+void AppCoreUiBase::Run(int argc, char** argv) {
+  impl_->Run(argc, argv);
+}
+
+void AppCoreUiBase::Exit() {
+  impl_->Exit();
+}
+
 void AppCoreUiBase::Dispose() {
   impl_->handler_->Fini();
   impl_->FiniWl();
@@ -410,6 +473,10 @@ void AppCoreUiBase::Dispose() {
   impl_->PluginFini();
 }
 
+std::unique_ptr<AppCoreTaskBase> AppCoreUiBase::CreateTask() {
+  return std::make_unique<AppCoreTaskBase>();
+}
+
 void AppCoreUiBase::Impl::PrepareToSuspend() {
   if (parent_->IsBgAllowed() && !parent_->IsSuspended()) {
     SuspendedState suspend = SUSPENDED_STATE_WILL_ENTER_SUSPEND;
@@ -432,6 +499,9 @@ void AppCoreUiBase::Impl::DoPause() {
     state_ = AS_PAUSED;
     traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:PAUSE");
     _D("Call pause callback");
+    if (parent_->GetHint() & HINT_DUAL_THREAD)
+      service_->Post(AppCoreTaskBase::UiState::PAUSED);
+
     int ret;
     if (core_ui_delegator_)
       ret = core_ui_delegator_->OnPause();
@@ -456,6 +526,9 @@ void AppCoreUiBase::Impl::DoResume() {
     LOG(LOG_DEBUG, "LAUNCH", "[%s:Application:resume:start]", appid_.c_str());
     traceBegin(TTRACE_TAG_APPLICATION_MANAGER, "APPCORE:RESUME");
     _D("Call resume callback");
+    if (parent_->GetHint() & HINT_DUAL_THREAD)
+      service_->Post(AppCoreTaskBase::UiState::RESUMED);
+
     parent_->RemoveSuspendTimer();
     if (core_ui_delegator_)
       core_ui_delegator_->OnResume();
index 2d06eb9..310665b 100644 (file)
@@ -20,6 +20,7 @@
 #include <memory>
 
 #include <app_core_base.hh>
+#include <app_core_task_base.hh>
 #include <interface_app_core_ui.hh>
 #include <interface_window.hh>
 
@@ -39,6 +40,7 @@ class EXPORT_API AppCoreUiBase : public AppCoreBase,
   constexpr static int HINT_WINDOW_AUTO_CONTROL = 0x10;
   constexpr static int HINT_LEGACY_CONTROL = 0x20;
   constexpr static int HINT_WINDOW_ID_CONTROL = 0x40;
+  constexpr static int HINT_DUAL_THREAD = 0x80;
 
   AppCoreUiBase(unsigned int hint);
   virtual ~AppCoreUiBase();
@@ -68,12 +70,18 @@ class EXPORT_API AppCoreUiBase : public AppCoreBase,
   void SetSystemResourceReclaiming(bool enable);
   void Run(int argc, char** argv) override;
   void Dispose() override;
+  virtual std::unique_ptr<AppCoreTaskBase> CreateTask();
+  void Exit() override;
 
  protected:
   void SetCoreUiDelegator(IAppCoreUi* delegator);
   void SetWindowDelegator(IWindow* delegator);
 
  private:
+  void DoRun(int argc, char** argv);
+  void DoExit();
+
+ private:
   class Impl;
   std::unique_ptr<Impl> impl_;
 };
index ace58b1..f95e1f2 100644 (file)
@@ -19,8 +19,9 @@
 
 #include <stdexcept>
 
-#include "common/log_private.hh"
 #include "common/ecore_handler.hh"
+#include "common/glib_private.hh"
+#include "common/log_private.hh"
 
 namespace tizen_cpp {
 
@@ -36,14 +37,14 @@ void EcoreHandler::AddFlushTimer() {
   if (flush_timer_ != 0)
     return;
 
-  flush_timer_ = g_timeout_add(5000, FlushMemoryCb, this);
+  flush_timer_ = GLib::TimeoutAdd(5000, FlushMemoryCb, this);
 }
 
 void EcoreHandler::RemoveFlushTimer() {
   if (flush_timer_ == 0)
     return;
 
-  g_source_remove(flush_timer_);
+  GLib::SourceRemove(flush_timer_);
   flush_timer_ = 0;
 }
 
diff --git a/tizen-cpp/common/glib_private.cc b/tizen-cpp/common/glib_private.cc
new file mode 100644 (file)
index 0000000..80e3fee
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/glib_private.hh"
+
+namespace tizen_cpp {
+
+guint GLib::IdleAdd(GSourceFunc func, gpointer data) {
+  return IdleAddFull(G_PRIORITY_DEFAULT, g_main_context_get_thread_default(),
+      func, data);
+}
+
+guint GLib::IdleAdd(GMainContext* context, GSourceFunc func, gpointer data) {
+  return IdleAddFull(G_PRIORITY_DEFAULT, context, func, data);
+}
+
+guint GLib::IdleAddFull(guint priority, GMainContext* context,
+    GSourceFunc func, gpointer data) {
+  auto* source = g_idle_source_new();
+  if (source == nullptr)
+    return 0;
+
+  g_source_set_callback(source, func, data, nullptr);
+  g_source_set_priority(source, priority);
+  guint source_id = g_source_attach(source, context);
+  g_source_unref(source);
+  return source_id;
+}
+
+guint GLib::TimeoutAdd(guint interval, GSourceFunc func, gpointer data) {
+  return TimeoutAdd(g_main_context_get_thread_default(), interval, func, data);
+}
+
+guint GLib::TimeoutAdd(GMainContext* context, guint interval,
+    GSourceFunc func, gpointer data) {
+  auto* source = g_timeout_source_new(interval);
+  if (source == nullptr)
+    return 0;
+
+  g_source_set_callback(source, func, data, nullptr);
+  guint source_id = g_source_attach(source, context);
+  g_source_unref(source);
+  return source_id;
+}
+
+void GLib::SourceRemove(guint source_id) {
+  SourceRemove(g_main_context_get_thread_default(), source_id);
+}
+
+void GLib::SourceRemove(GMainContext* context, guint source_id) {
+  auto* source = g_main_context_find_source_by_id(context, source_id);
+  if (source != nullptr && !g_source_is_destroyed(source))
+    g_source_destroy(source);
+}
+
+}  // namespace tizen_cpp
diff --git a/tizen-cpp/common/glib_private.hh b/tizen-cpp/common/glib_private.hh
new file mode 100644 (file)
index 0000000..f2dd1aa
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TIZEN_CPP_COMMON_GLIB_PRIVATE_HH_
+#define TIZEN_CPP_COMMON_GLIB_PRIVATE_HH_
+
+#include <glib.h>
+
+namespace tizen_cpp {
+
+class GLib {
+ public:
+  static guint IdleAdd(GSourceFunc func, gpointer data);
+  static guint IdleAdd(GMainContext* context, GSourceFunc func, gpointer data);
+  static guint IdleAddFull(guint priority, GMainContext* context,
+      GSourceFunc func, gpointer data);
+  static guint TimeoutAdd(guint interval, GSourceFunc func, gpointer data);
+  static guint TimeoutAdd(GMainContext* context, guint interval,
+      GSourceFunc func, gpointer data);
+  static void SourceRemove(guint source_id);
+  static void SourceRemove(GMainContext* context, guint source_id);
+};
+
+}  // namespace tizen_cpp
+
+#endif  // TIZEN_CPP_COMMON_GLIB_PRIVATE_HH_