implement ds_tizen_hwc 88/280388/1
authorChangyeon Lee <cyeon.lee@samsung.com>
Wed, 24 Aug 2022 06:40:50 +0000 (15:40 +0900)
committerTizen Window System <tizen.windowsystem@gmail.com>
Mon, 29 Aug 2022 09:56:14 +0000 (18:56 +0900)
This is the server implementation for tizen_hwc protocol.

Change-Id: I4ba3b103efaf6ce2c8863d63f26b40e120ffc327

include/libds-tizen/hwc.h [new file with mode: 0644]
packaging/libds-tizen.spec
src/hwc/hwc.c [new file with mode: 0644]
src/hwc/meson.build [new file with mode: 0644]
src/meson.build
tests/meson.build
tests/tc_hwc.cpp [new file with mode: 0644]

diff --git a/include/libds-tizen/hwc.h b/include/libds-tizen/hwc.h
new file mode 100644 (file)
index 0000000..11ffe9c
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef LIBDS_TIZEN_HWC_H
+#define LIBDS_TIZEN_HWC_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+#include <libds/surface.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ds_tizen_hwc;
+
+struct ds_tizen_hwc_commit_feedback;
+
+struct ds_tizen_hwc *
+ds_tizen_hwc_create(struct wl_display *display);
+
+void
+ds_tizen_hwc_add_destroy_listener(struct ds_tizen_hwc *hwc,
+        struct wl_listener *listener);
+
+void
+ds_tizen_hwc_add_new_commit_feedback_listener(
+        struct ds_tizen_hwc *hwc,
+        struct wl_listener *listener);
+
+void
+ds_tizen_hwc_commit_feedback_add_destroy_listener(
+        struct ds_tizen_hwc_commit_feedback *commit_feedback,
+        struct wl_listener *listener);
+
+struct ds_surface *
+ds_tizen_hwc_commit_feedback_get_surface(
+        struct ds_tizen_hwc_commit_feedback *commit_feedback);
+
+void
+ds_tizen_hwc_commit_feedback_send_committed(
+        struct ds_tizen_hwc_commit_feedback *commit_feedback);
+
+void
+ds_tizen_hwc_commit_feedback_send_discarded(
+        struct ds_tizen_hwc_commit_feedback *commit_feedback);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index fc126c7..53d3d2f 100644 (file)
@@ -32,6 +32,8 @@ BuildRequires:  pkgconfig(tizen-dpms-server)
 BuildRequires:  pkgconfig(tizen-dpms-client)
 BuildRequires:  pkgconfig(tizen-surface-server)
 BuildRequires:  pkgconfig(tizen-surface-client)
+BuildRequires : pkgconfig(tizen-hwc-server)
+BuildRequires : pkgconfig(tizen-hwc-client)
 BuildRequires:  pkgconfig(cynara-client)
 BuildRequires:  pkgconfig(cynara-session)
 BuildRequires:  pkgconfig(libsmack)
@@ -295,6 +297,21 @@ Group:   Development/Libraries
 %description input-method-devel
 Development package for tizen input-method
 
+## libds-tizen-hwc
+%package hwc
+Summary: Library for tizen hwc
+Group:   Development/Libraries
+
+%description hwc
+Library for tizen hwc
+
+%package hwc-devel
+Summary: Development package for tizen hwc
+Group:   Development/Libraries
+
+%description hwc-devel
+Development package for tizen hwc
+
 %prep
 %setup -q
 cp %{SOURCE1001} .
@@ -571,3 +588,18 @@ ninja -C builddir install
 %{_bindir}/libds-tizen-input-method-tests
 %{_bindir}/libds-tizen-input-method-manager-tests
 %{_bindir}/ime-keyboard
