xref: /haiku/src/servers/app/CursorManager.cpp (revision b809ff1c59fc4e92cef50577e726f15cbda042e4)
1 /*
2  * Copyright 2001-2009, 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 
28 CursorManager::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
98 CursorManager::~CursorManager()
99 {
100 	for (int32 i = 0; i < fCursorList.CountItems(); i++)
101 		delete (ServerCursor*)fCursorList.ItemAtFast(i);
102 }
103 
104 
105 ServerCursor*
106 CursorManager::CreateCursor(team_id clientTeam, const uint8* cursorData)
107 {
108 	if (!Lock())
109 		return NULL;
110 
111 	ServerCursor* cursor = _FindCursor(clientTeam, cursorData);
112 
113 	if (!cursor) {
114 		cursor = new (std::nothrow) ServerCursor(cursorData);
115 		if (cursor) {
116 			cursor->SetOwningTeam(clientTeam);
117 			if (AddCursor(cursor) < B_OK) {
118 				delete cursor;
119 				cursor = NULL;
120 			}
121 		}
122 	} else
123 		cursor->AcquireReference();
124 
125 	Unlock();
126 
127 	return cursor;
128 }
129 
130 
131 /*!	\brief Registers a cursor with the manager.
132 	\param cursor ServerCursor object to register
133 	\return The token assigned to the cursor or B_ERROR if cursor is NULL
134 */
135 int32
136 CursorManager::AddCursor(ServerCursor* cursor, int32 token)
137 {
138 	if (!cursor)
139 		return B_BAD_VALUE;
140 	if (!Lock())
141 		return B_ERROR;
142 
143 	if (!fCursorList.AddItem(cursor)) {
144 		Unlock();
145 		return B_NO_MEMORY;
146 	}
147 
148 	if (token == -1)
149 		token = fTokenSpace.NewToken(kCursorToken, cursor);
150 	else
151 		fTokenSpace.SetToken(token, kCursorToken, cursor);
152 
153 	cursor->fToken = token;
154 	cursor->AttachedToManager(this);
155 
156 	Unlock();
157 
158 	return token;
159 }
160 
161 
162 /*!	\brief Removes a cursor if it's not referenced anymore.
163 
164 	If this was the last reference to this cursor, it will be deleted.
165 	Only if the cursor is deleted, \c true is returned.
166 */
167 bool
168 CursorManager::RemoveCursor(ServerCursor* cursor)
169 {
170 	if (!Lock())
171 		return false;
172 
173 	// TODO: this doesn't work as it looks like, and it's not safe!
174 	if (cursor->CountReferences() > 0) {
175 		// cursor has been referenced again in the mean time
176 		Unlock();
177 		return false;
178 	}
179 
180 	_RemoveCursor(cursor);
181 
182 	Unlock();
183 	return true;
184 }
185 
186 
187 /*!	\brief Removes and deletes all of an application's cursors
188 	\param signature Signature to which the cursors belong
189 */
190 void
191 CursorManager::DeleteCursors(team_id team)
192 {
193 	if (!Lock())
194 		return;
195 
196 	for (int32 index = fCursorList.CountItems(); index-- > 0;) {
197 		ServerCursor* cursor = (ServerCursor*)fCursorList.ItemAtFast(index);
198 		if (cursor->OwningTeam() == team)
199 			cursor->ReleaseReference();
200 	}
201 
202 	Unlock();
203 }
204 
205 
206 /*!	\brief Sets all the cursors from a specified CursorSet
207 	\param path Path to the cursor set
208 
209 	All cursors in the set will be assigned. If the set does not specify a
210 	cursor for a particular cursor specifier, it will remain unchanged.
211 	This function will fail if passed a NULL path, an invalid path, or the
212 	path to a non-CursorSet file.
213 */
214 void
215 CursorManager::SetCursorSet(const char* path)
216 {
217 	BAutolock locker (this);
218 
219 	CursorSet cursorSet(NULL);
220 
221 	if (!path || cursorSet.Load(path) != B_OK)
222 		return;
223 
224 	_LoadCursor(fCursorSystemDefault, cursorSet, B_CURSOR_ID_SYSTEM_DEFAULT);
225 	_LoadCursor(fCursorContextMenu, cursorSet, B_CURSOR_ID_CONTEXT_MENU);
226 	_LoadCursor(fCursorCopy, cursorSet, B_CURSOR_ID_COPY);
227 	_LoadCursor(fCursorCreateLink, cursorSet, B_CURSOR_ID_CREATE_LINK);
228 	_LoadCursor(fCursorCrossHair, cursorSet, B_CURSOR_ID_CROSS_HAIR);
229 	_LoadCursor(fCursorFollowLink, cursorSet, B_CURSOR_ID_FOLLOW_LINK);
230 	_LoadCursor(fCursorGrab, cursorSet, B_CURSOR_ID_GRAB);
231 	_LoadCursor(fCursorGrabbing, cursorSet, B_CURSOR_ID_GRABBING);
232 	_LoadCursor(fCursorHelp, cursorSet, B_CURSOR_ID_HELP);
233 	_LoadCursor(fCursorIBeam, cursorSet, B_CURSOR_ID_I_BEAM);
234 	_LoadCursor(fCursorIBeamHorizontal, cursorSet,
235 		B_CURSOR_ID_I_BEAM_HORIZONTAL);
236 	_LoadCursor(fCursorMove, cursorSet, B_CURSOR_ID_MOVE);
237 	_LoadCursor(fCursorNotAllowed, cursorSet, B_CURSOR_ID_NOT_ALLOWED);
238 	_LoadCursor(fCursorProgress, cursorSet, B_CURSOR_ID_PROGRESS);
239 	_LoadCursor(fCursorResizeEast, cursorSet, B_CURSOR_ID_RESIZE_EAST);
240 	_LoadCursor(fCursorResizeEastWest, cursorSet,
241 		B_CURSOR_ID_RESIZE_EAST_WEST);
242 	_LoadCursor(fCursorResizeNorth, cursorSet, B_CURSOR_ID_RESIZE_NORTH);
243 	_LoadCursor(fCursorResizeNorthEast, cursorSet,
244 		B_CURSOR_ID_RESIZE_NORTH_EAST);
245 	_LoadCursor(fCursorResizeNorthEastSouthWest, cursorSet,
246 		B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST);
247 	_LoadCursor(fCursorResizeNorthSouth, cursorSet,
248 		B_CURSOR_ID_RESIZE_NORTH_SOUTH);
249 	_LoadCursor(fCursorResizeNorthWest, cursorSet,
250 		B_CURSOR_ID_RESIZE_NORTH_WEST);
251 	_LoadCursor(fCursorResizeNorthWestSouthEast, cursorSet,
252 		B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST);
253 	_LoadCursor(fCursorResizeSouth, cursorSet, B_CURSOR_ID_RESIZE_SOUTH);
254 	_LoadCursor(fCursorResizeSouthEast, cursorSet,
255 		B_CURSOR_ID_RESIZE_SOUTH_EAST);
256 	_LoadCursor(fCursorResizeSouthWest, cursorSet,
257 		B_CURSOR_ID_RESIZE_SOUTH_WEST);
258 	_LoadCursor(fCursorResizeWest, cursorSet, B_CURSOR_ID_RESIZE_WEST);
259 	_LoadCursor(fCursorZoomIn, cursorSet, B_CURSOR_ID_ZOOM_IN);
260 	_LoadCursor(fCursorZoomOut, cursorSet, B_CURSOR_ID_ZOOM_OUT);
261 }
262 
263 
264 /*!	\brief Acquire the cursor which is used for a particular system cursor
265 	\param which Which system cursor to get
266 	\return Pointer to the particular cursor used or NULL if which is
267 	invalid or the cursor has not been assigned
268 */
269 ServerCursor*
270 CursorManager::GetCursor(BCursorID which)
271 {
272 	BAutolock locker(this);
273 
274 	switch (which) {
275 		case B_CURSOR_ID_SYSTEM_DEFAULT:
276 			return fCursorSystemDefault;
277 		case B_CURSOR_ID_CONTEXT_MENU:
278 			return fCursorContextMenu;
279 		case B_CURSOR_ID_COPY:
280 			return fCursorCopy;
281 		case B_CURSOR_ID_CREATE_LINK:
282 			return fCursorCreateLink;
283 		case B_CURSOR_ID_CROSS_HAIR:
284 			return fCursorCrossHair;
285 		case B_CURSOR_ID_FOLLOW_LINK:
286 			return fCursorFollowLink;
287 		case B_CURSOR_ID_GRAB:
288 			return fCursorGrab;
289 		case B_CURSOR_ID_GRABBING:
290 			return fCursorGrabbing;
291 		case B_CURSOR_ID_HELP:
292 			return fCursorHelp;
293 		case B_CURSOR_ID_I_BEAM:
294 			return fCursorIBeam;
295 		case B_CURSOR_ID_I_BEAM_HORIZONTAL:
296 			return fCursorIBeamHorizontal;
297 		case B_CURSOR_ID_MOVE:
298 			return fCursorMove;
299 		case B_CURSOR_ID_NO_CURSOR:
300 			return fCursorNoCursor;
301 		case B_CURSOR_ID_NOT_ALLOWED:
302 			return fCursorNotAllowed;
303 		case B_CURSOR_ID_PROGRESS:
304 			return fCursorProgress;
305 		case B_CURSOR_ID_RESIZE_EAST:
306 			return fCursorResizeEast;
307 		case B_CURSOR_ID_RESIZE_EAST_WEST:
308 			return fCursorResizeEastWest;
309 		case B_CURSOR_ID_RESIZE_NORTH:
310 			return fCursorResizeNorth;
311 		case B_CURSOR_ID_RESIZE_NORTH_EAST:
312 			return fCursorResizeNorthEast;
313 		case B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST:
314 			return fCursorResizeNorthEastSouthWest;
315 		case B_CURSOR_ID_RESIZE_NORTH_SOUTH:
316 			return fCursorResizeNorthSouth;
317 		case B_CURSOR_ID_RESIZE_NORTH_WEST:
318 			return fCursorResizeNorthWest;
319 		case B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST:
320 			return fCursorResizeNorthWestSouthEast;
321 		case B_CURSOR_ID_RESIZE_SOUTH:
322 			return fCursorResizeSouth;
323 		case B_CURSOR_ID_RESIZE_SOUTH_EAST:
324 			return fCursorResizeSouthEast;
325 		case B_CURSOR_ID_RESIZE_SOUTH_WEST:
326 			return fCursorResizeSouthWest;
327 		case B_CURSOR_ID_RESIZE_WEST:
328 			return fCursorResizeWest;
329 		case B_CURSOR_ID_ZOOM_IN:
330 			return fCursorZoomIn;
331 		case B_CURSOR_ID_ZOOM_OUT:
332 			return fCursorZoomOut;
333 
334 		default:
335 			return NULL;
336 	}
337 }
338 
339 
340 /*!	\brief Internal function which finds the cursor with a particular ID
341 	\param token ID of the cursor to find
342 	\return The cursor or NULL if not found
343 */
344 ServerCursor *
345 CursorManager::FindCursor(int32 token)
346 {
347 	if (!Lock())
348 		return NULL;
349 
350 	ServerCursor* cursor;
351 	if (fTokenSpace.GetToken(token, kCursorToken, (void**)&cursor) != B_OK)
352 		cursor = NULL;
353 
354 	Unlock();
355 
356 	return cursor;
357 }
358 
359 
360 void
361 CursorManager::_InitCursor(ServerCursor*& cursorMember,
362 	const uint8* cursorBits, BCursorID id, const BPoint& hotSpot)
363 {
364 	if (cursorBits) {
365 		cursorMember = new ServerCursor(cursorBits, kCursorWidth,
366 			kCursorHeight, kCursorFormat);
367 	} else
368 		cursorMember = new ServerCursor(kCursorNoCursor, 1, 1, kCursorFormat);
369 
370 	cursorMember->SetHotSpot(hotSpot);
371 	AddCursor(cursorMember, id);
372 }
373 
374 
375 void
376 CursorManager::_LoadCursor(ServerCursor*& cursorMember, const CursorSet& set,
377 	BCursorID id)
378 {
379 	ServerCursor* cursor;
380 	if (set.FindCursor(id, &cursor) == B_OK) {
381 		int32 index = fCursorList.IndexOf(cursorMember);
382 		if (index >= 0) {
383 			ServerCursor* items = reinterpret_cast<ServerCursor*>(
384 				fCursorList.Items());
385 			items[index] = cursor;
386 		}
387 		delete cursorMember;
388 		cursorMember = cursor;
389 	}
390 }
391 
392 
393 ServerCursor*
394 CursorManager::_FindCursor(team_id clientTeam, const uint8* cursorData)
395 {
396 	int32 count = fCursorList.CountItems();
397 	for (int32 i = 0; i < count; i++) {
398 		ServerCursor* cursor = (ServerCursor*)fCursorList.ItemAtFast(i);
399 		if (cursor->OwningTeam() == clientTeam
400 			&& cursor->CursorData()
401 			&& memcmp(cursor->CursorData(), cursorData, 68) == 0) {
402 			return cursor;
403 		}
404 	}
405 	return NULL;
406 }
407 
408 
409 void
410 CursorManager::_RemoveCursor(ServerCursor* cursor)
411 {
412 	fCursorList.RemoveItem(cursor);
413 	fTokenSpace.RemoveToken(cursor->fToken);
414 }
415