master chld/haikuprg / decor / MacDecorator / DecorManager.cpp
  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