From cc319ca0ff48a1dae64815863092f183e3058a9f Mon Sep 17 00:00:00 2001
From: tangjie02 <tangjie02@kylinsec.com.cn>
Date: Wed, 7 Dec 2022 17:34:34 +0800
Subject: [PATCH] feature(backlight): Support brightness modification by
 changing gamma value.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- 支持通过改变gamma值来调节亮度。

Signed-off-by: tangjie02 <tangjie02@kylinsec.com.cn>
---
 .../com.kylinsec.kiran.power.gschema.xml.in   |  13 +-
 include/power-i.h                             |  10 +
 lib/base/base.h                               |   1 +
 lib/base/misc-utils.cpp                       |  51 ++++
 lib/base/misc-utils.h                         |  40 +++
 plugins/power/CMakeLists.txt                  |   2 +-
 ...ght-base.h => power-backlight-interface.h} |  15 ++
 plugins/power/backlight/power-backlight-kbd.h |   2 +-
 .../backlight/power-backlight-monitor-tool.h  |   2 +-
 ...p => power-backlight-monitor-x11-atom.cpp} |  24 +-
 ...1.h => power-backlight-monitor-x11-atom.h} |  10 +-
 .../power-backlight-monitor-x11-gamma.cpp     | 237 ++++++++++++++++++
 .../power-backlight-monitor-x11-gamma.h       |  60 +++++
 ...> power-backlight-monitors-controller.cpp} | 118 ++++-----
 ... => power-backlight-monitors-controller.h} |  25 +-
 .../power-backlight-monitors-tool.cpp         |  93 +++++++
 .../backlight/power-backlight-monitors-tool.h |  48 ++++
 ...1.cpp => power-backlight-monitors-x11.cpp} |  64 +++--
 ...t-x11.h => power-backlight-monitors-x11.h} |  41 ++-
 plugins/power/backlight/power-backlight.cpp   |   4 +-
 plugins/power/backlight/power-backlight.h     |   3 +-
 plugins/power/tools/main.cpp                  | 129 ++++++----
 plugins/power/tools/power-backlight-helper.h  |   1 +
 23 files changed, 797 insertions(+), 196 deletions(-)
 create mode 100644 lib/base/misc-utils.cpp
 create mode 100644 lib/base/misc-utils.h
 rename plugins/power/backlight/{power-backlight-base.h => power-backlight-interface.h} (82%)
 rename plugins/power/backlight/{power-backlight-monitor-x11.cpp => power-backlight-monitor-x11-atom.cpp} (84%)
 rename plugins/power/backlight/{power-backlight-monitor-x11.h => power-backlight-monitor-x11-atom.h} (78%)
 create mode 100644 plugins/power/backlight/power-backlight-monitor-x11-gamma.cpp
 create mode 100644 plugins/power/backlight/power-backlight-monitor-x11-gamma.h
 rename plugins/power/backlight/{power-backlight-moitor.cpp => power-backlight-monitors-controller.cpp} (65%)
 rename plugins/power/backlight/{power-backlight-monitor.h => power-backlight-monitors-controller.h} (80%)
 create mode 100644 plugins/power/backlight/power-backlight-monitors-tool.cpp
 create mode 100644 plugins/power/backlight/power-backlight-monitors-tool.h
 rename plugins/power/backlight/{power-backlight-x11.cpp => power-backlight-monitors-x11.cpp} (62%)
 rename plugins/power/backlight/{power-backlight-x11.h => power-backlight-monitors-x11.h} (63%)

diff --git a/data/schemas/com.kylinsec.kiran.power.gschema.xml.in b/data/schemas/com.kylinsec.kiran.power.gschema.xml.in
index 6798e3b..414939e 100644
--- a/data/schemas/com.kylinsec.kiran.power.gschema.xml.in
+++ b/data/schemas/com.kylinsec.kiran.power.gschema.xml.in
@@ -28,6 +28,12 @@
         <value nick="never" value="2"/>
     </enum>
 
+    <enum id="com.kylinsec.kiran.power.backlight-policy">
+        <value nick="auto" value="0"/>
+        <value nick="tool" value="1"/>
+        <value nick="x11" value="2"/>
+    </enum>
+
     <schema id="com.kylinsec.kiran.power" path="/com/kylinsec/kiran/power/">
 
         <key name="computer-battery-idle-time" type="i">
@@ -71,7 +77,7 @@
         </key>
 
         <key name="display-idle-dim-scale" type="i">
-            <default>30</default>
+            <default>0</default>
             <range min="0" max="100"/>
             <description>The scale of the brightness reduction when the display changes dim. Do nothing when the value is 0.</description>
         </key>
@@ -111,6 +117,9 @@
             <description>Display options for the notification icon.</description>
         </key>
 
-
+        <key name="monitor-backlight-policy" enum="com.kylinsec.kiran.power.backlight-policy">
+            <default>'auto'</default>
+            <description>Set the policy for obtaining brightness values.</description>
+        </key>
     </schema>
 </schemalist>
diff --git a/include/power-i.h b/include/power-i.h
index d24e3ac..12e27c8 100644
--- a/include/power-i.h
+++ b/include/power-i.h
@@ -108,6 +108,14 @@ extern "C"
         POWER_TRAY_ICON_POLICY_NERVER,
     };
 
+    enum PowerMonitorBacklightPolicy
+    {
+        // 自动
+        POWER_MONITOR_BACKLIGHT_POLICY_AUTO = 0,
+        POWER_MONITOR_BACKLIGHT_POLICY_TOOL = 1,
+        POWER_MONITOR_BACKLIGHT_POLICY_X11 = 2,
+    };
+
 #define POWER_DBUS_NAME "com.kylinsec.Kiran.SessionDaemon.Power"
 #define POWER_OBJECT_PATH "/com/kylinsec/Kiran/SessionDaemon/Power"
 
@@ -141,6 +149,8 @@ extern "C"
 #define POWER_SCHEMA_BATTERY_CRITICAL_ACTION "battery-critical-action"
 // 在什么情况下需要显示托盘图标
 #define POWER_SCHEMA_TRAY_ICON_POLICY "tray-icon-policy"
+// 设置获取显示器亮度值的策略，'tool'是直接操作背光设备文件，'x11'是通过xrandr接口调节亮度
+#define POWER_SCHEMA_MONITOR_BACKLIGHT_POLICY "monitor-backlight-policy"
 
 #ifdef __cplusplus
 }
diff --git a/lib/base/base.h b/lib/base/base.h
index 05ed8a3..ed06a46 100644
--- a/lib/base/base.h
+++ b/lib/base/base.h
@@ -26,5 +26,6 @@
 #include "lib/base/def.h"
 #include "lib/base/error.h"
 #include "lib/base/file-utils.h"
+#include "lib/base/misc-utils.h"
 #include "lib/base/stl-helper.h"
 #include "lib/base/str-utils.h"
