From 73a2b29f2ec1aa7b31fd513fda123dd4cb2980aa Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 26 Jul 2002 19:55:10 +0000 Subject: [PATCH] * Implemented InitData(), which should now be complete with respect to what we need for the roster. * B_{ARGV,REFS}_RECEIVED and B_READY_TO_RUN messages are dispatched now. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@457 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/kits/app/Application.cpp | 284 ++++++++++++++++++++++++++++++++--- 1 file changed, 266 insertions(+), 18 deletions(-) diff --git a/src/kits/app/Application.cpp b/src/kits/app/Application.cpp index d985158d43..a340a522db 100644 --- a/src/kits/app/Application.cpp +++ b/src/kits/app/Application.cpp @@ -27,12 +27,19 @@ //------------------------------------------------------------------------------ // Standard Includes ----------------------------------------------------------- +#include #include +#include +#include // System Includes ------------------------------------------------------------- +#include #include #include +#include +#include #include +#include #include #include @@ -49,6 +56,9 @@ BMessenger be_app_messenger; BResources* BApplication::_app_resources = NULL; BLocker BApplication::_app_resources_lock("_app_resources_lock"); +// argc/argv +extern const int __libc_argc; +extern const char * const *__libc_argv; //------------------------------------------------------------------------------ @@ -61,17 +71,12 @@ enum { NOT_IMPLEMENTED = B_ERROR, }; +// prototypes of helper functions +static const char* looper_name_for(const char *signature); +static void assert_app_signature(const char *signature); +static status_t get_app_path(char *buffer); +static thread_id main_thread_for(team_id team); -// Returns the looper name for a given signature: Normally "AppLooperPort", -// but in case of the registrar a special name. -static -const char* -looper_name_for(const char *signature) -{ - if (signature && !strcmp(signature, kRegistrarSignature)) - return kRosterPortName; - return "AppLooperPort"; -} //------------------------------------------------------------------------------ BApplication::BApplication(const char* signature) : BLooper(looper_name_for(signature)), @@ -107,6 +112,8 @@ BApplication::BApplication(const char* signature, status_t* error) //------------------------------------------------------------------------------ BApplication::~BApplication() { + // unregister from the roster + be_roster->RemoveApp(Team()); } //------------------------------------------------------------------------------ BApplication::BApplication(BMessage* data) @@ -188,6 +195,8 @@ void BApplication::Quit() // BLooper::Quit() handles that gracefully. else if (find_thread(NULL) != fTaskID) BLooper::Quit(); + // prevent the BLooper destructor from killing the main thread + fTaskID = -1; } //------------------------------------------------------------------------------ bool BApplication::QuitRequested() @@ -314,9 +323,56 @@ BResources* BApplication::AppResources() return NULL; // not implemented } //------------------------------------------------------------------------------ -void BApplication::DispatchMessage(BMessage* an_event, BHandler* handler) +void BApplication::DispatchMessage(BMessage* message, BHandler* handler) { - BLooper::DispatchMessage(an_event, handler); + switch (message->what) { + case B_ARGV_RECEIVED: + { + // build the argv vector + status_t error = B_OK; + int32 argc; + char **argv = NULL; + if (message->FindInt32("argc", &argc) == B_OK && argc > 0) { + argv = new char*[argc]; + for (int32 i = 0; error == B_OK && i < argc; i++) + argv[i] = NULL; + // copy the arguments + for (int32 i = 0; error == B_OK && i < argc; i++) { + const char *arg = NULL; + error = message->FindString("argv", i, &arg); + if (error == B_OK && arg) { + argv[i] = new(nothrow) char[strlen(arg) + 1]; + if (argv[i]) + strcpy(argv[i], arg); + else + error = B_NO_MEMORY; + } + } + } + // call the hook + if (error == B_OK) + ArgvReceived(argc, argv); + // cleanup + if (argv) { + for (int32 i = 0; i < argc; i++) + delete[] argv[i]; + delete[] argv; + } + break; + } + case B_REFS_RECEIVED: + RefsReceived(message); + break; + case B_READY_TO_RUN: + // TODO: Find out, whether to set fReadyToRunCalled before or + // after calling the hook. + ReadyToRun(); + fReadyToRunCalled = true; + break; + default: + BLooper::DispatchMessage(message, handler); + break; + } } //------------------------------------------------------------------------------ void BApplication::SetPulseRate(bigtime_t rate) @@ -392,12 +448,110 @@ void BApplication::run_task() void BApplication::InitData(const char* signature, status_t* error) { // check signature + assert_app_signature(signature); fAppName = signature; - // ... - // check the looper - // ... - // everything went fine - fInitError = B_OK; + bool isRegistrar = (signature && !strcmp(signature, kRegistrarSignature)); + // get team and thread + team_id team = Team(); + thread_id thread = main_thread_for(team); + // get app executable ref + entry_ref ref; + char appFilePath[B_PATH_NAME_LENGTH + 1]; + fInitError = get_app_path(appFilePath); + if (fInitError == B_OK) { + BEntry entry(appFilePath, true); + fInitError = entry.GetRef(&ref); + } + // get the BAppFileInfo and extract the information we need + uint32 appFlags = B_REG_DEFAULT_APP_FLAGS; + if (fInitError == B_OK) { + BAppFileInfo fileInfo; + BFile file(&ref, B_READ_ONLY); + fInitError = fileInfo.SetTo(&file); + if (fInitError == B_OK) { + fileInfo.GetAppFlags(&appFlags); + char appFileSignature[B_MIME_TYPE_LENGTH + 1]; + // compare the file signature and the supplied signature + if (fileInfo.GetSignature(appFileSignature) == B_OK) { + if (strcmp(appFileSignature, signature) != 0) { + printf("Signature in rsrc doesn't match constructor arg. " + "(%s,%s)\n", signature, appFileSignature); + } + } + } + } + // check whether or not we are pre-registered + bool preRegistered = false; + app_info appInfo; + if (fInitError == B_OK && !isRegistrar) + preRegistered = be_roster->IsAppPreRegistered(&ref, team, &appInfo); + if (preRegistered) { + // we are pre-registered => the app info has been filled in + // Check whether we need to replace the looper port with a port + // created by the roster. + if (appInfo.port >= 0 && appInfo.port != fMsgPort) { + delete_port(fMsgPort); + fMsgPort = appInfo.port; + } else + appInfo.port = fMsgPort; + // Create a B_ARGV_RECEIVED message and send it to ourselves. + // TODO: When BLooper::AddMessage() is done, use that instead of + // PostMessage(). + if (!(appFlags & B_ARGV_ONLY) && __libc_argc > 1) { + BMessage argvMessage(B_ARGV_RECEIVED); + do_argv(&argvMessage); + PostMessage(&argvMessage, this); + } + // complete the registration + fInitError = be_roster->CompleteRegistration(team, thread, + appInfo.port); + } else if (fInitError == B_OK) { + // not pre-registered -- try to register the application + team_id otherTeam = -1; + status_t regError = B_OK; + // the registrar must not register + if (!isRegistrar) { + regError = be_roster->AddApplication(signature, &ref, appFlags, + team, thread, fMsgPort, true, NULL, &otherTeam); + } + if (regError == B_ALREADY_RUNNING) { + // An instance is already running and we asked for + // single/exclusive launch. Send our argv to the running app and + // exit. + if (!(appFlags & B_ARGV_ONLY) && otherTeam >= 0 + && __libc_argc > 1) { + BMessage argvMessage(B_ARGV_RECEIVED); + do_argv(&argvMessage); + // We need to replace the first argv string with the path + // of the respective application. + app_info otherAppInfo; + if (be_roster->GetRunningAppInfo(otherTeam, &otherAppInfo) + == B_OK) { + BPath path; + if (path.SetTo(&otherAppInfo.ref) == B_OK) + argvMessage.ReplaceString("argv", 0, path.Path()); + } + BMessenger(NULL, otherTeam).SendMessage(&argvMessage); + } + exit(0); + } + if (regError == B_OK) { + // the registrations was successful + // Create a B_ARGV_RECEIVED message and send it to ourselves. + // TODO: When BLooper::AddMessage() is done, use that instead of + // PostMessage(). + if (!(appFlags & B_ARGV_ONLY) && __libc_argc > 1) { + BMessage argvMessage(B_ARGV_RECEIVED); + do_argv(&argvMessage); + PostMessage(&argvMessage, this); + } + // send a B_READY_TO_RUN message as well + PostMessage(B_READY_TO_RUN, this); + } else + fInitError = regError; + } + // TODO: SetName() + // return the error if (error) *error = fInitError; } @@ -459,8 +613,21 @@ bool BApplication::window_quit_loop(bool, bool) return false; // not implemented } //------------------------------------------------------------------------------ -void BApplication::do_argv(BMessage* msg) +void BApplication::do_argv(BMessage* message) { + if (message) { + int32 argc = __libc_argc; + const char * const *argv = __libc_argv; + // add argc + message->AddInt32("argc", argc); + // add argv + for (int32 i = 0; i < argc; i++) + message->AddString("argv", argv[i]); + // add current working directory + char cwd[B_PATH_NAME_LENGTH + 1]; + if (getcwd(cwd, B_PATH_NAME_LENGTH + 1)) + message->AddString("cwd", cwd); + } } //------------------------------------------------------------------------------ uint32 BApplication::InitialWorkspace() @@ -489,6 +656,87 @@ int32 BApplication::async_quit_entry(void* data) } //------------------------------------------------------------------------------ +// assert_app_signature +// +// Terminates with and error message, when the supplied signature is not a +// valid application signature. +static +void +assert_app_signature(const char *signature) +{ + bool isValid = false; + BMimeType type(signature); + if (type.IsValid() && !type.IsSupertypeOnly() + && BMimeType("application").Contains(&type)) { + isValid = true; + } + if (!isValid) { + printf("bad signature (%s), must begin with \"application/\" and " + "can't conflict with existing registered mime types inside " + "the \"application\" media type.\n", signature); + exit(1); + } +} + +// looper_name_for +// +// Returns the looper name for a given signature: Normally "AppLooperPort", +// but in case of the registrar a special name. +static +const char* +looper_name_for(const char *signature) +{ + if (signature && !strcmp(signature, kRegistrarSignature)) + return kRosterPortName; + return "AppLooperPort"; +} + +// get_app_path +// +// Returns the path of the application. buffer must be of length +// B_PATH_NAME_LENGTH + 1. +static +status_t +get_app_path(char *buffer) +{ + status_t error = (buffer ? B_OK : B_BAD_VALUE); + image_info info; + int32 cookie = 0; + bool found = false; + if (error == B_OK) { + while (!found && get_next_image_info(0, &cookie, &info) == B_OK) { + if (info.type == B_APP_IMAGE) { + strncpy(buffer, info.name, B_PATH_NAME_LENGTH); + buffer[B_PATH_NAME_LENGTH] = 0; + found = true; + } + } + } + if (error == B_OK && !found) + error = B_ENTRY_NOT_FOUND; + return error; +} + +// main_thread_for +// +// Returns the ID of the supplied team's main thread. +static +thread_id +main_thread_for(team_id team) +{ + // For I can't find any trace of how to explicitly get the main thread, + // I assume the main thread is the one with the least thread ID. + thread_id thread = -1; + int32 cookie = 0; + thread_info info; + while (get_next_thread_info(team, &cookie, &info) == B_OK) { + if (thread < 0 || info.thread < thread) + thread = info.thread; + } + return thread; +} + + /* * $Log $ *