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
CursorManager()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
~CursorManager()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*
CreateCursor(team_id clientTeam,const uint8 * cursorData)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*
CreateCursor(team_id clientTeam,BRect r,color_space format,int32 flags,BPoint hotspot,int32 bytesPerRow)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
AddCursor(ServerCursor * cursor,int32 token)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
RemoveCursor(ServerCursor * cursor)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
DeleteCursors(team_id team)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
SetCursorSet(const char * path)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*
GetCursor(BCursorID which)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*
FindCursor(int32 token)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
_InitCursor(ServerCursor * & cursorMember,const uint8 * cursorBits,BCursorID id,const BPoint & hotSpot)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
_LoadCursor(ServerCursor * & cursorMember,const CursorSet & set,BCursorID id)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*
_FindCursor(team_id clientTeam,const uint8 * cursorData)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
_RemoveCursor(ServerCursor * cursor)433 CursorManager::_RemoveCursor(ServerCursor* cursor)
434 {
435 fCursorList.RemoveItem(cursor);
436 fTokenSpace.RemoveToken(cursor->fToken);
437 cursor->fToken = -1;
438 }
439