\ No newline at end of file
diff --git a/lib/base/misc-utils.cpp b/lib/base/misc-utils.cpp
new file mode 100644
index 0000000..f52ad92
--- /dev/null
+++ b/lib/base/misc-utils.cpp
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd. 
+ * kiran-cc-daemon is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2. 
+ * You may obtain a copy of Mulan PSL v2 at:
+ *          http://license.coscl.org.cn/MulanPSL2 
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
+ * See the Mulan PSL v2 for more details.  
+ * 
+ * Author:     tangjie02 <tangjie02@kylinos.com.cn>
+ */
+
+#include "lib/base/misc-utils.h"
+#include "lib/base/base.h"
+
+namespace Kiran
+{
+MiscUtils::MiscUtils()
+{
+}
+
+Glib::OptionEntry MiscUtils::create_option_entry(const char &short_name,
+                                                 const Glib::ustring &long_name,
+                                                 const Glib::ustring &description,
+                                                 const Glib::ustring &arg_description,
+                                                 int32_t flags)
+{
+    Glib::OptionEntry result;
+    result.set_short_name(short_name);
+    result.set_long_name(long_name);
+    result.set_description(description);
+    result.set_arg_description(arg_description);
+    result.set_flags(flags);
+    return result;
+}
+
+Glib::OptionEntry MiscUtils::create_option_entry(const Glib::ustring &long_name,
+                                                 const Glib::ustring &description,
+                                                 const Glib::ustring &arg_description,
+                                                 int32_t flags)
+{
+    Glib::OptionEntry result;
+    result.set_long_name(long_name);
+    result.set_description(description);
+    result.set_arg_description(arg_description);
+    result.set_flags(flags);
+    return result;
+}
+}  // namespace Kiran
diff --git a/lib/base/misc-utils.h b/lib/base/misc-utils.h
new file mode 100644
index 0000000..73eaf62
--- /dev/null
+++ b/lib/base/misc-utils.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd. 
+ * kiran-cc-daemon is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2. 
+ * You may obtain a copy of Mulan PSL v2 at:
+ *          http://license.coscl.org.cn/MulanPSL2 
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
+ * See the Mulan PSL v2 for more details.  
+ * 
+ * Author:     tangjie02 <tangjie02@kylinos.com.cn>
+ */
+
+#pragma once
+
+#include <giomm.h>
+
+namespace Kiran
+{
+class MiscUtils
+{
+public:
+    MiscUtils();
+    virtual ~MiscUtils(){};
+
+    static Glib::OptionEntry create_option_entry(const char &short_name,
+                                                 const Glib::ustring &long_name,
+                                                 const Glib::ustring &description,
+                                                 const Glib::ustring &arg_description = Glib::ustring(),
+                                                 int32_t flags = 0);
+
+    static Glib::OptionEntry create_option_entry(const Glib::ustring &long_name,
+                                                 const Glib::ustring &description,
+                                                 const Glib::ustring &arg_description = Glib::ustring(),
+                                                 int32_t flags = 0);
+
+};  // namespace KS
+
+}  // namespace Kiran
diff --git a/plugins/power/CMakeLists.txt b/plugins/power/CMakeLists.txt
index f81cc09..67db9e0 100644
--- a/plugins/power/CMakeLists.txt
+++ b/plugins/power/CMakeLists.txt
@@ -7,7 +7,7 @@ file(GLOB_RECURSE POWER_CPP_FILES ./*.cpp)
 
 # Filter tools directory list(FILTER POWER_H_FILES EXCLUDE REGEX .*tools/.*)
 # list(FILTER POWER_CPP_FILES EXCLUDE REGEX .*tools/.*)
-list(FILTER POWER_CPP_FILES EXCLUDE REGEX "(.*tools/main.cpp|.*/tray/.*)")
+list(FILTER POWER_CPP_FILES EXCLUDE REGEX "(.*tools/.*|.*/tray/.*)")
 
 gen_dbus_stub(
   POWER power com.kylinsec.
diff --git a/plugins/power/backlight/power-backlight-base.h b/plugins/power/backlight/power-backlight-interface.h
similarity index 82%
rename from plugins/power/backlight/power-backlight-base.h
rename to plugins/power/backlight/power-backlight-interface.h
index 1a4aa64..fa64c13 100644
--- a/plugins/power/backlight/power-backlight-base.h
+++ b/plugins/power/backlight/power-backlight-interface.h
@@ -59,4 +59,19 @@ public:
 };
 
 using PowerBacklightAbsoluteVec = std::vector<std::shared_ptr<PowerBacklightAbsolute>>;
+
+class PowerBacklightMonitors
+{
+public:
+    PowerBacklightMonitors(){};
+    virtual ~PowerBacklightMonitors(){};
+
+    virtual void init() = 0;
+
+    // 获取所有显示器亮度设置对象
+    virtual PowerBacklightAbsoluteVec get_monitors() = 0;
+    virtual sigc::signal<void> signal_monitor_changed() = 0;
+    virtual sigc::signal<void> signal_brightness_changed() = 0;
+};
+
 }  // namespace Kiran
\ No newline at end of file
diff --git a/plugins/power/backlight/power-backlight-kbd.h b/plugins/power/backlight/power-backlight-kbd.h
index 88d528c..f2dc62f 100644
--- a/plugins/power/backlight/power-backlight-kbd.h
+++ b/plugins/power/backlight/power-backlight-kbd.h
@@ -14,7 +14,7 @@
 
 #pragma once
 
