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
31typedef float get_version(void);
32typedef DecorAddOn* create_decor_addon(image_id id, const char* name);
33
34// Globals
35DecorManager gDecorManager;
36
37
38DecorAddOn::DecorAddOn(image_id id, const char* name)
39	:
40	fImageID(id),
41 	fName(name)
42{
43}
44
45
46DecorAddOn::~DecorAddOn()
47{
48}
49
50
51status_t
52DecorAddOn::InitCheck() const
53{
54	return B_OK;
55}
56
57
58Decorator*
59DecorAddOn::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
84WindowBehaviour*
85DecorAddOn::AllocateWindowBehaviour(Window* window)
86{
87	return new (std::nothrow)SATWindowBehaviour(window,
88		window->Desktop()->GetStackAndTile());
89}
90
91
92const DesktopListenerList&
93DecorAddOn::GetDesktopListeners()
94{
95	return fDesktopListeners;
96}
97
98
99Decorator*
100DecorAddOn::_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
110DecorManager::DecorManager()
111	:
112	fDefaultDecor(-1, "Default"),
113	fCurrentDecor(&fDefaultDecor),
114	fPreviewDecor(NULL),
115	fPreviewWindow(NULL),
116	fCurrentDecorPath("Default")
117{
118	_LoadSettingsFromDisk();
119}
120
121
122DecorManager::~DecorManager()
123{
124}
125
126
127Decorator*
128DecorManager::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
156WindowBehaviour*
157DecorManager::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
169void
170DecorManager::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
184status_t
185DecorManager::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
226const DesktopListenerList&
227DecorManager::GetDesktopListeners()
228{
229	return fCurrentDecor->GetDesktopListeners();
230}
231
232
233BString
234DecorManager::GetCurrentDecorator() const
235{
236	return fCurrentDecorPath.String();
237}
238
239
240status_t
241DecorManager::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
276DecorAddOn*
277DecorManager::_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
318static const char* kSettingsDir = "system/app_server";
319static const char* kSettingsFile = "decorator_settings";
320
321
322bool
323DecorManager::_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
357bool
358DecorManager::_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