diff --git a/CMakeLists.txt b/CMakeLists.txt
index c382c9e..9f796f3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,17 @@
 cmake_minimum_required(VERSION 3.10)
 project(spark_store)
+set(EXECUTABLE_NAME sparkstore)
+
+# Begin Compilation Options
+
+# When set to ON, DTK Plugin for DDE platform will be built.
+# Note that only machines with DTK and dev packages installed can build the plugin.
+set(SPARK_BUILD_DTK_PLUGIN ON)
+
+# When set to ON, store will transmit telemetry even in Debug builds.
+set(SPARK_FORCE_TELEMETRY OFF)
+
+# End Compilation Options
 
 set(CMAKE_CXX_STANDARD 14)
 set(CMAKE_AUTOMOC ON)
@@ -7,16 +19,34 @@ set(CMAKE_AUTORCC ON)
 set(CMAKE_AUTOUIC ON)
 
 set(QT_VERSION 5)
-set(REQUIRED_LIBS Core Gui Widgets)
-set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets)
+set(REQUIRED_LIBS
+    Core
+    Gui
+    Widgets
+    Concurrent)
+set(REQUIRED_LIBS_QUALIFIED
+    Qt5::Core
+    Qt5::Gui
+    Qt5::Widgets
+    Qt5::Concurrent)
 
 include_directories(inc)
