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