+
+%files hwc
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license LICENSE
+%{_libdir}/libds-tizen-hwc.so*
+
+%files hwc-devel
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license LICENSE
+%{_includedir}/libds-tizen/hwc.h
+%{_libdir}/pkgconfig/libds-tizen-hwc.pc
+%{_libdir}/libds-tizen-hwc.so*
+%{_bindir}/libds-tizen-hwc-tests
diff --git a/src/hwc/hwc.c b/src/hwc/hwc.c
new file mode 100644 (file)
index 0000000..7346911
--- /dev/null
@@ -0,0 +1,297 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <tizen-hwc-server-protocol.h>
+#include <libds/log.h>
+
+#include "util.h"
+#include "libds-tizen/hwc.h"
+
+#define TIZEN_HWC_VERSION 1
+
+struct ds_tizen_hwc
+{
+    struct wl_global *global;
+
+    struct wl_list clients;
+
+    struct wl_listener destroy;
+
+    struct {
+        struct wl_signal destroy;
+        struct wl_signal new_commit_feedback;
+    } events;
+};
+
+struct ds_tizen_hwc_client
+{
+    struct ds_tizen_hwc *hwc;
+
+    struct wl_resource *resource;
+    struct wl_client *wl_client;
+
+    struct {
+        struct wl_signal destroy;
+    } events;
+
+    struct wl_list link;
+};
+
+struct ds_tizen_hwc_commit_feedback
+{
+    struct wl_resource *resource;
+
+    struct ds_surface *surface;
+    uint32_t serial;
+
+    struct {
+        struct wl_listener surface_destroy;
+    } listener;
+
+    struct {
+        struct wl_signal destroy;
+    } events;
+};
+
+static void
+hwc_commit_feedback_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+    struct ds_tizen_hwc_commit_feedback *commit_feedback;
+
+    commit_feedback = wl_container_of(listener, commit_feedback,
+            listener.surface_destroy);
+
+    if (commit_feedback->listener.surface_destroy.notify) {
+        wl_list_remove(&commit_feedback->listener.surface_destroy.link);
+        commit_feedback->listener.surface_destroy.notify = NULL;
+    }
+
+    commit_feedback->surface = NULL;
+}
+
+static void
+_hwc_commit_feedback_cb_resource_destroy(struct wl_resource *commit_feedback_resource)
+{
+    struct ds_tizen_hwc_commit_feedback *commit_feedback;
+
+    commit_feedback = wl_resource_get_user_data(commit_feedback_resource);
+
+    ds_inf("hwc_commit_feedback:%p destroy", commit_feedback);
+
+    if (commit_feedback->listener.surface_destroy.notify) {
+        wl_list_remove(&commit_feedback->listener.surface_destroy.link);
+        commit_feedback->listener.surface_destroy.notify = NULL;
+    }
+
+    wl_signal_emit(&commit_feedback->events.destroy, commit_feedback);
+
+    free(commit_feedback);
+}
+
+static void
+hwc_handle_create_commit_feedback(struct wl_client *client,
+    struct wl_resource *hwc_client_resource,
+    struct wl_resource *surface_resource,
+    uint32_t id,
+    uint32_t serial)
+{
+    struct ds_tizen_hwc_client *hwc_client;
+    struct ds_tizen_hwc_commit_feedback *commit_feedback;
+    struct ds_surface *surface;
+
+    hwc_client = wl_resource_get_user_data(hwc_client_resource);
+
+    surface = ds_surface_from_resource(surface_resource);
+
+    commit_feedback = calloc(1, sizeof *commit_feedback);
+    if (!commit_feedback) {
+        wl_client_post_no_memory(client);
+        return;
+    }
+
+    commit_feedback->resource = wl_resource_create(client, &tizen_hwc_commit_feedback_interface,
+            wl_resource_get_version(hwc_client_resource), id);
+    if (!commit_feedback->resource) {
+        wl_client_post_no_memory(client);
+        free(commit_feedback);
+        return;
+    }
+
+    commit_feedback->listener.surface_destroy.notify = hwc_commit_feedback_handle_surface_destroy;
+    ds_surface_add_destroy_listener(surface, &commit_feedback->listener.surface_destroy);
+    commit_feedback->surface = surface;
+
+    commit_feedback->serial = serial;
+
+    wl_resource_set_implementation(commit_feedback->resource, NULL,
+            commit_feedback, _hwc_commit_feedback_cb_resource_destroy);
+
+    ds_inf("hwc_commit_feedback:%p create", commit_feedback);
+
+    wl_signal_init(&commit_feedback->events.destroy);
+
+    wl_signal_emit(&hwc_client->hwc->events.new_commit_feedback, commit_feedback);
+}
+
+static void
+hwc_handle_destroy(struct wl_client *wl_client,
+    struct wl_resource *resource)
+{
+    wl_resource_destroy(resource);
+}
+
+static const struct tizen_hwc_interface hwc_impl =
+{
+    .destroy = hwc_handle_destroy,
+    .create_commit_feedback = hwc_handle_create_commit_feedback,
+};
+
+static void
+_tizen_hwc_client_handle_resource_destroy(struct wl_resource *resource)
+{
+    struct ds_tizen_hwc_client *client;
+
+    client = wl_resource_get_user_data(resource);
+
+    ds_inf("_tizen_hwc_client_handle_destroy (client:%p)", client);
+
+    wl_list_remove(&client->link);
+    free(client);
+}
+
+static void
+hwc_bind(struct wl_client *wl_client, void *data, uint32_t version,
+         uint32_t id)
+{
+    struct ds_tizen_hwc *hwc = data;
+    struct ds_tizen_hwc_client *hwc_client;
+
+    hwc_client = calloc(1, sizeof *hwc_client);
+    if (hwc_client == NULL) {
+        ds_err("calloc() failed. tizen_hwc");
+        wl_client_post_no_memory(wl_client);
+        return;
+    }
+
+    ds_inf("tizen_hwc_client binds. (hwc_client:%p)", hwc_client);
+
+    hwc_client->hwc = hwc;
+    hwc_client->wl_client = wl_client;
+
+    hwc_client->resource = wl_resource_create(wl_client, &tizen_hwc_interface,
+            MIN(version, TIZEN_HWC_VERSION), id);
+    if (hwc_client->resource == NULL) {
+        ds_err("tizen_hwc : wl_resource_create() failed.");
+        free(hwc_client);
+        wl_client_post_no_memory(wl_client);
+        return;
+    }
+
+    wl_resource_set_implementation(hwc_client->resource, &hwc_impl, hwc_client,
+            _tizen_hwc_client_handle_resource_destroy);
+
+    wl_signal_init(&hwc_client->events.destroy);
+
+    wl_list_insert(&hwc->clients, &hwc_client->link);
+}
+
+static void
+hwc_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+    struct ds_tizen_hwc *hwc;
+
+    hwc = wl_container_of(listener, hwc, destroy);
+
+    ds_inf("Global destroy: hwc(%p)", hwc);
+
+    wl_signal_emit(&hwc->events.destroy, hwc);
+    wl_list_remove(&hwc->destroy.link);
+    wl_global_destroy(hwc->global);
+    free(hwc);
+}
+
+WL_EXPORT struct ds_tizen_hwc *
+ds_tizen_hwc_create(struct wl_display *display)
+{
+    struct ds_tizen_hwc *hwc;
+
+    hwc = calloc(1, sizeof *hwc);
+    if (!hwc) {
+        ds_err("calloc() failed.");
+        return NULL;
+    }
+
+    hwc->global = wl_global_create(display, &tizen_hwc_interface,
+            TIZEN_HWC_VERSION, hwc, hwc_bind);
+    if (!hwc->global) {
+        ds_err("wl_global_create() failed. tizen_hwc_interface");
+        free(hwc);
+        return NULL;
+    }
+
+    wl_list_init(&hwc->clients);
+
+    hwc->destroy.notify = hwc_handle_display_destroy;
+    wl_display_add_destroy_listener(display, &hwc->destroy);
+
+    wl_signal_init(&hwc->events.destroy);
+    wl_signal_init(&hwc->events.new_commit_feedback);
+
+    ds_inf("Global created: tizen_hwc(%p)", hwc);
+
+    return hwc;
+}
+
+WL_EXPORT void
+ds_tizen_hwc_add_destroy_listener(
+        struct ds_tizen_hwc *hwc,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&hwc->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_tizen_hwc_add_new_commit_feedback_listener(
+        struct ds_tizen_hwc *hwc,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&hwc->events.new_commit_feedback, listener);
+}
+
+WL_EXPORT void
+ds_tizen_hwc_commit_feedback_add_destroy_listener(
+        struct ds_tizen_hwc_commit_feedback *commit_feedback,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&commit_feedback->events.destroy, listener);
+}
+
+WL_EXPORT struct ds_surface *
+ds_tizen_hwc_commit_feedback_get_surface(
+        struct ds_tizen_hwc_commit_feedback *commit_feedback)
+{
+    return commit_feedback->surface;
+}
+
+WL_EXPORT void
+ds_tizen_hwc_commit_feedback_send_committed(
+        struct ds_tizen_hwc_commit_feedback *commit_feedback)
+{
+    ds_inf("hwc_commit_feedback:%p send committed", commit_feedback);
+
+    tizen_hwc_commit_feedback_send_committed(commit_feedback->resource,
+            commit_feedback->serial);
+    wl_resource_destroy(commit_feedback->resource);
+}
+
+WL_EXPORT void
+ds_tizen_hwc_commit_feedback_send_discarded(
+        struct ds_tizen_hwc_commit_feedback *commit_feedback)
+{
+    ds_inf("hwc_commit_feedback:%p send discard", commit_feedback);
+
+    tizen_hwc_commit_feedback_send_discarded(commit_feedback->resource,
+            commit_feedback->serial);
+    wl_resource_destroy(commit_feedback->resource);
+}
diff --git a/src/hwc/meson.build b/src/hwc/meson.build
new file mode 100644 (file)
index 0000000..7f9caa0
--- /dev/null
@@ -0,0 +1,29 @@
+libds_tizen_hwc_files = [
+  'hwc.c',
+]
+
+libds_tizen_hwc_deps = [
+  deps_libds_tizen,
+  dependency('tizen-hwc-server', required: true),
+]
+
+lib_libds_tizen_hwc = shared_library('ds-tizen-hwc', libds_tizen_hwc_files,
+  dependencies: libds_tizen_hwc_deps,
+  include_directories: [ common_inc, include_directories('.'), include_directories('..') ],
+  version: meson.project_version(),
+  install: true
+)
+
+deps_libds_tizen_hwc = declare_dependency(
+  link_with: lib_libds_tizen_hwc,
+  dependencies: libds_tizen_hwc_deps,
+  include_directories: [ common_inc, include_directories('.') ],
+)
+
+pkgconfig = import('pkgconfig')
+pkgconfig.generate(lib_libds_tizen_hwc,
+  version: meson.project_version(),
+  filebase: 'libds-tizen-hwc',
+  name: 'libds-tizen-hwc',
+  description: 'tizen hwc extension of libds-tizen for tizen platform',
+)
index aef63ac..8d254fa 100644 (file)
@@ -43,4 +43,5 @@ subdir('screen_rotation')
 subdir('global_resource')
 subdir('embedded_compositor')
 subdir('input_method')