+include_directories(plugin)
 
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
+set(PRE_CONFIGURE_FILE "src/gitver.cpp.in")
+set(POST_CONFIGURE_FILE "${CMAKE_CURRENT_BINARY_DIR}/src/gitver.cpp")
+include(cmake/git_watcher.cmake)
+add_library(gitver STATIC ${POST_CONFIGURE_FILE})
+#target_include_directories(gitver PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+add_dependencies(gitver check_git)
+
+
 set(SOURCE_FILES
     src/main.cpp
     resource/resource.qrc
+    inc/gitver.h
     gui/spkwindow.cpp
     inc/spkwindow.h
     gui/spktitlebar.cpp
@@ -24,6 +54,7 @@ set(SOURCE_FILES
     inc/spkui_general.h
     gui/spkui_general.cpp
     inc/deepinplatform.h
+    inc/dtk/spkdtkplugin.h
     src/spklogging.cpp
     inc/spklogging.h
     inc/spkuimsg.h
@@ -33,6 +64,8 @@ set(SOURCE_FILES
     gui/spkmsgbox.cpp
     inc/spkdialog.h
     gui/spkdialog.cpp
+    inc/spkabout.h
+    gui/spkabout.cpp
     inc/spkstore.h
     src/spkstore.cpp
     gui/spkmainwindow.cpp
@@ -40,7 +73,7 @@ set(SOURCE_FILES
     gui/spkpageqsstest.cpp
     gui/spkcategoryselector.cpp
     inc/spkconfig.h
-    src/telemetry/collectid.cpp
+    inc/telemetry/collectid.h
     )
 
 include(cmake/FindLibNotify.cmake)
@@ -50,6 +83,7 @@ include(cmake/FindGdk3.cmake)
 include_directories(${GLIB_INCLUDE_DIRS})
 include_directories(${GDK3_INCLUDE_DIRS})
 set(LIBLINKING ${LIBLINKING}
+    gitver
     ${LIBNOTIFY_LIBRARIES}
     ${GLIB_LIBRARIES}
     ${GDK3_LIBRARIES}
@@ -59,7 +93,15 @@ set(LIBLINKING ${LIBLINKING}
 add_compile_options(-g)
 add_link_options(-rdynamic)
 
-add_executable(${PROJECT_NAME} ${SOURCE_FILES})
-
+# Find Qt before adding other build targets
 find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)
-target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED} ${LIBLINKING})
+
+add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES})
+
+if(SPARK_BUILD_DTK_PLUGIN)
+    add_subdirectory(plugin/dtkplugin)
+endif()
+
+target_link_libraries(${EXECUTABLE_NAME} ${REQUIRED_LIBS_QUALIFIED} ${LIBLINKING})
+
+install(TARGETS ${EXECUTABLE_NAME} RUNTIME DESTINATION in)
diff --git a/cmake/git_watcher.cmake b/cmake/git_watcher.cmake
new file mode 100644
index 0000000..dd8a7bc
--- /dev/null
+++ b/cmake/git_watcher.cmake
@@ -0,0 +1,335 @@
+# git_watcher.cmake
+# https://raw.githubusercontent.com/andrew-hardin/cmake-git-version-tracking/master/git_watcher.cmake
+#
+# Released under the MIT License.
+# https://raw.githubusercontent.com/andrew-hardin/cmake-git-version-tracking/master/LICENSE
+
+
+# This file defines a target that monitors the state of a git repo.
+# If the state changes (e.g. a commit is made), then a file gets reconfigured.
+# Here are the primary variables that control script behavior:
+#
+#   PRE_CONFIGURE_FILE (REQUIRED)
+#   -- The path to the file that'll be configured.
+#
+#   POST_CONFIGURE_FILE (REQUIRED)
+#   -- The path to the configured PRE_CONFIGURE_FILE.
+#
+#   GIT_STATE_FILE (OPTIONAL)
+#   -- The path to the file used to store the previous build's git state.
+#      Defaults to the current binary directory.
+#
+#   GIT_WORKING_DIR (OPTIONAL)
+#   -- The directory from which git commands will be run.
+#      Defaults to the directory with the top level CMakeLists.txt.
+#
+#   GIT_EXECUTABLE (OPTIONAL)
+#   -- The path to the git executable. It'll automatically be set if the
+#      user doesn't supply a path.
+#
+#   GIT_FAIL_IF_NONZERO_EXIT (optional)
+#   -- Raise a FATAL_ERROR if any of the git commands return a non-zero
+#      exit code. This is set to TRUE by default. You can set this to FALSE
+#      if you'd like the build to continue even if a git command fails.
+#
+# DESIGN
+#   - This script was designed similar to a Python application
+#     with a Main() function. I wanted to keep it compact to
+#     simplify "copy + paste" usage.
+#
+#   - This script is invoked under two CMake contexts:
+#       1. Configure time (when build files are created).
+#       2. Build time (called via CMake -P).
+#     The first invocation is what registers the script to
+#     be executed at build time.
+#
+# MODIFICATIONS
+#   You may wish to track other git properties like when the last
+#   commit was made. There are two sections you need to modify,
+#   and they're tagged with a ">>>" header.
+
+# Short hand for converting paths to absolute.
+macro(PATH_TO_ABSOLUTE var_name)
+    get_filename_component(${var_name} "${${var_name}}" ABSOLUTE)
+endmacro()
+
+# Check that a required variable is set.
+macro(CHECK_REQUIRED_VARIABLE var_name)
+    if(NOT DEFINED ${var_name})
+        message(FATAL_ERROR "The \"${var_name}\" variable must be defined.")
+    endif()
+    PATH_TO_ABSOLUTE(${var_name})
+endmacro()
+
+# Check that an optional variable is set, or, set it to a default value.
+macro(CHECK_OPTIONAL_VARIABLE_NOPATH var_name default_value)
+    if(NOT DEFINED ${var_name})
+        set(${var_name} ${default_value})
+    endif()
+endmacro()
+
+# Check that an optional variable is set, or, set it to a default value.
+# Also converts that path to an abspath.
+macro(CHECK_OPTIONAL_VARIABLE var_name default_value)
+    CHECK_OPTIONAL_VARIABLE_NOPATH(${var_name} ${default_value})
+    PATH_TO_ABSOLUTE(${var_name})
+endmacro()
+
+CHECK_REQUIRED_VARIABLE(PRE_CONFIGURE_FILE)
+CHECK_REQUIRED_VARIABLE(POST_CONFIGURE_FILE)
+CHECK_OPTIONAL_VARIABLE(GIT_STATE_FILE "${CMAKE_BINARY_DIR}/git-state-hash")
+CHECK_OPTIONAL_VARIABLE(GIT_WORKING_DIR "${CMAKE_SOURCE_DIR}")
+CHECK_OPTIONAL_VARIABLE_NOPATH(GIT_FAIL_IF_NONZERO_EXIT TRUE)
+
+# Check the optional git variable.
+# If it's not set, we'll try to find it using the CMake packaging system.
+if(NOT DEFINED GIT_EXECUTABLE)
+    find_package(Git QUIET REQUIRED)
+endif()
+CHECK_REQUIRED_VARIABLE(GIT_EXECUTABLE)
+
+
+set(_state_variable_names
+    GIT_RETRIEVED_STATE
+    GIT_HEAD_SHA1
+    GIT_IS_DIRTY
+    GIT_AUTHOR_NAME
+    GIT_AUTHOR_EMAIL
+    GIT_COMMIT_DATE_ISO8601
+    GIT_COMMIT_SUBJECT
+    GIT_COMMIT_BODY
+    GIT_DESCRIBE
+    # >>>
+    # 1. Add the name of the additional git variable you're interested in monitoring
+    #    to this list.
+)
+
+
+
+# Macro: RunGitCommand
+# Description: short-hand macro for calling a git function. Outputs are the
+#              "exit_code" and "output" variables.
+macro(RunGitCommand)
+    execute_process(COMMAND
+        "${GIT_EXECUTABLE}" ${ARGV}
+        WORKING_DIRECTORY "${_working_dir}"
+        RESULT_VARIABLE exit_code
+        OUTPUT_VARIABLE output
+        ERROR_VARIABLE stderr
+        OUTPUT_STRIP_TRAILING_WHITESPACE)
+    if(NOT exit_code EQUAL 0)
+        set(ENV{GIT_RETRIEVED_STATE} "false")
+
+        # Issue 26: git info not properly set
+        #
+        # Check if we should fail if any of the exit codes are non-zero.
+        if(GIT_FAIL_IF_NONZERO_EXIT)
+            string(REPLACE ";" " " args_with_spaces "${ARGV}")
+            message(FATAL_ERROR "${stderr} (${GIT_EXECUTABLE} ${args_with_spaces})")
+        endif()
+    endif()
+endmacro()
+
+
+
+# Function: GetGitState
+# Description: gets the current state of the git repo.
+# Args:
+#   _working_dir (in)  string; the directory from which git commands will be executed.
+function(GetGitState _working_dir)
+
+    # This is an error code that'll be set to FALSE if the
+    # RunGitCommand ever returns a non-zero exit code.
+    set(ENV{GIT_RETRIEVED_STATE} "true")
+
+    # Get whether or not the working tree is dirty.
+    RunGitCommand(status --porcelain)
+    if(NOT exit_code EQUAL 0)
+        set(ENV{GIT_IS_DIRTY} "false")
+    else()
+        if(NOT "${output}" STREQUAL "")
+            set(ENV{GIT_IS_DIRTY} "true")
+        else()
+            set(ENV{GIT_IS_DIRTY} "false")
+        endif()
+    endif()
+
+    # There's a long list of attributes grabbed from git show.
+    set(object HEAD)
+    RunGitCommand(show -s "--format=%H" ${object})
+    if(exit_code EQUAL 0)
+        set(ENV{GIT_HEAD_SHA1} ${output})
+    endif()
+
+    RunGitCommand(show -s "--format=%an" ${object})
+    if(exit_code EQUAL 0)
+        set(ENV{GIT_AUTHOR_NAME} "${output}")
+    endif()
+
+    RunGitCommand(show -s "--format=%ae" ${object})
+    if(exit_code EQUAL 0)
+        set(ENV{GIT_AUTHOR_EMAIL} "${output}")
+    endif()
+
+    RunGitCommand(show -s "--format=%ci" ${object})
+    if(exit_code EQUAL 0)
+        set(ENV{GIT_COMMIT_DATE_ISO8601} "${output}")
+    endif()
+
+    RunGitCommand(show -s "--format=%s" ${object})
+    if(exit_code EQUAL 0)
+        # Escape quotes
+        string(REPLACE "\"" "\\\"" output "${output}")
+        set(ENV{GIT_COMMIT_SUBJECT} "${output}")
+    endif()
+
+    RunGitCommand(show -s "--format=%b" ${object})
+    if(exit_code EQUAL 0)
+        if(output)
+            # Escape quotes
+            string(REPLACE "\"" "\\\"" output "${output}")
+            # Escape line breaks in the commit message.
+            string(REPLACE "\r\n" "\\r\\n\\\r\n" safe "${output}")
+            if(safe STREQUAL output)
+                # Didn't have windows lines - try unix lines.
+                string(REPLACE "\n" "\\n\\\n" safe "${output}")
+            endif()
+        else()
+            # There was no commit body - set the safe string to empty.
+            set(safe "")
+        endif()
+        set(ENV{GIT_COMMIT_BODY} "\"${safe}\"")
+    else()
+        set(ENV{GIT_COMMIT_BODY} "\"\"") # empty string.
+    endif()
+
+    # Get output of git describe
+    RunGitCommand(describe --tags ${object})
+    if(NOT exit_code EQUAL 0)
+        set(ENV{GIT_DESCRIBE} "unknown")
+    else()
+        set(ENV{GIT_DESCRIBE} "${output}")
+    endif()
+
+    # >>>
+    # 2. Additional git properties can be added here via the
+    #    "execute_process()" command. Be sure to set them in
+    #    the environment using the same variable name you added
+    #    to the "_state_variable_names" list.
+
+endfunction()
+
+
+
+# Function: GitStateChangedAction
+# Description: this function is executed when the state of the git
+#              repository changes (e.g. a commit is made).
+function(GitStateChangedAction)
+    foreach(var_name ${_state_variable_names})
+        set(${var_name} $ENV{${var_name}})
+    endforeach()
+    configure_file("${PRE_CONFIGURE_FILE}" "${POST_CONFIGURE_FILE}" @ONLY)
+endfunction()
+
+
+
+# Function: HashGitState
+# Description: loop through the git state variables and compute a unique hash.
+# Args:
+#   _state (out)  string; a hash computed from the current git state.
+function(HashGitState _state)
+    set(ans "")
+    foreach(var_name ${_state_variable_names})
+        string(SHA256 ans "${ans}$ENV{${var_name}}")
+    endforeach()
+    set(${_state} ${ans} PARENT_SCOPE)
+endfunction()
+
+
+
+# Function: CheckGit
+# Description: check if the git repo has changed. If so, update the state file.
+# Args:
+#   _working_dir    (in)  string; the directory from which git commands will be ran.
+#   _state_changed (out)    bool; whether or no the state of the repo has changed.
+function(CheckGit _working_dir _state_changed)
+
+    # Get the current state of the repo.
+    GetGitState("${_working_dir}")
+
+    # Convert that state into a hash that we can compare against
+    # the hash stored on-disk.
+    HashGitState(state)
+
+    # Issue 14: post-configure file isn't being regenerated.
+    #
+    # Update the state to include the SHA256 for the pre-configure file.
+    # This forces the post-configure file to be regenerated if the
+    # pre-configure file has changed.
+    file(SHA256 ${PRE_CONFIGURE_FILE} preconfig_hash)
+    string(SHA256 state "${preconfig_hash}${state}")
+
+    # Check if the state has changed compared to the backup on disk.
+    if(EXISTS "${GIT_STATE_FILE}")
+        file(READ "${GIT_STATE_FILE}" OLD_HEAD_CONTENTS)
+        if(OLD_HEAD_CONTENTS STREQUAL "${state}")
+            # State didn't change.
+            set(${_state_changed} "false" PARENT_SCOPE)
+            return()
+        endif()
+    endif()
+
+    # The state has changed.
+    # We need to update the state file on disk.
+    # Future builds will compare their state to this file.
+    file(WRITE "${GIT_STATE_FILE}" "${state}")
+    set(${_state_changed} "true" PARENT_SCOPE)
+endfunction()
+
+
+
+# Function: SetupGitMonitoring
+# Description: this function sets up custom commands that make the build system
+#              check the state of git before every build. If the state has
+#              changed, then a file is configured.
+function(SetupGitMonitoring)
+    add_custom_target(check_git
+        ALL
+        DEPENDS ${PRE_CONFIGURE_FILE}
+        BYPRODUCTS
+            ${POST_CONFIGURE_FILE}
+            ${GIT_STATE_FILE}
+        COMMENT "Checking the git repository for changes..."
+        COMMAND
+            ${CMAKE_COMMAND}
+            -D_BUILD_TIME_CHECK_GIT=TRUE
+            -DGIT_WORKING_DIR=${GIT_WORKING_DIR}
+            -DGIT_EXECUTABLE=${GIT_EXECUTABLE}
+            -DGIT_STATE_FILE=${GIT_STATE_FILE}
+            -DPRE_CONFIGURE_FILE=${PRE_CONFIGURE_FILE}
+            -DPOST_CONFIGURE_FILE=${POST_CONFIGURE_FILE}
+            -DGIT_FAIL_IF_NONZERO_EXIT=${GIT_FAIL_IF_NONZERO_EXIT}
+            -P "${CMAKE_CURRENT_LIST_FILE}")
+endfunction()
+
+
+
+# Function: Main
+# Description: primary entry-point to the script. Functions are selected based
+#              on whether it's configure or build time.
+function(Main)
+    if(_BUILD_TIME_CHECK_GIT)
+        # Check if the repo has changed.
+        # If so, run the change action.
+        CheckGit("${GIT_WORKING_DIR}" changed)
+        if(changed OR NOT EXISTS "${POST_CONFIGURE_FILE}")
+            GitStateChangedAction()
+        endif()
+    else()
+        # >> Executes at configure time.
+        SetupGitMonitoring()
+    endif()
+endfunction()
+
+# And off we go...
+Main()
diff --git a/gui/spkabout.cpp b/gui/spkabout.cpp
new file mode 100644
index 0000000..45ebdb9
--- /dev/null
+++ b/gui/spkabout.cpp
@@ -0,0 +1,19 @@
+
+#include "spkabout.h"
+
+SpkAbout::SpkAbout(QWidget *parent) : SpkDialog(parent)
+{
+  setWindowModality(Qt::ApplicationModal);
+}
+
+void SpkAbout::Show()
+{
+  SpkAbout *b = new SpkAbout;
+
+  b->GetTitleBar()->SetOperationButton(SpkTitleBar::OperationButton::Close);
+
+  // TODO: Waiting for qygw
+
+  b->Exec();
+  delete b;
+}
diff --git a/gui/spkdialog.cpp b/gui/spkdialog.cpp
index c1c4862..fee9810 100644
--- a/gui/spkdialog.cpp
+++ b/gui/spkdialog.cpp
@@ -3,7 +3,7 @@
 #include "spkdialog.h"
 
 SpkDialog::SpkDialog(QWidget *parent) :
