#include "AppServer.h"
#include "ServerApp.h"
#include "ServerProtocol.h"
#include <Accelerant.h>
#include <AppDefs.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <Message.h>
#include <Path.h>
#include <PortLink.h>
#include <StopWatch.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef DEBUG_KEYHANDLING
# define KBTRACE(x) printf x
#else
# define KBTRACE(x) ;
#endif
#ifdef DEBUG_SERVER
# define STRACE(x) printf x
#else
# define STRACE(x) ;
#endif
port_id gAppServerPort;
static AppServer *sAppServer = NULL;
\brief Constructor
This loads the default fonts, allocates all the major global variables, spawns the main housekeeping
threads, loads user preferences for the UI and decorator, and allocates various locks.
*/
AppServer::AppServer(void)
:
fCursorSem(-1),
fCursorArea(-1)
{
fMessagePort = create_port(200, SERVER_PORT_NAME);
if (fMessagePort == B_NO_MORE_PORTS)
debugger("app_server could not create message port");
gAppServerPort = fMessagePort;
fAppList = new BList();
fQuittingServer = false;
sAppServer = this;
fActiveAppLock = create_sem(1,"app_server_active_sem");
fAppListLock = create_sem(1,"app_server_applist_sem");
fPicassoThreadID = spawn_thread(PicassoThread, "picasso", B_NORMAL_PRIORITY, this);
if (fPicassoThreadID >= 0)
resume_thread(fPicassoThreadID);
}
\brief Destructor
Reached only when the server is asked to shut down in Test mode. Kills all apps, shuts down the
desktop, kills the housekeeping threads, etc.
*/
AppServer::~AppServer()
{
}
\brief Thread function for watching for dead apps
\param data Pointer to the app_server to which the thread belongs
\return Throwaway value - always 0
*/
int32
AppServer::PicassoThread(void *data)
{
for (;;) {
acquire_sem(sAppServer->fAppListLock);
for (int32 i = 0;;) {
ServerApp *app = (ServerApp *)sAppServer->fAppList->ItemAt(i++);
if (!app)
break;
app->PingTarget();
}
release_sem(sAppServer->fAppListLock);
snooze(1000000);
}
return 0;
}
\brief Starts Input Server
*/
void
AppServer::LaunchInputServer()
{
}
\brief Starts the Cursor Thread
*/
void
AppServer::LaunchCursorThread()
{
}
\brief The call that starts it all...
\return Always 0
*/
thread_id
AppServer::Run(void)
{
MainLoop();
kill_thread(fPicassoThreadID);
return 0;
}
void
AppServer::MainLoop(void)
{
BPrivate::PortLink pmsg(-1, fMessagePort);
while (1) {
STRACE(("info: AppServer::MainLoop listening on port %ld.\n", fMessagePort));
int32 code;
status_t err = pmsg.GetNextMessage(code);
if (err < B_OK) {
STRACE(("MainLoop:pmsg.GetNextMessage() failed\n"));
continue;
}
switch (code) {
case B_QUIT_REQUESTED:
case AS_GET_DESKTOP:
case AS_CREATE_APP:
case AS_DELETE_APP:
DispatchMessage(code, pmsg);
break;
default:
{
STRACE(("Server::MainLoop received unexpected code %ld\n",
code));
break;
}
}
}
}
\brief Message handling function for all messages sent to the app_server
\param code ID of the message sent
\param buffer Attachment buffer for the message.
*/
void
AppServer::DispatchMessage(int32 code, BPrivate::PortLink &msg)
{
switch (code) {
case AS_GET_DESKTOP:
{
port_id replyPort;
if (msg.Read<port_id>(&replyPort) < B_OK)
break;
int32 userID;
msg.Read<int32>(&userID);
BPrivate::LinkSender reply(replyPort);
reply.StartMessage(B_OK);
reply.Attach<port_id>(fMessagePort);
reply.Flush();
break;
}
case AS_CREATE_APP:
{
team_id clientTeamID = -1;
port_id clientLooperPort = -1;
port_id clientReplyPort = -1;
int32 clientToken;
char *appSignature = NULL;
msg.Read<port_id>(&clientReplyPort);
msg.Read<port_id>(&clientLooperPort);
msg.Read<team_id>(&clientTeamID);
msg.Read<int32>(&clientToken);
if (msg.ReadString(&appSignature) != B_OK)
break;
port_id serverListen = create_port(DEFAULT_MONITOR_PORT_SIZE, appSignature);
if (serverListen < B_OK) {
printf("No more ports left. Time to crash. Have a nice day! :)\n");
break;
}
if (set_port_owner(serverListen, clientTeamID) < B_OK) {
delete_port(serverListen);
printf("Could not transfer port ownership to client %ld!\n", clientTeamID);
break;
}
acquire_sem(fAppListLock);
ServerApp *app = new ServerApp(clientReplyPort, serverListen, clientLooperPort,
clientTeamID, clientToken, appSignature);
fAppList->AddItem(app);
release_sem(fAppListLock);
BPrivate::PortLink replylink(clientReplyPort);
replylink.StartMessage(B_OK);
replylink.Attach<int32>(serverListen);
replylink.Flush();
free(appSignature);
break;
}
case AS_DELETE_APP:
{
int32 i = 0, appnum = fAppList->CountItems();
ServerApp *srvapp = NULL;
thread_id srvapp_id = -1;
if (msg.Read<thread_id>(&srvapp_id) < B_OK)
break;
acquire_sem(fAppListLock);
for (i = 0; i < appnum; i++) {
srvapp = (ServerApp *)fAppList->ItemAt(i);
if (srvapp != NULL && srvapp->MonitorThreadID() == srvapp_id) {
srvapp = (ServerApp *)fAppList->RemoveItem(i);
if (srvapp) {
delete srvapp;
srvapp = NULL;
}
break;
}
}
release_sem(fAppListLock);
break;
}
case B_QUIT_REQUESTED:
break;
default:
break;
}
}
\brief Send a quick (no attachments) message to all applications
Quite useful for notification for things like server shutdown, system
color changes, etc.
*/
void
AppServer::Broadcast(int32 code)
{
acquire_sem(fAppListLock);
for (int32 i = 0; i < fAppList->CountItems(); i++) {
ServerApp *app = (ServerApp *)fAppList->ItemAt(i);
if (!app)
{ printf("PANIC in AppServer::Broadcast()\n"); continue; }
app->PostMessage(code);
}
release_sem(fAppListLock);
}
\brief Finds the application with the given signature
\param sig MIME signature of the application to find
\return the corresponding ServerApp or NULL if not found
This call should be made only when necessary because it locks the app list
while it does its searching.
*/
ServerApp *
AppServer::FindApp(const char *sig)
{
if (!sig)
return NULL;
ServerApp *foundapp=NULL;
acquire_sem(fAppListLock);
for(int32 i=0; i<fAppList->CountItems();i++)
{
foundapp=(ServerApp*)fAppList->ItemAt(i);
if(foundapp && foundapp->Title() == sig)
{
release_sem(fAppListLock);
return foundapp;
}
}
release_sem(fAppListLock);
return NULL;
}
\brief Entry function to run the entire server
\param argc Number of command-line arguments present
\param argv String array of the command-line arguments
\return -1 if the app_server is already running, 0 if everything's OK.
*/
int
main(int argc, char** argv)
{
if (find_port(SERVER_PORT_NAME) >= B_OK)
return -1;
AppServer app_server;
app_server.Run();
return 0;
}