xref: /haiku/src/apps/terminal/TerminalRoster.cpp (revision 1c9368988e35e0d0945c64632632a14868dd9f1a)
1*105093fdSIngo Weinhold /*
2*105093fdSIngo Weinhold  * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3*105093fdSIngo Weinhold  * Distributed under the terms of the MIT License.
4*105093fdSIngo Weinhold  */
5*105093fdSIngo Weinhold 
6*105093fdSIngo Weinhold 
7*105093fdSIngo Weinhold #include "TerminalRoster.h"
8*105093fdSIngo Weinhold 
9*105093fdSIngo Weinhold #include <stdio.h>
10*105093fdSIngo Weinhold 
11*105093fdSIngo Weinhold #include <new>
12*105093fdSIngo Weinhold 
13*105093fdSIngo Weinhold #include <Looper.h>
14*105093fdSIngo Weinhold #include <Roster.h>
15*105093fdSIngo Weinhold #include <String.h>
16*105093fdSIngo Weinhold 
17*105093fdSIngo Weinhold #include <AutoLocker.h>
18*105093fdSIngo Weinhold 
19*105093fdSIngo Weinhold #include "TermConst.h"
20*105093fdSIngo Weinhold 
21*105093fdSIngo Weinhold 
22*105093fdSIngo Weinhold static const bigtime_t kAppsRunningCheckInterval = 1000000;
23*105093fdSIngo Weinhold 
24*105093fdSIngo Weinhold 
25*105093fdSIngo Weinhold // #pragma mark - Info
26*105093fdSIngo Weinhold 
27*105093fdSIngo Weinhold 
28*105093fdSIngo Weinhold /*!	Creates an Info with the given \a id and \a team ID.
29*105093fdSIngo Weinhold 	\c workspaces is set to 0 and \c minimized to \c true.
30*105093fdSIngo Weinhold */
Info(int32 id,team_id team)31*105093fdSIngo Weinhold TerminalRoster::Info::Info(int32 id, team_id team)
32*105093fdSIngo Weinhold 	:
33*105093fdSIngo Weinhold 	id(id),
34*105093fdSIngo Weinhold 	team(team),
35*105093fdSIngo Weinhold 	workspaces(0),
36*105093fdSIngo Weinhold 	minimized(true)
37*105093fdSIngo Weinhold {
38*105093fdSIngo Weinhold }
39*105093fdSIngo Weinhold 
40*105093fdSIngo Weinhold 
41*105093fdSIngo Weinhold /*!	Create an Info and initializes its data from \a archive.
42*105093fdSIngo Weinhold */
Info(const BMessage & archive)43*105093fdSIngo Weinhold TerminalRoster::Info::Info(const BMessage& archive)
44*105093fdSIngo Weinhold {
45*105093fdSIngo Weinhold 	if (archive.FindInt32("id", &id) != B_OK)
46*105093fdSIngo Weinhold 		id = -1;
47*105093fdSIngo Weinhold 	if (archive.FindInt32("team", &team) != B_OK)
48*105093fdSIngo Weinhold 		team = -1;
49*105093fdSIngo Weinhold 	if (archive.FindUInt32("workspaces", &workspaces) != B_OK)
50*105093fdSIngo Weinhold 		workspaces = 0;
51*105093fdSIngo Weinhold 	if (archive.FindBool("minimized", &minimized) != B_OK)
52*105093fdSIngo Weinhold 		minimized = true;
53*105093fdSIngo Weinhold }
54*105093fdSIngo Weinhold 
55*105093fdSIngo Weinhold 
56*105093fdSIngo Weinhold /*!	Writes the Info's data into fields of \a archive.
57*105093fdSIngo Weinhold 	The const BMessage& constructor can restore an identical Info from it.
58*105093fdSIngo Weinhold */
59*105093fdSIngo Weinhold status_t
Archive(BMessage & archive) const60*105093fdSIngo Weinhold TerminalRoster::Info::Archive(BMessage& archive) const
61*105093fdSIngo Weinhold {
62*105093fdSIngo Weinhold 	status_t error;
63*105093fdSIngo Weinhold 	if ((error = archive.AddInt32("id", id)) != B_OK
64*105093fdSIngo Weinhold 		|| (error = archive.AddInt32("team", team)) != B_OK
65*105093fdSIngo Weinhold 		|| (error = archive.AddUInt32("workspaces", workspaces)) != B_OK
66*105093fdSIngo Weinhold 		|| (error = archive.AddBool("minimized", minimized)) != B_OK) {
67*105093fdSIngo Weinhold 		return error;
68*105093fdSIngo Weinhold 	}
69*105093fdSIngo Weinhold 
70*105093fdSIngo Weinhold 	return B_OK;
71*105093fdSIngo Weinhold }
72*105093fdSIngo Weinhold 
73*105093fdSIngo Weinhold 
74*105093fdSIngo Weinhold /*!	Compares two Infos.
75*105093fdSIngo Weinhold 	Infos are considered equal, iff all data members are.
76*105093fdSIngo Weinhold */
77*105093fdSIngo Weinhold bool
operator ==(const Info & other) const78*105093fdSIngo Weinhold TerminalRoster::Info::operator==(const Info& other) const
79*105093fdSIngo Weinhold {
80*105093fdSIngo Weinhold 	return id == other.id && team == other.team
81*105093fdSIngo Weinhold 		&& workspaces == other.workspaces && minimized == other.minimized;
82*105093fdSIngo Weinhold }
83*105093fdSIngo Weinhold 
84*105093fdSIngo Weinhold 
85*105093fdSIngo Weinhold // #pragma mark - TerminalRoster
86*105093fdSIngo Weinhold 
87*105093fdSIngo Weinhold 
88*105093fdSIngo Weinhold /*!	Creates a TerminalRoster.
89*105093fdSIngo Weinhold 	Most methods cannot be used until Register() has been invoked.
90*105093fdSIngo Weinhold */
TerminalRoster()91*105093fdSIngo Weinhold TerminalRoster::TerminalRoster()
92*105093fdSIngo Weinhold 	:
93*105093fdSIngo Weinhold 	BHandler("terminal roster"),
94*105093fdSIngo Weinhold 	fLock("terminal roster"),
95*105093fdSIngo Weinhold 	fClipboard(TERM_SIGNATURE),
96*105093fdSIngo Weinhold 	fInfos(10, true),
97*105093fdSIngo Weinhold 	fOurInfo(NULL),
98*105093fdSIngo Weinhold 	fLastCheckedTime(0),
99*105093fdSIngo Weinhold 	fListener(NULL),
100*105093fdSIngo Weinhold 	fInfosUpdated(false)
101*105093fdSIngo Weinhold {
102*105093fdSIngo Weinhold }
103*105093fdSIngo Weinhold 
104*105093fdSIngo Weinhold 
105*105093fdSIngo Weinhold /*!	Locks the object.
106*105093fdSIngo Weinhold 	Also makes sure the roster list is reasonably up-to-date.
107*105093fdSIngo Weinhold */
108*105093fdSIngo Weinhold bool
Lock()109*105093fdSIngo Weinhold TerminalRoster::Lock()
110*105093fdSIngo Weinhold {
111*105093fdSIngo Weinhold 	// lock
112*105093fdSIngo Weinhold 	bool locked = fLock.Lock();
113*105093fdSIngo Weinhold 	if (!locked)
114*105093fdSIngo Weinhold 		return false;
115*105093fdSIngo Weinhold 
116*105093fdSIngo Weinhold 	// make sure we're registered
117*105093fdSIngo Weinhold 	if (fOurInfo == NULL) {
118*105093fdSIngo Weinhold 		fLock.Unlock();
119*105093fdSIngo Weinhold 		return false;
120*105093fdSIngo Weinhold 	}
121*105093fdSIngo Weinhold 
122*105093fdSIngo Weinhold 	// If the check interval has passed, make sure all infos still have running
123*105093fdSIngo Weinhold 	// teams.
124*105093fdSIngo Weinhold 	bigtime_t now = system_time();
125*105093fdSIngo Weinhold 	if (fLastCheckedTime + kAppsRunningCheckInterval) {
126*105093fdSIngo Weinhold 		bool needsUpdate = false;
127*105093fdSIngo Weinhold 		for (int32 i = 0; const Info* info = TerminalAt(i); i++) {
128*105093fdSIngo Weinhold 			if (!_TeamIsRunning(info->team)) {
129*105093fdSIngo Weinhold 				needsUpdate = true;
130*105093fdSIngo Weinhold 				break;
131*105093fdSIngo Weinhold 			}
132*105093fdSIngo Weinhold 		}
133*105093fdSIngo Weinhold 
134*105093fdSIngo Weinhold 		if (needsUpdate) {
135*105093fdSIngo Weinhold 			AutoLocker<BClipboard> clipboardLocker(fClipboard);
136*105093fdSIngo Weinhold 			if (clipboardLocker.IsLocked()) {
137*105093fdSIngo Weinhold 				if (_UpdateInfos(true) == B_OK)
138*105093fdSIngo Weinhold 					_UpdateClipboard();
139*105093fdSIngo Weinhold 			}
140*105093fdSIngo Weinhold 		} else
141*105093fdSIngo Weinhold 			fLastCheckedTime = now;
142*105093fdSIngo Weinhold 	}
143*105093fdSIngo Weinhold 
144*105093fdSIngo Weinhold 	return true;
145*105093fdSIngo Weinhold }
146*105093fdSIngo Weinhold 
147*105093fdSIngo Weinhold 
148*105093fdSIngo Weinhold /*!	Unlocks the object.
149*105093fdSIngo Weinhold 	As a side effect the listener will be notified, if the terminal list has
150*105093fdSIngo Weinhold 	changed in any way.
151*105093fdSIngo Weinhold */
152*105093fdSIngo Weinhold void
Unlock()153*105093fdSIngo Weinhold TerminalRoster::Unlock()
154*105093fdSIngo Weinhold {
155*105093fdSIngo Weinhold 	if (fOurInfo != NULL && fInfosUpdated) {
156*105093fdSIngo Weinhold 		// the infos have changed -- notify our listener
157*105093fdSIngo Weinhold 		_NotifyListener();
158*105093fdSIngo Weinhold 	}
159*105093fdSIngo Weinhold 
160*105093fdSIngo Weinhold 	fLock.Unlock();
161*105093fdSIngo Weinhold }
162*105093fdSIngo Weinhold 
163*105093fdSIngo Weinhold 
164*105093fdSIngo Weinhold /*!	Registers a terminal with the roster and establishes a link.
165*105093fdSIngo Weinhold 
166*105093fdSIngo Weinhold 	The object attaches itself to the supplied \a looper and will receive
167*105093fdSIngo Weinhold 	updates via messaging (obviously the looper must run (not necessarily
168*105093fdSIngo Weinhold 	right now) for this to work).
169*105093fdSIngo Weinhold 
170*105093fdSIngo Weinhold 	\param teamID The team ID of this team.
171*105093fdSIngo Weinhold 	\param looper A looper the object can attach itself to.
172*105093fdSIngo Weinhold 	\return \c B_OK, if successful, another error code otherwise.
173*105093fdSIngo Weinhold */
174*105093fdSIngo Weinhold status_t
Register(team_id teamID,BLooper * looper)175*105093fdSIngo Weinhold TerminalRoster::Register(team_id teamID, BLooper* looper)
176*105093fdSIngo Weinhold {
177*105093fdSIngo Weinhold 	AutoLocker<BLocker> locker(fLock);
178*105093fdSIngo Weinhold 
179*105093fdSIngo Weinhold 	if (fOurInfo != NULL) {
180*105093fdSIngo Weinhold 		// already registered
181*105093fdSIngo Weinhold 		return B_BAD_VALUE;
182*105093fdSIngo Weinhold 	}
183*105093fdSIngo Weinhold 
184*105093fdSIngo Weinhold 	// lock the clipboard
185*105093fdSIngo Weinhold 	AutoLocker<BClipboard> clipboardLocker(fClipboard);
186*105093fdSIngo Weinhold 	if (!clipboardLocker.IsLocked())
187*105093fdSIngo Weinhold 		return B_BAD_VALUE;
188*105093fdSIngo Weinhold 
189*105093fdSIngo Weinhold 	// get the current infos from the clipboard
190*105093fdSIngo Weinhold 	status_t error = _UpdateInfos(true);
191*105093fdSIngo Weinhold 	if (error != B_OK)
192*105093fdSIngo Weinhold 		return error;
193*105093fdSIngo Weinhold 
194*105093fdSIngo Weinhold 	// find an unused ID
195*105093fdSIngo Weinhold 	int32 id = 0;
196*105093fdSIngo Weinhold 	for (int32 i = 0; const Info* info = TerminalAt(i); i++) {
197*105093fdSIngo Weinhold 		if (info->id > id)
198*105093fdSIngo Weinhold 			break;
199*105093fdSIngo Weinhold 		id++;
200*105093fdSIngo Weinhold 	}
201*105093fdSIngo Weinhold 
202*105093fdSIngo Weinhold 	// create our own info
203*105093fdSIngo Weinhold 	fOurInfo = new(std::nothrow) Info(id, teamID);
204*105093fdSIngo Weinhold 	if (fOurInfo == NULL)
205*105093fdSIngo Weinhold 		return B_NO_MEMORY;
206*105093fdSIngo Weinhold 
207*105093fdSIngo Weinhold 	// insert it
208*105093fdSIngo Weinhold 	if (!fInfos.BinaryInsert(fOurInfo, &_CompareInfos)) {
209*105093fdSIngo Weinhold 		delete fOurInfo;
210*105093fdSIngo Weinhold 		fOurInfo = NULL;
211*105093fdSIngo Weinhold 		return B_NO_MEMORY;
212*105093fdSIngo Weinhold 	}
213*105093fdSIngo Weinhold 
214*105093fdSIngo Weinhold 	// update the clipboard
215*105093fdSIngo Weinhold 	error = _UpdateClipboard();
216*105093fdSIngo Weinhold 	if (error != B_OK) {
217*105093fdSIngo Weinhold 		fInfos.MakeEmpty(true);
218*105093fdSIngo Weinhold 		fOurInfo = NULL;
219*105093fdSIngo Weinhold 		return error;
220*105093fdSIngo Weinhold 	}
221*105093fdSIngo Weinhold 
222*105093fdSIngo Weinhold 	// add ourselves to the looper and start watching
223*105093fdSIngo Weinhold 	looper->AddHandler(this);
224*105093fdSIngo Weinhold 
225*105093fdSIngo Weinhold 	be_roster->StartWatching(this, B_REQUEST_QUIT);
226*105093fdSIngo Weinhold 	fClipboard.StartWatching(this);
227*105093fdSIngo Weinhold 
228*105093fdSIngo Weinhold 	// Update again in case we've missed a update message sent before we were
229*105093fdSIngo Weinhold 	// listening.
230*105093fdSIngo Weinhold 	_UpdateInfos(false);
231*105093fdSIngo Weinhold 
232*105093fdSIngo Weinhold 	return B_OK;
233*105093fdSIngo Weinhold }
234*105093fdSIngo Weinhold 
235*105093fdSIngo Weinhold 
236*105093fdSIngo Weinhold /*!	Unregisters the terminal from the roster and closes the link.
237*105093fdSIngo Weinhold 
238*105093fdSIngo Weinhold 	Basically undoes all effects of Register().
239*105093fdSIngo Weinhold */
240*105093fdSIngo Weinhold void
Unregister()241*105093fdSIngo Weinhold TerminalRoster::Unregister()
242*105093fdSIngo Weinhold {
243*105093fdSIngo Weinhold 	AutoLocker<BLocker> locker(fLock);
244*105093fdSIngo Weinhold 	if (!locker.IsLocked())
245*105093fdSIngo Weinhold 		return;
246*105093fdSIngo Weinhold 
247*105093fdSIngo Weinhold 	// stop watching and remove ourselves from the looper
248*105093fdSIngo Weinhold 	be_roster->StartWatching(this);
249*105093fdSIngo Weinhold 	fClipboard.StartWatching(this);
250*105093fdSIngo Weinhold 
251*105093fdSIngo Weinhold 	Looper()->RemoveHandler(this);
252*105093fdSIngo Weinhold 
253*105093fdSIngo Weinhold 	// lock the clipboard and get the current infos
254*105093fdSIngo Weinhold 	AutoLocker<BClipboard> clipboardLocker(fClipboard);
255*105093fdSIngo Weinhold 	if (!clipboardLocker.IsLocked() || _UpdateInfos(false) != B_OK)
256*105093fdSIngo Weinhold 		return;
257*105093fdSIngo Weinhold 
258*105093fdSIngo Weinhold 	// remove our info and update the clipboard
259*105093fdSIngo Weinhold 	fInfos.RemoveItem(fOurInfo);
260*105093fdSIngo Weinhold 	fOurInfo = NULL;
261*105093fdSIngo Weinhold 
262*105093fdSIngo Weinhold 	_UpdateClipboard();
263*105093fdSIngo Weinhold }
264*105093fdSIngo Weinhold 
265*105093fdSIngo Weinhold 
266*105093fdSIngo Weinhold /*!	Returns the ID assigned to this terminal when it was registered.
267*105093fdSIngo Weinhold */
268*105093fdSIngo Weinhold int32
ID() const269*105093fdSIngo Weinhold TerminalRoster::ID() const
270*105093fdSIngo Weinhold {
271*105093fdSIngo Weinhold 	return fOurInfo != NULL ? fOurInfo->id : -1;
272*105093fdSIngo Weinhold }
273*105093fdSIngo Weinhold 
274*105093fdSIngo Weinhold 
275*105093fdSIngo Weinhold /*!	Updates this terminal's window status.
276*105093fdSIngo Weinhold 	All other running terminals will be notified, if the status changed.
277*105093fdSIngo Weinhold 
278*105093fdSIngo Weinhold 	\param minimized \c true, if the window is minimized.
279*105093fdSIngo Weinhold 	\param workspaces The window's workspaces mask.
280*105093fdSIngo Weinhold */
281*105093fdSIngo Weinhold void
SetWindowInfo(bool minimized,uint32 workspaces)282*105093fdSIngo Weinhold TerminalRoster::SetWindowInfo(bool minimized, uint32 workspaces)
283*105093fdSIngo Weinhold {
284*105093fdSIngo Weinhold 	AutoLocker<TerminalRoster> locker(this);
285*105093fdSIngo Weinhold 	if (!locker.IsLocked())
286*105093fdSIngo Weinhold 		return;
287*105093fdSIngo Weinhold 
288*105093fdSIngo Weinhold 	if (minimized == fOurInfo->minimized && workspaces == fOurInfo->workspaces)
289*105093fdSIngo Weinhold 		return;
290*105093fdSIngo Weinhold 
291*105093fdSIngo Weinhold 	fOurInfo->minimized = minimized;
292*105093fdSIngo Weinhold 	fOurInfo->workspaces = workspaces;
293*105093fdSIngo Weinhold 	fInfosUpdated = true;
294*105093fdSIngo Weinhold 
295*105093fdSIngo Weinhold 	// lock the clipboard and get the current infos
296*105093fdSIngo Weinhold 	AutoLocker<BClipboard> clipboardLocker(fClipboard);
297*105093fdSIngo Weinhold 	if (!clipboardLocker.IsLocked() || _UpdateInfos(false) != B_OK)
298*105093fdSIngo Weinhold 		return;
299*105093fdSIngo Weinhold 
300*105093fdSIngo Weinhold 	// update the clipboard to make our change known to the others
301*105093fdSIngo Weinhold 	_UpdateClipboard();
302*105093fdSIngo Weinhold }
303*105093fdSIngo Weinhold 
304*105093fdSIngo Weinhold 
305*105093fdSIngo Weinhold /*!	Overriden to handle update messages.
306*105093fdSIngo Weinhold */
307*105093fdSIngo Weinhold void
MessageReceived(BMessage * message)308*105093fdSIngo Weinhold TerminalRoster::MessageReceived(BMessage* message)
309*105093fdSIngo Weinhold {
310*105093fdSIngo Weinhold 	switch (message->what) {
311*105093fdSIngo Weinhold 		case B_SOME_APP_QUIT:
312*105093fdSIngo Weinhold 		{
313*105093fdSIngo Weinhold 			BString signature;
314*105093fdSIngo Weinhold 			if (message->FindString("be:signature", &signature) != B_OK
315*105093fdSIngo Weinhold 				|| signature != TERM_SIGNATURE) {
316*105093fdSIngo Weinhold 				break;
317*105093fdSIngo Weinhold 			}
318*105093fdSIngo Weinhold 			// fall through
319*105093fdSIngo Weinhold 		}
320*105093fdSIngo Weinhold 
321*105093fdSIngo Weinhold 		case B_CLIPBOARD_CHANGED:
322*105093fdSIngo Weinhold 		{
323*105093fdSIngo Weinhold 			// lock ourselves and the clipboard and update the infos
324*105093fdSIngo Weinhold 			AutoLocker<TerminalRoster> locker(this);
325*105093fdSIngo Weinhold 			AutoLocker<BClipboard> clipboardLocker(fClipboard);
326*105093fdSIngo Weinhold 			if (clipboardLocker.IsLocked()) {
327*105093fdSIngo Weinhold 				_UpdateInfos(false);
328*105093fdSIngo Weinhold 
329*105093fdSIngo Weinhold 				if (fInfosUpdated)
330*105093fdSIngo Weinhold 					_NotifyListener();
331*105093fdSIngo Weinhold 			}
332*105093fdSIngo Weinhold 
333*105093fdSIngo Weinhold 			break;
334*105093fdSIngo Weinhold 		}
335*105093fdSIngo Weinhold 
336*105093fdSIngo Weinhold 		default:
337*105093fdSIngo Weinhold 			BHandler::MessageReceived(message);
338*105093fdSIngo Weinhold 			break;
339*105093fdSIngo Weinhold 	}
340*105093fdSIngo Weinhold }
341*105093fdSIngo Weinhold 
342*105093fdSIngo Weinhold 
343*105093fdSIngo Weinhold /*!	Updates the terminal info list from the clipboard.
344*105093fdSIngo Weinhold 
345*105093fdSIngo Weinhold 	\param checkApps If \c true, it is checked for each found info whether the
346*105093fdSIngo Weinhold 		respective team is still running. If not, the info is removed from the
347*105093fdSIngo Weinhold 		list (though not from the clipboard).
348*105093fdSIngo Weinhold 	\return \c B_OK, if the update went fine, another error code otherwise. When
349*105093fdSIngo Weinhold 		an error occurs the object state will still be consistent, but might no
350*105093fdSIngo Weinhold 		longer be up-to-date.
351*105093fdSIngo Weinhold */
352*105093fdSIngo Weinhold status_t
_UpdateInfos(bool checkApps)353*105093fdSIngo Weinhold TerminalRoster::_UpdateInfos(bool checkApps)
354*105093fdSIngo Weinhold {
355*105093fdSIngo Weinhold 	BMessage* data = fClipboard.Data();
356*105093fdSIngo Weinhold 
357*105093fdSIngo Weinhold 	// find out how many infos we can expect
358*105093fdSIngo Weinhold 	type_code type;
359*105093fdSIngo Weinhold 	int32 count;
360*105093fdSIngo Weinhold 	status_t error = data->GetInfo("teams", &type, &count);
361*105093fdSIngo Weinhold 	if (error != B_OK)
362*105093fdSIngo Weinhold 		count = 0;
363*105093fdSIngo Weinhold 
364*105093fdSIngo Weinhold 	// create an info list from the message
365*105093fdSIngo Weinhold 	InfoList infos(10, true);
366*105093fdSIngo Weinhold 	for (int32 i = 0; i < count; i++) {
367*105093fdSIngo Weinhold 		// get the team's message
368*105093fdSIngo Weinhold 		BMessage teamData;
369*105093fdSIngo Weinhold 		error = data->FindMessage("teams", i, &teamData);
370*105093fdSIngo Weinhold 		if (error != B_OK)
371*105093fdSIngo Weinhold 			return error;
372*105093fdSIngo Weinhold 
373*105093fdSIngo Weinhold 		// create the info
374*105093fdSIngo Weinhold 		Info* info = new(std::nothrow) Info(teamData);
375*105093fdSIngo Weinhold 		if (info == NULL)
376*105093fdSIngo Weinhold 			return B_NO_MEMORY;
377*105093fdSIngo Weinhold 		if (info->id < 0 || info->team < 0
378*105093fdSIngo Weinhold 			|| infos.BinarySearchByKey(info->id, &_CompareIDInfo) != NULL
379*105093fdSIngo Weinhold 			|| (checkApps && !_TeamIsRunning(info->team))) {
380*105093fdSIngo Weinhold 			// invalid/duplicate info -- skip
381*105093fdSIngo Weinhold 			delete info;
382*105093fdSIngo Weinhold 			fInfosUpdated = true;
383*105093fdSIngo Weinhold 			continue;
384*105093fdSIngo Weinhold 		}
385*105093fdSIngo Weinhold 
386*105093fdSIngo Weinhold 		// add it to the list
387*105093fdSIngo Weinhold 		if (!infos.BinaryInsert(info, &_CompareInfos)) {
388*105093fdSIngo Weinhold 			delete info;
389*105093fdSIngo Weinhold 			return B_NO_MEMORY;
390*105093fdSIngo Weinhold 		}
391*105093fdSIngo Weinhold 	}
392*105093fdSIngo Weinhold 
393*105093fdSIngo Weinhold 	// update the current info list from the infos we just read
394*105093fdSIngo Weinhold 	int32 oldIndex = 0;
395*105093fdSIngo Weinhold 	int32 newIndex = 0;
396*105093fdSIngo Weinhold 	while (oldIndex < fInfos.CountItems() || newIndex < infos.CountItems()) {
397*105093fdSIngo Weinhold 		Info* oldInfo = fInfos.ItemAt(oldIndex);
398*105093fdSIngo Weinhold 		Info* newInfo = infos.ItemAt(newIndex);
399*105093fdSIngo Weinhold 
400*105093fdSIngo Weinhold 		if (oldInfo == NULL || (newInfo != NULL && oldInfo->id > newInfo->id)) {
401*105093fdSIngo Weinhold 			// new info is not in old list -- transfer it
402*105093fdSIngo Weinhold 			if (!fInfos.AddItem(newInfo, oldIndex++))
403*105093fdSIngo Weinhold 				return B_NO_MEMORY;
404*105093fdSIngo Weinhold 			infos.RemoveItemAt(newIndex);
405*105093fdSIngo Weinhold 			fInfosUpdated = true;
406*105093fdSIngo Weinhold 		} else if (newInfo == NULL || oldInfo->id < newInfo->id) {
407*105093fdSIngo Weinhold 			// old info is not in new list -- delete it, unless it's our own
408*105093fdSIngo Weinhold 			if (oldInfo == fOurInfo) {
409*105093fdSIngo Weinhold 				oldIndex++;
410*105093fdSIngo Weinhold 			} else {
411*105093fdSIngo Weinhold 				delete fInfos.RemoveItemAt(oldIndex);
412*105093fdSIngo Weinhold 				fInfosUpdated = true;
413*105093fdSIngo Weinhold 			}
414*105093fdSIngo Weinhold 		} else {
415*105093fdSIngo Weinhold 			// info is in both lists -- update the old info, unless it's our own
416*105093fdSIngo Weinhold 			if (oldInfo != fOurInfo) {
417*105093fdSIngo Weinhold 				if (*oldInfo != *newInfo) {
418*105093fdSIngo Weinhold 					*oldInfo = *newInfo;
419*105093fdSIngo Weinhold 					fInfosUpdated = true;
420*105093fdSIngo Weinhold 				}
421*105093fdSIngo Weinhold 			}
422*105093fdSIngo Weinhold 
423*105093fdSIngo Weinhold 			oldIndex++;
424*105093fdSIngo Weinhold 			newIndex++;
425*105093fdSIngo Weinhold 		}
426*105093fdSIngo Weinhold 	}
427*105093fdSIngo Weinhold 
428*105093fdSIngo Weinhold 	if (checkApps)
429*105093fdSIngo Weinhold 		fLastCheckedTime = system_time();
430*105093fdSIngo Weinhold 
431*105093fdSIngo Weinhold 	return B_OK;
432*105093fdSIngo Weinhold }
433*105093fdSIngo Weinhold 
434*105093fdSIngo Weinhold 
435*105093fdSIngo Weinhold /*!	Updates the clipboard with the object's terminal info list.
436*105093fdSIngo Weinhold 
437*105093fdSIngo Weinhold 	\return \c B_OK, if the update went fine, another error code otherwise. When
438*105093fdSIngo Weinhold 		an error occurs the object state will still be consistent, but might no
439*105093fdSIngo Weinhold 		longer be in sync with the clipboard.
440*105093fdSIngo Weinhold */
441*105093fdSIngo Weinhold status_t
_UpdateClipboard()442*105093fdSIngo Weinhold TerminalRoster::_UpdateClipboard()
443*105093fdSIngo Weinhold {
444*105093fdSIngo Weinhold 	// get the clipboard data message
445*105093fdSIngo Weinhold 	BMessage* data = fClipboard.Data();
446*105093fdSIngo Weinhold 	if (data == NULL)
447*105093fdSIngo Weinhold 		return B_BAD_VALUE;
448*105093fdSIngo Weinhold 
449*105093fdSIngo Weinhold 	// clear the message and add all infos
450*105093fdSIngo Weinhold 	data->MakeEmpty();
451*105093fdSIngo Weinhold 
452*105093fdSIngo Weinhold 	for (int32 i = 0; const Info* info = TerminalAt(i); i++) {
453*105093fdSIngo Weinhold 		BMessage teamData;
454*105093fdSIngo Weinhold 		status_t error = info->Archive(teamData);
455*105093fdSIngo Weinhold 		if (error != B_OK
456*105093fdSIngo Weinhold 			|| (error = data->AddMessage("teams", &teamData)) != B_OK) {
457*105093fdSIngo Weinhold 			fClipboard.Revert();
458*105093fdSIngo Weinhold 			return error;
459*105093fdSIngo Weinhold 		}
460*105093fdSIngo Weinhold 	}
461*105093fdSIngo Weinhold 
462*105093fdSIngo Weinhold 	// commit the changes
463*105093fdSIngo Weinhold 	status_t error = fClipboard.Commit();
464*105093fdSIngo Weinhold 	if (error != B_OK) {
465*105093fdSIngo Weinhold 		fClipboard.Revert();
466*105093fdSIngo Weinhold 		return error;
467*105093fdSIngo Weinhold 	}
468*105093fdSIngo Weinhold 
469*105093fdSIngo Weinhold 	return B_OK;
470*105093fdSIngo Weinhold }
471*105093fdSIngo Weinhold 
472*105093fdSIngo Weinhold 
473*105093fdSIngo Weinhold /*!	Notifies the listener, if something has changed.
474*105093fdSIngo Weinhold */
475*105093fdSIngo Weinhold void
_NotifyListener()476*105093fdSIngo Weinhold TerminalRoster::_NotifyListener()
477*105093fdSIngo Weinhold {
478*105093fdSIngo Weinhold 	if (!fInfosUpdated)
479*105093fdSIngo Weinhold 		return;
480*105093fdSIngo Weinhold 
481*105093fdSIngo Weinhold 	if (fListener != NULL)
482*105093fdSIngo Weinhold 		fListener->TerminalInfosUpdated(this);
483*105093fdSIngo Weinhold 
484*105093fdSIngo Weinhold 	fInfosUpdated = false;
485*105093fdSIngo Weinhold }
486*105093fdSIngo Weinhold 
487*105093fdSIngo Weinhold 
488*105093fdSIngo Weinhold /*static*/ int
_CompareInfos(const Info * a,const Info * b)489*105093fdSIngo Weinhold TerminalRoster::_CompareInfos(const Info* a, const Info* b)
490*105093fdSIngo Weinhold {
491*105093fdSIngo Weinhold 	return a->id - b->id;
492*105093fdSIngo Weinhold }
493*105093fdSIngo Weinhold 
494*105093fdSIngo Weinhold 
495*105093fdSIngo Weinhold /*static*/ int
_CompareIDInfo(const int32 * id,const Info * info)496*105093fdSIngo Weinhold TerminalRoster::_CompareIDInfo(const int32* id, const Info* info)
497*105093fdSIngo Weinhold {
498*105093fdSIngo Weinhold 	return *id - info->id;
499*105093fdSIngo Weinhold }
500*105093fdSIngo Weinhold 
501*105093fdSIngo Weinhold 
502*105093fdSIngo Weinhold bool
_TeamIsRunning(team_id teamID)503*105093fdSIngo Weinhold TerminalRoster::_TeamIsRunning(team_id teamID)
504*105093fdSIngo Weinhold {
505*105093fdSIngo Weinhold 	// we are running for sure
506*105093fdSIngo Weinhold 	if (fOurInfo != NULL && fOurInfo->team == teamID)
507*105093fdSIngo Weinhold 		return true;
508*105093fdSIngo Weinhold 
509*105093fdSIngo Weinhold 	team_info info;
510*105093fdSIngo Weinhold 	return get_team_info(teamID, &info) == B_OK;
511*105093fdSIngo Weinhold }
512*105093fdSIngo Weinhold 
513*105093fdSIngo Weinhold 
514*105093fdSIngo Weinhold // #pragma mark - Listener
515*105093fdSIngo Weinhold 
516*105093fdSIngo Weinhold 
~Listener()517*105093fdSIngo Weinhold TerminalRoster::Listener::~Listener()
518*105093fdSIngo Weinhold {
519*105093fdSIngo Weinhold }
520