-  SpkWindow(parent)
+  SpkWindow(parent, Qt::Dialog)
 {
   mDialogWidget = new QWidget;
   mMainVLay = new QVBoxLayout(mDialogWidget);
@@ -19,7 +19,8 @@ SpkDialog::SpkDialog(QWidget *parent) :
 
   SetCentralWidget(mDialogWidget);
 
-  connect(mBtnGroup, QOverload<int>::of(&QButtonGroup::idClicked),
+  // idClicked is not available on platforms we support, shouldn't change it
+  connect(mBtnGroup, QOverload<int>::of(&QButtonGroup::buttonClicked),
           this, &SpkDialog::ButtonPressed);
   connect(this, &SpkWindow::Closed, this, &SpkDialog::ForceClose);
 }
diff --git a/gui/spkmsgbox.cpp b/gui/spkmsgbox.cpp
index c78eb94..222fc8b 100644
--- a/gui/spkmsgbox.cpp
+++ b/gui/spkmsgbox.cpp
@@ -5,21 +5,23 @@
 #include <QScreen>
 #include "spkui_general.h"
 #include "spkmsgbox.h"
+#include "spkstore.h"
 
 // Suppress unwanted Clazy check warnings
 // clazy:excludeall=connect-3arg-lambda,lambda-in-connect
 
-const QSize SpkMsgBox::IconSize; // I don't know why I need it
+const QSize SpkMsgBox::IconSize; // I don't know why I need it, compiler wants that
 
-SpkMsgBox::SpkMsgBox()
+SpkMsgBox::SpkMsgBox(QWidget *parent)
 {
+  Q_UNUSED(parent);
   setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
 }
 
 int SpkMsgBox::StaticExec(QString msg, QString title, QMessageBox::Icon icon,
                           QMessageBox::StandardButtons buttons, QString extra)
 {
-  SpkMsgBox *b = new SpkMsgBox;
+  SpkMsgBox *b = new SpkMsgBox(SpkStore::Instance->GetRootWindow());
   QWidget *wMsgWidget = new QWidget;
   QHBoxLayout *wMsg = new QHBoxLayout(wMsgWidget);
   QPushButton *wExpandBtn;
@@ -53,7 +55,7 @@ int SpkMsgBox::StaticExec(QString msg, QString title, QMessageBox::Icon icon,
     wMsg->addWidget(wIcon);
   }
   wMsgText->setText(msg);
-  wMsgText->setAlignment(Qt::AlignCenter);
+  wMsgText->setAlignment(Qt::AlignLeft);
   wMsg->addWidget(wMsgText);
   wMsg->setSpacing(10);
   wMsgWidget->setLayout(wMsg);
@@ -95,7 +97,8 @@ int SpkMsgBox::StaticExec(QString msg, QString title, QMessageBox::Icon icon,
   InitialHeight = b->minimumSizeHint().height();
   auto pos = (SpkUi::PrimaryScreenSize - b->sizeHint()) / 2;
   b->move(pos.width(), pos.height());
-  b->setWindowModality(Qt::WindowModal);
+  b->setWindowModality(Qt::ApplicationModal);
+  b->setFixedSize(b->sizeHint());
 
   auto r = b->Exec();
   if(r != -1)
diff --git a/gui/spkui_general.cpp b/gui/spkui_general.cpp
index 1b8dd48..9ac7933 100644
--- a/gui/spkui_general.cpp
+++ b/gui/spkui_general.cpp
@@ -11,10 +11,11 @@
 #include <QDateTime>
 #include <QDebug>
 #include <QScreen>
+#include <QPluginLoader>
+#include <QStyleFactory>
 #include <csignal>
-#include <dlfcn.h>
 #include <execinfo.h>
-#include "deepinplatform.h"
+
 #include "spkui_general.h"
 #include "spkmsgbox.h"
 #include "spklogging.h"
@@ -24,6 +25,8 @@ namespace SpkUi
   QString StylesheetLight, StylesheetDark, *CurrentStylesheet = &StylesheetLight;
   QColor ColorLine, ColorBack;
   QSize PrimaryScreenSize;
+  SpkDtkPlugin *DtkPlugin = nullptr;
+
   namespace Priv
   {
     bool CrashHandlerActivated;
@@ -50,9 +53,10 @@ namespace SpkUi
     signal(SIGFPE, SpkUi::CrashSignalHandler);
 
     // Prepare theme following for DDE
-    PrepareForDeepinDesktop();
+    if(CheckIsDeepinDesktop())
+      PrepareForDeepinDesktop();
 
-    // Data initialization
+    // Misc data initialization
     PrimaryScreenSize = QGuiApplication::primaryScreen()->size();
   }
 
@@ -64,6 +68,7 @@ namespace SpkUi
   bool CheckIsDeepinDesktop()
   {
     QString Desktop(getenv("XDG_CURRENT_DESKTOP"));
+    // This method of checking is from DTK source code.
     if(Desktop.contains("deepin", Qt::CaseInsensitive) ||
        Desktop.contains("tablet", Qt::CaseInsensitive))
       return true;
@@ -73,7 +78,30 @@ namespace SpkUi
 
   void PrepareForDeepinDesktop()
   {
+#ifndef NDEBUG
+    // Normally it's installed to system library path
+    qApp->addLibraryPath(qApp->applicationDirPath() + "/plugin/dtkplugin");
+#endif
+    QPluginLoader p("libspkdtkplugin");
+    if(p.load())
+    {
+      auto i = qobject_cast<SpkDtkPlugin*>(p.instance());
+      if(i)
+        DtkPlugin = i;
+    }
 
+    // FIXME: Chameleon style kept adding unwanted blue focus indication border
+    // to widgets that shouldn't have borders.
+    // We need to eliminate this irritating problem.
+    auto styles = QStyleFactory::keys();
+    styles.removeAll("chameleon");
+    if(styles.contains("Fusion"))
+      qApp->setStyle(QStyleFactory::create("Fusion"));
+    else if(styles.size()) // What? This shouldn't happen.
+      qApp->setStyle(QStyleFactory::create(styles[0]));
+    else // Duh...
+      sWarn(QObject::tr("Cannot find styles other than 'chameleon'! You may see widgets "
+                        "with unwanted blue borders."));
   }
 
   void SetGlobalStyle(const SpkUiStyle aStyle)
diff --git a/gui/spkwindow.cpp b/gui/spkwindow.cpp
index 5f11741..553a2e2 100644
--- a/gui/spkwindow.cpp
+++ b/gui/spkwindow.cpp
@@ -12,9 +12,12 @@
 
 #include <QDebug>
 
-SpkWindow::SpkWindow(QWidget *parent) : QMainWindow(parent)
+SpkWindow::SpkWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags)
 {
-  setWindowFlags(Qt::FramelessWindowHint); // Remove default title bar, we'll have custom title bar
+  if(SpkUi::DtkPlugin)
+    SpkUi::DtkPlugin->addWindow(this, parent); // Register window to DXcb so we got Deepin
+  else
+    setWindowFlags(Qt::FramelessWindowHint); // Remove default title bar
   mCornerRadius = 5;
   mUserCentralWidget = nullptr;
   mResizable = true;
diff --git a/inc/deepinplatform.h b/inc/deepinplatform.h
index 8abc117..21a0915 100644
--- a/inc/deepinplatform.h
+++ b/inc/deepinplatform.h
@@ -2,8 +2,7 @@
 // Created by rigoligo on 2021/5/8.
 //
 
-#ifndef _DEEPINPLATFORM_H_
-#define _DEEPINPLATFORM_H_
+#pragma once
 
 // Stub
 
diff --git a/inc/dtk/spkdtkplugin.h b/inc/dtk/spkdtkplugin.h
new file mode 100644
index 0000000..8929b73
--- /dev/null
+++ b/inc/dtk/spkdtkplugin.h
@@ -0,0 +1,15 @@
+
+#pragma once
+
+#include <QWidget>
+
+class SpkDtkPlugin
+{
+  public:
+    virtual ~SpkDtkPlugin() = default;
+
+    virtual void addWindow(QWidget* w, QObject* parent) = 0;
+};
+QT_BEGIN_NAMESPACE
+Q_DECLARE_INTERFACE(SpkDtkPlugin, "org.spark-store.client.dtkplugin")
+QT_END_NAMESPACE
diff --git a/inc/gitver.h b/inc/gitver.h
new file mode 100644
index 0000000..15e192e
--- /dev/null
+++ b/inc/gitver.h
@@ -0,0 +1,8 @@
+
+#pragma once
+
+namespace GitVer
+{
+  const char *DescribeTags();
+  const char *CommitDate();
+}
diff --git a/inc/spkabout.h b/inc/spkabout.h
new file mode 100644
index 0000000..7565e1a
--- /dev/null
+++ b/inc/spkabout.h
@@ -0,0 +1,11 @@
+
+#pragma once
+
+#include "spkdialog.h"
+
+class SpkAbout : public SpkDialog
+{
+  public:
+    SpkAbout(QWidget* parent = nullptr);
+    void Show();
+};
diff --git a/inc/spkdialog.h b/inc/spkdialog.h
index 97cf46b..174e185 100644
--- a/inc/spkdialog.h
+++ b/inc/spkdialog.h
@@ -1,5 +1,4 @@
-#ifndef SPKDIALOG_H
-#define SPKDIALOG_H
+#pragma once
 
 #include <QList>
 #include <QButtonGroup>
@@ -33,5 +32,3 @@ class SpkDialog : public SpkWindow
     QButtonGroup *mBtnGroup;
     QList<QWidget*> mWidgetsList, mParentsList;
 };
-
-#endif // SPKDIALOG_H
diff --git a/inc/spklogging.h b/inc/spklogging.h
index dee4625..fe5ed38 100644
--- a/inc/spklogging.h
+++ b/inc/spklogging.h
@@ -3,8 +3,7 @@
  * @brief Simple logging for Spark Store
  */
 
-#ifndef _SPKLOGGING_H_
-#define _SPKLOGGING_H_
+#pragma once
 
 #pragma push_macro("signals")
 #undef signals
@@ -43,5 +42,3 @@ class SpkLogger
 #define sErrPop(X) SpkLogger::GetInstance()->Error(X,true)
 #define sCritical(X) SpkLogger::GetInstance()->Critical(X)
 #define sNotify(X)
-
-#endif //_SPKLOGGING_H_
diff --git a/inc/spkmainwindow.h b/inc/spkmainwindow.h
index 90ccffc..2f6d91f 100644
--- a/inc/spkmainwindow.h
+++ b/inc/spkmainwindow.h
@@ -2,8 +2,7 @@
 // Created by rigoligo on 2021/5/9.
 //
 
-#ifndef _SPKMAINWINDOW_H_
-#define _SPKMAINWINDOW_H_
+#pragma once
 
 #include "spkwindow.h"
 #include <QTextEdit>
@@ -74,5 +73,3 @@ class SpkMainWindow : public SpkWindow
     
     void PopulateCategories(QJsonObject);
 };
-
-#endif //_SPKMAINWINDOW_H_
diff --git a/inc/spkmsgbox.h b/inc/spkmsgbox.h
index 373367a..c899b74 100644
--- a/inc/spkmsgbox.h
+++ b/inc/spkmsgbox.h
@@ -1,5 +1,4 @@
-#ifndef SPKMSGBOX_H
-#define SPKMSGBOX_H
+#pragma once
 
 #include <QStyle>
 #include <QMessageBox>
@@ -9,7 +8,7 @@ class SpkMsgBox : public SpkDialog
 {
     Q_OBJECT
   public:
-    SpkMsgBox();
+    SpkMsgBox(QWidget *parent = nullptr);
     static int StaticExec(QString msg, QString title, QMessageBox::Icon = QMessageBox::NoIcon,
                            QMessageBox::StandardButtons = QMessageBox::Ok, QString extra = "");
   private:
@@ -18,5 +17,3 @@ class SpkMsgBox : public SpkDialog
     static constexpr QSize IconSize {48, 48};
     static constexpr int Margin = 10;
 };
-
-#endif // SPKMSGBOX_H
diff --git a/inc/spkpageqsstest.h b/inc/spkpageqsstest.h
index ab842a9..311bf4c 100644
--- a/inc/spkpageqsstest.h
+++ b/inc/spkpageqsstest.h
@@ -1,5 +1,4 @@
-#ifndef SPKPAGEQSSTEST_H
-#define SPKPAGEQSSTEST_H
+#pragma once
 
 #include <QWidget>
 #include <QSplitter>
@@ -40,5 +39,3 @@ namespace SpkUi
       void FetchStylesheet();
   };
 }
