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 7511a1650de02a38b05df48e77eded336d232d71..acef89c6f90826b2718fdcc54fb7770dc8393660 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 9668ed42a6df61c3487d219fda985d668dea9a84..08a41fbff1dd8d7c6c2fef329060d4be299b1ef6 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 5adf51c8beee76b5c719d180e45bb5a97adb5fad..a2bb1b5fc01b93ffac2afcbbd0f463c5a97a2f3c 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 6fa4a7490374442495e756cef5b8d52aa8bbd2bb..b9bc366318e04516b09037afcfae99ccd474172a 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 1a0d337194be96289e776976527638f3f3a226ae..56d0c14bef98f185c63772ab5548c294b0bd7ee1 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 8e255fc1ba32d0b193b68aab00cc8c03abf53f02..f2752e26c4234f2b066e1e580c1e279a69f25b2e 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 a3c67cad181be025b0277cdec1ec58f479861395..8666ad33b03add3252a07727cd0e3bb684f20c4a 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 2d06eb94f30104a350bc1206715feb2da06c650d..310665bb49db9998c7df8b35056a9cbef5bd41d8 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,11 +70,17 @@ 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 ace58b11c98ee23188cdf4f3fb20456d2189ef66..f95e1f257cce9ae0b37fece4df1d2b3578a5f204 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_