-subdir('text_input')
\ No newline at end of file
+subdir('text_input')
+subdir('hwc')
index 0405794..1b78a64 100644 (file)
@@ -283,4 +283,24 @@ executable('libds-tizen-text-input-tests',
   ],
   install_dir: libds_tizen_bindir,
   install : true
-)
\ No newline at end of file
+)
+
+## hwc tests
+tc_hwc_files = [
+  'tc_main.cpp',
+  'tc_hwc.cpp',
+]
+
+executable('libds-tizen-hwc-tests',
+  [
+    tc_mock_files,
+    tc_hwc_files
+  ],
+  dependencies: [
+    deps_test_common,
+    deps_libds_tizen_hwc,
+    dependency('tizen-hwc-client', required: true),
+  ],
+  install_dir: libds_tizen_bindir,
+  install : true
+)
diff --git a/tests/tc_hwc.cpp b/tests/tc_hwc.cpp
new file mode 100644 (file)
index 0000000..77c93b1
--- /dev/null
@@ -0,0 +1,398 @@
+#include "tc_main.h"
+#include "mockclient.h"
+#include "mockcompositor.h"
+#include <libds-tizen/hwc.h>
+#include <tizen-hwc-client-protocol.h>
+
+#define TIZEN_HWC_VERSION 1
+
+class MockHwcCompositor : public MockCompositor
+{
+public:
+    MockHwcCompositor()
+        : MockCompositor(&MockHwcCompositor::TestSetup, this)
+    {
+        ds_inf("%s : this(%p)", __func__, this);
+
+        bHwcDestroyed = false;
+        bSurfaceDestroyed = false;
+    }
+
+    ~MockHwcCompositor()
+    {
+        ds_inf("%s : this(%p)", __func__, this);
+    }
+
+    static void TestSetup(void *data)
+    {
+        MockHwcCompositor *mockComp =
+            static_cast<MockHwcCompositor *>(data);
+        Compositor *comp = mockComp->compositor;
+
+        ds_inf("%s: mockComp(%p)", __func__, mockComp);
+
+        // new surface listener
+        mockComp->mNewSurfaceListener.notify =
+            MockHwcCompositor::NewSurfaceCallback;
+        mockComp->mNewSurfaceListener.parent = mockComp;
+        ds_compositor_add_new_surface_listener(comp->compositor,
+                &mockComp->mNewSurfaceListener);
+
+        mockComp->mHwc =
+            ds_tizen_hwc_create(comp->display);
+
+        // destroy listener
+        mockComp->mHwcDestroyListener.notify =
+            MockHwcCompositor::HwcDestroyCallback;
+        mockComp->mHwcDestroyListener.parent = mockComp;
+        ds_tizen_hwc_add_destroy_listener(mockComp->mHwc,
+            &mockComp->mHwcDestroyListener);
+
+        // add_ignore_output_transform listener
+        mockComp->mHwcNewCommitFeedbackListener.notify =
+            MockHwcCompositor::HwcNewCommitFeedbackCallback;
+        mockComp->mHwcNewCommitFeedbackListener.parent = mockComp;
+        ds_tizen_hwc_add_new_commit_feedback_listener(
+            mockComp->mHwc,
+            &mockComp->mHwcNewCommitFeedbackListener);
+    }
+
+    static void NewSurfaceCallback(struct wl_listener *listener, void *data)
+    {
+        MockHwcCompositor *mockComp =
+            reinterpret_cast<NewSurfaceListener *>(listener)->parent;
+        struct ds_surface *surface = static_cast<struct ds_surface *>(data);
+
+        ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface);
+
+        mockComp->mSurface = surface;
+
+        // del surface listener
+        mockComp->mDelSurfaceListener.notify =
+            MockHwcCompositor::DelSurfaceCallback;
+        mockComp->mDelSurfaceListener.parent = mockComp;
+        ds_surface_add_destroy_listener(surface,
+            &mockComp->mDelSurfaceListener);
+    }
+
+    static void DelSurfaceCallback(struct wl_listener *listener, void *data)
+    {
+        MockHwcCompositor *mockComp =
+            reinterpret_cast<DelSurfaceListener *>(listener)->parent;
+        struct ds_surface *surface = static_cast<struct ds_surface *>(data);
+
+        ds_inf("%s: mockComp(%p), surface(%p)", __func__, mockComp, surface);
+
+        if (ds_surface_get_wl_resource(mockComp->mSurface) ==
+            ds_surface_get_wl_resource(surface)) {
+            ds_inf("%s: surface is deleted.", __func__);
+            mockComp->bSurfaceDestroyed = true;
+        }
+    }
+
+    static void HwcDestroyCallback(struct wl_listener *listener, void *data)
+    {
+        ds_inf("%s", __func__);
+
+        MockHwcCompositor *mockComp =
+            reinterpret_cast<HwcDestroyListener *>(listener)->parent;
+
+        mockComp->bHwcDestroyed = true;
+    }
+
+    static void HwcNewCommitFeedbackCallback(struct wl_listener *listener,
+        void *data)
+    {
+        ds_inf("%s", __func__);
+
+        MockHwcCompositor *mockComp =
+            reinterpret_cast<HwcNewCommitFeedbackListener *>(listener)->parent;
+        struct ds_tizen_hwc_commit_feedback *commitFeedback =
+            static_cast<struct ds_tizen_hwc_commit_feedback *>(data);
+
+        ds_inf("%s: mockComp(%p), commitFeedback(%p)", __func__, mockComp, commitFeedback);
+
+        mockComp->mCommitFeedback = commitFeedback;
+        mockComp->mSurface =
+            ds_tizen_hwc_commit_feedback_get_surface(mockComp->mCommitFeedback);
+
+        mockComp->mHwcCommitFeedbackDestroyListener.notify =
+            MockHwcCompositor::HwcCommitFeedbackDestroyCallback;
+        mockComp->mHwcCommitFeedbackDestroyListener.parent = mockComp;
+        ds_tizen_hwc_commit_feedback_add_destroy_listener(mockComp->mCommitFeedback,
+            &mockComp->mHwcCommitFeedbackDestroyListener);
+    }
+
+    static void HwcCommitFeedbackDestroyCallback(struct wl_listener *listener,
+        void *data)
+    {
+        ds_inf("%s", __func__);
+
+        MockHwcCompositor *mockComp =
+            reinterpret_cast<HwcCommitFeedbackDestroyListener *>(listener)->parent;
+        struct ds_tizen_hwc_commit_feedback *commitFeedback =
+            static_cast<struct ds_tizen_hwc_commit_feedback *>(data);
+
+        ds_inf("%s: mockComp(%p), info(%p)", __func__, mockComp, commitFeedback);
+
+        if (mockComp->mCommitFeedback == commitFeedback) {
+            ds_inf("%s: commitFeedback is deleted.", __func__);
+        }
+    }
+
+    void SendHwcCommitFeedbackCommitted(void)
+    {
+        ds_inf("%s", __func__);
+
+        ds_tizen_hwc_commit_feedback_send_committed(mCommitFeedback);
+    }
+
+    void SendHwcCommitFeedbackDiscarded(void)
+    {
+        ds_inf("%s", __func__);
+
+        ds_tizen_hwc_commit_feedback_send_discarded(mCommitFeedback);
+    }
+
+public:
+    bool bHwcDestroyed;
+    bool bSurfaceDestroyed;
+
+private:
+    struct ds_tizen_hwc *mHwc;
+
+    struct HwcDestroyListener : ::wl_listener {
+        MockHwcCompositor *parent;
+    };
+    HwcDestroyListener mHwcDestroyListener;
+
+    struct ds_surface *mSurface;
+
+    struct NewSurfaceListener : ::wl_listener {
+        MockHwcCompositor *parent;
+    };
+    NewSurfaceListener mNewSurfaceListener;
+
+    struct DelSurfaceListener : ::wl_listener {
+        MockHwcCompositor *parent;
+    };
+    DelSurfaceListener mDelSurfaceListener;
+
+    struct ds_tizen_hwc_commit_feedback *mCommitFeedback;
+
+    struct HwcNewCommitFeedbackListener : ::wl_listener {
+        MockHwcCompositor *parent;
+    };
+    HwcNewCommitFeedbackListener mHwcNewCommitFeedbackListener;
+
+    struct HwcCommitFeedbackDestroyListener : ::wl_listener {
+        MockHwcCompositor *parent;
+    };
+    HwcCommitFeedbackDestroyListener mHwcCommitFeedbackDestroyListener;
+};
+
+class MockHwcClient : public MockClient
+{
+public:
+    MockHwcClient()
+        : bCommittedEvent(false),
+          bDiscardedEvent(false),
+          compositorRes(nullptr),
+          tizenHwcRes(nullptr)
+    {}
+    MockHwcClient(const struct wl_registry_listener *listener)
+        : MockClient(listener, this)
+    {
+        ds_inf("%s", __func__);
+    }
+    ~MockHwcClient()
+    {
+        ds_inf("%s", __func__);
+    }
+
+    void SetWlCompositor(struct wl_compositor *globalRes)
+    {
+        ds_inf("%s", __func__);
+
+        compositorRes = globalRes;
+    }
+
+    struct wl_compositor *GetWlCompositor()
+    {
+        ds_inf("%s", __func__);
+
+        return compositorRes;
+    }
+
+    void SetTizenHwc(struct tizen_hwc *resource)
+    {
+        ds_inf("%s", __func__);
+
+        tizenHwcRes = resource;
+    }
+
+    struct tizen_hwc *GetTizenHwc()
+    {
+        ds_inf("%s", __func__);
+
+        return tizenHwcRes;
+    }
+
+public:
+    bool bCommittedEvent;
+    bool bDiscardedEvent;
+private:
+    struct wl_compositor *compositorRes;
+    struct tizen_hwc *tizenHwcRes;
+};
+
+static void
+client_registry_cb_global(void *data, struct wl_registry *registry,
+    uint32_t name, const char *interface, uint32_t version)
+{
+    ds_inf("%s", __func__);
+
+    MockHwcClient *client = static_cast<MockHwcClient *>(data);
+    struct wl_compositor *compositor_res;
+    struct tizen_hwc *tizen_hwc_res;
+
+    if (!strcmp(interface, "wl_compositor")) {
+        compositor_res = (struct wl_compositor *)wl_registry_bind(registry,
+            name, &wl_compositor_interface, 1);
+        if (compositor_res == nullptr) {
+            ds_err("wl_registry_bind() failed. wl_compositor resource.");
+            return;
+        }
+        client->SetWlCompositor(compositor_res);
+    } else if (!strcmp(interface, "tizen_hwc")) {
+        tizen_hwc_res = (struct tizen_hwc *)wl_registry_bind(registry,
+            name, &tizen_hwc_interface, TIZEN_HWC_VERSION);
+        if (tizen_hwc_res == nullptr) {
+            ds_err("wl_registry_bind() failed. tizen_hwc resource.");
+            return;
+        }
+        client->SetTizenHwc(tizen_hwc_res);
+    }
+}
+
+static void
+client_registry_cb_global_remove(void *data, struct wl_registry *registry,
+    uint32_t name)
+{
+    ds_inf("%s", __func__);
+
+    MockHwcClient *client = static_cast<MockHwcClient *>(data);
+    struct wl_compositor *compositor_res = client->GetWlCompositor();
+    struct tizen_hwc *tizen_hwc_res = client->GetTizenHwc();
+
+    tizen_hwc_destroy(tizen_hwc_res);
+    wl_compositor_destroy(compositor_res);
+}
+
+static const struct wl_registry_listener registry_listener = {
+    .global = client_registry_cb_global,
+    .global_remove = client_registry_cb_global_remove
+};
+
+class HwcTest : public ::testing::Test
+{
+public:
+    void SetUp(void) override;
+    void TearDown(void) override;
+
+    MockHwcCompositor *comp;
+    MockHwcClient *client;
+    struct wl_compositor *compositorRes;
+    struct tizen_hwc *tizenHwcRes;
+    struct wl_surface *surfaceRes;
+    struct tizen_hwc_commit_feedback *tizenHwcCommitFeedbackRes;
+};
+
+static void
+tizen_hwc_commit_feedback_cb_committed(void *data,
+    struct tizen_hwc_commit_feedback *hwc_commit_feedback,
+    uint32_t serial)
+{
+    ds_inf("%s", __func__);
+
+    MockHwcClient *client = static_cast<MockHwcClient *>(data);
+
+    client->bCommittedEvent = true;
+
+    tizen_hwc_commit_feedback_destroy(hwc_commit_feedback);
+}
+
+static void
+tizen_hwc_commit_feedback_cb_discarded(void *data,
+    struct tizen_hwc_commit_feedback *hwc_commit_feedback,
+    uint32_t serial)
+{
+    ds_inf("%s", __func__);
+
+    MockHwcClient *client = static_cast<MockHwcClient *>(data);
+
+    client->bDiscardedEvent = true;
+
+    tizen_hwc_commit_feedback_destroy(hwc_commit_feedback);
+}
+
+static const struct tizen_hwc_commit_feedback_listener tizen_hwc_commit_feedback_cb_listener = {
+    .committed = tizen_hwc_commit_feedback_cb_committed,
+    .discarded = tizen_hwc_commit_feedback_cb_discarded,
+};
+
+void
+HwcTest::SetUp(void)
+{
+    ds_inf("%s", __func__);
+
+    comp = new MockHwcCompositor();
+    client = new MockHwcClient(&registry_listener);
+    compositorRes = client->GetWlCompositor();
+    tizenHwcRes = client->GetTizenHwc();
+    surfaceRes = wl_compositor_create_surface(compositorRes);
+
+    tizenHwcCommitFeedbackRes =
+        tizen_hwc_create_commit_feedback(tizenHwcRes, surfaceRes, 1);
+
+    tizen_hwc_commit_feedback_add_listener(tizenHwcCommitFeedbackRes,
+        &tizen_hwc_commit_feedback_cb_listener, client);
+
+    client->RoundTrip();
+}
+
+void
+HwcTest::TearDown(void)
+{
+    ds_inf("%s", __func__);
+
+    wl_surface_destroy(surfaceRes);
+    client->RoundTrip();
+    EXPECT_TRUE(comp->bSurfaceDestroyed);
+
+    delete client;
+    delete comp;
+}
+
+TEST_F(HwcTest, Create_P)
+{
+    EXPECT_TRUE(true);
+}
+
+TEST_F(HwcTest, Ev_TizenHwcCommitFeedbackCommitted)
+{
+    comp->SendHwcCommitFeedbackCommitted();
+    comp->Process();
+
+    client->RoundTrip();
+    EXPECT_TRUE(client->bCommittedEvent);
+}
+
+TEST_F(HwcTest, Ev_TizenHwcCommitFeedbackDiscared)
+{
+    comp->SendHwcCommitFeedbackDiscarded();
+    comp->Process();
+
+    client->RoundTrip();
+    EXPECT_TRUE(client->bDiscardedEvent);
+}