|
1 | | -// |
2 | | -// Copyright (C) 2014 Tom Beckmann |
3 | | -// |
4 | | -// This program is free software: you can redistribute it and/or modify |
5 | | -// it under the terms of the GNU General Public License as published by |
6 | | -// the Free Software Foundation, either version 3 of the License, or |
7 | | -// (at your option) any later version. |
8 | | -// |
9 | | -// This program is distributed in the hope that it will be useful, |
10 | | -// but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | -// GNU General Public License for more details. |
13 | | -// |
14 | | -// You should have received a copy of the GNU General Public License |
15 | | -// along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | | -// |
17 | | - |
18 | | -namespace Gala { |
19 | | - public delegate PluginInfo RegisterPluginFunction (); |
| 1 | +/* |
| 2 | + * SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | + * SPDX-FileCopyrightText: 2014 Tom Beckmann |
| 4 | + * 2025 elementary, Inc. (https://elementary.io) |
| 5 | + */ |
| 6 | + |
| 7 | +public class Gala.PluginManager : Object { |
| 8 | + private static PluginManager? instance = null; |
| 9 | + public static unowned PluginManager get_default () { |
| 10 | + if (instance == null) |
| 11 | + instance = new PluginManager (); |
| 12 | + |
| 13 | + return instance; |
| 14 | + } |
20 | 15 |
|
21 | | - public class PluginManager : Object { |
22 | | - private static PluginManager? instance = null; |
23 | | - public static unowned PluginManager get_default () { |
24 | | - if (instance == null) |
25 | | - instance = new PluginManager (); |
| 16 | + public delegate PluginInfo RegisterPluginFunction (); |
26 | 17 |
|
27 | | - return instance; |
28 | | - } |
| 18 | + public signal void regions_changed (); |
29 | 19 |
|
30 | | - public signal void regions_changed (); |
| 20 | + public bool initialized { get; private set; default = false; } |
31 | 21 |
|
32 | | - public bool initialized { get; private set; default = false; } |
| 22 | + private X.Xrectangle[] _regions = {}; |
| 23 | + public unowned X.Xrectangle[] get_regions () { |
| 24 | + return _regions; |
| 25 | + } |
33 | 26 |
|
34 | | - private X.Xrectangle[] _regions = {}; |
35 | | - public unowned X.Xrectangle[] get_regions () { |
36 | | - return _regions; |
37 | | - } |
| 27 | + public string? window_switcher_provider { get; private set; default = null; } |
| 28 | + public string? window_overview_provider { get; private set; default = null; } |
| 29 | + public string? workspace_view_provider { get; private set; default = null; } |
38 | 30 |
|
39 | | - public string? window_switcher_provider { get; private set; default = null; } |
40 | | - public string? window_overview_provider { get; private set; default = null; } |
41 | | - public string? workspace_view_provider { get; private set; default = null; } |
| 31 | + private HashTable<string,Plugin> plugins; |
| 32 | + private File plugin_dir; |
42 | 33 |
|
43 | | - private HashTable<string,Plugin> plugins; |
44 | | - private File plugin_dir; |
| 34 | + private WindowManager? wm = null; |
45 | 35 |
|
46 | | - private WindowManager? wm = null; |
| 36 | + private Gee.LinkedList<PluginInfo?> load_later_plugins; |
47 | 37 |
|
48 | | - private Gee.LinkedList<PluginInfo?> load_later_plugins; |
| 38 | + private PluginManager () { |
| 39 | + plugins = new HashTable<string,Plugin> (str_hash, str_equal); |
| 40 | + load_later_plugins = new Gee.LinkedList<PluginInfo?> (); |
49 | 41 |
|
50 | | - private PluginManager () { |
51 | | - plugins = new HashTable<string,Plugin> (str_hash, str_equal); |
52 | | - load_later_plugins = new Gee.LinkedList<PluginInfo?> (); |
| 42 | + if (!Module.supported ()) { |
| 43 | + warning ("Modules are not supported on this platform"); |
| 44 | + return; |
| 45 | + } |
53 | 46 |
|
54 | | - if (!Module.supported ()) { |
55 | | - warning ("Modules are not supported on this platform"); |
56 | | - return; |
| 47 | + plugin_dir = File.new_for_path (Config.PLUGINDIR); |
| 48 | + if (!plugin_dir.query_exists ()) |
| 49 | + return; |
| 50 | + |
| 51 | + try { |
| 52 | + var enumerator = plugin_dir.enumerate_children (FileAttribute.STANDARD_NAME + |
| 53 | + "," + FileAttribute.STANDARD_CONTENT_TYPE, 0); |
| 54 | + FileInfo info; |
| 55 | + while ((info = enumerator.next_file ()) != null) { |
| 56 | + if (info.get_content_type () == "application/x-sharedlib") |
| 57 | + load_module (info.get_name ()); |
57 | 58 | } |
| 59 | + } catch (Error e) { |
| 60 | + warning (e.message); |
| 61 | + } |
58 | 62 |
|
59 | | - plugin_dir = File.new_for_path (Config.PLUGINDIR); |
60 | | - if (!plugin_dir.query_exists ()) |
61 | | - return; |
62 | | - |
63 | | - try { |
64 | | - var enumerator = plugin_dir.enumerate_children (FileAttribute.STANDARD_NAME + |
65 | | - "," + FileAttribute.STANDARD_CONTENT_TYPE, 0); |
66 | | - FileInfo info; |
67 | | - while ((info = enumerator.next_file ()) != null) { |
68 | | - if (info.get_content_type () == "application/x-sharedlib") |
69 | | - load_module (info.get_name ()); |
| 63 | + try { |
| 64 | + plugin_dir.monitor_directory (FileMonitorFlags.NONE, null).changed.connect ((file, other_file, type) => { |
| 65 | + if (type == FileMonitorEvent.CREATED) { |
| 66 | + load_module (file.get_basename ()); |
70 | 67 | } |
71 | | - } catch (Error e) { |
72 | | - warning (e.message); |
73 | | - } |
74 | | - |
75 | | - try { |
76 | | - plugin_dir.monitor_directory (FileMonitorFlags.NONE, null).changed.connect ((file, other_file, type) => { |
77 | | - if (type == FileMonitorEvent.CREATED) { |
78 | | - load_module (file.get_basename ()); |
79 | | - } |
80 | | - }); |
81 | | - } catch (Error e) { |
82 | | - warning (e.message); |
83 | | - } |
| 68 | + }); |
| 69 | + } catch (Error e) { |
| 70 | + warning (e.message); |
84 | 71 | } |
| 72 | + } |
85 | 73 |
|
86 | | - private bool load_module (string plugin_name) { |
87 | | - var path = Module.build_path (plugin_dir.get_path (), plugin_name); |
88 | | - var module = Module.open (path, ModuleFlags.LOCAL); |
89 | | - if (module == null) { |
90 | | - warning (Module.error ()); |
91 | | - return false; |
92 | | - } |
93 | | - |
94 | | - void* function; |
95 | | - module.symbol ("register_plugin", out function); |
96 | | - if (function == null) { |
97 | | - warning ("%s failed to register: register_plugin() function not found", plugin_name); |
98 | | - return false; |
99 | | - } |
100 | | - unowned RegisterPluginFunction register = (RegisterPluginFunction)function; |
| 74 | + private bool load_module (string plugin_name) { |
| 75 | + var path = Module.build_path (plugin_dir.get_path (), plugin_name); |
| 76 | + var module = Module.open (path, ModuleFlags.LOCAL); |
| 77 | + if (module == null) { |
| 78 | + warning (Module.error ()); |
| 79 | + return false; |
| 80 | + } |
101 | 81 |
|
102 | | - var info = register (); |
103 | | - if (info.plugin_type.is_a (typeof (Plugin)) == false) { |
104 | | - warning ("%s does not return a class of type Plugin", plugin_name); |
105 | | - return false; |
106 | | - } |
| 82 | + void* function; |
| 83 | + module.symbol ("register_plugin", out function); |
| 84 | + if (function == null) { |
| 85 | + warning ("%s failed to register: register_plugin() function not found", plugin_name); |
| 86 | + return false; |
| 87 | + } |
| 88 | + unowned RegisterPluginFunction register = (RegisterPluginFunction)function; |
107 | 89 |
|
108 | | - if (!check_provides (info.name, info.provides)) { |
109 | | - return false; |
110 | | - } |
| 90 | + var info = register (); |
| 91 | + if (info.plugin_type.is_a (typeof (Plugin)) == false) { |
| 92 | + warning ("%s does not return a class of type Plugin", plugin_name); |
| 93 | + return false; |
| 94 | + } |
111 | 95 |
|
112 | | - info.module_name = plugin_name; |
113 | | - module.make_resident (); |
| 96 | + if (!check_provides (info.name, info.provides)) { |
| 97 | + return false; |
| 98 | + } |
114 | 99 |
|
115 | | - if (info.load_priority == LoadPriority.DEFERRED && !initialized) { |
116 | | - load_later_plugins.add (info); |
117 | | - } else { |
118 | | - load_plugin_class (info); |
119 | | - } |
| 100 | + info.module_name = plugin_name; |
| 101 | + module.make_resident (); |
120 | 102 |
|
121 | | - return true; |
| 103 | + if (info.load_priority == LoadPriority.DEFERRED && !initialized) { |
| 104 | + load_later_plugins.add (info); |
| 105 | + } else { |
| 106 | + load_plugin_class (info); |
122 | 107 | } |
123 | 108 |
|
124 | | - private void load_plugin_class (PluginInfo info) { |
125 | | - var plugin = (Plugin)Object.@new (info.plugin_type); |
126 | | - plugins.set (info.module_name, plugin); |
| 109 | + return true; |
| 110 | + } |
127 | 111 |
|
128 | | - debug ("Loaded plugin %s (%s)", info.name, info.module_name); |
| 112 | + private void load_plugin_class (PluginInfo info) { |
| 113 | + var plugin = (Plugin)Object.@new (info.plugin_type); |
| 114 | + plugins.set (info.module_name, plugin); |
129 | 115 |
|
130 | | - if (initialized) { |
131 | | - initialize_plugin (info.module_name, plugin); |
132 | | - recalculate_regions (); |
133 | | - } |
134 | | - } |
| 116 | + debug ("Loaded plugin %s (%s)", info.name, info.module_name); |
135 | 117 |
|
136 | | - private void initialize_plugin (string plugin_name, Plugin plugin) { |
137 | | - plugin.initialize (wm); |
138 | | - plugin.region_changed.connect (recalculate_regions); |
| 118 | + if (initialized) { |
| 119 | + initialize_plugin (info.module_name, plugin); |
| 120 | + recalculate_regions (); |
139 | 121 | } |
| 122 | + } |
140 | 123 |
|
141 | | - private bool check_provides (string name, PluginFunction provides) { |
142 | | - var message = "Plugins %s and %s both provide %s functionality, using first one only"; |
143 | | - switch (provides) { |
144 | | - case PluginFunction.WORKSPACE_VIEW: |
145 | | - if (workspace_view_provider != null) { |
146 | | - warning (message, workspace_view_provider, name, "workspace view"); |
147 | | - return false; |
148 | | - } |
149 | | - workspace_view_provider = name; |
150 | | - return true; |
151 | | - case PluginFunction.WINDOW_OVERVIEW: |
152 | | - if (window_overview_provider != null) { |
153 | | - warning (message, window_overview_provider, name, "window overview"); |
154 | | - return false; |
155 | | - } |
156 | | - window_overview_provider = name; |
157 | | - return true; |
158 | | - case PluginFunction.WINDOW_SWITCHER: |
159 | | - if (window_switcher_provider != null) { |
160 | | - warning (message, window_switcher_provider, name, "window switcher"); |
161 | | - return false; |
162 | | - } |
163 | | - window_switcher_provider = name; |
164 | | - return true; |
165 | | - default: |
166 | | - break; |
167 | | - } |
| 124 | + private void initialize_plugin (string plugin_name, Plugin plugin) { |
| 125 | + plugin.initialize (wm); |
| 126 | + plugin.region_changed.connect (recalculate_regions); |
| 127 | + } |
168 | 128 |
|
169 | | - return true; |
| 129 | + private bool check_provides (string name, PluginFunction provides) { |
| 130 | + var message = "Plugins %s and %s both provide %s functionality, using first one only"; |
| 131 | + switch (provides) { |
| 132 | + case PluginFunction.WORKSPACE_VIEW: |
| 133 | + if (workspace_view_provider != null) { |
| 134 | + warning (message, workspace_view_provider, name, "workspace view"); |
| 135 | + return false; |
| 136 | + } |
| 137 | + workspace_view_provider = name; |
| 138 | + return true; |
| 139 | + case PluginFunction.WINDOW_OVERVIEW: |
| 140 | + if (window_overview_provider != null) { |
| 141 | + warning (message, window_overview_provider, name, "window overview"); |
| 142 | + return false; |
| 143 | + } |
| 144 | + window_overview_provider = name; |
| 145 | + return true; |
| 146 | + case PluginFunction.WINDOW_SWITCHER: |
| 147 | + if (window_switcher_provider != null) { |
| 148 | + warning (message, window_switcher_provider, name, "window switcher"); |
| 149 | + return false; |
| 150 | + } |
| 151 | + window_switcher_provider = name; |
| 152 | + return true; |
| 153 | + default: |
| 154 | + break; |
170 | 155 | } |
171 | 156 |
|
172 | | - public void initialize (WindowManager _wm) { |
173 | | - wm = _wm; |
| 157 | + return true; |
| 158 | + } |
174 | 159 |
|
175 | | - plugins.@foreach (initialize_plugin); |
176 | | - recalculate_regions (); |
| 160 | + public void initialize (WindowManager _wm) { |
| 161 | + wm = _wm; |
177 | 162 |
|
178 | | - initialized = true; |
179 | | - } |
| 163 | + plugins.@foreach (initialize_plugin); |
| 164 | + recalculate_regions (); |
180 | 165 |
|
181 | | - public void load_waiting_plugins () { |
182 | | - foreach (var info in load_later_plugins) { |
183 | | - load_plugin_class (info); |
184 | | - } |
| 166 | + initialized = true; |
| 167 | + } |
185 | 168 |
|
186 | | - load_later_plugins.clear (); |
| 169 | + public void load_waiting_plugins () { |
| 170 | + foreach (var info in load_later_plugins) { |
| 171 | + load_plugin_class (info); |
187 | 172 | } |
188 | 173 |
|
189 | | - public Plugin? get_plugin (string id) { |
190 | | - return plugins.lookup (id); |
191 | | - } |
| 174 | + load_later_plugins.clear (); |
| 175 | + } |
192 | 176 |
|
193 | | - /** |
194 | | - * Iterate over all plugins and grab their regions, update the regions |
195 | | - * array accordingly and emit the regions_changed signal. |
196 | | - */ |
197 | | - private void recalculate_regions () { |
198 | | - X.Xrectangle[] regions = {}; |
199 | | - |
200 | | - plugins.@foreach ((name, plugin) => { |
201 | | - foreach (var region in plugin.get_region ()) { |
202 | | - X.Xrectangle rect = { |
203 | | - (short) region.x, |
204 | | - (short) region.y, |
205 | | - (ushort) region.width, |
206 | | - (ushort) region.height |
207 | | - }; |
208 | | - |
209 | | - regions += rect; |
210 | | - } |
211 | | - }); |
| 177 | + public Plugin? get_plugin (string id) { |
| 178 | + return plugins.lookup (id); |
| 179 | + } |
212 | 180 |
|
213 | | - this._regions = regions; |
214 | | - regions_changed (); |
215 | | - } |
| 181 | + /** |
| 182 | + * Iterate over all plugins and grab their regions, update the regions |
| 183 | + * array accordingly and emit the regions_changed signal. |
| 184 | + */ |
| 185 | + private void recalculate_regions () { |
| 186 | + X.Xrectangle[] regions = {}; |
| 187 | + |
| 188 | + plugins.@foreach ((name, plugin) => { |
| 189 | + foreach (var region in plugin.get_region ()) { |
| 190 | + X.Xrectangle rect = { |
| 191 | + (short) region.x, |
| 192 | + (short) region.y, |
| 193 | + (ushort) region.width, |
| 194 | + (ushort) region.height |
| 195 | + }; |
| 196 | + |
| 197 | + regions += rect; |
| 198 | + } |
| 199 | + }); |
| 200 | + |
| 201 | + this._regions = regions; |
| 202 | + regions_changed (); |
216 | 203 | } |
217 | 204 | } |
0 commit comments