1708 lines
36 KiB
C++
1708 lines
36 KiB
C++
/*
|
|
* 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 <Application.h>
|
|
|
|
#include <new>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <unistd.h>
|
|
|
|
#include <Alert.h>
|
|
#include <AppFileInfo.h>
|
|
#include <Cursor.h>
|
|
#include <Debug.h>
|
|
#include <Entry.h>
|
|
#include <File.h>
|
|
#include <Locker.h>
|
|
#include <MessageRunner.h>
|
|
#include <ObjectList.h>
|
|
#include <Path.h>
|
|
#include <PropertyInfo.h>
|
|
#include <RegistrarDefs.h>
|
|
#include <Resources.h>
|
|
#include <Roster.h>
|
|
#include <Window.h>
|
|
|
|
#include <AppMisc.h>
|
|
#include <AppServerLink.h>
|
|
#include <AutoLocker.h>
|
|
#include <BitmapPrivate.h>
|
|
#include <DraggerPrivate.h>
|
|
#include <LaunchDaemonDefs.h>
|
|
#include <LaunchRoster.h>
|
|
#include <LooperList.h>
|
|
#include <MenuWindow.h>
|
|
#include <PicturePrivate.h>
|
|
#include <PortLink.h>
|
|
#include <RosterPrivate.h>
|
|
#include <ServerMemoryAllocator.h>
|
|
#include <ServerProtocol.h>
|
|
|
|
|
|
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<BLooper> 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<bool>(sync);
|
|
link.Attach<int32>(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<BLooperList> 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<BLooperList> listLock(gLooperList);
|
|
if (listLock.IsLocked())
|
|
looper = gLooperList.LooperAt(index);
|
|
|
|
return looper;
|
|
}
|
|
|
|
|
|
status_t
|
|
BApplication::RegisterLooper(BLooper* looper)
|
|
{
|
|
BWindow* window = dynamic_cast<BWindow*>(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<BWindow*>(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<BLooperList> listLock(gLooperList);
|
|
if (!listLock.IsLocked())
|
|
break;
|
|
|
|
BWindow* window = NULL;
|
|
uint32 count = gLooperList.CountLoopers();
|
|
for (uint32 index = 0; index < count; ++index) {
|
|
window = dynamic_cast<BWindow*>(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 && i<count; i++) {
|
|
BMessenger messenger(LooperAt(i));
|
|
err = reply.AddMessenger("result", messenger);
|
|
}
|
|
} else if (strcmp("Windows", property) == 0) {
|
|
int32 count = CountWindows();
|
|
err = B_OK;
|
|
for (int32 i=0; err == B_OK && i<count; i++) {
|
|
BMessenger messenger(WindowAt(i));
|
|
err = reply.AddMessenger("result", messenger);
|
|
}
|
|
} else if (strcmp("Window", 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 = 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<BRect>(rect);
|
|
link.Attach<int32>(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, "a<app_server", 100);
|
|
if (status != B_OK)
|
|
return status;
|
|
|
|
// AS_CREATE_APP:
|
|
//
|
|
// Attach data:
|
|
// 1) port_id - receiver port of a regular app
|
|
// 2) port_id - looper port for this BApplication
|
|
// 3) team_id - team identification field
|
|
// 4) int32 - handler ID token of the app
|
|
// 5) char* - signature of the regular app
|
|
|
|
fServerLink->StartMessage(AS_CREATE_APP);
|
|
fServerLink->Attach<port_id>(fServerLink->ReceiverPort());
|
|
fServerLink->Attach<port_id>(_get_looper_port_(this));
|
|
fServerLink->Attach<team_id>(Team());
|
|
fServerLink->Attach<int32>(_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<port_id>(&serverPort);
|
|
fServerLink->Read<area_id>(&sharedReadOnlyArea);
|
|
fServerLink->Read<team_id>(&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<BLooperList> listLock(gLooperList);
|
|
if (!listLock.IsLocked())
|
|
return;
|
|
|
|
uint32 count = gLooperList.CountLoopers();
|
|
for (uint32 i = 0; i < count ; i++) {
|
|
BWindow* window = dynamic_cast<BWindow*>(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<BWindow*>(gLooperList.LooperAt(i));
|
|
if (window != NULL && !window->fOffscreen && (includeMenus
|
|
|| dynamic_cast<BMenuWindow*>(window) == NULL)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
BWindow*
|
|
BApplication::_WindowAt(uint32 index, bool includeMenus) const
|
|
{
|
|
AutoLocker<BLooperList> listLock(gLooperList);
|
|
if (!listLock.IsLocked())
|
|
return NULL;
|
|
|
|
uint32 count = gLooperList.CountLoopers();
|
|
for (uint32 i = 0; i < count && index < count; i++) {
|
|
BWindow* window = dynamic_cast<BWindow*>(gLooperList.LooperAt(i));
|
|
if (window == NULL || (window != NULL && window->fOffscreen)
|
|
|| (!includeMenus && dynamic_cast<BMenuWindow*>(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;
|
|
}
|