Atlas - testcustomcursor.c
Home / ext / SDL / test Lines: 1 | Size: 17914 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11*/ 12 13#include <SDL3/SDL_test_common.h> 14#include <SDL3/SDL_main.h> 15 16#ifdef SDL_PLATFORM_EMSCRIPTEN 17#include <emscripten/emscripten.h> 18#endif 19 20#include <stdlib.h> 21 22enum 23{ 24 CUSTOM_CURSOR_ARGUMENTS = -1, 25 CUSTOM_CURSOR_ARROW = -2, 26 CUSTOM_CURSOR_CROSS = -3, 27 CUSTOM_ANIMATED_CURSOR = -4, 28 CUSTOM_ANIMATED_CURSOR_ONE_SHOT = -5, 29}; 30 31/* Stolen from the mailing list */ 32/* Creates a new mouse cursor from an XPM */ 33 34/* XPM */ 35static const char *arrow[] = { 36 /* width height num_colors chars_per_pixel */ 37 " 32 32 3 1", 38 /* colors */ 39 "X c #000000", 40 ". c #ffffff", 41 " c None", 42 /* pixels */ 43 "X ", 44 "XX ", 45 "X.X ", 46 "X..X ", 47 "X...X ", 48 "X....X ", 49 "X.....X ", 50 "X......X ", 51 "X.......X ", 52 "X........X ", 53 "X.....XXXXX ", 54 "X..X..X ", 55 "X.X X..X ", 56 "XX X..X ", 57 "X X..X ", 58 " X..X ", 59 " X..X ", 60 " X..X ", 61 " XX ", 62 " ", 63 " ", 64 " ", 65 " ", 66 " ", 67 " ", 68 " ", 69 " ", 70 " ", 71 " ", 72 " ", 73 " ", 74 " ", 75 "0,0" 76}; 77 78static const char *cross[] = { 79 /* width height num_colors chars_per_pixel */ 80 " 32 32 3 1", 81 /* colors */ 82 "o c #000000", 83 ". c #ffffff", 84 " c None", 85 /* pixels */ 86 " ", 87 " ", 88 " ", 89 " ", 90 " oo ", 91 " oo ", 92 " oo ", 93 " oo ", 94 " oo ", 95 " oo ", 96 " oo ", 97 " oo ", 98 " oo ", 99 " oo ", 100 " oo ", 101 " oooooooooooooooooooooooo ", 102 " oooooooooooooooooooooooo ", 103 " oo ", 104 " oo ", 105 " oo ", 106 " oo ", 107 " oo ", 108 " oo ", 109 " oo ", 110 " oo ", 111 " oo ", 112 " oo ", 113 " oo ", 114 " ", 115 " ", 116 " ", 117 " ", 118 "16,16" 119}; 120 121static SDL_Surface *load_image_file(const char *file) 122{ 123 SDL_Surface *surface = SDL_LoadSurface(file); 124 if (surface) { 125 if (SDL_GetSurfacePalette(surface)) { 126 const Uint8 bpp = SDL_BITSPERPIXEL(surface->format); 127 const Uint8 mask = (1 << bpp) - 1; 128 if (SDL_PIXELORDER(surface->format) == SDL_BITMAPORDER_4321) 129 SDL_SetSurfaceColorKey(surface, 1, (*(Uint8 *)surface->pixels) & mask); 130 else 131 SDL_SetSurfaceColorKey(surface, 1, ((*(Uint8 *)surface->pixels) >> (8 - bpp)) & mask); 132 } else { 133 switch (SDL_BITSPERPIXEL(surface->format)) { 134 case 15: 135 SDL_SetSurfaceColorKey(surface, 1, (*(Uint16 *)surface->pixels) & 0x00007FFF); 136 break; 137 case 16: 138 SDL_SetSurfaceColorKey(surface, 1, *(Uint16 *)surface->pixels); 139 break; 140 case 24: 141 SDL_SetSurfaceColorKey(surface, 1, (*(Uint32 *)surface->pixels) & 0x00FFFFFF); 142 break; 143 case 32: 144 SDL_SetSurfaceColorKey(surface, 1, *(Uint32 *)surface->pixels); 145 break; 146 } 147 } 148 } 149 return surface; 150} 151 152static SDL_Surface *load_image(const char *file) 153{ 154 SDL_Surface *surface = load_image_file(file); 155 if (surface) { 156 /* Add a 2x version of this image, if available */ 157 SDL_Surface *surface2x = NULL; 158 const char *ext = SDL_strrchr(file, '.'); 159 size_t len = SDL_strlen(file) + 2 + 1; 160 char *file2x = (char *)SDL_malloc(len); 161 if (file2x) { 162 SDL_strlcpy(file2x, file, len); 163 if (ext) { 164 SDL_memcpy(file2x + (ext - file), "2x", 3); 165 SDL_strlcat(file2x, ext, len); 166 } else { 167 SDL_strlcat(file2x, "2x", len); 168 } 169 surface2x = load_image_file(file2x); 170 SDL_free(file2x); 171 } 172 if (surface2x) { 173 SDL_AddSurfaceAlternateImage(surface, surface2x); 174 SDL_DestroySurface(surface2x); 175 } 176 } 177 return surface; 178} 179 180static SDL_Cursor *init_color_cursor(char **files, int num_frames) 181{ 182 SDL_Cursor *cursor = NULL; 183 SDL_CursorFrameInfo *frames = (SDL_CursorFrameInfo *)SDL_calloc(num_frames, sizeof(SDL_CursorFrameInfo)); 184 int i; 185 186 for (i = 0; i < num_frames; i++) { 187 SDL_Surface *img = load_image(files[i]); 188 if (!img) { 189 SDL_LogWarn(SDL_LOG_CATEGORY_TEST, "Failed to load %s", files[i]); 190 goto cleanup; 191 } 192 frames[i].surface = img; 193 if (i > 0) { 194 frames[i].duration = 150; 195 } 196 } 197 198 if (num_frames == 1) { 199 cursor = SDL_CreateColorCursor(frames[0].surface, 0, 0); 200 } else { 201 cursor = SDL_CreateAnimatedCursor(frames, num_frames, 0, 0); 202 } 203 204cleanup: 205 for (i = 0; i < num_frames; ++i) { 206 SDL_DestroySurface(frames[i].surface); 207 } 208 SDL_free(frames); 209 210 return cursor; 211} 212 213static SDL_Cursor *init_system_cursor(const char *image[]) 214{ 215 int i, row, col; 216 Uint8 data[4 * 32]; 217 Uint8 mask[4 * 32]; 218 int hot_x = 0; 219 int hot_y = 0; 220 221 i = -1; 222 for (row = 0; row < 32; ++row) { 223 for (col = 0; col < 32; ++col) { 224 if (col % 8) { 225 data[i] <<= 1; 226 mask[i] <<= 1; 227 } else { 228 ++i; 229 data[i] = mask[i] = 0; 230 } 231 switch (image[4 + row][col]) { 232 case 'X': 233 data[i] |= 0x01; 234 mask[i] |= 0x01; 235 break; 236 case '.': 237 mask[i] |= 0x01; 238 break; 239 case 'o': 240 data[i] |= 0x01; 241 break; 242 case ' ': 243 break; 244 } 245 } 246 } 247 (void)SDL_sscanf(image[4 + row], "%d,%d", &hot_x, &hot_y); 248 return SDL_CreateCursor(data, mask, 32, 32, hot_x, hot_y); 249} 250 251static SDL_Cursor *init_animated_cursor(const char *image[], bool oneshot) 252{ 253 int row, col; 254 SDL_Surface *surface, *invsurface; 255 Uint32 *pixels, *invpixels; 256 SDL_CursorFrameInfo frames[6]; 257 int hot_x = 0; 258 int hot_y = 0; 259 260 surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888); 261 if (!surface) { 262 return NULL; 263 } 264 265 invsurface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888); 266 if (!invsurface) { 267 SDL_DestroySurface(surface); 268 return NULL; 269 } 270 271 for (row = 4; row < 36; ++row) { 272 pixels = (Uint32 *)((Uint8 *)surface->pixels + ((row - 4) * surface->pitch)); 273 invpixels = (Uint32 *)((Uint8 *)invsurface->pixels + ((row - 4) * surface->pitch)); 274 for (col = 0; col < 32; ++col) { 275 switch (image[row][col]) { 276 case 'X': 277 pixels[col] = 0xFFFFFFFF; 278 invpixels[col] = 0xFF000000; 279 break; 280 case '.': 281 pixels[col] = 0xFF000000; 282 invpixels[col] = 0xFFFFFFFF; 283 break; 284 case ' ': 285 pixels[col] = 0; 286 invpixels[col] = 0; 287 break; 288 } 289 } 290 } 291 292 int frame_count = 2; 293 294 frames[0].surface = surface; 295 frames[0].duration = 100; 296 297 frames[1].surface = invsurface; 298 frames[1].duration = 100; 299 300 if (oneshot) { 301 frames[2].surface = surface; 302 frames[2].duration = 200; 303 304 frames[3].surface = invsurface; 305 frames[3].duration = 300; 306 307 frames[4].surface = surface; 308 frames[4].duration = 400; 309 310 frames[5].surface = invsurface; 311 frames[5].duration = 0; 312 313 frame_count = 6; 314 } 315 316 SDL_Cursor *cursor = SDL_CreateAnimatedCursor(frames, frame_count, hot_x, hot_y); 317 318 SDL_DestroySurface(surface); 319 SDL_DestroySurface(invsurface); 320 321 return cursor; 322} 323 324static SDLTest_CommonState *state; 325static int done; 326static SDL_Cursor *cursors[5 + SDL_SYSTEM_CURSOR_COUNT]; 327static SDL_SystemCursor cursor_types[5 + SDL_SYSTEM_CURSOR_COUNT]; 328static int num_cursors; 329static int current_cursor; 330static bool current_cursor_valid; 331static bool show_cursor; 332 333/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ 334static void 335quit(int rc) 336{ 337 SDLTest_CommonQuit(state); 338 /* Let 'main()' return normally */ 339 if (rc != 0) { 340 exit(rc); 341 } 342} 343 344static const char *get_active_cursor_name() 345{ 346 // if (!current_cursor_valid) { 347 // return ""; 348 // } 349 switch ((int)cursor_types[current_cursor]) { 350 case CUSTOM_CURSOR_ARGUMENTS: 351 return "Custom cursor (arguments)"; 352 case CUSTOM_CURSOR_ARROW: 353 return "Custom cursor (arrow)"; 354 case CUSTOM_CURSOR_CROSS: 355 return "Custom cursor (cross)"; 356 case CUSTOM_ANIMATED_CURSOR_ONE_SHOT: 357 return "Animated custom cursor (one-shot)"; 358 case CUSTOM_ANIMATED_CURSOR: 359 return "Animated custom cursor"; 360 case SDL_SYSTEM_CURSOR_DEFAULT: 361 return "Default"; 362 case SDL_SYSTEM_CURSOR_TEXT: 363 return "Text"; 364 case SDL_SYSTEM_CURSOR_WAIT: 365 return "Wait"; 366 case SDL_SYSTEM_CURSOR_CROSSHAIR: 367 return "Crosshair"; 368 case SDL_SYSTEM_CURSOR_PROGRESS: 369 return "Progress: Small wait cursor (or Wait if not available)"; 370 case SDL_SYSTEM_CURSOR_NWSE_RESIZE: 371 return "Double arrow pointing northwest and southeast"; 372 case SDL_SYSTEM_CURSOR_NESW_RESIZE: 373 return "Double arrow pointing northeast and southwest"; 374 case SDL_SYSTEM_CURSOR_EW_RESIZE: 375 return "Double arrow pointing west and east"; 376 case SDL_SYSTEM_CURSOR_NS_RESIZE: 377 return "Double arrow pointing north and south"; 378 case SDL_SYSTEM_CURSOR_MOVE: 379 return "Move: Four pointed arrow pointing north, south, east, and west"; 380 case SDL_SYSTEM_CURSOR_NOT_ALLOWED: 381 return "Not Allowed: Slashed circle or crossbones"; 382 case SDL_SYSTEM_CURSOR_POINTER: 383 return "Pointer: Hand"; 384 case SDL_SYSTEM_CURSOR_NW_RESIZE: 385 return "Window resize top-left"; 386 case SDL_SYSTEM_CURSOR_N_RESIZE: 387 return "Window resize top"; 388 case SDL_SYSTEM_CURSOR_NE_RESIZE: 389 return "Window resize top-right"; 390 case SDL_SYSTEM_CURSOR_E_RESIZE: 391 return "Window resize right"; 392 case SDL_SYSTEM_CURSOR_SE_RESIZE: 393 return "Window resize bottom-right"; 394 case SDL_SYSTEM_CURSOR_S_RESIZE: 395 return "Window resize bottom"; 396 case SDL_SYSTEM_CURSOR_SW_RESIZE: 397 return "Window resize bottom-left"; 398 case SDL_SYSTEM_CURSOR_W_RESIZE: 399 return "Window resize left"; 400 default: 401 return "UNKNOWN CURSOR TYPE, FIX THIS PROGRAM."; 402 } 403} 404 405static void loop(void) 406{ 407 int i; 408 SDL_Event event; 409 /* Check for events */ 410 while (SDL_PollEvent(&event)) { 411 SDLTest_CommonEvent(state, &event, &done); 412 if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { 413 if (event.button.button == SDL_BUTTON_LEFT) { 414 if (num_cursors == 0) { 415 continue; 416 } 417 418 current_cursor_valid = true; 419 ++current_cursor; 420 if (current_cursor == num_cursors) { 421 current_cursor = 0; 422 } 423 424 SDL_SetCursor(cursors[current_cursor]); 425 426 SDL_Log("%s", get_active_cursor_name()); 427 428 } else { 429 show_cursor = !show_cursor; 430 if (show_cursor) { 431 SDL_ShowCursor(); 432 } else { 433 SDL_HideCursor(); 434 } 435 } 436 } 437 } 438 439 for (i = 0; i < state->num_windows; ++i) { 440 SDL_Renderer *renderer = state->renderers[i]; 441 SDL_FRect rect; 442 int x, y, row; 443 int window_w = 0, window_h = 0; 444 const float scale = SDL_GetWindowPixelDensity(state->windows[i]); 445 446 SDL_GetWindowSizeInPixels(state->windows[i], &window_w, &window_h); 447 rect.w = 128.0f * scale; 448 rect.h = 128.0f * scale; 449 for (y = 0, row = 0; y < window_h; y += (int)rect.h, ++row) { 450 bool black = ((row % 2) == 0) ? true : false; 451 for (x = 0; x < window_w; x += (int)rect.w) { 452 rect.x = (float)x; 453 rect.y = (float)y; 454 455 if (black) { 456 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF); 457 } else { 458 SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); 459 } 460 SDL_RenderFillRect(renderer, &rect); 461 462 black = !black; 463 } 464 } 465 SDL_SetRenderDrawColor(renderer, 0xff, 0x00, 0x00, 0xFF); 466 SDL_RenderDebugText(renderer, 0.0f, 0.0f, get_active_cursor_name()); 467 SDL_RenderPresent(renderer); 468 } 469#ifdef SDL_PLATFORM_EMSCRIPTEN 470 if (done) { 471 emscripten_cancel_main_loop(); 472 } 473#endif 474} 475 476int main(int argc, char *argv[]) 477{ 478 int i; 479 char **frames = NULL; 480 int num_frames = -1; 481 SDL_Cursor *cursor; 482 483 /* Initialize test framework */ 484 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); 485 if (!state) { 486 return 1; 487 } 488 489 for (i = 1; i < argc;) { 490 int consumed; 491 492 consumed = SDLTest_CommonArg(state, i); 493 if (consumed == 0) { 494 if (SDL_strcmp(argv[i], "--frames") == 0 && frames == NULL) { 495 num_frames = 0; 496 while (i + 1 + num_frames < argc && argv[i + 1 + num_frames][0] != '-') { 497 num_frames += 1; 498 } 499 frames = &argv[i + 1]; 500 consumed = num_frames + 1; 501 } 502 } 503 if (consumed <= 0) { 504 static const char *options[] = { "[--frames frame0.bmp [frame1.bmp [frame2.bmp ...]]]", NULL}; 505 SDLTest_CommonLogUsage(state, argv[0], options); 506 quit(1); 507 } 508 i += consumed; 509 } 510 511 if (!SDLTest_CommonInit(state)) { 512 quit(2); 513 } 514 515 num_cursors = 0; 516 517 if (num_frames > 0) { 518 /* Only load the first file in the list for the icon. */ 519 SDL_Surface *icon = load_image(frames[0]); 520 if (icon) { 521 for (i = 0; i < state->num_windows; ++i) { 522 SDL_SetWindowIcon(state->windows[i], icon); 523 } 524 SDL_DestroySurface(icon); 525 } 526 527 cursor = init_color_cursor(frames, num_frames); 528 if (cursor) { 529 cursors[num_cursors] = cursor; 530 cursor_types[num_cursors] = (SDL_SystemCursor)CUSTOM_CURSOR_ARGUMENTS; 531 num_cursors++; 532 } 533 } 534 535 cursor = init_system_cursor(arrow); 536 if (cursor) { 537 cursors[num_cursors] = cursor; 538 cursor_types[num_cursors] = (SDL_SystemCursor)CUSTOM_CURSOR_ARROW; 539 num_cursors++; 540 } 541 542 cursor = init_system_cursor(cross); 543 if (cursor) { 544 cursors[num_cursors] = cursor; 545 cursor_types[num_cursors] = (SDL_SystemCursor)CUSTOM_CURSOR_CROSS; 546 num_cursors++; 547 } 548 549 cursor = init_animated_cursor(arrow, false); 550 if (cursor) { 551 cursors[num_cursors] = cursor; 552 cursor_types[num_cursors] = (SDL_SystemCursor)CUSTOM_ANIMATED_CURSOR; 553 num_cursors++; 554 } 555 556 cursor = init_animated_cursor(arrow, true); 557 if (cursor) { 558 cursors[num_cursors] = cursor; 559 cursor_types[num_cursors] = (SDL_SystemCursor)CUSTOM_ANIMATED_CURSOR_ONE_SHOT; 560 num_cursors++; 561 } 562 563 for (i = 0; i < SDL_SYSTEM_CURSOR_COUNT; ++i) { 564 cursor = SDL_CreateSystemCursor((SDL_SystemCursor)i); 565 if (cursor) { 566 cursors[num_cursors] = cursor; 567 cursor_types[num_cursors] = i; 568 num_cursors++; 569 } 570 } 571 572 if (num_cursors > 0) { 573 SDL_SetCursor(cursors[0]); 574 } 575 576 show_cursor = SDL_CursorVisible(); 577 578 /* Main render loop */ 579 done = 0; 580#ifdef SDL_PLATFORM_EMSCRIPTEN 581 emscripten_set_main_loop(loop, 0, 1); 582#else 583 while (!done) { 584 loop(); 585 } 586#endif 587 588 for (i = 0; i < num_cursors; ++i) { 589 SDL_DestroyCursor(cursors[i]); 590 } 591 quit(0); 592 593 /* keep the compiler happy ... */ 594 return 0; 595} 596[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.