xref: /haiku/src/servers/app/decorator/DecorManager.cpp (revision 90f9b5590f6ae5c7b51c2977a77fd001c7ecc0ba)
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