xref: /haiku/src/servers/app/CursorManager.cpp (revision eea5774f46bba925156498abf9cb1a1165647bf7)
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.
186 
187 	If this was the last reference to this cursor, it will be deleted.
188 */
189 void
190 CursorManager::RemoveCursor(ServerCursor* cursor)
191 {
192 	if (!Lock())
193 		return;
194 
195 	_RemoveCursor(cursor);
196 	cursor->ReleaseReference();
197 
198 	Unlock();
199 }
200 
201 
202 /*!	\brief Removes and deletes all of an application's cursors
203 	\param signature Signature to which the cursors belong
204 */
205 void
206 CursorManager::DeleteCursors(team_id team)
207 {
208 	if (!Lock())
209 		return;
210 
211 	for (int32 index = fCursorList.CountItems(); index-- > 0;) {
212 		ServerCursor* cursor = (ServerCursor*)fCursorList.ItemAtFast(index);
213 		if (cursor->OwningTeam() != team)
214 			continue;
215 
216 		_RemoveCursor(cursor);
217 		cursor->ReleaseReference();
218 	}
219 
220 	Unlock();
221 }
222 
223 
224 /*!	\brief Sets all the cursors from a specified CursorSet
225 	\param path Path to the cursor set
226 
227 	All cursors in the set will be assigned. If the set does not specify a
228 	cursor for a particular cursor specifier, it will remain unchanged.
229 	This function will fail if passed a NULL path, an invalid path, or the
230 	path to a non-CursorSet file.
231 */
232 void
233 CursorManager::SetCursorSet(const char* path)
234 {
235 	BAutolock locker (this);
236 
237 	CursorSet cursorSet(NULL);
238 
239 	if (!path || cursorSet.Load(path) != B_OK)
240 		return;
241 
242 	_LoadCursor(fCursorSystemDefault, cursorSet, B_CURSOR_ID_SYSTEM_DEFAULT);
243 	_LoadCursor(fCursorContextMenu, cursorSet, B_CURSOR_ID_CONTEXT_MENU);
244 	_LoadCursor(fCursorCopy, cursorSet, B_CURSOR_ID_COPY);
245 	_LoadCursor(fCursorCreateLink, cursorSet, B_CURSOR_ID_CREATE_LINK);
246 	_LoadCursor(fCursorCrossHair, cursorSet, B_CURSOR_ID_CROSS_HAIR);
247 	_LoadCursor(fCursorFollowLink, cursorSet, B_CURSOR_ID_FOLLOW_LINK);
248 	_LoadCursor(fCursorGrab, cursorSet, B_CURSOR_ID_GRAB);
249 	_LoadCursor(fCursorGrabbing, cursorSet, B_CURSOR_ID_GRABBING);
250 	_LoadCursor(fCursorHelp, cursorSet, B_CURSOR_ID_HELP);
251 	_LoadCursor(fCursorIBeam, cursorSet, B_CURSOR_ID_I_BEAM);
252 	_LoadCursor(fCursorIBeamHorizontal, cursorSet,
253 		B_CURSOR_ID_I_BEAM_HORIZONTAL);
254 	_LoadCursor(fCursorMove, cursorSet, B_CURSOR_ID_MOVE);
255 	_LoadCursor(fCursorNotAllowed, cursorSet, B_CURSOR_ID_NOT_ALLOWED);
256 	_LoadCursor(fCursorProgress, cursorSet, B_CURSOR_ID_PROGRESS);
257 	_LoadCursor(fCursorResizeEast, cursorSet, B_CURSOR_ID_RESIZE_EAST);
258 	_LoadCursor(fCursorResizeEastWest, cursorSet,
259 		B_CURSOR_ID_RESIZE_EAST_WEST);
260 	_LoadCursor(fCursorResizeNorth, cursorSet, B_CURSOR_ID_RESIZE_NORTH);
261 	_LoadCursor(fCursorResizeNorthEast, cursorSet,
262 		B_CURSOR_ID_RESIZE_NORTH_EAST);
263 	_LoadCursor(fCursorResizeNorthEastSouthWest, cursorSet,
264 		B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST);
265 	_LoadCursor(fCursorResizeNorthSouth, cursorSet,
266 		B_CURSOR_ID_RESIZE_NORTH_SOUTH);
267 	_LoadCursor(fCursorResizeNorthWest, cursorSet,
268 		B_CURSOR_ID_RESIZE_NORTH_WEST);
269 	_LoadCursor(fCursorResizeNorthWestSouthEast, cursorSet,
270 		B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST);
271 	_LoadCursor(fCursorResizeSouth, cursorSet, B_CURSOR_ID_RESIZE_SOUTH);
272 	_LoadCursor(fCursorResizeSouthEast, cursorSet,
273 		B_CURSOR_ID_RESIZE_SOUTH_EAST);
274 	_LoadCursor(fCursorResizeSouthWest, cursorSet,
275 		B_CURSOR_ID_RESIZE_SOUTH_WEST);
276 	_LoadCursor(fCursorResizeWest, cursorSet, B_CURSOR_ID_RESIZE_WEST);
277 	_LoadCursor(fCursorZoomIn, cursorSet, B_CURSOR_ID_ZOOM_IN);
278 	_LoadCursor(fCursorZoomOut, cursorSet, B_CURSOR_ID_ZOOM_OUT);
279 }
280 
281 
282 /*!	\brief Acquire the cursor which is used for a particular system cursor
283 	\param which Which system cursor to get
284 	\return Pointer to the particular cursor used or NULL if which is
285 	invalid or the cursor has not been assigned
286 */
287 ServerCursor*
288 CursorManager::GetCursor(BCursorID which)
289 {
290 	BAutolock locker(this);
291 
292 	switch (which) {
293 		case B_CURSOR_ID_SYSTEM_DEFAULT:
294 			return fCursorSystemDefault;
295 		case B_CURSOR_ID_CONTEXT_MENU:
296 			return fCursorContextMenu;
297 		case B_CURSOR_ID_COPY:
298 			return fCursorCopy;
299 		case B_CURSOR_ID_CREATE_LINK:
300 			return fCursorCreateLink;
301 		case B_CURSOR_ID_CROSS_HAIR:
302 			return fCursorCrossHair;
303 		case B_CURSOR_ID_FOLLOW_LINK:
304 			return fCursorFollowLink;
305 		case B_CURSOR_ID_GRAB:
306 			return fCursorGrab;
307 		case B_CURSOR_ID_GRABBING:
308 			return fCursorGrabbing;
309 		case B_CURSOR_ID_HELP:
310 			return fCursorHelp;
311 		case B_CURSOR_ID_I_BEAM:
312 			return fCursorIBeam;
313 		case B_CURSOR_ID_I_BEAM_HORIZONTAL:
314 			return fCursorIBeamHorizontal;
315 		case B_CURSOR_ID_MOVE:
316 			return fCursorMove;
317 		case B_CURSOR_ID_NO_CURSOR:
318 			return fCursorNoCursor;
319 		case B_CURSOR_ID_NOT_ALLOWED:
320 			return fCursorNotAllowed;
321 		case B_CURSOR_ID_PROGRESS:
322 			return fCursorProgress;
323 		case B_CURSOR_ID_RESIZE_EAST:
324 			return fCursorResizeEast;
325 		case B_CURSOR_ID_RESIZE_EAST_WEST:
326 			return fCursorResizeEastWest;
327 		case B_CURSOR_ID_RESIZE_NORTH:
328 			return fCursorResizeNorth;
329 		case B_CURSOR_ID_RESIZE_NORTH_EAST:
330 			return fCursorResizeNorthEast;
331 		case B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST:
332 			return fCursorResizeNorthEastSouthWest;
333 		case B_CURSOR_ID_RESIZE_NORTH_SOUTH:
334 			return fCursorResizeNorthSouth;
335 		case B_CURSOR_ID_RESIZE_NORTH_WEST:
336 			return fCursorResizeNorthWest;
337 		case B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST:
338 			return fCursorResizeNorthWestSouthEast;
339 		case B_CURSOR_ID_RESIZE_SOUTH:
340 			return fCursorResizeSouth;
341 		case B_CURSOR_ID_RESIZE_SOUTH_EAST:
342 			return fCursorResizeSouthEast;
343 		case B_CURSOR_ID_RESIZE_SOUTH_WEST:
344 			return fCursorResizeSouthWest;
345 		case B_CURSOR_ID_RESIZE_WEST:
346 			return fCursorResizeWest;
347 		case B_CURSOR_ID_ZOOM_IN:
348 			return fCursorZoomIn;
349 		case B_CURSOR_ID_ZOOM_OUT:
350 			return fCursorZoomOut;
351 
352 		default:
353 			return NULL;
354 	}
355 }
356 
357 
358 /*!	\brief Internal function which finds the cursor with a particular ID
359 	\param token ID of the cursor to find
360 	\return The cursor or NULL if not found
361 */
362 ServerCursor*
363 CursorManager::FindCursor(int32 token)
364 {
365 	if (!Lock())
366 		return NULL;
367 
368 	ServerCursor* cursor;
369 	if (fTokenSpace.GetToken(token, kCursorToken, (void**)&cursor) != B_OK)
370 		cursor = NULL;
371 
372 	Unlock();
373 
374 	return cursor;
375 }
376 
377 
378 /*!	\brief Initializes a predefined system cursor.
379 
380 	This method must only be called in the CursorManager's constructor,
381 	as it may throw exceptions.
382 */
383 void
384 CursorManager::_InitCursor(ServerCursor*& cursorMember,
385 	const uint8* cursorBits, BCursorID id, const BPoint& hotSpot)
386 {
387 	if (cursorBits) {
388 		cursorMember = new ServerCursor(cursorBits, kCursorWidth,
389 			kCursorHeight, kCursorFormat);
390 	} else
391 		cursorMember = new ServerCursor(kCursorNoCursor, 1, 1, kCursorFormat);
392 
393 	cursorMember->SetHotSpot(hotSpot);
394 	AddCursor(cursorMember, id);
395 }
396 
397 
398 void
399 CursorManager::_LoadCursor(ServerCursor*& cursorMember, const CursorSet& set,
400 	BCursorID id)
401 {
402 	ServerCursor* cursor;
403 	if (set.FindCursor(id, &cursor) == B_OK) {
404 		int32 index = fCursorList.IndexOf(cursorMember);
405 		if (index >= 0) {
406 			ServerCursor** items = reinterpret_cast<ServerCursor**>(
407 				fCursorList.Items());
408 			items[index] = cursor;
409 		}
410 		delete cursorMember;
411 		cursorMember = cursor;
412 	}
413 }
414 
415 
416 ServerCursor*
417 CursorManager::_FindCursor(team_id clientTeam, const uint8* cursorData)
418 {
419 	int32 count = fCursorList.CountItems();
420 	for (int32 i = 0; i < count; i++) {
421 		ServerCursor* cursor = (ServerCursor*)fCursorList.ItemAtFast(i);
422 		if (cursor->OwningTeam() == clientTeam
423 			&& cursor->CursorData()
424 			&& memcmp(cursor->CursorData(), cursorData, 68) == 0) {
425 			return cursor;
426 		}
427 	}
428 	return NULL;
429 }
430 
431 
432 void
433 CursorManager::_RemoveCursor(ServerCursor* cursor)
434 {
435 	fCursorList.RemoveItem(cursor);
436 	fTokenSpace.RemoveToken(cursor->fToken);
437 	cursor->fToken = -1;
438 }
439