summaryrefslogtreecommitdiff
path: root/gui-libs/pixdecor/files/pixdecor-shell-theme.patch
blob: 5be67928ac2d0958c48a901d138c9a453a283a78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
diff --git a/metadata/pixdecor.xml b/metadata/pixdecor.xml
index 5ce4729..843cc1b 100644
--- a/metadata/pixdecor.xml
+++ b/metadata/pixdecor.xml
@@ -6,6 +6,11 @@
 		<category>Effects</category>
 		<group>
 			<_short>Window frames</_short>
+			<option name="follow_shell_theme" type="bool">
+				<_short>Follow shell theme colors</_short>
+				<_long>Use the current shell palette for frames, title text, buttons and pinstripe hue. Custom colors are retained and used when disabled or when the shell is unavailable. Border geometry, shading and pinstripe opacity stay unchanged.</_long>
+				<default>false</default>
+			</option>
 			<option name="border_size" type="int">
 				<_short>Border size</_short>
 				<_long>Sets the border size in pixels.</_long>
diff --git a/src/deco-effects.cpp b/src/deco-effects.cpp
index f092244..5563f7e 100644
--- a/src/deco-effects.cpp
+++ b/src/deco-effects.cpp
@@ -32,6 +32,7 @@
 #include <wayfire/render.hpp>
 
 #include "deco-effects.hpp"
+#include "shell-theme.hpp"
 #include "smoke-shaders.hpp"
 #include "sculpted-overlay.hpp"
 
