2
0

testgdk.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /*
  2. Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
  3. This software is provided 'as-is', without any express or implied
  4. warranty. In no event will the authors be held liable for any damages
  5. arising from the use of this software.
  6. Permission is granted to anyone to use this software for any purpose,
  7. including commercial applications, and to alter it and redistribute it
  8. freely.
  9. */
  10. /* testgdk: Basic tests of using task queue/xbl (with simple drawing) in GDK.
  11. * NOTE: As of June 2022 GDK, login will only work if MicrosoftGame.config is
  12. * configured properly. See README-gdk.md.
  13. */
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <time.h>
  17. #include "SDL_test.h"
  18. #include "SDL_test_common.h"
  19. #include "../src/core/windows/SDL_windows.h"
  20. extern "C" {
  21. #include "../test/testutils.h"
  22. }
  23. #include <XGameRuntime.h>
  24. #define NUM_SPRITES 100
  25. #define MAX_SPEED 1
  26. static SDLTest_CommonState *state;
  27. static int num_sprites;
  28. static SDL_Texture **sprites;
  29. static SDL_bool cycle_color;
  30. static SDL_bool cycle_alpha;
  31. static int cycle_direction = 1;
  32. static int current_alpha = 0;
  33. static int current_color = 0;
  34. static int sprite_w, sprite_h;
  35. static SDL_BlendMode blendMode = SDL_BLENDMODE_BLEND;
  36. int done;
  37. static struct
  38. {
  39. SDL_AudioSpec spec;
  40. Uint8 *sound; /* Pointer to wave data */
  41. Uint32 soundlen; /* Length of wave data */
  42. int soundpos; /* Current play position */
  43. } wave;
  44. static SDL_AudioDeviceID device;
  45. static void
  46. close_audio()
  47. {
  48. if (device != 0) {
  49. SDL_CloseAudioDevice(device);
  50. device = 0;
  51. }
  52. }
  53. /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
  54. static void
  55. quit(int rc)
  56. {
  57. SDL_free(sprites);
  58. close_audio();
  59. SDL_FreeWAV(wave.sound);
  60. SDLTest_CommonQuit(state);
  61. /* If rc is 0, just let main return normally rather than calling exit.
  62. * This allows testing of platforms where SDL_main is required and does meaningful cleanup.
  63. */
  64. if (rc != 0) {
  65. exit(rc);
  66. }
  67. }
  68. static void
  69. open_audio()
  70. {
  71. /* Initialize fillerup() variables */
  72. device = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wave.spec, NULL, 0);
  73. if (!device) {
  74. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError());
  75. SDL_FreeWAV(wave.sound);
  76. quit(2);
  77. }
  78. /* Let the audio run */
  79. SDL_PauseAudioDevice(device, SDL_FALSE);
  80. }
  81. static void
  82. reopen_audio()
  83. {
  84. close_audio();
  85. open_audio();
  86. }
  87. void SDLCALL
  88. fillerup(void *unused, Uint8 *stream, int len)
  89. {
  90. Uint8 *waveptr;
  91. int waveleft;
  92. /* Set up the pointers */
  93. waveptr = wave.sound + wave.soundpos;
  94. waveleft = wave.soundlen - wave.soundpos;
  95. /* Go! */
  96. while (waveleft <= len) {
  97. SDL_memcpy(stream, waveptr, waveleft);
  98. stream += waveleft;
  99. len -= waveleft;
  100. waveptr = wave.sound;
  101. waveleft = wave.soundlen;
  102. wave.soundpos = 0;
  103. }
  104. SDL_memcpy(stream, waveptr, len);
  105. wave.soundpos += len;
  106. }
  107. void
  108. UserLoggedIn(XUserHandle user)
  109. {
  110. HRESULT hr;
  111. char gamertag[128];
  112. hr = XUserGetGamertag(user, XUserGamertagComponent::UniqueModern, sizeof(gamertag), gamertag, NULL);
  113. if (SUCCEEDED(hr)) {
  114. SDL_Log("User logged in: %s", gamertag);
  115. } else {
  116. SDL_Log("[GDK] UserLoggedIn -- XUserGetGamertag failed: 0x%08x.", hr);
  117. }
  118. XUserCloseHandle(user);
  119. }
  120. void
  121. AddUserUICallback(XAsyncBlock *asyncBlock)
  122. {
  123. HRESULT hr;
  124. XUserHandle user = NULL;
  125. hr = XUserAddResult(asyncBlock, &user);
  126. if (SUCCEEDED(hr)) {
  127. uint64_t userId;
  128. hr = XUserGetId(user, &userId);
  129. if (FAILED(hr)) {
  130. /* If unable to get the user ID, it means the account is banned, etc. */
  131. SDL_Log("[GDK] AddUserSilentCallback -- XUserGetId failed: 0x%08x.", hr);
  132. XUserCloseHandle(user);
  133. /* Per the docs, likely should call XUserResolveIssueWithUiAsync here. */
  134. } else {
  135. UserLoggedIn(user);
  136. }
  137. } else {
  138. SDL_Log("[GDK] AddUserUICallback -- XUserAddAsync failed: 0x%08x.", hr);
  139. }
  140. delete asyncBlock;
  141. }
  142. void
  143. AddUserUI()
  144. {
  145. HRESULT hr;
  146. XAsyncBlock *asyncBlock = new XAsyncBlock;
  147. asyncBlock->context = NULL;
  148. asyncBlock->queue = NULL; /* A null queue will use the global process task queue */
  149. asyncBlock->callback = &AddUserUICallback;
  150. hr = XUserAddAsync(XUserAddOptions::None, asyncBlock);
  151. if (FAILED(hr)) {
  152. delete asyncBlock;
  153. SDL_Log("[GDK] AddUserSilent -- failed: 0x%08x", hr);
  154. }
  155. }
  156. void
  157. AddUserSilentCallback(XAsyncBlock *asyncBlock)
  158. {
  159. HRESULT hr;
  160. XUserHandle user = NULL;
  161. hr = XUserAddResult(asyncBlock, &user);
  162. if (SUCCEEDED(hr)) {
  163. uint64_t userId;
  164. hr = XUserGetId(user, &userId);
  165. if (FAILED(hr)) {
  166. /* If unable to get the user ID, it means the account is banned, etc. */
  167. SDL_Log("[GDK] AddUserSilentCallback -- XUserGetId failed: 0x%08x. Trying with UI.", hr);
  168. XUserCloseHandle(user);
  169. AddUserUI();
  170. } else {
  171. UserLoggedIn(user);
  172. }
  173. } else {
  174. SDL_Log("[GDK] AddUserSilentCallback -- XUserAddAsync failed: 0x%08x. Trying with UI.", hr);
  175. AddUserUI();
  176. }
  177. delete asyncBlock;
  178. }
  179. void
  180. AddUserSilent()
  181. {
  182. HRESULT hr;
  183. XAsyncBlock *asyncBlock = new XAsyncBlock;
  184. asyncBlock->context = NULL;
  185. asyncBlock->queue = NULL; /* A null queue will use the global process task queue */
  186. asyncBlock->callback = &AddUserSilentCallback;
  187. hr = XUserAddAsync(XUserAddOptions::AddDefaultUserSilently, asyncBlock);
  188. if (FAILED(hr)) {
  189. delete asyncBlock;
  190. SDL_Log("[GDK] AddUserSilent -- failed: 0x%08x", hr);
  191. }
  192. }
  193. int
  194. LoadSprite(const char *file)
  195. {
  196. int i;
  197. for (i = 0; i < state->num_windows; ++i) {
  198. /* This does the SDL_LoadBMP step repeatedly, but that's OK for test code. */
  199. sprites[i] = LoadTexture(state->renderers[i], file, SDL_TRUE, &sprite_w, &sprite_h);
  200. if (!sprites[i]) {
  201. return -1;
  202. }
  203. if (SDL_SetTextureBlendMode(sprites[i], blendMode) < 0) {
  204. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set blend mode: %s\n", SDL_GetError());
  205. SDL_DestroyTexture(sprites[i]);
  206. return -1;
  207. }
  208. }
  209. /* We're ready to roll. :) */
  210. return 0;
  211. }
  212. void
  213. DrawSprites(SDL_Renderer * renderer, SDL_Texture * sprite)
  214. {
  215. SDL_Rect viewport, temp;
  216. /* Query the sizes */
  217. SDL_RenderGetViewport(renderer, &viewport);
  218. /* Cycle the color and alpha, if desired */
  219. if (cycle_color) {
  220. current_color += cycle_direction;
  221. if (current_color < 0) {
  222. current_color = 0;
  223. cycle_direction = -cycle_direction;
  224. }
  225. if (current_color > 255) {
  226. current_color = 255;
  227. cycle_direction = -cycle_direction;
  228. }
  229. SDL_SetTextureColorMod(sprite, 255, (Uint8) current_color,
  230. (Uint8) current_color);
  231. }
  232. if (cycle_alpha) {
  233. current_alpha += cycle_direction;
  234. if (current_alpha < 0) {
  235. current_alpha = 0;
  236. cycle_direction = -cycle_direction;
  237. }
  238. if (current_alpha > 255) {
  239. current_alpha = 255;
  240. cycle_direction = -cycle_direction;
  241. }
  242. SDL_SetTextureAlphaMod(sprite, (Uint8) current_alpha);
  243. }
  244. /* Draw a gray background */
  245. SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
  246. SDL_RenderClear(renderer);
  247. /* Test points */
  248. SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
  249. SDL_RenderDrawPoint(renderer, 0, 0);
  250. SDL_RenderDrawPoint(renderer, viewport.w-1, 0);
  251. SDL_RenderDrawPoint(renderer, 0, viewport.h-1);
  252. SDL_RenderDrawPoint(renderer, viewport.w-1, viewport.h-1);
  253. /* Test horizontal and vertical lines */
  254. SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
  255. SDL_RenderDrawLine(renderer, 1, 0, viewport.w-2, 0);
  256. SDL_RenderDrawLine(renderer, 1, viewport.h-1, viewport.w-2, viewport.h-1);
  257. SDL_RenderDrawLine(renderer, 0, 1, 0, viewport.h-2);
  258. SDL_RenderDrawLine(renderer, viewport.w-1, 1, viewport.w-1, viewport.h-2);
  259. /* Test fill and copy */
  260. SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
  261. temp.x = 1;
  262. temp.y = 1;
  263. temp.w = sprite_w;
  264. temp.h = sprite_h;
  265. SDL_RenderFillRect(renderer, &temp);
  266. SDL_RenderCopy(renderer, sprite, NULL, &temp);
  267. temp.x = viewport.w-sprite_w-1;
  268. temp.y = 1;
  269. temp.w = sprite_w;
  270. temp.h = sprite_h;
  271. SDL_RenderFillRect(renderer, &temp);
  272. SDL_RenderCopy(renderer, sprite, NULL, &temp);
  273. temp.x = 1;
  274. temp.y = viewport.h-sprite_h-1;
  275. temp.w = sprite_w;
  276. temp.h = sprite_h;
  277. SDL_RenderFillRect(renderer, &temp);
  278. SDL_RenderCopy(renderer, sprite, NULL, &temp);
  279. temp.x = viewport.w-sprite_w-1;
  280. temp.y = viewport.h-sprite_h-1;
  281. temp.w = sprite_w;
  282. temp.h = sprite_h;
  283. SDL_RenderFillRect(renderer, &temp);
  284. SDL_RenderCopy(renderer, sprite, NULL, &temp);
  285. /* Test diagonal lines */
  286. SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
  287. SDL_RenderDrawLine(renderer, sprite_w, sprite_h,
  288. viewport.w-sprite_w-2, viewport.h-sprite_h-2);
  289. SDL_RenderDrawLine(renderer, viewport.w-sprite_w-2, sprite_h,
  290. sprite_w, viewport.h-sprite_h-2);
  291. /* Update the screen! */
  292. SDL_RenderPresent(renderer);
  293. }
  294. void
  295. loop()
  296. {
  297. int i;
  298. SDL_Event event;
  299. /* Check for events */
  300. while (SDL_PollEvent(&event)) {
  301. if (event.type == SDL_KEYDOWN && !event.key.repeat) {
  302. SDL_Log("Initial SDL_KEYDOWN: %s", SDL_GetScancodeName(event.key.keysym.scancode));
  303. }
  304. #if defined(__XBOXONE__) || defined(__XBOXSERIES__)
  305. /* On Xbox, ignore the keydown event because the features aren't supported */
  306. if (event.type != SDL_KEYDOWN) {
  307. SDLTest_CommonEvent(state, &event, &done);
  308. }
  309. #else
  310. SDLTest_CommonEvent(state, &event, &done);
  311. #endif
  312. }
  313. for (i = 0; i < state->num_windows; ++i) {
  314. if (state->windows[i] == NULL) {
  315. continue;
  316. }
  317. DrawSprites(state->renderers[i], sprites[i]);
  318. }
  319. }
  320. int
  321. main(int argc, char *argv[])
  322. {
  323. int i;
  324. const char *icon = "icon.bmp";
  325. char *soundname = NULL;
  326. /* Initialize parameters */
  327. num_sprites = NUM_SPRITES;
  328. /* Initialize test framework */
  329. state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO | SDL_INIT_AUDIO);
  330. if (!state) {
  331. return 1;
  332. }
  333. for (i = 1; i < argc;) {
  334. int consumed;
  335. consumed = SDLTest_CommonArg(state, i);
  336. if (consumed == 0) {
  337. consumed = -1;
  338. if (SDL_strcasecmp(argv[i], "--blend") == 0) {
  339. if (argv[i + 1]) {
  340. if (SDL_strcasecmp(argv[i + 1], "none") == 0) {
  341. blendMode = SDL_BLENDMODE_NONE;
  342. consumed = 2;
  343. } else if (SDL_strcasecmp(argv[i + 1], "blend") == 0) {
  344. blendMode = SDL_BLENDMODE_BLEND;
  345. consumed = 2;
  346. } else if (SDL_strcasecmp(argv[i + 1], "add") == 0) {
  347. blendMode = SDL_BLENDMODE_ADD;
  348. consumed = 2;
  349. } else if (SDL_strcasecmp(argv[i + 1], "mod") == 0) {
  350. blendMode = SDL_BLENDMODE_MOD;
  351. consumed = 2;
  352. } else if (SDL_strcasecmp(argv[i + 1], "sub") == 0) {
  353. blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_SUBTRACT, SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_SUBTRACT);
  354. consumed = 2;
  355. }
  356. }
  357. } else if (SDL_strcasecmp(argv[i], "--cyclecolor") == 0) {
  358. cycle_color = SDL_TRUE;
  359. consumed = 1;
  360. } else if (SDL_strcasecmp(argv[i], "--cyclealpha") == 0) {
  361. cycle_alpha = SDL_TRUE;
  362. consumed = 1;
  363. } else if (SDL_isdigit(*argv[i])) {
  364. num_sprites = SDL_atoi(argv[i]);
  365. consumed = 1;
  366. } else if (argv[i][0] != '-') {
  367. icon = argv[i];
  368. consumed = 1;
  369. }
  370. }
  371. if (consumed < 0) {
  372. static const char *options[] = {
  373. "[--blend none|blend|add|mod]",
  374. "[--cyclecolor]",
  375. "[--cyclealpha]",
  376. "[num_sprites]",
  377. "[icon.bmp]",
  378. NULL };
  379. SDLTest_CommonLogUsage(state, argv[0], options);
  380. quit(1);
  381. }
  382. i += consumed;
  383. }
  384. if (!SDLTest_CommonInit(state)) {
  385. quit(2);
  386. }
  387. /* Create the windows, initialize the renderers, and load the textures */
  388. sprites =
  389. (SDL_Texture **) SDL_malloc(state->num_windows * sizeof(*sprites));
  390. if (!sprites) {
  391. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!\n");
  392. quit(2);
  393. }
  394. for (i = 0; i < state->num_windows; ++i) {
  395. SDL_Renderer *renderer = state->renderers[i];
  396. SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
  397. SDL_RenderClear(renderer);
  398. }
  399. if (LoadSprite(icon) < 0) {
  400. quit(2);
  401. }
  402. soundname = GetResourceFilename(argc > 1 ? argv[1] : NULL, "sample.wav");
  403. if (!soundname) {
  404. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s\n", SDL_GetError());
  405. quit(1);
  406. }
  407. /* Load the wave file into memory */
  408. if (SDL_LoadWAV(soundname, &wave.spec, &wave.sound, &wave.soundlen) == NULL) {
  409. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", soundname, SDL_GetError());
  410. quit(1);
  411. }
  412. wave.spec.callback = fillerup;
  413. /* Show the list of available drivers */
  414. SDL_Log("Available audio drivers:");
  415. for (i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
  416. SDL_Log("%i: %s", i, SDL_GetAudioDriver(i));
  417. }
  418. SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());
  419. open_audio();
  420. /* Main render loop */
  421. done = 0;
  422. /* Try to add the default user silently */
  423. AddUserSilent();
  424. while (!done) {
  425. loop();
  426. }
  427. quit(0);
  428. SDL_free(soundname);
  429. return 0;
  430. }
  431. /* vi: set ts=4 sw=4 expandtab: */