-
-#endif // SPKPAGEQSSTEST_H
diff --git a/inc/spkstore.h b/inc/spkstore.h
index 130f0ca..85d8d70 100644
--- a/inc/spkstore.h
+++ b/inc/spkstore.h
@@ -2,8 +2,7 @@
 // Created by rigoligo on 2021/5/12.
 //
 
-#ifndef _SPKSTORE_H_
-#define _SPKSTORE_H_
+#pragma once
 
 #include <QMap>
 #include <QString>
@@ -20,15 +19,14 @@ class SpkStore : public QObject
 {
     Q_OBJECT
   public:
-    SpkStore *Instance;
+    static SpkStore *Instance;
     SpkStore(bool aCli, QString &aLogPath);
     ~SpkStore();
 
+    SpkMainWindow* GetRootWindow() { return mMainWindow; };
 
   private:
     SpkLogger *mLogger;
-    SpkMainWindow *mMainWindow;
+    SpkMainWindow *mMainWindow = nullptr;
 
 };
-
-#endif //_SPKSTORE_H_
diff --git a/inc/spktitlebar.h b/inc/spktitlebar.h
index 37a4096..d0e3770 100644
--- a/inc/spktitlebar.h
+++ b/inc/spktitlebar.h
@@ -2,8 +2,7 @@
 // Created by rigoligo on 2021/5/8.
 //
 
