xref: /haiku/src/servers/app/CursorManager.cpp (revision a5061ecec55353a5f394759473f1fd6df04890da)
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 
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 		ServerCursor* cursor = ((ServerCursor*)fCursorList.ItemAtFast(i));
102 		cursor->fManager = NULL;
103 		cursor->ReleaseReference();
104 	}
105 }
106 
107 
108 ServerCursor*
109 CursorManager::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 
131 ServerCursor*
132 CursorManager::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 */
158 int32
159 CursorManager::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 */
190 bool
191 CursorManager::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 */
213 void
214 CursorManager::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 */
237 void
238 CursorManager::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 */
292 ServerCursor*
293 CursorManager::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 */
367 ServerCursor*
368 CursorManager::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 */
388 void
389 CursorManager::_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 
403 void
404 CursorManager::_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 
421 ServerCursor*
422 CursorManager::_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 
437 void
438 CursorManager::_RemoveCursor(ServerCursor* cursor)
439 {
440 	fCursorList.RemoveItem(cursor);
441 	fTokenSpace.RemoveToken(cursor->fToken);
442 }
443