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 38 DecorAddOn::DecorAddOn(image_id id, const char* name) 39 : 40 fImageID(id), 41 fName(name) 42 { 43 } 44 45 46 DecorAddOn::~DecorAddOn() 47 { 48 } 49 50 51 status_t 52 DecorAddOn::InitCheck() const 53 { 54 return B_OK; 55 } 56 57 58 Decorator* 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* 85 DecorAddOn::AllocateWindowBehaviour(Window* window) 86 { 87 return new (std::nothrow)SATWindowBehaviour(window, 88 window->Desktop()->GetStackAndTile()); 89 } 90 91 92 const DesktopListenerList& 93 DecorAddOn::GetDesktopListeners() 94 { 95 return fDesktopListeners; 96 } 97 98 99 Decorator* 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 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 122 DecorManager::~DecorManager() 123 { 124 } 125 126 127 Decorator* 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* 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 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 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& 227 DecorManager::GetDesktopListeners() 228 { 229 return fCurrentDecor->GetDesktopListeners(); 230 } 231 232 233 BString 234 DecorManager::GetCurrentDecorator() const 235 { 236 return fCurrentDecorPath.String(); 237 } 238 239 240 status_t 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* 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 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 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