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 #include <unistd.h> 11 12 #include <Accelerant.h> 13 #include <AppDefs.h> 14 #include <Directory.h> 15 #include <Entry.h> 16 #include <File.h> 17 #include <Message.h> 18 #include <Path.h> 19 #include <PortLink.h> 20 #include <StopWatch.h> 21 #include <RosterPrivate.h> 22 #include <Autolock.h> 23 24 #include "BitmapManager.h" 25 #include "ColorSet.h" 26 #include "CursorManager.h" 27 #include "DecorManager.h" 28 #include "DefaultDecorator.h" 29 #include "Desktop.h" 30 #include "FontServer.h" 31 #include "HWInterface.h" 32 #include "RegistrarDefs.h" 33 #include "RGBColor.h" 34 #include "Layer.h" 35 #include "WinBorder.h" 36 #include "RootLayer.h" 37 #include "ScreenManager.h" 38 #include "ServerApp.h" 39 #include "ServerConfig.h" 40 #include "ServerCursor.h" 41 #include "ServerProtocol.h" 42 #include "ServerWindow.h" 43 #include "SystemPalette.h" 44 #include "Utils.h" 45 46 #include "AppServer.h" 47 48 //#define DEBUG_KEYHANDLING 49 //#define DEBUG_SERVER 50 51 #ifdef DEBUG_KEYHANDLING 52 # include <stdio.h> 53 # define KBTRACE(x) printf x 54 #else 55 # define KBTRACE(x) ; 56 #endif 57 58 #ifdef DEBUG_SERVER 59 # include <stdio.h> 60 # define STRACE(x) printf x 61 #else 62 # define STRACE(x) ; 63 #endif 64 65 66 // Globals 67 static Desktop *sDesktop; 68 69 port_id gAppServerPort; 70 71 static AppServer *sAppServer; 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 AppServer::AppServer() 83 : MessageLooper("app_server"), 84 fCursorSem(-1), 85 fCursorArea(-1) 86 { 87 fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, SERVER_PORT_NAME); 88 if (fMessagePort < B_OK) 89 debugger("app_server could not create message port"); 90 91 fLink.SetReceiverPort(fMessagePort); 92 gAppServerPort = fMessagePort; 93 94 // Create the input port. The input_server will send it's messages here. 95 // TODO: If we want multiple user support we would need an individual 96 // port for each user and do the following for each RootLayer. 97 fServerInputPort = create_port(200, SERVER_INPUT_PORT); 98 if (fServerInputPort < B_OK) 99 debugger("app_server could not create input port"); 100 101 sAppServer = this; 102 103 // Create the font server and scan the proper directories. 104 gFontServer = new FontServer; 105 gFontServer->Lock(); 106 gFontServer->ScanSystemFolders(); 107 gFontServer->SaveList(); 108 109 if (!gFontServer->SetSystemPlain(DEFAULT_PLAIN_FONT_FAMILY, 110 DEFAULT_PLAIN_FONT_STYLE, 111 DEFAULT_PLAIN_FONT_SIZE) && 112 !gFontServer->SetSystemPlain(FALLBACK_PLAIN_FONT_FAMILY, 113 DEFAULT_PLAIN_FONT_STYLE, 114 DEFAULT_PLAIN_FONT_SIZE)) { 115 printf("Couldn't set plain to %s (fallback: %s), %s %d pt\n", 116 DEFAULT_PLAIN_FONT_FAMILY, 117 FALLBACK_PLAIN_FONT_FAMILY, 118 DEFAULT_PLAIN_FONT_STYLE, 119 DEFAULT_PLAIN_FONT_SIZE); 120 } 121 122 if (!gFontServer->SetSystemBold(DEFAULT_BOLD_FONT_FAMILY, 123 DEFAULT_BOLD_FONT_STYLE, 124 DEFAULT_BOLD_FONT_SIZE) && 125 !gFontServer->SetSystemBold(FALLBACK_BOLD_FONT_FAMILY, 126 DEFAULT_BOLD_FONT_STYLE, 127 DEFAULT_BOLD_FONT_SIZE)) { 128 printf("Couldn't set bold to %s (fallback: %s), %s %d pt\n", 129 DEFAULT_BOLD_FONT_FAMILY, 130 FALLBACK_BOLD_FONT_FAMILY, 131 DEFAULT_BOLD_FONT_STYLE, 132 DEFAULT_BOLD_FONT_SIZE); 133 } 134 135 if (!gFontServer->SetSystemFixed(DEFAULT_FIXED_FONT_FAMILY, 136 DEFAULT_FIXED_FONT_STYLE, 137 DEFAULT_FIXED_FONT_SIZE) && 138 !gFontServer->SetSystemFixed(FALLBACK_FIXED_FONT_FAMILY, 139 DEFAULT_FIXED_FONT_STYLE, 140 DEFAULT_FIXED_FONT_SIZE)) { 141 printf("Couldn't set fixed to %s (fallback: %s), %s %d pt\n", 142 DEFAULT_FIXED_FONT_FAMILY, 143 FALLBACK_FIXED_FONT_FAMILY, 144 DEFAULT_FIXED_FONT_STYLE, 145 DEFAULT_FIXED_FONT_SIZE); 146 } 147 148 gFontServer->Unlock(); 149 150 // Load the GUI colors here and set the global set to the values contained therein. If this 151 // is not possible, set colors to the defaults 152 if (LoadColorSet(SERVER_SETTINGS_DIR COLOR_SETTINGS_NAME, &gGUIColorSet) != B_OK) 153 gGUIColorSet.SetToDefaults(); 154 155 gScreenManager = new ScreenManager(); 156 gScreenManager->Run(); 157 158 // the system palette needs to be initialized before the desktop, 159 // since it is used there already 160 InitializeColorMap(); 161 162 // Create the bitmap allocator. Object declared in BitmapManager.cpp 163 gBitmapManager = new BitmapManager(); 164 165 // Set up the Desktop 166 sDesktop = new Desktop(); 167 sDesktop->Init(); 168 sDesktop->Run(); 169 170 #if 0 171 LaunchCursorThread(); 172 #endif 173 } 174 175 /*! 176 \brief Destructor 177 Reached only when the server is asked to shut down in Test mode. 178 */ 179 AppServer::~AppServer() 180 { 181 delete gBitmapManager; 182 183 gScreenManager->Lock(); 184 gScreenManager->Quit(); 185 186 delete gFontServer; 187 } 188 189 190 /*! 191 \brief Starts Input Server 192 */ 193 void 194 AppServer::LaunchInputServer() 195 { 196 // We are supposed to start the input_server, but it's a BApplication 197 // that depends on the registrar running, which is started after app_server 198 // so we wait on roster thread to launch the input server 199 200 fISThreadID = B_ERROR; 201 202 while (!BRoster::Private().IsMessengerValid(false) && !fQuitting) { 203 snooze(250000); 204 BRoster::Private::DeleteBeRoster(); 205 BRoster::Private::InitBeRoster(); 206 } 207 208 if (fQuitting) 209 return; 210 211 // we use an area for cursor data communication with input_server 212 // area id and sem id are sent to the input_server 213 214 if (fCursorArea < B_OK) 215 fCursorArea = create_area("isCursor", (void**) &fCursorAddr, B_ANY_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA); 216 if (fCursorSem < B_OK) 217 fCursorSem = create_sem(0, "isSem"); 218 219 int32 arg_c = 1; 220 char **arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1)); 221 #if TEST_MODE 222 arg_v[0] = strdup("/boot/home/svnhaiku/trunk/distro/x86.R1/beos/system/servers/input_server"); 223 #else 224 arg_v[0] = strdup("/system/servers/input_server"); 225 #endif 226 arg_v[1] = NULL; 227 fISThreadID = load_image(arg_c, (const char**)arg_v, (const char **)environ); 228 free(arg_v[0]); 229 230 int32 tmpbuf[2] = {fCursorSem, fCursorArea}; 231 int32 code = 0; 232 send_data(fISThreadID, code, (void *)tmpbuf, sizeof(tmpbuf)); 233 234 resume_thread(fISThreadID); 235 setpgid(fISThreadID, 0); 236 237 // we receive 238 239 thread_id sender; 240 code = receive_data(&sender, (void *)tmpbuf, sizeof(tmpbuf)); 241 fISASPort = tmpbuf[0]; 242 fISPort = tmpbuf[1]; 243 244 // if at any time, one of these ports is error prone, it might mean input_server is gone 245 // then relaunch input_server 246 } 247 248 249 /*! 250 \brief Starts the Cursor Thread 251 */ 252 void 253 AppServer::LaunchCursorThread() 254 { 255 // Spawn our cursor thread 256 fCursorThreadID = spawn_thread(CursorThread, "CursorThreadOfTheDeath", B_REAL_TIME_DISPLAY_PRIORITY - 1, this); 257 if (fCursorThreadID >= 0) 258 resume_thread(fCursorThreadID); 259 260 } 261 262 /*! 263 \brief The Cursor Thread task 264 */ 265 int32 266 AppServer::CursorThread(void* data) 267 { 268 AppServer *server = (AppServer *)data; 269 270 server->LaunchInputServer(); 271 272 do { 273 while (acquire_sem(server->fCursorSem) == B_OK) { 274 BPoint p; 275 p.y = *server->fCursorAddr & 0x7fff; 276 p.x = *server->fCursorAddr >> 15 & 0x7fff; 277 278 sDesktop->GetHWInterface()->MoveCursorTo(p.x, p.y); 279 STRACE(("CursorThread : %f, %f\n", p.x, p.y)); 280 } 281 282 snooze(100000); 283 } while (!server->IsQuitting()); 284 285 return B_OK; 286 } 287 288 289 /*! 290 \brief The call that starts it all... 291 */ 292 void 293 AppServer::RunLooper() 294 { 295 rename_thread(find_thread(NULL), "picasso"); 296 _message_thread((void *)this); 297 } 298 299 300 /*! 301 \brief Message handling function for all messages sent to the app_server 302 \param code ID of the message sent 303 \param buffer Attachment buffer for the message. 304 305 */ 306 void 307 AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg) 308 { 309 switch (code) { 310 case AS_GET_DESKTOP: 311 { 312 port_id replyPort; 313 if (msg.Read<port_id>(&replyPort) < B_OK) 314 break; 315 316 int32 userID; 317 msg.Read<int32>(&userID); 318 319 BPrivate::LinkSender reply(replyPort); 320 reply.StartMessage(B_OK); 321 reply.Attach<port_id>(sDesktop->MessagePort()); 322 reply.Flush(); 323 break; 324 } 325 326 case AS_QUERY_FONTS_CHANGED: 327 { 328 // Client application is asking if the font list has changed since 329 // the last client-side refresh 330 331 // Attached data: 332 // 1) port_id reply port 333 334 gFontServer->Lock(); 335 bool needsUpdate = gFontServer->FontsNeedUpdated(); 336 gFontServer->Unlock(); 337 338 // Seeing how the client merely wants an answer, we'll skip the BPortLink 339 // and all its overhead and just write the code to port. 340 port_id replyPort; 341 if (msg.Read<port_id>(&replyPort) < B_OK) 342 break; 343 344 BPrivate::PortLink reply(replyPort); 345 reply.StartMessage(needsUpdate ? SERVER_TRUE : SERVER_FALSE); 346 reply.Flush(); 347 break; 348 } 349 350 #if TEST_MODE 351 case B_QUIT_REQUESTED: 352 { 353 thread_id thread = sDesktop->Thread(); 354 355 // We've been asked to quit, so (for now) broadcast to all 356 // test apps to quit. This situation will occur only when the server 357 // is compiled as a regular Be application. 358 359 sDesktop->PostMessage(B_QUIT_REQUESTED); 360 fQuitting = true; 361 362 // we just wait for the desktop to kill itself 363 status_t status; 364 wait_for_thread(thread, &status); 365 366 kill_thread(fCursorThreadID); 367 delete this; 368 369 // we are now clear to exit 370 exit(0); 371 break; 372 } 373 #endif 374 375 case AS_SET_SYSCURSOR_DEFAULTS: 376 { 377 sDesktop->GetCursorManager().SetDefaults(); 378 break; 379 } 380 381 case AS_ACTIVATE_APP: 382 { 383 // Someone is requesting to activation of a certain app. 384 385 // Attached data: 386 // 1) port_id reply port 387 // 2) team_id team 388 389 status_t error = B_OK; 390 391 // get the parameters 392 port_id replyPort; 393 team_id team; 394 if (msg.Read(&replyPort) < B_OK 395 || msg.Read(&team) < B_OK) { 396 error = B_ERROR; 397 } 398 399 // activate one of the app's windows. 400 if (error == B_OK) { 401 error = B_BAD_TEAM_ID; 402 if (sDesktop->Lock()) { 403 // search for an unhidden window to give focus to 404 int32 windowCount = sDesktop->WindowList().CountItems(); 405 Layer *layer; 406 for (int32 i = 0; i < windowCount; ++i) 407 // is this layer in fact a WinBorder? 408 if ((layer = static_cast<Layer*>(sDesktop->WindowList().ItemAtFast(i)))) { 409 WinBorder *winBorder = dynamic_cast<WinBorder*>(layer); 410 // if winBorder is valid and not hidden, then we've found our target 411 if (winBorder && !winBorder->IsHidden() 412 && winBorder->App()->ClientTeam() == team) { 413 if (sDesktop->ActiveRootLayer() 414 && sDesktop->ActiveRootLayer()->Lock()) { 415 sDesktop->ActiveRootLayer()->SetActive(winBorder); 416 sDesktop->ActiveRootLayer()->Unlock(); 417 418 if (sDesktop->ActiveRootLayer()->Active() == winBorder) 419 error = B_OK; 420 else 421 error = B_ERROR; 422 } 423 } 424 } 425 sDesktop->Unlock(); 426 } 427 } 428 429 // send the reply 430 BPrivate::PortLink replyLink(replyPort); 431 replyLink.StartMessage(error); 432 replyLink.Flush(); 433 break; 434 } 435 436 default: 437 STRACE(("Server::MainLoop received unexpected code %ld (offset %ld)\n", 438 code, code - SERVER_TRUE)); 439 break; 440 } 441 } 442 443 444 // #pragma mark - 445 446 447 /*! 448 \brief Entry function to run the entire server 449 \param argc Number of command-line arguments present 450 \param argv String array of the command-line arguments 451 \return -1 if the app_server is already running, 0 if everything's OK. 452 */ 453 int 454 main(int argc, char** argv) 455 { 456 // There can be only one.... 457 if (find_port(SERVER_PORT_NAME) >= B_OK) 458 return -1; 459 460 srand(real_time_clock_usecs()); 461 462 AppServer* server = new AppServer; 463 server->RunLooper(); 464 465 return 0; 466 } 467