1 /* 2 * Copyright (c) 2001-2005, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 11 #include <Accelerant.h> 12 #include <AppDefs.h> 13 #include <Directory.h> 14 #include <Entry.h> 15 #include <File.h> 16 #include <Message.h> 17 #include <Path.h> 18 #include <PortLink.h> 19 #include <StopWatch.h> 20 #include <RosterPrivate.h> 21 22 #include "BitmapManager.h" 23 #include "ColorSet.h" 24 #include "CursorManager.h" 25 #include "DecorManager.h" 26 #include "DefaultDecorator.h" 27 #include "Desktop.h" 28 #include "FontServer.h" 29 #include "HWInterface.h" 30 #include "RegistrarDefs.h" 31 #include "RGBColor.h" 32 #include "RootLayer.h" 33 #include "ServerApp.h" 34 #include "ServerCursor.h" 35 #include "ServerProtocol.h" 36 #include "ServerWindow.h" 37 #include "SystemPalette.h" 38 #include "Utils.h" 39 40 #include "AppServer.h" 41 42 //#define DEBUG_KEYHANDLING 43 //#define DEBUG_SERVER 44 45 #ifdef DEBUG_KEYHANDLING 46 # include <stdio.h> 47 # define KBTRACE(x) printf x 48 #else 49 # define KBTRACE(x) ; 50 #endif 51 52 #ifdef DEBUG_SERVER 53 # include <stdio.h> 54 # define STRACE(x) printf x 55 #else 56 # define STRACE(x) ; 57 #endif 58 59 60 static const uint32 kMsgShutdownServer = 'shuT'; 61 62 63 // Globals 64 Desktop *gDesktop; 65 66 port_id gAppServerPort; 67 68 static AppServer *sAppServer; 69 70 //! Default background color for workspaces 71 //RGBColor workspace_default_color(51,102,160); 72 73 //! System-wide GUI color object 74 ColorSet gGUIColorSet; 75 76 /*! 77 \brief Constructor 78 79 This loads the default fonts, allocates all the major global variables, spawns the main housekeeping 80 threads, loads user preferences for the UI and decorator, and allocates various locks. 81 */ 82 #if TEST_MODE 83 AppServer::AppServer(void) : BApplication (SERVER_SIGNATURE), 84 #else 85 AppServer::AppServer(void) : 86 #endif 87 fCursorSem(-1), 88 fCursorArea(-1) 89 { 90 fMessagePort = create_port(200, SERVER_PORT_NAME); 91 if (fMessagePort == B_NO_MORE_PORTS) 92 debugger("app_server could not create message port"); 93 94 gAppServerPort = fMessagePort; 95 96 // Create the input port. The input_server will send it's messages here. 97 // TODO: If we want multiple user support we would need an individual 98 // port for each user and do the following for each RootLayer. 99 fServerInputPort = create_port(200, SERVER_INPUT_PORT); 100 if (fServerInputPort == B_NO_MORE_PORTS) 101 debugger("app_server could not create input port"); 102 103 fQuitting = false; 104 sAppServer = this; 105 106 // Create the font server and scan the proper directories. 107 gFontServer = new FontServer; 108 gFontServer->Lock(); 109 110 // Used for testing purposes 111 112 // TODO: Re-enable scanning of all font directories when server is actually put to use 113 gFontServer->ScanDirectory("/boot/beos/etc/fonts/ttfonts/"); 114 // gFontServer->ScanDirectory("/boot/beos/etc/fonts/PS-Type1/"); 115 // gFontServer->ScanDirectory("/boot/home/config/fonts/ttfonts/"); 116 // gFontServer->ScanDirectory("/boot/home/config/fonts/psfonts/"); 117 gFontServer->SaveList(); 118 119 if (!gFontServer->SetSystemPlain(DEFAULT_PLAIN_FONT_FAMILY, 120 DEFAULT_PLAIN_FONT_STYLE, 121 DEFAULT_PLAIN_FONT_SIZE) && 122 !gFontServer->SetSystemPlain(FALLBACK_PLAIN_FONT_FAMILY, 123 DEFAULT_PLAIN_FONT_STYLE, 124 DEFAULT_PLAIN_FONT_SIZE)) { 125 printf("Couldn't set plain to %s (fallback: %s), %s %d pt\n", 126 DEFAULT_PLAIN_FONT_FAMILY, 127 FALLBACK_PLAIN_FONT_FAMILY, 128 DEFAULT_PLAIN_FONT_STYLE, 129 DEFAULT_PLAIN_FONT_SIZE); 130 } 131 132 if (!gFontServer->SetSystemBold(DEFAULT_BOLD_FONT_FAMILY, 133 DEFAULT_BOLD_FONT_STYLE, 134 DEFAULT_BOLD_FONT_SIZE) && 135 !gFontServer->SetSystemBold(FALLBACK_BOLD_FONT_FAMILY, 136 DEFAULT_BOLD_FONT_STYLE, 137 DEFAULT_BOLD_FONT_SIZE)) { 138 printf("Couldn't set bold to %s (fallback: %s), %s %d pt\n", 139 DEFAULT_BOLD_FONT_FAMILY, 140 FALLBACK_BOLD_FONT_FAMILY, 141 DEFAULT_BOLD_FONT_STYLE, 142 DEFAULT_BOLD_FONT_SIZE); 143 } 144 145 if (!gFontServer->SetSystemFixed(DEFAULT_FIXED_FONT_FAMILY, 146 DEFAULT_FIXED_FONT_STYLE, 147 DEFAULT_FIXED_FONT_SIZE) && 148 !gFontServer->SetSystemFixed(FALLBACK_FIXED_FONT_FAMILY, 149 DEFAULT_FIXED_FONT_STYLE, 150 DEFAULT_FIXED_FONT_SIZE)) { 151 printf("Couldn't set fixed to %s (fallback: %s), %s %d pt\n", 152 DEFAULT_FIXED_FONT_FAMILY, 153 FALLBACK_FIXED_FONT_FAMILY, 154 DEFAULT_FIXED_FONT_STYLE, 155 DEFAULT_FIXED_FONT_SIZE); 156 } 157 158 gFontServer->Unlock(); 159 160 // Load the GUI colors here and set the global set to the values contained therein. If this 161 // is not possible, set colors to the defaults 162 if (LoadColorSet(SERVER_SETTINGS_DIR COLOR_SETTINGS_NAME, &gGUIColorSet) != B_OK) 163 gGUIColorSet.SetToDefaults(); 164 165 // Set up the Desktop 166 gDesktop = new Desktop(); 167 gDesktop->Init(); 168 169 // TODO: Maybe this is not the best place for this 170 InitializeColorMap(); 171 172 // Create the bitmap allocator. Object declared in BitmapManager.cpp 173 gBitmapManager = new BitmapManager(); 174 175 // This is necessary to mediate access between the Poller and app_server threads 176 fActiveAppLock = create_sem(1,"app_server_active_sem"); 177 178 // This locker is for app_server and Picasso to vy for control of the ServerApp list 179 fAppListLock = create_sem(1,"app_server_applist_sem"); 180 181 // Spawn our thread-monitoring thread 182 fPicassoThreadID = spawn_thread(PicassoThread, "picasso", B_NORMAL_PRIORITY, this); 183 if (fPicassoThreadID >= 0) 184 resume_thread(fPicassoThreadID); 185 186 #if 0 187 LaunchCursorThread(); 188 #endif 189 } 190 191 /*! 192 \brief Destructor 193 194 Reached only when the server is asked to shut down in Test mode. Kills all apps, shuts down the 195 desktop, kills the housekeeping threads, etc. 196 */ 197 AppServer::~AppServer() 198 { 199 debugger("We shouldn't be here! MainLoop()::B_QUIT_REQUESTED should see if we can exit the server.\n"); 200 /* 201 ServerApp *tempapp; 202 int32 i; 203 acquire_sem(fAppListLock); 204 for(i=0;i<fAppList->CountItems();i++) 205 { 206 tempapp=(ServerApp *)fAppList->ItemAt(i); 207 if(tempapp!=NULL) 208 delete tempapp; 209 } 210 delete fAppList; 211 release_sem(fAppListLock); 212 213 delete gBitmapManager; 214 215 delete gDesktop; 216 217 // If these threads are still running, kill them - after this, if exit_poller 218 // is deleted, who knows what will happen... These things will just return an 219 // error and fail if the threads have already exited. 220 kill_thread(fPicassoThreadID); 221 kill_thread(fCursorThreadID); 222 kill_thread(fISThreadID); 223 224 delete gFontServer; 225 226 make_decorator=NULL; 227 */ 228 } 229 230 /*! 231 \brief Thread function for watching for dead apps 232 \param data Pointer to the app_server to which the thread belongs 233 \return Throwaway value - always 0 234 */ 235 int32 236 AppServer::PicassoThread(void *data) 237 { 238 while (!sAppServer->fQuitting) { 239 acquire_sem(sAppServer->fAppListLock); 240 for (int32 i = 0;;) { 241 ServerApp *app = (ServerApp *)sAppServer->fAppList.ItemAt(i++); 242 if (!app) 243 break; 244 245 app->PingTarget(); 246 } 247 release_sem(sAppServer->fAppListLock); 248 // we do this every second so as not to suck *too* many CPU cycles 249 snooze(1000000); 250 } 251 252 // app_server is about to quit 253 // wait some more, and then make sure it'll shutdown everything 254 snooze(2000000); 255 sAppServer->PostMessage(kMsgShutdownServer); 256 257 return 0; 258 } 259 260 261 /*! 262 \brief Starts Input Server 263 */ 264 void 265 AppServer::LaunchInputServer() 266 { 267 // We are supposed to start the input_server, but it's a BApplication 268 // that depends on the registrar running, which is started after app_server 269 // so we wait on roster thread to launch the input server 270 271 fISThreadID = B_ERROR; 272 273 while (!BRoster::Private().IsMessengerValid(false) && !fQuitting) { 274 snooze(250000); 275 BRoster::Private::DeleteBeRoster(); 276 BRoster::Private::InitBeRoster(); 277 } 278 279 if (fQuitting) 280 return; 281 282 // we use an area for cursor data communication with input_server 283 // area id and sem id are sent to the input_server 284 285 if (fCursorArea < B_OK) 286 fCursorArea = create_area("isCursor", (void**) &fCursorAddr, B_ANY_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA); 287 if (fCursorSem < B_OK) 288 fCursorSem = create_sem(0, "isSem"); 289 290 int32 arg_c = 1; 291 char **arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1)); 292 #if TEST_MODE 293 arg_v[0] = strdup("/boot/home/svnhaiku/trunk/distro/x86.R1/beos/system/servers/input_server"); 294 #else 295 arg_v[0] = strdup("/system/servers/input_server"); 296 #endif 297 arg_v[1] = NULL; 298 fISThreadID = load_image(arg_c, (const char**)arg_v, (const char **)environ); 299 free(arg_v[0]); 300 301 int32 tmpbuf[2] = {fCursorSem, fCursorArea}; 302 int32 code = 0; 303 send_data(fISThreadID, code, (void *)tmpbuf, sizeof(tmpbuf)); 304 305 resume_thread(fISThreadID); 306 setpgid(fISThreadID, 0); 307 308 // we receive 309 310 thread_id sender; 311 code = receive_data(&sender, (void *)tmpbuf, sizeof(tmpbuf)); 312 fISASPort = tmpbuf[0]; 313 fISPort = tmpbuf[1]; 314 315 // if at any time, one of these ports is error prone, it might mean input_server is gone 316 // then relaunch input_server 317 } 318 319 320 /*! 321 \brief Starts the Cursor Thread 322 */ 323 void 324 AppServer::LaunchCursorThread() 325 { 326 // Spawn our cursor thread 327 fCursorThreadID = spawn_thread(CursorThread, "CursorThreadOfTheDeath", B_REAL_TIME_DISPLAY_PRIORITY - 1, this); 328 if (fCursorThreadID >= 0) 329 resume_thread(fCursorThreadID); 330 331 } 332 333 /*! 334 \brief The Cursor Thread task 335 */ 336 int32 337 AppServer::CursorThread(void* data) 338 { 339 AppServer *server = (AppServer *)data; 340 341 server->LaunchInputServer(); 342 343 do { 344 while (acquire_sem(server->fCursorSem) == B_OK) { 345 BPoint p; 346 p.y = *server->fCursorAddr & 0x7fff; 347 p.x = *server->fCursorAddr >> 15 & 0x7fff; 348 349 gDesktop->GetHWInterface()->MoveCursorTo(p.x, p.y); 350 STRACE(("CursorThread : %f, %f\n", p.x, p.y)); 351 } 352 353 snooze(100000); 354 355 } while (!server->fQuitting); 356 357 return B_OK; 358 } 359 360 361 /*! 362 \brief Send a message to the AppServer with no attachments 363 \param code ID code of the message to post 364 */ 365 void 366 AppServer::PostMessage(int32 code) 367 { 368 BPrivate::LinkSender link(fMessagePort); 369 link.StartMessage(code); 370 link.Flush(); 371 } 372 373 374 /*! 375 \brief The call that starts it all... 376 \return Always 0 377 */ 378 thread_id 379 AppServer::Run(void) 380 { 381 MainLoop(); 382 kill_thread(fPicassoThreadID); 383 return 0; 384 } 385 386 387 //! Main message-monitoring loop for the regular message port - no input messages! 388 void 389 AppServer::MainLoop(void) 390 { 391 BPrivate::PortLink link(-1, fMessagePort); 392 393 while (1) { 394 STRACE(("info: AppServer::MainLoop listening on port %ld.\n", fMessagePort)); 395 396 int32 code; 397 status_t err = link.GetNextMessage(code); 398 if (err < B_OK) { 399 STRACE(("MainLoop:link.GetNextMessage() failed\n")); 400 continue; 401 } 402 403 DispatchMessage(code, link); 404 } 405 } 406 407 /*! 408 \brief Message handling function for all messages sent to the app_server 409 \param code ID of the message sent 410 \param buffer Attachment buffer for the message. 411 412 */ 413 void 414 AppServer::DispatchMessage(int32 code, BPrivate::PortLink &msg) 415 { 416 switch (code) { 417 case AS_CREATE_APP: 418 { 419 // Create the ServerApp to node monitor a new BApplication 420 421 // Attached data: 422 // 1) port_id - receiver port of a regular app 423 // 2) port_id - client looper port - for send messages to the client 424 // 2) team_id - app's team ID 425 // 3) int32 - handler token of the regular app 426 // 4) char * - signature of the regular app 427 428 // Find the necessary data 429 team_id clientTeamID = -1; 430 port_id clientLooperPort = -1; 431 port_id clientReplyPort = -1; 432 int32 htoken = B_NULL_TOKEN; 433 char *appSignature = NULL; 434 435 msg.Read<port_id>(&clientReplyPort); 436 msg.Read<port_id>(&clientLooperPort); 437 msg.Read<team_id>(&clientTeamID); 438 msg.Read<int32>(&htoken); 439 if (msg.ReadString(&appSignature) != B_OK) 440 break; 441 442 ServerApp *app = new ServerApp(clientReplyPort, clientLooperPort, 443 clientTeamID, htoken, appSignature); 444 if (app->InitCheck() == B_OK 445 && app->Run()) { 446 // add the new ServerApp to the known list of ServerApps 447 acquire_sem(fAppListLock); 448 fAppList.AddItem(app); 449 release_sem(fAppListLock); 450 } else { 451 delete app; 452 453 // if everything went well, ServerApp::Run() will notify 454 // the client - but since it didn't, we do it here 455 BPrivate::LinkSender reply(clientReplyPort); 456 reply.StartMessage(SERVER_FALSE); 457 reply.Flush(); 458 } 459 460 // This is necessary because BPortLink::ReadString allocates memory 461 free(appSignature); 462 break; 463 } 464 465 case AS_DELETE_APP: 466 { 467 // Delete a ServerApp. Received only from the respective ServerApp when a 468 // BApplication asks it to quit. 469 470 // Attached Data: 471 // 1) thread_id - thread ID of the ServerApp to be deleted 472 473 thread_id thread = -1; 474 if (msg.Read<thread_id>(&thread) < B_OK) 475 break; 476 477 acquire_sem(fAppListLock); 478 479 // Run through the list of apps and nuke the proper one 480 481 int32 count = fAppList.CountItems(); 482 ServerApp *removeApp = NULL; 483 484 for (int32 i = 0; i < count; i++) { 485 ServerApp *app = (ServerApp *)fAppList.ItemAt(i); 486 487 if (app != NULL && app->Thread() == thread) { 488 fAppList.RemoveItem(i); 489 removeApp = app; 490 break; 491 } 492 } 493 494 release_sem(fAppListLock); 495 496 if (removeApp != NULL) 497 removeApp->Quit(); 498 499 if (fQuitting && count <= 1) 500 PostMessage(kMsgShutdownServer); 501 break; 502 } 503 504 case AS_UPDATED_CLIENT_FONTLIST: 505 // received when the client-side global font list has been 506 // refreshed 507 gFontServer->Lock(); 508 gFontServer->FontsUpdated(); 509 gFontServer->Unlock(); 510 break; 511 512 case AS_QUERY_FONTS_CHANGED: 513 { 514 // Client application is asking if the font list has changed since 515 // the last client-side refresh 516 517 // Attached data: 518 // 1) port_id reply port 519 520 gFontServer->Lock(); 521 bool needsUpdate = gFontServer->FontsNeedUpdated(); 522 gFontServer->Unlock(); 523 524 // Seeing how the client merely wants an answer, we'll skip the BPortLink 525 // and all its overhead and just write the code to port. 526 port_id replyport; 527 if (msg.Read<port_id>(&replyport) < B_OK) 528 break; 529 BPrivate::PortLink replylink(replyport); 530 replylink.StartMessage(needsUpdate ? SERVER_TRUE : SERVER_FALSE); 531 replylink.Flush(); 532 break; 533 } 534 535 #if TEST_MODE 536 case B_QUIT_REQUESTED: 537 // We've been asked to quit, so (for now) broadcast to all 538 // test apps to quit. This situation will occur only when the server 539 // is compiled as a regular Be application. 540 541 fQuitting = true; 542 BroadcastToAllApps(AS_QUIT_APP); 543 544 // We now need to process the remaining AS_DELETE_APP messages and 545 // wait for the kMsgShutdownServer message. 546 // If an application does not quit as asked, the picasso thread 547 // will send us this message in 2-3 seconds. 548 break; 549 550 case kMsgShutdownServer: 551 // let's kill all remaining applications 552 553 acquire_sem(fAppListLock); 554 555 for (int32 i = 0; i < fAppList.CountItems(); i++) { 556 ServerApp *app = (ServerApp*)fAppList.ItemAt(i); 557 558 kill_thread(app->Thread()); 559 kill_team(app->ClientTeam()); 560 } 561 562 kill_thread(fPicassoThreadID); 563 564 release_sem(fAppListLock); 565 566 delete gDesktop; 567 delete gBitmapManager; 568 delete gFontServer; 569 570 // we are now clear to exit 571 exit_thread(0); 572 break; 573 #endif 574 575 case AS_SET_SYSCURSOR_DEFAULTS: 576 { 577 // although this isn't pretty, ATM we only have RootLayer. 578 // this messages should be handled somewhere into a RootLayer 579 // specific area - this set is intended for a RootLayer. 580 gDesktop->ActiveRootLayer()->GetCursorManager().SetDefaults(); 581 break; 582 } 583 584 default: 585 STRACE(("Server::MainLoop received unexpected code %ld (offset %ld)\n", 586 code, code - SERVER_TRUE)); 587 break; 588 } 589 } 590 591 /*! 592 \brief Finds the application with the given signature 593 \param sig MIME signature of the application to find 594 \return the corresponding ServerApp or NULL if not found 595 596 This call should be made only when necessary because it locks the app list 597 while it does its searching. 598 */ 599 ServerApp * 600 AppServer::FindApp(const char *signature) 601 { 602 if (!signature) 603 return NULL; 604 605 acquire_sem(fAppListLock); 606 607 ServerApp *foundApp = NULL; 608 for (int32 i = 0; i < fAppList.CountItems(); i++) { 609 ServerApp *app = (ServerApp*)fAppList.ItemAt(i); 610 if (app && !strcasecmp(app->Signature(), signature)) { 611 foundApp = app; 612 break; 613 } 614 } 615 616 release_sem(fAppListLock); 617 618 return foundApp; 619 } 620 621 622 // #pragma mark - 623 624 625 /*! 626 \brief Send a quick (no attachments) message to all applications 627 628 Quite useful for notification for things like server shutdown, system 629 color changes, etc. 630 */ 631 void 632 BroadcastToAllApps(const int32 &code) 633 { 634 acquire_sem(sAppServer->fAppListLock); 635 636 for (int32 i = 0; i < sAppServer->fAppList.CountItems(); i++) { 637 ServerApp *app = (ServerApp *)sAppServer->fAppList.ItemAt(i); 638 639 if (!app) { 640 printf("PANIC in Broadcast()\n"); 641 continue; 642 } 643 app->PostMessage(code); 644 } 645 646 release_sem(sAppServer->fAppListLock); 647 } 648 649 650 /*! 651 \brief Entry function to run the entire server 652 \param argc Number of command-line arguments present 653 \param argv String array of the command-line arguments 654 \return -1 if the app_server is already running, 0 if everything's OK. 655 */ 656 int 657 main(int argc, char** argv) 658 { 659 // There can be only one.... 660 if (find_port(SERVER_PORT_NAME) >= B_OK) 661 return -1; 662 663 srand(real_time_clock_usecs()); 664 665 AppServer server; 666 server.Run(); 667 668 return 0; 669 } 670