-#ifndef _SPKTITLEBAR_H_
-#define _SPKTITLEBAR_H_
+#pragma once
 
 #include <QHBoxLayout>
 #include <QLabel>
@@ -70,5 +69,3 @@ class SpkTitleBar : public QWidget
     void MinimizeWindow();
     void MaximizeRestoreWindow();
 };
-
-#endif //_SPKTITLEBAR_H_
diff --git a/inc/spkui_general.h b/inc/spkui_general.h
index ee38667..3f9da65 100644
--- a/inc/spkui_general.h
+++ b/inc/spkui_general.h
@@ -2,13 +2,15 @@
 // Created by rigoligo on 2021/5/8.
 //
 
-#ifndef _SPKUI_GENERAL_H_
-#define _SPKUI_GENERAL_H_
+#pragma once
 
+#include <QWidget>
 #include <QString>
 #include <QSize>
 #include <QColor>
 
+#include "dtkplugin/spkdtkplugin.h"
+
 namespace SpkUi
 {
   constexpr int StackTraceArraySize = 64;
@@ -17,6 +19,7 @@ namespace SpkUi
   extern QString StylesheetLight, StylesheetDark, *CurrentStylesheet;
   extern QColor ColorLine, ColorBack;
   extern QSize PrimaryScreenSize;
+  extern SpkDtkPlugin *DtkPlugin;
   enum SpkUiStyle { Light, Dark };
   enum SpkButtonStyle { Normal = 0, Recommend, Warn };
 
@@ -34,5 +37,3 @@ namespace SpkUi
 
   void SetGlobalStyle(SpkUiStyle);
 };