-#include "plugins/power/backlight/power-backlight-base.h"
+#include "plugins/power/backlight/power-backlight-interface.h"
 
 namespace Kiran
 {
diff --git a/plugins/power/backlight/power-backlight-monitor-tool.h b/plugins/power/backlight/power-backlight-monitor-tool.h
index a99b0b5..24e8850 100644
--- a/plugins/power/backlight/power-backlight-monitor-tool.h
+++ b/plugins/power/backlight/power-backlight-monitor-tool.h
@@ -14,7 +14,7 @@
 
 #pragma once
 
-#include "plugins/power/backlight/power-backlight-base.h"
+#include "plugins/power/backlight/power-backlight-interface.h"
 
 namespace Kiran
 {
diff --git a/plugins/power/backlight/power-backlight-monitor-x11.cpp b/plugins/power/backlight/power-backlight-monitor-x11-atom.cpp
similarity index 84%
rename from plugins/power/backlight/power-backlight-monitor-x11.cpp
rename to plugins/power/backlight/power-backlight-monitor-x11-atom.cpp
index 7e90f99..85a6ca4 100644
--- a/plugins/power/backlight/power-backlight-monitor-x11.cpp
+++ b/plugins/power/backlight/power-backlight-monitor-x11-atom.cpp
@@ -12,20 +12,19 @@
  * Author:     tangjie02 <tangjie02@kylinos.com.cn>
  */
 
-#include "plugins/power/backlight/power-backlight-monitor-x11.h"
-
+#include "plugins/power/backlight/power-backlight-monitor-x11-atom.h"
 #include <X11/Xatom.h>
 
 namespace Kiran
 {
-PowerBacklightMonitorX11::PowerBacklightMonitorX11(Atom backlight_atom, RROutput output) : backlight_atom_(backlight_atom),
-                                                                                           output_(output)
+PowerBacklightMonitorX11Atom::PowerBacklightMonitorX11Atom(Atom backlight_atom, RROutput output) : backlight_atom_(backlight_atom),
+                                                                                                   output_(output)
 {
     this->display_ = gdk_display_get_default();
     this->xdisplay_ = GDK_DISPLAY_XDISPLAY(this->display_);
 }
 
-bool PowerBacklightMonitorX11::set_brightness_value(int32_t brightness_value)
+bool PowerBacklightMonitorX11Atom::set_brightness_value(int32_t brightness_value)
 {
     gdk_x11_display_error_trap_push(this->display_);
 
@@ -46,7 +45,7 @@ bool PowerBacklightMonitorX11::set_brightness_value(int32_t brightness_value)
     return true;
 }
 
-int32_t PowerBacklightMonitorX11::get_brightness_value()
+int32_t PowerBacklightMonitorX11Atom::get_brightness_value()
 {
     RETURN_VAL_IF_TRUE(this->backlight_atom_ == None, -1);
 
@@ -91,16 +90,17 @@ int32_t PowerBacklightMonitorX11::get_brightness_value()
     return result;
 }
 
-bool PowerBacklightMonitorX11::get_brightness_range(int32_t &min, int32_t &max)
+bool PowerBacklightMonitorX11Atom::get_brightness_range(int32_t &min, int32_t &max)
 {
     XRRPropertyInfo *info = NULL;
 
-    SCOPE_EXIT({
-        if (info != NULL)
+    SCOPE_EXIT(
         {
-            XFree(info);
-        }
-    });
+            if (info != NULL)
+            {
+                XFree(info);
+            }
+        });
 
     info = XRRQueryOutputProperty(this->xdisplay_, this->output_, this->backlight_atom_);
     if (info == NULL)
diff --git a/plugins/power/backlight/power-backlight-monitor-x11.h b/plugins/power/backlight/power-backlight-monitor-x11-atom.h
similarity index 78%
rename from plugins/power/backlight/power-backlight-monitor-x11.h
rename to plugins/power/backlight/power-backlight-monitor-x11-atom.h
index 6249c25..7009991 100644
--- a/plugins/power/backlight/power-backlight-monitor-x11.h
+++ b/plugins/power/backlight/power-backlight-monitor-x11-atom.h
@@ -18,17 +18,18 @@
 //
 #include <X11/extensions/Xrandr.h>
 #include <gdk/gdkx.h>
+#include "plugins/power/backlight/power-backlight-interface.h"
 
-#include "plugins/power/backlight/power-backlight-base.h"
+typedef struct _XDisplay Display;
 
 namespace Kiran
 {
 // 通过Xrandr扩展调节单个显示设备亮度值
-class PowerBacklightMonitorX11 : public PowerBacklightAbsolute
+class PowerBacklightMonitorX11Atom : public PowerBacklightAbsolute
 {
 public:
-    PowerBacklightMonitorX11(Atom backlight_atom, RROutput output);
-    virtual ~PowerBacklightMonitorX11(){};
+    PowerBacklightMonitorX11Atom(Atom backlight_atom, RROutput output);
+    virtual ~PowerBacklightMonitorX11Atom(){};
 
     // 设置亮度值
     virtual bool set_brightness_value(int32_t brightness_value) override;
@@ -44,5 +45,4 @@ private:
     RROutput output_;
 };
 
-using PowerBacklightMonitorX11Vec = std::vector<std::shared_ptr<PowerBacklightMonitorX11>>;
 }  // namespace Kiran
diff --git a/plugins/power/backlight/power-backlight-monitor-x11-gamma.cpp b/plugins/power/backlight/power-backlight-monitor-x11-gamma.cpp
new file mode 100644
index 0000000..bbaf464
--- /dev/null
+++ b/plugins/power/backlight/power-backlight-monitor-x11-gamma.cpp
@@ -0,0 +1,237 @@
+/**
+ * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd. 
+ * kiran-cc-daemon is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2. 
+ * You may obtain a copy of Mulan PSL v2 at:
+ *          http://license.coscl.org.cn/MulanPSL2 
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
+ * See the Mulan PSL v2 for more details.  
+ * 
+ * Author:     tangjie02 <tangjie02@kylinos.com.cn>
+ */
+
+#include "plugins/power/backlight/power-backlight-monitor-x11-gamma.h"
+
+#include <X11/Xatom.h>
+#include <algorithm>
+
+namespace Kiran
+{
+#define GAMMA_MIN_BRIGHTNESS 0
+#define GAMMA_MAX_BRIGHTNESS 100
+
+PowerBacklightMonitorX11Gamma::PowerBacklightMonitorX11Gamma(RROutput output,
+                                                             RRCrtc crtc) : output_(output),
+                                                                            crtc_(crtc)
+{
+    this->display_ = gdk_display_get_default();
+    this->xdisplay_ = GDK_DISPLAY_XDISPLAY(this->display_);
+}
+
+bool PowerBacklightMonitorX11Gamma::set_brightness_value(int32_t brightness_value)
+{
+    RETURN_VAL_IF_TRUE(this->crtc_ == None, false);
+
+    auto size = XRRGetCrtcGammaSize(this->xdisplay_, this->crtc_);
+
+    if (!size)
+    {
+        KLOG_WARNING("Gamma size is 0.");
+        return false;
+    }
+    else
+    {
+        KLOG_DEBUG("The gamma size is %d.", size);
+    }
+
+    /*
+	 * The gamma-correction lookup table managed through XRR[GS]etCrtcGamma
+	 * is 2^n in size, where 'n' is the number of significant bits in
+	 * the X Color.  Because an X Color is 16 bits, size cannot be larger
+	 * than 2^16.
+	 */
+    if (size > 65536)
+    {
+        KLOG_WARNING("Gamma correction table is impossibly large.");
+        return false;
+    }
+
+    auto crtc_gamma = XRRAllocGamma(size);
+    if (!crtc_gamma)
+    {
+        KLOG_WARNING("Gamma allocation failed.");
+        return false;
+    }
+
+    SCOPE_EXIT(
+        {
+            if (crtc_gamma)
+            {
+                XRRFreeGamma(crtc_gamma);
+            }
+        });
+
+    auto gamma_info = this->get_gamma_info();
+    auto gamma_red = 1.0 / gamma_info.red;
+    auto gamma_green = 1.0 / gamma_info.green;
+    auto gamma_blue = 1.0 / gamma_info.blue;
+    auto gamma_brightness = (double)brightness_value / 100.0;
+
+    for (int i = 0; i < size; i++)
+    {
+        if (gamma_red == 1.0 && gamma_brightness == 1.0)
+        {
+            crtc_gamma->red[i] = (double)i / (double)(size - 1) * 65535.0;
+        }
+        else
+        {
+            crtc_gamma->red[i] = std::min(pow((double)i / (double)(size - 1),
+                                              gamma_red) *
+                                              gamma_brightness,
+                                          (double)1.0) *
+                                 65535.0;
+        }
+
+        if (gamma_green == 1.0 && gamma_brightness == 1.0)
+        {
+            crtc_gamma->green[i] = (double)i / (double)(size - 1) * 65535.0;
+        }
+        else
+        {
+            crtc_gamma->green[i] = std::min(pow((double)i / (double)(size - 1),
+                                                gamma_green) *
+                                                gamma_brightness,
+                                            1.0) *
+                                   65535.0;
+        }
+
+        if (gamma_blue == 1.0 && gamma_brightness == 1.0)
+        {
+            crtc_gamma->blue[i] = (double)i / (double)(size - 1) * 65535.0;
+        }
+        else
+        {
+            crtc_gamma->blue[i] = std::min(pow((double)i / (double)(size - 1),
+                                               gamma_blue) *
+                                               gamma_brightness,
+                                           1.0) *
+                                  65535.0;
+        }
+    }
+
+    XRRSetCrtcGamma(this->xdisplay_, this->crtc_, crtc_gamma);
+
+    return true;
+}
+
+int32_t PowerBacklightMonitorX11Gamma::get_brightness_value()
+{
+    auto gamma_info = this->get_gamma_info();
+    RETURN_VAL_IF_TRUE(gamma_info.brightness >= 1.0, 1);
+    RETURN_VAL_IF_TRUE(gamma_info.brightness <= 0.001, 0);
+    return std::min(int32_t((gamma_info.brightness * GAMMA_MAX_BRIGHTNESS) + 0.5), GAMMA_MAX_BRIGHTNESS);
+}
+
+bool PowerBacklightMonitorX11Gamma::get_brightness_range(int32_t &min, int32_t &max)
+{
+    min = GAMMA_MIN_BRIGHTNESS;
+    max = GAMMA_MAX_BRIGHTNESS;
+    return true;
+}
+
+int PowerBacklightMonitorX11Gamma::find_last_non_clamped(unsigned short array[], int size)
+{
+    int i;
+    for (i = size - 1; i > 0; i--)
+    {
+        if (array[i] < 0xffff)
+            return i;
+    }
+    return 0;
+}
+
+GammaInfo PowerBacklightMonitorX11Gamma::get_gamma_info()
+{
+    GammaInfo gamma_info;
+
+    RETURN_VAL_IF_TRUE(this->crtc_ == 0, gamma_info);
+
+    auto size = XRRGetCrtcGammaSize(this->xdisplay_, this->crtc_);
+    if (!size)
+    {
+        KLOG_WARNING("Gamma size is 0.");
+        return gamma_info;
+    }
+
+    auto crtc_gamma = XRRGetCrtcGamma(this->xdisplay_, this->crtc_);
+    if (!crtc_gamma)
+    {
+        KLOG_WARNING("Failed to get gamma for output(%d).", (int)this->output_);
+        return gamma_info;
+    }
+
+    /*
+     * Here is a bit tricky because gamma is a whole curve for each
+     * color.  So, typically, we need to represent 3 * 256 values as 3 + 1
+     * values.  Therefore, we approximate the gamma curve (v) by supposing
+     * it always follows the way we set it: a power function (i^g)
+     * multiplied by a brightness (b).
+     * v = i^g * b
+     * so g = (ln(v) - ln(b))/ln(i)
+     * and b can be found using two points (v1,i1) and (v2, i2):
+     * b = e^((ln(v2)*ln(i1) - ln(v1)*ln(i2))/ln(i1/i2))
+     * For the best resolution, we select i2 at the highest place not
+     * clamped and i1 at i2/2. Note that if i2 = 1 (as in most normal
+     * cases), then b = v2.
+     */
+    auto last_red = find_last_non_clamped(crtc_gamma->red, size);
+    auto last_green = find_last_non_clamped(crtc_gamma->green, size);
+    auto last_blue = find_last_non_clamped(crtc_gamma->blue, size);
+    auto best_array = crtc_gamma->red;
+    auto last_best = last_red;
+    if (last_green > last_best)
+    {
+        last_best = last_green;
+        best_array = crtc_gamma->green;
+    }
+    if (last_blue > last_best)
+    {
+        last_best = last_blue;
+        best_array = crtc_gamma->blue;
+    }
+    if (last_best == 0)
+        last_best = 1;
+
+    auto middle = last_best / 2;
+    auto i1 = (double)(middle + 1) / size;
+    auto v1 = (double)(best_array[middle]) / 65535;
+    auto i2 = (double)(last_best + 1) / size;
+    auto v2 = (double)(best_array[last_best]) / 65535;
+    if (v2 >= 0.0001)
+    {
+        if ((last_best + 1) == size)
+        {
+            gamma_info.brightness = v2;
+        }
+        else
+        {
+            gamma_info.brightness = exp((log(v2) * log(i1) - log(v1) * log(i2)) / log(i1 / i2));
+        }
+        gamma_info.red = log((double)(crtc_gamma->red[last_red / 2]) / gamma_info.brightness / 65535) / log((double)((last_red / 2) + 1) / size);
+        gamma_info.green = log((double)(crtc_gamma->green[last_green / 2]) / gamma_info.brightness / 65535) / log((double)((last_green / 2) + 1) / size);
+        gamma_info.blue = log((double)(crtc_gamma->blue[last_blue / 2]) / gamma_info.brightness / 65535) / log((double)((last_blue / 2) + 1) / size);
+    }
+
+    XRRFreeGamma(crtc_gamma);
+
+    KLOG_DEBUG("Gamma info: red(%.2f), green(%.2f), blue(%.2f), brightness(%.2f).",
+               gamma_info.red,
+               gamma_info.green,
+               gamma_info.blue,
+               gamma_info.brightness);
+    return gamma_info;
+}
+
+}  // namespace Kiran
diff --git a/plugins/power/backlight/power-backlight-monitor-x11-gamma.h b/plugins/power/backlight/power-backlight-monitor-x11-gamma.h
new file mode 100644
index 0000000..85d2ed0
--- /dev/null
+++ b/plugins/power/backlight/power-backlight-monitor-x11-gamma.h
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd. 
+ * kiran-cc-daemon is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2. 
+ * You may obtain a copy of Mulan PSL v2 at:
+ *          http://license.coscl.org.cn/MulanPSL2 
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
+ * See the Mulan PSL v2 for more details.  
+ * 
+ * Author:     tangjie02 <tangjie02@kylinos.com.cn>
+ */
+
+#pragma once
+
+#include <gdkmm.h>
+//
+#include <X11/extensions/Xrandr.h>
+#include <gdk/gdkx.h>
+
+#include "plugins/power/backlight/power-backlight-interface.h"
+
+namespace Kiran
+{
+struct GammaInfo
+{
+    GammaInfo() : brightness(0.0), red(1.0), green(1.0), blue(1.0) {}
+    double brightness;
+    double red;
+    double green;
+    double blue;
+};
+
+// 通过Xrandr扩展调节crtc的gamma值来实现亮度变化
+class PowerBacklightMonitorX11Gamma : public PowerBacklightAbsolute
+{
+public:
+    PowerBacklightMonitorX11Gamma(RROutput output, RRCrtc crtc);
+    virtual ~PowerBacklightMonitorX11Gamma(){};
+
+    // 设置亮度值
+    virtual bool set_brightness_value(int32_t brightness_value) override;
+    // 获取亮度值
+    virtual int32_t get_brightness_value() override;
+    // 获取亮度最大最小值
+    virtual bool get_brightness_range(int32_t &min, int32_t &max) override;
+
+private:
+    int find_last_non_clamped(unsigned short array[], int size);
+    GammaInfo get_gamma_info();
+
+private:
+    GdkDisplay *display_;
+    Display *xdisplay_;
+    RROutput output_;
+    RRCrtc crtc_;
+};
+
+}  // namespace Kiran
diff --git a/plugins/power/backlight/power-backlight-moitor.cpp b/plugins/power/backlight/power-backlight-monitors-controller.cpp
similarity index 65%
rename from plugins/power/backlight/power-backlight-moitor.cpp
rename to plugins/power/backlight/power-backlight-monitors-controller.cpp
index d73ee97..7e83f55 100644
--- a/plugins/power/backlight/power-backlight-moitor.cpp
+++ b/plugins/power/backlight/power-backlight-monitors-controller.cpp
@@ -12,50 +12,42 @@
  * Author:     tangjie02 <tangjie02@kylinos.com.cn>
  */
 
-#include "plugins/power/backlight/power-backlight-monitor-tool.h"
-#include "plugins/power/backlight/power-backlight-monitor.h"
+#include "plugins/power/backlight/power-backlight-monitors-controller.h"
+#include "plugins/power/backlight/power-backlight-monitors-tool.h"
+#include "plugins/power/backlight/power-backlight-monitors-x11.h"
 
 namespace Kiran
 {
-PowerBacklightMonitor::PowerBacklightMonitor() : brightness_percentage_(-1)
+PowerBacklightMonitorsController::PowerBacklightMonitorsController() : brightness_percentage_(-1)
 {
+    this->power_settings_ = Gio::Settings::create(POWER_SCHEMA_ID);
 }
 
-PowerBacklightMonitor::~PowerBacklightMonitor()
+PowerBacklightMonitorsController::~PowerBacklightMonitorsController()
 {
 }
 
-void PowerBacklightMonitor::init()
+void PowerBacklightMonitorsController::init()
 {
-    KLOG_PROFILE("");
-
-    backlight_x11_.init();
-    this->backlight_helper_.init();
-
-    this->load_absolute_monitors();
+    this->load_backlight_monitors();
     this->brightness_percentage_ = this->get_brightness();
-
-    this->backlight_x11_.signal_monitor_changed().connect(sigc::mem_fun(this, &PowerBacklightMonitor::on_x11_monitor_changed));
-    this->backlight_helper_.signal_brightness_changed().connect(sigc::mem_fun(this, &PowerBacklightMonitor::on_helper_brightness_changed));
 }
 
-bool PowerBacklightMonitor::set_brightness(int32_t percentage)
+bool PowerBacklightMonitorsController::set_brightness(int32_t percentage)
 {
-    KLOG_PROFILE("percentage: %d.", percentage);
-
-    RETURN_VAL_IF_TRUE(this->absolute_monitors_.size() == 0, false);
-
-    for (auto &monitor : this->absolute_monitors_)
+    auto monitors = this->backlight_monitors_->get_monitors();
+    for (auto &monitor : monitors)
     {
         RETURN_VAL_IF_FALSE(this->set_brightness_percentage(monitor, percentage), false);
     }
-
+    this->update_cached_brightness();
     return true;
 }
 
-int32_t PowerBacklightMonitor::get_brightness()
+int32_t PowerBacklightMonitorsController::get_brightness()
 {
-    for (auto &monitor : this->absolute_monitors_)
+    auto monitors = this->backlight_monitors_->get_monitors();
+    for (auto &monitor : monitors)
     {
         auto percentage = this->get_brightness_percentage(monitor);
         RETURN_VAL_IF_TRUE(percentage >= 0, percentage);
@@ -63,43 +55,57 @@ int32_t PowerBacklightMonitor::get_brightness()
     return -1;
 }
 
-bool PowerBacklightMonitor::brightness_up()
+bool PowerBacklightMonitorsController::brightness_up()
 {
-    RETURN_VAL_IF_TRUE(this->absolute_monitors_.size() == 0, false);
-
-    for (auto &monitor : this->absolute_monitors_)
+    auto monitors = this->backlight_monitors_->get_monitors();
+    for (auto &monitor : monitors)
     {
         this->brightness_value_up(monitor);
     }
     return true;
 }
 
-bool PowerBacklightMonitor::brightness_down()
+bool PowerBacklightMonitorsController::brightness_down()
 {
-    RETURN_VAL_IF_TRUE(this->absolute_monitors_.size() == 0, false);
-    for (auto &monitor : this->absolute_monitors_)
+    auto monitors = this->backlight_monitors_->get_monitors();
+    for (auto &monitor : monitors)
     {
         this->brightness_value_down(monitor);
     }
     return true;
 }
 
-void PowerBacklightMonitor::load_absolute_monitors()
+void PowerBacklightMonitorsController::load_backlight_monitors()
 {
-    this->absolute_monitors_.clear();
+    auto monitor_backlight_policy = this->power_settings_->get_enum(POWER_SCHEMA_MONITOR_BACKLIGHT_POLICY);
 
-    if (this->backlight_x11_.support_backlight_extension())
+    switch (monitor_backlight_policy)
     {
-        auto monitors = this->backlight_x11_.get_monitors();
-        this->absolute_monitors_ = PowerBacklightAbsoluteVec(monitors.begin(), monitors.end());
-    }
-    else
+    case PowerMonitorBacklightPolicy::POWER_MONITOR_BACKLIGHT_POLICY_TOOL:
+        this->backlight_monitors_ = std::make_shared<PowerBacklightMonitorsTool>();
+        break;
+    case PowerMonitorBacklightPolicy::POWER_MONITOR_BACKLIGHT_POLICY_X11:
+        this->backlight_monitors_ = std::make_shared<PowerBacklightMonitorsX11>();
+        break;
+    default:
     {
-        this->absolute_monitors_.push_back(std::make_shared<PowerBacklightMonitorTool>());
+        if (PowerBacklightMonitorsTool::support_backlight())
+        {
+            this->backlight_monitors_ = std::make_shared<PowerBacklightMonitorsTool>();
+        }
+        else
+        {
+            this->backlight_monitors_ = std::make_shared<PowerBacklightMonitorsX11>();
+        }
     }
+    }
+
+    this->backlight_monitors_->init();
+    this->backlight_monitors_->signal_monitor_changed().connect(sigc::mem_fun(this, &PowerBacklightMonitorsController::on_monitor_changed));
+    this->backlight_monitors_->signal_brightness_changed().connect(sigc::mem_fun(this, &PowerBacklightMonitorsController::update_cached_brightness));
 }
 
-bool PowerBacklightMonitor::set_brightness_percentage(std::shared_ptr<PowerBacklightAbsolute> absolute_monitor, int32_t percentage)
+bool PowerBacklightMonitorsController::set_brightness_percentage(std::shared_ptr<PowerBacklightAbsolute> absolute_monitor, int32_t percentage)
 {
     int32_t brightness_min = -1;
     int32_t brightness_max = -1;
@@ -161,7 +167,7 @@ bool PowerBacklightMonitor::set_brightness_percentage(std::shared_ptr<PowerBackl
     return true;
 }
 
-int32_t PowerBacklightMonitor::get_brightness_percentage(std::shared_ptr<PowerBacklightAbsolute> absolute_monitor)
+int32_t PowerBacklightMonitorsController::get_brightness_percentage(std::shared_ptr<PowerBacklightAbsolute> absolute_monitor)
 {
     int32_t brightness_min = -1;
     int32_t brightness_max = -1;
@@ -182,7 +188,7 @@ int32_t PowerBacklightMonitor::get_brightness_percentage(std::shared_ptr<PowerBa
     return percentage;
 }
 
-bool PowerBacklightMonitor::brightness_value_up(std::shared_ptr<PowerBacklightAbsolute> absolute_monitor)
+bool PowerBacklightMonitorsController::brightness_value_up(std::shared_ptr<PowerBacklightAbsolute> absolute_monitor)
 {
     int32_t brightness_min = -1;
     int32_t brightness_max = -1;
@@ -199,7 +205,7 @@ bool PowerBacklightMonitor::brightness_value_up(std::shared_ptr<PowerBacklightAb
     return absolute_monitor->set_brightness_value(brightness_current_value);
 }
 
-bool PowerBacklightMonitor::brightness_value_down(std::shared_ptr<PowerBacklightAbsolute> absolute_monitor)
+bool PowerBacklightMonitorsController::brightness_value_down(std::shared_ptr<PowerBacklightAbsolute> absolute_monitor)
 {
     int32_t brightness_min = -1;
     int32_t brightness_max = -1;
@@ -216,15 +222,14 @@ bool PowerBacklightMonitor::brightness_value_down(std::shared_ptr<PowerBacklight
     return absolute_monitor->set_brightness_value(brightness_current_value);
 }
 
-int32_t PowerBacklightMonitor::brightness_discrete2percent(int32_t discrete, int32_t levels)
+int32_t PowerBacklightMonitorsController::brightness_discrete2percent(int32_t discrete, int32_t levels)
 {
-    // TODO: test
     RETURN_VAL_IF_TRUE(discrete > levels, 100);
     RETURN_VAL_IF_TRUE(levels <= 1, 0);
     return (int32_t)(((double)discrete * (100.0 / (double)(levels - 1))) + 0.5);
 }
 
-int32_t PowerBacklightMonitor::brightness_percent2discrete(int32_t percentage, int32_t levels)
+int32_t PowerBacklightMonitorsController::brightness_percent2discrete(int32_t percentage, int32_t levels)
 {
     RETURN_VAL_IF_TRUE(percentage > 100, levels);
     RETURN_VAL_IF_TRUE(levels == 0, 0);
@@ -232,7 +237,7 @@ int32_t PowerBacklightMonitor::brightness_percent2discrete(int32_t percentage, i
     return (int32_t)((((double)percentage * (double)(levels - 1)) / 100.0) + 0.5);
 }
 
-int32_t PowerBacklightMonitor::get_brightness_step(uint32_t levels)
+int32_t PowerBacklightMonitorsController::get_brightness_step(uint32_t levels)
 {
     if (levels > 20)
     {
@@ -241,7 +246,7 @@ int32_t PowerBacklightMonitor::get_brightness_step(uint32_t levels)
     return 1;
 }
 
-void PowerBacklightMonitor::update_cached_brightness()
+void PowerBacklightMonitorsController::update_cached_brightness()
 {
     auto brightness_percentage = this->get_brightness();
     if (brightness_percentage != this->brightness_percentage_)
@@ -251,24 +256,7 @@ void PowerBacklightMonitor::update_cached_brightness()
     }
 }
 
-void PowerBacklightMonitor::on_x11_monitor_changed(PBXMonitorEvent x11_monitor_event)
-{
-    switch (x11_monitor_event)
-    {
-    case PBXMonitorEvent::PBX_MONITOR_EVENT_PROPERTY_CHANGED:
-    {
-        this->update_cached_brightness();
-        break;
-    }
-    case PBXMonitorEvent::PBX_MONITOR_EVENT_SCREEN_CHANGED:
-        this->load_absolute_monitors();
-        break;
-    default:
-        break;
-    }
-}
-
-void PowerBacklightMonitor::on_helper_brightness_changed(int32_t brightness_value)
+void PowerBacklightMonitorsController::on_monitor_changed()
 {
     this->update_cached_brightness();
 }
diff --git a/plugins/power/backlight/power-backlight-monitor.h b/plugins/power/backlight/power-backlight-monitors-controller.h
similarity index 80%
rename from plugins/power/backlight/power-backlight-monitor.h
rename to plugins/power/backlight/power-backlight-monitors-controller.h
index b14e7f9..d678e23 100644
--- a/plugins/power/backlight/power-backlight-monitor.h
+++ b/plugins/power/backlight/power-backlight-monitors-controller.h
@@ -18,9 +18,7 @@
 //
 #include <X11/Xatom.h>
 
-#include "plugins/power/backlight/power-backlight-base.h"
-#include "plugins/power/backlight/power-backlight-x11.h"
-#include "plugins/power/tools/power-backlight-helper.h"
+#include "plugins/power/backlight/power-backlight-interface.h"
 
 namespace Kiran
 {
@@ -28,11 +26,12 @@ namespace Kiran
    台式机的显示器不一定带有背光控制器，因此可能无法通过该模块的接口调节亮度，
    台式机的显示器一般可以直接通过显示器周边的按钮调节亮度 */
 
-class PowerBacklightMonitor : public PowerBacklightPercentage
+
+class PowerBacklightMonitorsController : public PowerBacklightPercentage
 {
 public:
-    PowerBacklightMonitor();
-    virtual ~PowerBacklightMonitor();
+    PowerBacklightMonitorsController();
+    virtual ~PowerBacklightMonitorsController();
 
     virtual void init();
 
@@ -55,7 +54,7 @@ public:
     virtual sigc::signal<void, int32_t> &signal_brightness_changed() override { return this->brightness_changed_; };
 
 private:
-    void load_absolute_monitors();
+    void load_backlight_monitors();
     // 设置单个显示器的亮度百分比
     bool set_brightness_percentage(std::shared_ptr<PowerBacklightAbsolute> absolute_monitor, int32_t percentage);
     // 获取单个显示器的亮度
@@ -71,18 +70,14 @@ private:
 
     // 更新缓存的亮度百分比并发送信号
     void update_cached_brightness();
-
-    void on_x11_monitor_changed(PBXMonitorEvent x11_monitor_event);
-    void on_helper_brightness_changed(int32_t brightness_value);
+    void on_monitor_changed();
 
 private:
-    PowerBacklightX11 backlight_x11_;
-    PowerBacklightHelper backlight_helper_;
-
-    // 用于调节显示器的绝对值
-    PowerBacklightAbsoluteVec absolute_monitors_;
+    std::shared_ptr<PowerBacklightMonitors> backlight_monitors_;
 
     int32_t brightness_percentage_;
     sigc::signal<void, int32_t> brightness_changed_;
+
+    Glib::RefPtr<Gio::Settings> power_settings_;
 };
 }  // namespace Kiran
\ No newline at end of file
diff --git a/plugins/power/backlight/power-backlight-monitors-tool.cpp b/plugins/power/backlight/power-backlight-monitors-tool.cpp
new file mode 100644
index 0000000..6759e38
--- /dev/null
+++ b/plugins/power/backlight/power-backlight-monitors-tool.cpp
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd. 
+ * kiran-cc-daemon is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2. 
+ * You may obtain a copy of Mulan PSL v2 at:
+ *          http://license.coscl.org.cn/MulanPSL2 
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
+ * See the Mulan PSL v2 for more details.  
+ * 
+ * Author:     tangjie02 <tangjie02@kylinos.com.cn>
+ */
+
+#include "plugins/power/backlight/power-backlight-monitors-tool.h"
+#include "config.h"
+#include "plugins/power/backlight/power-backlight-monitor-tool.h"
+
+namespace Kiran
+{
+#define POWER_BACKLIGHT_HELPER KCC_INSTALL_BINDIR "/kiran-power-backlight-helper"
+
+PowerBacklightMonitorsTool::PowerBacklightMonitorsTool()
+{
+    auto backlight_dir = this->get_backlight_dir();
+    if (!backlight_dir.empty())
+    {
+        auto filename = Glib::build_filename(backlight_dir, "brightness");
+        this->brightness_monitor_ = FileUtils::make_monitor_file(filename,
+                                                                 sigc::mem_fun(this, &PowerBacklightMonitorsTool::on_brightness_changed),
+                                                                 Gio::FILE_MONITOR_NONE);
+    }
+}
+
+bool PowerBacklightMonitorsTool::support_backlight()
+{
+    try
+    {
+        std::string standard_output;
+        int32_t exit_status = 0;
+        auto cmdline = fmt::format("pkexec {0} --support-backlight", POWER_BACKLIGHT_HELPER);
+        Glib::spawn_command_line_sync(cmdline, &standard_output, nullptr, &exit_status);
+        RETURN_VAL_IF_TRUE(exit_status != 0, false);
+        return (std::strtol(standard_output.c_str(), nullptr, 0) == 1);
+    }
+    catch (const Glib::Error &e)
+    {
+        KLOG_WARNING("%s.", e.what().c_str());
+    }
+    return false;
+}
+
+void PowerBacklightMonitorsTool::init()
+{
+    this->backlight_monitors_.clear();
+    this->backlight_monitors_.push_back(std::make_shared<PowerBacklightMonitorTool>());
+}
+
+std::string PowerBacklightMonitorsTool::get_backlight_dir()
+{
+    try
+    {
+        std::string standard_output;
+        int32_t exit_status = 0;
+        auto cmdline = fmt::format("pkexec {0} --get-backlight-dir", POWER_BACKLIGHT_HELPER);
+        Glib::spawn_command_line_sync(cmdline, &standard_output, nullptr, &exit_status);
+        RETURN_VAL_IF_TRUE(exit_status != 0, std::string());
+        return standard_output;
+    }
+    catch (const Glib::Error &e)
+    {
+        KLOG_WARNING("%s.", e.what().c_str());
+    }
+    return std::string();
+}
+
+void PowerBacklightMonitorsTool::on_brightness_changed(const Glib::RefPtr<Gio::File> &file,
+                                                       const Glib::RefPtr<Gio::File> &other_file,
+                                                       Gio::FileMonitorEvent event_type)
+{
+    switch (event_type)
+    {
+    case Gio::FILE_MONITOR_EVENT_CHANGED:
+    {
+        this->brightness_changed_.emit();
+        break;
+    }
+    default:
+        break;
+    }
+}
+
+}  // namespace Kiran
\ No newline at end of file
diff --git a/plugins/power/backlight/power-backlight-monitors-tool.h b/plugins/power/backlight/power-backlight-monitors-tool.h
new file mode 100644
index 0000000..6b8a84e
--- /dev/null
+++ b/plugins/power/backlight/power-backlight-monitors-tool.h
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd. 
+ * kiran-cc-daemon is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2. 
+ * You may obtain a copy of Mulan PSL v2 at:
+ *          http://license.coscl.org.cn/MulanPSL2 
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
+ * See the Mulan PSL v2 for more details.  
+ * 
+ * Author:     tangjie02 <tangjie02@kylinos.com.cn>
+ */
+
+#pragma once
+
+#include "plugins/power/backlight/power-backlight-interface.h"
+
+namespace Kiran
+{
+class PowerBacklightMonitorsTool : public PowerBacklightMonitors
+{
+public:
+    PowerBacklightMonitorsTool();
+    virtual ~PowerBacklightMonitorsTool(){};
+
+    static bool support_backlight();
+
+    virtual void init();
+    // 获取所有显示器亮度设置对象
+    virtual PowerBacklightAbsoluteVec get_monitors() { return this->backlight_monitors_; };
+    virtual sigc::signal<void> signal_monitor_changed() { return this->monitor_changed_; };
+    virtual sigc::signal<void> signal_brightness_changed() { return this->brightness_changed_; };
+
+private:
+    std::string get_backlight_dir();
+    void on_brightness_changed(const Glib::RefPtr<Gio::File> &file,
+                               const Glib::RefPtr<Gio::File> &other_file,
+                               Gio::FileMonitorEvent event_type);
+
+private:
+    sigc::signal<void> monitor_changed_;
+    sigc::signal<void> brightness_changed_;
+    Glib::RefPtr<Gio::FileMonitor> brightness_monitor_;
+
+    PowerBacklightAbsoluteVec backlight_monitors_;
+};
+}  // namespace Kiran
\ No newline at end of file
diff --git a/plugins/power/backlight/power-backlight-x11.cpp b/plugins/power/backlight/power-backlight-monitors-x11.cpp
similarity index 62%
rename from plugins/power/backlight/power-backlight-x11.cpp
rename to plugins/power/backlight/power-backlight-monitors-x11.cpp
index 9dfed20..80a15a0 100644
--- a/plugins/power/backlight/power-backlight-x11.cpp
+++ b/plugins/power/backlight/power-backlight-monitors-x11.cpp
@@ -12,15 +12,17 @@
  * Author:     tangjie02 <tangjie02@kylinos.com.cn>
  */
 
-#include "plugins/power/backlight/power-backlight-x11.h"
+#include "plugins/power/backlight/power-backlight-monitors-x11.h"
+#include "plugins/power/backlight/power-backlight-monitor-x11-atom.h"
+#include "plugins/power/backlight/power-backlight-monitor-x11-gamma.h"
 
 namespace Kiran
 {
-PowerBacklightX11::PowerBacklightX11() : event_base_(0),
-                                         error_base_(0),
-                                         extension_supported_(false),
-                                         backlight_atom_(None),
-                                         resources_(NULL)
+PowerBacklightMonitorsX11::PowerBacklightMonitorsX11() : event_base_(0),
+                                                         error_base_(0),
+                                                         extension_supported_(false),
+                                                         backlight_atom_(None),
+                                                         resources_(NULL)
 {
     this->display_ = gdk_display_get_default();
     this->xdisplay_ = GDK_DISPLAY_XDISPLAY(this->display_);
@@ -30,24 +32,21 @@ PowerBacklightX11::PowerBacklightX11() : event_base_(0),
     this->xroot_window_ = GDK_WINDOW_XID(this->root_window_);
 }
 
-PowerBacklightX11::~PowerBacklightX11()
+PowerBacklightMonitorsX11::~PowerBacklightMonitorsX11()
 {
     this->clear_resource();
 
     if (this->extension_supported_)
     {
-        gdk_window_remove_filter(this->root_window_, &PowerBacklightX11::window_event, this);
+        gdk_window_remove_filter(this->root_window_, &PowerBacklightMonitorsX11::window_event, this);
     }
 }
 
-void PowerBacklightX11::init()
+void PowerBacklightMonitorsX11::init()
 {
     RETURN_IF_FALSE(this->init_xrandr());
 
     this->backlight_atom_ = this->get_backlight_atom();
-    RETURN_IF_TRUE(this->backlight_atom_ == None);
-
-    KLOG_DEBUG("Support brightness settings");
     this->load_resource();
 
     XRRSelectInput(this->xdisplay_, this->xroot_window_, RRScreenChangeNotifyMask | RROutputPropertyNotifyMask);
@@ -55,11 +54,11 @@ void PowerBacklightX11::init()
                                          this->event_base_,
                                          RRNotify + 1);
 
-    gdk_window_add_filter(this->root_window_, &PowerBacklightX11::window_event, this);
+    gdk_window_add_filter(this->root_window_, &PowerBacklightMonitorsX11::window_event, this);
     this->extension_supported_ = true;
 }
 
-bool PowerBacklightX11::init_xrandr()
+bool PowerBacklightMonitorsX11::init_xrandr()
 {
     KLOG_PROFILE("");
 
@@ -84,7 +83,7 @@ bool PowerBacklightX11::init_xrandr()
     return true;
 }
 
-Atom PowerBacklightX11::get_backlight_atom()
+Atom PowerBacklightMonitorsX11::get_backlight_atom()
 {
     RETURN_VAL_IF_TRUE(this->xdisplay_ == NULL, false);
 
@@ -103,7 +102,7 @@ Atom PowerBacklightX11::get_backlight_atom()
     return backlight_atom;
 }
 
-void PowerBacklightX11::load_resource()
+void PowerBacklightMonitorsX11::load_resource()
 {
     this->clear_resource();
     this->resources_ = XRRGetScreenResourcesCurrent(this->xdisplay_, this->xroot_window_);
@@ -111,12 +110,33 @@ void PowerBacklightX11::load_resource()
     this->backlight_monitors_.clear();
     for (int32_t i = 0; i < this->resources_->noutput; ++i)
     {
-        auto monitor = std::make_shared<PowerBacklightMonitorX11>(this->backlight_atom_, this->resources_->outputs[i]);
+        std::shared_ptr<PowerBacklightAbsolute> monitor;
+        auto output_info = XRRGetOutputInfo(this->xdisplay_, this->resources_, this->resources_->outputs[i]);
+        if (!output_info)
+        {
+            KLOG_WARNING("Not found output info for %d.", (int)this->resources_->outputs[i]);
+            continue;
+        }
+
+        if (!output_info->crtc)
+        {
+            KLOG_DEBUG("Not found crtc for output %d, ignore it.", (int)this->resources_->outputs[i]);
+            continue;
+        }
+
+        if (this->backlight_atom_ != None)
+        {
+            monitor = std::make_shared<PowerBacklightMonitorX11Atom>(this->backlight_atom_, this->resources_->outputs[i]);
+        }
+        else
+        {
+            monitor = std::make_shared<PowerBacklightMonitorX11Gamma>(this->resources_->outputs[i], output_info->crtc);
+        }
         this->backlight_monitors_.push_back(monitor);
     }
 }
 
-void PowerBacklightX11::clear_resource()
+void PowerBacklightMonitorsX11::clear_resource()
 {
     if (this->resources_)
     {
@@ -125,9 +145,9 @@ void PowerBacklightX11::clear_resource()
     }
 }
 
-GdkFilterReturn PowerBacklightX11::window_event(GdkXEvent *gdk_event, GdkEvent *event, gpointer data)
+GdkFilterReturn PowerBacklightMonitorsX11::window_event(GdkXEvent *gdk_event, GdkEvent *event, gpointer data)
 {
-    PowerBacklightX11 *backlight = (PowerBacklightX11 *)data;
+    PowerBacklightMonitorsX11 *backlight = (PowerBacklightMonitorsX11 *)data;
 
     XEvent *xevent = (XEvent *)gdk_event;
     RETURN_VAL_IF_FALSE(backlight, GDK_FILTER_CONTINUE);
@@ -138,12 +158,12 @@ GdkFilterReturn PowerBacklightX11::window_event(GdkXEvent *gdk_event, GdkEvent *
     case RRScreenChangeNotify:
     {
         backlight->load_resource();
-        backlight->monitor_changed_.emit(PBXMonitorEvent::PBX_MONITOR_EVENT_SCREEN_CHANGED);
+        backlight->monitor_changed_.emit();
         break;
     }
     case RROutputPropertyNotifyMask:
     {
-        backlight->monitor_changed_.emit(PBXMonitorEvent::PBX_MONITOR_EVENT_PROPERTY_CHANGED);
+        backlight->brightness_changed_.emit();
         break;
     }
     default:
diff --git a/plugins/power/backlight/power-backlight-x11.h b/plugins/power/backlight/power-backlight-monitors-x11.h
similarity index 63%
rename from plugins/power/backlight/power-backlight-x11.h
rename to plugins/power/backlight/power-backlight-monitors-x11.h
index 870389d..9be81a7 100644
--- a/plugins/power/backlight/power-backlight-x11.h
+++ b/plugins/power/backlight/power-backlight-monitors-x11.h
@@ -12,38 +12,34 @@
  * Author:     tangjie02 <tangjie02@kylinos.com.cn>
  */
 
-#include "plugins/power/backlight/power-backlight-monitor-x11.h"
+#include <gdkmm.h>
+//
+#include <X11/extensions/Xrandr.h>
+#include <gdk/gdkx.h>
+
+#include "plugins/power/backlight/power-backlight-interface.h"
 
 namespace Kiran
 {
-enum PBXMonitorEvent
-{
-    // 显示器列表变化
-    PBX_MONITOR_EVENT_SCREEN_CHANGED,
-    // 显示器属性(亮度)可能发生变化
-    PBX_MONITOR_EVENT_PROPERTY_CHANGED,
-};
-
-class PowerBacklightX11
+class PowerBacklightMonitorsX11 : public PowerBacklightMonitors
 {
 public:
-    PowerBacklightX11();
-    virtual ~PowerBacklightX11();
-
-    void init();
-
-    // 是否支持设置亮度
-    bool support_backlight_extension() { return this->extension_supported_; };
+    PowerBacklightMonitorsX11();
+    virtual ~PowerBacklightMonitorsX11();
 
+    virtual void init();
     // 获取所有显示器亮度设置对象
-    PowerBacklightMonitorX11Vec get_monitors() { return this->backlight_monitors_; }
-
-    sigc::signal<void, PBXMonitorEvent> signal_monitor_changed() { return this->monitor_changed_; };
+    virtual PowerBacklightAbsoluteVec get_monitors() { return this->backlight_monitors_; }
+    virtual sigc::signal<void> signal_monitor_changed() { return this->monitor_changed_; };
+    virtual sigc::signal<void> signal_brightness_changed() { return this->brightness_changed_; }
 
 private:
     bool init_xrandr();
     Atom get_backlight_atom();
 
+    // 是否支持设置亮度
+    bool support_backlight_extension() { return this->extension_supported_; };
+
     void load_resource();
     void clear_resource();
 
@@ -62,8 +58,9 @@ private:
     Atom backlight_atom_;
     XRRScreenResources *resources_;
 
-    PowerBacklightMonitorX11Vec backlight_monitors_;
+    PowerBacklightAbsoluteVec backlight_monitors_;
 
-    sigc::signal<void, PBXMonitorEvent> monitor_changed_;
+    sigc::signal<void> monitor_changed_;
+    sigc::signal<void> brightness_changed_;
 };
 }  // namespace Kiran
\ No newline at end of file
diff --git a/plugins/power/backlight/power-backlight.cpp b/plugins/power/backlight/power-backlight.cpp
index 6763842..54a27b2 100644
--- a/plugins/power/backlight/power-backlight.cpp
+++ b/plugins/power/backlight/power-backlight.cpp
@@ -15,13 +15,13 @@
 #include "plugins/power/backlight/power-backlight.h"
 
 #include "plugins/power/backlight/power-backlight-kbd.h"
-#include "plugins/power/backlight/power-backlight-monitor.h"
+#include "plugins/power/backlight/power-backlight-monitors-controller.h"
 
 namespace Kiran
 {
 PowerBacklight::PowerBacklight()
 {
-    this->backlight_monitor_ = std::make_shared<PowerBacklightMonitor>();
+    this->backlight_monitor_ = std::make_shared<PowerBacklightMonitorsController>();
     this->backlight_kbd_ = std::make_shared<PowerBacklightKbd>();
 }
 
diff --git a/plugins/power/backlight/power-backlight.h b/plugins/power/backlight/power-backlight.h
index b8647ce..8c1ad9d 100644
--- a/plugins/power/backlight/power-backlight.h
+++ b/plugins/power/backlight/power-backlight.h
@@ -14,10 +14,9 @@
 
 #pragma once
 
-#include "plugins/power/backlight/power-backlight-base.h"
+#include "plugins/power/backlight/power-backlight-interface.h"
 #include "power-i.h"
 
-
 namespace Kiran
 {
 // 背光设备的亮度控制管理
diff --git a/plugins/power/tools/main.cpp b/plugins/power/tools/main.cpp
index f84b397..a5bc5be 100644
--- a/plugins/power/tools/main.cpp
+++ b/plugins/power/tools/main.cpp
@@ -18,8 +18,25 @@
 #include "config.h"
 #include "plugins/power/tools/power-backlight-helper.h"
 
+struct CommandOptions
+{
+    CommandOptions() : show_version(false),
+                       support_backlight(false),
+                       get_backlight_direcotry(false),
+                       get_brightness_value(false),
+                       get_max_brightness_value(false),
+                       set_brightness_value(-1) {}
+    bool show_version;
+    bool support_backlight;
+    bool get_backlight_direcotry;
+    bool get_brightness_value;
+    bool get_max_brightness_value;
+    int32_t set_brightness_value;
+};
+
 int main(int argc, char* argv[])
 {
+    CommandOptions options;
     Kiran::PowerBacklightHelper backlight_helper;
 
     Gio::init();
@@ -35,21 +52,59 @@ int main(int argc, char* argv[])
     Glib::OptionContext context;
     Glib::OptionGroup group("backlight-helper", _("power backlight helper"));
 
-    Glib::OptionEntry entry1;
-    entry1.set_long_name("get-brightness-value");
-    entry1.set_flags(Glib::OptionEntry::FLAG_NO_ARG);
-    entry1.set_description(N_("Get the current brightness value"));
+    group.add_entry(Kiran::MiscUtils::create_option_entry("version", N_("Output version infomation and exit.")),
+                    options.show_version);
+    group.add_entry(Kiran::MiscUtils::create_option_entry("support-backlight", N_("Whether the backlight device exists.")),
+                    options.support_backlight);
+    group.add_entry(Kiran::MiscUtils::create_option_entry("get-backlight-directory", N_("Get backlight monitor directory.")),
+                    options.support_backlight);
+    group.add_entry(Kiran::MiscUtils::create_option_entry("get-brightness-value", N_("Get the current brightness value.")),
+                    options.get_brightness_value);
+    group.add_entry(Kiran::MiscUtils::create_option_entry("get-max-brightness-value", N_("Get the max brightness value.")),
+                    options.get_max_brightness_value);
+    group.add_entry(Kiran::MiscUtils::create_option_entry("set-brightness-value", N_("Set the brightness value.")),
+                    options.set_brightness_value);
+
+    group.set_translation_domain(GETTEXT_PACKAGE);
+    context.set_main_group(group);
+
+    try
+    {
+        context.parse(argc, argv);
+    }
+    catch (const Glib::Exception& e)
+    {
+        KLOG_WARNING("%s", e.what().c_str());
+        return EXIT_FAILURE;
+    }
+
+    if (options.show_version)
+    {
+        fmt::print("{0}", PROJECT_VERSION);
+        return EXIT_SUCCESS;
+    }
 
-    Glib::OptionEntry entry2;
-    entry2.set_long_name("get-max-brightness-value");
-    entry2.set_flags(Glib::OptionEntry::FLAG_NO_ARG);
-    entry2.set_description(N_("Get the max brightness value"));
+    if (options.support_backlight)
+    {
+        fmt::print("{0}", backlight_helper.support_backlight() ? 1 : 0);
+        return EXIT_SUCCESS;
+    }
 
-    Glib::OptionEntry entry3;
-    entry3.set_long_name("set-brightness-value");
-    entry3.set_description(N_("Set the brightness value"));
+    if (options.get_backlight_direcotry)
+    {
+        fmt::print("{0}", backlight_helper.get_backlight_dir());
+        return EXIT_SUCCESS;
+    }
 
-    group.add_entry(entry1, [&backlight_helper](const Glib::ustring& option_name, const Glib::ustring&, bool) -> bool {
+    // 不支持获取和设置则直接返回
+    if (!backlight_helper.support_backlight())
+    {
+        fmt::print(stderr, "{0}", _("No backlights were found on your system"));
+        return EXIT_FAILURE;
+    }
+
+    if (options.get_brightness_value)
+    {
         auto brightness_value = backlight_helper.get_brightness_value();
         if (brightness_value >= 0)
         {
@@ -57,13 +112,14 @@ int main(int argc, char* argv[])
         }
         else
         {
-            fmt::print("{0}", _("Could not get the value of the backlight"));
-            return false;
+            fmt::print(stderr, "{0}", _("Could not get the value of the backlight"));
+            return EXIT_FAILURE;
         }
-        return true;
-    });
+        return EXIT_SUCCESS;
+    }
 
-    group.add_entry(entry2, [&backlight_helper](const Glib::ustring& option_name, const Glib::ustring&, bool) -> bool {
+    if (options.get_max_brightness_value)
+    {
         auto brightness_value = backlight_helper.get_brightness_max_value();
         if (brightness_value >= 0)
         {
@@ -71,40 +127,21 @@ int main(int argc, char* argv[])
         }
         else
         {
-            fmt::print("{0}", _("Could not get the maximum value of the backlight"));
-            return false;
+            fmt::print(stderr, "{0}", _("Could not get the maximum value of the backlight"));
+            return EXIT_FAILURE;
         }
-        return true;
-    });
+        return EXIT_SUCCESS;
+    }
 
-    group.add_entry(entry3, [&backlight_helper](const Glib::ustring& option_name, const Glib::ustring& value, bool has_value) -> bool {
+    if (options.set_brightness_value >= 0)
+    {
         std::string error;
-        auto brightness_value = std::strtol(value.c_str(), nullptr, 0);
-        if (!backlight_helper.set_brightness_value(brightness_value, error))
+        if (!backlight_helper.set_brightness_value(options.set_brightness_value, error))
         {
-            fmt::print("{0}", error);
+            fmt::print(stderr, "{0}", error);
+            return EXIT_FAILURE;
         }
-        return true;
-    });
-
-    group.set_translation_domain(GETTEXT_PACKAGE);
-    context.set_main_group(group);
-
-    // 不支持获取和设置则直接返回
-    if (!backlight_helper.support_backlight())
-    {
-        fmt::print("{0}", _("No backlights were found on your system"));
-        return EXIT_FAILURE;
-    }
-
-    try
-    {
-        context.parse(argc, argv);
-    }
-    catch (const Glib::Exception& e)
-    {
-        KLOG_WARNING("%s", e.what().c_str());
-        return EXIT_FAILURE;
+        return EXIT_SUCCESS;
     }
 
     return EXIT_SUCCESS;
diff --git a/plugins/power/tools/power-backlight-helper.h b/plugins/power/tools/power-backlight-helper.h
index d7bb069..39bcb03 100644
--- a/plugins/power/tools/power-backlight-helper.h
+++ b/plugins/power/tools/power-backlight-helper.h
@@ -28,6 +28,7 @@ public:
 
     // 是否支持亮度设置
     bool support_backlight() { return (this->brightness_value_ >= 0); };
+    std::string get_backlight_dir() { return this->backlight_dir_; };
 
     // 获取亮度值
     int32_t get_brightness_value();
-- 
2.33.0

