1 /*
2 * Copyright (c) 2001-2020 Haiku, Inc. All right reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Author:
6 * DarkWyrm <bpmagic@columbus.rr.com>
7 * Clemens Zeidler <haiku@clemens-zeidler.de>
8 * Joseph Groover <looncraz@satx.rr.com>
9 * John Scipione <jscipione@gmail.com>
10 */
11
12 #include "DecorManager.h"
13
14 #include <Directory.h>
15 #include <Entry.h>
16 #include <File.h>
17 #include <FindDirectory.h>
18 #include <Message.h>
19 #include <Path.h>
20 #include <Rect.h>
21
22 #include <syslog.h>
23
24 #include "AppServer.h"
25 #include "Desktop.h"
26 #include "DesktopSettings.h"
27 #include "ServerConfig.h"
28 #include "SATDecorator.h"
29 #include "Window.h"
30
31 typedef float get_version(void);
32 typedef DecorAddOn* create_decor_addon(image_id id, const char* name);
33
34 // Globals
35 DecorManager gDecorManager;
36
37
DecorAddOn(image_id id,const char * name)38 DecorAddOn::DecorAddOn(image_id id, const char* name)
39 :
40 fImageID(id),
41 fName(name)
42 {
43 }
44
45
~DecorAddOn()46 DecorAddOn::~DecorAddOn()
47 {
48 }
49
50
51 status_t
InitCheck() const52 DecorAddOn::InitCheck() const
53 {
54 return B_OK;
55 }
56
57
58 Decorator*
AllocateDecorator(Desktop * desktop,DrawingEngine * engine,BRect rect,const char * title,window_look look,uint32 flags)59 DecorAddOn::AllocateDecorator(Desktop* desktop, DrawingEngine* engine,
60 BRect rect, const char* title, window_look look, uint32 flags)
61 {
62 if (!desktop->LockSingleWindow())
63 return NULL;
64
65 DesktopSettings settings(desktop);
66 Decorator* decorator;
67 decorator = _AllocateDecorator(settings, rect, desktop);
68 desktop->UnlockSingleWindow();
69 if (!decorator)
70 return NULL;
71
72 decorator->UpdateColors(settings);
73
74 if (decorator->AddTab(settings, title, look, flags) == NULL) {
75 delete decorator;
76 return NULL;
77 }
78
79 decorator->SetDrawingEngine(engine);
80 return decorator;
81 }
82
83
84 WindowBehaviour*
AllocateWindowBehaviour(Window * window)85 DecorAddOn::AllocateWindowBehaviour(Window* window)
86 {
87 return new (std::nothrow)SATWindowBehaviour(window,
88 window->Desktop()->GetStackAndTile());
89 }
90
91
92 const DesktopListenerList&
GetDesktopListeners()93 DecorAddOn::GetDesktopListeners()
94 {
95 return fDesktopListeners;
96 }
97
98
99 Decorator*
_AllocateDecorator(DesktopSettings & settings,BRect rect,Desktop * desktop)100 DecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect,
101 Desktop* desktop)
102 {
103 return new (std::nothrow)SATDecorator(settings, rect, desktop);
104 }
105
106
107 // #pragma mark -
108
109
DecorManager()110 DecorManager::DecorManager()
111 :
112 fDefaultDecor(-1, "Default"),
113 fCurrentDecor(&fDefaultDecor),
114 fPreviewDecor(NULL),
115 fPreviewWindow(NULL),
116 fCurrentDecorPath("Default")
117 {
118 _LoadSettingsFromDisk();
119 }
120
121
~DecorManager()122 DecorManager::~DecorManager()
123 {
124 }
125
126
127 Decorator*
AllocateDecorator(Window * window)128 DecorManager::AllocateDecorator(Window* window)
129 {
130 // Create a new instance of the current decorator.
131 // Ownership is that of the caller
132
133 if (!fCurrentDecor) {
134 // We should *never* be here. If we do, it's a bug.
135 debugger("DecorManager::AllocateDecorator has a NULL decorator");
136 return NULL;
137 }
138
139 // Are we previewing a specific decorator?
140 if (window == fPreviewWindow) {
141 if (fPreviewDecor != NULL) {
142 return fPreviewDecor->AllocateDecorator(window->Desktop(),
143 window->GetDrawingEngine(), window->Frame(), window->Title(),
144 window->Look(), window->Flags());
145 } else {
146 fPreviewWindow = NULL;
147 }
148 }
149
150 return fCurrentDecor->AllocateDecorator(window->Desktop(),
151 window->GetDrawingEngine(), window->Frame(), window->Title(),
152 window->Look(), window->Flags());
153 }
154
155
156 WindowBehaviour*
AllocateWindowBehaviour(Window * window)157 DecorManager::AllocateWindowBehaviour(Window* window)
158 {
159 if (!fCurrentDecor) {
160 // We should *never* be here. If we do, it's a bug.
161 debugger("DecorManager::AllocateDecorator has a NULL decorator");
162 return NULL;
163 }
164
165 return fCurrentDecor->AllocateWindowBehaviour(window);
166 }
167
168
169 void
CleanupForWindow(Window * window)170 DecorManager::CleanupForWindow(Window* window)
171 {
172 // Given window is being deleted, do any cleanup needed
173 if (fPreviewWindow == window && window != NULL){
174 fPreviewWindow = NULL;
175
176 if (fPreviewDecor != NULL)
177 unload_add_on(fPreviewDecor->ImageID());
178
179 fPreviewDecor = NULL;
180 }
181 }
182
183
184 status_t
PreviewDecorator(BString path,Window * window)185 DecorManager::PreviewDecorator(BString path, Window* window)
186 {
187 if (fPreviewWindow != NULL && fPreviewWindow != window){
188 // Reset other window to current decorator - only one can preview
189 Window* oldPreviewWindow = fPreviewWindow;
190 fPreviewWindow = NULL;
191 oldPreviewWindow->ReloadDecor();
192 }
193
194 if (window == NULL)
195 return B_BAD_VALUE;
196
197 // We have to jump some hoops because the window must be able to
198 // delete its decorator before we unload the add-on
199 status_t error = B_OK;
200 DecorAddOn* decorPtr = _LoadDecor(path, error);
201 if (decorPtr == NULL)
202 return error == B_OK ? B_ERROR : error;
203
204 BRegion border;
205 window->GetBorderRegion(&border);
206
207 DecorAddOn* oldDecor = fPreviewDecor;
208 fPreviewDecor = decorPtr;
209 fPreviewWindow = window;
210 // After this call, the window has deleted its decorator.
211 fPreviewWindow->ReloadDecor();
212
213 BRegion newBorder;
214 window->GetBorderRegion(&newBorder);
215
216 border.Include(&newBorder);
217 window->Desktop()->RebuildAndRedrawAfterWindowChange(window, border);
218
219 if (oldDecor != NULL)
220 unload_add_on(oldDecor->ImageID());
221
222 return B_OK;
223 }
224
225
226 const DesktopListenerList&
GetDesktopListeners()227 DecorManager::GetDesktopListeners()
228 {
229 return fCurrentDecor->GetDesktopListeners();
230 }
231
232
233 BString
GetCurrentDecorator() const234 DecorManager::GetCurrentDecorator() const
235 {
236 return fCurrentDecorPath.String();
237 }
238
239
240 status_t
SetDecorator(BString path,Desktop * desktop)241 DecorManager::SetDecorator(BString path, Desktop* desktop)
242 {
243 status_t error = B_OK;
244 DecorAddOn* newDecor = _LoadDecor(path, error);
245 if (newDecor == NULL)
246 return error == B_OK ? B_ERROR : error;
247
248 DecorAddOn* oldDecor = fCurrentDecor;
249
250 BString oldPath = fCurrentDecorPath;
251 image_id oldImage = fCurrentDecor->ImageID();
252
253 fCurrentDecor = newDecor;
254 fCurrentDecorPath = path.String();
255
256 if (desktop->ReloadDecor(oldDecor)) {
257 // now safe to unload all old decorator data
258 // saves us from deleting oldDecor...
259 unload_add_on(oldImage);
260 _SaveSettingsToDisk();
261 return B_OK;
262 }
263
264 // TODO: unloading the newDecor and its image
265 // problem is we don't know how many windows failed... or why they failed...
266 syslog(LOG_WARNING,
267 "app_server:DecorManager:SetDecorator:\"%s\" *partly* failed\n",
268 fCurrentDecorPath.String());
269
270 fCurrentDecor = oldDecor;
271 fCurrentDecorPath = oldPath;
272 return B_ERROR;
273 }
274
275
276 DecorAddOn*
_LoadDecor(BString _path,status_t & error)277 DecorManager::_LoadDecor(BString _path, status_t& error )
278 {
279 if (_path == "Default") {
280 error = B_OK;
281 return &fDefaultDecor;
282 }
283
284 BEntry entry(_path.String(), true);
285 if (!entry.Exists()) {
286 error = B_ENTRY_NOT_FOUND;
287 return NULL;
288 }
289
290 BPath path(&entry);
291 image_id image = load_add_on(path.Path());
292 if (image < 0) {
293 error = B_BAD_IMAGE_ID;
294 return NULL;
295 }
296
297 create_decor_addon* createFunc;
298 if (get_image_symbol(image, "instantiate_decor_addon", B_SYMBOL_TYPE_TEXT,
299 (void**)&createFunc) != B_OK) {
300 unload_add_on(image);
301 error = B_MISSING_SYMBOL;
302 return NULL;
303 }
304
305 char name[B_FILE_NAME_LENGTH];
306 entry.GetName(name);
307 DecorAddOn* newDecor = createFunc(image, name);
308 if (newDecor == NULL || newDecor->InitCheck() != B_OK) {
309 unload_add_on(image);
310 error = B_ERROR;
311 return NULL;
312 }
313
314 return newDecor;
315 }
316
317
318 static const char* kSettingsDir = "system/app_server";
319 static const char* kSettingsFile = "decorator_settings";
320
321
322 bool
_LoadSettingsFromDisk()323 DecorManager::_LoadSettingsFromDisk()
324 {
325 // get the user settings directory
326 BPath path;
327 status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
328 if (error != B_OK)
329 return false;
330
331 path.Append(kSettingsDir);
332 path.Append(kSettingsFile);
333 BFile file(path.Path(), B_READ_ONLY);
334 if (file.InitCheck() != B_OK)
335 return false;
336
337 BMessage settings;
338 if (settings.Unflatten(&file) == B_OK) {
339 BString itemPath;
340 if (settings.FindString("decorator", &itemPath) == B_OK) {
341 status_t error = B_OK;
342 DecorAddOn* decor = _LoadDecor(itemPath, error);
343 if (decor != NULL) {
344 fCurrentDecor = decor;
345 fCurrentDecorPath = itemPath;
346 return true;
347 } else {
348 //TODO: do something with the reported error
349 }
350 }
351 }
352
353 return false;
354 }
355
356
357 bool
_SaveSettingsToDisk()358 DecorManager::_SaveSettingsToDisk()
359 {
360 // get the user settings directory
361 BPath path;
362 status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
363 if (error != B_OK)
364 return false;
365
366 path.Append(kSettingsDir);
367 if (create_directory(path.Path(), 777) != B_OK)
368 return false;
369
370 path.Append(kSettingsFile);
371 BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
372 if (file.InitCheck() != B_OK)
373 return false;
374
375 BMessage settings;
376 if (settings.AddString("decorator", fCurrentDecorPath.String()) != B_OK)
377 return false;
378 if (settings.Flatten(&file) != B_OK)
379 return false;
380
381 return true;
382 }
383
384