1 /* 2 * Copyright 2001-2005, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Adrian Oanca <adioanca@cotty.iren.ro> 7 * Stephan Aßmus <superstippi@gmx.de> 8 * Axel Dörfler, axeld@pinc-software.de 9 */ 10 11 /** Class used to encapsulate desktop management */ 12 13 14 #include <stdio.h> 15 16 #include <Message.h> 17 #include <Region.h> 18 19 #include <WindowInfo.h> 20 #include <ServerProtocol.h> 21 22 #include "AppServer.h" 23 #include "DisplayDriverPainter.h" 24 #include "Globals.h" 25 #include "Layer.h" 26 #include "RootLayer.h" 27 #include "ServerConfig.h" 28 #include "ServerScreen.h" 29 #include "ServerApp.h" 30 #include "ServerWindow.h" 31 #include "WinBorder.h" 32 #include "Workspace.h" 33 #include "DesktopSettingsPrivate.h" 34 35 36 #ifdef __HAIKU__ 37 # define USE_ACCELERANT 1 38 #else 39 # define USE_ACCELERANT 0 40 #endif 41 42 #if USE_ACCELERANT 43 # include "AccelerantHWInterface.h" 44 #else 45 # include "ViewHWInterface.h" 46 #endif 47 48 #include "Desktop.h" 49 50 //#define DEBUG_DESKTOP 51 52 #ifdef DEBUG_DESKTOP 53 # define STRACE(a) printf(a) 54 #else 55 # define STRACE(a) /* nothing */ 56 #endif 57 58 59 Desktop::Desktop() 60 : MessageLooper("desktop"), 61 fSettings(new DesktopSettings::Private()), 62 fAppListLock("application list"), 63 fShutdownSemaphore(-1), 64 fWinBorderList(64), 65 fActiveScreen(NULL), 66 fCursorManager() 67 { 68 // TODO: use user name 69 char name[B_OS_NAME_LENGTH]; 70 snprintf(name, sizeof(name), "d:%s", "baron"); 71 72 fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name); 73 if (fMessagePort < B_OK) 74 return; 75 76 fLink.SetReceiverPort(fMessagePort); 77 } 78 79 80 Desktop::~Desktop() 81 { 82 for (int32 i = 0; WinBorder *border = (WinBorder *)fWinBorderList.ItemAt(i); i++) 83 delete border; 84 85 delete fRootLayer; 86 delete fSettings; 87 88 delete_port(fMessagePort); 89 } 90 91 92 void 93 Desktop::Init() 94 { 95 fVirtualScreen.RestoreConfiguration(*this, fSettings->WorkspacesMessage(0)); 96 97 // TODO: temporary workaround, fActiveScreen will be removed 98 fActiveScreen = fVirtualScreen.ScreenAt(0); 99 100 // TODO: add user identity to the name 101 char name[32]; 102 sprintf(name, "RootLayer %d", 1); 103 104 fRootLayer = new RootLayer(name, 4, this, GetDisplayDriver()); 105 fRootLayer->RunThread(); 106 107 // take care of setting the default cursor 108 ServerCursor *cursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT); 109 if (cursor) 110 fVirtualScreen.HWInterface()->SetCursor(cursor); 111 } 112 113 114 void 115 Desktop::_GetLooperName(char* name, size_t length) 116 { 117 snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron"); 118 } 119 120 121 void 122 Desktop::_PrepareQuit() 123 { 124 // let's kill all remaining applications 125 126 fAppListLock.Lock(); 127 128 int32 count = fAppList.CountItems(); 129 for (int32 i = 0; i < count; i++) { 130 ServerApp *app = (ServerApp*)fAppList.ItemAt(i); 131 team_id clientTeam = app->ClientTeam(); 132 133 app->Quit(); 134 kill_team(clientTeam); 135 } 136 137 // wait for the last app to die 138 if (count > 0) 139 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 250000); 140 141 fAppListLock.Unlock(); 142 } 143 144 145 void 146 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) 147 { 148 switch (code) { 149 case AS_CREATE_APP: 150 { 151 // Create the ServerApp to node monitor a new BApplication 152 153 // Attached data: 154 // 1) port_id - receiver port of a regular app 155 // 2) port_id - client looper port - for sending messages to the client 156 // 2) team_id - app's team ID 157 // 3) int32 - handler token of the regular app 158 // 4) char * - signature of the regular app 159 160 // Find the necessary data 161 team_id clientTeamID = -1; 162 port_id clientLooperPort = -1; 163 port_id clientReplyPort = -1; 164 int32 htoken = B_NULL_TOKEN; 165 char *appSignature = NULL; 166 167 link.Read<port_id>(&clientReplyPort); 168 link.Read<port_id>(&clientLooperPort); 169 link.Read<team_id>(&clientTeamID); 170 link.Read<int32>(&htoken); 171 if (link.ReadString(&appSignature) != B_OK) 172 break; 173 174 ServerApp *app = new ServerApp(this, clientReplyPort, 175 clientLooperPort, clientTeamID, htoken, appSignature); 176 if (app->InitCheck() == B_OK 177 && app->Run()) { 178 // add the new ServerApp to the known list of ServerApps 179 fAppListLock.Lock(); 180 fAppList.AddItem(app); 181 fAppListLock.Unlock(); 182 } else { 183 delete app; 184 185 // if everything went well, ServerApp::Run() will notify 186 // the client - but since it didn't, we do it here 187 BPrivate::LinkSender reply(clientReplyPort); 188 reply.StartMessage(SERVER_FALSE); 189 reply.Flush(); 190 } 191 192 // This is necessary because BPortLink::ReadString allocates memory 193 free(appSignature); 194 break; 195 } 196 197 case AS_DELETE_APP: 198 { 199 // Delete a ServerApp. Received only from the respective ServerApp when a 200 // BApplication asks it to quit. 201 202 // Attached Data: 203 // 1) thread_id - thread ID of the ServerApp to be deleted 204 205 thread_id thread = -1; 206 if (link.Read<thread_id>(&thread) < B_OK) 207 break; 208 209 fAppListLock.Lock(); 210 211 // Run through the list of apps and nuke the proper one 212 213 int32 count = fAppList.CountItems(); 214 ServerApp *removeApp = NULL; 215 216 for (int32 i = 0; i < count; i++) { 217 ServerApp *app = (ServerApp *)fAppList.ItemAt(i); 218 219 if (app != NULL && app->Thread() == thread) { 220 fAppList.RemoveItem(i); 221 removeApp = app; 222 break; 223 } 224 } 225 226 fAppListLock.Unlock(); 227 228 if (removeApp != NULL) 229 removeApp->Quit(fShutdownSemaphore); 230 231 if (fQuitting && count <= 1) { 232 // wait for the last app to die 233 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000); 234 PostMessage(kMsgQuitLooper); 235 } 236 break; 237 } 238 239 case B_QUIT_REQUESTED: 240 // We've been asked to quit, so (for now) broadcast to all 241 // test apps to quit. This situation will occur only when the server 242 // is compiled as a regular Be application. 243 244 fAppListLock.Lock(); 245 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 246 fShutdownCount = fAppList.CountItems(); 247 fAppListLock.Unlock(); 248 249 fQuitting = true; 250 BroadcastToAllApps(AS_QUIT_APP); 251 252 // We now need to process the remaining AS_DELETE_APP messages and 253 // wait for the kMsgShutdownServer message. 254 // If an application does not quit as asked, the picasso thread 255 // will send us this message in 2-3 seconds. 256 257 // if there are no apps to quit, shutdown directly 258 if (fShutdownCount == 0) 259 PostMessage(kMsgQuitLooper); 260 break; 261 262 default: 263 printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code); 264 265 if (link.NeedsReply()) { 266 // the client is now blocking and waiting for a reply! 267 fLink.StartMessage(B_ERROR); 268 fLink.Flush(); 269 } 270 break; 271 } 272 } 273 274 275 /*! 276 \brief Send a quick (no attachments) message to all applications 277 278 Quite useful for notification for things like server shutdown, system 279 color changes, etc. 280 */ 281 void 282 Desktop::BroadcastToAllApps(int32 code) 283 { 284 BAutolock locker(fAppListLock); 285 286 for (int32 i = 0; i < fAppList.CountItems(); i++) { 287 ServerApp *app = (ServerApp *)fAppList.ItemAt(i); 288 289 if (!app) { 290 printf("PANIC in Broadcast()\n"); 291 continue; 292 } 293 app->PostMessage(code); 294 } 295 } 296 297 298 //--------------------------------------------------------------------------- 299 // Methods for layer(WinBorder) manipulation. 300 //--------------------------------------------------------------------------- 301 302 303 void 304 Desktop::AddWinBorder(WinBorder *winBorder) 305 { 306 if (!winBorder) 307 return; 308 309 // R2: how to determine the RootLayer to which this window should be added??? 310 // for now, use ActiveRootLayer() because we only have one instance. 311 312 int32 feel = winBorder->Feel(); 313 314 // we are ServerApp thread, we need to lock RootLayer here. 315 ActiveRootLayer()->Lock(); 316 317 // we're playing with window list. lock first. 318 Lock(); 319 320 if (fWinBorderList.HasItem(winBorder)) { 321 Unlock(); 322 ActiveRootLayer()->Unlock(); 323 debugger("AddWinBorder: WinBorder already in Desktop list\n"); 324 return; 325 } 326 327 // we have a new window. store a record of it. 328 fWinBorderList.AddItem(winBorder); 329 330 // add FLOATING_APP windows to the local list of all normal windows. 331 // This is to keep the order all floating windows (app or subset) when we go from 332 // one normal window to another. 333 if (feel == B_FLOATING_APP_WINDOW_FEEL || feel == B_NORMAL_WINDOW_FEEL) { 334 WinBorder *wb = NULL; 335 int32 count = fWinBorderList.CountItems(); 336 int32 feelToLookFor = (feel == B_NORMAL_WINDOW_FEEL ? 337 B_FLOATING_APP_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL); 338 339 for (int32 i = 0; i < count; i++) { 340 wb = (WinBorder *)fWinBorderList.ItemAt(i); 341 342 if (wb->App()->ClientTeam() == winBorder->App()->ClientTeam() 343 && wb->Feel() == feelToLookFor) { 344 // R2: RootLayer comparison is needed. 345 feel == B_NORMAL_WINDOW_FEEL ? 346 winBorder->fSubWindowList.AddWinBorder(wb) : 347 wb->fSubWindowList.AddWinBorder(winBorder); 348 } 349 } 350 } 351 352 // add application's list of modal windows. 353 if (feel == B_MODAL_APP_WINDOW_FEEL) { 354 winBorder->App()->fAppSubWindowList.AddWinBorder(winBorder); 355 } 356 357 // send WinBorder to be added to workspaces 358 ActiveRootLayer()->AddWinBorder(winBorder); 359 360 // hey, unlock! 361 Unlock(); 362 363 ActiveRootLayer()->Unlock(); 364 } 365 366 367 void 368 Desktop::RemoveWinBorder(WinBorder *winBorder) 369 { 370 if (!winBorder) 371 return; 372 373 // we are ServerApp thread, we need to lock RootLayer here. 374 ActiveRootLayer()->Lock(); 375 376 // we're playing with window list. lock first. 377 Lock(); 378 379 // remove from main WinBorder list. 380 if (fWinBorderList.RemoveItem(winBorder)) { 381 int32 feel = winBorder->Feel(); 382 383 // floating app/subset and modal_subset windows require special atention because 384 // they are/may_be added to the list of a lot normal windows. 385 if (feel == B_FLOATING_SUBSET_WINDOW_FEEL 386 || feel == B_MODAL_SUBSET_WINDOW_FEEL 387 || feel == B_FLOATING_APP_WINDOW_FEEL) 388 { 389 WinBorder *wb = NULL; 390 int32 count = fWinBorderList.CountItems(); 391 392 for (int32 i = 0; i < count; i++) { 393 wb = (WinBorder*)fWinBorderList.ItemAt(i); 394 395 if (wb->Feel() == B_NORMAL_WINDOW_FEEL 396 && wb->App()->ClientTeam() == winBorder->App()->ClientTeam()) { 397 // R2: RootLayer comparison is needed. We'll see. 398 wb->fSubWindowList.RemoveItem(winBorder); 399 } 400 } 401 } 402 403 // remove from application's list 404 if (feel == B_MODAL_APP_WINDOW_FEEL) { 405 winBorder->App()->fAppSubWindowList.RemoveItem(winBorder); 406 } 407 } else { 408 Unlock(); 409 ActiveRootLayer()->Unlock(); 410 debugger("RemoveWinBorder: WinBorder not found in Desktop list\n"); 411 return; 412 } 413 414 // Tell to winBorder's RootLayer about this. 415 ActiveRootLayer()->RemoveWinBorder(winBorder); 416 417 Unlock(); 418 ActiveRootLayer()->Unlock(); 419 } 420 421 422 void 423 Desktop::AddWinBorderToSubset(WinBorder *winBorder, WinBorder *toWinBorder) 424 { 425 // NOTE: we can safely lock the entire method body, because this method is called from 426 // RootLayer's thread only. 427 428 // we're playing with window list. lock first. 429 Lock(); 430 431 if (!winBorder || !toWinBorder 432 || !fWinBorderList.HasItem(winBorder) 433 || !fWinBorderList.HasItem(toWinBorder)) { 434 Unlock(); 435 debugger("AddWinBorderToSubset: NULL WinBorder or not found in Desktop list\n"); 436 return; 437 } 438 439 if ((winBorder->Feel() == B_FLOATING_SUBSET_WINDOW_FEEL 440 || winBorder->Feel() == B_MODAL_SUBSET_WINDOW_FEEL) 441 && toWinBorder->Feel() == B_NORMAL_WINDOW_FEEL 442 && toWinBorder->App()->ClientTeam() == winBorder->App()->ClientTeam() 443 && !toWinBorder->fSubWindowList.HasItem(winBorder)) { 444 // add to normal_window's list 445 toWinBorder->fSubWindowList.AddWinBorder(winBorder); 446 } else { 447 Unlock(); 448 debugger("AddWinBorderToSubset: you must add a subset_window to a normal_window's subset with the same team_id\n"); 449 return; 450 } 451 452 // send WinBorder to be added to workspaces, if not already in there. 453 ActiveRootLayer()->AddSubsetWinBorder(winBorder, toWinBorder); 454 455 Unlock(); 456 } 457 458 459 void 460 Desktop::RemoveWinBorderFromSubset(WinBorder *winBorder, WinBorder *fromWinBorder) 461 { 462 // NOTE: we can safely lock the entire method body, because this method is called from 463 // RootLayer's thread only. 464 465 // we're playing with window list. lock first. 466 Lock(); 467 468 if (!winBorder || !fromWinBorder 469 || !fWinBorderList.HasItem(winBorder) 470 || !fWinBorderList.HasItem(fromWinBorder)) { 471 Unlock(); 472 debugger("RemoveWinBorderFromSubset: NULL WinBorder or not found in Desktop list\n"); 473 return; 474 } 475 476 // remove WinBorder from workspace, if needed - some other windows may still have it in their subset 477 ActiveRootLayer()->RemoveSubsetWinBorder(winBorder, fromWinBorder); 478 479 if (fromWinBorder->Feel() == B_NORMAL_WINDOW_FEEL) { 480 //remove from this normal_window's subset. 481 fromWinBorder->fSubWindowList.RemoveItem(winBorder); 482 } else { 483 Unlock(); 484 debugger("RemoveWinBorderFromSubset: you must remove a subset_window from a normal_window's subset\n"); 485 return; 486 } 487 488 Unlock(); 489 } 490 491 492 void 493 Desktop::SetWinBorderFeel(WinBorder *winBorder, uint32 feel) 494 { 495 // NOTE: this method is called from RootLayer thread only 496 497 // we're playing with window list. lock first. 498 Lock(); 499 500 RemoveWinBorder(winBorder); 501 winBorder->QuietlySetFeel(feel); 502 AddWinBorder(winBorder); 503 504 Unlock(); 505 } 506 507 508 WinBorder * 509 Desktop::FindWinBorderByClientToken(int32 token, team_id teamID) 510 { 511 BAutolock locker(this); 512 513 WinBorder *wb; 514 for (int32 i = 0; (wb = (WinBorder *)fWinBorderList.ItemAt(i)); i++) { 515 if (wb->Window()->ClientToken() == token 516 && wb->Window()->ClientTeam() == teamID) 517 return wb; 518 } 519 520 return NULL; 521 } 522 523 524 void 525 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 526 { 527 BAutolock locker(this); 528 529 // compute the number of windows 530 531 int32 count = 0; 532 if (team >= B_OK) { 533 for (int32 i = 0; i < fWinBorderList.CountItems(); i++) { 534 WinBorder* border = (WinBorder*)fWinBorderList.ItemAt(i); 535 536 if (border->Window()->ClientTeam() == team) 537 count++; 538 } 539 } else 540 count = fWinBorderList.CountItems(); 541 542 // write list 543 544 sender.StartMessage(SERVER_TRUE); 545 sender.Attach<int32>(count); 546 547 for (int32 i = 0; i < fWinBorderList.CountItems(); i++) { 548 WinBorder* border = (WinBorder*)fWinBorderList.ItemAt(i); 549 550 if (team >= B_OK && border->Window()->ClientTeam() != team) 551 continue; 552 553 sender.Attach<int32>(border->Window()->ServerToken()); 554 } 555 556 sender.Flush(); 557 } 558 559 560 void 561 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 562 { 563 BAutolock locker(this); 564 BAutolock tokenLocker(BPrivate::gDefaultTokens); 565 566 ServerWindow* window; 567 if (BPrivate::gDefaultTokens.GetToken(serverToken, 568 B_SERVER_TOKEN, (void**)&window) != B_OK) { 569 sender.StartMessage(B_ENTRY_NOT_FOUND); 570 sender.Flush(); 571 return; 572 } 573 574 window_info info; 575 window->GetInfo(info); 576 577 int32 length = window->Title() ? strlen(window->Title()) : 0; 578 579 sender.StartMessage(B_OK); 580 sender.Attach<int32>(sizeof(window_info) + length + 1); 581 sender.Attach(&info, sizeof(window_info)); 582 if (length > 0) 583 sender.Attach(window->Title(), length + 1); 584 else 585 sender.Attach<char>('\0'); 586 sender.Flush(); 587 } 588 589