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