all teams in serveral server areas, and instead of having to eventually clone them all several times in BBitmap, we now have one or more areas per team, and BBitmap will only clone areas once if needed. As a side effect, this method should be magnitudes faster than the previous version. * This method is also much more secure: instead of putting the allocation maintenance structures into those everyone-read-write areas, they are now separated, so that faulty applications cannot crash the app_server this way anymore. This should fix bug #172. * Freeing memory is not yet implemented though! (although all memory will be freed upon app exit) * There are now 3 different bitmap allocation strategies: per ClientMemoryAllocator (ie. via ServerApp), per area (for overlays, not yet implemented), and using malloc()/free() for server-only bitmaps. * ServerBitmap now deletes its buffers itself. * Cleaned up BBitmap and BApplication a bit. * The test environment currently doesn't build anymore, will fix it next. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16826 a95241bf-73f2-0310-859d-f6bbb57e9c96
1340 lines
29 KiB
C++
1340 lines
29 KiB
C++
/*
|
|
* Copyright 2001-2006, Haiku.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Erik Jaesler ([email protected])
|
|
*/
|
|
|
|
|
|
#include <new>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <Alert.h>
|
|
#include <AppFileInfo.h>
|
|
#include <Application.h>
|
|
#include <AppMisc.h>
|
|
#include <MessageRunner.h>
|
|
#include <Cursor.h>
|
|
#include <Debug.h>
|
|
#include <Entry.h>
|
|
#include <File.h>
|
|
#include <Locker.h>
|
|
#include <Path.h>
|
|
#include <PropertyInfo.h>
|
|
#include <RegistrarDefs.h>
|
|
#include <Resources.h>
|
|
#include <Roster.h>
|
|
#include <RosterPrivate.h>
|
|
#include <Window.h>
|
|
|
|
#include <AppServerLink.h>
|
|
#include <LooperList.h>
|
|
#include <MenuWindow.h>
|
|
#include <ObjectLocker.h>
|
|
#include <PortLink.h>
|
|
#include <ServerMemoryAllocator.h>
|
|
#include <ServerProtocol.h>
|
|
|
|
using namespace BPrivate;
|
|
|
|
// Globals ---------------------------------------------------------------------
|
|
BApplication *be_app = NULL;
|
|
BMessenger be_app_messenger;
|
|
|
|
BResources *BApplication::sAppResources = NULL;
|
|
BLocker BApplication::sAppResourcesLock("_app_resources_lock");
|
|
|
|
|
|
static property_info sPropertyInfo[] = {
|
|
{
|
|
"Window",
|
|
{},
|
|
{B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
|
|
NULL, 0,
|
|
{},
|
|
{},
|
|
{}
|
|
},
|
|
{
|
|
"Window",
|
|
{},
|
|
{B_NAME_SPECIFIER},
|
|
NULL, 1,
|
|
{},
|
|
{},
|
|
{}
|
|
},
|
|
{
|
|
"Looper",
|
|
{},
|
|
{B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
|
|
NULL, 2,
|
|
{},
|
|
{},
|
|
{}
|
|
},
|
|
{
|
|
"Looper",
|
|
{},
|
|
{B_ID_SPECIFIER},
|
|
NULL, 3,
|
|
{},
|
|
{},
|
|
{}
|
|
},
|
|
{
|
|
"Looper",
|
|
{},
|
|
{B_NAME_SPECIFIER},
|
|
NULL, 4,
|
|
{},
|
|
{},
|
|
{}
|
|
},
|
|
{
|
|
"Name",
|
|
{B_GET_PROPERTY},
|
|
{B_DIRECT_SPECIFIER},
|
|
NULL, 5,
|
|
{B_STRING_TYPE},
|
|
{},
|
|
{}
|
|
},
|
|
{
|
|
"Window",
|
|
{B_COUNT_PROPERTIES},
|
|
{B_DIRECT_SPECIFIER},
|
|
NULL, 5,
|
|
{B_INT32_TYPE},
|
|
{},
|
|
{}
|
|
},
|
|
{
|
|
"Loopers",
|
|
{B_GET_PROPERTY},
|
|
{B_DIRECT_SPECIFIER},
|
|
NULL, 5,
|
|
{B_MESSENGER_TYPE},
|
|
{},
|
|
{}
|
|
},
|
|
{
|
|
"Windows",
|
|
{B_GET_PROPERTY},
|
|
{B_DIRECT_SPECIFIER},
|
|
NULL, 5,
|
|
{B_MESSENGER_TYPE},
|
|
{},
|
|
{}
|
|
},
|
|
{
|
|
"Looper",
|
|
{B_COUNT_PROPERTIES},
|
|
{B_DIRECT_SPECIFIER},
|
|
NULL, 5,
|
|
{B_INT32_TYPE},
|
|
{},
|
|
{}
|
|
},
|
|
{}
|
|
};
|
|
|
|
// argc/argv
|
|
extern const int __libc_argc;
|
|
extern const char * const *__libc_argv;
|
|
|
|
|
|
// debugging
|
|
//#define DBG(x) x
|
|
#define DBG(x)
|
|
#define OUT printf
|
|
|
|
|
|
// prototypes of helper functions
|
|
static const char* looper_name_for(const char *signature);
|
|
static status_t check_app_signature(const char *signature);
|
|
static void fill_argv_message(BMessage &message);
|
|
|
|
|
|
BApplication::BApplication(const char *signature)
|
|
: BLooper(looper_name_for(signature))
|
|
{
|
|
_InitData(signature, true, NULL);
|
|
}
|
|
|
|
|
|
BApplication::BApplication(const char *signature, status_t *_error)
|
|
: BLooper(looper_name_for(signature))
|
|
{
|
|
_InitData(signature, true, _error);
|
|
}
|
|
|
|
|
|
BApplication::BApplication(const char *signature, bool initGUI,
|
|
status_t *_error)
|
|
: BLooper(looper_name_for(signature))
|
|
{
|
|
_InitData(signature, initGUI, _error);
|
|
}
|
|
|
|
|
|
BApplication::BApplication(BMessage *data)
|
|
// Note: BeOS calls the private BLooper(int32, port_id, const char *)
|
|
// constructor here, test if it's needed
|
|
: BLooper(looper_name_for(NULL))
|
|
{
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
BApplication::BApplication(uint32 signature)
|
|
{
|
|
}
|
|
|
|
|
|
BApplication::BApplication(const BApplication &rhs)
|
|
{
|
|
}
|
|
|
|
|
|
BApplication::~BApplication()
|
|
{
|
|
Lock();
|
|
|
|
// tell all loopers(usually windows) to quit. Also, wait for them.
|
|
_QuitAllWindows(true);
|
|
|
|
// unregister from the roster
|
|
BRoster::Private().RemoveApp(Team());
|
|
|
|
#ifndef RUN_WITHOUT_APP_SERVER
|
|
// tell app_server we're quitting...
|
|
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
|
|
|
|
// uninitialize be_app, the be_app_messenger is invalidated automatically
|
|
be_app = NULL;
|
|
}
|
|
|
|
|
|
BApplication &
|
|
BApplication::operator=(const BApplication &rhs)
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
|
|
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)
|
|
debugger("2 BApplication objects were created. Only one is allowed.");
|
|
|
|
fServerLink = new BPrivate::PortLink(-1, -1);
|
|
fServerAllocator = 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 isRegistrar = signature
|
|
&& !strcasecmp(signature, kRegistrarSignature);
|
|
// 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 && !isRegistrar
|
|
&& !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 && !isRegistrar) {
|
|
preRegistered = BRoster::Private().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;
|
|
// check the signature and correct it, if necessary
|
|
if (strcasecmp(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;
|
|
// the registrar must not register
|
|
if (!isRegistrar) {
|
|
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 && __libc_argc > 1) {
|
|
app_info otherAppInfo;
|
|
if (be_roster->GetRunningAppInfo(otherTeam, &otherAppInfo) == B_OK
|
|
&& !(otherAppInfo.flags & B_ARGV_ONLY)) {
|
|
// 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
|
|
BMessenger(NULL, otherTeam).SendMessage(&argvMessage);
|
|
}
|
|
}
|
|
} 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 sucessfully 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
|
|
|
|
// TODO: Not completely sure about the order, but this should be close.
|
|
|
|
// init be_app and be_app_messenger
|
|
if (fInitError == B_OK) {
|
|
be_app = this;
|
|
be_app_messenger = BMessenger(NULL, this);
|
|
}
|
|
|
|
// set the BHandler's name
|
|
if (fInitError == B_OK)
|
|
SetName(ref.name);
|
|
|
|
// create meta MIME
|
|
if (fInitError == B_OK) {
|
|
BPath path;
|
|
if (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 (fInitError == B_OK && 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) {
|
|
*_error = fInitError;
|
|
} else if (fInitError != B_OK) {
|
|
DBG(OUT("BApplication::InitData() failed: %s\n", strerror(fInitError)));
|
|
exit(0);
|
|
}
|
|
DBG(OUT("BApplication::InitData() done\n"));
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
AssertLocked();
|
|
|
|
if (fRunCalled)
|
|
debugger("BApplication::Run was already called. Can only be called once.");
|
|
|
|
// Note: We need a local variable too (for the return value), since
|
|
// fTaskID is cleared by Quit().
|
|
// ToDo: actually, it's not clobbered there?!
|
|
thread_id thread = fTaskID = find_thread(NULL);
|
|
|
|
fRunCalled = true;
|
|
|
|
task_looper();
|
|
|
|
delete fPulseRunner;
|
|
return thread;
|
|
}
|
|
|
|
|
|
void
|
|
BApplication::Quit()
|
|
{
|
|
bool unlock = false;
|
|
if (!IsLocked()) {
|
|
const char *name = Name();
|
|
if (!name)
|
|
name = "no-name";
|
|
printf("ERROR - you must Lock the application object before calling "
|
|
"Quit(), team=%ld, 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) != fTaskID) {
|
|
// 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;
|
|
}
|
|
|
|
// Bebook says: B_SILENT_RELAUNCH
|
|
// Sent to a single-launch application when it's activated by being launched
|
|
// (for example, if the user double-clicks its icon in Tracker).
|
|
case B_SILENT_RELAUNCH:
|
|
be_roster->ActivateApp(Team());
|
|
// supposed to fall through
|
|
default:
|
|
BLooper::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
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()
|
|
{
|
|
thread_info info;
|
|
if (get_thread_info(Thread(), &info) == B_OK) {
|
|
BAlert *alert = new BAlert("_about_", info.name, "OK");
|
|
alert->Go(NULL);
|
|
}
|
|
}
|
|
|
|
|
|
BHandler *
|
|
BApplication::ResolveSpecifier(BMessage *msg, int32 index,
|
|
BMessage *specifier, int32 form, const char *property)
|
|
{
|
|
// ToDo: implement ResolveSpecifier()!
|
|
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 code = SERVER_FALSE;
|
|
link.StartMessage(AS_QUERY_CURSOR_HIDDEN);
|
|
link.FlushWithReply(code);
|
|
|
|
return code == SERVER_TRUE;
|
|
}
|
|
|
|
|
|
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
|
|
{
|
|
BObjectLocker<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;
|
|
BObjectLocker<BLooperList> listLock(gLooperList);
|
|
if (listLock.IsLocked())
|
|
looper = gLooperList.LooperAt(index);
|
|
|
|
return looper;
|
|
}
|
|
|
|
|
|
bool
|
|
BApplication::IsLaunching() const
|
|
{
|
|
return !fReadyToRunCalled;
|
|
}
|
|
|
|
|
|
status_t
|
|
BApplication::GetAppInfo(app_info *info) const
|
|
{
|
|
return be_roster->GetRunningAppInfo(be_app->Team(), info);
|
|
}
|
|
|
|
|
|
BResources *
|
|
BApplication::AppResources()
|
|
{
|
|
BObjectLocker<BLocker> lock(sAppResourcesLock);
|
|
|
|
// BApplication caches its resources, so check
|
|
// if it already happened.
|
|
if (sAppResources != NULL)
|
|
return sAppResources;
|
|
|
|
entry_ref ref;
|
|
bool found = false;
|
|
|
|
// App is already running. Get its entry ref with
|
|
// GetRunningAppInfo()
|
|
app_info appInfo;
|
|
if (be_app && be_roster->GetRunningAppInfo(be_app->Team(), &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) {
|
|
BFile file(&ref, B_READ_ONLY);
|
|
if (file.InitCheck() == B_OK) {
|
|
BResources *resources = new BResources();
|
|
if (resources->SetTo(&file, false) < B_OK)
|
|
delete resources;
|
|
else
|
|
sAppResources = resources;
|
|
}
|
|
}
|
|
|
|
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 = false;
|
|
message->FindBool("active", &active);
|
|
AppActivated(active);
|
|
break;
|
|
}
|
|
|
|
case _SHOW_DRAG_HANDLES_:
|
|
{
|
|
bool visible = false;
|
|
message->FindBool("visible", &visible);
|
|
// TODO: Call the registrar or whoever is responsible for this
|
|
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)
|
|
return B_BAD_VALUE;
|
|
|
|
status_t status = data->AddString("Suites", "suite/vnd.Be-application");
|
|
if (status == B_OK) {
|
|
BPropertyInfo PropertyInfo(sPropertyInfo);
|
|
status = data->AddFlat("message", &PropertyInfo);
|
|
if (status == B_OK)
|
|
status = BHandler::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)
|
|
{
|
|
// TODO: Implement
|
|
printf("message:\n");
|
|
message->PrintToStream();
|
|
printf("index: %ld\n", index);
|
|
printf("specifier:\n");
|
|
specifier->PrintToStream();
|
|
printf("what: %ld\n", what);
|
|
printf("property: %s\n", property ? property : "");
|
|
return false;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
error = _SetupServerAllocator();
|
|
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()
|
|
{
|
|
port_id serverPort = find_port(SERVER_PORT_NAME);
|
|
if (serverPort < B_OK)
|
|
return serverPort;
|
|
|
|
// Create the port so that the app_server knows where to send messages
|
|
port_id clientPort = create_port(100, "a<app_server");
|
|
if (clientPort < B_OK)
|
|
return clientPort;
|
|
|
|
// We can't use AppServerLink because be_app == NULL
|
|
fServerLink->SetTo(serverPort, clientPort);
|
|
|
|
fServerLink->StartMessage(AS_GET_DESKTOP);
|
|
fServerLink->Attach<port_id>(clientPort);
|
|
fServerLink->Attach<int32>(getuid());
|
|
|
|
int32 code;
|
|
if (fServerLink->FlushWithReply(code) != B_OK || code != B_OK) {
|
|
fServerLink->SetSenderPort(-1);
|
|
return B_ERROR;
|
|
}
|
|
|
|
// we talk to the desktop to create our application
|
|
fServerLink->Read<port_id>(&serverPort);
|
|
fServerLink->SetSenderPort(serverPort);
|
|
|
|
// 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>(clientPort);
|
|
fServerLink->Attach<port_id>(_get_looper_port_(this));
|
|
fServerLink->Attach<team_id>(Team());
|
|
fServerLink->Attach<int32>(_get_object_token_(this));
|
|
fServerLink->AttachString(fAppName);
|
|
|
|
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->SetSenderPort(serverPort);
|
|
} else {
|
|
fServerLink->SetSenderPort(-1);
|
|
debugger("BApplication: couldn't obtain new app_server comm port");
|
|
return B_ERROR;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
#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)
|
|
{
|
|
BList looperList;
|
|
{
|
|
BObjectLocker<BLooperList> listLock(gLooperList);
|
|
if (listLock.IsLocked()) {
|
|
gLooperList.GetLooperList(&looperList);
|
|
|
|
// Filter the list: We replace the BLooper pointers by BWindow
|
|
// pointers (dynamic_cast<>() on unlocked loopers is only safe as
|
|
// long as the looper list is locked!). Furthermore we filter out
|
|
// windows, that have not been run yet -- those belong to their
|
|
// creator yet (which also has a lock) and we must not try to
|
|
// delete them.
|
|
int32 count = looperList.CountItems();
|
|
for (int32 i = 0; i < count; i++) {
|
|
BWindow *window
|
|
= dynamic_cast<BWindow*>((BLooper*)looperList.ItemAt(i));
|
|
if (window && window->Thread() < 0)
|
|
window = NULL;
|
|
looperList.ReplaceItem(i, window);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int32 i = looperList.CountItems(); i-- > 0; ) {
|
|
BWindow *window = (BWindow*)looperList.ItemAt(i);
|
|
|
|
// don't quit file panels if we haven't been asked for it
|
|
if (window == NULL || (!quitFilePanels && window->IsFilePanel()))
|
|
continue;
|
|
|
|
if (window->Lock()) {
|
|
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();
|
|
}
|
|
}
|
|
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 char*[argc + 1];
|
|
argv[argc] = 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] = strdup(arg);
|
|
if (argv[i] == NULL)
|
|
error = B_NO_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
// call the hook
|
|
if (error == B_OK && argc > 0)
|
|
ArgvReceived(argc, argv);
|
|
|
|
// 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
|
|
{
|
|
int32 count = 0;
|
|
BList windowList;
|
|
if (_GetWindowList(&windowList, includeMenus) == B_OK)
|
|
count = windowList.CountItems();
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
BWindow *
|
|
BApplication::_WindowAt(uint32 index, bool includeMenus) const
|
|
{
|
|
BList windowList;
|
|
BWindow *window = NULL;
|
|
if (_GetWindowList(&windowList, includeMenus) == B_OK) {
|
|
if ((int32)index < windowList.CountItems())
|
|
window = static_cast<BWindow *>(windowList.ItemAt(index));
|
|
}
|
|
|
|
return window;
|
|
}
|
|
|
|
|
|
status_t
|
|
BApplication::_GetWindowList(BList *list, bool includeMenus) const
|
|
{
|
|
ASSERT(list);
|
|
|
|
// Windows are BLoopers, so we can just check each BLooper to see if it's
|
|
// a BWindow (or BMenuWindow)
|
|
BObjectLocker<BLooperList> listLock(gLooperList);
|
|
if (!listLock.IsLocked())
|
|
return B_ERROR;
|
|
|
|
BLooper *looper = NULL;
|
|
for (int32 i = 0; i < gLooperList.CountLoopers(); i++) {
|
|
looper = gLooperList.LooperAt(i);
|
|
if (dynamic_cast<BWindow *>(looper)) {
|
|
if (includeMenus || dynamic_cast<BMenuWindow *>(looper) == NULL)
|
|
list->AddItem(looper);
|
|
}
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
/*!
|
|
\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
|
|
- \c B_OK: \a signature is a valid app signature.
|
|
- \c 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);
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Returns the looper name for a given signature.
|
|
|
|
Normally this is "AppLooperPort", but in case of the registrar a
|
|
special name.
|
|
|
|
\return The looper name.
|
|
*/
|
|
static const char *
|
|
looper_name_for(const char *signature)
|
|
{
|
|
if (signature && !strcasecmp(signature, kRegistrarSignature))
|
|
return BPrivate::get_roster_port_name();
|
|
return "AppLooperPort";
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief 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++)
|
|
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);
|
|
}
|
|
|