@@ -2592,7 +2593,7 @@ void smoke_t::step_effect(const wf::scene::render_instruction_t& data, wf::geome
                 GL_CALL(glUniform4fv(9, 1, shadow_color_f));
                 if (std::string(overlay_engine) == "sculpted")
                 {
-                    wf::color_t rim = pinstripe_color;
+                    wf::color_t rim = shell_pinstripe_color(wf::color_t(pinstripe_color));
                     GLfloat rim_f[4] = {GLfloat(rim.r), GLfloat(rim.g), GLfloat(rim.b), GLfloat(rim.a)};
                     GL_CALL(glUniform1f(10, border_depth));
                     GL_CALL(glUniform4fv(11, 1, rim_f));
diff --git a/src/deco-theme.cpp b/src/deco-theme.cpp
index 32a2ced..9ae7166 100644
--- a/src/deco-theme.cpp
+++ b/src/deco-theme.cpp
@@ -1,4 +1,5 @@
 #include "deco-theme.hpp"
+#include "shell-theme.hpp"
 #include <wayfire/core.hpp>
 #include <wayfire/opengl.hpp>
 #include <map>
@@ -43,6 +44,13 @@ void pixdecor_theme_t::update_colors(void)
     bg = wf::color_t(bg_color);
     fg_text = wf::color_t(fg_text_color);
     bg_text = wf::color_t(bg_text_color);
+    if (shell_colors_active())
+    {
+        fg = shell_palette->surface;
+        bg = shell_palette->background;
+        fg_text = shell_palette->foreground;
+        bg_text = shell_palette->muted;
+    }
 }
 
 std::unique_ptr<PangoFontDescription,
@@ -218,11 +226,8 @@ static cairo_surface_t *get_cairo_surface(button_type_t button, int w, int h, in
 
     /** Draw the button  */
     cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
-    cairo_set_source_rgba(cr,
-        wf::color_t(button_color).r,
-        wf::color_t(button_color).g,
-        wf::color_t(button_color).b,
-        wf::color_t(button_color).a * alpha);
+    const auto ink = shell_button_color(wf::color_t(button_color));
+    cairo_set_source_rgba(cr, ink.r, ink.g, ink.b, ink.a * alpha);
     double line_width = button_line_thickness;
     switch (button)
     {
diff --git a/src/decoration.cpp b/src/decoration.cpp
index 0ceb16f..fa08e0b 100644
--- a/src/decoration.cpp
+++ b/src/decoration.cpp
@@ -21,6 +21,10 @@
 #include "shade.hpp"
 #include "deco-effects.hpp"
 #include <wayfire/config/config-manager.hpp>
+#include <wayfire/plugins/ipc/ipc-method-repository.hpp>
+#include <wayfire/util.hpp>
+#include <chrono>
+#include "shell-theme.hpp"
 
 namespace wf
 {
@@ -28,6 +32,20 @@ namespace pixdecor
 {
 class wayfire_pixdecor : public wf::plugin_interface_t
 {
+    wf::option_wrapper_t<bool> follow_theme;
+    wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> theme_methods;
+    wf::ipc::client_interface_t *theme_owner = nullptr;
+    std::string theme_instance, theme_signature;
+    wf::signal::connection_t<wf::ipc::client_disconnected_signal> theme_disconnected =
+        [this] (wf::ipc::client_disconnected_signal *event)
+    {
+        if (event->client != theme_owner) return;
+        theme_owner = nullptr;
+        shell_palette.reset();
+        theme_signature.clear();
+        update_colors();
+        recreate_frames();
+    };
     wf::option_wrapper_t<int> border_size{"pixdecor/border_size"};
     wf::option_wrapper_t<std::string> title_font{"pixdecor/title_font"};
     wf::option_wrapper_t<int> title_text_align{"pixdecor/title_text_align"};
@@ -240,6 +258,53 @@ class wayfire_pixdecor : public wf::plugin_interface_t
             }
         };
         promote("border_depth", 0.65);
+        promote("follow_shell_theme", false);
+        follow_theme.load_option("pixdecor/follow_shell_theme");
+        follow_shell_theme = follow_theme;
+        follow_theme.set_callback([this]
+        {
+            follow_shell_theme = follow_theme;
+            update_colors();
+            recreate_frames();
+        });
+        theme_instance = std::to_string(getpid()) + ":" + std::to_string(
+            std::chrono::steady_clock::now().time_since_epoch().count());
+        theme_methods->connect(&theme_disconnected);
+        theme_methods->register_method("pixdecor/theme-status", [this] (wf::json_t)
+        {
+            auto result = wf::ipc::json_ok();
+            result["version"] = 1;
+            result["instance"] = theme_instance;
+            result["following"] = bool(follow_theme);
+            result["received"] = shell_palette.has_value();
+            result["signature"] = theme_signature;
+            return result;
+        });
+        theme_methods->register_method("pixdecor/theme-configure",
+            [this] (wf::json_t data, wf::ipc::client_interface_t *client)
+        {
+            if (!client || !data["version"].is_int64() || data["version"].as_int64() != 1 ||
+                !data["instance"].is_string() || data["instance"].as_string() != theme_instance ||
+                (theme_owner && theme_owner != client))
+                return wf::ipc::json_error("Unavailable theme owner or instance");
+            try
+            {
+                const auto next = parse_shell_palette(data);
+                const auto signature = data.serialize();
+                if (signature != theme_signature)
+                {
+                    shell_palette = next;
+                    theme_owner = client;
+                    theme_signature = signature;
+                    update_colors();
+                    recreate_frames();
+                }
+                return wf::ipc::json_ok();
+            } catch (const std::exception& error)
+            {
+                return wf::ipc::json_error(error.what());
+            }
+        });
         promote("pinstripe_color", wf::color_t{90.0 / 255, 113.0 / 255, 129.0 / 255, 36.0 / 255});
         border_depth.load_option("pixdecor/border_depth");
         pinstripe_color.load_option("pixdecor/pinstripe_color");
@@ -550,6 +615,12 @@ class wayfire_pixdecor : public wf::plugin_interface_t
 
     void fini() override
     {
+        theme_methods->unregister_method("pixdecor/theme-status");
+        theme_methods->unregister_method("pixdecor/theme-configure");
+        theme_disconnected.disconnect();
+        shell_palette.reset();
+        follow_shell_theme = false;
+        theme_owner = nullptr;
         for (auto view : wf::get_core().get_all_views())
         {
             if (auto toplevel = wf::toplevel_cast(view))
diff --git a/src/shell-theme.hpp b/src/shell-theme.hpp
new file mode 100644
index 0000000..5bc7877
--- /dev/null
+++ b/src/shell-theme.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <wayfire/util.hpp>
+#include <wayfire/nonstd/json.hpp>
+#include <optional>
+#include <stdexcept>
+#include <string>
+
+namespace wf::pixdecor
+{
+struct shell_palette_t
+{
+    wf::color_t background, surface, foreground, muted, border;
+};
+
+inline wf::color_t shell_color(const wf::json_t& value)
+{
+    if (!value.is_string()) throw std::invalid_argument("Expected a theme color");
+    const auto text = value.as_string();
+    if (text.size() != 7 || text.front() != '#' ||
+        text.find_first_not_of("0123456789abcdefABCDEF", 1) != std::string::npos)
+        throw std::invalid_argument("Expected #RRGGBB");
+    return {std::stoul(text.substr(1, 2), nullptr, 16) / 255.0,
+        std::stoul(text.substr(3, 2), nullptr, 16) / 255.0,
+        std::stoul(text.substr(5, 2), nullptr, 16) / 255.0, 1.0};
+}
+
+inline shell_palette_t parse_shell_palette(const wf::json_t& data)
+{
+    return {shell_color(data["background"]), shell_color(data["surface"]),
+        shell_color(data["foreground"]), shell_color(data["muted"]),
+        shell_color(data["border"])};
+}
+
+// Live presentation only. Saved custom colors remain in wayfire.ini.
+inline std::optional<shell_palette_t> shell_palette;
+inline bool follow_shell_theme = false;
+inline bool shell_colors_active() { return follow_shell_theme && shell_palette.has_value(); }
+inline wf::color_t shell_button_color(wf::color_t custom)
+{
+    return shell_colors_active() ? shell_palette->foreground : custom;
+}
+inline wf::color_t shell_pinstripe_color(wf::color_t custom)
+{
+    if (!shell_colors_active()) return custom;
+    auto themed = shell_palette->border;
+    themed.a = custom.a;
+    return themed;
+}
+}