-
-#endif //_SPKUI_GENERAL_H_
diff --git a/inc/spkuimsg.h b/inc/spkuimsg.h
index 97cf966..b0cc640 100644
--- a/inc/spkuimsg.h
+++ b/inc/spkuimsg.h
@@ -2,8 +2,7 @@
 // Created by rigoligo on 2021/5/9.
 //
 
-#ifndef _SPKUIMSG_H_
-#define _SPKUIMSG_H_
+#pragma once
 
 #pragma push_macro("signals")
 #undef signals
@@ -31,5 +30,3 @@ class SpkUiMessage : public QObject
   signals:
 
 };
-
-#endif //_SPKUIMSG_H_
diff --git a/inc/spkwindow.h b/inc/spkwindow.h
index 8ef64a1..3e30ac6 100644
--- a/inc/spkwindow.h
+++ b/inc/spkwindow.h
@@ -2,8 +2,7 @@
 // Created by rigoligo on 2021/5/8.
 //
 
-#ifndef _SPKWINDOW_H_
-#define _SPKWINDOW_H_
+#pragma once
 
 #include <QMainWindow>
 #include <QFrame>
@@ -32,7 +31,7 @@ class SpkWindow : public QMainWindow
     static constexpr int BorderWidth = 10;
 
   public:
