main.cpp
5476 bytes
1#include <hyprland/src/Compositor.hpp>
2#include <hyprland/src/config/values/ConfigValues.hpp>
3#include <hyprland/src/plugins/PluginAPI.hpp>
4#include <hyprland/src/render/Renderer.hpp>
5#include <hyprland/src/render/OpenGL.hpp>
6#include <hyprland/src/version.h>
7
8#include "dispatchers.hpp"
9#include "globals.hpp"
10#include "TabGroup.hpp"
11
12APICALL EXPORT std::string PLUGIN_API_VERSION() { return HYPRLAND_API_VERSION; }
13
14APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
15 PHANDLE = handle;
16
17#ifndef HY3_NO_VERSION_CHECK
18 const std::string COMPOSITOR_HASH = __hyprland_api_get_hash();
19 const std::string CLIENT_HASH = __hyprland_api_get_client_hash();
20
21 if (COMPOSITOR_HASH != CLIENT_HASH) {
22 HyprlandAPI::addNotification(
23 PHANDLE,
24 "[hy3] hy3 was compiled for a different version of hyprland; refusing to load.",
25 CHyprColor {1.0, 0.2, 0.2, 1.0},
26 10000
27 );
28
29 throw std::runtime_error("[hy3] target hyprland version mismatch");
30 }
31#endif
32
33#define CONF(NAME, TYPE, VALUE) \
34 HyprlandAPI::addConfigValueV2(PHANDLE, makeShared<Config::Values::TYPE>("plugin:hy3:" NAME, "", VALUE))
35
36 // general
37 CONF("no_gaps_when_only", Int, 0);
38 CONF("node_collapse_policy", Int, 2);
39 CONF("group_inset", Int, 10);
40 CONF("tab_first_window", Bool, false);
41
42 // tabs
43 CONF("tabs:height", Int, 22);
44 CONF("tabs:padding", Int, 5);
45 CONF("tabs:padding_horizontal", Int, -1);
46 CONF("tabs:padding_outer", Bool, true);
47 CONF("tabs:from_top", Bool, false);
48 CONF("tabs:radius", Int, 6);
49 CONF("tabs:border_width", Int, 2);
50 CONF("tabs:render_text", Bool, true);
51 CONF("tabs:text_center", Bool, true);
52 CONF("tabs:text_font", String, "Sans");
53 CONF("tabs:text_height", Int, 8);
54 CONF("tabs:text_padding", Int, 3);
55 CONF("tabs:opacity", Float, 1.0);
56 CONF("tabs:blur", Bool, true);
57 CONF("tabs:colors:active", Color, 0x4033ccff);
58 CONF("tabs:colors:active_border", Color, 0xee33ccff);
59 CONF("tabs:colors:active_text", Color, 0xffffffff);
60 CONF("tabs:colors:active_alt_monitor", Color, 0x40606060);
61 CONF("tabs:colors:active_alt_monitor_border", Color, 0xee808080);
62 CONF("tabs:colors:active_alt_monitor_text", Color, 0xffffffff);
63 CONF("tabs:colors:focused", Color, 0x40606060);
64 CONF("tabs:colors:focused_border", Color, 0xee808080);
65 CONF("tabs:colors:focused_text", Color, 0xffffffff);
66 CONF("tabs:colors:inactive", Color, 0x20303030);
67 CONF("tabs:colors:inactive_border", Color, 0xaa606060);
68 CONF("tabs:colors:inactive_text", Color, 0xffffffff);
69 CONF("tabs:colors:urgent", Color, 0x40ff2233);
70 CONF("tabs:colors:urgent_border", Color, 0xeeff2233);
71 CONF("tabs:colors:urgent_text", Color, 0xffffffff);
72 CONF("tabs:colors:locked", Color, 0x40909033);
73 CONF("tabs:colors:locked_border", Color, 0xee909033);
74 CONF("tabs:colors:locked_text", Color, 0xffffffff);
75
76 // autotiling
77 CONF("autotile:enable", Bool, false);
78 CONF("autotile:ephemeral_groups", Bool, true);
79 CONF("autotile:trigger_height", Int, 0);
80 CONF("autotile:trigger_width", Int, 0);
81 CONF("autotile:workspaces", String, "all");
82
83#undef CONF
84
85 HyprlandAPI::addTiledAlgo(PHANDLE, "hy3", &typeid(Hy3Layout), []() -> UP<Layout::ITiledAlgorithm> {
86 return makeUnique<Hy3Layout>();
87 });
88
89 g_renderListener = Event::bus()->m_events.render.stage.listen([](const Event::SRenderStageEvent& event) {
90 static bool rendering_normally = false;
91 static std::vector<Hy3TabGroup*> rendered_groups;
92
93 switch (event.stage) {
94 case RENDER_PRE_WINDOWS:
95 rendering_normally = true;
96 rendered_groups.clear();
97 break;
98 case RENDER_POST_WINDOW:
99 if (!rendering_normally || !event.context) break;
100
101 for (auto& wp: g_tabGroups) {
102 auto* entry = wp.get();
103 if (!entry) continue;
104 if (!entry->hidden
105 && entry->target_window == event.context->get().m_data.currentWindow.lock()
106 && std::find(rendered_groups.begin(), rendered_groups.end(), entry)
107 == rendered_groups.end())
108 {
109 g_pHyprRenderer->addPassElement(event.context->get(), makeUnique<Hy3TabPassElement>(entry));
110 rendered_groups.push_back(entry);
111 }
112 }
113 break;
114 case RENDER_POST_WINDOWS: rendering_normally = false; break;
115 default: break;
116 }
117 });
118
119 g_tickListener = Event::bus()->m_events.tick.listen([]() {
120 for (auto& wp: g_tabGroups) {
121 if (auto* tg = wp.get()) tg->tick();
122 }
123 std::erase_if(g_destroyingTabGroups, [](auto& up) { return up->bar.destroy; });
124 std::erase_if(g_tabGroups, [](auto& wp) { return !wp; });
125 });
126
127 g_windowTitleListener = Event::bus()->m_events.window.title.listen([](PHLWINDOW window) {
128 if (!window) return;
129 auto* hy3 = hy3InstanceForWorkspace(window->m_workspace);
130 if (!hy3) return;
131 auto* node = hy3->getNodeFromWindow(window.get());
132 if (!node) return;
133 node->updateTabBarRecursive();
134 });
135
136 g_urgentListener = Event::bus()->m_events.window.urgent.listen([](PHLWINDOW window) {
137 if (!window) return;
138 window->m_hints |= Desktop::View::WINDOW_HINT_URGENT;
139 auto* hy3 = hy3InstanceForWorkspace(window->m_workspace);
140 if (!hy3) return;
141 auto* node = hy3->getNodeFromWindow(window.get());
142 if (!node) return;
143 node->updateTabBarRecursive();
144 });
145
146 registerDispatchers();
147
148 HyprlandAPI::reloadConfig();
149
150 return {"hy3", "i3 like layout for hyprland", "outfoxxed", "0.1"};
151}
152
153APICALL EXPORT void PLUGIN_EXIT() {
154 g_renderListener.reset();
155 g_tickListener.reset();
156 g_windowTitleListener.reset();
157 g_urgentListener.reset();
158
159 g_tabGroups.clear();
160 g_destroyingTabGroups.clear();
161}