1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2005, Haiku, Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: AppServer.cpp 23 // Author: DarkWyrm <bpmagic@columbus.rr.com> 24 // Description: main manager object for the app_server 25 // 26 //------------------------------------------------------------------------------ 27 #include "AppServer.h" 28 #include "ServerApp.h" 29 #include "ServerProtocol.h" 30 31 #include <Accelerant.h> 32 #include <AppDefs.h> 33 #include <Directory.h> 34 #include <Entry.h> 35 #include <File.h> 36 #include <Message.h> 37 #include <Path.h> 38 #include <PortLink.h> 39 #include <StopWatch.h> 40 41 #include <stdio.h> 42 #include <stdlib.h> 43 44 //#define DEBUG_KEYHANDLING 45 //#define DEBUG_SERVER 46 47 #ifdef DEBUG_KEYHANDLING 48 # define KBTRACE(x) printf x 49 #else 50 # define KBTRACE(x) ; 51 #endif 52 53 #ifdef DEBUG_SERVER 54 # define STRACE(x) printf x 55 #else 56 # define STRACE(x) ; 57 #endif 58 59 // Globals 60 61 port_id gAppServerPort; 62 63 //! Used to access the app_server from new_decorator 64 static AppServer *sAppServer = NULL; 65 66 //! Default background color for workspaces 67 //RGBColor workspace_default_color(51,102,160); 68 69 70 /*! 71 \brief Constructor 72 73 This loads the default fonts, allocates all the major global variables, spawns the main housekeeping 74 threads, loads user preferences for the UI and decorator, and allocates various locks. 75 */ 76 AppServer::AppServer(void) 77 : 78 fCursorSem(-1), 79 fCursorArea(-1) 80 { 81 fMessagePort = create_port(200, SERVER_PORT_NAME); 82 if (fMessagePort == B_NO_MORE_PORTS) 83 debugger("app_server could not create message port"); 84 85 gAppServerPort = fMessagePort; 86 87 fAppList = new BList(); 88 fQuittingServer = false; 89 90 // We need this in order for new_decorator to be able to instantiate new decorators 91 sAppServer = this; 92 93 // This is necessary to mediate access between the Poller and app_server threads 94 fActiveAppLock = create_sem(1,"app_server_active_sem"); 95 96 // This locker is for app_server and Picasso to vy for control of the ServerApp list 97 fAppListLock = create_sem(1,"app_server_applist_sem"); 98 99 // Spawn our thread-monitoring thread 100 fPicassoThreadID = spawn_thread(PicassoThread, "picasso", B_NORMAL_PRIORITY, this); 101 if (fPicassoThreadID >= 0) 102 resume_thread(fPicassoThreadID); 103 } 104 105 /*! 106 \brief Destructor 107 108 Reached only when the server is asked to shut down in Test mode. Kills all apps, shuts down the 109 desktop, kills the housekeeping threads, etc. 110 */ 111 AppServer::~AppServer() 112 { 113 } 114 115 116 /*! 117 \brief Thread function for watching for dead apps 118 \param data Pointer to the app_server to which the thread belongs 119 \return Throwaway value - always 0 120 */ 121 int32 122 AppServer::PicassoThread(void *data) 123 { 124 for (;;) { 125 acquire_sem(sAppServer->fAppListLock); 126 for (int32 i = 0;;) { 127 ServerApp *app = (ServerApp *)sAppServer->fAppList->ItemAt(i++); 128 if (!app) 129 break; 130 131 app->PingTarget(); 132 } 133 release_sem(sAppServer->fAppListLock); 134 // we do this every other second so as not to suck *too* many CPU cycles 135 snooze(1000000); 136 } 137 return 0; 138 } 139 140 141 /*! 142 \brief Starts Input Server 143 */ 144 void 145 AppServer::LaunchInputServer() 146 { 147 } 148 149 150 /*! 151 \brief Starts the Cursor Thread 152 */ 153 void 154 AppServer::LaunchCursorThread() 155 { 156 } 157 158 159 /*! 160 \brief The call that starts it all... 161 \return Always 0 162 */ 163 thread_id 164 AppServer::Run(void) 165 { 166 MainLoop(); 167 kill_thread(fPicassoThreadID); 168 return 0; 169 } 170 171 172 //! Main message-monitoring loop for the regular message port - no input messages! 173 void 174 AppServer::MainLoop(void) 175 { 176 BPrivate::PortLink pmsg(-1, fMessagePort); 177 178 while (1) { 179 STRACE(("info: AppServer::MainLoop listening on port %ld.\n", fMessagePort)); 180 181 int32 code; 182 status_t err = pmsg.GetNextMessage(code); 183 if (err < B_OK) { 184 STRACE(("MainLoop:pmsg.GetNextMessage() failed\n")); 185 continue; 186 } 187 188 switch (code) { 189 case B_QUIT_REQUESTED: 190 case AS_GET_DESKTOP: 191 case AS_CREATE_APP: 192 case AS_DELETE_APP: 193 DispatchMessage(code, pmsg); 194 break; 195 default: 196 { 197 STRACE(("Server::MainLoop received unexpected code %ld\n", 198 code)); 199 break; 200 } 201 } 202 } 203 } 204 205 /*! 206 \brief Message handling function for all messages sent to the app_server 207 \param code ID of the message sent 208 \param buffer Attachment buffer for the message. 209 210 */ 211 void 212 AppServer::DispatchMessage(int32 code, BPrivate::PortLink &msg) 213 { 214 switch (code) { 215 case AS_GET_DESKTOP: 216 { 217 port_id replyPort; 218 if (msg.Read<port_id>(&replyPort) < B_OK) 219 break; 220 221 int32 userID; 222 msg.Read<int32>(&userID); 223 224 BPrivate::LinkSender reply(replyPort); 225 reply.StartMessage(B_OK); 226 reply.Attach<port_id>(fMessagePort); 227 reply.Flush(); 228 break; 229 } 230 231 case AS_CREATE_APP: 232 { 233 // Create the ServerApp to node monitor a new BApplication 234 235 // Attached data: 236 // 1) port_id - receiver port of a regular app 237 // 2) port_id - client looper port - for send messages to the client 238 // 2) team_id - app's team ID 239 // 3) int32 - handler token of the regular app 240 // 4) char * - signature of the regular app 241 242 // Find the necessary data 243 team_id clientTeamID = -1; 244 port_id clientLooperPort = -1; 245 port_id clientReplyPort = -1; 246 int32 clientToken; 247 char *appSignature = NULL; 248 249 msg.Read<port_id>(&clientReplyPort); 250 msg.Read<port_id>(&clientLooperPort); 251 msg.Read<team_id>(&clientTeamID); 252 msg.Read<int32>(&clientToken); 253 if (msg.ReadString(&appSignature) != B_OK) 254 break; 255 256 port_id serverListen = create_port(DEFAULT_MONITOR_PORT_SIZE, appSignature); 257 if (serverListen < B_OK) { 258 printf("No more ports left. Time to crash. Have a nice day! :)\n"); 259 break; 260 } 261 262 // we let the application own the port, so that we get aware when it's gone 263 if (set_port_owner(serverListen, clientTeamID) < B_OK) { 264 delete_port(serverListen); 265 printf("Could not transfer port ownership to client %ld!\n", clientTeamID); 266 break; 267 } 268 269 // Create the ServerApp subthread for this app 270 acquire_sem(fAppListLock); 271 272 ServerApp *app = new ServerApp(clientReplyPort, serverListen, clientLooperPort, 273 clientTeamID, clientToken, appSignature); 274 275 // add the new ServerApp to the known list of ServerApps 276 fAppList->AddItem(app); 277 278 release_sem(fAppListLock); 279 280 BPrivate::PortLink replylink(clientReplyPort); 281 replylink.StartMessage(B_OK); 282 replylink.Attach<int32>(serverListen); 283 replylink.Flush(); 284 285 // This is necessary because BPortLink::ReadString allocates memory 286 free(appSignature); 287 break; 288 } 289 case AS_DELETE_APP: 290 { 291 // Delete a ServerApp. Received only from the respective ServerApp when a 292 // BApplication asks it to quit. 293 294 // Attached Data: 295 // 1) thread_id - thread ID of the ServerApp to be deleted 296 297 int32 i = 0, appnum = fAppList->CountItems(); 298 ServerApp *srvapp = NULL; 299 thread_id srvapp_id = -1; 300 301 if (msg.Read<thread_id>(&srvapp_id) < B_OK) 302 break; 303 304 acquire_sem(fAppListLock); 305 306 // Run through the list of apps and nuke the proper one 307 for (i = 0; i < appnum; i++) { 308 srvapp = (ServerApp *)fAppList->ItemAt(i); 309 310 if (srvapp != NULL && srvapp->MonitorThreadID() == srvapp_id) { 311 srvapp = (ServerApp *)fAppList->RemoveItem(i); 312 if (srvapp) { 313 delete srvapp; 314 srvapp = NULL; 315 } 316 break; // jump out of our for() loop 317 } 318 } 319 320 release_sem(fAppListLock); 321 break; 322 } 323 324 case B_QUIT_REQUESTED: 325 break; 326 327 default: 328 // we should never get here. 329 break; 330 } 331 } 332 333 /*! 334 \brief Send a quick (no attachments) message to all applications 335 336 Quite useful for notification for things like server shutdown, system 337 color changes, etc. 338 */ 339 void 340 AppServer::Broadcast(int32 code) 341 { 342 acquire_sem(fAppListLock); 343 344 for (int32 i = 0; i < fAppList->CountItems(); i++) { 345 ServerApp *app = (ServerApp *)fAppList->ItemAt(i); 346 347 if (!app) 348 { printf("PANIC in AppServer::Broadcast()\n"); continue; } 349 app->PostMessage(code); 350 } 351 352 release_sem(fAppListLock); 353 } 354 355 /*! 356 \brief Finds the application with the given signature 357 \param sig MIME signature of the application to find 358 \return the corresponding ServerApp or NULL if not found 359 360 This call should be made only when necessary because it locks the app list 361 while it does its searching. 362 */ 363 ServerApp * 364 AppServer::FindApp(const char *sig) 365 { 366 if (!sig) 367 return NULL; 368 369 ServerApp *foundapp=NULL; 370 371 acquire_sem(fAppListLock); 372 373 for(int32 i=0; i<fAppList->CountItems();i++) 374 { 375 foundapp=(ServerApp*)fAppList->ItemAt(i); 376 if(foundapp && foundapp->Title() == sig) 377 { 378 release_sem(fAppListLock); 379 return foundapp; 380 } 381 } 382 383 release_sem(fAppListLock); 384 385 // couldn't find a match 386 return NULL; 387 } 388 389 390 // #pragma mark - 391 392 393 /*! 394 \brief Entry function to run the entire server 395 \param argc Number of command-line arguments present 396 \param argv String array of the command-line arguments 397 \return -1 if the app_server is already running, 0 if everything's OK. 398 */ 399 int 400 main(int argc, char** argv) 401 { 402 // There can be only one.... 403 if (find_port(SERVER_PORT_NAME) >= B_OK) 404 return -1; 405 406 AppServer app_server; 407 app_server.Run(); 408 return 0; 409 } 410