#include "TerminalRoster.h"
#include <stdio.h>
#include <new>
#include <Looper.h>
#include <Roster.h>
#include <String.h>
#include <AutoLocker.h>
#include "TermConst.h"
static const bigtime_t kAppsRunningCheckInterval = 1000000;
TerminalRoster::Info::Info(int32 id, team_id team)
:
id(id),
team(team),
workspaces(0),
minimized(true)
{
}
TerminalRoster::Info::Info(const BMessage& archive)
{
if (archive.FindInt32("id", &id) != B_OK)
id = -1;
if (archive.FindInt32("team", &team) != B_OK)
team = -1;
if (archive.FindUInt32("workspaces", &workspaces) != B_OK)
workspaces = 0;
if (archive.FindBool("minimized", &minimized) != B_OK)
minimized = true;
}
status_t
TerminalRoster::Info::Archive(BMessage& archive) const
{
status_t error;
if ((error = archive.AddInt32("id", id)) != B_OK
|| (error = archive.AddInt32("team", team)) != B_OK
|| (error = archive.AddUInt32("workspaces", workspaces)) != B_OK
|| (error = archive.AddBool("minimized", minimized)) != B_OK) {
return error;
}
return B_OK;
}
bool
TerminalRoster::Info::operator==(const Info& other) const
{
return id == other.id && team == other.team
&& workspaces == other.workspaces && minimized == other.minimized;
}
TerminalRoster::TerminalRoster()
:
BHandler("terminal roster"),
fLock("terminal roster"),
fClipboard(TERM_SIGNATURE),
fInfos(10),
fOurInfo(NULL),
fLastCheckedTime(0),
fListener(NULL),
fInfosUpdated(false)
{
}
bool
TerminalRoster::Lock()
{
bool locked = fLock.Lock();
if (!locked)
return false;
if (fOurInfo == NULL) {
fLock.Unlock();
return false;
}
bigtime_t now = system_time();
if (fLastCheckedTime + kAppsRunningCheckInterval) {
bool needsUpdate = false;
for (int32 i = 0; const Info* info = TerminalAt(i); i++) {
if (!_TeamIsRunning(info->team)) {
needsUpdate = true;
break;
}
}
if (needsUpdate) {
AutoLocker<BClipboard> clipboardLocker(fClipboard);
if (clipboardLocker.IsLocked()) {
if (_UpdateInfos(true) == B_OK)
_UpdateClipboard();
}
} else
fLastCheckedTime = now;
}
return true;
}
void
TerminalRoster::Unlock()
{
if (fOurInfo != NULL && fInfosUpdated) {
_NotifyListener();
}
fLock.Unlock();
}
status_t
TerminalRoster::Register(team_id teamID, BLooper* looper)
{
AutoLocker<BLocker> locker(fLock);
if (fOurInfo != NULL) {
return B_BAD_VALUE;
}
AutoLocker<BClipboard> clipboardLocker(fClipboard);
if (!clipboardLocker.IsLocked())
return B_BAD_VALUE;
status_t error = _UpdateInfos(true);
if (error != B_OK)
return error;
int32 id = 0;
for (int32 i = 0; const Info* info = TerminalAt(i); i++) {
if (info->id > id)
break;
id++;
}
fOurInfo = new(std::nothrow) Info(id, teamID);
if (fOurInfo == NULL)
return B_NO_MEMORY;
if (!fInfos.BinaryInsert(fOurInfo, &_CompareInfos)) {
delete fOurInfo;
fOurInfo = NULL;
return B_NO_MEMORY;
}
error = _UpdateClipboard();
if (error != B_OK) {
fInfos.MakeEmpty(true);
fOurInfo = NULL;
return error;
}
looper->AddHandler(this);
be_roster->StartWatching(this, B_REQUEST_QUIT);
fClipboard.StartWatching(this);
_UpdateInfos(false);
return B_OK;
}
void
TerminalRoster::Unregister()
{
AutoLocker<BLocker> locker(fLock);
if (!locker.IsLocked())
return;
be_roster->StartWatching(this);
fClipboard.StartWatching(this);
Looper()->RemoveHandler(this);
AutoLocker<BClipboard> clipboardLocker(fClipboard);
if (!clipboardLocker.IsLocked() || _UpdateInfos(false) != B_OK)
return;
fInfos.RemoveItem(fOurInfo);
fOurInfo = NULL;
_UpdateClipboard();
}
int32
TerminalRoster::ID() const
{
return fOurInfo != NULL ? fOurInfo->id : -1;
}
void
TerminalRoster::SetWindowInfo(bool minimized, uint32 workspaces)
{
AutoLocker<TerminalRoster> locker(this);
if (!locker.IsLocked())
return;
if (minimized == fOurInfo->minimized && workspaces == fOurInfo->workspaces)
return;
fOurInfo->minimized = minimized;
fOurInfo->workspaces = workspaces;
fInfosUpdated = true;
AutoLocker<BClipboard> clipboardLocker(fClipboard);
if (!clipboardLocker.IsLocked() || _UpdateInfos(false) != B_OK)
return;
_UpdateClipboard();
}
void
TerminalRoster::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_SOME_APP_QUIT:
{
BString signature;
if (message->FindString("be:signature", &signature) != B_OK
|| signature != TERM_SIGNATURE) {
break;
}
}
case B_CLIPBOARD_CHANGED:
{
AutoLocker<TerminalRoster> locker(this);
AutoLocker<BClipboard> clipboardLocker(fClipboard);
if (clipboardLocker.IsLocked()) {
_UpdateInfos(false);
if (fInfosUpdated)
_NotifyListener();
}
break;
}
default:
BHandler::MessageReceived(message);
break;
}
}
status_t
TerminalRoster::_UpdateInfos(bool checkApps)
{
BMessage* data = fClipboard.Data();
type_code type;
int32 count;
status_t error = data->GetInfo("teams", &type, &count);
if (error != B_OK)
count = 0;
InfoList infos(10);
for (int32 i = 0; i < count; i++) {
BMessage teamData;
error = data->FindMessage("teams", i, &teamData);
if (error != B_OK)
return error;
Info* info = new(std::nothrow) Info(teamData);
if (info == NULL)
return B_NO_MEMORY;
if (info->id < 0 || info->team < 0
|| infos.BinarySearchByKey(info->id, &_CompareIDInfo) != NULL
|| (checkApps && !_TeamIsRunning(info->team))) {
delete info;
fInfosUpdated = true;
continue;
}
if (!infos.BinaryInsert(info, &_CompareInfos)) {
delete info;
return B_NO_MEMORY;
}
}
int32 oldIndex = 0;
int32 newIndex = 0;
while (oldIndex < fInfos.CountItems() || newIndex < infos.CountItems()) {
Info* oldInfo = fInfos.ItemAt(oldIndex);
Info* newInfo = infos.ItemAt(newIndex);
if (oldInfo == NULL || (newInfo != NULL && oldInfo->id > newInfo->id)) {
if (!fInfos.AddItem(newInfo, oldIndex++))
return B_NO_MEMORY;
infos.RemoveItemAt(newIndex);
fInfosUpdated = true;
} else if (newInfo == NULL || oldInfo->id < newInfo->id) {
if (oldInfo == fOurInfo) {
oldIndex++;
} else {
delete fInfos.RemoveItemAt(oldIndex);
fInfosUpdated = true;
}
} else {
if (oldInfo != fOurInfo) {
if (*oldInfo != *newInfo) {
*oldInfo = *newInfo;
fInfosUpdated = true;
}
}
oldIndex++;
newIndex++;
}
}
if (checkApps)
fLastCheckedTime = system_time();
return B_OK;
}
status_t
TerminalRoster::_UpdateClipboard()
{
BMessage* data = fClipboard.Data();
if (data == NULL)
return B_BAD_VALUE;
data->MakeEmpty();
for (int32 i = 0; const Info* info = TerminalAt(i); i++) {
BMessage teamData;
status_t error = info->Archive(teamData);
if (error != B_OK
|| (error = data->AddMessage("teams", &teamData)) != B_OK) {
fClipboard.Revert();
return error;
}
}
status_t error = fClipboard.Commit();
if (error != B_OK) {
fClipboard.Revert();
return error;
}
return B_OK;
}
void
TerminalRoster::_NotifyListener()
{
if (!fInfosUpdated)
return;
if (fListener != NULL)
fListener->TerminalInfosUpdated(this);
fInfosUpdated = false;
}
int
TerminalRoster::_CompareInfos(const Info* a, const Info* b)
{
return a->id - b->id;
}
int
TerminalRoster::_CompareIDInfo(const int32* id, const Info* info)
{
return *id - info->id;
}
bool
TerminalRoster::_TeamIsRunning(team_id teamID)
{
if (fOurInfo != NULL && fOurInfo->team == teamID)
return true;
team_info info;
return get_team_info(teamID, &info) == B_OK;
}
TerminalRoster::Listener::~Listener()
{
}