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 "DrawingEngine.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(uid_t userID) 60 : MessageLooper("desktop"), 61 fUserID(userID), 62 fSettings(new DesktopSettings::Private()), 63 fAppListLock("application list"), 64 fShutdownSemaphore(-1), 65 fWinBorderList(64), 66 fActiveScreen(NULL), 67 fCursorManager() 68 { 69 // TODO: use user name 70 char name[B_OS_NAME_LENGTH]; 71 snprintf(name, sizeof(name), "d:%s", "baron"); 72 73 fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name); 74 if (fMessagePort < B_OK) 75 return; 76 77 fLink.SetReceiverPort(fMessagePort); 78 gFontManager->AttachUser(fUserID); 79 } 80 81 82 Desktop::~Desktop() 83 { 84 for (int32 i = 0; WinBorder *border = (WinBorder *)fWinBorderList.ItemAt(i); i++) 85 delete border; 86 87 delete fRootLayer; 88 delete fSettings; 89 90 delete_port(fMessagePort); 91 gFontManager->DetachUser(fUserID); 92 } 93 94 95 void 96 Desktop::Init() 97 { 98 fVirtualScreen.RestoreConfiguration(*this, fSettings->WorkspacesMessage(0)); 99 100 // TODO: temporary workaround, fActiveScreen will be removed 101 fActiveScreen = fVirtualScreen.ScreenAt(0); 102 103 // TODO: add user identity to the name 104 char name[32]; 105 sprintf(name, "RootLayer %d", 1); 106 107 fRootLayer = new RootLayer(name, 4, this, GetDrawingEngine()); 108 fRootLayer->RunThread(); 109 110 // take care of setting the default cursor 111 ServerCursor *cursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT); 112 if (cursor) 113 fVirtualScreen.HWInterface()->SetCursor(cursor); 114 } 115 116 117 void 118 Desktop::_GetLooperName(char* name, size_t length) 119 { 120 snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron"); 121 } 122 123 124 void 125 Desktop::_PrepareQuit() 126 { 127 // let's kill all remaining applications 128 129 fAppListLock.Lock(); 130 131 int32 count = fAppList.CountItems(); 132 for (int32 i = 0; i < count; i++) { 133 ServerApp *app = (ServerApp*)fAppList.ItemAt(i); 134 team_id clientTeam = app->ClientTeam(); 135 136 app->Quit(); 137 kill_team(clientTeam); 138 } 139 140 // wait for the last app to die 141 if (count > 0) 142 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 250000); 143 144 fAppListLock.Unlock(); 145 } 146 147 148 void 149 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) 150 { 151 switch (code) { 152 case AS_CREATE_APP: 153 { 154 // Create the ServerApp to node monitor a new BApplication 155 156 // Attached data: 157 // 1) port_id - receiver port of a regular app 158 // 2) port_id - client looper port - for sending messages to the client 159 // 2) team_id - app's team ID 160 // 3) int32 - handler token of the regular app 161 // 4) char * - signature of the regular app 162 163 // Find the necessary data 164 team_id clientTeamID = -1; 165 port_id clientLooperPort = -1; 166 port_id clientReplyPort = -1; 167 int32 htoken = B_NULL_TOKEN; 168 char *appSignature = NULL; 169 170 link.Read<port_id>(&clientReplyPort); 171 link.Read<port_id>(&clientLooperPort); 172 link.Read<team_id>(&clientTeamID); 173 link.Read<int32>(&htoken); 174 if (link.ReadString(&appSignature) != B_OK) 175 break; 176 177 ServerApp *app = new ServerApp(this, clientReplyPort, 178 clientLooperPort, clientTeamID, htoken, appSignature); 179 if (app->InitCheck() == B_OK 180 && app->Run()) { 181 // add the new ServerApp to the known list of ServerApps 182 fAppListLock.Lock(); 183 fAppList.AddItem(app); 184 fAppListLock.Unlock(); 185 } else { 186 delete app; 187 188 // if everything went well, ServerApp::Run() will notify 189 // the client - but since it didn't, we do it here 190 BPrivate::LinkSender reply(clientReplyPort); 191 reply.StartMessage(SERVER_FALSE); 192 reply.Flush(); 193 } 194 195 // This is necessary because BPortLink::ReadString allocates memory 196 free(appSignature); 197 break; 198 } 199 200 case AS_DELETE_APP: 201 { 202 // Delete a ServerApp. Received only from the respective ServerApp when a 203 // BApplication asks it to quit. 204 205 // Attached Data: 206 // 1) thread_id - thread ID of the ServerApp to be deleted 207 208 thread_id thread = -1; 209 if (link.Read<thread_id>(&thread) < B_OK) 210 break; 211 212 fAppListLock.Lock(); 213 214 // Run through the list of apps and nuke the proper one 215 216 int32 count = fAppList.CountItems(); 217 ServerApp *removeApp = NULL; 218 219 for (int32 i = 0; i < count; i++) { 220 ServerApp *app = (ServerApp *)fAppList.ItemAt(i); 221 222 if (app != NULL && app->Thread() == thread) { 223 fAppList.RemoveItem(i); 224 removeApp = app; 225 break; 226 } 227 } 228 229 fAppListLock.Unlock(); 230 231 if (removeApp != NULL) 232 removeApp->Quit(fShutdownSemaphore); 233 234 if (fQuitting && count <= 1) { 235 // wait for the last app to die 236 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000); 237 PostMessage(kMsgQuitLooper); 238 } 239 break; 240 } 241 242 case AS_ACTIVATE_APP: 243 { 244 // Someone is requesting to activation of a certain app. 245 246 // Attached data: 247 // 1) port_id reply port 248 // 2) team_id team 249 250 status_t status; 251 252 // get the parameters 253 port_id replyPort; 254 team_id team; 255 if (link.Read(&replyPort) == B_OK 256 && link.Read(&team) == B_OK) 257 status = _ActivateApp(team); 258 else 259 status = B_ERROR; 260 261 // send the reply 262 BPrivate::PortLink replyLink(replyPort); 263 replyLink.StartMessage(status); 264 replyLink.Flush(); 265 break; 266 } 267 268 case AS_SET_SYSCURSOR_DEFAULTS: 269 { 270 GetCursorManager().SetDefaults(); 271 break; 272 } 273 274 case B_QUIT_REQUESTED: 275 // We've been asked to quit, so (for now) broadcast to all 276 // test apps to quit. This situation will occur only when the server 277 // is compiled as a regular Be application. 278 279 fAppListLock.Lock(); 280 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 281 fShutdownCount = fAppList.CountItems(); 282 fAppListLock.Unlock(); 283 284 fQuitting = true; 285 BroadcastToAllApps(AS_QUIT_APP); 286 287 // We now need to process the remaining AS_DELETE_APP messages and 288 // wait for the kMsgShutdownServer message. 289 // If an application does not quit as asked, the picasso thread 290 // will send us this message in 2-3 seconds. 291 292 // if there are no apps to quit, shutdown directly 293 if (fShutdownCount == 0) 294 PostMessage(kMsgQuitLooper); 295 break; 296 297 default: 298 printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code); 299 300 if (link.NeedsReply()) { 301 // the client is now blocking and waiting for a reply! 302 fLink.StartMessage(B_ERROR); 303 fLink.Flush(); 304 } 305 break; 306 } 307 } 308 309 310 /*! 311 \brief activate one of the app's windows. 312 */ 313 status_t 314 Desktop::_ActivateApp(team_id team) 315 { 316 status_t status = B_BAD_TEAM_ID; 317 318 // search for an unhidden window to give focus to 319 int32 windowCount = WindowList().CountItems(); 320 for (int32 i = 0; i < windowCount; ++i) { 321 // is this layer in fact a WinBorder? 322 WinBorder *winBorder = WindowList().ItemAt(i); 323 324 // if winBorder is valid and not hidden, then we've found our target 325 if (winBorder != NULL && !winBorder->IsHidden() 326 && winBorder->App()->ClientTeam() == team) { 327 if (fRootLayer->Lock()) { 328 fRootLayer->SetActive(winBorder); 329 fRootLayer->Unlock(); 330 331 if (fRootLayer->Active() == winBorder) 332 return B_OK; 333 334 status = B_ERROR; 335 } 336 } 337 } 338 339 return status; 340 } 341 342 343 /*! 344 \brief Send a quick (no attachments) message to all applications 345 346 Quite useful for notification for things like server shutdown, system 347 color changes, etc. 348 */ 349 void 350 Desktop::BroadcastToAllApps(int32 code) 351 { 352 BAutolock locker(fAppListLock); 353 354 for (int32 i = 0; i < fAppList.CountItems(); i++) { 355 ServerApp *app = (ServerApp *)fAppList.ItemAt(i); 356 357 if (!app) { 358 printf("PANIC in Broadcast()\n"); 359 continue; 360 } 361 app->PostMessage(code); 362 } 363 } 364 365 366 // #pragma mark - Methods for WinBorder manipulation 367 368 369 void 370 Desktop::AddWinBorder(WinBorder *winBorder) 371 { 372 if (!winBorder) 373 return; 374 375 // R2: how to determine the RootLayer to which this window should be added??? 376 // for now, use ActiveRootLayer() because we only have one instance. 377 378 int32 feel = winBorder->Feel(); 379 380 // we are ServerApp thread, we need to lock RootLayer here. 381 ActiveRootLayer()->Lock(); 382 383 // we're playing with window list. lock first. 384 Lock(); 385 386 if (fWinBorderList.HasItem(winBorder)) { 387 Unlock(); 388 ActiveRootLayer()->Unlock(); 389 debugger("AddWinBorder: WinBorder already in Desktop list\n"); 390 return; 391 } 392 393 // we have a new window. store a record of it. 394 fWinBorderList.AddItem(winBorder); 395 396 // add FLOATING_APP windows to the local list of all normal windows. 397 // This is to keep the order all floating windows (app or subset) when we go from 398 // one normal window to another. 399 if (feel == B_FLOATING_APP_WINDOW_FEEL || feel == B_NORMAL_WINDOW_FEEL) { 400 WinBorder *wb = NULL; 401 int32 count = fWinBorderList.CountItems(); 402 int32 feelToLookFor = (feel == B_NORMAL_WINDOW_FEEL ? 403 B_FLOATING_APP_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL); 404 405 for (int32 i = 0; i < count; i++) { 406 wb = (WinBorder *)fWinBorderList.ItemAt(i); 407 408 if (wb->App()->ClientTeam() == winBorder->App()->ClientTeam() 409 && wb->Feel() == feelToLookFor) { 410 // R2: RootLayer comparison is needed. 411 feel == B_NORMAL_WINDOW_FEEL ? 412 winBorder->fSubWindowList.AddWinBorder(wb) : 413 wb->fSubWindowList.AddWinBorder(winBorder); 414 } 415 } 416 } 417 418 // add application's list of modal windows. 419 if (feel == B_MODAL_APP_WINDOW_FEEL) { 420 winBorder->App()->fAppSubWindowList.AddWinBorder(winBorder); 421 } 422 423 // send WinBorder to be added to workspaces 424 ActiveRootLayer()->AddWinBorder(winBorder); 425 426 // hey, unlock! 427 Unlock(); 428 429 ActiveRootLayer()->Unlock(); 430 } 431 432 433 void 434 Desktop::RemoveWinBorder(WinBorder *winBorder) 435 { 436 if (!winBorder) 437 return; 438 439 // we are ServerApp thread, we need to lock RootLayer here. 440 ActiveRootLayer()->Lock(); 441 442 // we're playing with window list. lock first. 443 Lock(); 444 445 // remove from main WinBorder list. 446 if (fWinBorderList.RemoveItem(winBorder)) { 447 int32 feel = winBorder->Feel(); 448 449 // floating app/subset and modal_subset windows require special atention because 450 // they are/may_be added to the list of a lot normal windows. 451 if (feel == B_FLOATING_SUBSET_WINDOW_FEEL 452 || feel == B_MODAL_SUBSET_WINDOW_FEEL 453 || feel == B_FLOATING_APP_WINDOW_FEEL) 454 { 455 WinBorder *wb = NULL; 456 int32 count = fWinBorderList.CountItems(); 457 458 for (int32 i = 0; i < count; i++) { 459 wb = (WinBorder*)fWinBorderList.ItemAt(i); 460 461 if (wb->Feel() == B_NORMAL_WINDOW_FEEL 462 && wb->App()->ClientTeam() == winBorder->App()->ClientTeam()) { 463 // R2: RootLayer comparison is needed. We'll see. 464 wb->fSubWindowList.RemoveItem(winBorder); 465 } 466 } 467 } 468 469 // remove from application's list 470 if (feel == B_MODAL_APP_WINDOW_FEEL) { 471 winBorder->App()->fAppSubWindowList.RemoveItem(winBorder); 472 } 473 } else { 474 Unlock(); 475 ActiveRootLayer()->Unlock(); 476 debugger("RemoveWinBorder: WinBorder not found in Desktop list\n"); 477 return; 478 } 479 480 // Tell to winBorder's RootLayer about this. 481 ActiveRootLayer()->RemoveWinBorder(winBorder); 482 483 Unlock(); 484 ActiveRootLayer()->Unlock(); 485 } 486 487 488 void 489 Desktop::AddWinBorderToSubset(WinBorder *winBorder, WinBorder *toWinBorder) 490 { 491 // NOTE: we can safely lock the entire method body, because this method is called from 492 // RootLayer's thread only. 493 494 // we're playing with window list. lock first. 495 Lock(); 496 497 if (!winBorder || !toWinBorder 498 || !fWinBorderList.HasItem(winBorder) 499 || !fWinBorderList.HasItem(toWinBorder)) { 500 Unlock(); 501 debugger("AddWinBorderToSubset: NULL WinBorder or not found in Desktop list\n"); 502 return; 503 } 504 505 if ((winBorder->Feel() == B_FLOATING_SUBSET_WINDOW_FEEL 506 || winBorder->Feel() == B_MODAL_SUBSET_WINDOW_FEEL) 507 && toWinBorder->Feel() == B_NORMAL_WINDOW_FEEL 508 && toWinBorder->App()->ClientTeam() == winBorder->App()->ClientTeam() 509 && !toWinBorder->fSubWindowList.HasItem(winBorder)) { 510 // add to normal_window's list 511 toWinBorder->fSubWindowList.AddWinBorder(winBorder); 512 } else { 513 Unlock(); 514 debugger("AddWinBorderToSubset: you must add a subset_window to a normal_window's subset with the same team_id\n"); 515 return; 516 } 517 518 // send WinBorder to be added to workspaces, if not already in there. 519 ActiveRootLayer()->AddSubsetWinBorder(winBorder, toWinBorder); 520 521 Unlock(); 522 } 523 524 525 void 526 Desktop::RemoveWinBorderFromSubset(WinBorder *winBorder, WinBorder *fromWinBorder) 527 { 528 // NOTE: we can safely lock the entire method body, because this method is called from 529 // RootLayer's thread only. 530 531 // we're playing with window list. lock first. 532 Lock(); 533 534 if (!winBorder || !fromWinBorder 535 || !fWinBorderList.HasItem(winBorder) 536 || !fWinBorderList.HasItem(fromWinBorder)) { 537 Unlock(); 538 debugger("RemoveWinBorderFromSubset: NULL WinBorder or not found in Desktop list\n"); 539 return; 540 } 541 542 // remove WinBorder from workspace, if needed - some other windows may still have it in their subset 543 ActiveRootLayer()->RemoveSubsetWinBorder(winBorder, fromWinBorder); 544 545 if (fromWinBorder->Feel() == B_NORMAL_WINDOW_FEEL) { 546 //remove from this normal_window's subset. 547 fromWinBorder->fSubWindowList.RemoveItem(winBorder); 548 } else { 549 Unlock(); 550 debugger("RemoveWinBorderFromSubset: you must remove a subset_window from a normal_window's subset\n"); 551 return; 552 } 553 554 Unlock(); 555 } 556 557 558 void 559 Desktop::SetWinBorderFeel(WinBorder *winBorder, uint32 feel) 560 { 561 // NOTE: this method is called from RootLayer thread only 562 563 // we're playing with window list. lock first. 564 Lock(); 565 566 RemoveWinBorder(winBorder); 567 winBorder->QuietlySetFeel(feel); 568 AddWinBorder(winBorder); 569 570 Unlock(); 571 } 572 573 574 WinBorder * 575 Desktop::FindWinBorderByClientToken(int32 token, team_id teamID) 576 { 577 BAutolock locker(this); 578 579 WinBorder *wb; 580 for (int32 i = 0; (wb = (WinBorder *)fWinBorderList.ItemAt(i)); i++) { 581 if (wb->Window()->ClientToken() == token 582 && wb->Window()->ClientTeam() == teamID) 583 return wb; 584 } 585 586 return NULL; 587 } 588 589 590 const BObjectList<WinBorder> & 591 Desktop::WindowList() const 592 { 593 if (!IsLocked()) 594 debugger("You must lock before getting registered windows list\n"); 595 596 return fWinBorderList; 597 } 598 599 600 void 601 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 602 { 603 BAutolock locker(this); 604 605 // compute the number of windows 606 607 int32 count = 0; 608 if (team >= B_OK) { 609 for (int32 i = 0; i < fWinBorderList.CountItems(); i++) { 610 WinBorder* border = fWinBorderList.ItemAt(i); 611 612 if (border->Window()->ClientTeam() == team) 613 count++; 614 } 615 } else 616 count = fWinBorderList.CountItems(); 617 618 // write list 619 620 sender.StartMessage(SERVER_TRUE); 621 sender.Attach<int32>(count); 622 623 for (int32 i = 0; i < fWinBorderList.CountItems(); i++) { 624 WinBorder* border = fWinBorderList.ItemAt(i); 625 626 if (team >= B_OK && border->Window()->ClientTeam() != team) 627 continue; 628 629 sender.Attach<int32>(border->Window()->ServerToken()); 630 } 631 632 sender.Flush(); 633 } 634 635 636 void 637 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 638 { 639 BAutolock locker(this); 640 BAutolock tokenLocker(BPrivate::gDefaultTokens); 641 642 ServerWindow* window; 643 if (BPrivate::gDefaultTokens.GetToken(serverToken, 644 B_SERVER_TOKEN, (void**)&window) != B_OK) { 645 sender.StartMessage(B_ENTRY_NOT_FOUND); 646 sender.Flush(); 647 return; 648 } 649 650 window_info info; 651 window->GetInfo(info); 652 653 int32 length = window->Title() ? strlen(window->Title()) : 0; 654 655 sender.StartMessage(B_OK); 656 sender.Attach<int32>(sizeof(window_info) + length + 1); 657 sender.Attach(&info, sizeof(window_info)); 658 if (length > 0) 659 sender.Attach(window->Title(), length + 1); 660 else 661 sender.Attach<char>('\0'); 662 sender.Flush(); 663 } 664 665