* Added new class TerminalRoster, which maintains a list of all running
terminals, including their window minimized status and workspaces. These information are shared via our special-purpose clipboard. TerminalRoster mainly acts as a cache. * Removed the terminal ID management from TermApp. Most is now done by TerminalRoster, the rest has been moved to TermWindow. * Moved the terminal position file reading/writing from TermApp to TermWindow. * Moved the remaining terminal window title handling from TermApp to TermWindow. * Replaced the solution for #6613 implemented in r39530 (enabling/disabling the "Switch Terminals" menu item depending on whether there are other Terminals). The new solution is more correct, since it does enable the menu item, if and only if switching to another Terminal will happen when triggering it. I.e. minimized Terminals and ones on other workspaces are ignored. Should also fix #6612, since there's no synchronous communication between different terminal apps anymore. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39562 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
441d3509c9
commit
105093fddb
@ -4,7 +4,7 @@ SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||
|
||||
UseHeaders [ FDirName $(HAIKU_TOP) src kits tracker ] ;
|
||||
|
||||
UsePrivateHeaders libroot kernel system ;
|
||||
UsePrivateHeaders libroot kernel shared system ;
|
||||
|
||||
Application Terminal :
|
||||
ActiveProcessInfo.cpp
|
||||
@ -27,6 +27,7 @@ Application Terminal :
|
||||
TermApp.cpp
|
||||
TerminalBuffer.cpp
|
||||
TerminalCharClassifier.cpp
|
||||
TerminalRoster.cpp
|
||||
TermConst.cpp
|
||||
TermParse.cpp
|
||||
TermScrollView.cpp
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2001-2009, Haiku, Inc. All rights reserved.
|
||||
* Copyright 2001-2010, Haiku, Inc. All rights reserved.
|
||||
* Copyright (c) 2003-2004 Kian Duffy <myob@users.sourceforge.net>
|
||||
* Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
|
||||
*
|
||||
@ -19,7 +19,6 @@
|
||||
#include <Catalog.h>
|
||||
#include <Clipboard.h>
|
||||
#include <Catalog.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <InterfaceDefs.h>
|
||||
#include <Locale.h>
|
||||
#include <NodeInfo.h>
|
||||
@ -28,7 +27,6 @@
|
||||
#include <Screen.h>
|
||||
#include <String.h>
|
||||
|
||||
|
||||
#include "Arguments.h"
|
||||
#include "Globals.h"
|
||||
#include "PrefHandler.h"
|
||||
@ -40,9 +38,6 @@
|
||||
static bool sUsageRequested = false;
|
||||
//static bool sGeometryRequested = false;
|
||||
|
||||
const ulong MSG_ACTIVATE_TERM = 'msat';
|
||||
const ulong MSG_TERM_WINDOW_INFO = 'mtwi';
|
||||
|
||||
|
||||
int
|
||||
main()
|
||||
@ -59,29 +54,10 @@ main()
|
||||
TermApp::TermApp()
|
||||
: BApplication(TERM_SIGNATURE),
|
||||
fStartFullscreen(false),
|
||||
fWindowTitleUserDefined(false),
|
||||
fWindowNumber(-1),
|
||||
fTermWindow(NULL),
|
||||
fArgs(NULL)
|
||||
{
|
||||
|
||||
fArgs = new Arguments(0, NULL);
|
||||
|
||||
fWindowTitle = B_TRANSLATE("Terminal");
|
||||
_RegisterTerminal();
|
||||
|
||||
if (fWindowNumber > 0)
|
||||
fWindowTitle << " " << fWindowNumber;
|
||||
|
||||
if (_LoadWindowPosition(&fTermFrame, &fTermWorkspaces) != B_OK) {
|
||||
int i = fWindowNumber / 16;
|
||||
int j = fWindowNumber % 16;
|
||||
int k = (j * 16) + (i * 64) + 50;
|
||||
int l = (j * 16) + 50;
|
||||
|
||||
fTermFrame.Set(k, l, k + 50, k + 50);
|
||||
fTermWorkspaces = B_CURRENT_WORKSPACE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -120,7 +96,7 @@ TermApp::ReadyToRun()
|
||||
// init the mouse copy'n'paste clipboard
|
||||
gMouseClipboard = new BClipboard(MOUSE_CLIPBOARD_NAME, true);
|
||||
|
||||
status_t status = _MakeTermWindow(fTermFrame, fTermWorkspaces);
|
||||
status_t status = _MakeTermWindow();
|
||||
|
||||
// failed spawn, print stdout and open alert panel
|
||||
// TODO: This alert does never show up.
|
||||
@ -161,9 +137,6 @@ TermApp::QuitRequested()
|
||||
void
|
||||
TermApp::Quit()
|
||||
{
|
||||
if (!sUsageRequested)
|
||||
_UnregisterTerminal();
|
||||
|
||||
BApplication::Quit();
|
||||
}
|
||||
|
||||
@ -179,27 +152,10 @@ void
|
||||
TermApp::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case MENU_SWITCH_TERM:
|
||||
_SwitchTerm();
|
||||
break;
|
||||
|
||||
case MSG_ACTIVATE_TERM:
|
||||
fTermWindow->Activate();
|
||||
break;
|
||||
|
||||
case MSG_TERM_WINDOW_INFO:
|
||||
{
|
||||
BMessage reply(B_REPLY);
|
||||
reply.AddBool("minimized", fTermWindow->IsMinimized());
|
||||
reply.AddInt32("workspaces", fTermWindow->Workspaces());
|
||||
message->SendReply(&reply);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_SAVE_WINDOW_POSITION:
|
||||
_SaveWindowPosition(message);
|
||||
break;
|
||||
|
||||
case MSG_CHECK_CHILDREN:
|
||||
_HandleChildCleanup();
|
||||
break;
|
||||
@ -223,10 +179,8 @@ TermApp::ArgvReceived(int32 argc, char **argv)
|
||||
return;
|
||||
}
|
||||
|
||||
if (fArgs->Title() != NULL) {
|
||||
if (fArgs->Title() != NULL)
|
||||
fWindowTitle = fArgs->Title();
|
||||
fWindowTitleUserDefined = true;
|
||||
}
|
||||
|
||||
fStartFullscreen = fArgs->FullScreen();
|
||||
}
|
||||
@ -270,102 +224,10 @@ TermApp::RefsReceived(BMessage* message)
|
||||
|
||||
|
||||
status_t
|
||||
TermApp::_GetWindowPositionFile(BFile* file, uint32 openMode)
|
||||
{
|
||||
BPath path;
|
||||
status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
status = path.Append("Terminal_windows");
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
return file->SetTo(path.Path(), openMode);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TermApp::_LoadWindowPosition(BRect* frame, uint32* workspaces)
|
||||
{
|
||||
status_t status;
|
||||
BMessage position;
|
||||
|
||||
BFile file;
|
||||
status = _GetWindowPositionFile(&file, B_READ_ONLY);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
status = position.Unflatten(&file);
|
||||
|
||||
file.Unset();
|
||||
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
status = position.FindRect("rect", fWindowNumber - 1, frame);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
int32 _workspaces;
|
||||
status = position.FindInt32("workspaces", fWindowNumber - 1, &_workspaces);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
if (modifiers() & B_SHIFT_KEY)
|
||||
*workspaces = _workspaces;
|
||||
else
|
||||
*workspaces = B_CURRENT_WORKSPACE;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TermApp::_SaveWindowPosition(BMessage* position)
|
||||
{
|
||||
BFile file;
|
||||
BMessage originalSettings;
|
||||
|
||||
// We append ourself to the existing settings file
|
||||
// So we have to read it, insert our BMessage, and rewrite it.
|
||||
|
||||
status_t status = _GetWindowPositionFile(&file, B_READ_ONLY);
|
||||
if (status == B_OK) {
|
||||
originalSettings.Unflatten(&file);
|
||||
// No error checking on that : it fails if the settings
|
||||
// file is missing, but we can create it.
|
||||
|
||||
file.Unset();
|
||||
}
|
||||
|
||||
// Append the new settings
|
||||
BRect rect;
|
||||
position->FindRect("rect", &rect);
|
||||
if (originalSettings.ReplaceRect("rect", fWindowNumber - 1, rect) != B_OK)
|
||||
originalSettings.AddRect("rect", rect);
|
||||
|
||||
int32 workspaces;
|
||||
position->FindInt32("workspaces", &workspaces);
|
||||
if (originalSettings.ReplaceInt32("workspaces", fWindowNumber - 1, workspaces)
|
||||
!= B_OK)
|
||||
originalSettings.AddInt32("workspaces", workspaces);
|
||||
|
||||
// Resave the whole thing
|
||||
status = _GetWindowPositionFile (&file,
|
||||
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
return originalSettings.Flatten(&file);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TermApp::_MakeTermWindow(BRect &frame, uint32 workspaces)
|
||||
TermApp::_MakeTermWindow()
|
||||
{
|
||||
try {
|
||||
fTermWindow = new TermWindow(frame, fWindowTitle,
|
||||
fWindowTitleUserDefined, fWindowNumber, workspaces, fArgs);
|
||||
fTermWindow = new TermWindow(fWindowTitle, fArgs);
|
||||
} catch (int error) {
|
||||
return (status_t)error;
|
||||
} catch (...) {
|
||||
@ -378,240 +240,6 @@ TermApp::_MakeTermWindow(BRect &frame, uint32 workspaces)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermApp::_ActivateTermWindow(team_id id)
|
||||
{
|
||||
BMessenger app(TERM_SIGNATURE, id);
|
||||
if (app.IsTargetLocal())
|
||||
fTermWindow->Activate();
|
||||
else
|
||||
app.SendMessage(MSG_ACTIVATE_TERM);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermApp::_SwitchTerm()
|
||||
{
|
||||
team_id myId = be_app->Team();
|
||||
BList teams;
|
||||
be_roster->GetAppList(TERM_SIGNATURE, &teams);
|
||||
|
||||
int32 numTerms = teams.CountItems();
|
||||
if (numTerms <= 1)
|
||||
return;
|
||||
|
||||
// Find our position in the app teams.
|
||||
int32 i;
|
||||
|
||||
for (i = 0; i < numTerms; i++) {
|
||||
if (myId == reinterpret_cast<team_id>(teams.ItemAt(i)))
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
if (--i < 0)
|
||||
i = numTerms - 1;
|
||||
} while (!_IsSwitchTarget(reinterpret_cast<team_id>(teams.ItemAt(i))));
|
||||
|
||||
// Activate switched terminal.
|
||||
_ActivateTermWindow(reinterpret_cast<team_id>(teams.ItemAt(i)));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TermApp::_IsSwitchTarget(team_id id)
|
||||
{
|
||||
uint32 currentWorkspace = 1L << current_workspace();
|
||||
|
||||
BMessenger app(TERM_SIGNATURE, id);
|
||||
if (app.IsTargetLocal()) {
|
||||
return !fTermWindow->IsMinimized()
|
||||
&& (fTermWindow->Workspaces() & currentWorkspace) != 0;
|
||||
}
|
||||
|
||||
BMessage reply;
|
||||
if (app.SendMessage(MSG_TERM_WINDOW_INFO, &reply) != B_OK)
|
||||
return false;
|
||||
|
||||
bool minimized;
|
||||
int32 workspaces;
|
||||
if (reply.FindBool("minimized", &minimized) != B_OK
|
||||
|| reply.FindInt32("workspaces", &workspaces) != B_OK)
|
||||
return false;
|
||||
|
||||
return !minimized && (workspaces & currentWorkspace) != 0;
|
||||
}
|
||||
|
||||
|
||||
/*! Checks if all teams that have an ID-to-team mapping in the message
|
||||
are still running.
|
||||
The IDs for teams that are gone will be made available again, and
|
||||
their mapping is removed from the message.
|
||||
*/
|
||||
void
|
||||
TermApp::_SanitizeIDs(BMessage* data, uint8* windows, ssize_t length)
|
||||
{
|
||||
BList teams;
|
||||
be_roster->GetAppList(TERM_SIGNATURE, &teams);
|
||||
|
||||
for (int32 i = 0; i < length; i++) {
|
||||
if (!windows[i])
|
||||
continue;
|
||||
|
||||
BString id("id-");
|
||||
id << i + 1;
|
||||
|
||||
team_id team;
|
||||
if (data->FindInt32(id.String(), &team) != B_OK)
|
||||
continue;
|
||||
|
||||
if (!teams.HasItem((void*)team)) {
|
||||
windows[i] = false;
|
||||
data->RemoveName(id.String());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Removes the current fWindowNumber (ID) from the supplied array, or
|
||||
finds a free ID in it, and sets fWindowNumber accordingly.
|
||||
*/
|
||||
bool
|
||||
TermApp::_UpdateIDs(bool set, uint8* windows, ssize_t maxLength,
|
||||
ssize_t* _length)
|
||||
{
|
||||
ssize_t length = *_length;
|
||||
|
||||
if (set) {
|
||||
int32 i;
|
||||
for (i = 0; i < length; i++) {
|
||||
if (!windows[i]) {
|
||||
windows[i] = true;
|
||||
fWindowNumber = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == length) {
|
||||
if (length >= maxLength)
|
||||
return false;
|
||||
|
||||
windows[length] = true;
|
||||
length++;
|
||||
fWindowNumber = length;
|
||||
}
|
||||
} else {
|
||||
// update information and write it back
|
||||
windows[fWindowNumber - 1] = false;
|
||||
}
|
||||
|
||||
*_length = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermApp::_UpdateRegistration(bool set)
|
||||
{
|
||||
if (set)
|
||||
fWindowNumber = -1;
|
||||
else if (fWindowNumber < 0)
|
||||
return;
|
||||
|
||||
#ifdef __HAIKU__
|
||||
// use BClipboard - it supports atomic access in Haiku
|
||||
BClipboard clipboard(TERM_SIGNATURE);
|
||||
|
||||
while (true) {
|
||||
if (!clipboard.Lock())
|
||||
return;
|
||||
|
||||
BMessage* data = clipboard.Data();
|
||||
|
||||
const uint8* windowsData;
|
||||
uint8 windows[512];
|
||||
ssize_t length;
|
||||
if (data->FindData("ids", B_RAW_TYPE,
|
||||
(const void**)&windowsData, &length) != B_OK)
|
||||
length = 0;
|
||||
|
||||
if (length > (ssize_t)sizeof(windows))
|
||||
length = sizeof(windows);
|
||||
if (length > 0)
|
||||
memcpy(windows, windowsData, length);
|
||||
|
||||
_SanitizeIDs(data, windows, length);
|
||||
|
||||
status_t status = B_OK;
|
||||
if (_UpdateIDs(set, windows, sizeof(windows), &length)) {
|
||||
// add/remove our ID-to-team mapping
|
||||
BString id("id-");
|
||||
id << fWindowNumber;
|
||||
|
||||
if (set)
|
||||
data->AddInt32(id.String(), Team());
|
||||
else
|
||||
data->RemoveName(id.String());
|
||||
|
||||
data->RemoveName("ids");
|
||||
//if (data->ReplaceData("ids", B_RAW_TYPE, windows, length) != B_OK)
|
||||
data->AddData("ids", B_RAW_TYPE, windows, length);
|
||||
|
||||
status = clipboard.Commit(true);
|
||||
}
|
||||
|
||||
clipboard.Unlock();
|
||||
|
||||
if (status == B_OK)
|
||||
break;
|
||||
}
|
||||
#else // !__HAIKU__
|
||||
// use a file to store the IDs - unfortunately, locking
|
||||
// doesn't work on BeOS either here
|
||||
int fd = open("/tmp/terminal_ids", O_RDWR | O_CREAT);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
struct flock lock;
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_CUR;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = -1;
|
||||
fcntl(fd, F_SETLKW, &lock);
|
||||
|
||||
uint8 windows[512];
|
||||
ssize_t length = read_pos(fd, 0, windows, sizeof(windows));
|
||||
if (length < 0) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length > (ssize_t)sizeof(windows))
|
||||
length = sizeof(windows);
|
||||
|
||||
if (_UpdateIDs(set, windows, sizeof(windows), &length))
|
||||
write_pos(fd, 0, windows, length);
|
||||
|
||||
close(fd);
|
||||
#endif // !__HAIKU__
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermApp::_UnregisterTerminal()
|
||||
{
|
||||
_UpdateRegistration(false);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermApp::_RegisterTerminal()
|
||||
{
|
||||
_UpdateRegistration(true);
|
||||
}
|
||||
|
||||
|
||||
//#ifndef B_NETPOSITIVE_APP_SIGNATURE
|
||||
//#define B_NETPOSITIVE_APP_SIGNATURE "application/x-vnd.Be-NPOS"
|
||||
//#endif
|
||||
|
@ -58,23 +58,7 @@ protected:
|
||||
virtual void ArgvReceived(int32 argc, char** argv);
|
||||
|
||||
private:
|
||||
status_t _MakeTermWindow(BRect& frame,
|
||||
uint32 workspaces);
|
||||
status_t _GetWindowPositionFile(BFile* file,
|
||||
uint32 openMode);
|
||||
status_t _LoadWindowPosition(BRect* frame,
|
||||
uint32* workspaces);
|
||||
status_t _SaveWindowPosition(BMessage* message);
|
||||
void _SwitchTerm();
|
||||
void _ActivateTermWindow(team_id id);
|
||||
bool _IsSwitchTarget(team_id id);
|
||||
void _SanitizeIDs(BMessage* data, uint8* windows,
|
||||
ssize_t length);
|
||||
bool _UpdateIDs(bool set, uint8* windows,
|
||||
ssize_t maxLength, ssize_t* _length);
|
||||
void _UpdateRegistration(bool set);
|
||||
void _UnregisterTerminal();
|
||||
void _RegisterTerminal();
|
||||
status_t _MakeTermWindow();
|
||||
|
||||
void _HandleChildCleanup();
|
||||
static void _SigChildHandler(int signal, void* data);
|
||||
@ -85,12 +69,8 @@ private:
|
||||
private:
|
||||
bool fStartFullscreen;
|
||||
BString fWindowTitle;
|
||||
bool fWindowTitleUserDefined;
|
||||
int32 fWindowNumber;
|
||||
|
||||
BWindow* fTermWindow;
|
||||
BRect fTermFrame;
|
||||
uint32 fTermWorkspaces;
|
||||
Arguments* fArgs;
|
||||
};
|
||||
|
||||
|
@ -93,6 +93,8 @@ static const uint32 MSG_REPORT_MOUSE_EVENT = 'mous';
|
||||
static const uint32 MSG_SAVE_WINDOW_POSITION = 'swps';
|
||||
static const uint32 MSG_MOVE_TAB_LEFT = 'mvtl';
|
||||
static const uint32 MSG_MOVE_TAB_RIGHT = 'mvtr';
|
||||
static const uint32 MSG_ACTIVATE_TERM = 'msat';
|
||||
|
||||
|
||||
// Preference Read/Write Keys
|
||||
static const char* const PREF_HALF_FONT_FAMILY = "Half Font Family";
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <Catalog.h>
|
||||
#include <Clipboard.h>
|
||||
#include <Dragger.h>
|
||||
#include <File.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <LayoutBuilder.h>
|
||||
#include <LayoutUtils.h>
|
||||
#include <Locale.h>
|
||||
@ -34,6 +36,8 @@
|
||||
#include <ScrollView.h>
|
||||
#include <String.h>
|
||||
|
||||
#include <AutoLocker.h>
|
||||
|
||||
#include "ActiveProcessInfo.h"
|
||||
#include "Arguments.h"
|
||||
#include "AppearPrefView.h"
|
||||
@ -64,6 +68,7 @@ static const uint32 kEditTabTitle = 'ETti';
|
||||
static const uint32 kEditWindowTitle = 'EWti';
|
||||
static const uint32 kTabTitleChanged = 'TTch';
|
||||
static const uint32 kWindowTitleChanged = 'WTch';
|
||||
static const uint32 kUpdateSwitchTerminalsMenuItem = 'Ustm';
|
||||
|
||||
|
||||
#undef B_TRANSLATE_CONTEXT
|
||||
@ -150,13 +155,10 @@ struct TermWindow::Session {
|
||||
// #pragma mark - TermWindow
|
||||
|
||||
|
||||
TermWindow::TermWindow(BRect frame, const BString& title,
|
||||
bool isUserDefinedTitle, int32 windowIndex, uint32 workspaces,
|
||||
Arguments* args)
|
||||
TermWindow::TermWindow(const BString& title, Arguments* args)
|
||||
:
|
||||
BWindow(frame, title, B_DOCUMENT_WINDOW,
|
||||
B_CURRENT_WORKSPACE | B_QUIT_ON_WINDOW_CLOSE, workspaces),
|
||||
fWindowIndex(windowIndex),
|
||||
BWindow(BRect(0, 0, 0, 0), title, B_DOCUMENT_WINDOW,
|
||||
B_CURRENT_WORKSPACE | B_QUIT_ON_WINDOW_CLOSE),
|
||||
fTitleUpdateRunner(this, BMessage(kUpdateTitles), 1000000),
|
||||
fNextSessionID(0),
|
||||
fTabView(NULL),
|
||||
@ -178,27 +180,55 @@ TermWindow::TermWindow(BRect frame, const BString& title,
|
||||
fMatchWord(false),
|
||||
fFullScreen(false)
|
||||
{
|
||||
// register this terminal
|
||||
fTerminalRoster.Register(Team(), this);
|
||||
fTerminalRoster.SetListener(this);
|
||||
int32 id = fTerminalRoster.ID();
|
||||
|
||||
// apply the title settings
|
||||
fTitle.title = title;
|
||||
fTitle.pattern = title;
|
||||
fTitle.patternUserDefined = isUserDefinedTitle;
|
||||
if (fTitle.pattern.Length() == 0) {
|
||||
fTitle.pattern = B_TRANSLATE("Terminal");
|
||||
|
||||
if (id >= 0)
|
||||
fTitle.pattern << " " << id + 1;
|
||||
|
||||
fTitle.patternUserDefined = false;
|
||||
} else
|
||||
fTitle.patternUserDefined = true;
|
||||
|
||||
fTitle.title = fTitle.pattern;
|
||||
fTitle.pattern = title;
|
||||
|
||||
_TitleSettingsChanged();
|
||||
|
||||
// get the saved window position and workspaces
|
||||
BRect frame;
|
||||
uint32 workspaces;
|
||||
if (_LoadWindowPosition(&frame, &workspaces) == B_OK) {
|
||||
// apply
|
||||
MoveTo(frame.LeftTop());
|
||||
ResizeTo(frame.Width(), frame.Height());
|
||||
SetWorkspaces(workspaces);
|
||||
} else {
|
||||
// use computed defaults
|
||||
int i = id / 16;
|
||||
int j = id % 16;
|
||||
int k = (j * 16) + (i * 64) + 50;
|
||||
int l = (j * 16) + 50;
|
||||
|
||||
MoveTo(k, l);
|
||||
}
|
||||
|
||||
// init the GUI and add a tab
|
||||
_InitWindow();
|
||||
_AddTab(args);
|
||||
|
||||
// register as an application roster listener -- we want to know when other
|
||||
// terminals are started and quit, so we can update the our
|
||||
// "Switch Terminals" menu item.
|
||||
be_roster->StartWatching(this);
|
||||
|
||||
_UpdateSwitchTerminalsMenuItem();
|
||||
}
|
||||
|
||||
|
||||
TermWindow::~TermWindow()
|
||||
{
|
||||
be_roster->StopWatching(this);
|
||||
fTerminalRoster.Unregister();
|
||||
|
||||
_FinishTitleDialog();
|
||||
|
||||
@ -301,10 +331,7 @@ TermWindow::QuitRequested()
|
||||
if (!_CanClose(-1))
|
||||
return false;
|
||||
|
||||
BMessage position = BMessage(MSG_SAVE_WINDOW_POSITION);
|
||||
position.AddRect("rect", Frame());
|
||||
position.AddInt32("workspaces", Workspaces());
|
||||
be_app->PostMessage(&position);
|
||||
_SaveWindowPosition();
|
||||
|
||||
return BWindow::QuitRequested();
|
||||
}
|
||||
@ -423,6 +450,98 @@ TermWindow::_SetupMenu()
|
||||
;
|
||||
|
||||
AddChild(fMenuBar);
|
||||
|
||||
_UpdateSwitchTerminalsMenuItem();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TermWindow::_GetWindowPositionFile(BFile* file, uint32 openMode)
|
||||
{
|
||||
BPath path;
|
||||
status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
status = path.Append("Terminal_windows");
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
return file->SetTo(path.Path(), openMode);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TermWindow::_LoadWindowPosition(BRect* frame, uint32* workspaces)
|
||||
{
|
||||
status_t status;
|
||||
BMessage position;
|
||||
|
||||
BFile file;
|
||||
status = _GetWindowPositionFile(&file, B_READ_ONLY);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
status = position.Unflatten(&file);
|
||||
|
||||
file.Unset();
|
||||
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
int32 id = fTerminalRoster.ID();
|
||||
status = position.FindRect("rect", id, frame);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
int32 _workspaces;
|
||||
status = position.FindInt32("workspaces", id, &_workspaces);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
if (modifiers() & B_SHIFT_KEY)
|
||||
*workspaces = _workspaces;
|
||||
else
|
||||
*workspaces = B_CURRENT_WORKSPACE;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TermWindow::_SaveWindowPosition()
|
||||
{
|
||||
BFile file;
|
||||
BMessage originalSettings;
|
||||
|
||||
// We append ourself to the existing settings file
|
||||
// So we have to read it, insert our BMessage, and rewrite it.
|
||||
|
||||
status_t status = _GetWindowPositionFile(&file, B_READ_ONLY);
|
||||
if (status == B_OK) {
|
||||
originalSettings.Unflatten(&file);
|
||||
// No error checking on that : it fails if the settings
|
||||
// file is missing, but we can create it.
|
||||
|
||||
file.Unset();
|
||||
}
|
||||
|
||||
// Append the new settings
|
||||
int32 id = fTerminalRoster.ID();
|
||||
BRect rect(Frame());
|
||||
if (originalSettings.ReplaceRect("rect", id, rect) != B_OK)
|
||||
originalSettings.AddRect("rect", rect);
|
||||
|
||||
int32 workspaces = Workspaces();
|
||||
if (originalSettings.ReplaceInt32("workspaces", id, workspaces) != B_OK)
|
||||
originalSettings.AddInt32("workspaces", workspaces);
|
||||
|
||||
// Resave the whole thing
|
||||
status = _GetWindowPositionFile (&file,
|
||||
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
return originalSettings.Flatten(&file);
|
||||
}
|
||||
|
||||
|
||||
@ -472,7 +591,7 @@ TermWindow::MessageReceived(BMessage *message)
|
||||
break;
|
||||
|
||||
case MENU_SWITCH_TERM:
|
||||
be_app->PostMessage(MENU_SWITCH_TERM);
|
||||
_SwitchTerminal();
|
||||
break;
|
||||
|
||||
case MENU_NEW_TERM:
|
||||
@ -809,16 +928,9 @@ TermWindow::MessageReceived(BMessage *message)
|
||||
_OpenSetWindowTitleDialog();
|
||||
break;
|
||||
|
||||
case B_SOME_APP_LAUNCHED:
|
||||
case B_SOME_APP_QUIT:
|
||||
{
|
||||
BString signature;
|
||||
if (message->FindString("be:signature", &signature) == B_OK
|
||||
&& signature == TERM_SIGNATURE) {
|
||||
_UpdateSwitchTerminalsMenuItem();
|
||||
}
|
||||
case kUpdateSwitchTerminalsMenuItem:
|
||||
_UpdateSwitchTerminalsMenuItem();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BWindow::MessageReceived(message);
|
||||
@ -830,7 +942,8 @@ TermWindow::MessageReceived(BMessage *message)
|
||||
void
|
||||
TermWindow::WindowActivated(bool activated)
|
||||
{
|
||||
BWindow::WindowActivated(activated);
|
||||
if (activated)
|
||||
_UpdateSwitchTerminalsMenuItem();
|
||||
}
|
||||
|
||||
|
||||
@ -1164,6 +1277,28 @@ TermWindow::FrameResized(float newWidth, float newHeight)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
|
||||
{
|
||||
fTerminalRoster.SetWindowInfo(IsMinimized(), Workspaces());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermWindow::WorkspaceActivated(int32 workspace, bool state)
|
||||
{
|
||||
fTerminalRoster.SetWindowInfo(IsMinimized(), Workspaces());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermWindow::Minimize(bool minimize)
|
||||
{
|
||||
BWindow::Minimize(minimize);
|
||||
fTerminalRoster.SetWindowInfo(IsMinimized(), Workspaces());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermWindow::TabSelected(SmartTabView* tabView, int32 index)
|
||||
{
|
||||
@ -1290,6 +1425,13 @@ TermWindow::SetTitleDialogDone(SetTitleDialog* dialog)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermWindow::TerminalInfosUpdated(TerminalRoster* roster)
|
||||
{
|
||||
PostMessage(kUpdateSwitchTerminalsMenuItem);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermWindow::PreviousTermView(TermView* view)
|
||||
{
|
||||
@ -1372,12 +1514,7 @@ TermWindow::_MakeWindowSizeMenu()
|
||||
void
|
||||
TermWindow::_UpdateSwitchTerminalsMenuItem()
|
||||
{
|
||||
// get the running Terminal teams
|
||||
BList teams;
|
||||
be_roster->GetAppList(TERM_SIGNATURE, &teams);
|
||||
|
||||
// update the menu item
|
||||
fSwitchTerminalsMenuItem->SetEnabled(teams.CountItems() > 1);
|
||||
fSwitchTerminalsMenuItem->SetEnabled(_FindSwitchTerminalTarget() >= 0);
|
||||
}
|
||||
|
||||
|
||||
@ -1435,8 +1572,8 @@ TermWindow::_UpdateSessionTitle(int32 index)
|
||||
return;
|
||||
|
||||
// evaluate the window title pattern
|
||||
WindowTitlePlaceholderMapper windowMapper(activeProcessInfo, fWindowIndex,
|
||||
sessionTitle);
|
||||
WindowTitlePlaceholderMapper windowMapper(activeProcessInfo,
|
||||
fTerminalRoster.ID() + 1, sessionTitle);
|
||||
const BString& windowTitle = PatternEvaluator::Evaluate(fTitle.pattern,
|
||||
windowMapper);
|
||||
|
||||
@ -1530,6 +1667,60 @@ TermWindow::_FinishTitleDialog()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TermWindow::_SwitchTerminal()
|
||||
{
|
||||
team_id teamID = _FindSwitchTerminalTarget();
|
||||
if (teamID < 0)
|
||||
return;
|
||||
|
||||
BMessenger app(TERM_SIGNATURE, teamID);
|
||||
app.SendMessage(MSG_ACTIVATE_TERM);
|
||||
}
|
||||
|
||||
|
||||
team_id
|
||||
TermWindow::_FindSwitchTerminalTarget()
|
||||
{
|
||||
AutoLocker<TerminalRoster> rosterLocker(fTerminalRoster);
|
||||
|
||||
team_id myTeamID = Team();
|
||||
|
||||
int32 numTerms = fTerminalRoster.CountTerminals();
|
||||
if (numTerms <= 1)
|
||||
return -1;
|
||||
|
||||
// Find our position in the Terminal teams.
|
||||
int32 i;
|
||||
|
||||
for (i = 0; i < numTerms; i++) {
|
||||
if (myTeamID == fTerminalRoster.TerminalAt(i)->team)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == numTerms) {
|
||||
// we didn't find ourselves -- that shouldn't happen
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32 currentWorkspace = 1L << current_workspace();
|
||||
|
||||
while (true) {
|
||||
if (--i < 0)
|
||||
i = numTerms - 1;
|
||||
|
||||
const TerminalRoster::Info* info = fTerminalRoster.TerminalAt(i);
|
||||
if (info->team == myTeamID) {
|
||||
// That's ourselves again. We've run through the complete list.
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!info->minimized && (info->workspaces & currentWorkspace) != 0)
|
||||
return info->team;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TermWindow::SessionID
|
||||
TermWindow::_NewSessionID()
|
||||
{
|
||||
|
@ -38,10 +38,12 @@
|
||||
|
||||
#include "SmartTabView.h"
|
||||
#include "SetTitleDialog.h"
|
||||
#include "TerminalRoster.h"
|
||||
#include "TermView.h"
|
||||
|
||||
|
||||
class Arguments;
|
||||
class BFile;
|
||||
class BFont;
|
||||
class BMenu;
|
||||
class BMenuBar;
|
||||
@ -51,11 +53,11 @@ class TermViewContainerView;
|
||||
|
||||
|
||||
class TermWindow : public BWindow, private SmartTabView::Listener,
|
||||
private TermView::Listener, private SetTitleDialog::Listener {
|
||||
private TermView::Listener, private SetTitleDialog::Listener,
|
||||
private TerminalRoster::Listener {
|
||||
public:
|
||||
TermWindow(BRect frame, const BString& title,
|
||||
bool isUserDefinedTitle, int32 windowIndex,
|
||||
uint32 workspaces, Arguments* args);
|
||||
TermWindow(const BString& title,
|
||||
Arguments* args);
|
||||
virtual ~TermWindow();
|
||||
|
||||
void SessionChanged();
|
||||
@ -63,10 +65,15 @@ public:
|
||||
protected:
|
||||
virtual bool QuitRequested();
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
virtual void WindowActivated(bool);
|
||||
virtual void WindowActivated(bool activated);
|
||||
virtual void MenusBeginning();
|
||||
virtual void Zoom(BPoint leftTop, float width, float height);
|
||||
virtual void FrameResized(float newWidth, float newHeight);
|
||||
virtual void WorkspacesChanged(uint32 oldWorkspaces,
|
||||
uint32 newWorkspaces);
|
||||
virtual void WorkspaceActivated(int32 workspace,
|
||||
bool state);
|
||||
virtual void Minimize(bool minimize);
|
||||
|
||||
private:
|
||||
// SmartTabView::Listener
|
||||
@ -92,6 +99,9 @@ private:
|
||||
bool titleUserDefined);
|
||||
virtual void SetTitleDialogDone(SetTitleDialog* dialog);
|
||||
|
||||
// TerminalRoster::Listener
|
||||
virtual void TerminalInfosUpdated(TerminalRoster* roster);
|
||||
|
||||
private:
|
||||
struct Title {
|
||||
BString title;
|
||||
@ -120,6 +130,7 @@ private:
|
||||
|
||||
struct Session;
|
||||
|
||||
private:
|
||||
void _SetTermColors(TermViewContainerView* termView);
|
||||
void _InitWindow();
|
||||
void _SetupMenu();
|
||||
@ -127,6 +138,12 @@ private:
|
||||
static BMenu* _MakeWindowSizeMenu();
|
||||
void _UpdateSwitchTerminalsMenuItem();
|
||||
|
||||
status_t _GetWindowPositionFile(BFile* file,
|
||||
uint32 openMode);
|
||||
status_t _LoadWindowPosition(BRect* frame,
|
||||
uint32* workspaces);
|
||||
status_t _SaveWindowPosition();
|
||||
|
||||
void _GetPreferredFont(BFont &font);
|
||||
status_t _DoPageSetup();
|
||||
void _DoPrint();
|
||||
@ -160,13 +177,17 @@ private:
|
||||
void _OpenSetWindowTitleDialog();
|
||||
void _FinishTitleDialog();
|
||||
|
||||
void _SwitchTerminal();
|
||||
team_id _FindSwitchTerminalTarget();
|
||||
|
||||
SessionID _NewSessionID();
|
||||
int32 _NewSessionIndex();
|
||||
|
||||
private:
|
||||
TerminalRoster fTerminalRoster;
|
||||
|
||||
Title fTitle;
|
||||
BString fSessionTitlePattern;
|
||||
int32 fWindowIndex;
|
||||
BMessageRunner fTitleUpdateRunner;
|
||||
|
||||
BList fSessions;
|
||||
|
520
src/apps/terminal/TerminalRoster.cpp
Normal file
520
src/apps/terminal/TerminalRoster.cpp
Normal file
@ -0,0 +1,520 @@
|
||||
/*
|
||||
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#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;
|
||||
|
||||
|
||||
// #pragma mark - Info
|
||||
|
||||
|
||||
/*! Creates an Info with the given \a id and \a team ID.
|
||||
\c workspaces is set to 0 and \c minimized to \c true.
|
||||
*/
|
||||
TerminalRoster::Info::Info(int32 id, team_id team)
|
||||
:
|
||||
id(id),
|
||||
team(team),
|
||||
workspaces(0),
|
||||
minimized(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*! Create an Info and initializes its data from \a archive.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*! Writes the Info's data into fields of \a archive.
|
||||
The const BMessage& constructor can restore an identical Info from it.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*! Compares two Infos.
|
||||
Infos are considered equal, iff all data members are.
|
||||
*/
|
||||
bool
|
||||
TerminalRoster::Info::operator==(const Info& other) const
|
||||
{
|
||||
return id == other.id && team == other.team
|
||||
&& workspaces == other.workspaces && minimized == other.minimized;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - TerminalRoster
|
||||
|
||||
|
||||
/*! Creates a TerminalRoster.
|
||||
Most methods cannot be used until Register() has been invoked.
|
||||
*/
|
||||
TerminalRoster::TerminalRoster()
|
||||
:
|
||||
BHandler("terminal roster"),
|
||||
fLock("terminal roster"),
|
||||
fClipboard(TERM_SIGNATURE),
|
||||
fInfos(10, true),
|
||||
fOurInfo(NULL),
|
||||
fLastCheckedTime(0),
|
||||
fListener(NULL),
|
||||
fInfosUpdated(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*! Locks the object.
|
||||
Also makes sure the roster list is reasonably up-to-date.
|
||||
*/
|
||||
bool
|
||||
TerminalRoster::Lock()
|
||||
{
|
||||
// lock
|
||||
bool locked = fLock.Lock();
|
||||
if (!locked)
|
||||
return false;
|
||||
|
||||
// make sure we're registered
|
||||
if (fOurInfo == NULL) {
|
||||
fLock.Unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the check interval has passed, make sure all infos still have running
|
||||
// teams.
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*! Unlocks the object.
|
||||
As a side effect the listener will be notified, if the terminal list has
|
||||
changed in any way.
|
||||
*/
|
||||
void
|
||||
TerminalRoster::Unlock()
|
||||
{
|
||||
if (fOurInfo != NULL && fInfosUpdated) {
|
||||
// the infos have changed -- notify our listener
|
||||
_NotifyListener();
|
||||
}
|
||||
|
||||
fLock.Unlock();
|
||||
}
|
||||
|
||||
|
||||
/*! Registers a terminal with the roster and establishes a link.
|
||||
|
||||
The object attaches itself to the supplied \a looper and will receive
|
||||
updates via messaging (obviously the looper must run (not necessarily
|
||||
right now) for this to work).
|
||||
|
||||
\param teamID The team ID of this team.
|
||||
\param looper A looper the object can attach itself to.
|
||||
\return \c B_OK, if successful, another error code otherwise.
|
||||
*/
|
||||
status_t
|
||||
TerminalRoster::Register(team_id teamID, BLooper* looper)
|
||||
{
|
||||
AutoLocker<BLocker> locker(fLock);
|
||||
|
||||
if (fOurInfo != NULL) {
|
||||
// already registered
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// lock the clipboard
|
||||
AutoLocker<BClipboard> clipboardLocker(fClipboard);
|
||||
if (!clipboardLocker.IsLocked())
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// get the current infos from the clipboard
|
||||
status_t error = _UpdateInfos(true);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// find an unused ID
|
||||
int32 id = 0;
|
||||
for (int32 i = 0; const Info* info = TerminalAt(i); i++) {
|
||||
if (info->id > id)
|
||||
break;
|
||||
id++;
|
||||
}
|
||||
|
||||
// create our own info
|
||||
fOurInfo = new(std::nothrow) Info(id, teamID);
|
||||
if (fOurInfo == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
// insert it
|
||||
if (!fInfos.BinaryInsert(fOurInfo, &_CompareInfos)) {
|
||||
delete fOurInfo;
|
||||
fOurInfo = NULL;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
// update the clipboard
|
||||
error = _UpdateClipboard();
|
||||
if (error != B_OK) {
|
||||
fInfos.MakeEmpty(true);
|
||||
fOurInfo = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
// add ourselves to the looper and start watching
|
||||
looper->AddHandler(this);
|
||||
|
||||
be_roster->StartWatching(this, B_REQUEST_QUIT);
|
||||
fClipboard.StartWatching(this);
|
||||
|
||||
// Update again in case we've missed a update message sent before we were
|
||||
// listening.
|
||||
_UpdateInfos(false);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Unregisters the terminal from the roster and closes the link.
|
||||
|
||||
Basically undoes all effects of Register().
|
||||
*/
|
||||
void
|
||||
TerminalRoster::Unregister()
|
||||
{
|
||||
AutoLocker<BLocker> locker(fLock);
|
||||
if (!locker.IsLocked())
|
||||
return;
|
||||
|
||||
// stop watching and remove ourselves from the looper
|
||||
be_roster->StartWatching(this);
|
||||
fClipboard.StartWatching(this);
|
||||
|
||||
Looper()->RemoveHandler(this);
|
||||
|
||||
// lock the clipboard and get the current infos
|
||||
AutoLocker<BClipboard> clipboardLocker(fClipboard);
|
||||
if (!clipboardLocker.IsLocked() || _UpdateInfos(false) != B_OK)
|
||||
return;
|
||||
|
||||
// remove our info and update the clipboard
|
||||
fInfos.RemoveItem(fOurInfo);
|
||||
delete fOurInfo;
|
||||
fOurInfo = NULL;
|
||||
|
||||
_UpdateClipboard();
|
||||
}
|
||||
|
||||
|
||||
/*! Returns the ID assigned to this terminal when it was registered.
|
||||
*/
|
||||
int32
|
||||
TerminalRoster::ID() const
|
||||
{
|
||||
return fOurInfo != NULL ? fOurInfo->id : -1;
|
||||
}
|
||||
|
||||
|
||||
/*! Updates this terminal's window status.
|
||||
All other running terminals will be notified, if the status changed.
|
||||
|
||||
\param minimized \c true, if the window is minimized.
|
||||
\param workspaces The window's workspaces mask.
|
||||
*/
|
||||
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;
|
||||
|
||||
// lock the clipboard and get the current infos
|
||||
AutoLocker<BClipboard> clipboardLocker(fClipboard);
|
||||
if (!clipboardLocker.IsLocked() || _UpdateInfos(false) != B_OK)
|
||||
return;
|
||||
|
||||
// update the clipboard to make our change known to the others
|
||||
_UpdateClipboard();
|
||||
}
|
||||
|
||||
|
||||
/*! Overriden to handle update messages.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
// fall through
|
||||
}
|
||||
|
||||
case B_CLIPBOARD_CHANGED:
|
||||
{
|
||||
// lock ourselves and the clipboard and update the infos
|
||||
AutoLocker<TerminalRoster> locker(this);
|
||||
AutoLocker<BClipboard> clipboardLocker(fClipboard);
|
||||
if (clipboardLocker.IsLocked()) {
|
||||
_UpdateInfos(false);
|
||||
|
||||
if (fInfosUpdated)
|
||||
_NotifyListener();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BHandler::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! Updates the terminal info list from the clipboard.
|
||||
|
||||
\param checkApps If \c true, it is checked for each found info whether the
|
||||
respective team is still running. If not, the info is removed from the
|
||||
list (though not from the clipboard).
|
||||
\return \c B_OK, if the update went fine, another error code otherwise. When
|
||||
an error occurs the object state will still be consistent, but might no
|
||||
longer be up-to-date.
|
||||
*/
|
||||
status_t
|
||||
TerminalRoster::_UpdateInfos(bool checkApps)
|
||||
{
|
||||
BMessage* data = fClipboard.Data();
|
||||
|
||||
// find out how many infos we can expect
|
||||
type_code type;
|
||||
int32 count;
|
||||
status_t error = data->GetInfo("teams", &type, &count);
|
||||
if (error != B_OK)
|
||||
count = 0;
|
||||
|
||||
// create an info list from the message
|
||||
InfoList infos(10, true);
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
// get the team's message
|
||||
BMessage teamData;
|
||||
error = data->FindMessage("teams", i, &teamData);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// create the info
|
||||
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))) {
|
||||
// invalid/duplicate info -- skip
|
||||
delete info;
|
||||
fInfosUpdated = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// add it to the list
|
||||
if (!infos.BinaryInsert(info, &_CompareInfos)) {
|
||||
delete info;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// update the current info list from the infos we just read
|
||||
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)) {
|
||||
// new info is not in old list -- transfer it
|
||||
if (!fInfos.AddItem(newInfo, oldIndex++))
|
||||
return B_NO_MEMORY;
|
||||
infos.RemoveItemAt(newIndex);
|
||||
fInfosUpdated = true;
|
||||
} else if (newInfo == NULL || oldInfo->id < newInfo->id) {
|
||||
// old info is not in new list -- delete it, unless it's our own
|
||||
if (oldInfo == fOurInfo) {
|
||||
oldIndex++;
|
||||
} else {
|
||||
delete fInfos.RemoveItemAt(oldIndex);
|
||||
fInfosUpdated = true;
|
||||
}
|
||||
} else {
|
||||
// info is in both lists -- update the old info, unless it's our own
|
||||
if (oldInfo != fOurInfo) {
|
||||
if (*oldInfo != *newInfo) {
|
||||
*oldInfo = *newInfo;
|
||||
fInfosUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
oldIndex++;
|
||||
newIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkApps)
|
||||
fLastCheckedTime = system_time();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Updates the clipboard with the object's terminal info list.
|
||||
|
||||
\return \c B_OK, if the update went fine, another error code otherwise. When
|
||||
an error occurs the object state will still be consistent, but might no
|
||||
longer be in sync with the clipboard.
|
||||
*/
|
||||
status_t
|
||||
TerminalRoster::_UpdateClipboard()
|
||||
{
|
||||
// get the clipboard data message
|
||||
BMessage* data = fClipboard.Data();
|
||||
if (data == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// clear the message and add all infos
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// commit the changes
|
||||
status_t error = fClipboard.Commit();
|
||||
if (error != B_OK) {
|
||||
fClipboard.Revert();
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Notifies the listener, if something has changed.
|
||||
*/
|
||||
void
|
||||
TerminalRoster::_NotifyListener()
|
||||
{
|
||||
if (!fInfosUpdated)
|
||||
return;
|
||||
|
||||
if (fListener != NULL)
|
||||
fListener->TerminalInfosUpdated(this);
|
||||
|
||||
fInfosUpdated = false;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ int
|
||||
TerminalRoster::_CompareInfos(const Info* a, const Info* b)
|
||||
{
|
||||
return a->id - b->id;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ int
|
||||
TerminalRoster::_CompareIDInfo(const int32* id, const Info* info)
|
||||
{
|
||||
return *id - info->id;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TerminalRoster::_TeamIsRunning(team_id teamID)
|
||||
{
|
||||
// we are running for sure
|
||||
if (fOurInfo != NULL && fOurInfo->team == teamID)
|
||||
return true;
|
||||
|
||||
team_info info;
|
||||
return get_team_info(teamID, &info) == B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Listener
|
||||
|
||||
|
||||
TerminalRoster::Listener::~Listener()
|
||||
{
|
||||
}
|
113
src/apps/terminal/TerminalRoster.h
Normal file
113
src/apps/terminal/TerminalRoster.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef TERMINAL_ROSTER_H
|
||||
#define TERMINAL_ROSTER_H
|
||||
|
||||
|
||||
#include <Clipboard.h>
|
||||
#include <Handler.h>
|
||||
#include <Locker.h>
|
||||
|
||||
#include <ObjectList.h>
|
||||
|
||||
|
||||
/*! Provides access to the (virtual) global terminal roster.
|
||||
|
||||
Register() registers our terminal with the roster and establishes the link
|
||||
to the roster. From then on CountTerminals() and TerminalAt() provide
|
||||
access to the terminal list and allows to post our window status via
|
||||
SetWindowInfo(). The list is automatically kept up to date. Unregister()
|
||||
will remove our terminal from the roster and terminate the connection.
|
||||
|
||||
A single Listener can be set. It is notified whenever the terminal list
|
||||
has changed.
|
||||
*/
|
||||
class TerminalRoster : private BHandler {
|
||||
public:
|
||||
class Listener;
|
||||
|
||||
public:
|
||||
struct Info {
|
||||
int32 id;
|
||||
team_id team;
|
||||
uint32 workspaces;
|
||||
bool minimized;
|
||||
|
||||
public:
|
||||
Info(int32 id, team_id team);
|
||||
Info(const BMessage& archive);
|
||||
|
||||
status_t Archive(BMessage& archive) const;
|
||||
|
||||
bool operator==(const Info& other) const;
|
||||
bool operator!=(const Info& other) const
|
||||
{ return !(*this == other); }
|
||||
};
|
||||
|
||||
public:
|
||||
TerminalRoster();
|
||||
|
||||
bool Lock();
|
||||
void Unlock();
|
||||
|
||||
status_t Register(team_id teamID, BLooper* looper);
|
||||
void Unregister();
|
||||
bool IsRegistered() const
|
||||
{ return fOurInfo != NULL; }
|
||||
|
||||
int32 ID() const;
|
||||
|
||||
void SetWindowInfo(bool minimized,
|
||||
uint32 workspaces);
|
||||
|
||||
// roster must be locked
|
||||
int32 CountTerminals() const
|
||||
{ return fInfos.CountItems(); }
|
||||
const Info* TerminalAt(int32 index) const
|
||||
{ return fInfos.ItemAt(index); }
|
||||
|
||||
void SetListener(Listener* listener)
|
||||
{ fListener = listener; }
|
||||
|
||||
private:
|
||||
// BHandler
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
private:
|
||||
typedef BObjectList<Info> InfoList;
|
||||
|
||||
private:
|
||||
status_t _UpdateInfos(bool checkApps);
|
||||
status_t _UpdateClipboard();
|
||||
|
||||
void _NotifyListener();
|
||||
|
||||
static int _CompareInfos(const Info* a, const Info* b);
|
||||
static int _CompareIDInfo(const int32* id,
|
||||
const Info* info);
|
||||
|
||||
bool _TeamIsRunning(team_id teamID);
|
||||
|
||||
private:
|
||||
BLocker fLock;
|
||||
BClipboard fClipboard;
|
||||
InfoList fInfos;
|
||||
Info* fOurInfo;
|
||||
bigtime_t fLastCheckedTime;
|
||||
Listener* fListener;
|
||||
bool fInfosUpdated;
|
||||
};
|
||||
|
||||
|
||||
class TerminalRoster::Listener {
|
||||
public:
|
||||
virtual ~Listener();
|
||||
|
||||
virtual void TerminalInfosUpdated(TerminalRoster* roster)
|
||||
= 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // TERMINAL_ROSTER_H
|
Loading…
Reference in New Issue
Block a user