-    SpkWindow(QWidget *parent = nullptr);
+    SpkWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
     ~SpkWindow() override;
     void SetCentralWidget(QWidget *);
     bool GetUseTitleBar();
@@ -63,5 +62,3 @@ class SpkWindow : public QMainWindow
   private:
     void PopulateUi();
 };
-
-#endif //_SPKWINDOW_H_
diff --git a/inc/telemetry/collectid.h b/inc/telemetry/collectid.h
new file mode 100644
index 0000000..32465f3
--- /dev/null
+++ b/inc/telemetry/collectid.h
@@ -0,0 +1,20 @@
+
+#pragma once
+
+#include <QObject>
+#include <QFile>
+
+namespace SpkTelemetry
+{
+  void FunCollectId()
+  {
+#if !defined(NDEBUG) || !defined(SPARK_FORCE_TELEMETRY) // Debug builds shouldn't transmit telemetry data unless asked for
+    return;
+#else
+    QFile idFile("/etc/machine-id");
+    if(!idFile.open(QFile::ReadOnly))
+      return;
+    auto machineId = idFile.readAll();
+#endif
+  }
+}
diff --git a/plugin/dtkplugin/CMakeLists.txt b/plugin/dtkplugin/CMakeLists.txt
new file mode 100644
index 0000000..32fffb2
--- /dev/null
+++ b/plugin/dtkplugin/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(PLUGIN_SOURCE
+	spkdtkplugin.h
+	spkdtkplugin_impl.h
+        spkdtkplugin.cpp)
+
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(dtkwidget REQUIRED IMPORTED_TARGET dtkwidget)
+pkg_check_modules(dtkgui REQUIRED IMPORTED_TARGET dtkgui)
+pkg_check_modules(dtkcore REQUIRED IMPORTED_TARGET dtkcore)
+add_library(spkdtkplugin SHARED ${PLUGIN_SOURCE})
+
+target_link_libraries(spkdtkplugin PkgConfig::dtkwidget PkgConfig::dtkgui PkgConfig::dtkcore ${REQUIRED_LIBS_QUALIFIED})
+
+install(TARGETS spkdtkplugin LIBRARY DESTINATION lib)
diff --git a/plugin/dtkplugin/spkdtkplugin.cpp b/plugin/dtkplugin/spkdtkplugin.cpp
new file mode 100644
index 0000000..d9891fe
--- /dev/null
+++ b/plugin/dtkplugin/spkdtkplugin.cpp
@@ -0,0 +1,11 @@
+#include <DGuiApplicationHelper>
+#include <DPlatformWindowHandle>
+#include "spkdtkplugin_impl.h"
+
+using Dtk::Widget::DPlatformWindowHandle;
+
+void SpkDtkPluginImpl::addWindow(QWidget *w, QObject *parent)
+{
+  DPlatformWindowHandle *h = new DPlatformWindowHandle(w, parent);
+  Q_UNUSED(h);
+}
diff --git a/plugin/dtkplugin/spkdtkplugin.h b/plugin/dtkplugin/spkdtkplugin.h
new file mode 100644
index 0000000..8929b73
--- /dev/null
+++ b/plugin/dtkplugin/spkdtkplugin.h
@@ -0,0 +1,15 @@
+
+#pragma once
+
+#include <QWidget>
+
+class SpkDtkPlugin
+{
+  public:
+    virtual ~SpkDtkPlugin() = default;
+
+    virtual void addWindow(QWidget* w, QObject* parent) = 0;
+};
+QT_BEGIN_NAMESPACE
+Q_DECLARE_INTERFACE(SpkDtkPlugin, "org.spark-store.client.dtkplugin")
+QT_END_NAMESPACE
diff --git a/plugin/dtkplugin/spkdtkplugin_impl.h b/plugin/dtkplugin/spkdtkplugin_impl.h
new file mode 100644
index 0000000..9bbe7aa
--- /dev/null
+++ b/plugin/dtkplugin/spkdtkplugin_impl.h
@@ -0,0 +1,16 @@
+
+#pragma once
+
+#include <QWidget>
+#include <QtPlugin>
+#include "spkdtkplugin.h"
+
+class SpkDtkPluginImpl : public QObject, SpkDtkPlugin
+{
+  Q_OBJECT
+  Q_PLUGIN_METADATA(IID "org.spark-store.client.dtkplugin")
+  Q_INTERFACES(SpkDtkPlugin)
+
+  public:
+    void addWindow(QWidget* w, QObject* parent) override;
+};
diff --git a/src/gitver.cpp.in b/src/gitver.cpp.in
new file mode 100644
index 0000000..70f3508
--- /dev/null
+++ b/src/gitver.cpp.in
@@ -0,0 +1,15 @@
+
+#include "gitver.h"
+
+namespace GitVer
+{
+  const char *DescribeTags()
+  {
+    return "@GIT_DESCRIBE@";
+  }
+
+  const char *CommitDate()
+  {
+    return "@GIT_COMMIT_DATE_ISO8601@";
+  }
+}
diff --git a/src/main.cpp b/src/main.cpp
index d946514..3fa170d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -16,7 +16,7 @@ int main (int argc, char *argv[])
 
   SpkStore store(false, LogPath);
 
-  //*((int*)0) = 0;
+  SpkMsgBox::StaticExec("E", "F");
 
   return QApplication::exec();
 }
diff --git a/src/spkstore.cpp b/src/spkstore.cpp
index fe58654..d4dce8c 100644
--- a/src/spkstore.cpp
+++ b/src/spkstore.cpp
@@ -1,19 +1,26 @@
 
 #include <spkui_general.h>
+#include "dtk/spkdtkplugin.h"
+#include <QPluginLoader>
+#include <QDir>
+#include <QApplication>
 #include "spkstore.h"
 
+SpkStore *SpkStore::Instance = nullptr;
+
 SpkStore::SpkStore(bool aCli, QString &aLogPath)
 {
   mLogger = new SpkLogger(aLogPath);
 
+  Q_ASSERT(Instance == nullptr);
   Instance = this;
 
   if(aCli)
     return;
 
   SpkUi::Initialize();
-
   mMainWindow = new SpkMainWindow;
+
   mMainWindow->show();
 }