/* * Copyright 2001-2015 Haiku, inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de * Jerome Duval * Erik Jaesler, erik@cgsoftware.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace BPrivate; static const char* kDefaultLooperName = "AppLooperPort"; BApplication* be_app = NULL; BMessenger be_app_messenger; pthread_once_t sAppResourcesInitOnce = PTHREAD_ONCE_INIT; BResources* BApplication::sAppResources = NULL; BObjectList sOnQuitLooperList; enum { kWindowByIndex, kWindowByName, kLooperByIndex, kLooperByID, kLooperByName, kApplication }; static property_info sPropertyInfo[] = { { "Window", {}, {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER}, NULL, kWindowByIndex, {}, {}, {} }, { "Window", {}, {B_NAME_SPECIFIER}, NULL, kWindowByName, {}, {}, {} }, { "Looper", {}, {B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER}, NULL, kLooperByIndex, {}, {}, {} }, { "Looper", {}, {B_ID_SPECIFIER}, NULL, kLooperByID, {}, {}, {} }, { "Looper", {}, {B_NAME_SPECIFIER}, NULL, kLooperByName, {}, {}, {} }, { "Name", {B_GET_PROPERTY}, {B_DIRECT_SPECIFIER}, NULL, kApplication, {B_STRING_TYPE}, {}, {} }, { "Window", {B_COUNT_PROPERTIES}, {B_DIRECT_SPECIFIER}, NULL, kApplication, {B_INT32_TYPE}, {}, {} }, { "Loopers", {B_GET_PROPERTY}, {B_DIRECT_SPECIFIER}, NULL, kApplication, {B_MESSENGER_TYPE}, {}, {} }, { "Windows", {B_GET_PROPERTY}, {B_DIRECT_SPECIFIER}, NULL, kApplication, {B_MESSENGER_TYPE}, {}, {} }, { "Looper", {B_COUNT_PROPERTIES}, {B_DIRECT_SPECIFIER}, NULL, kApplication, {B_INT32_TYPE}, {}, {} }, { 0 } }; // argc/argv extern const int __libc_argc; extern const char* const *__libc_argv; // debugging //#define DBG(x) x #define DBG(x) #define OUT printf // #pragma mark - static helper functions /*! \brief Checks whether the supplied string is a valid application signature. An error message is printed, if the string is no valid app signature. \param signature The string to be checked. \return A status code. \retval B_OK \a signature is a valid app signature. \retval B_BAD_VALUE \a signature is \c NULL or no valid app signature. */ static status_t check_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); } return (isValid ? B_OK : B_BAD_VALUE); } #ifndef RUN_WITHOUT_REGISTRAR // Fills the passed BMessage with B_ARGV_RECEIVED infos. static void fill_argv_message(BMessage &message) { message.what = B_ARGV_RECEIVED; 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++) { if (argv[i] != NULL) message.AddString("argv", argv[i]); } // add current working directory char cwd[B_PATH_NAME_LENGTH]; if (getcwd(cwd, B_PATH_NAME_LENGTH)) message.AddString("cwd", cwd); } #endif // #pragma mark - BApplication BApplication::BApplication(const char* signature) : BLooper(kDefaultLooperName) { _InitData(signature, true, NULL); } BApplication::BApplication(const char* signature, status_t* _error) : BLooper(kDefaultLooperName) { _InitData(signature, true, _error); } BApplication::BApplication(const char* signature, const char* looperName, port_id port, bool initGUI, status_t* _error) : BLooper(B_NORMAL_PRIORITY + 1, port < 0 ? _GetPort(signature) : port, looperName != NULL ? looperName : kDefaultLooperName) { _InitData(signature, initGUI, _error); if (port < 0) fOwnsPort = false; } BApplication::BApplication(BMessage* data) // Note: BeOS calls the private BLooper(int32, port_id, const char*) // constructor here, test if it's needed : BLooper(kDefaultLooperName) { const char* signature = NULL; data->FindString("mime_sig", &signature); _InitData(signature, true, NULL); bigtime_t pulseRate; if (data->FindInt64("_pulse", &pulseRate) == B_OK) SetPulseRate(pulseRate); } #ifdef _BEOS_R5_COMPATIBLE_ BApplication::BApplication(uint32 signature) { } BApplication::BApplication(const BApplication &rhs) { } BApplication& BApplication::operator=(const BApplication &rhs) { return *this; } #endif BApplication::~BApplication() { Lock(); // tell all loopers(usually windows) to quit. Also, wait for them. _QuitAllWindows(true); // quit registered loopers for (int32 i = 0; i < sOnQuitLooperList.CountItems(); i++) { BLooper* looper = sOnQuitLooperList.ItemAt(i); if (looper->Lock()) looper->Quit(); } // unregister from the roster BRoster::Private().RemoveApp(Team()); #ifndef RUN_WITHOUT_APP_SERVER // tell app_server we're quitting... if (be_app) { // be_app can be NULL here if the application fails to initialize // correctly. For example, if it's already running and it's set to // exclusive launch. BPrivate::AppServerLink link; link.StartMessage(B_QUIT_REQUESTED); link.Flush(); } delete_port(fServerLink->SenderPort()); delete_port(fServerLink->ReceiverPort()); delete fServerLink; #endif // RUN_WITHOUT_APP_SERVER delete fServerAllocator; // uninitialize be_app, the be_app_messenger is invalidated automatically be_app = NULL; } void BApplication::_InitData(const char* signature, bool initGUI, status_t* _error) { DBG(OUT("BApplication::InitData(`%s', %p)\n", signature, _error)); // check whether there exists already an application if (be_app != NULL) debugger("2 BApplication objects were created. Only one is allowed."); fServerLink = new BPrivate::PortLink(-1, -1); fServerAllocator = NULL; fServerReadOnlyMemory = NULL; fInitialWorkspace = 0; //fDraggedMessage = NULL; fReadyToRunCalled = false; // initially, there is no pulse fPulseRunner = NULL; fPulseRate = 0; // check signature fInitError = check_app_signature(signature); fAppName = signature; #ifndef RUN_WITHOUT_REGISTRAR bool registerApp = signature == NULL || (strcasecmp(signature, B_REGISTRAR_SIGNATURE) != 0 && strcasecmp(signature, kLaunchDaemonSignature) != 0); // get team and thread team_id team = Team(); thread_id thread = BPrivate::main_thread_for(team); #endif // get app executable ref entry_ref ref; if (fInitError == B_OK) { fInitError = BPrivate::get_app_ref(&ref); if (fInitError != B_OK) { DBG(OUT("BApplication::InitData(): Failed to get app ref: %s\n", strerror(fInitError))); } } // 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]; // compare the file signature and the supplied signature if (fileInfo.GetSignature(appFileSignature) == B_OK && strcasecmp(appFileSignature, signature) != 0) { printf("Signature in rsrc doesn't match constructor arg. (%s, %s)\n", signature, appFileSignature); } } else { DBG(OUT("BApplication::InitData(): Failed to get info from: " "BAppFileInfo: %s\n", strerror(fInitError))); } } #ifndef RUN_WITHOUT_REGISTRAR // check whether be_roster is valid if (fInitError == B_OK && registerApp && !BRoster::Private().IsMessengerValid(false)) { printf("FATAL: be_roster is not valid. Is the registrar running?\n"); fInitError = B_NO_INIT; } // check whether or not we are pre-registered bool preRegistered = false; app_info appInfo; if (fInitError == B_OK && registerApp) { if (BRoster::Private().IsAppRegistered(&ref, team, 0, &preRegistered, &appInfo) != B_OK) { preRegistered = false; } } 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; // check the signature and correct it, if necessary, also the case if (strcmp(appInfo.signature, fAppName)) BRoster::Private().SetSignature(team, fAppName); // complete the registration fInitError = BRoster::Private().CompleteRegistration(team, thread, appInfo.port); } else if (fInitError == B_OK) { // not pre-registered -- try to register the application team_id otherTeam = -1; if (registerApp) { fInitError = BRoster::Private().AddApplication(signature, &ref, appFlags, team, thread, fMsgPort, true, NULL, &otherTeam); if (fInitError != B_OK) { DBG(OUT("BApplication::InitData(): Failed to add app: %s\n", strerror(fInitError))); } } if (fInitError == B_ALREADY_RUNNING) { // An instance is already running and we asked for // single/exclusive launch. Send our argv to the running app. // Do that only, if the app is NOT B_ARGV_ONLY. if (otherTeam >= 0) { BMessenger otherApp(NULL, otherTeam); app_info otherAppInfo; bool argvOnly = be_roster->GetRunningAppInfo(otherTeam, &otherAppInfo) == B_OK && (otherAppInfo.flags & B_ARGV_ONLY) != 0; if (__libc_argc > 1 && !argvOnly) { // create an B_ARGV_RECEIVED message BMessage argvMessage(B_ARGV_RECEIVED); fill_argv_message(argvMessage); // replace the first argv string with the path of the // other application BPath path; if (path.SetTo(&otherAppInfo.ref) == B_OK) argvMessage.ReplaceString("argv", 0, path.Path()); // send the message otherApp.SendMessage(&argvMessage); } else if (!argvOnly) otherApp.SendMessage(B_SILENT_RELAUNCH); } } else if (fInitError == B_OK) { // the registrations was successful // Create a B_ARGV_RECEIVED message and send it to ourselves. // Do that even, if we are B_ARGV_ONLY. // TODO: When BLooper::AddMessage() is done, use that instead of // PostMessage(). DBG(OUT("info: BApplication successfully registered.\n")); if (__libc_argc > 1) { BMessage argvMessage(B_ARGV_RECEIVED); fill_argv_message(argvMessage); PostMessage(&argvMessage, this); } // send a B_READY_TO_RUN message as well PostMessage(B_READY_TO_RUN, this); } else if (fInitError > B_ERRORS_END) { // Registrar internal errors shouldn't fall into the user's hands. fInitError = B_ERROR; } } #else // We need to have ReadyToRun called even when we're not using the registrar PostMessage(B_READY_TO_RUN, this); #endif // ifndef RUN_WITHOUT_REGISTRAR if (fInitError == B_OK) { // TODO: Not completely sure about the order, but this should be close. // init be_app and be_app_messenger be_app = this; be_app_messenger = BMessenger(NULL, this); // set the BHandler's name SetName(ref.name); // create meta MIME BPath path; if (registerApp && path.SetTo(&ref) == B_OK) create_app_meta_mime(path.Path(), false, true, false); #ifndef RUN_WITHOUT_APP_SERVER // app server connection and IK initialization if (initGUI) fInitError = _InitGUIContext(); #endif // RUN_WITHOUT_APP_SERVER } // Return the error or exit, if there was an error and no error variable // has been supplied. if (_error != NULL) { *_error = fInitError; } else if (fInitError != B_OK) { DBG(OUT("BApplication::InitData() failed: %s\n", strerror(fInitError))); exit(0); } DBG(OUT("BApplication::InitData() done\n")); } port_id BApplication::_GetPort(const char* signature) { return BLaunchRoster().GetPort(signature, NULL); } BArchivable* BApplication::Instantiate(BMessage* data) { if (validate_instantiation(data, "BApplication")) return new BApplication(data); return NULL; } status_t BApplication::Archive(BMessage* data, bool deep) const { status_t status = BLooper::Archive(data, deep); if (status < B_OK) return status; app_info info; status = GetAppInfo(&info); if (status < B_OK) return status; status = data->AddString("mime_sig", info.signature); if (status < B_OK) return status; return data->AddInt64("_pulse", fPulseRate); } status_t BApplication::InitCheck() const { return fInitError; } thread_id BApplication::Run() { if (fInitError != B_OK) return fInitError; Loop(); delete fPulseRunner; return fThread; } void BApplication::Quit() { bool unlock = false; if (!IsLocked()) { const char* name = Name(); if (name == NULL) name = "no-name"; printf("ERROR - you must Lock the application object before calling " "Quit(), team=%" B_PRId32 ", looper=%s\n", Team(), name); unlock = true; if (!Lock()) return; } // Delete the object, if not running only. if (!fRunCalled) { delete this; } else if (find_thread(NULL) != fThread) { // ToDo: why shouldn't we set fTerminating to true directly in this case? // We are not the looper thread. // We push a _QUIT_ into the queue. // TODO: When BLooper::AddMessage() is done, use that instead of // PostMessage()??? This would overtake messages that are still at // the port. // NOTE: We must not unlock here -- otherwise we had to re-lock, which // may not work. This is bad, since, if the port is full, it // won't get emptier, as the looper thread needs to lock the object // before dispatching messages. while (PostMessage(_QUIT_, this) == B_WOULD_BLOCK) snooze(10000); } else { // We are the looper thread. // Just set fTerminating to true which makes us fall through the // message dispatching loop and return from Run(). fTerminating = true; } // If we had to lock the object, unlock now. if (unlock) Unlock(); } bool BApplication::QuitRequested() { return _QuitAllWindows(false); } void BApplication::Pulse() { // supposed to be implemented by subclasses } void BApplication::ReadyToRun() { // supposed to be implemented by subclasses } void BApplication::MessageReceived(BMessage* message) { switch (message->what) { case B_COUNT_PROPERTIES: case B_GET_PROPERTY: case B_SET_PROPERTY: { int32 index; BMessage specifier; int32 what; const char* property = NULL; if (message->GetCurrentSpecifier(&index, &specifier, &what, &property) < B_OK || !ScriptReceived(message, index, &specifier, what, property)) { BLooper::MessageReceived(message); } break; } case B_SILENT_RELAUNCH: // Sent to a B_SINGLE_LAUNCH application when it's launched again // (see _InitData()) be_roster->ActivateApp(Team()); break; case kMsgAppServerRestarted: _ReconnectToServer(); break; case kMsgDeleteServerMemoryArea: { int32 serverArea; if (message->FindInt32("server area", &serverArea) == B_OK) { // The link is not used, but we currently borrow its lock BPrivate::AppServerLink link; fServerAllocator->RemoveArea(serverArea); } break; } default: BLooper::MessageReceived(message); } } void BApplication::ArgvReceived(int32 argc, char** argv) { // supposed to be implemented by subclasses } void BApplication::AppActivated(bool active) { // supposed to be implemented by subclasses } void BApplication::RefsReceived(BMessage* message) { // supposed to be implemented by subclasses } void BApplication::AboutRequested() { // supposed to be implemented by subclasses } BHandler* BApplication::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, int32 what, const char* property) { BPropertyInfo propInfo(sPropertyInfo); status_t err = B_OK; uint32 data; if (propInfo.FindMatch(message, 0, specifier, what, property, &data) >= 0) { switch (data) { case kWindowByIndex: { int32 index; err = specifier->FindInt32("index", &index); if (err != B_OK) break; if (what == B_REVERSE_INDEX_SPECIFIER) index = CountWindows() - index; BWindow* window = WindowAt(index); if (window != NULL) { message->PopSpecifier(); BMessenger(window).SendMessage(message); } else err = B_BAD_INDEX; break; } case kWindowByName: { const char* name; err = specifier->FindString("name", &name); if (err != B_OK) break; for (int32 i = 0;; i++) { BWindow* window = WindowAt(i); if (window == NULL) { err = B_NAME_NOT_FOUND; break; } if (window->Title() != NULL && !strcmp(window->Title(), name)) { message->PopSpecifier(); BMessenger(window).SendMessage(message); break; } } break; } case kLooperByIndex: { int32 index; err = specifier->FindInt32("index", &index); if (err != B_OK) break; if (what == B_REVERSE_INDEX_SPECIFIER) index = CountLoopers() - index; BLooper* looper = LooperAt(index); if (looper != NULL) { message->PopSpecifier(); BMessenger(looper).SendMessage(message); } else err = B_BAD_INDEX; break; } case kLooperByID: // TODO: implement getting looper by ID! break; case kLooperByName: { const char* name; err = specifier->FindString("name", &name); if (err != B_OK) break; for (int32 i = 0;; i++) { BLooper* looper = LooperAt(i); if (looper == NULL) { err = B_NAME_NOT_FOUND; break; } if (looper->Name() != NULL && strcmp(looper->Name(), name) == 0) { message->PopSpecifier(); BMessenger(looper).SendMessage(message); break; } } break; } case kApplication: return this; } } else { return BLooper::ResolveSpecifier(message, index, specifier, what, property); } if (err != B_OK) { BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); reply.AddInt32("error", err); reply.AddString("message", strerror(err)); message->SendReply(&reply); } return NULL; } void BApplication::ShowCursor() { BPrivate::AppServerLink link; link.StartMessage(AS_SHOW_CURSOR); link.Flush(); } void BApplication::HideCursor() { BPrivate::AppServerLink link; link.StartMessage(AS_HIDE_CURSOR); link.Flush(); } void BApplication::ObscureCursor() { BPrivate::AppServerLink link; link.StartMessage(AS_OBSCURE_CURSOR); link.Flush(); } bool BApplication::IsCursorHidden() const { BPrivate::AppServerLink link; int32 status = B_ERROR; link.StartMessage(AS_QUERY_CURSOR_HIDDEN); link.FlushWithReply(status); return status == B_OK; } void BApplication::SetCursor(const void* cursorData) { BCursor cursor(cursorData); SetCursor(&cursor, true); // forces the cursor to be sync'ed } void BApplication::SetCursor(const BCursor* cursor, bool sync) { BPrivate::AppServerLink link; link.StartMessage(AS_SET_CURSOR); link.Attach(sync); link.Attach(cursor->fServerToken); if (sync) { int32 code; link.FlushWithReply(code); } else link.Flush(); } int32 BApplication::CountWindows() const { return _CountWindows(false); // we're ignoring menu windows } BWindow* BApplication::WindowAt(int32 index) const { return _WindowAt(index, false); // we're ignoring menu windows } int32 BApplication::CountLoopers() const { AutoLocker ListLock(gLooperList); if (ListLock.IsLocked()) return gLooperList.CountLoopers(); // Some bad, non-specific thing has happened return B_ERROR; } BLooper* BApplication::LooperAt(int32 index) const { BLooper* looper = NULL; AutoLocker listLock(gLooperList); if (listLock.IsLocked()) looper = gLooperList.LooperAt(index); return looper; } status_t BApplication::RegisterLooper(BLooper* looper) { BWindow* window = dynamic_cast(looper); if (window != NULL) return B_BAD_VALUE; if (sOnQuitLooperList.HasItem(looper)) return B_ERROR; if (sOnQuitLooperList.AddItem(looper) != true) return B_ERROR; return B_OK; } status_t BApplication::UnregisterLooper(BLooper* looper) { BWindow* window = dynamic_cast(looper); if (window != NULL) return B_BAD_VALUE; if (!sOnQuitLooperList.HasItem(looper)) return B_ERROR; if (sOnQuitLooperList.RemoveItem(looper) != true) return B_ERROR; return B_OK; } bool BApplication::IsLaunching() const { return !fReadyToRunCalled; } const char* BApplication::Signature() const { return fAppName; } status_t BApplication::GetAppInfo(app_info* info) const { if (be_app == NULL || be_roster == NULL) return B_NO_INIT; return be_roster->GetRunningAppInfo(be_app->Team(), info); } BResources* BApplication::AppResources() { if (sAppResources == NULL) pthread_once(&sAppResourcesInitOnce, &_InitAppResources); return sAppResources; } void BApplication::DispatchMessage(BMessage* message, BHandler* handler) { if (handler != this) { // it's not ours to dispatch BLooper::DispatchMessage(message, handler); return; } switch (message->what) { case B_ARGV_RECEIVED: _ArgvReceived(message); break; case B_REFS_RECEIVED: { // this adds the refs that are part of this message to the recent // lists, but only folders and documents are handled here entry_ref ref; int32 i = 0; while (message->FindRef("refs", i++, &ref) == B_OK) { BEntry entry(&ref, true); if (entry.InitCheck() != B_OK) continue; if (entry.IsDirectory()) BRoster().AddToRecentFolders(&ref); else { // filter out applications, we only want to have documents // in the recent files list BNode node(&entry); BNodeInfo info(&node); char mimeType[B_MIME_TYPE_LENGTH]; if (info.GetType(mimeType) != B_OK || strcasecmp(mimeType, B_APP_MIME_TYPE)) BRoster().AddToRecentDocuments(&ref); } } RefsReceived(message); break; } case B_READY_TO_RUN: if (!fReadyToRunCalled) { ReadyToRun(); fReadyToRunCalled = true; } break; case B_ABOUT_REQUESTED: AboutRequested(); break; case B_PULSE: Pulse(); break; case B_APP_ACTIVATED: { bool active; if (message->FindBool("active", &active) == B_OK) AppActivated(active); break; } case B_COLORS_UPDATED: { AutoLocker listLock(gLooperList); if (!listLock.IsLocked()) break; BWindow* window = NULL; uint32 count = gLooperList.CountLoopers(); for (uint32 index = 0; index < count; ++index) { window = dynamic_cast(gLooperList.LooperAt(index)); if (window == NULL || (window != NULL && window->fOffscreen)) continue; window->PostMessage(message); } break; } case _SHOW_DRAG_HANDLES_: { bool show; if (message->FindBool("show", &show) != B_OK) break; BDragger::Private::UpdateShowAllDraggers(show); break; } // TODO: Handle these as well case _DISPOSE_DRAG_: case _PING_: puts("not yet handled message:"); DBG(message->PrintToStream()); break; default: BLooper::DispatchMessage(message, handler); break; } } void BApplication::SetPulseRate(bigtime_t rate) { if (rate < 0) rate = 0; // BeBook states that we have only 100,000 microseconds granularity rate -= rate % 100000; if (!Lock()) return; if (rate != 0) { // reset existing pulse runner, or create new one if (fPulseRunner == NULL) { BMessage pulse(B_PULSE); fPulseRunner = new BMessageRunner(be_app_messenger, &pulse, rate); } else fPulseRunner->SetInterval(rate); } else { // turn off pulse messages delete fPulseRunner; fPulseRunner = NULL; } fPulseRate = rate; Unlock(); } status_t BApplication::GetSupportedSuites(BMessage* data) { if (data == NULL) return B_BAD_VALUE; status_t status = data->AddString("suites", "suite/vnd.Be-application"); if (status == B_OK) { BPropertyInfo propertyInfo(sPropertyInfo); status = data->AddFlat("messages", &propertyInfo); if (status == B_OK) status = BLooper::GetSupportedSuites(data); } return status; } status_t BApplication::Perform(perform_code d, void* arg) { return BLooper::Perform(d, arg); } void BApplication::_ReservedApplication1() {} void BApplication::_ReservedApplication2() {} void BApplication::_ReservedApplication3() {} void BApplication::_ReservedApplication4() {} void BApplication::_ReservedApplication5() {} void BApplication::_ReservedApplication6() {} void BApplication::_ReservedApplication7() {} void BApplication::_ReservedApplication8() {} bool BApplication::ScriptReceived(BMessage* message, int32 index, BMessage* specifier, int32 what, const char* property) { BMessage reply(B_REPLY); status_t err = B_BAD_SCRIPT_SYNTAX; switch (message->what) { case B_GET_PROPERTY: if (strcmp("Loopers", property) == 0) { int32 count = CountLoopers(); err = B_OK; for (int32 i=0; err == B_OK && iFindInt32("index", &index); if (err != B_OK) break; if (what == B_REVERSE_INDEX_SPECIFIER) index = CountWindows() - index; err = B_BAD_INDEX; BWindow* window = WindowAt(index); if (window == NULL) break; BMessenger messenger(window); err = reply.AddMessenger("result", messenger); break; } case B_NAME_SPECIFIER: { const char* name; err = specifier->FindString("name", &name); if (err != B_OK) break; err = B_NAME_NOT_FOUND; for (int32 i = 0; i < CountWindows(); i++) { BWindow* window = WindowAt(i); if (window && window->Name() != NULL && !strcmp(window->Name(), name)) { BMessenger messenger(window); err = reply.AddMessenger("result", messenger); break; } } break; } } } else if (strcmp("Looper", property) == 0) { switch (what) { case B_INDEX_SPECIFIER: case B_REVERSE_INDEX_SPECIFIER: { int32 index = -1; err = specifier->FindInt32("index", &index); if (err != B_OK) break; if (what == B_REVERSE_INDEX_SPECIFIER) index = CountLoopers() - index; err = B_BAD_INDEX; BLooper* looper = LooperAt(index); if (looper == NULL) break; BMessenger messenger(looper); err = reply.AddMessenger("result", messenger); break; } case B_NAME_SPECIFIER: { const char* name; err = specifier->FindString("name", &name); if (err != B_OK) break; err = B_NAME_NOT_FOUND; for (int32 i = 0; i < CountLoopers(); i++) { BLooper* looper = LooperAt(i); if (looper != NULL && looper->Name() && strcmp(looper->Name(), name) == 0) { BMessenger messenger(looper); err = reply.AddMessenger("result", messenger); break; } } break; } case B_ID_SPECIFIER: { // TODO debug_printf("Looper's ID specifier used but not " "implemented.\n"); break; } } } else if (strcmp("Name", property) == 0) err = reply.AddString("result", Name()); break; case B_COUNT_PROPERTIES: if (strcmp("Looper", property) == 0) err = reply.AddInt32("result", CountLoopers()); else if (strcmp("Window", property) == 0) err = reply.AddInt32("result", CountWindows()); break; } if (err == B_BAD_SCRIPT_SYNTAX) return false; if (err < B_OK) { reply.what = B_MESSAGE_NOT_UNDERSTOOD; reply.AddString("message", strerror(err)); } reply.AddInt32("error", err); message->SendReply(&reply); return true; } void BApplication::BeginRectTracking(BRect rect, bool trackWhole) { BPrivate::AppServerLink link; link.StartMessage(AS_BEGIN_RECT_TRACKING); link.Attach(rect); link.Attach(trackWhole); link.Flush(); } void BApplication::EndRectTracking() { BPrivate::AppServerLink link; link.StartMessage(AS_END_RECT_TRACKING); link.Flush(); } status_t BApplication::_SetupServerAllocator() { fServerAllocator = new (std::nothrow) BPrivate::ServerMemoryAllocator(); if (fServerAllocator == NULL) return B_NO_MEMORY; return fServerAllocator->InitCheck(); } status_t BApplication::_InitGUIContext() { // An app_server connection is necessary for a lot of stuff, so get that first. status_t error = _ConnectToServer(); if (error != B_OK) return error; // Initialize the IK after we have set be_app because of a construction // of a AppServerLink (which depends on be_app) nested inside the call // to get_menu_info. error = _init_interface_kit_(); if (error != B_OK) return error; // create global system cursors B_CURSOR_SYSTEM_DEFAULT = new BCursor(B_HAND_CURSOR); B_CURSOR_I_BEAM = new BCursor(B_I_BEAM_CURSOR); // TODO: would be nice to get the workspace at launch time from the registrar fInitialWorkspace = current_workspace(); return B_OK; } status_t BApplication::_ConnectToServer() { status_t status = create_desktop_connection(fServerLink, "aStartMessage(AS_CREATE_APP); fServerLink->Attach(fServerLink->ReceiverPort()); fServerLink->Attach(_get_looper_port_(this)); fServerLink->Attach(Team()); fServerLink->Attach(_get_object_token_(this)); fServerLink->AttachString(fAppName); area_id sharedReadOnlyArea; team_id serverTeam; port_id serverPort; int32 code; if (fServerLink->FlushWithReply(code) == B_OK && code == B_OK) { // We don't need to contact the main app_server anymore // directly; we now talk to our server alter ego only. fServerLink->Read(&serverPort); fServerLink->Read(&sharedReadOnlyArea); fServerLink->Read(&serverTeam); } else { fServerLink->SetSenderPort(-1); debugger("BApplication: couldn't obtain new app_server comm port"); return B_ERROR; } fServerLink->SetTargetTeam(serverTeam); fServerLink->SetSenderPort(serverPort); status = _SetupServerAllocator(); if (status != B_OK) return status; area_id area; uint8* base; status = fServerAllocator->AddArea(sharedReadOnlyArea, area, base, true); if (status < B_OK) return status; fServerReadOnlyMemory = base; return B_OK; } void BApplication::_ReconnectToServer() { delete_port(fServerLink->SenderPort()); delete_port(fServerLink->ReceiverPort()); if (_ConnectToServer() != B_OK) debugger("Can't reconnect to app server!"); AutoLocker listLock(gLooperList); if (!listLock.IsLocked()) return; uint32 count = gLooperList.CountLoopers(); for (uint32 i = 0; i < count ; i++) { BWindow* window = dynamic_cast(gLooperList.LooperAt(i)); if (window == NULL) continue; BMessenger windowMessenger(window); windowMessenger.SendMessage(kMsgAppServerRestarted); } reconnect_bitmaps_to_app_server(); reconnect_pictures_to_app_server(); } #if 0 void BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset, BRect dragRect, BHandler* replyTo) { // TODO: implement } void BApplication::send_drag(BMessage* message, int32 vs_token, BPoint offset, int32 bitmapToken, drawing_mode dragMode, BHandler* replyTo) { // TODO: implement } void BApplication::write_drag(_BSession_* session, BMessage* message) { // TODO: implement } #endif bool BApplication::_WindowQuitLoop(bool quitFilePanels, bool force) { int32 index = 0; while (true) { BWindow* window = WindowAt(index); if (window == NULL) break; // NOTE: the window pointer might be stale, in case the looper // was already quit by quitting an earlier looper... but fortunately, // we can still call Lock() on the invalid pointer, and it // will return false... if (!window->Lock()) continue; // don't quit file panels if we haven't been asked for it if (!quitFilePanels && window->IsFilePanel()) { window->Unlock(); index++; continue; } if (!force && !window->QuitRequested() && !(quitFilePanels && window->IsFilePanel())) { // the window does not want to quit, so we don't either window->Unlock(); return false; } // Re-lock, just to make sure that the user hasn't done nasty // things in QuitRequested(). Quit() unlocks fully, thus // double-locking is harmless. if (window->Lock()) window->Quit(); index = 0; // we need to continue at the start of the list again - it // might have changed } return true; } bool BApplication::_QuitAllWindows(bool force) { AssertLocked(); // We need to unlock here because BWindow::QuitRequested() must be // allowed to lock the application - which would cause a deadlock Unlock(); bool quit = _WindowQuitLoop(false, force); if (quit) quit = _WindowQuitLoop(true, force); Lock(); return quit; } void BApplication::_ArgvReceived(BMessage* message) { ASSERT(message != NULL); // build the argv vector status_t error = B_OK; int32 argc = 0; char** argv = NULL; if (message->FindInt32("argc", &argc) == B_OK && argc > 0) { // allocate a NULL terminated array argv = new(std::nothrow) char*[argc + 1]; if (argv == NULL) return; // 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] = strdup(arg); if (argv[i] == NULL) error = B_NO_MEMORY; } else argc = i; } argv[argc] = NULL; } // call the hook if (error == B_OK && argc > 0) ArgvReceived(argc, argv); if (error != B_OK) { printf("Error parsing B_ARGV_RECEIVED message. Message:\n"); message->PrintToStream(); } // cleanup if (argv) { for (int32 i = 0; i < argc; i++) free(argv[i]); delete[] argv; } } uint32 BApplication::InitialWorkspace() { return fInitialWorkspace; } int32 BApplication::_CountWindows(bool includeMenus) const { uint32 count = 0; for (int32 i = 0; i < gLooperList.CountLoopers(); i++) { BWindow* window = dynamic_cast(gLooperList.LooperAt(i)); if (window != NULL && !window->fOffscreen && (includeMenus || dynamic_cast(window) == NULL)) { count++; } } return count; } BWindow* BApplication::_WindowAt(uint32 index, bool includeMenus) const { AutoLocker listLock(gLooperList); if (!listLock.IsLocked()) return NULL; uint32 count = gLooperList.CountLoopers(); for (uint32 i = 0; i < count && index < count; i++) { BWindow* window = dynamic_cast(gLooperList.LooperAt(i)); if (window == NULL || (window != NULL && window->fOffscreen) || (!includeMenus && dynamic_cast(window) != NULL)) { index++; continue; } if (i == index) return window; } return NULL; } /*static*/ void BApplication::_InitAppResources() { entry_ref ref; bool found = false; // App is already running. Get its entry ref with // GetAppInfo() app_info appInfo; if (be_app && be_app->GetAppInfo(&appInfo) == B_OK) { ref = appInfo.ref; found = true; } else { // Run() hasn't been called yet found = BPrivate::get_app_ref(&ref) == B_OK; } if (!found) return; BFile file(&ref, B_READ_ONLY); if (file.InitCheck() != B_OK) return; BResources* resources = new (std::nothrow) BResources(&file, false); if (resources == NULL || resources->InitCheck() != B_OK) { delete resources; return; } sAppResources = resources; }