1/*
2 * Copyright 2001-2016, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		DarkWyrm <bpmagic@columbus.rr.com>
7 */
8
9
10/*!	Handles the system's cursor infrastructure */
11
12
13#include "CursorManager.h"
14
15#include "CursorData.h"
16#include "ServerCursor.h"
17#include "ServerConfig.h"
18#include "ServerTokenSpace.h"
19
20#include <Autolock.h>
21#include <Directory.h>
22#include <String.h>
23
24#include <new>
25#include <stdio.h>
26
27
28CursorManager::CursorManager()
29	:
30	BLocker("CursorManager")
31{
32	// Init system cursors
33	const BPoint kHandHotspot(1, 1);
34	const BPoint kResizeHotspot(8, 8);
35	_InitCursor(fCursorSystemDefault, kCursorSystemDefaultBits,
36		B_CURSOR_ID_SYSTEM_DEFAULT, kHandHotspot);
37	_InitCursor(fCursorContextMenu, kCursorContextMenuBits,
38		B_CURSOR_ID_CONTEXT_MENU, kHandHotspot);
39	_InitCursor(fCursorCopy, kCursorCopyBits,
40		B_CURSOR_ID_COPY, kHandHotspot);
41	_InitCursor(fCursorCreateLink, kCursorCreateLinkBits,
42		B_CURSOR_ID_CREATE_LINK, kHandHotspot);
43	_InitCursor(fCursorCrossHair, kCursorCrossHairBits,
44		B_CURSOR_ID_CROSS_HAIR, BPoint(10, 10));
45	_InitCursor(fCursorFollowLink, kCursorFollowLinkBits,
46		B_CURSOR_ID_FOLLOW_LINK, BPoint(5, 0));
47	_InitCursor(fCursorGrab, kCursorGrabBits,
48		B_CURSOR_ID_GRAB, kHandHotspot);
49	_InitCursor(fCursorGrabbing, kCursorGrabbingBits,
50		B_CURSOR_ID_GRABBING, kHandHotspot);
51	_InitCursor(fCursorHelp, kCursorHelpBits,
52		B_CURSOR_ID_HELP, BPoint(0, 8));
53	_InitCursor(fCursorIBeam, kCursorIBeamBits,
54		B_CURSOR_ID_I_BEAM, BPoint(7, 9));
55	_InitCursor(fCursorIBeamHorizontal, kCursorIBeamHorizontalBits,
56		B_CURSOR_ID_I_BEAM_HORIZONTAL, BPoint(8, 8));
57	_InitCursor(fCursorMove, kCursorMoveBits,
58		B_CURSOR_ID_MOVE, kResizeHotspot);
59	_InitCursor(fCursorNoCursor, 0, B_CURSOR_ID_NO_CURSOR, BPoint(0, 0));
60	_InitCursor(fCursorNotAllowed, kCursorNotAllowedBits,
61		B_CURSOR_ID_NOT_ALLOWED, BPoint(8, 8));
62	_InitCursor(fCursorProgress, kCursorProgressBits,
63		B_CURSOR_ID_PROGRESS, BPoint(7, 10));
64	_InitCursor(fCursorResizeEast, kCursorResizeEastBits,
65		B_CURSOR_ID_RESIZE_EAST, kResizeHotspot);
66	_InitCursor(fCursorResizeEastWest, kCursorResizeEastWestBits,
67		B_CURSOR_ID_RESIZE_EAST_WEST, kResizeHotspot);
68	_InitCursor(fCursorResizeNorth, kCursorResizeNorthBits,
69		B_CURSOR_ID_RESIZE_NORTH, kResizeHotspot);
70	_InitCursor(fCursorResizeNorthEast, kCursorResizeNorthEastBits,
71		B_CURSOR_ID_RESIZE_NORTH_EAST, kResizeHotspot);
72	_InitCursor(fCursorResizeNorthEastSouthWest,
73		kCursorResizeNorthEastSouthWestBits,
74		B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST, kResizeHotspot);
75	_InitCursor(fCursorResizeNorthSouth, kCursorResizeNorthSouthBits,
76		B_CURSOR_ID_RESIZE_NORTH_SOUTH, kResizeHotspot);
77	_InitCursor(fCursorResizeNorthWest, kCursorResizeNorthWestBits,
78		B_CURSOR_ID_RESIZE_NORTH_WEST, kResizeHotspot);
79	_InitCursor(fCursorResizeNorthWestSouthEast,
80		kCursorResizeNorthWestSouthEastBits,
81		B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST, kResizeHotspot);
82	_InitCursor(fCursorResizeSouth, kCursorResizeSouthBits,
83		B_CURSOR_ID_RESIZE_SOUTH, kResizeHotspot);
84	_InitCursor(fCursorResizeSouthEast, kCursorResizeSouthEastBits,
85		B_CURSOR_ID_RESIZE_SOUTH_EAST, kResizeHotspot);
86	_InitCursor(fCursorResizeSouthWest, kCursorResizeSouthWestBits,
87		B_CURSOR_ID_RESIZE_SOUTH_WEST, kResizeHotspot);
88	_InitCursor(fCursorResizeWest, kCursorResizeWestBits,
89		B_CURSOR_ID_RESIZE_WEST, kResizeHotspot);
90	_InitCursor(fCursorZoomIn, kCursorZoomInBits,
91		B_CURSOR_ID_ZOOM_IN, BPoint(6, 6));
92	_InitCursor(fCursorZoomOut, kCursorZoomOutBits,
93		B_CURSOR_ID_ZOOM_OUT, BPoint(6, 6));
94}
95
96
97//! Does all the teardown
98CursorManager::~CursorManager()
99{
100	for (int32 i = 0; i < fCursorList.CountItems(); i++) {
101		ServerCursor* cursor = ((ServerCursor*)fCursorList.ItemAtFast(i));
102		cursor->fManager = NULL;
103		cursor->ReleaseReference();
104	}
105}
106
107
108ServerCursor*
109CursorManager::CreateCursor(team_id clientTeam, const uint8* cursorData)
110{
111	if (!Lock())
112		return NULL;
113
114	ServerCursorReference cursor(_FindCursor(clientTeam, cursorData), false);
115
116	if (!cursor) {
117		cursor.SetTo(new (std::nothrow) ServerCursor(cursorData), true);
118		if (cursor) {
119			cursor->SetOwningTeam(clientTeam);
120			if (AddCursor(cursor) < B_OK)
121				cursor = NULL;
122		}
123	}
124
125	Unlock();
126
127	return cursor.Detach();
128}
129
130
131ServerCursor*
132CursorManager::CreateCursor(team_id clientTeam, BRect r, color_space format,
133	int32 flags, BPoint hotspot, int32 bytesPerRow)
134{
135	if (!Lock())
136		return NULL;
137
138	ServerCursor* cursor = new (std::nothrow) ServerCursor(r, format, flags,
139		hotspot, bytesPerRow);
140	if (cursor != NULL) {
141		cursor->SetOwningTeam(clientTeam);
142		if (AddCursor(cursor) < B_OK) {
143			delete cursor;
144			cursor = NULL;
145		}
146	}
147
148	Unlock();
149
150	return cursor;
151}
152
153
154/*!	\brief Registers a cursor with the manager.
155	\param cursor ServerCursor object to register
156	\return The token assigned to the cursor or B_ERROR if cursor is NULL
157*/
158int32
159CursorManager::AddCursor(ServerCursor* cursor, int32 token)
160{
161	if (!cursor)
162		return B_BAD_VALUE;
163	if (!Lock())
164		return B_ERROR;
165
166	if (!fCursorList.AddItem(cursor)) {
167		Unlock();
168		return B_NO_MEMORY;
169	}
170
171	if (token == -1)
172		token = fTokenSpace.NewToken(kCursorToken, cursor);
173	else
174		fTokenSpace.SetToken(token, kCursorToken, cursor);
175
176	cursor->fToken = token;
177	cursor->AttachedToManager(this);
178
179	Unlock();
180
181	return token;
182}
183
184
185/*!	\brief Removes a cursor if it's not referenced anymore.
186
187	If this was the last reference to this cursor, it will be deleted.
188	Only if the cursor is deleted, \c true is returned.
189*/
190bool
191CursorManager::RemoveCursor(ServerCursor* cursor)
192{
193	if (!Lock())
194		return false;
195
196	// TODO: this doesn't work as it looks like, and it's not safe!
197	if (cursor->CountReferences() > 0) {
198		// cursor has been referenced again in the mean time
199		Unlock();
200		return false;
201	}
202
203	_RemoveCursor(cursor);
204
205	Unlock();
206	return true;
207}
208
209
210/*!	\brief Removes and deletes all of an application's cursors
211	\param signature Signature to which the cursors belong
212*/
213void
214CursorManager::DeleteCursors(team_id team)
215{
216	if (!Lock())
217		return;
218
219	for (int32 index = fCursorList.CountItems(); index-- > 0;) {
220		ServerCursor* cursor = (ServerCursor*)fCursorList.ItemAtFast(index);
221		if (cursor->OwningTeam() == team)
222			cursor->ReleaseReference();
223	}
224
225	Unlock();
226}
227
228
229/*!	\brief Sets all the cursors from a specified CursorSet
230	\param path Path to the cursor set
231
232	All cursors in the set will be assigned. If the set does not specify a
233	cursor for a particular cursor specifier, it will remain unchanged.
234	This function will fail if passed a NULL path, an invalid path, or the
235	path to a non-CursorSet file.
236*/
237void
238CursorManager::SetCursorSet(const char* path)
239{
240	BAutolock locker (this);
241
242	CursorSet cursorSet(NULL);
243
244	if (!path || cursorSet.Load(path) != B_OK)
245		return;
246
247	_LoadCursor(fCursorSystemDefault, cursorSet, B_CURSOR_ID_SYSTEM_DEFAULT);
248	_LoadCursor(fCursorContextMenu, cursorSet, B_CURSOR_ID_CONTEXT_MENU);
249	_LoadCursor(fCursorCopy, cursorSet, B_CURSOR_ID_COPY);
250	_LoadCursor(fCursorCreateLink, cursorSet, B_CURSOR_ID_CREATE_LINK);
251	_LoadCursor(fCursorCrossHair, cursorSet, B_CURSOR_ID_CROSS_HAIR);
252	_LoadCursor(fCursorFollowLink, cursorSet, B_CURSOR_ID_FOLLOW_LINK);
253	_LoadCursor(fCursorGrab, cursorSet, B_CURSOR_ID_GRAB);
254	_LoadCursor(fCursorGrabbing, cursorSet, B_CURSOR_ID_GRABBING);
255	_LoadCursor(fCursorHelp, cursorSet, B_CURSOR_ID_HELP);
256	_LoadCursor(fCursorIBeam, cursorSet, B_CURSOR_ID_I_BEAM);
257	_LoadCursor(fCursorIBeamHorizontal, cursorSet,
258		B_CURSOR_ID_I_BEAM_HORIZONTAL);
259	_LoadCursor(fCursorMove, cursorSet, B_CURSOR_ID_MOVE);
260	_LoadCursor(fCursorNotAllowed, cursorSet, B_CURSOR_ID_NOT_ALLOWED);
261	_LoadCursor(fCursorProgress, cursorSet, B_CURSOR_ID_PROGRESS);
262	_LoadCursor(fCursorResizeEast, cursorSet, B_CURSOR_ID_RESIZE_EAST);
263	_LoadCursor(fCursorResizeEastWest, cursorSet,
264		B_CURSOR_ID_RESIZE_EAST_WEST);
265	_LoadCursor(fCursorResizeNorth, cursorSet, B_CURSOR_ID_RESIZE_NORTH);
266	_LoadCursor(fCursorResizeNorthEast, cursorSet,
267		B_CURSOR_ID_RESIZE_NORTH_EAST);
268	_LoadCursor(fCursorResizeNorthEastSouthWest, cursorSet,
269		B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST);
270	_LoadCursor(fCursorResizeNorthSouth, cursorSet,
271		B_CURSOR_ID_RESIZE_NORTH_SOUTH);
272	_LoadCursor(fCursorResizeNorthWest, cursorSet,
273		B_CURSOR_ID_RESIZE_NORTH_WEST);
274	_LoadCursor(fCursorResizeNorthWestSouthEast, cursorSet,
275		B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST);
276	_LoadCursor(fCursorResizeSouth, cursorSet, B_CURSOR_ID_RESIZE_SOUTH);
277	_LoadCursor(fCursorResizeSouthEast, cursorSet,
278		B_CURSOR_ID_RESIZE_SOUTH_EAST);
279	_LoadCursor(fCursorResizeSouthWest, cursorSet,
280		B_CURSOR_ID_RESIZE_SOUTH_WEST);
281	_LoadCursor(fCursorResizeWest, cursorSet, B_CURSOR_ID_RESIZE_WEST);
282	_LoadCursor(fCursorZoomIn, cursorSet, B_CURSOR_ID_ZOOM_IN);
283	_LoadCursor(fCursorZoomOut, cursorSet, B_CURSOR_ID_ZOOM_OUT);
284}
285
286
287/*!	\brief Acquire the cursor which is used for a particular system cursor
288	\param which Which system cursor to get
289	\return Pointer to the particular cursor used or NULL if which is
290	invalid or the cursor has not been assigned
291*/
292ServerCursor*
293CursorManager::GetCursor(BCursorID which)
294{
295	BAutolock locker(this);
296
297	switch (which) {
298		case B_CURSOR_ID_SYSTEM_DEFAULT:
299			return fCursorSystemDefault;
300		case B_CURSOR_ID_CONTEXT_MENU:
301			return fCursorContextMenu;
302		case B_CURSOR_ID_COPY:
303			return fCursorCopy;
304		case B_CURSOR_ID_CREATE_LINK:
305			return fCursorCreateLink;
306		case B_CURSOR_ID_CROSS_HAIR:
307			return fCursorCrossHair;
308		case B_CURSOR_ID_FOLLOW_LINK:
309			return fCursorFollowLink;
310		case B_CURSOR_ID_GRAB:
311			return fCursorGrab;
312		case B_CURSOR_ID_GRABBING:
313			return fCursorGrabbing;
314		case B_CURSOR_ID_HELP:
315			return fCursorHelp;
316		case B_CURSOR_ID_I_BEAM:
317			return fCursorIBeam;
318		case B_CURSOR_ID_I_BEAM_HORIZONTAL:
319			return fCursorIBeamHorizontal;
320		case B_CURSOR_ID_MOVE:
321			return fCursorMove;
322		case B_CURSOR_ID_NO_CURSOR:
323			return fCursorNoCursor;
324		case B_CURSOR_ID_NOT_ALLOWED:
325			return fCursorNotAllowed;
326		case B_CURSOR_ID_PROGRESS:
327			return fCursorProgress;
328		case B_CURSOR_ID_RESIZE_EAST:
329			return fCursorResizeEast;
330		case B_CURSOR_ID_RESIZE_EAST_WEST:
331			return fCursorResizeEastWest;
332		case B_CURSOR_ID_RESIZE_NORTH:
333			return fCursorResizeNorth;
334		case B_CURSOR_ID_RESIZE_NORTH_EAST:
335			return fCursorResizeNorthEast;
336		case B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST:
337			return fCursorResizeNorthEastSouthWest;
338		case B_CURSOR_ID_RESIZE_NORTH_SOUTH:
339			return fCursorResizeNorthSouth;
340		case B_CURSOR_ID_RESIZE_NORTH_WEST:
341			return fCursorResizeNorthWest;
342		case B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST:
343			return fCursorResizeNorthWestSouthEast;
344		case B_CURSOR_ID_RESIZE_SOUTH:
345			return fCursorResizeSouth;
346		case B_CURSOR_ID_RESIZE_SOUTH_EAST:
347			return fCursorResizeSouthEast;
348		case B_CURSOR_ID_RESIZE_SOUTH_WEST:
349			return fCursorResizeSouthWest;
350		case B_CURSOR_ID_RESIZE_WEST:
351			return fCursorResizeWest;
352		case B_CURSOR_ID_ZOOM_IN:
353			return fCursorZoomIn;
354		case B_CURSOR_ID_ZOOM_OUT:
355			return fCursorZoomOut;
356
357		default:
358			return NULL;
359	}
360}
361
362
363/*!	\brief Internal function which finds the cursor with a particular ID
364	\param token ID of the cursor to find
365	\return The cursor or NULL if not found
366*/
367ServerCursor*
368CursorManager::FindCursor(int32 token)
369{
370	if (!Lock())
371		return NULL;
372
373	ServerCursor* cursor;
374	if (fTokenSpace.GetToken(token, kCursorToken, (void**)&cursor) != B_OK)
375		cursor = NULL;
376
377	Unlock();
378
379	return cursor;
380}
381
382
383/*!	\brief Initializes a predefined system cursor.
384
385	This method must only be called in the CursorManager's constructor,
386	as it may throw exceptions.
387*/
388void
389CursorManager::_InitCursor(ServerCursor*& cursorMember,
390	const uint8* cursorBits, BCursorID id, const BPoint& hotSpot)
391{
392	if (cursorBits) {
393		cursorMember = new ServerCursor(cursorBits, kCursorWidth,
394			kCursorHeight, kCursorFormat);
395	} else
396		cursorMember = new ServerCursor(kCursorNoCursor, 1, 1, kCursorFormat);
397
398	cursorMember->SetHotSpot(hotSpot);
399	AddCursor(cursorMember, id);
400}
401
402
403void
404CursorManager::_LoadCursor(ServerCursor*& cursorMember, const CursorSet& set,
405	BCursorID id)
406{
407	ServerCursor* cursor;
408	if (set.FindCursor(id, &cursor) == B_OK) {
409		int32 index = fCursorList.IndexOf(cursorMember);
410		if (index >= 0) {
411			ServerCursor** items = reinterpret_cast<ServerCursor**>(
412				fCursorList.Items());
413			items[index] = cursor;
414		}
415		delete cursorMember;
416		cursorMember = cursor;
417	}
418}
419
420
421ServerCursor*
422CursorManager::_FindCursor(team_id clientTeam, const uint8* cursorData)
423{
424	int32 count = fCursorList.CountItems();
425	for (int32 i = 0; i < count; i++) {
426		ServerCursor* cursor = (ServerCursor*)fCursorList.ItemAtFast(i);
427		if (cursor->OwningTeam() == clientTeam
428			&& cursor->CursorData()
429			&& memcmp(cursor->CursorData(), cursorData, 68) == 0) {
430			return cursor;
431		}
432	}
433	return NULL;
434}
435
436
437void
438CursorManager::_RemoveCursor(ServerCursor* cursor)
439{
440	fCursorList.RemoveItem(cursor);
441	fTokenSpace.RemoveToken(cursor->fToken);
442}
443