├── .gitignore ├── README.md ├── color_picker.png ├── debuginator.gif ├── plugins └── stingray │ └── the_debuginator_plugin │ ├── CMakeLists.txt │ ├── c_api_the_debuginator.cpp │ ├── c_api_the_debuginator.h │ ├── copy_latest_debuginator_h.bat │ ├── resource.h │ ├── the_debuginator.h │ ├── the_debuginator_plugin.cpp │ └── the_debuginator_plugin.rc.in ├── tests ├── 3rdparty │ ├── SDL_GameControllerDB-master │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── check.py │ │ └── gamecontrollerdb.txt │ ├── Simple-SDL2-Audio │ │ ├── README.md │ │ └── src │ │ │ ├── simple_audio.c │ │ │ └── simple_audio.h │ ├── kenney_interfacesounds │ │ ├── License.txt │ │ ├── bong_001.wav │ │ ├── pluck_001.wav │ │ ├── pluck_002.wav │ │ └── select_007.wav │ └── liberation-fonts-ttf-2.00.1 │ │ ├── AUTHORS │ │ ├── ChangeLog │ │ ├── LICENSE │ │ ├── LiberationMono-Bold.ttf │ │ ├── LiberationMono-BoldItalic.ttf │ │ ├── LiberationMono-Italic.ttf │ │ ├── LiberationMono-Regular.ttf │ │ ├── LiberationSans-Bold.ttf │ │ ├── LiberationSans-BoldItalic.ttf │ │ ├── LiberationSans-Italic.ttf │ │ ├── LiberationSans-Regular.ttf │ │ ├── LiberationSerif-Bold.ttf │ │ ├── LiberationSerif-BoldItalic.ttf │ │ ├── LiberationSerif-Italic.ttf │ │ ├── LiberationSerif-Regular.ttf │ │ ├── README │ │ └── TODO ├── sdl │ ├── demo.cpp │ ├── demo.h │ ├── game.cpp │ ├── game.h │ ├── gui.cpp │ ├── gui.h │ ├── main.cpp │ ├── sdl.vcxproj │ └── sdl.vcxproj.filters ├── sk5.jpg ├── sk5.txt ├── the-debuginator.sln ├── the-debuginator_solution_suppressions.cfg ├── unittest │ ├── unittest.c │ ├── unittest.vcxproj │ └── unittest.vcxproj.filters └── zig-minimal │ ├── build.bat │ └── src │ ├── build.zig │ ├── c.zig │ ├── main.zig │ ├── the_debuginator_wrapper.c │ └── the_debuginator_wrapper.h ├── the_debuginator.h ├── the_debuginator_queue.h └── tools └── stub_gen.py /.gitignore: -------------------------------------------------------------------------------- 1 | tests/Build/ 2 | *.VC.db 3 | *.VC.VC.opendb 4 | tests/.vs/ 5 | tests/zig/src/zig-cache 6 | tests/zig/src/out 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Debuginator 2 | 3 | A juicy feature-packed debug menu intended for games. 4 | 5 | It's conceptually based on the debug menu I wrote at Fatshark for **Warhammer: End Times – Vermintide**, where it was - and still is! - used extensively by everyone. Quote by one of the game designers during the Christmas party: *"The debug menu saved man-years for us"*. He was drunk. But not wrong. 6 | 7 | The API is designed to have what I think is just the right amount of responsibilities. The application reads input and uses that to control the menu, and the menu sends back what it wants the application to draw. 8 | 9 | It's nearly feature complete now - unless someone has any cool requests - and as far as I know, bug free. 10 | 11 | I'd really like to add lots of nice GIFs but they get quite large and I'm not keen on forcing people to download megabytes just to view a GitHub project's README. So here's just one with a low resolution and framerate: 12 | 13 | GIF demo. Click to go to youtube demo! 15 | 16 | There's a fancier demo on Youtube: https://youtu.be/8lA5HQik2wo 17 | 18 | ## Features 19 | 20 | These are subject to change and in various level of implementedness (including currently not at all). 21 | 22 | - :heavy_check_mark: Done. 23 | - :factory: Work in progress 24 | - :small_blue_diamond: Some parts done, other parts on hold. 25 | - :red_circle: Not started. 26 | 27 | ### :heavy_check_mark: STB-style single header library 28 | 29 | Though tests, examples and plugins are in separate files/folders/projects. 30 | 31 | ### :heavy_check_mark: Batteries included 32 | 33 | Think of the_debuginator.h less as a library and more as a *thing* you plug in to get a nice, juicy, feature-packed debug menu. 34 | 35 | Where it makes sense, I've added functionality for extending the menu with your own custom features. 36 | 37 | ### :heavy_check_mark: Politely coded 38 | 39 | It's written in **C99**, to make it easy to add to any project on any platform. In one case I use an anonymous union (I pragma away the warning), so if your compiler doesn't support that, I recommend forking and fixing. I just like the convenience. 40 | 41 | **No globals or static variables**, in case you want to have it in a plugin and reload (or instantiate more than one!). 42 | 43 | **Built using maximum warning levels and warnings as errors**. I use a few #pragmas to ignore warnings I don't think is problematic. Define `DEBUGINATOR_ENABLE_WARNINGS` if you want to handle that manually. 44 | 45 | **Statically analyzed with Cppcheck.** Again, I suppress a few issues that I think aren't problematic. You can check `the-debuginator_solution_suppressions.cfg` if you're interested. 46 | 47 | **It doesn't allocate any memory.** You pass in a buffer at creation and that's what it'll use internally. 48 | 49 | **The library has no dependencies.** It will include some standard headers but only if you don't provide overrides. (For the SDL reference demo, a font is included, and to build you need SDL 2 and SDL TTF.) 50 | 51 | **Multi-platform.** Tested on MSVC 2017/Windows 10 and on the PS4 (so I've been told), and builds on Clang/Linux. 52 | 53 | ### :heavy_check_mark: Scrollable 54 | 55 | Can handle any number of items, the "hot" one will be centered (ish) smoothly. 56 | 57 | Also supports scrolling without changing the hot item, for example using the mouse wheel, touch, or a gamepad stick. 58 | 59 | ### :heavy_check_mark: Performant 60 | 61 | Tested with 10000 menu items with no noticable hit on my laptop running a debug build. That's good enough for me! Hey, it's a debug menu. 62 | 63 | Memory-wise I think you can estimate about 200 bytes, maybe 300, for a typical item in the menu. I've opted to add features, improve speed, and make the code easier to read, at the cost of memory usage. It wouldn't be too hard to optimize it a bit in this regard if you're strained for memory, but it's left as an exercise for the reader. 64 | 65 | Btw, 200 bytes doesn't sound like much, but if you do have 10000 items - perhaps a few for each enemy and item spawned in your level - that means you'll need to spare 2mb of memory, which could potentially be an issue on consoles, phones, Raspberries, and so on. Just bear it in mind. 66 | 67 | ### :heavy_check_mark: Search filter 68 | 69 | Quickly and easily filter the items to find the one you want. Uses a fuzzy search mechanism to allow a user who isn't entirely sure what something is called to find it quickly. Adding a space to the search makes the filter run in "exact" mode; each part of the filter must be matched as-is. 70 | 71 | Check my post on this for a bit of details about it: https://medium.com/@Srekel/implementing-a-fuzzy-search-algorithm-for-the-debuginator-cacc349e6c55 72 | 73 | ### :heavy_check_mark: Save/Load of settings 74 | 75 | So you start up with the settings you had when you exited. Simple interface, application needs to handle the actual I/O. 76 | 77 | ### :heavy_check_mark: Dynamic add and remove of items 78 | 79 | So you could, for example, have items that are only available when you are in the game's main menu, or have one item for each currently spawned enemy in the game. 80 | 81 | ### :heavy_check_mark: Left or right aligned 82 | 83 | Because some games already have other important stuff on the left side of the screen. 84 | 85 | Hey, you could even have one instance of The Debuginator on the left and another on the right. 86 | 87 | ### :heavy_check_mark: Hierarchical 88 | 89 | Put things in folders in folders in folders. They can be expanded and collapsed. 90 | 91 | Folder states are persisted automatically if you implement the save interface. 92 | This is useful when you have a lot of items but different users are only interested in different subsets of them. 93 | 94 | ### :heavy_check_mark: Custom item editors 95 | 96 | Different editors for different types of items, and support for users adding their own. 97 | 98 | ### :heavy_check_mark: Input 99 | 100 | The API for manouvering the menu is agnostic regarding keyboard/gamepad. It's handled at application layer, though guidelines for how to bind keys exist - see the demo. 101 | 102 | Also supports mouse and touch input. 103 | 104 | ### :heavy_check_mark: Presets 105 | 106 | Activate one item to activate a number of other ones. Useful for if a user tends to reset her debug settings but has a lot of them she often wants to enable. Or if your QA should play under certain circumstances. Or if you have a few settings that is just really nice to always have enabled, for new devs. 107 | 108 | ### :heavy_check_mark: Hotkeys 109 | 110 | Press a key when debug menu is active to assign the current item/value. Press it again when the menu is closed to toggle/set the value. 111 | 112 | Hot keys are persisted automatically if you implement the save interface. 113 | 114 | ### :heavy_check_mark: Default values 115 | 116 | Different colors for when the current value of an item is different from it's defined *default value* makes it clearer to the developer that she is or isn't working under the game's standard conditions. 117 | 118 | ### :heavy_check_mark: Unit tests 119 | 120 | I used these extensively in the beginning of the project but the fun and usefulness of keeping them up to date is gone. They are surpassed by the SDL reference demo. I'm conflicted as whether to mark it as a done feature but yeah, for all intents and purposes, they are done. 121 | 122 | ### :heavy_check_mark: Nice look & feel 123 | 124 | Nice default color scheme, multiple themes to choose from, unique "editors" for different types of data, smooth animations, juicy feedback. 125 | 126 | Yes, it's important. 127 | 128 | ### :heavy_check_mark: Reference implementation 129 | 130 | Written in near-C C++ and SDL. 131 | 132 | There's also a plugin for Autodesk Stingray. Since Stingray is basically dead I'm not likely to work further on the plugin, but it's useful as another reference implementation. 133 | 134 | ### :factory: Thread safe 135 | 136 | Not really - I don't want to add multithreading constructs to the_debuginator - it's up to the application to ensure that no thread writes or reads to it while another thread is writing. While I don't intend to change that, I **am** working on a way to make it easier to use in a multithreaded environment (that's how our own game at Warpzone Studios is engineered) but it's not done yet. 137 | 138 | ### :red_circle: Accordiony 139 | 140 | The idea is that, no matter how far down or deep you scroll, you can always see the folders above the current item. 141 | 142 | ### :red_circle: Favorites 143 | 144 | Set your most used ones. They will show up in a special folder near the top. 145 | 146 | ## Can I help? 147 | 148 | Sure. Any helpful comments, bug reports, or PRs appreciated. 149 | 150 | Even though I think The Debuginator has most of the features I envision for it, have probably missed something, so I would love feature requests! 151 | 152 | ## License 153 | 154 | Similarly to STB's single header libraries: The Debuginator and other source files in this repository are in the public domain. You can do anything you want with them. You have no legal obligation to do anything else, although I appreciate attribution. 155 | 156 | They are also licensed under the MIT open source license, if you have lawyers who are unhappy with public domain. Every source file includes an explicit dual-license for you to choose from. 157 | 158 | Note that this does **NOT** include any folders that has LICENSE or README files that specifies their own license. They retain their own licenses. 159 | 160 | # How to use the_debuginator.h 161 | 162 | I really recommend looking at the SDL demo for a real use case on how to set it up and use The Debuginator. It also includes basic save/load which is something you *really* want for your debug menu! :) 163 | 164 | ## Installation 165 | Put the_debuginator.h somewhere in your project. It's an STB-style single header library and as such usage is a bit special. 166 | 167 | Add this to *one* cpp file: 168 | 169 | ```C 170 | #define DEBUGINATOR_IMPLEMENTATION 171 | // #define DEBUGINATOR_OPTIONAL_SETTING_OR_OVERRIDE_X 172 | // #define DEBUGINATOR_OPTIONAL_SETTING_OR_OVERRIDE_Y 173 | // #define DEBUGINATOR_OPTIONAL_SETTING_OR_OVERRIDE_ETC 174 | #include "path/to/the_debuginator.h" 175 | ``` 176 | 177 | ## Setup 178 | 179 | In the same cpp file: 180 | 181 | ```C 182 | // Set up debuginator callbacks 183 | void draw_text(const char* text, DebuginatorVector2* position, DebuginatorColor* color, DebuginatorFont* font, void* userdata) { 184 | // Your code goes here 185 | } 186 | 187 | void draw_rect(DebuginatorVector2* position, DebuginatorVector2* size, DebuginatorColor* color, void* userdata) { 188 | // Your code goes here 189 | } 190 | 191 | void word_wrap(const char* text, DebuginatorFont font, float max_width, unsigned* row_count, unsigned* row_lengths, int row_lengths_buffer_size, void* app_userdata) { 192 | // Your code goes here 193 | } 194 | 195 | void word_wrap2(const char* text, DebuginatorFont font, float max_width, char** buffer, int buffer_size, void* userdata) { 196 | // Your code goes here 197 | } 198 | 199 | DebuginatorVector2 text_size(const char* text, DebuginatorFont* font, void* userdata) { 200 | // Your code goes here 201 | } 202 | 203 | int main(...) { 204 | TheDebuginatorConfig config; 205 | debuginator_get_default_config(&config); 206 | 207 | config.memory_arena_capacity = 1024 * 512; 208 | config.memory_arena = (char*)malloc(config.memory_arena_capacity); 209 | 210 | config.draw_rect = draw_rect; 211 | config.draw_text = draw_text; 212 | config.word_wrap = word_wrap; 213 | config.text_size = text_size; 214 | 215 | config.size.x = 500; 216 | config.size.y = GetMyScreenResolution().y; // You need to calculate this. 217 | 218 | config.screen_resolution.x = config.size.x; 219 | config.screen_resolution.y = config.size.y; 220 | 221 | // There's a bunch of other things you CAN change in the config, but these things 222 | // are the necessary stuff. 223 | 224 | config.create_default_debuginator_items = true; // I recommend this 225 | 226 | TheDebuginator debuginator; 227 | debuginator_create(&config, &debuginator); 228 | 229 | // debuginator is now the thing that you pass into the API functions to update/change/draw The Debuginator. 230 | } 231 | ``` 232 | 233 | Actual usage, such as adding items or modifying settings, can be done from any C or C++ file. 234 | 235 | ## How to use 236 | 237 | ### The gist of it 238 | This is the core for creating an item: 239 | 240 | ```C 241 | DebuginatorItem* debuginator_create_array_item(TheDebuginator* debuginator, 242 | DebuginatorItem* parent, const char* path, const char* description, 243 | DebuginatorOnItemChangedCallback on_item_changed_callback, void* user_data, 244 | const char** value_titles, void* values, int num_values, int value_size) 245 | ``` 246 | 247 | Don't be alarmed - there are utility functions that make it easier to use! :) See below. 248 | 249 | Each *item* is defined by its path. If you create two items with the same path, only one will actually exist - with the data from the last call. 250 | 251 | An *leaf item* has a list of *values*. A value has a title and a... value. The Debuginator doesn't care about what the values are, it just sees them as an array; a pointer and an element size. When you activate a value on an item, you'll get a callback with the value pointing to the correct place in that array. For bools, you don't really need to care about that stuff, it's handled by the wrapper function. 252 | 253 | The leaf item also has a userdata field that you will use in your callbacks. 254 | 255 | In addition to leaf items, there are folder items which currently doesn't really do anything in particular except be there. You don't need to create folder items before items, they'll be created implicitly if they don't already exist. You can pass NULL to the *parent* parameter, in fact, it's the most common use case. It's mainly there as an optimization. 256 | 257 | ### Saving and loading 258 | 259 | I recommend looking at the SDL demo for a good example of how to do this. But here's how it works. 260 | 261 | Saving is fairly straightforward. Call debuginator_save and pass in a callback that gets called for each item who's value is different from the default. I recommend storing it to a single key-value map, like "MyGame/MySetting = True". 262 | 263 | Folders will also save their state if they are collapsed. This is so that they remain collapsed if you for example close the game down and open it up again. 264 | 265 | ### Examples 266 | 267 | Here's how to add a boolean item that toggles god mode for the player: 268 | 269 | ```C 270 | struct Player { 271 | int health; 272 | bool godmode; 273 | } 274 | 275 | Player my_player; 276 | my_player.health = 100; 277 | my_player.godmode = false; 278 | 279 | debuginator_create_bool_item( 280 | &debuginator, 281 | "Player Mechanics/God Mode", 282 | "Player is invincible if enabled", 283 | &my_player.godmode); 284 | ``` 285 | 286 | Here's how to create a preset item - it'll toggle multiple things. 287 | 288 | ```C 289 | const char* preset_paths[2] = { "Workflow/Skip intro", "Player Mechanics/God Mode" }; 290 | const char* preset_value_titles[2] = { "True", "True" }; 291 | debuginator_create_preset_item(&debuginator, 292 | "My Presets/Good workflow", 293 | preset_paths, preset_value_titles, NULL, 2); 294 | ``` 295 | 296 | Here's how to create a generic item with a few strings and no callback (not sure what use case there is for that, but hey)... If you look at the definition of the other create_item functions, you'll see that they simply wrap this one. 297 | 298 | ```C 299 | { 300 | static const char* string_titles[5] = { "String A", "String B", "String C", "String D", "String E" }; 301 | debuginator_create_array_item(&debuginator, 302 | NULL, "My Game/String Test", 303 | "Multiple strings.", NULL, NULL, 304 | string_titles, NULL, 5, 0); 305 | } 306 | ``` 307 | 308 | Here's how to create a generic item with multiple things WITH a callback. 309 | 310 | ```C 311 | void on_change_ui_size(DebuginatorItem* item, void* value, const char* value_title, void* app_userdata) { 312 | (void)value_title; 313 | TheDebuginatorWrapper* wrapper = (TheDebuginatorWrapper*)app_userdata; 314 | int size_category = *(int*)value; 315 | if (size_category == 0) { 316 | set_ui_size(&wrapper->debuginator, 14, 22); 317 | } 318 | else if (size_category == 1) { 319 | set_ui_size(&wrapper->debuginator, 20, 30); 320 | } 321 | else if (size_category == 2) { 322 | set_ui_size(&wrapper->debuginator, 32, 40); 323 | } 324 | else if (size_category == 3) { 325 | set_ui_size(&wrapper->debuginator, 64, 70); 326 | } 327 | } 328 | 329 | // Later... 330 | 331 | static const char* uisize_titles[4] = { "Small", "Medium", "Large", "ULTRA LARGE" }; 332 | static int uisizes[4] = { 0, 1, 2, 3 }; 333 | DebuginatorItem* uisize_item = debuginator_create_array_item(debuginator, 334 | NULL, "Debuginator/UI size", 335 | "Change font and item size.", on_change_ui_size, wrapper, 336 | uisize_titles, uisizes, 4, sizeof(uisizes[0])); 337 | ``` 338 | 339 | ## A note on memory 340 | 341 | The Debuginator uses (what I call) a block allocator. It's slightly wasteful in terms of memory but should be pretty efficient for allocating and deallocating. 342 | 343 | You provide a buffer for The Debuginator to use, and it'll use that. When there's no more memory.. it'll probably crash or something. 344 | 345 | If you want to give The Debuginator a string for it to own (and deallocate), you can do that. Look at: 346 | ```C 347 | char* debuginator_copy_string(TheDebuginator* debuginator, const char* string, int length); 348 | ``` 349 | 350 | ## API 351 | 352 | You know, it's best to just look in the header file and see which functions are exposed, but... here's the API such as it is currently. There's additional information in the code. 353 | 354 | ```C 355 | bool debuginator_is_open(TheDebuginator* debuginator); 356 | void debuginator_set_open(TheDebuginator* debuginator, bool open); 357 | 358 | DebuginatorItem* debuginator_create_array_item(TheDebuginator* debuginator, 359 | DebuginatorItem* parent, const char* path, const char* description, 360 | DebuginatorOnItemChangedCallback on_item_changed_callback, void* user_data, 361 | const char** value_titles, void* values, int num_values, int value_size); 362 | 363 | DebuginatorItem* debuginator_create_bool_item(TheDebuginator* debuginator, const char* path, const char* description, void* user_data); 364 | DebuginatorItem* debuginator_create_preset_item(TheDebuginator* debuginator, const char* path, const char** paths, const char** value_titles, int** value_indices, int num_paths); 365 | 366 | DebuginatorItem* debuginator_create_folder_item(TheDebuginator* debuginator, DebuginatorItem* parent, const char* title, int title_length); 367 | DebuginatorItem* debuginator_get_item(TheDebuginator* debuginator, DebuginatorItem* parent, const char* path, bool create_if_not_exist); 368 | void debuginator_set_hot_item(TheDebuginator* debuginator, const char* path); 369 | DebuginatorItem* debuginator_get_hot_item(TheDebuginator* debuginator); 370 | void debuginator_remove_item(TheDebuginator* debuginator, DebuginatorItem* item); 371 | void debuginator_remove_item_by_path(TheDebuginator* debuginator, const char* path); 372 | 373 | int debuginator_save(TheDebuginator* debuginator, DebuginatorSaveItemCallback callback, char* save_buffer, int save_buffer_size); 374 | void debuginator_load_item(TheDebuginator* debuginator, const char* path, const char* value_title); 375 | void debuginator_set_default_value(TheDebuginator* debuginator, const char* path, const char* value_title, int value_index); // value index is used if value_title == NULL 376 | void debuginator_set_edit_type(TheDebuginator* debuginator, const char* path, DebuginatorItemEditorDataType edit_type); 377 | 378 | void debuginator_activate(TheDebuginator* debuginator, DebuginatorItem* item); 379 | 380 | void debuginator_move_to_next_leaf(TheDebuginator* debuginator, bool long_move); 381 | void debuginator_move_to_prev_leaf(TheDebuginator* debuginator, bool long_move); 382 | void debuginator_move_to_child(TheDebuginator* debuginator, bool toggle_and_activate); 383 | void debuginator_move_to_parent(TheDebuginator* debuginator); 384 | 385 | bool debuginator_is_filtering_enabled(TheDebuginator* debuginator); 386 | void debuginator_set_filtering_enabled(TheDebuginator* debuginator, bool enabled); 387 | char* debuginator_get_filter(TheDebuginator* debuginator); 388 | void debuginator_update_filter(TheDebuginator* debuginator, const char* wanted_filter); 389 | 390 | void debuginator_set_item_height(TheDebuginator* debuginator, int item_height); 391 | void debuginator_set_size(TheDebuginator* debuginator, int width, int height); 392 | 393 | ``` 394 | 395 | # How to run the Unit Test 396 | 397 | Open the Visual Studio solution. Build it. Set the *unittest* project as the StartUp project. Run. 398 | 399 | Note: The SDL demo project won't build until you fix the dependencies so you might want to unload it. 400 | Note: The unit tests haven't been updated in a long while so you probably don't want to do this. 401 | 402 | # How to run the SDL demo 403 | 404 | Open the Visual Studio solution. Build it. Set the *sdl* project as the StartUp project. Run. 405 | 406 | You'll need dependencies to build it. Get SDL 2 and SDLTTF 2. I'll update this with better instructions at some point. :) 407 | 408 | # How to use Stingray Plugin 409 | 410 | Not supported anymore (I mean, technically it should work with little problems but Stingray isn't available to the public any more so you are almost certainly not using it). 411 | -------------------------------------------------------------------------------- /color_picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/color_picker.png -------------------------------------------------------------------------------- /debuginator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/debuginator.gif -------------------------------------------------------------------------------- /plugins/stingray/the_debuginator_plugin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | project(the_debuginator_plugin) 3 | 4 | # Include common plugin CMake scripts and set the type of plugin to be to and ENGINE_PLUGIN 5 | # set(ENGINE_PLUGIN ON) 6 | # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${REPOSITORY_DIR}/cmake") 7 | # include(CMakePlugin) 8 | 9 | # Scan and add project source files 10 | find_source_files(ALL_SOURCE_FILES) 11 | 12 | # Add windows version resource if windows dll 13 | if( PLATFORM_WINDOWS ) 14 | if( BUILD_SHARED_LIBS ) 15 | include_directories(${PROJECT_SOURCE_DIR}) 16 | configure_file("${PROJECT_SOURCE_DIR}/${PROJECT_NAME}.rc.in" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.rc") 17 | set(RESOURCE_FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.rc") 18 | source_group("Resources" FILES ${RESOURCE_FILES}) 19 | list(APPEND ALL_SOURCE_FILES ${RESOURCE_FILES}) 20 | endif() 21 | endif() 22 | 23 | # Define automatic namespace for C++ 24 | add_compile_options(-DPLUGIN_NAMESPACE=${PROJECT_NAME}) 25 | 26 | # Include editor plugin sdk files 27 | include_directories(${REPOSITORY_DIR}/stingray_sdk) 28 | include_directories(${REPOSITORY_DIR}/runtime/sdk) 29 | 30 | # Create target and set compile/link options 31 | add_library(${PROJECT_NAME} ${ALL_SOURCE_FILES}) 32 | 33 | # Set target properties 34 | set_system_properties(${PROJECT_NAME}) 35 | set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "${ENGINE_PLUGINS_FOLDER_NAME}") 36 | set(TARGET_BASE_NAME "${PROJECT_NAME}_${ENGINE_PLUGIN_SUFFIX}_$>") 37 | set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${TARGET_BASE_NAME}") 38 | if( BUILD_SHARED_LIBS AND PLATFORM_IOS ) 39 | set_target_properties(${PROJECT_NAME} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${ENGINE_IOS_CODE_SIGN_IDENTITY}") 40 | endif() 41 | 42 | 43 | # Set engine runtime plugin properties and enable hot-reloading. 44 | set_plugin_runtime_output_directory("${TARGET_BASE_NAME}" "${ENGINE_PLUGINS_INSTALL_DIR}") 45 | -------------------------------------------------------------------------------- /plugins/stingray/the_debuginator_plugin/c_api_the_debuginator.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Include Debuginator, override any appropriate functions 3 | #include 4 | #define DEBUGINATOR_IMPLEMENTATION 5 | #define DEBUGINATOR_assert XENSURE 6 | #include "the_debuginator.h" 7 | 8 | #include 9 | #include "c_api_the_debuginator.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | struct TheDebuginatorWrapper { 17 | TheDebuginator debuginator; 18 | char id[64]; 19 | ConstWindowPtr window; 20 | GuiPtr gui; 21 | uint64_t font; 22 | MaterialPtr font_material; 23 | bool memory_owned_by_this_plugin; 24 | int font_size; 25 | bool axis_selected_repeat; 26 | bool axis_scroll_repeat; 27 | float scroll_repeat_timer; 28 | }; 29 | 30 | struct InputWrapper { 31 | InputControllerCApi* api; 32 | CApiInputControllerPtr controller; 33 | 34 | float time_since_pressed; 35 | 36 | // TODO: Pre-calc hashes. 37 | bool button(const char* button) { 38 | using namespace stingray_plugin_foundation; 39 | return api->button(controller, api->button_id(controller, hash32(button))) > 0; 40 | } 41 | 42 | bool pressed(const char* button) { 43 | using namespace stingray_plugin_foundation; 44 | return api->pressed(controller, api->button_id(controller, hash32(button))) > 0; 45 | } 46 | 47 | bool pressed_repeat(const char* button) { 48 | using namespace stingray_plugin_foundation; 49 | bool pressed = api->button(controller, api->button_id(controller, hash32(button))) > 0; 50 | 51 | // Faux repeat support. 52 | if (pressed) { 53 | if (time_since_pressed == -1) { 54 | time_since_pressed = 0; 55 | return true; 56 | } 57 | else if (time_since_pressed > 0.3f) { 58 | time_since_pressed = 0.25f; 59 | return true; 60 | } 61 | return false; 62 | } 63 | 64 | return false; 65 | } 66 | 67 | CApiVector3 axis(const char *axis) { 68 | using namespace stingray_plugin_foundation; 69 | return api->axis(controller, api->axis_id(controller, hash32(axis)), 0); 70 | } 71 | }; 72 | 73 | struct PluginMemory { 74 | AllocatorObject* allocator_object; 75 | TheDebuginatorWrapper debuginators[8]; 76 | int num_debuginators = 0; 77 | InputWrapper input_wrapper; 78 | }; 79 | 80 | static PluginMemory* plugin_memory = nullptr; 81 | 82 | // Engine APIs 83 | static AllocatorApi* allocator_api = nullptr; 84 | static ScriptApi* script_api = nullptr; 85 | 86 | void set_ui_size(TheDebuginator* debuginator, int font_size, int item_height); 87 | 88 | namespace { 89 | void draw_text(const char* text, DebuginatorVector2* position, DebuginatorColor* color, DebuginatorFont* font, void* app_userdata) { 90 | TheDebuginatorWrapper* wrapper = (TheDebuginatorWrapper*)app_userdata; 91 | CApiVector2 gui_position = { position->x, position->y }; 92 | CApiVector4 gui_color = { (float)color->a, (float)color->r, (float)color->g, (float)color->b }; 93 | gui_position.y = wrapper->debuginator.screen_resolution.y - position->y - wrapper->font_size * 0.75; 94 | script_api->Gui->text(wrapper->gui, text, wrapper->font, wrapper->font_size, wrapper->font_material, &gui_position, 100 + 1, 0, &gui_color); 95 | } 96 | 97 | void draw_rect(DebuginatorVector2* position, DebuginatorVector2* size, DebuginatorColor* color, void* app_userdata) { 98 | TheDebuginatorWrapper* wrapper = (TheDebuginatorWrapper*)app_userdata; 99 | CApiVector2 gui_position = { position->x, position->y }; 100 | CApiVector4 gui_color = { (float)color->a, (float)color->r, (float)color->g, (float)color->b }; 101 | gui_position.y = wrapper->debuginator.screen_resolution.y - position->y - size->y; 102 | script_api->Gui->rect(wrapper->gui, &gui_position, 100, (ConstVector2Ptr)size, &gui_color); 103 | } 104 | 105 | void word_wrap(const char* text, DebuginatorFont font, float max_width, unsigned* row_count, unsigned* row_lengths, unsigned row_lengths_buffer_size, void* app_userdata) { 106 | TheDebuginatorWrapper* wrapper = (TheDebuginatorWrapper*)app_userdata; 107 | *row_count = script_api->Gui->word_wrap(wrapper->gui, text, wrapper->font, wrapper->font_size, max_width, " ", "-+&/", "\n", 0, row_lengths, row_lengths_buffer_size); 108 | } 109 | 110 | DebuginatorVector2 text_size(const char* text, DebuginatorFont* font, void* app_userdata) { 111 | TheDebuginatorWrapper* wrapper = (TheDebuginatorWrapper*)app_userdata; 112 | TextExtentsResult results = script_api->Gui->text_extents(wrapper->gui, text, wrapper->font, wrapper->font_size, 0); 113 | CApiVector2 text_size = { results.max.x - results.min.x, results.max.y - results.min.y }; 114 | //text_size.x *= 2; // WHYYYY 115 | text_size.y *= 2; 116 | return *(DebuginatorVector2*)&text_size; 117 | } 118 | 119 | // Item callbacks 120 | void on_change_ui_size(DebuginatorItem* item, void* value, const char* value_title, void* app_userdata) { 121 | (void)value_title; 122 | TheDebuginatorWrapper* wrapper = (TheDebuginatorWrapper*)app_userdata; 123 | int size_category = *(int*)value; 124 | if (size_category == 0) { 125 | set_ui_size(&wrapper->debuginator, 14, 22); 126 | } 127 | else if (size_category == 1) { 128 | set_ui_size(&wrapper->debuginator, 20, 30); 129 | } 130 | else if (size_category == 2) { 131 | set_ui_size(&wrapper->debuginator, 32, 40); 132 | } 133 | else if (size_category == 3) { 134 | set_ui_size(&wrapper->debuginator, 64, 70); 135 | } 136 | } 137 | } 138 | 139 | void setup_api(GetApiFunction get_engine_api, const char* plugin_name) { 140 | allocator_api = (AllocatorApi*)get_engine_api(ALLOCATOR_API_ID); 141 | script_api = (ScriptApi*)get_engine_api(C_API_ID); 142 | 143 | if (plugin_memory == nullptr) { 144 | AllocatorObject* allocator_object = allocator_api->make_plugin_allocator(plugin_name); 145 | plugin_memory = (PluginMemory*)allocator_api->allocate(allocator_object, sizeof(PluginMemory), 16); 146 | memset(plugin_memory, 0, sizeof(*plugin_memory)); 147 | plugin_memory->allocator_object = allocator_object; 148 | plugin_memory->input_wrapper.time_since_pressed = -1; 149 | } 150 | } 151 | 152 | void shutdown_api() { 153 | if (plugin_memory != nullptr) { 154 | for (int i = 0; i < plugin_memory->num_debuginators; i++) { 155 | TheDebuginatorWrapper* wrapper = &plugin_memory->debuginators[i]; 156 | if (wrapper->memory_owned_by_this_plugin) { 157 | allocator_api->deallocate(plugin_memory->allocator_object, wrapper->debuginator.memory_arena); 158 | } 159 | } 160 | 161 | AllocatorObject* allocator_object = plugin_memory->allocator_object; 162 | allocator_api->deallocate(plugin_memory->allocator_object, plugin_memory); 163 | allocator_api->destroy_plugin_allocator(allocator_object); 164 | plugin_memory = nullptr; 165 | } 166 | } 167 | 168 | void* start_reload(GetApiFunction get_engine_api) { 169 | return plugin_memory; 170 | } 171 | 172 | void finish_reload(GetApiFunction get_engine_api, void *state) { 173 | plugin_memory = (PluginMemory*)state; 174 | } 175 | 176 | void update_debuginators(float dt) { 177 | for (int i = 0; i < plugin_memory->num_debuginators; i++) { 178 | TheDebuginatorWrapper* wrapper = &plugin_memory->debuginators[i]; 179 | 180 | CApiVector2 resolution = script_api->Gui->resolution(NULL, wrapper->window); 181 | wrapper->debuginator.size.y = resolution.y; 182 | wrapper->debuginator.screen_resolution.y = resolution.y; 183 | 184 | debuginator_update(&wrapper->debuginator, dt); 185 | debuginator_draw(&wrapper->debuginator, dt); 186 | } 187 | 188 | if (plugin_memory->input_wrapper.time_since_pressed != -1) { 189 | plugin_memory->input_wrapper.time_since_pressed += dt; 190 | } 191 | } 192 | 193 | void destroy_debuginator(TheDebuginator* debuginator); 194 | TheDebuginator* get_debuginator(const char* id); 195 | 196 | TheDebuginator* create_debuginator(const char* id, DebuginatorPluginCreateContext* context) { 197 | if (id == NULL) { 198 | id = "default"; 199 | } 200 | 201 | XASSERT(plugin_memory->num_debuginators < 8, "Too many debuginators created. Good lord, how many do you need?!"); 202 | XASSERT(get_debuginator(id) == NULL, "Debuginator with that name already exists."); 203 | 204 | TheDebuginatorWrapper* wrapper = &plugin_memory->debuginators[plugin_memory->num_debuginators]; 205 | memset(wrapper, 0, sizeof(TheDebuginatorWrapper)); 206 | strcpy_s(wrapper->id, sizeof(plugin_memory->debuginators[plugin_memory->num_debuginators].id), id); 207 | 208 | wrapper->window = context->window; 209 | wrapper->font = context->font; 210 | wrapper->font_material = context->font_material; 211 | wrapper->gui = context->gui; 212 | wrapper->font_size = context->font_size > 0 ? context->font_size : 20; 213 | 214 | if (context->config.draw_text == NULL) { 215 | XASSERT(wrapper->font, "No font defined"); 216 | XASSERT(wrapper->font_material, "No font_material defined"); 217 | XASSERT(wrapper->gui, "No gui defined"); 218 | context->config.draw_text = draw_text; 219 | context->config.draw_rect = draw_rect; 220 | context->config.word_wrap = word_wrap; 221 | context->config.text_size = text_size; 222 | context->config.app_user_data = wrapper;; 223 | } 224 | 225 | if (context->config.memory_arena == NULL) { 226 | wrapper->memory_owned_by_this_plugin = true; 227 | XASSERT(context->config.memory_arena_capacity != 0, "You must provide a wanted memory size."); 228 | context->config.memory_arena = (char*)allocator_api->allocate(plugin_memory->allocator_object, context->config.memory_arena_capacity, 16); 229 | } 230 | 231 | context->config.screen_resolution.x = script_api->Gui->resolution(NULL, wrapper->window).x; 232 | context->config.screen_resolution.y = script_api->Gui->resolution(NULL, wrapper->window).y; 233 | context->config.size.y = context->config.screen_resolution.y; 234 | 235 | TheDebuginator* debuginator = &wrapper->debuginator; 236 | debuginator_create(&context->config, debuginator); 237 | 238 | { 239 | static const char* uisize_titles[4] = { "Small", "Medium", "Large", "ULTRA LARGE" }; 240 | static int uisize_indices[4] = { 0, 1, 2, 3 }; 241 | DebuginatorItem* uisize_item = debuginator_create_array_item(debuginator, NULL, "Debuginator/UI size", 242 | "Change font and item size.", on_change_ui_size, wrapper, 243 | uisize_titles, uisize_indices, 4, sizeof(uisize_indices[0])); 244 | 245 | uisize_item->leaf.default_index = 1; 246 | uisize_item->leaf.hot_index = 1; 247 | uisize_item->leaf.active_index = 1; 248 | debuginator_activate(debuginator, uisize_item, false); 249 | } 250 | 251 | plugin_memory->num_debuginators++; 252 | return debuginator; 253 | } 254 | 255 | void destroy_debuginator(TheDebuginator* debuginator) { 256 | if (debuginator == NULL) { 257 | debuginator = get_debuginator("default"); 258 | } 259 | 260 | for (int i = 0; i < plugin_memory->num_debuginators; i++) { 261 | if (debuginator == &plugin_memory->debuginators[i].debuginator) { 262 | TheDebuginatorWrapper* wrapper = &plugin_memory->debuginators[i]; 263 | if (wrapper->memory_owned_by_this_plugin) { 264 | allocator_api->deallocate(plugin_memory->allocator_object, debuginator->memory_arena); 265 | } 266 | 267 | plugin_memory->debuginators[i] = plugin_memory->debuginators[--plugin_memory->num_debuginators]; 268 | break; 269 | } 270 | } 271 | } 272 | 273 | TheDebuginator* get_debuginator(const char* id) { 274 | if (id == NULL) { 275 | id = "default"; 276 | } 277 | 278 | for (int i = 0; i < plugin_memory->num_debuginators; i++) { 279 | if (strcmp(id, plugin_memory->debuginators[i].id) == 0) { 280 | return &plugin_memory->debuginators[i].debuginator; 281 | } 282 | } 283 | 284 | return NULL; 285 | } 286 | 287 | void handle_default_input(TheDebuginator* debuginator, unsigned devices) { 288 | if (debuginator == NULL) { 289 | debuginator = get_debuginator("default"); 290 | } 291 | 292 | TheDebuginatorWrapper* wrapper = (TheDebuginatorWrapper*)debuginator; 293 | 294 | InputControllerCApi* api = script_api->Input->InputController; 295 | InputWrapper& input_wrapper = plugin_memory->input_wrapper; 296 | input_wrapper.api = api; 297 | 298 | if (!script_api->Window->has_focus(wrapper->window)) { 299 | input_wrapper.time_since_pressed = -1; 300 | return; 301 | } 302 | 303 | while (devices & Debuginator_Keyboard) { // So we can break out of the scope 304 | devices &= ~Debuginator_Keyboard; 305 | CApiInputControllerPtr keyboard = script_api->Input->keyboard(); 306 | input_wrapper.controller = keyboard; 307 | 308 | if (api->any_released(input_wrapper.controller) != UINT_MAX) { 309 | input_wrapper.time_since_pressed = -1; 310 | } 311 | 312 | if (!debuginator_is_open(debuginator)) { 313 | if (input_wrapper.pressed("right")) { 314 | debuginator_set_open(debuginator, true); 315 | } 316 | 317 | break; 318 | } 319 | 320 | bool ctrl_pressed = input_wrapper.button("left ctrl") || 321 | input_wrapper.button("right ctrl"); 322 | 323 | if (input_wrapper.pressed_repeat("up")) { 324 | bool long_move = ctrl_pressed; 325 | debuginator_move_to_prev_leaf(debuginator, long_move); 326 | } 327 | else if (input_wrapper.pressed_repeat("down")) { 328 | bool long_move = ctrl_pressed; 329 | debuginator_move_to_next_leaf(debuginator, long_move); 330 | } 331 | else if (input_wrapper.pressed_repeat("home")) { 332 | debuginator_move_to_root(debuginator); 333 | debuginator->focus_height = default_focus_height; 334 | } 335 | else if (input_wrapper.pressed_repeat("end")) { 336 | debuginator_move_to_root(debuginator); 337 | debuginator_move_sibling_previous(debuginator); 338 | debuginator->focus_height = default_focus_height; 339 | } 340 | else if (input_wrapper.pressed("left")) { 341 | DebuginatorItem* hot_item = debuginator_get_hot_item(debuginator); 342 | if (debuginator_is_open(debuginator) && (hot_item->is_folder || (!hot_item->is_folder && hot_item->leaf.is_expanded))) { 343 | debuginator_move_to_parent(debuginator); 344 | } 345 | } 346 | else if (input_wrapper.pressed("escape") || input_wrapper.pressed("delete")) { 347 | debuginator_set_open(debuginator, false); 348 | } 349 | else if (input_wrapper.pressed("enter")) { 350 | debuginator_move_to_child(debuginator, false); 351 | } 352 | 353 | if (input_wrapper.pressed_repeat("backspace")) { 354 | const char* filter = debuginator_get_filter(debuginator); 355 | int filter_length = (int)strlen(filter); 356 | 357 | if (filter_length > 0) { 358 | char new_filter[64] = { 0 }; 359 | memcpy(new_filter, filter, filter_length); 360 | const char* filter = debuginator_get_filter(debuginator); 361 | int filter_length = (int)strlen(filter); 362 | if (filter_length > 0) { 363 | new_filter[--filter_length] = '\0'; 364 | } 365 | debuginator_update_filter(debuginator, new_filter); 366 | } 367 | else if (debuginator_is_filtering_enabled(debuginator)) { 368 | debuginator_set_filtering_enabled(debuginator, false); 369 | } 370 | } 371 | 372 | if (ctrl_pressed && input_wrapper.pressed_repeat("w")) { 373 | debuginator_update_filter(debuginator, ""); 374 | } else { 375 | unsigned int num_key_strokes; 376 | const int* keystrokes = script_api->Input->Keyboard->keystrokes(keyboard, &num_key_strokes); 377 | if (num_key_strokes > 0) { 378 | const char* filter = debuginator_get_filter(debuginator); 379 | int filter_length = (int)strlen(filter); 380 | if (filter_length + (int)num_key_strokes < wrapper->font_size) { 381 | char new_filter[64] = { 0 }; 382 | memcpy(new_filter, filter, filter_length); 383 | int real_strokes = 0; 384 | for (unsigned int i = 0; i < num_key_strokes; ++i) { 385 | if (32 <= keystrokes[i] && keystrokes[i] <= 125) { 386 | new_filter[filter_length++] = (char)keystrokes[i]; 387 | ++real_strokes; 388 | } 389 | } 390 | 391 | if (real_strokes > 0) { 392 | if (!debuginator_is_filtering_enabled(debuginator)) { 393 | debuginator_set_filtering_enabled(debuginator, true); 394 | } 395 | debuginator_update_filter(debuginator, new_filter); 396 | // Update focus height to match new hot item 397 | float max_height = debuginator_total_height(debuginator); 398 | int active_height = 0; 399 | debuginator__distance_to_hot_item(debuginator->root, debuginator->hot_item, debuginator->item_height, &active_height); 400 | float height_pixels = debuginator->focus_height * debuginator->size.y - active_height; 401 | if (height_pixels < -(max_height - (1.f - default_focus_height) * debuginator->size.y)) { 402 | debuginator->focus_height = -(max_height - (1.f - default_focus_height) * debuginator->size.y - active_height) / debuginator->size.y; 403 | } else if (height_pixels > default_focus_height * debuginator->size.y) { 404 | debuginator->focus_height = (default_focus_height * debuginator->size.y + active_height) / debuginator->size.y; 405 | } 406 | } 407 | } 408 | } 409 | } 410 | } 411 | 412 | if (devices & Debuginator_Mouse) { 413 | CApiInputControllerPtr mouse = script_api->Input->mouse(); 414 | input_wrapper.controller = mouse; 415 | 416 | if (api->any_released(input_wrapper.controller) != UINT_MAX) { 417 | input_wrapper.time_since_pressed = -1; 418 | } 419 | 420 | 421 | if (debuginator_is_open(debuginator)) { 422 | CApiVector3 scroll = input_wrapper.axis("wheel"); 423 | if (scroll.y != 0) { 424 | debuginator->focus_height += scroll.y * 0.05f; 425 | float max_height = debuginator_total_height(debuginator); 426 | int active_height = 0; 427 | debuginator__distance_to_hot_item(debuginator->root, debuginator->hot_item, debuginator->item_height, &active_height); 428 | float height_pixels = debuginator->focus_height * debuginator->size.y - active_height; 429 | if (height_pixels < -(max_height - (1.f - default_focus_height) * debuginator->size.y)) { 430 | debuginator->focus_height = -(max_height - (1.f - default_focus_height) * debuginator->size.y - active_height) / debuginator->size.y; 431 | } else if (height_pixels > default_focus_height * debuginator->size.y) { 432 | debuginator->focus_height = (default_focus_height * debuginator->size.y + active_height) / debuginator->size.y; 433 | } 434 | } 435 | 436 | if (input_wrapper.pressed_repeat("left")) { 437 | CApiVector3 pos = input_wrapper.axis("cursor"); 438 | float y = debuginator->screen_resolution.y - pos.y; 439 | if (pos.x > debuginator->top_left.x && pos.x < debuginator->top_left.x + debuginator->size.x) { 440 | debuginator_activate_closest_by_height(debuginator, y); 441 | } 442 | } 443 | } 444 | } 445 | 446 | if (devices & Debuginator_Gamepad) { 447 | int xbox_style_pads = 0; 448 | 449 | #if !defined(PS4) 450 | xbox_style_pads = script_api->Input->num_pads(); 451 | #endif 452 | for (int i = 0; i < xbox_style_pads; i++) { 453 | input_wrapper.controller = script_api->Input->pad(i); 454 | 455 | if (!api->active(input_wrapper.controller)) { 456 | continue; 457 | } 458 | 459 | if (api->any_released(input_wrapper.controller) != UINT_MAX) { 460 | input_wrapper.time_since_pressed = -1; 461 | } 462 | 463 | if (!debuginator_is_open(debuginator)) { 464 | if (input_wrapper.pressed("start")) { 465 | debuginator_set_open(debuginator, true); 466 | } 467 | 468 | continue; 469 | } 470 | 471 | CApiVector3 left_stick = input_wrapper.axis("left"); 472 | float DEADZONE = 0.4f; 473 | if (fabs(left_stick.x) > DEADZONE) { 474 | if (wrapper->axis_selected_repeat) { 475 | left_stick.x = 0.f; 476 | } else { 477 | wrapper->axis_selected_repeat = true; 478 | } 479 | } else { 480 | wrapper->axis_selected_repeat = false; 481 | } 482 | float SCROLL_TIMEOUT = 0.25f; 483 | float SCROLL_REPEAT_TIMEOUT = 0.05f; 484 | if (fabs(left_stick.y) > DEADZONE) { 485 | if (wrapper->axis_scroll_repeat && wrapper->scroll_repeat_timer < 0.f) { 486 | left_stick.y = 0.f; 487 | } else if (!wrapper->axis_scroll_repeat) { 488 | wrapper->scroll_repeat_timer = -SCROLL_TIMEOUT; 489 | wrapper->axis_scroll_repeat = true; 490 | } else { 491 | wrapper->scroll_repeat_timer = -SCROLL_REPEAT_TIMEOUT; 492 | wrapper->axis_scroll_repeat = true; 493 | } 494 | } else { 495 | wrapper->axis_scroll_repeat = false; 496 | } 497 | 498 | if (input_wrapper.pressed_repeat("d_up") || left_stick.y > DEADZONE) { 499 | bool long_move = false; 500 | debuginator_move_to_prev_leaf(debuginator, long_move); 501 | } 502 | else if (input_wrapper.pressed_repeat("d_down") || left_stick.y < -DEADZONE) { 503 | bool long_move = false; 504 | debuginator_move_to_next_leaf(debuginator, long_move); 505 | } 506 | else if (input_wrapper.pressed("d_left") || input_wrapper.pressed("b")) { 507 | DebuginatorItem* hot_item = debuginator_get_hot_item(debuginator); 508 | if (debuginator_is_open(debuginator) && !hot_item->leaf.is_expanded) { 509 | debuginator_set_open(debuginator, false); 510 | } 511 | else if (!hot_item->is_folder && hot_item->leaf.is_expanded) { 512 | debuginator_move_to_parent(debuginator); 513 | } 514 | } 515 | else if (input_wrapper.pressed("start")) { 516 | debuginator_set_open(debuginator, false); 517 | } 518 | else if (input_wrapper.pressed("d_right") || input_wrapper.pressed("a") || fabs(left_stick.x) > DEADZONE) { 519 | debuginator_move_to_child(debuginator, false); 520 | } 521 | else if (input_wrapper.pressed_repeat("left_shoulder")) { 522 | bool long_move = true; 523 | debuginator_move_to_prev_leaf(debuginator, long_move); 524 | debuginator->focus_height = default_focus_height; 525 | } 526 | else if (input_wrapper.pressed_repeat("right_shoulder")) { 527 | bool long_move = true; 528 | debuginator_move_to_next_leaf(debuginator, long_move); 529 | debuginator->focus_height = default_focus_height; 530 | } 531 | else if (input_wrapper.pressed("x")) { 532 | debuginator_move_to_child(debuginator, true); 533 | } 534 | } 535 | 536 | // Todo generalize this 537 | int num_ps4_pads = 0; 538 | #if defined(WINDOWSPC) 539 | num_ps4_pads = script_api->Input->num_windows_ps4_pads(); 540 | #elif defined(PS4) 541 | num_ps4_pads = script_api->Input->num_pads(); 542 | #endif 543 | for (int i = 0; i < num_ps4_pads; i++) { 544 | #if defined(WINDOWSPC) 545 | input_wrapper.controller = script_api->Input->windows_ps4_pad(i); 546 | #else 547 | input_wrapper.controller = script_api->Input->pad(i); 548 | #endif 549 | 550 | if (!api->active(input_wrapper.controller)) { 551 | continue; 552 | } 553 | 554 | if (api->any_released(input_wrapper.controller) != UINT_MAX) { 555 | input_wrapper.time_since_pressed = -1; 556 | } 557 | 558 | if (!debuginator_is_open(debuginator)) { 559 | if (input_wrapper.pressed("options")) { 560 | debuginator_set_open(debuginator, true); 561 | } 562 | 563 | continue; 564 | } 565 | 566 | CApiVector3 left_stick = input_wrapper.axis("left"); 567 | float DEADZONE = 0.4f; 568 | if (fabs(left_stick.x) > DEADZONE) { 569 | if (wrapper->axis_selected_repeat) { 570 | left_stick.x = 0.f; 571 | } else { 572 | wrapper->axis_selected_repeat = true; 573 | } 574 | } else { 575 | wrapper->axis_selected_repeat = false; 576 | } 577 | float SCROLL_TIMEOUT = 0.25f; 578 | float SCROLL_REPEAT_TIMEOUT = 0.05f; 579 | if (fabs(left_stick.y) > DEADZONE) { 580 | if (wrapper->axis_scroll_repeat && wrapper->scroll_repeat_timer < 0.f) { 581 | left_stick.y = 0.f; 582 | } else if (!wrapper->axis_scroll_repeat) { 583 | wrapper->scroll_repeat_timer = -SCROLL_TIMEOUT; 584 | wrapper->axis_scroll_repeat = true; 585 | } else { 586 | wrapper->scroll_repeat_timer = -SCROLL_REPEAT_TIMEOUT; 587 | wrapper->axis_scroll_repeat = true; 588 | } 589 | } else { 590 | wrapper->axis_scroll_repeat = false; 591 | } 592 | 593 | if (input_wrapper.pressed_repeat("up") || left_stick.y > DEADZONE) { 594 | bool long_move = false; 595 | debuginator_move_to_prev_leaf(debuginator, long_move); 596 | } 597 | else if (input_wrapper.pressed_repeat("down") || left_stick.y < -DEADZONE) { 598 | bool long_move = false; 599 | debuginator_move_to_next_leaf(debuginator, long_move); 600 | } 601 | else if (input_wrapper.pressed("left") || input_wrapper.pressed("circle")) { 602 | DebuginatorItem* hot_item = debuginator_get_hot_item(debuginator); 603 | if (debuginator_is_open(debuginator) && !hot_item->leaf.is_expanded) { 604 | debuginator_set_open(debuginator, false); 605 | } 606 | else if (!hot_item->is_folder && hot_item->leaf.is_expanded) { 607 | debuginator_move_to_parent(debuginator); 608 | } 609 | } 610 | else if (input_wrapper.pressed("options")) { 611 | debuginator_set_open(debuginator, false); 612 | } 613 | else if (input_wrapper.pressed("right") || input_wrapper.pressed("cross") || fabs(left_stick.x) > DEADZONE) { 614 | debuginator_move_to_child(debuginator, false); 615 | } 616 | else if (input_wrapper.pressed_repeat("l1")) { 617 | bool long_move = true; 618 | debuginator_move_to_prev_leaf(debuginator, long_move); 619 | } 620 | else if (input_wrapper.pressed_repeat("r1")) { 621 | bool long_move = true; 622 | debuginator_move_to_next_leaf(debuginator, long_move); 623 | } 624 | else if (input_wrapper.pressed("square")) { 625 | debuginator_move_to_child(debuginator, true); 626 | } 627 | } 628 | } 629 | } 630 | 631 | void set_ui_size(TheDebuginator* debuginator, int font_size, int item_height) { 632 | if (debuginator == NULL) { 633 | debuginator = get_debuginator("default"); 634 | } 635 | 636 | debuginator_set_item_height(debuginator, item_height); 637 | 638 | TheDebuginatorWrapper* wrapper = (TheDebuginatorWrapper*)debuginator; 639 | wrapper->font_size = font_size; 640 | } 641 | 642 | void get_debuginator_api(TheDebuginatorApi* api) { 643 | api->create_debuginator = create_debuginator; 644 | api->destroy_debuginator = destroy_debuginator; 645 | api->get_debuginator = get_debuginator; 646 | api->handle_default_input = handle_default_input; 647 | 648 | api->get_default_config = debuginator_get_default_config; 649 | api->is_open = debuginator_is_open; 650 | api->set_open = debuginator_set_open; 651 | api->create_array_item = debuginator_create_array_item; 652 | api->create_bool_item = debuginator_create_bool_item; 653 | api->create_preset_item = debuginator_create_preset_item; 654 | api->new_folder_item = debuginator_new_folder_item; 655 | api->get_item = debuginator_get_item; 656 | api->set_hot_item = debuginator_set_hot_item; 657 | api->get_hot_item = debuginator_get_hot_item; 658 | api->remove_item = debuginator_remove_item; 659 | api->remove_item_by_path = debuginator_remove_item_by_path; 660 | api->save = debuginator_save; 661 | api->load_item = debuginator_load_item; 662 | api->set_default_value = debuginator_set_default_value; 663 | api->set_edit_type = debuginator_set_edit_type; 664 | api->activate = debuginator_activate; 665 | api->move_to_next_leaf = debuginator_move_to_next_leaf; 666 | api->move_to_prev_leaf = debuginator_move_to_prev_leaf; 667 | api->move_to_child = debuginator_move_to_child; 668 | api->move_to_parent = debuginator_move_to_parent; 669 | api->is_filtering_enabled = debuginator_is_filtering_enabled; 670 | api->set_filtering_enabled = debuginator_set_filtering_enabled; 671 | api->get_filter = debuginator_get_filter; 672 | api->update_filter = debuginator_update_filter; 673 | api->set_item_height = debuginator_set_item_height; 674 | api->set_size = debuginator_set_size; 675 | } 676 | -------------------------------------------------------------------------------- /plugins/stingray/the_debuginator_plugin/c_api_the_debuginator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define THE_DEBUGINATOR_API_ID 0x0ac5036c // 0x0ac5036c049d75bf 4 | 5 | #include "the_debuginator.h" 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | typedef struct CApiWindow CApiWindow; 13 | typedef const CApiWindow* ConstWindowPtr; 14 | typedef struct CApiGui* GuiPtr; 15 | typedef struct CApiMaterial* MaterialPtr; 16 | typedef void *(*GetApiFunction)(unsigned api); 17 | 18 | typedef struct DebuginatorPluginCreateContext { 19 | TheDebuginatorConfig config; 20 | ConstWindowPtr window; 21 | GuiPtr gui; 22 | uint64_t font; 23 | MaterialPtr font_material; 24 | int font_size; 25 | } DebuginatorPluginCreateContext; 26 | 27 | typedef enum DebuginatorInputDevices { 28 | Debuginator_Keyboard = 1 << 0, 29 | Debuginator_Gamepad = 1 << 1, 30 | Debuginator_Mouse = 1 << 2, 31 | Debuginator_Touch = 1 << 3, 32 | } DebuginatorInputDevices; 33 | 34 | typedef struct TheDebuginatorApi { 35 | // Plugin API 36 | TheDebuginator*(*create_debuginator)(const char* id, DebuginatorPluginCreateContext* context); 37 | TheDebuginator*(*get_debuginator)(const char* id); 38 | void(*destroy_debuginator)(TheDebuginator* debuginator); 39 | 40 | void(*handle_default_input)(TheDebuginator* debuginator, unsigned devices); 41 | void(*set_ui_size)(TheDebuginator* debuginator, int font_size, int item_height); 42 | 43 | // Direct Debuginator API 44 | void(*get_default_config)(TheDebuginatorConfig* config); 45 | bool(*is_open)(TheDebuginator* debuginator); 46 | void(*set_open)(TheDebuginator* debuginator, bool open); 47 | 48 | DebuginatorItem*(*create_array_item)(TheDebuginator* debuginator, 49 | DebuginatorItem* parent, const char* path, const char* description, 50 | DebuginatorOnItemChangedCallback on_item_changed_callback, void* user_data, 51 | const char** value_titles, void* values, int num_values, int value_size); 52 | 53 | DebuginatorItem*(*create_bool_item)(TheDebuginator* debuginator, const char* path, const char* description, void* user_data); 54 | DebuginatorItem*(*create_preset_item)(TheDebuginator* debuginator, const char* path, const char** paths, const char** value_titles, int** value_indices, int num_paths); 55 | 56 | DebuginatorItem*(*new_folder_item)(TheDebuginator* debuginator, DebuginatorItem* parent, const char* title, int title_length); 57 | DebuginatorItem*(*get_item)(TheDebuginator* debuginator, DebuginatorItem* parent, const char* path, bool create_if_not_exist); 58 | void(*set_hot_item)(TheDebuginator* debuginator, const char* path); 59 | DebuginatorItem*(*get_hot_item)(TheDebuginator* debuginator); 60 | void(*remove_item)(TheDebuginator* debuginator, DebuginatorItem* item); 61 | void(*remove_item_by_path)(TheDebuginator* debuginator, const char* path); 62 | 63 | int(*save)(TheDebuginator* debuginator, DebuginatorSaveItemCallback callback, char* save_buffer, int save_buffer_size); 64 | void(*load_item)(TheDebuginator* debuginator, const char* path, const char* value_title); 65 | void(*set_default_value)(TheDebuginator* debuginator, const char* path, const char* value_title, int value_index); // value index is used if value_title == NULL 66 | void(*set_edit_type)(TheDebuginator* debuginator, const char* path, DebuginatorItemEditorDataType edit_type); 67 | 68 | void(*activate)(TheDebuginator* debuginator, DebuginatorItem* item, bool animate); 69 | void(*move_to_next_leaf)(TheDebuginator* debuginator, bool long_move); 70 | void(*move_to_prev_leaf)(TheDebuginator* debuginator, bool long_move); 71 | void(*move_to_child)(TheDebuginator* debuginator, bool toggle_and_activate); 72 | void(*move_to_parent)(TheDebuginator* debuginator); 73 | 74 | bool(*is_filtering_enabled)(TheDebuginator* debuginator); 75 | void(*set_filtering_enabled)(TheDebuginator* debuginator, bool enabled); 76 | const char*(*get_filter)(TheDebuginator* debuginator); 77 | void(*update_filter)(TheDebuginator* debuginator, const char* wanted_filter); 78 | 79 | void(*set_item_height)(TheDebuginator* debuginator, int item_height); 80 | void(*set_size)(TheDebuginator* debuginator, int width, int height); 81 | } TheDebuginatorApi; 82 | 83 | void get_debuginator_api(TheDebuginatorApi* api); 84 | void setup_api(GetApiFunction get_engine_api, const char* plugin_name); 85 | void shutdown_api(); 86 | void update_debuginators(float dt); 87 | 88 | void* start_reload(GetApiFunction get_engine_api); 89 | void finish_reload(GetApiFunction get_engine_api, void *state); 90 | 91 | #ifdef __cplusplus 92 | } 93 | #endif 94 | -------------------------------------------------------------------------------- /plugins/stingray/the_debuginator_plugin/copy_latest_debuginator_h.bat: -------------------------------------------------------------------------------- 1 | REM This is kind of an ugly solution but it's the one with the least resistance... 2 | 3 | copy /Y /V ..\..\..\the_debuginator.h .\the_debuginator.h -------------------------------------------------------------------------------- /plugins/stingray/the_debuginator_plugin/resource.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //{{NO_DEPENDENCIES}} 4 | // Microsoft Visual C++ generated include file. 5 | // Used by wwise_plugin.rc 6 | // 7 | #define VS_VERSION_INFO 1 8 | 9 | // Next default values for new objects 10 | // 11 | #ifdef APSTUDIO_INVOKED 12 | #ifndef APSTUDIO_READONLY_SYMBOLS 13 | #define _APS_NEXT_RESOURCE_VALUE 101 14 | #define _APS_NEXT_COMMAND_VALUE 40001 15 | #define _APS_NEXT_CONTROL_VALUE 1001 16 | #define _APS_NEXT_SYMED_VALUE 101 17 | #endif 18 | #endif 19 | -------------------------------------------------------------------------------- /plugins/stingray/the_debuginator_plugin/the_debuginator_plugin.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "c_api_the_debuginator.h" 3 | 4 | #include 5 | #include 6 | 7 | /* PLUGIN API */ 8 | 9 | const char* get_name() { return "the_debuginator_plugin"; } 10 | 11 | void setup_plugin(GetApiFunction get_engine_api) 12 | { 13 | setup_api(get_engine_api, get_name()); 14 | } 15 | 16 | void shutdown_plugin() 17 | { 18 | shutdown_api(); 19 | } 20 | 21 | void update_plugin(float dt) 22 | { 23 | update_debuginators(dt); 24 | } 25 | 26 | extern "C" { 27 | 28 | /** 29 | * Load and define plugin APIs. 30 | */ 31 | PLUGIN_DLLEXPORT void *get_plugin_api(unsigned api) 32 | { 33 | if (api == PLUGIN_API_ID) { 34 | static PluginApi plugin_api = { 0 }; 35 | plugin_api.get_name = get_name; 36 | plugin_api.setup_game = setup_plugin; 37 | plugin_api.update_game = update_plugin; 38 | plugin_api.shutdown_game = shutdown_plugin; 39 | return &plugin_api; 40 | } 41 | else if (api == THE_DEBUGINATOR_API_ID) 42 | { 43 | static TheDebuginatorApi debuginator_api = { 0 }; 44 | get_debuginator_api(&debuginator_api); 45 | return &debuginator_api; 46 | } 47 | 48 | return nullptr; 49 | } 50 | 51 | } 52 | 53 | /* EXAMPLE */ 54 | 55 | // #define DEBUGINATOR_EXAMPLE 56 | #if defined(DEBUGINATOR_EXAMPLE) 57 | 58 | #include "the_debuginator.h" 59 | 60 | void* get_engine_api(int id) { return NULL; }; 61 | 62 | struct example_data { 63 | bool my_bool; 64 | bool limit_framerate; 65 | }; 66 | 67 | static example_data my_userdata; 68 | 69 | void example() { 70 | PluginManagerApi *plugin_manager_api = (PluginManagerApi*)get_engine_api(PLUGIN_MANAGER_API_ID); 71 | TheDebuginatorApi* debuginator_api = (TheDebuginatorApi*)plugin_manager_api->get_next_plugin_api(THE_DEBUGINATOR_API_ID, NULL); 72 | 73 | int max_number_of_items = 100000; 74 | int width = 500; 75 | float focus_height = 0.6f; 76 | TheDebuginator* debuginator = debuginator_api->create_debuginator("Example", NULL, max_number_of_items, width, focus_height, &my_userdata); 77 | 78 | debuginator_create_bool_item(debuginator, "SDL Demo/Throttle framerate", "Disables sleeping between frames.", &my_userdata.limit_framerate); 79 | 80 | debuginator_api->destroy_debuginator(debuginator); 81 | } 82 | #endif 83 | -------------------------------------------------------------------------------- /plugins/stingray/the_debuginator_plugin/the_debuginator_plugin.rc.in: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "windows.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""windows.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Version 51 | // 52 | 53 | VS_VERSION_INFO VERSIONINFO 54 | FILEVERSION @PRODUCT_VERSION_MAJOR@,@PRODUCT_VERSION_MINOR@,@PRODUCT_VERSION_TCID@,@PRODUCT_VERSION_REVISION@ 55 | PRODUCTVERSION @PRODUCT_VERSION_MAJOR@,@PRODUCT_VERSION_MINOR@,@PRODUCT_VERSION_TCID@,@PRODUCT_VERSION_REVISION@ 56 | FILEFLAGSMASK 0x3fL 57 | #ifdef _DEBUG 58 | FILEFLAGS 0x1L 59 | #else 60 | FILEFLAGS 0x0L 61 | #endif 62 | FILEOS 0x40004L 63 | FILETYPE 0x0L 64 | FILESUBTYPE 0x0L 65 | BEGIN 66 | BLOCK "StringFileInfo" 67 | BEGIN 68 | BLOCK "040904b0" 69 | BEGIN 70 | VALUE "CompanyName", "@PRODUCT_COMPANY@" 71 | VALUE "FileDescription", "@PROJECT_NAME@" 72 | VALUE "FileVersion", "@PRODUCT_VERSION_MAJOR@.@PRODUCT_VERSION_MINOR@.@PRODUCT_VERSION_TCID@.@PRODUCT_VERSION_REVISION@" 73 | VALUE "InternalName", "@PROJECT_NAME@" 74 | VALUE "LegalCopyright", "@PRODUCT_COPYRIGHT@" 75 | VALUE "ProductName", "@PRODUCT_COMPANY@ @PRODUCT_NAME@" 76 | VALUE "ProductVersion", "@PRODUCT_VERSION_MAJOR@.@PRODUCT_VERSION_MINOR@.@PRODUCT_VERSION_TCID@.@PRODUCT_VERSION_REVISION@" 77 | END 78 | END 79 | BLOCK "VarFileInfo" 80 | BEGIN 81 | VALUE "Translation", 0x409, 1200 82 | END 83 | END 84 | 85 | #endif // English (United States) resources 86 | ///////////////////////////////////////////////////////////////////////////// 87 | 88 | 89 | 90 | #ifndef APSTUDIO_INVOKED 91 | ///////////////////////////////////////////////////////////////////////////// 92 | // 93 | // Generated from the TEXTINCLUDE 3 resource. 94 | // 95 | 96 | 97 | ///////////////////////////////////////////////////////////////////////////// 98 | #endif // not APSTUDIO_INVOKED 99 | 100 | -------------------------------------------------------------------------------- /tests/3rdparty/SDL_GameControllerDB-master/.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3.5 3 | script: ./check.py gamecontrollerdb.txt 4 | -------------------------------------------------------------------------------- /tests/3rdparty/SDL_GameControllerDB-master/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Simple DirectMedia Layer 3 | Copyright (C) 1997-2013 Sam Lantinga 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | 21 | -------------------------------------------------------------------------------- /tests/3rdparty/SDL_GameControllerDB-master/README.md: -------------------------------------------------------------------------------- 1 | ##SDL_GameControllerDB 2 | 3 | [![Build Status](https://travis-ci.org/gabomdq/SDL_GameControllerDB.svg?branch=master)](https://travis-ci.org/gabomdq/SDL_GameControllerDB) 4 | 5 | A community source database of game controller mappings to be used with SDL2 Game Controller functionality. 6 | 7 | ####Usage: 8 | 9 | Download gamecontrollerdb.txt, place it in your app's directory and load with: 10 | 11 | ``` 12 | SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); 13 | ``` 14 | 15 | ####Creating new mappings: 16 | 17 | To create new mappings, you can use the controllermap utility provided with 18 | SDL2, or using Steam's Big Picture mode, configure your joystick and then 19 | look in config/config.vdf in your Steam installation directory for the 20 | SDL_GamepadBind entry. 21 | 22 | ####Checking your mappings: 23 | You need to have python3 installed. Run 24 | 25 | ``` 26 | python3 check.py gamecontrollerdb.txt 27 | ``` 28 | 29 | ####References: 30 | 31 | * [SDL2](http://www.libsdl.org) 32 | * [SDL_GameControllerAddMappingsFromFile](http://wiki.libsdl.org/SDL_GameControllerAddMappingsFromFile) 33 | -------------------------------------------------------------------------------- /tests/3rdparty/SDL_GameControllerDB-master/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import fileinput 4 | import string 5 | import sys 6 | 7 | success = True 8 | 9 | def error (message): 10 | global success; 11 | success = False 12 | print("Error at #" + str(fileinput.lineno()), ":", message) 13 | print(line) 14 | 15 | def check_guid (guid): 16 | if len (guid) != 32: 17 | error ("The length of the guid string must be equal to 32") 18 | for c in guid: 19 | if not c in string.hexdigits: 20 | error ("Each character in guid string must be a hex character " + string.hexdigits) 21 | 22 | def check_mapping (mappingstring): 23 | keys = ["platform", "leftx", "lefty", "rightx", "righty", "a", "b", "back", "dpdown", \ 24 | "dpleft", "dpright", "dpup", "guide", "leftshoulder", "leftstick", \ 25 | "lefttrigger", "rightshoulder", "rightstick", "righttrigger", "start", "x", "y"] 26 | platforms = ["Linux", "Mac OS X", "Windows"] 27 | mappings = mappingstring.split (',') 28 | for mapping in mappings: 29 | if not mapping: 30 | continue 31 | if len (mapping.split(':')) != 2: 32 | error ("Invalid mapping : " + mapping) 33 | continue 34 | key = mapping.split (':')[0] 35 | value = mapping.split (':')[1] 36 | if not key in keys: 37 | error ("Invalid key \"" + key + "\" in mapping string") 38 | 39 | # Check values 40 | if key == "platform": 41 | if value not in platforms: 42 | error ("Invalid platform \"" + value + "\" in mapping string") 43 | else: 44 | if not value: 45 | continue 46 | if not value[0] in ['a', 'h', 'b']: 47 | error ("Invalid value \"" + value + "\" for key \"" + key + 48 | "\". Should start with a, b, or h") 49 | elif value[0] in ['a', 'b']: 50 | if not value[1:].isnumeric(): 51 | error ("Invalid value \"" + value + "\" for key \"" + key + 52 | "\". Should be followed by a number after 'a' or 'b'") 53 | else: 54 | dpad_positions = map(str, [0, 1, 2, 4, 8, 1|2, 2|4, 4|8, 8|1]) 55 | dpad_index = value[1:].split ('.')[0] 56 | dpad_position = value[1:].split ('.')[1] 57 | if not dpad_index.isnumeric(): 58 | error ("Invalid value \"" + value + "\" for key \"" + key + 59 | "\". Dpad index \"" + dpad_index + "\" should be a number") 60 | if not dpad_position in dpad_positions: 61 | error ("Invalid value \"" + value + "\" for key \"" + key + 62 | "\". Dpad position \"" + dpad_position + "\" should be one of" + 63 | ', '.join(dpad_positions)) 64 | 65 | for line in fileinput.input(): 66 | if line.startswith('#') or line == '\n': 67 | continue 68 | splitted = line[:-1].split(',', 2) 69 | if len(splitted) < 3 or not splitted[0] or not splitted[1] or not splitted[2]: 70 | error ("Either GUID/Name/Mappingstring is missing or empty") 71 | check_guid(splitted[0]) 72 | check_mapping(splitted[2]) 73 | 74 | if not success: 75 | sys.exit(1) 76 | -------------------------------------------------------------------------------- /tests/3rdparty/Simple-SDL2-Audio/README.md: -------------------------------------------------------------------------------- 1 | # Simple SDL2 Audio 2 | 3 | ## About 4 | 5 | * A simple native SDL2 Audio library that has 2 files, and an easy to use interface. 6 | * This library works without SDL2 Mixer, and plays a single music file at a time, and unlimited sounds (Mixes audio natively without Mixer) 7 | 8 | ## Install 9 | 10 | * Include `src/audio.c` and `src/audio.h` in your project 11 | 12 | ## Examples 13 | 14 | * `src/test.c` shows all the functionality possible: 15 | 16 | Basic use case: 17 | 18 | ```c 19 | // Initialize SDL2 Audio only 20 | SDL_Init(SDL_INIT_AUDIO); 21 | 22 | // Initialize Simple-SDL2-Audio 23 | initAudio(); 24 | 25 | // Play music and a sound 26 | playMusic("music/highlands.wav", SDL_MIX_MAXVOLUME); 27 | playSound("sounds/door1.wav", SDL_MIX_MAXVOLUME / 2); 28 | 29 | // Let play for 1 second 30 | SDL_Delay(1000); 31 | 32 | // End Simple-SDL2-Audio 33 | endAudio(); 34 | 35 | // End SDL2 36 | SDL_Quit(); 37 | ``` 38 | 39 | ## API Functions: 40 | 41 | ```c 42 | // Initialize Simple-SDL2-Audio on default audio device 43 | void initAudio(void); 44 | 45 | // Play many Sounds or single Musics 46 | void playSound(const char * filename, int volume); 47 | void playMusic(const char * filename, int volume); 48 | 49 | // Clean up Simple-SDL2-Audio 50 | void endAudio(void); 51 | 52 | // Pause or Unpause running audio 53 | void pauseAudio(void); 54 | void unpauseAudio(void); 55 | 56 | // Advanced functions used for caching WAV files in memory, create, play many times, free 57 | Audio * createAudio(const char * filename, uint8_t loop, int volume); 58 | void playSoundFromMemory(Audio * audio, int volume); 59 | void playMusicFromMemory(Audio * audio, int volume); 60 | void freeAudio(Audio * audio); 61 | ``` 62 | 63 | ## Difference between Music vs Sound 64 | 65 | * Only one music can play at a time, and it loops (to close music you can just run `endAudio()`, or use `pauseAudio()` and `unpauseAudio()`). 66 | * If you add another music when one is playing, the first one fades out before ending, and then playing the second. 67 | * If you play more than 2 music at once, the first fades as expected, only the last music queued before the first fade out is used 68 | 69 | * Any number of sounds can be played at once, but obviously the more, can become distorted 70 | * Can change `AUDIO_MAX_SOUNDS` in `src/audio.c` to limit how many sounds can be played at once to reduce distortion from too many playing 71 | 72 | ## Caveats 73 | 74 | * This implementation uses SDL_MixAudioFormat for mixing for simplicity. It's noted "Do not use this function for mixing together more than two streams of sample data". While only playing 1 music removes a lot of these issues, if you need something more powerful you should write your own mixing function. 75 | * This implementation ONLY plays WAV files, and they should all be the same format, but can have differing formats if you play around with `SDL_AUDIO_ALLOW_CHANGES` in `src/audio.c`, see the top of `src/audio.c` to set the format, stereo vs mono etc... No conversion 76 | * Caching: Using the standard `playMusic()` functions makes a disk read each call. To only make one disk read, cache, and play the audio from memory, use the `createAudio(); playSoundFromMemory(); freeAudio();` functions (recommend storing the Audio* object in a dictionary / hashmap) 77 | 78 | ## Features to add 79 | 80 | * Pause / unpause only music, only sound or ~~both~~ 81 | * Current implementation uses callback method, however in SDL 2.0.4 there exists `SDL_QueueAudio()` (no callback) 82 | 83 | ## Windows 7 Compatability 84 | 85 | * [Github Issue - Solution](https://github.com/jakebesworth/Simple-SDL2-Audio/issues/3) 86 | 87 | SDL2.0.6 updated how audio was handled, for Windows 7 using a later release of SDL2, you need to set `#define SDL_AUDIO_ALLOW_CHANGES SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE` which is a flag near the top of the file. 88 | 89 | ## Emscripten Compatibility 90 | 91 | * [Github Issue - Solution](https://github.com/jakebesworth/Simple-SDL2-Audio/issues/2) 92 | 93 | ## Resources 94 | 95 | * I made this project as a more modern version of https://gist.github.com/armornick/3447121 96 | * https://davidgow.net/handmadepenguin/ch7.html 97 | * http://rerwarwar.weebly.com/sdl2-audio.html 98 | 99 | ## Contributors 100 | 101 | * [Jake Besworth](https://github.com/jakebesworth) 102 | * [Lorenzo Mancini](https://github.com/lmancini) 103 | * [Ted](https://github.com/claimred) 104 | * [Eric Boez](https://github.com/ericb59) 105 | -------------------------------------------------------------------------------- /tests/3rdparty/Simple-SDL2-Audio/src/simple_audio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple-SDL2-Audio 3 | * 4 | * Copyright 2016 Jake Besworth 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "simple_audio.h" 27 | 28 | /* 29 | * Native WAVE format 30 | * 31 | * On some GNU/Linux you can identify a files properties using: 32 | * mplayer -identify music.wav 33 | * 34 | * On some GNU/Linux to convert any music to this or another specified format use: 35 | * ffmpeg -i in.mp3 -acodec pcm_s16le -ac 2 -ar 48000 out.wav 36 | */ 37 | /* SDL_AudioFormat of files, such as s16 little endian */ 38 | #define AUDIO_FORMAT AUDIO_S16LSB 39 | 40 | /* Frequency of the file */ 41 | #define AUDIO_FREQUENCY 48000 42 | 43 | /* 1 mono, 2 stereo, 4 quad, 6 (5.1) */ 44 | #define AUDIO_CHANNELS 2 45 | 46 | /* Specifies a unit of audio data to be used at a time. Must be a power of 2 */ 47 | #define AUDIO_SAMPLES 4096 48 | 49 | /* Max number of sounds that can be in the audio queue at anytime, stops too much mixing */ 50 | #define AUDIO_MAX_SOUNDS 25 51 | 52 | /* Flags OR'd together, which specify how SDL should behave when a device cannot offer a specific feature 53 | * If flag is set, SDL will change the format in the actual audio file structure (as opposed to gDevice->want) 54 | * 55 | * Note: If you're having issues with Emscripten / EMCC play around with these flags 56 | * 57 | * 0 Allow no changes 58 | * SDL_AUDIO_ALLOW_FREQUENCY_CHANGE Allow frequency changes (e.g. AUDIO_FREQUENCY is 48k, but allow files to play at 44.1k 59 | * SDL_AUDIO_ALLOW_FORMAT_CHANGE Allow Format change (e.g. AUDIO_FORMAT may be S32LSB, but allow wave files of S16LSB to play) 60 | * SDL_AUDIO_ALLOW_CHANNELS_CHANGE Allow any number of channels (e.g. AUDIO_CHANNELS being 2, allow actual 1) 61 | * SDL_AUDIO_ALLOW_ANY_CHANGE Allow all changes above 62 | */ 63 | // #define SDL_AUDIO_ALLOW_CHANGES SDL_AUDIO_ALLOW_ANY_CHANGE 64 | #define SDL_AUDIO_ALLOW_CHANGES SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE 65 | 66 | /* 67 | * Definition for the game global sound device 68 | * 69 | */ 70 | typedef struct privateAudioDevice 71 | { 72 | SDL_AudioDeviceID device; 73 | SDL_AudioSpec want; 74 | uint8_t audioEnabled; 75 | } PrivateAudioDevice; 76 | 77 | /* File scope variables to persist data */ 78 | static PrivateAudioDevice * gDevice; 79 | static uint32_t gSoundCount; 80 | 81 | /* 82 | * Add a music to the queue, addAudio wrapper for music due to fade 83 | * 84 | * @param new New Audio to add 85 | * 86 | */ 87 | static void addMusic(Audio * root, Audio * new); 88 | 89 | /* 90 | * Wrapper function for playMusic, playSound, playMusicFromMemory, playSoundFromMemory 91 | * 92 | * @param filename Provide a filename to load WAV from, or NULL if using FromMemory 93 | * @param audio Provide an Audio object if copying from memory, or NULL if using a filename 94 | * @param sound 1 if looping (music), 0 otherwise (sound) 95 | * @param volume See playSound for explanation 96 | * 97 | */ 98 | static inline void playAudio(const char * filename, Audio * audio, uint8_t loop, int volume); 99 | 100 | /* 101 | * Add a sound to the end of the queue 102 | * 103 | * @param root Root of queue 104 | * @param new New Audio to add 105 | * 106 | */ 107 | static void addAudio(Audio * root, Audio * new); 108 | 109 | /* 110 | * Audio callback function for OpenAudioDevice 111 | * 112 | * @param userdata Points to linked list of sounds to play, first being a placeholder 113 | * @param stream Stream to mix sound into 114 | * @param len Length of sound to play 115 | * 116 | */ 117 | static inline void audioCallback(void * userdata, uint8_t * stream, int len); 118 | 119 | void playSound(const char * filename, int volume) 120 | { 121 | playAudio(filename, NULL, 0, volume); 122 | } 123 | 124 | void playMusic(const char * filename, int volume) 125 | { 126 | playAudio(filename, NULL, 1, volume); 127 | } 128 | 129 | void playSoundFromMemory(Audio * audio, int volume) 130 | { 131 | playAudio(NULL, audio, 0, volume); 132 | } 133 | 134 | void playMusicFromMemory(Audio * audio, int volume) 135 | { 136 | playAudio(NULL, audio, 1, volume); 137 | } 138 | 139 | void initAudio(void) 140 | { 141 | Audio * global; 142 | gDevice = calloc(1, sizeof(PrivateAudioDevice)); 143 | gSoundCount = 0; 144 | 145 | if(gDevice == NULL) 146 | { 147 | fprintf(stderr, "[%s: %d]Fatal Error: Memory c-allocation error\n", __FILE__, __LINE__); 148 | return; 149 | } 150 | 151 | gDevice->audioEnabled = 0; 152 | 153 | if(!(SDL_WasInit(SDL_INIT_AUDIO) & SDL_INIT_AUDIO)) 154 | { 155 | fprintf(stderr, "[%s: %d]Error: SDL_INIT_AUDIO not initialized\n", __FILE__, __LINE__); 156 | return; 157 | } 158 | 159 | SDL_memset(&(gDevice->want), 0, sizeof(gDevice->want)); 160 | 161 | (gDevice->want).freq = AUDIO_FREQUENCY; 162 | (gDevice->want).format = AUDIO_FORMAT; 163 | (gDevice->want).channels = AUDIO_CHANNELS; 164 | (gDevice->want).samples = AUDIO_SAMPLES; 165 | (gDevice->want).callback = audioCallback; 166 | (gDevice->want).userdata = calloc(1, sizeof(Audio)); 167 | 168 | global = (gDevice->want).userdata; 169 | 170 | if(global == NULL) 171 | { 172 | fprintf(stderr, "[%s: %d]Error: Memory allocation error\n", __FILE__, __LINE__); 173 | return; 174 | } 175 | 176 | global->buffer = NULL; 177 | global->next = NULL; 178 | 179 | /* want.userdata = new; */ 180 | if((gDevice->device = SDL_OpenAudioDevice(NULL, 0, &(gDevice->want), NULL, SDL_AUDIO_ALLOW_CHANGES)) == 0) 181 | { 182 | fprintf(stderr, "[%s: %d]Warning: failed to open audio device: %s\n", __FILE__, __LINE__, SDL_GetError()); 183 | } 184 | else 185 | { 186 | /* Set audio device enabled global flag */ 187 | gDevice->audioEnabled = 1; 188 | 189 | /* Unpause active audio stream */ 190 | unpauseAudio(); 191 | } 192 | } 193 | 194 | void endAudio(void) 195 | { 196 | if(gDevice->audioEnabled) 197 | { 198 | pauseAudio(); 199 | 200 | freeAudio((Audio *) (gDevice->want).userdata); 201 | 202 | /* Close down audio */ 203 | SDL_CloseAudioDevice(gDevice->device); 204 | } 205 | 206 | free(gDevice); 207 | } 208 | 209 | void pauseAudio(void) 210 | { 211 | if(gDevice->audioEnabled) 212 | { 213 | SDL_PauseAudioDevice(gDevice->device, 1); 214 | } 215 | } 216 | 217 | void unpauseAudio(void) 218 | { 219 | if(gDevice->audioEnabled) 220 | { 221 | SDL_PauseAudioDevice(gDevice->device, 0); 222 | } 223 | } 224 | 225 | void freeAudio(Audio * audio) 226 | { 227 | Audio * temp; 228 | 229 | while(audio != NULL) 230 | { 231 | if(audio->free == 1) 232 | { 233 | SDL_FreeWAV(audio->bufferTrue); 234 | } 235 | 236 | temp = audio; 237 | audio = audio->next; 238 | 239 | free(temp); 240 | } 241 | } 242 | 243 | Audio * createAudio(const char * filename, uint8_t loop, int volume) 244 | { 245 | Audio * new = calloc(1, sizeof(Audio)); 246 | 247 | if(new == NULL) 248 | { 249 | fprintf(stderr, "[%s: %d]Error: Memory allocation error\n", __FILE__, __LINE__); 250 | return NULL; 251 | } 252 | 253 | if(filename == NULL) 254 | { 255 | fprintf(stderr, "[%s: %d]Warning: filename NULL: %s\n", __FILE__, __LINE__, filename); 256 | return NULL; 257 | } 258 | 259 | new->next = NULL; 260 | new->loop = loop; 261 | new->fade = 0; 262 | new->free = 1; 263 | new->volume = (uint8_t)volume; 264 | 265 | if(SDL_LoadWAV(filename, &(new->audio), &(new->bufferTrue), &(new->lengthTrue)) == NULL) 266 | { 267 | fprintf(stderr, "[%s: %d]Warning: failed to open wave file: %s error: %s\n", __FILE__, __LINE__, filename, SDL_GetError()); 268 | free(new); 269 | return NULL; 270 | } 271 | 272 | new->buffer = new->bufferTrue; 273 | new->length = new->lengthTrue; 274 | (new->audio).callback = NULL; 275 | (new->audio).userdata = NULL; 276 | 277 | return new; 278 | } 279 | 280 | static inline void playAudio(const char * filename, Audio * audio, uint8_t loop, int volume) 281 | { 282 | Audio * new; 283 | 284 | /* Check if audio is enabled */ 285 | if(!gDevice->audioEnabled) 286 | { 287 | return; 288 | } 289 | 290 | /* If sound, check if under max number of sounds allowed, else don't play */ 291 | if(loop == 0) 292 | { 293 | if(gSoundCount >= AUDIO_MAX_SOUNDS) 294 | { 295 | return; 296 | } 297 | else 298 | { 299 | gSoundCount++; 300 | } 301 | } 302 | 303 | /* Load from filename or from Memory */ 304 | if(filename != NULL) 305 | { 306 | /* Create new music sound with loop */ 307 | new = createAudio(filename, loop, volume); 308 | } 309 | else if(audio != NULL) 310 | { 311 | new = malloc(sizeof(Audio)); 312 | 313 | if(new == NULL) 314 | { 315 | fprintf(stderr, "[%s: %d]Fatal Error: Memory allocation error\n", __FILE__, __LINE__); 316 | return; 317 | } 318 | 319 | memcpy(new, audio, sizeof(Audio)); 320 | 321 | new->volume = (uint8_t)volume; 322 | new->loop = loop; 323 | new->free = 0; 324 | } 325 | else 326 | { 327 | fprintf(stderr, "[%s: %d]Warning: filename and Audio parameters NULL\n", __FILE__, __LINE__); 328 | return; 329 | } 330 | 331 | /* Lock callback function */ 332 | SDL_LockAudioDevice(gDevice->device); 333 | 334 | if(loop == 1) 335 | { 336 | addMusic((Audio *) (gDevice->want).userdata, new); 337 | } 338 | else 339 | { 340 | addAudio((Audio *) (gDevice->want).userdata, new); 341 | } 342 | 343 | SDL_UnlockAudioDevice(gDevice->device); 344 | 345 | } 346 | 347 | static void addMusic(Audio * root, Audio * new) 348 | { 349 | uint8_t musicFound = 0; 350 | Audio * rootNext = root->next; 351 | 352 | /* Find any existing musics, 0, 1 or 2 and fade them out */ 353 | while(rootNext != NULL) 354 | { 355 | /* Phase out any current music */ 356 | if(rootNext->loop == 1 && rootNext->fade == 0) 357 | { 358 | if(musicFound) 359 | { 360 | rootNext->length = 0; 361 | rootNext->volume = 0; 362 | } 363 | 364 | rootNext->fade = 1; 365 | } 366 | /* Set flag to remove any queued up music in favour of new music */ 367 | else if(rootNext->loop == 1 && rootNext->fade == 1) 368 | { 369 | musicFound = 1; 370 | } 371 | 372 | rootNext = rootNext->next; 373 | } 374 | 375 | addAudio(root, new); 376 | } 377 | 378 | static inline void audioCallback(void * userdata, uint8_t * stream, int len) 379 | { 380 | Audio * audio = (Audio *) userdata; 381 | Audio * previous = audio; 382 | int tempLength; 383 | uint8_t music = 0; 384 | 385 | /* Silence the main buffer */ 386 | SDL_memset(stream, 0, len); 387 | 388 | /* First one is place holder */ 389 | audio = audio->next; 390 | 391 | while(audio != NULL) 392 | { 393 | if(audio->length > 0) 394 | { 395 | if(audio->fade == 1 && audio->loop == 1) 396 | { 397 | music = 1; 398 | 399 | if(audio->volume > 0) 400 | { 401 | audio->volume--; 402 | } 403 | else 404 | { 405 | audio->length = 0; 406 | } 407 | } 408 | 409 | if(music && audio->loop == 1 && audio->fade == 0) 410 | { 411 | tempLength = 0; 412 | } 413 | else 414 | { 415 | tempLength = ((uint32_t) len > audio->length) ? audio->length : (uint32_t) len; 416 | } 417 | 418 | SDL_MixAudioFormat(stream, audio->buffer, AUDIO_FORMAT, tempLength, audio->volume); 419 | 420 | audio->buffer += tempLength; 421 | audio->length -= tempLength; 422 | 423 | previous = audio; 424 | audio = audio->next; 425 | } 426 | else if(audio->loop == 1 && audio->fade == 0) 427 | { 428 | audio->buffer = audio->bufferTrue; 429 | audio->length = audio->lengthTrue; 430 | } 431 | else 432 | { 433 | previous->next = audio->next; 434 | 435 | if(audio->loop == 0) 436 | { 437 | gSoundCount--; 438 | } 439 | 440 | audio->next = NULL; 441 | freeAudio(audio); 442 | 443 | audio = previous->next; 444 | } 445 | } 446 | } 447 | 448 | static void addAudio(Audio * root, Audio * new) 449 | { 450 | if(root == NULL) 451 | { 452 | return; 453 | } 454 | 455 | while(root->next != NULL) 456 | { 457 | root = root->next; 458 | } 459 | 460 | root->next = new; 461 | } 462 | -------------------------------------------------------------------------------- /tests/3rdparty/Simple-SDL2-Audio/src/simple_audio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple-SDL2-Audio 3 | * 4 | * Copyright 2016 Jake Besworth 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | */ 19 | 20 | /* 21 | * audio.h 22 | * 23 | * All audio related functions go here 24 | * 25 | */ 26 | #ifndef SIMPLE_AUDIO_ 27 | #define SIMPLE_AUDIO_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" 31 | { 32 | #endif 33 | 34 | #include 35 | 36 | /* 37 | * Queue structure for all loaded sounds 38 | * 39 | */ 40 | typedef struct sound 41 | { 42 | uint32_t length; 43 | uint32_t lengthTrue; 44 | uint8_t * bufferTrue; 45 | uint8_t * buffer; 46 | uint8_t loop; 47 | uint8_t fade; 48 | uint8_t free; 49 | uint8_t volume; 50 | 51 | SDL_AudioSpec audio; 52 | 53 | struct sound * next; 54 | } Audio; 55 | 56 | /* 57 | * Create a Audio object 58 | * 59 | * @param filename Filename for the WAVE file to load 60 | * @param loop 0 ends after playing once (sound), 1 repeats and fades when other music added (music) 61 | * @param volume Volume, read playSound() 62 | * 63 | * @return returns a new Audio or NULL on failure, you must call freeAudio() on return Audio 64 | * 65 | */ 66 | Audio * createAudio(const char * filename, uint8_t loop, int volume); 67 | 68 | /* 69 | * Frees as many chained Audios as given 70 | * 71 | * @param audio Chain of sounds to free 72 | * 73 | */ 74 | void freeAudio(Audio * audio); 75 | 76 | /* 77 | * Play a wave file currently must be S16LE format 2 channel stereo 78 | * 79 | * @param filename Filename to open, use getAbsolutePath 80 | * @param volume Volume 0 - 128. SDL_MIX_MAXVOLUME constant for max volume 81 | * 82 | */ 83 | void playSound(const char * filename, int volume); 84 | 85 | /* 86 | * Plays a new music, only 1 at a time plays 87 | * 88 | * @param filename Filename of the WAVE file to load 89 | * @param volume Volume read playSound for moree 90 | * 91 | */ 92 | void playMusic(const char * filename, int volume); 93 | 94 | /* 95 | * Plays a sound from a createAudio object (clones), only 1 at a time plays 96 | * Advantage to this method is no more disk reads, only once, data is stored and constantly reused 97 | * 98 | * @param audio Audio object to clone and use 99 | * @param volume Volume read playSound for moree 100 | * 101 | */ 102 | void playSoundFromMemory(Audio * audio, int volume); 103 | 104 | /* 105 | * Plays a music from a createAudio object (clones), only 1 at a time plays 106 | * Advantage to this method is no more disk reads, only once, data is stored and constantly reused 107 | * 108 | * @param audio Audio object to clone and use 109 | * @param volume Volume read playSound for moree 110 | * 111 | */ 112 | void playMusicFromMemory(Audio * audio, int volume); 113 | 114 | /* 115 | * Free all audio related variables 116 | * Note, this needs to be run even if initAudio fails, because it frees the global audio device 117 | * 118 | */ 119 | void endAudio(void); 120 | 121 | /* 122 | * Initialize Audio Variable 123 | * 124 | */ 125 | void initAudio(void); 126 | 127 | /* 128 | * Pause audio from playing 129 | * 130 | */ 131 | void pauseAudio(void); 132 | 133 | /* 134 | * Unpause audio from playing 135 | * 136 | */ 137 | void unpauseAudio(void); 138 | 139 | #ifdef __cplusplus 140 | } 141 | #endif 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /tests/3rdparty/kenney_interfacesounds/License.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | Interface Sounds (1.0) 4 | 5 | Created/distributed by Kenney (www.kenney.nl) 6 | Creation date: 11-02-2020 7 | 8 | ------------------------------ 9 | 10 | License: (Creative Commons Zero, CC0) 11 | http://creativecommons.org/publicdomain/zero/1.0/ 12 | 13 | This content is free to use in personal, educational and commercial projects. 14 | Support us by crediting Kenney or www.kenney.nl (this is not mandatory) 15 | 16 | ------------------------------ 17 | 18 | Donate: http://support.kenney.nl 19 | Patreon: http://patreon.com/kenney/ 20 | 21 | Follow on Twitter for updates: 22 | http://twitter.com/KenneyNL -------------------------------------------------------------------------------- /tests/3rdparty/kenney_interfacesounds/bong_001.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/kenney_interfacesounds/bong_001.wav -------------------------------------------------------------------------------- /tests/3rdparty/kenney_interfacesounds/pluck_001.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/kenney_interfacesounds/pluck_001.wav -------------------------------------------------------------------------------- /tests/3rdparty/kenney_interfacesounds/pluck_002.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/kenney_interfacesounds/pluck_002.wav -------------------------------------------------------------------------------- /tests/3rdparty/kenney_interfacesounds/select_007.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/kenney_interfacesounds/select_007.wav -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/AUTHORS: -------------------------------------------------------------------------------- 1 | AUTHORS 2 | 3 | Current Contributors (sorted alphabetically): 4 | - Pravin Satpute 5 | Project Owner (Current) 6 | Red Hat, Inc. 7 | 8 | Previous Contributors 9 | 10 | - Steve Matteson 11 | Original Designer 12 | Ascender, Inc. 13 | -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/ChangeLog: -------------------------------------------------------------------------------- 1 | * Thu Oct 04 2012 Pravin Satpute 2 | - Resolved "Glyphs with multiple unicode encodings inhibit subsetting" #851790 3 | - Resolved #851791, #854601 and #851825 4 | - Following GASP table version as per Liberation old version. (Anti-aliasing disabled) 5 | - Added support for Serbian glyphs for wikipedia #657849 6 | - In Monospace fonts, isFixedPitch bit set via script for getting it recognized as Monospace in putty.exe 7 | 8 | * Fri Jul 06 2012 Pravin Satpute 9 | - Initial version of Liberation fonts based on croscore fonts version 1.21.0 10 | - Converted TTF files into SFD files to be open source. 11 | - Update Copyright and License file 12 | - set fsType bit to 0, Installable Embedding is allowed. 13 | - Absolute value in HHeadAscent/Descent values for maintaining Metric compatibility. 14 | 15 | -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LICENSE: -------------------------------------------------------------------------------- 1 | Digitized data copyright (c) 2010 Google Corporation 2 | with Reserved Font Arimo, Tinos and Cousine. 3 | Copyright (c) 2012 Red Hat, Inc. 4 | with Reserved Font Name Liberation. 5 | 6 | This Font Software is licensed under the SIL Open Font License, 7 | Version 1.1. 8 | 9 | This license is copied below, and is also available with a FAQ at: 10 | http://scripts.sil.org/OFL 11 | 12 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 13 | 14 | PREAMBLE The goals of the Open Font License (OFL) are to stimulate 15 | worldwide development of collaborative font projects, to support the font 16 | creation efforts of academic and linguistic communities, and to provide 17 | a free and open framework in which fonts may be shared and improved in 18 | partnership with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. 22 | The fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply to 27 | any document created using the fonts or their derivatives. 28 | 29 | 30 | 31 | DEFINITIONS 32 | "Font Software" refers to the set of files released by the Copyright 33 | Holder(s) under this license and clearly marked as such. 34 | This may include source files, build scripts and documentation. 35 | 36 | "Reserved Font Name" refers to any names specified as such after the 37 | copyright statement(s). 38 | 39 | "Original Version" refers to the collection of Font Software components 40 | as distributed by the Copyright Holder(s). 41 | 42 | "Modified Version" refers to any derivative made by adding to, deleting, 43 | or substituting ? in part or in whole ? 44 | any of the components of the Original Version, by changing formats or 45 | by porting the Font Software to a new environment. 46 | 47 | "Author" refers to any designer, engineer, programmer, technical writer 48 | or other person who contributed to the Font Software. 49 | 50 | 51 | PERMISSION & CONDITIONS 52 | 53 | Permission is hereby granted, free of charge, to any person obtaining a 54 | copy of the Font Software, to use, study, copy, merge, embed, modify, 55 | redistribute, and sell modified and unmodified copies of the Font 56 | Software, subject to the following conditions: 57 | 58 | 1) Neither the Font Software nor any of its individual components,in 59 | Original or Modified Versions, may be sold by itself. 60 | 61 | 2) Original or Modified Versions of the Font Software may be bundled, 62 | redistributed and/or sold with any software, provided that each copy 63 | contains the above copyright notice and this license. These can be 64 | included either as stand-alone text files, human-readable headers or 65 | in the appropriate machine-readable metadata fields within text or 66 | binary files as long as those fields can be easily viewed by the user. 67 | 68 | 3) No Modified Version of the Font Software may use the Reserved Font 69 | Name(s) unless explicit written permission is granted by the 70 | corresponding Copyright Holder. This restriction only applies to the 71 | primary font name as presented to the users. 72 | 73 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 74 | Software shall not be used to promote, endorse or advertise any 75 | Modified Version, except to acknowledge the contribution(s) of the 76 | Copyright Holder(s) and the Author(s) or with their explicit written 77 | permission. 78 | 79 | 5) The Font Software, modified or unmodified, in part or in whole, must 80 | be distributed entirely under this license, and must not be distributed 81 | under any other license. The requirement for fonts to remain under 82 | this license does not apply to any document created using the Font 83 | Software. 84 | 85 | 86 | 87 | TERMINATION 88 | This license becomes null and void if any of the above conditions are not met. 89 | 90 | 91 | 92 | DISCLAIMER 93 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 94 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 95 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 96 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 97 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 98 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 99 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 100 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER 101 | DEALINGS IN THE FONT SOFTWARE. 102 | 103 | -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationMono-Bold.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationMono-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationMono-BoldItalic.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationMono-Italic.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationMono-Regular.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSans-Bold.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSans-BoldItalic.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSans-Italic.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSans-Regular.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSerif-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSerif-Bold.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSerif-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSerif-BoldItalic.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSerif-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSerif-Italic.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSerif-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/3rdparty/liberation-fonts-ttf-2.00.1/LiberationSerif-Regular.ttf -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/README: -------------------------------------------------------------------------------- 1 | 1. What's this? 2 | ================= 3 | 4 | The Liberation Fonts is font collection which aims to provide document 5 | layout compatibility as usage of Times New Roman, Arial, Courier New. 6 | 7 | 8 | 2. Requirements 9 | ================= 10 | 11 | * fontforge is installed. 12 | (http://fontforge.sourceforge.net) 13 | 14 | 15 | 3. Install 16 | ============ 17 | 18 | 3.1 Decompress tarball 19 | 20 | You can extract the files by following command: 21 | 22 | $ tar zxvf liberation-fonts-[VERSION].tar.gz 23 | 24 | 3.2 Build from the source 25 | 26 | Change into directory liberation-fonts-[VERSION]/ and build from sources by 27 | following commands: 28 | 29 | $ cd liberation-fonts-[VERSION] 30 | $ make 31 | 32 | The built font files will be available in 'build' directory. 33 | 34 | 3.3 Install to system 35 | 36 | For Fedora, you could manually install the fonts by copying the TTFs to 37 | ~/.fonts for user wide usage, or to /usr/share/fonts/truetype/liberation 38 | for system-wide availability. Then, run "fc-cache" to let that cached. 39 | 40 | For other distributions, please check out corresponding documentation. 41 | 42 | 43 | 4. Usage 44 | ========== 45 | 46 | Simply select preferred liberation font in applications and start using. 47 | 48 | 49 | 5. License 50 | ============ 51 | 52 | This Font Software is licensed under the SIL Open Font License, 53 | Version 1.1. 54 | 55 | Please read file "LICENSE" for details. 56 | 57 | 58 | 6. For Maintainers 59 | ==================== 60 | 61 | Before packaging a new release based on a new source tarball, you have to 62 | update the version suffix in the Makefile: 63 | 64 | VER = [VERSION] 65 | 66 | Make sure that the defined version corresponds to the font software metadata 67 | which you can check with ftinfo/otfinfo or fontforge itself. It is highly 68 | recommended that file 'ChangeLog' is updated to reflect changes. 69 | 70 | Create a tarball with the following command: 71 | 72 | $ make dist 73 | 74 | The new versioned tarball will be available in the dist/ folder as 75 | 'liberation-fonts-[NEW_VERSION].tar.gz'. 76 | 77 | 7. Credits 78 | ============ 79 | 80 | Please read file "AUTHORS" for list of contributors. 81 | -------------------------------------------------------------------------------- /tests/3rdparty/liberation-fonts-ttf-2.00.1/TODO: -------------------------------------------------------------------------------- 1 | Here are todo for next release 2 | 1) Serbian glyph for wikipedia https://bugzilla.redhat.com/show_bug.cgi?id=657849 3 | - Improving shape of S_BE https://bugzilla.redhat.com/show_bug.cgi?id=657849#c96 4 | 2) Liberation Mono not recognizing as Mono in Windows application #861003 5 | - presently it is patch, we have to update zero width characters to fixed width 6 | -------------------------------------------------------------------------------- /tests/sdl/demo.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "../../the_debuginator.h" 3 | 4 | //#include 5 | #include 6 | #include 7 | 8 | #include "demo.h" 9 | 10 | void demo_trigger_next(DemoData* data) { 11 | if (data->current_slide == -1) { 12 | data->current_slide = 0; 13 | 14 | for (int i=0; i < data->num_slides; ++i) { 15 | DemoSlide& slide = data->slides[i]; 16 | slide.num_texts_visible = 0; 17 | memset(slide.anim_times, 0, sizeof(slide.anim_times)); 18 | } 19 | 20 | DemoSlide& slide = data->slides[data->current_slide]; 21 | slide.num_texts_visible++; 22 | 23 | return; 24 | } 25 | 26 | DemoSlide& slide = data->slides[data->current_slide]; 27 | if (slide.num_texts_visible == slide.num_texts) { 28 | if (data->current_slide != data->num_slides - 1) { 29 | data->current_slide++; 30 | data->slides[data->current_slide].num_texts_visible++; 31 | } 32 | else { 33 | data->current_slide = -1; 34 | } 35 | } 36 | else { 37 | slide.num_texts_visible++; 38 | } 39 | } 40 | static void on_demo_action(DebuginatorItem* item, void* value, const char* value_title, void* app_userdata) { 41 | (void)app_userdata; 42 | (void)value; 43 | DemoData* data = (DemoData*)item->user_data; 44 | if (strcmp(value_title, "Start") == 0) { 45 | demo_trigger_next(data); 46 | } 47 | if (strcmp(value_title, "Stop") == 0) { 48 | data->current_slide = -1; 49 | } 50 | if (strcmp(value_title, "Next") == 0) { 51 | demo_trigger_next(data); 52 | } 53 | if (strcmp(value_title, "Prev") == 0) { 54 | if (data->current_slide >= 0) { 55 | data->current_slide -= 1; 56 | } 57 | } 58 | } 59 | 60 | static void debug_menu_setup(TheDebuginator* debuginator, DemoData* data) { 61 | (void)data; 62 | { 63 | static const char* string_titles[4] = { "Start", "Stop", "Prev", "Next" }; 64 | debuginator_create_array_item(debuginator, NULL, "SDL Demo/Slide show", 65 | "Presentation demo", on_demo_action, data, 66 | string_titles, NULL, 4, 0); 67 | debuginator_set_edit_type(debuginator, "SDL Demo/Slide show", DEBUGINATOR_EditTypeActionArray); 68 | } 69 | } 70 | 71 | static DemoData s_demo_data; 72 | 73 | static void presentation_setup(DemoData* data) { 74 | { 75 | DemoSlide& slide = data->slides[data->num_slides++]; 76 | slide.texts[slide.num_texts++] = "Click to start slideshow!"; 77 | } 78 | { 79 | DemoSlide& slide = data->slides[data->num_slides++]; 80 | slide.texts[slide.num_texts++] = "What is it?"; 81 | slide.texts[slide.num_texts++] = "- Open source C library"; 82 | slide.texts[slide.num_texts++] = "- Debug menu for games"; 83 | slide.texts[slide.num_texts++] = "- Feature rich"; 84 | } 85 | { 86 | DemoSlide& slide = data->slides[data->num_slides++]; 87 | slide.texts[slide.num_texts++] = "So what does it look like?"; 88 | slide.texts[slide.num_texts++] = "- It's pretty sweet!"; 89 | slide.texts[slide.num_texts++] = "- Press Right Arrow to open it!"; 90 | } 91 | { 92 | DemoSlide& slide = data->slides[data->num_slides++]; 93 | slide.texts[slide.num_texts++] = "Core UI features"; 94 | slide.texts[slide.num_texts++] = "- Hierarchical"; 95 | slide.texts[slide.num_texts++] = " - Folders can be nested"; 96 | slide.texts[slide.num_texts++] = " - Folders can be collapsed"; 97 | slide.texts[slide.num_texts++] = "- Scrollable (no limit on items)"; 98 | slide.texts[slide.num_texts++] = "- Dynamic (add/remove/modify items live)"; 99 | slide.texts[slide.num_texts++] = "- Mouse/Keyboard/Gamepad support"; 100 | } 101 | { 102 | DemoSlide& slide = data->slides[data->num_slides++]; 103 | slide.texts[slide.num_texts++] = "Anything else?"; 104 | slide.texts[slide.num_texts++] = "..."; 105 | slide.texts[slide.num_texts++] = "You betcha!"; 106 | } 107 | { 108 | DemoSlide& slide = data->slides[data->num_slides++]; 109 | slide.texts[slide.num_texts++] = "Themes!"; 110 | slide.texts[slide.num_texts++] = "- Only Classic looks decent"; 111 | slide.texts[slide.num_texts++] = "- Please help me!"; 112 | } 113 | { 114 | DemoSlide& slide = data->slides[data->num_slides++]; 115 | slide.texts[slide.num_texts++] = "Alignment!"; 116 | slide.texts[slide.num_texts++] = "- Left or right"; 117 | } 118 | { 119 | DemoSlide& slide = data->slides[data->num_slides++]; 120 | slide.texts[slide.num_texts++] = "Search!"; 121 | slide.texts[slide.num_texts++] = "- Press backspace to start"; 122 | slide.texts[slide.num_texts++] = "- ... and to stop"; 123 | slide.texts[slide.num_texts++] = "- Fuzzy matching!"; 124 | } 125 | { 126 | DemoSlide& slide = data->slides[data->num_slides++]; 127 | slide.texts[slide.num_texts++] = "Item editors!"; 128 | slide.texts[slide.num_texts++] = "- Bool items"; 129 | slide.texts[slide.num_texts++] = "- 'Action array' items"; 130 | slide.texts[slide.num_texts++] = "- Support for custom ones"; 131 | } 132 | { 133 | DemoSlide& slide = data->slides[data->num_slides++]; 134 | slide.texts[slide.num_texts++] = "Presets!"; 135 | slide.texts[slide.num_texts++] = "- Activate one item..."; 136 | slide.texts[slide.num_texts++] = "- ... and affect multiple!"; 137 | } 138 | { 139 | DemoSlide& slide = data->slides[data->num_slides++]; 140 | slide.texts[slide.num_texts++] = "Hotkeys!"; 141 | slide.texts[slide.num_texts++] = "- Bind input to specific items"; 142 | slide.texts[slide.num_texts++] = "- Activate when menu is closed"; 143 | } 144 | { 145 | DemoSlide& slide = data->slides[data->num_slides++]; 146 | slide.texts[slide.num_texts++] = "Default and overriden values!"; 147 | slide.texts[slide.num_texts++] = "- See highlighted colors."; 148 | } 149 | { 150 | DemoSlide& slide = data->slides[data->num_slides++]; 151 | slide.texts[slide.num_texts++] = "Save & Load!"; 152 | slide.texts[slide.num_texts++] = "- Remembers state."; 153 | slide.texts[slide.num_texts++] = "- Items & Folders."; 154 | } 155 | { 156 | DemoSlide& slide = data->slides[data->num_slides++]; 157 | slide.texts[slide.num_texts++] = "That's it!"; 158 | slide.texts[slide.num_texts++] = "- I'm open to..."; 159 | slide.texts[slide.num_texts++] = "- ...suggestions."; 160 | slide.texts[slide.num_texts++] = "- ...feature requests."; 161 | slide.texts[slide.num_texts++] = "- ...bug reports."; 162 | slide.texts[slide.num_texts++] = "- @srekel"; 163 | slide.texts[slide.num_texts++] = "- srekel@gmail.com"; 164 | slide.texts[slide.num_texts++] = "- https://github.com/Srekel/the-debuginator/"; 165 | } 166 | } 167 | 168 | DemoData* demo_init(GuiHandle gui, TheDebuginator* debuginator) { 169 | memset(&s_demo_data, 0, sizeof(s_demo_data)); 170 | s_demo_data.gui = gui; 171 | s_demo_data.debuginator = debuginator; 172 | s_demo_data.current_slide = -1; 173 | s_demo_data.font_handle = gui_register_font_template(gui, "LiberationMono-Regular.ttf", 24); 174 | debug_menu_setup(debuginator, &s_demo_data); 175 | presentation_setup(&s_demo_data); 176 | demo_trigger_next(&s_demo_data); 177 | 178 | return &s_demo_data; 179 | } 180 | 181 | void demo_update(DemoData* data, float dt, Vector2 offset) { 182 | if (data->current_slide == -1) { 183 | return; 184 | } 185 | 186 | Vector2 pos = offset; 187 | pos.x += 50; 188 | DemoSlide& slide = data->slides[data->current_slide]; 189 | for (int i=0; i < slide.num_texts_visible; ++i) { 190 | float& anim_time = slide.anim_times[i]; 191 | anim_time += dt * 3; 192 | if (anim_time > 1.f) { 193 | anim_time = 1.f; 194 | } 195 | 196 | pos.x = 150.f + (-150.f + pos.x) * anim_time; 197 | Color color(150, 50, 150, 255); 198 | // unsigned char base_color = slide.num_texts_visible == slide.num_texts ? 100 : 50; 199 | unsigned char base_color = 50; 200 | color.a = (unsigned char)(anim_time * color.a); 201 | color.r = (unsigned char)(base_color + anim_time * color.r); 202 | color.g = (unsigned char)(base_color + anim_time * color.g); 203 | color.b = (unsigned char)(base_color + anim_time * color.b); 204 | gui_draw_text(data->gui, slide.texts[i], pos, data->font_handle, color); 205 | pos.y += 50; 206 | } 207 | 208 | unsigned char progress_bar_brightness = slide.num_texts_visible == slide.num_texts ? 250 : 150; 209 | Color progress_bar_background_color(100, 0, 100, 200); 210 | Color progress_bar_color(progress_bar_brightness, 0, progress_bar_brightness, progress_bar_brightness); 211 | Vector2 progress_bar_pos(offset.x + 50, offset.y + 20); 212 | Vector2 progress_bar_background_size(350, 5); 213 | Vector2 progress_bar_size(350.f * slide.num_texts_visible / slide.num_texts, 5); 214 | gui_draw_rect_filled(data->gui, progress_bar_pos, progress_bar_background_size, progress_bar_background_color); 215 | gui_draw_rect_filled(data->gui, progress_bar_pos, progress_bar_size, progress_bar_color); 216 | } 217 | -------------------------------------------------------------------------------- /tests/sdl/demo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "gui.h" 6 | 7 | struct TheDebuginator; 8 | 9 | struct DemoSlide { 10 | const char* texts[8]; 11 | float anim_times[8]; 12 | int num_texts; 13 | int num_texts_visible; 14 | }; 15 | 16 | struct DemoData { 17 | GuiHandle gui; 18 | TheDebuginator* debuginator; 19 | FontTemplateHandle font_handle; 20 | 21 | DemoSlide slides[32]; 22 | int num_slides; 23 | int current_slide; 24 | }; 25 | 26 | 27 | DemoData* demo_init(GuiHandle gui, TheDebuginator* debuginator); 28 | void demo_update(DemoData* demo_data, float dt, Vector2 offset); 29 | void demo_trigger_next(DemoData* demo_data); 30 | -------------------------------------------------------------------------------- /tests/sdl/game.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "../../the_debuginator.h" 3 | 4 | //#include 5 | #include 6 | #include 7 | 8 | #include "game.h" 9 | 10 | static void on_colorpicker(DebuginatorItem* item, void* value, const char* value_title, void* app_userdata) { 11 | (void)app_userdata; 12 | (void)value; 13 | (void)value_title; 14 | GameData* data = (GameData*)item->user_data; 15 | data->background_color = *(DebuginatorColor*)(value); 16 | } 17 | 18 | static void on_box_action(DebuginatorItem* item, void* value, const char* value_title, void* app_userdata) { 19 | (void)app_userdata; 20 | (void)value; 21 | GameBox* box = (GameBox*)item->user_data; 22 | if (strcmp(value_title, "Hide") == 0) { 23 | box->color.a = 0; 24 | } 25 | else if (strcmp(value_title, "Show") == 0) { 26 | box->color.a = box->alpha; 27 | } 28 | else if (strcmp(value_title, "Speed up") == 0) { 29 | box->velocity.x *= 2; 30 | box->velocity.y *= 2; 31 | } 32 | else if (strcmp(value_title, "Slow down") == 0) { 33 | box->velocity.x *= 0.5f; 34 | box->velocity.y *= 0.5f; 35 | } 36 | } 37 | 38 | #pragma warning(suppress: 4505) // unreferenced local function 39 | static void on_boxes_activated(DebuginatorItem* item, void* value, const char* value_title, void* app_userdata) { 40 | (void)app_userdata; 41 | (void)value; 42 | GameData* data = (GameData*)item->user_data; 43 | if (strcmp(value_title, "Add box") == 0 && data->boxes_n < 256) { 44 | GameBox* box = &data->boxes[data->boxes_n++]; 45 | box->randomize(); 46 | 47 | // Add item for box 48 | char folder[64] = { 0 }; 49 | sprintf_s(folder, 64, "Game/Boxes/Box %02d", data->boxes_n); 50 | static const char* box_action_titles[5] = { "Hide", "Show", "Speed up", "Slow down" }; 51 | debuginator_create_array_item(data->debuginator, NULL, folder, 52 | "Change box properties.", on_box_action, box, 53 | box_action_titles, NULL, 4, 0); 54 | debuginator_set_edit_type(data->debuginator, folder, DEBUGINATOR_EditTypeActionArray); 55 | } 56 | else if (strcmp(value_title, "Clear all boxes") == 0 && data->boxes_n < 256) { 57 | data->boxes_n = 0; 58 | 59 | debuginator_remove_item_by_path(data->debuginator, "Game/Boxes"); 60 | } 61 | 62 | sprintf_s(data->box_string, "Box count: %d", data->boxes_n); 63 | } 64 | 65 | // static void on_number_changed(DebuginatorItem* item, void* value, const char* value_title, void* app_userdata) { 66 | // (void)item, value_title, app_userdata; 67 | // float fvalue = *(float*)value; 68 | // GameData* data = (GameData*)item->user_data; 69 | // data->background_color.r = (unsigned char)fvalue; 70 | // } 71 | 72 | static void debug_menu_setup(TheDebuginator* debuginator, GameData* data) { 73 | (void)data; 74 | { 75 | static const char* string_titles[5] = { "String A", "String B", "String C", "String D", "String E" }; 76 | debuginator_create_array_item(debuginator, NULL, "SDL Demo/Edit types/String list", 77 | "Example of an item having multiple strings to choose from.", NULL, NULL, 78 | string_titles, NULL, 5, 0); 79 | } 80 | 81 | { 82 | DebuginatorColor start = { 200, 0, 200, 200 }; 83 | debuginator_create_colorpicker_item(debuginator, "SDL Demo/Edit types/Colorpicker", 84 | "Example of the colorpicker edit type.", on_colorpicker, data, &start); 85 | } 86 | 87 | debuginator_create_bool_item(debuginator, "SDL Demo/Draw boxes", "Whether to draw the animated boxes or not.", &data->draw_boxes); 88 | 89 | { 90 | sprintf_s(data->box_string, "Box count: %d", data->boxes_n); 91 | static const char* string_titles[3] = { "Clear all boxes", "Add box", data->box_string }; 92 | debuginator_create_array_item(debuginator, NULL, "SDL Demo/Edit types/Box action list", 93 | "Various things to do with the demo boxes. Expand the item to see all the actions.", on_boxes_activated, data, 94 | string_titles, NULL, 3, 0); 95 | debuginator_set_edit_type(debuginator, "SDL Demo/Edit types/Box action list", DEBUGINATOR_EditTypeActionArray); 96 | } 97 | { 98 | char description_stack[256] = {}; 99 | sprintf_s(description_stack, "Hello from the stack!"); 100 | debuginator_create_bool_item(debuginator, "Test/Description/Stack allocated", description_stack, &data->mybool); 101 | char* description_heap = (char*)malloc(256); 102 | sprintf_s(description_heap, 256, "Hello from the heap!"); 103 | debuginator_create_bool_item(debuginator, "Test/Description/Heap allocated", description_heap, &data->mybool); 104 | free(description_heap); 105 | } 106 | debuginator_create_bool_item(debuginator, "Test/Description/Empty", "", &data->mybool); 107 | debuginator_create_bool_item(debuginator, "Test/Description/NULL", "", &data->mybool); 108 | debuginator_create_bool_item(debuginator, "Test/Description/Long", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbb cccccccccccccccccccccccc", &data->mybool); 109 | debuginator_create_bool_item(debuginator, "Test/Description/Newlines", "* A\n* B\n* C\n* D\n* E\n* F\n* G\n* H\n* I\n* J\n", &data->mybool); 110 | debuginator_create_bool_item(debuginator, "Test/LOL/XXZZ", "Change a bool.", &data->mybool); 111 | debuginator_create_bool_item(debuginator, "Test/LOL2/YY XX", "Change a bool.", &data->mybool); 112 | debuginator_create_bool_item(debuginator, "Test/LOL2/YY", "Change a bool.", &data->mybool); 113 | debuginator_create_bool_item(debuginator, "Test/LOL2/YYYY", "Change a bool.", &data->mybool); 114 | debuginator_create_bool_item(debuginator, "Test/LOL2/GameBooool0", "Change a bool.", &data->mybool); 115 | debuginator_create_bool_item(debuginator, "Test/YYY", NULL, &data->mybool); 116 | 117 | 118 | // debuginator_create_bool_item(debuginator, "SimpleBool 1", "Change a bool.", &data->mybool); 119 | // debuginator_create_bool_item(debuginator, "Folder/SimpleBool 2", "Change a bool.", &data->mybool); 120 | // debuginator_create_bool_item(debuginator, "Folder/SimpleBool 3", "Change a bool.", &data->mybool); 121 | // debuginator_create_bool_item(debuginator, "Folder/Subfolder with a long name ololololol/SimpleBool 4 with a really long long title", "Change a bool.", &data->mybool); 122 | // debuginator_create_bool_item(debuginator, "SDL Demo/Load test", "Change a bool.", &data->load_test); 123 | 124 | debuginator_create_folder_item(debuginator, NULL, "Folder 2"); 125 | char folder[64] = { 0 }; 126 | for (int i = 0; i < 5; i++) { 127 | for (int j = 0; j < 5; j++) { 128 | for (int l = 0; l < 5; l++) { 129 | sprintf_s(folder, 64, "Test/Hierarchy/Test%02d/Folder%02d/GameBool%02d", i, j, l); 130 | debuginator_create_bool_item(debuginator, folder, "Change a bool.", &data->gamebool); 131 | } 132 | } 133 | } 134 | 135 | // for (int i = 0; i < 1; i++) { 136 | // for (int j = 0; j < 1; j++) { 137 | // sprintf_s(folder, 64, "Game/Test%02d/GameBool%02d", i, j); 138 | // DebuginatorItem* item = debuginator_get_item(debuginator, NULL, folder, false); 139 | // debuginator_remove_item(debuginator, item); 140 | // } 141 | // } 142 | 143 | DebuginatorItem* thu_item = debuginator_create_folder_item(debuginator, NULL, "Test/HierarchyUnsorted"); 144 | thu_item->folder.is_sorted = false; 145 | debuginator_create_bool_item(debuginator, "Test/HierarchyUnsorted/First", "Change a bool.", &data->gamebool); 146 | debuginator_create_bool_item(debuginator, "Test/HierarchyUnsorted/Second", "Change a bool.", &data->gamebool); 147 | debuginator_create_bool_item(debuginator, "Test/HierarchyUnsorted/Folder/Third", "Change a bool.", &data->gamebool); 148 | debuginator_create_bool_item(debuginator, "Test/HierarchyUnsorted/Fourth", "Change a bool.", &data->gamebool); 149 | debuginator_create_bool_item(debuginator, "Test/HierarchyUnsorted/Fifth", "Change a bool.", &data->gamebool); 150 | debuginator_create_bool_item(debuginator, "Test/Hierarchy/1 First", "Change a bool.", &data->gamebool); 151 | debuginator_create_bool_item(debuginator, "Test/Hierarchy/2 Second", "Change a bool.", &data->gamebool); 152 | debuginator_create_bool_item(debuginator, "Test/Hierarchy/3 Folder/Third", "Actually fifth.", &data->gamebool); 153 | debuginator_create_bool_item(debuginator, "Test/Hierarchy/4 Fourth", "Change a bool.", &data->gamebool); 154 | debuginator_create_bool_item(debuginator, "Test/Hierarchy/5 Fifth", "Change a bool.", &data->gamebool); 155 | debuginator_create_bool_item(debuginator, "Test/Leaf/First/Enable", "Change a bool.", &data->gamebool); 156 | debuginator_create_bool_item(debuginator, "Test/Leaf/Second/Enable", "Change a bool.", &data->gamebool); 157 | debuginator_create_bool_item(debuginator, "Test/Leaf/Folder/Third/Enable", "Change a bool.", &data->gamebool); 158 | debuginator_create_bool_item(debuginator, "Test/Leaf/Folder/Fourth/Enable", "Change a bool.", &data->gamebool); 159 | debuginator_create_bool_item(debuginator, "Test/Leaf/Fifth/Enable", "Change a bool.", &data->gamebool); 160 | 161 | debuginator_set_collapsed(debuginator, debuginator_get_item(debuginator, NULL, "Test", NULL), true); 162 | } 163 | 164 | static GameData s_game_data; 165 | 166 | GameData* game_init(GuiHandle gui, TheDebuginator* debuginator) { 167 | memset(&s_game_data, 0, sizeof(s_game_data)); 168 | s_game_data.window_size = gui_get_window_size(gui); 169 | s_game_data.gui = gui; 170 | s_game_data.debuginator = debuginator; 171 | debug_menu_setup(debuginator, &s_game_data); 172 | 173 | return &s_game_data; 174 | } 175 | 176 | void game_update(GameData* game_data, float dt) { 177 | 178 | s_game_data.window_size = gui_get_window_size(game_data->gui); 179 | debuginator_set_screen_resolution(game_data->debuginator, (int)s_game_data.window_size.x, (int)s_game_data.window_size.y); 180 | 181 | // bouncing boxes 182 | if (game_data->draw_boxes) { 183 | for (size_t box_i = 0; box_i < game_data->boxes_n; box_i++) { 184 | GameBox* box = &game_data->boxes[box_i]; 185 | box->pos.x += box->velocity.x * (float)dt; 186 | box->pos.y += box->velocity.y * (float)dt; 187 | if (box->pos.x < 0 && box->velocity.x < 0 || box->pos.x + box->size.x > game_data->window_size.x && box->velocity.x > 0) { 188 | box->velocity.x *= -1; 189 | } 190 | if (box->pos.y - box->size.y < 0 && box->velocity.y < 0 || box->pos.y + box->size.y > game_data->window_size.y && box->velocity.y > 0) { 191 | box->velocity.y *= -1; 192 | } 193 | gui_draw_rect_filled(game_data->gui, box->pos, box->size, box->color); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /tests/sdl/game.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "gui.h" 6 | 7 | struct TheDebuginator; 8 | 9 | struct GameBox { 10 | GameBox() { 11 | } 12 | void randomize() { 13 | pos.x = pos.y = 0; 14 | size.x = 50 + 100 * ((float)rand()) / RAND_MAX; 15 | size.y = 50 + 100 * ((float)rand()) / RAND_MAX; 16 | velocity.x = 5 + 150 * ((float)rand()) / RAND_MAX; 17 | velocity.y = 5 + 150 * ((float)rand()) / RAND_MAX; 18 | color.r = (unsigned char)(50 + 100 * ((float)rand()) / RAND_MAX); 19 | color.g = (unsigned char)(50 + 100 * ((float)rand()) / RAND_MAX); 20 | color.b = (unsigned char)(50 + 100 * ((float)rand()) / RAND_MAX); 21 | color.a = (unsigned char)(150 + 100 * ((float)rand()) / RAND_MAX); 22 | alpha = color.a; 23 | } 24 | Vector2 pos; 25 | Vector2 size; 26 | Vector2 velocity; 27 | Color color; 28 | unsigned char alpha; 29 | }; 30 | 31 | struct GameData { 32 | GuiHandle gui; 33 | TheDebuginator* debuginator; 34 | bool mybool; 35 | bool gamebool; 36 | bool load_test; 37 | bool draw_boxes; 38 | char mystring[256]; 39 | GameBox boxes[256]; 40 | int boxes_n; 41 | char box_string[32]; 42 | Vector2 window_size; 43 | DebuginatorColor background_color; 44 | }; 45 | 46 | 47 | GameData* game_init(GuiHandle gui, TheDebuginator* debuginator); 48 | void game_update(GameData* game_data, float dt); 49 | -------------------------------------------------------------------------------- /tests/sdl/gui.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "gui.h" 9 | 10 | struct FontTemplate { 11 | TTF_Font* font; 12 | //const char* font; 13 | //int size; 14 | }; 15 | 16 | struct Gui { 17 | SDL_Window* window; 18 | SDL_Renderer* renderer; 19 | SDL_GameController* game_controllers[32]; 20 | 21 | FontTemplate font_templates[8]; 22 | int font_template_count; 23 | 24 | TextureHandle texture_handles[8]; 25 | SDL_Texture* textures[8]; 26 | int num_textures; 27 | }; 28 | 29 | static Gui guis[1]; 30 | static int gui_count = 0; 31 | static bool audio_enabled = false; 32 | 33 | 34 | GuiHandle gui_create_gui(int resx, int resy, const char* window_title, bool vsync_on) { 35 | if (gui_count > 0) { 36 | return (GuiHandle)nullptr; 37 | } 38 | 39 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) == 0) { 40 | audio_enabled = true; 41 | initAudio(); 42 | } 43 | else if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) != 0) { 44 | goto LABEL_error; 45 | } 46 | 47 | if (TTF_Init() != 0) { 48 | goto LABEL_error; 49 | } 50 | 51 | SDL_Window* window = SDL_CreateWindow(window_title, 52 | SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, resx, resy, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); 53 | if (window == NULL) { 54 | goto LABEL_error; 55 | } 56 | 57 | SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | (SDL_RENDERER_PRESENTVSYNC & (vsync_on ? 0xFFFFFFFF : 0))); 58 | if (renderer == NULL) { 59 | goto LABEL_error; 60 | } 61 | 62 | goto LABEL_init_success; 63 | 64 | LABEL_error:; 65 | if (audio_enabled) { 66 | endAudio(); 67 | } 68 | 69 | SDL_Quit(); 70 | return (GuiHandle)nullptr; 71 | 72 | LABEL_init_success:; 73 | 74 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); 75 | 76 | Gui* gui = &guis[gui_count++]; 77 | SDL_memset(gui, 0, sizeof(*gui)); 78 | gui->renderer = renderer; 79 | gui->window = window; 80 | 81 | // Set up gamepads. Note: Doesn't handle adding while running. 82 | // If you don't have this file, make sure it gets copied from 3rdparty\SDL_GameControllerDB-master 83 | SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); 84 | SDL_JoystickEventState(SDL_ENABLE); 85 | int num_joysticks = SDL_NumJoysticks(); 86 | for( int i=0; i < num_joysticks; i++ ) { 87 | if (SDL_IsGameController(i)) { 88 | gui->game_controllers[i] = SDL_GameControllerOpen(i); 89 | } 90 | } 91 | 92 | return (GuiHandle)gui; 93 | } 94 | 95 | void gui_destroy_gui(GuiHandle gui_handle) { 96 | Gui* gui = (Gui*)gui_handle; 97 | 98 | SDL_DestroyWindow(gui->window); 99 | SDL_Quit(); 100 | } 101 | 102 | void gui_frame_begin(GuiHandle gui_handle) { 103 | Gui* gui = (Gui*)gui_handle; 104 | SDL_SetRenderDrawColor(gui->renderer, 30, 30, 30, 255); 105 | SDL_RenderClear(gui->renderer); 106 | } 107 | 108 | void gui_frame_end(GuiHandle gui_handle){ 109 | Gui* gui = (Gui*)gui_handle; 110 | SDL_RenderPresent(gui->renderer); 111 | } 112 | 113 | TextureHandle gui_load_texture(GuiHandle gui_handle, const char* texture_filename) { 114 | Gui* gui = (Gui*)gui_handle; 115 | SDL_Texture* texture = IMG_LoadTexture(gui->renderer, texture_filename); 116 | // TODO Assert 117 | gui->textures[gui->num_textures] = texture; 118 | return (TextureHandle)gui->num_textures++; 119 | } 120 | 121 | void gui_draw_texture(GuiHandle gui_handle, TextureHandle handle, Vector2 position, Vector2 size) { 122 | Gui* gui = (Gui*)gui_handle; 123 | SDL_Texture* texture = gui->textures[handle]; 124 | SDL_Rect texture_rect; 125 | texture_rect.x = (int)position.x; 126 | texture_rect.y = (int)position.y; 127 | texture_rect.w = (int)size.x; 128 | texture_rect.h = (int)size.y; 129 | if (texture_rect.w == 0) { 130 | int w, h; 131 | int res = SDL_QueryTexture(texture, NULL, NULL, &w, &h); 132 | (void)res; 133 | float ratio = (float)w / h; 134 | texture_rect.w = (int)(texture_rect.h * ratio); 135 | } 136 | if (texture_rect.h == 0) { 137 | int w, h; 138 | int res = SDL_QueryTexture(texture, NULL, NULL, &w, &h); 139 | (void)res; 140 | float ratio = (float)w / h; 141 | texture_rect.h = (int)(texture_rect.w / ratio); 142 | } 143 | SDL_RenderCopy(gui->renderer, texture, NULL, &texture_rect); 144 | } 145 | 146 | FontTemplateHandle gui_register_font_template(GuiHandle gui_handle, const char* font, int size) { 147 | TTF_Font* ttf_font = TTF_OpenFont(font, size); 148 | if (ttf_font == NULL) { 149 | return 0; 150 | } 151 | 152 | Gui* gui = (Gui*)gui_handle; 153 | FontTemplate* font_template = &gui->font_templates[gui->font_template_count++]; 154 | font_template->font = ttf_font; 155 | 156 | return (FontTemplateHandle)font_template; 157 | } 158 | 159 | void gui_unregister_font_template(GuiHandle gui_handle, FontTemplateHandle font_handle) { 160 | (void)gui_handle; 161 | FontTemplate* font_template = (FontTemplate*)font_handle; 162 | TTF_CloseFont(font_template->font); 163 | font_template->font = 0; 164 | } 165 | 166 | void gui_draw_text(GuiHandle gui_handle, const char* text, Vector2 position, FontTemplateHandle font_handle, Color color) { 167 | Gui* gui = (Gui*)gui_handle; 168 | SDL_Color* text_color = (SDL_Color*)&color; 169 | FontTemplate* font_template = (FontTemplate*)font_handle; 170 | SDL_Surface* text_surface = TTF_RenderText_Blended(font_template->font, text, *text_color); 171 | if (text_surface == NULL) { 172 | // TODO Assert 173 | return; 174 | } 175 | 176 | SDL_Texture* text_texture = SDL_CreateTextureFromSurface(gui->renderer, text_surface); 177 | if (text_texture == NULL) { 178 | // TODO Assert 179 | return; 180 | } 181 | 182 | SDL_FreeSurface(text_surface); 183 | 184 | SDL_Rect rectangle; 185 | rectangle.x = (int)position.x; 186 | rectangle.y = (int)position.y; 187 | TTF_SizeText(font_template->font, text, &rectangle.w, &rectangle.h); 188 | rectangle.y -= rectangle.h / 2; 189 | 190 | SDL_SetRenderDrawColor(gui->renderer, 255, 255, 255, 255); 191 | SDL_RenderCopy(gui->renderer, text_texture, NULL, &rectangle); 192 | 193 | SDL_DestroyTexture(text_texture); 194 | } 195 | 196 | void gui_draw_rect_filled(GuiHandle gui_handle, Vector2 position, Vector2 size, Color color) { 197 | Gui* gui = (Gui*)gui_handle; 198 | SDL_Rect rect; 199 | rect.x = (int)position.x; 200 | rect.y = (int)position.y; 201 | rect.w = (int)size.x; 202 | rect.h = (int)size.y; 203 | 204 | SDL_SetRenderDrawColor(gui->renderer, color.r, color.g, color.b, color.a); 205 | SDL_RenderFillRect(gui->renderer, &rect); 206 | } 207 | 208 | void gui_word_wrap(GuiHandle gui_handle, const char* text, FontTemplateHandle font_handle, float max_width, int* row_count, int* row_lengths, int row_lengths_buffer_size) { 209 | (void)gui_handle; 210 | FontTemplate* font_template = (FontTemplate*)font_handle; 211 | 212 | const char* current_line = text; 213 | const char* current_word = text; 214 | const char* current_char = text; 215 | char line[256]; 216 | while (*current_char != '\0') { 217 | bool found_newline = false; 218 | while (*current_char != '\0') { 219 | if (*current_char == ' ') { 220 | // We found a word end. 221 | // Include all trailing spaces in this word 222 | while (*current_char == ' ') { 223 | ++current_char; 224 | } 225 | break; 226 | } 227 | 228 | if (*current_char == '\n') { 229 | // We found a new line 230 | ++current_char; 231 | found_newline = true; 232 | break; 233 | } 234 | 235 | ++current_char; 236 | } 237 | 238 | memcpy(line, current_line, current_char - current_line); 239 | line[current_char - current_line] = '\0'; 240 | int width; 241 | if (TTF_SizeText(font_template->font, line, &width, NULL) != 0) { 242 | break; 243 | } 244 | 245 | bool line_too_long = width >= max_width; 246 | if (line_too_long || found_newline) { 247 | bool word_longer_than_line = current_word == current_line; 248 | if (found_newline) { 249 | row_lengths[*row_count] = (int)(current_char - current_line - 1); 250 | ++*row_count; 251 | current_word = current_char; 252 | current_line = current_char; 253 | } 254 | else if (word_longer_than_line) { 255 | row_lengths[*row_count] = (int)(current_char - current_line); 256 | ++*row_count; 257 | current_word = current_char; 258 | current_line = current_char; 259 | } 260 | else { 261 | // Move current word to next line. 262 | row_lengths[*row_count] = (int)(current_word - current_line); 263 | ++*row_count; 264 | 265 | //current_word = current_char; 266 | current_line = current_word; 267 | } 268 | 269 | if (*row_count == row_lengths_buffer_size) { 270 | break; 271 | } 272 | } 273 | else { 274 | // Current word fit. 275 | current_word = current_char; 276 | } 277 | } 278 | 279 | if (*current_line != '\0') { 280 | // Add last line 281 | row_lengths[*row_count] = (int)(current_char - current_line); 282 | ++*row_count; 283 | } 284 | } 285 | 286 | Vector2 gui_text_size(GuiHandle gui_handle, const char* text, FontTemplateHandle font_handle) { 287 | Gui* gui = (Gui*)gui_handle; 288 | (void)gui; 289 | FontTemplate* font_template = (FontTemplate*)font_handle; 290 | 291 | if (text[0] == '\0') { 292 | return Vector2(0, 0); 293 | } 294 | 295 | int x, y; 296 | TTF_SizeText(font_template->font, text, &x, &y); 297 | Vector2 out_text_size((float)x, (float)y); 298 | return out_text_size; 299 | } 300 | 301 | Vector2 gui_get_window_size(GuiHandle gui_handle) { 302 | Gui* gui = (Gui*)gui_handle; 303 | int w, h; 304 | SDL_GetWindowSize(gui->window, &w, &h); 305 | return Vector2((float)w, (float)h); 306 | } 307 | 308 | SDL_GameController** gui_get_controllers(GuiHandle gui_handle) { 309 | Gui* gui = (Gui*)gui_handle; 310 | return gui->game_controllers; 311 | } 312 | 313 | void gui_play_sound(DebuginatorSoundEvent event) { 314 | switch (event) { 315 | case DEBUGINATOR_SoundEventEnter: { 316 | playSound("bong_001.wav", SDL_MIX_MAXVOLUME / 16); 317 | } 318 | break; 319 | case DEBUGINATOR_SoundEventActivate: { 320 | playSound("select_007.wav", SDL_MIX_MAXVOLUME / 4); 321 | } 322 | break; 323 | case DEBUGINATOR_SoundEventCollapse: { 324 | playSound("pluck_001.wav", SDL_MIX_MAXVOLUME / 6); 325 | } 326 | break; 327 | case DEBUGINATOR_SoundEventExpand: { 328 | playSound("pluck_002.wav", SDL_MIX_MAXVOLUME / 6); 329 | } 330 | break; 331 | 332 | } 333 | } -------------------------------------------------------------------------------- /tests/sdl/gui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "../../the_debuginator.h" 6 | 7 | typedef intptr_t GuiHandle; 8 | typedef intptr_t FontTemplateHandle; 9 | typedef intptr_t TextureHandle; 10 | 11 | struct Vector2 { 12 | Vector2() : x(0), y(0) {} 13 | Vector2(float x, float y) : x(x), y(y) {} 14 | float x; 15 | float y; 16 | }; 17 | 18 | struct Color { 19 | Color() : r(0), g(0), b(0), a(0) {} 20 | Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) : r(r), g(g), b(b), a(a) {} 21 | unsigned char r; 22 | unsigned char g; 23 | unsigned char b; 24 | unsigned char a; 25 | }; 26 | 27 | GuiHandle gui_create_gui(int resx, int resy, const char* window_title, bool vsync_on); 28 | void gui_destroy_gui(GuiHandle gui_handle); 29 | 30 | void gui_frame_begin(GuiHandle gui_hande); 31 | void gui_frame_end(GuiHandle gui_hande); 32 | 33 | TextureHandle gui_load_texture(GuiHandle gui, const char* texture_filename); 34 | void gui_draw_texture(GuiHandle gui_handle, TextureHandle handle, Vector2 position, Vector2 size); 35 | 36 | FontTemplateHandle gui_register_font_template(GuiHandle gui_handle, const char* font, int size); 37 | void gui_unregister_font_template(GuiHandle gui_handle, FontTemplateHandle font_handle); 38 | 39 | void gui_draw_text(GuiHandle gui_handle, const char* text, Vector2 position, FontTemplateHandle font, Color color); 40 | void gui_draw_rect_filled(GuiHandle gui_handle, Vector2 position, Vector2 size, Color color); 41 | 42 | void gui_word_wrap(GuiHandle gui_handle, const char* text, FontTemplateHandle font_handle, float max_width, int* row_count, int* row_lengths, int row_lengths_buffer_size); 43 | Vector2 gui_text_size(GuiHandle gui_handle, const char* text, FontTemplateHandle font_handle); 44 | 45 | Vector2 gui_get_window_size(GuiHandle gui_handle); 46 | SDL_GameController** gui_get_controllers(GuiHandle gui_handle); 47 | 48 | void gui_play_sound(DebuginatorSoundEvent event); 49 | -------------------------------------------------------------------------------- /tests/sdl/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | static const int WIDTH = 500; 5 | 6 | static void unittest_debuginator_assert(bool test) { 7 | if (!test) { 8 | } 9 | assert(test); 10 | //DebugBreak(); 11 | } 12 | 13 | #define DEBUGINATOR_assert unittest_debuginator_assert 14 | #define DEBUGINATOR_static_assert unittest_debuginator_assert 15 | #define ASSERT unittest_debuginator_assert 16 | #define DEBUGINATOR_IMPLEMENTATION 17 | #include "../../the_debuginator.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "gui.h" 25 | #include "game.h" 26 | #include "demo.h" 27 | 28 | enum FontTemplate { 29 | FONT_DemoHeader, 30 | FONT_ItemTitle, 31 | FONT_ItemDescription, 32 | FONT_Count 33 | }; 34 | 35 | static FontTemplateHandle s_fonts[16]; 36 | 37 | static bool theme_setup(GuiHandle gui) { 38 | memset(s_fonts, 0, sizeof(*s_fonts)); 39 | s_fonts[FONT_DemoHeader] = gui_register_font_template(gui, "LiberationMono-Bold.ttf", 72); 40 | s_fonts[FONT_ItemTitle] = gui_register_font_template(gui, "LiberationMono-Regular.ttf", 18); 41 | s_fonts[FONT_ItemDescription] = gui_register_font_template(gui, "LiberationSerif-Regular.ttf", 18); 42 | 43 | for (size_t i = 0; i < FONT_Count; i++) { 44 | if (s_fonts[i] == 0) { 45 | return false; 46 | } 47 | } 48 | 49 | return true; 50 | } 51 | 52 | void draw_text(const char* text, DebuginatorVector2* position, DebuginatorColor* color, DebuginatorFont* font, void* userdata) { 53 | GuiHandle gui = (GuiHandle)userdata; 54 | int color_index = font->draw_type == DEBUGINATOR_ItemDescription ? (int)FONT_ItemDescription : (int)FONT_ItemTitle; 55 | gui_draw_text(gui, text, *(Vector2*)position, s_fonts[color_index], *(Color*)color); 56 | } 57 | 58 | void draw_rect(DebuginatorVector2* position, DebuginatorVector2* size, DebuginatorColor* color, void* userdata) { 59 | gui_draw_rect_filled((GuiHandle)userdata, *(Vector2*)position, *(Vector2*)size, *(Color*)color); 60 | } 61 | 62 | void draw_image(DebuginatorVector2* position, DebuginatorVector2* size, DebuginatorImageHandle handle, void* userdata) { 63 | gui_draw_texture((GuiHandle)userdata, handle.h.ull_value, *(Vector2*)position, *(Vector2*)size); 64 | } 65 | 66 | void word_wrap(const char* text, DebuginatorFont* font, float max_width, int* row_count, int* row_lengths, int row_lengths_buffer_size, void* app_userdata) { 67 | int color_index = font->draw_type == DEBUGINATOR_ItemDescription ? (int)FONT_ItemDescription : (int)FONT_ItemTitle; 68 | gui_word_wrap((GuiHandle)app_userdata, text, s_fonts[color_index], max_width, row_count, row_lengths, row_lengths_buffer_size); 69 | } 70 | 71 | DebuginatorVector2 text_size(const char* text, DebuginatorFont* font, void* userdata) { 72 | int color_index = font->draw_type == DEBUGINATOR_ItemDescription ? (int)FONT_ItemDescription : (int)FONT_ItemTitle; 73 | Vector2 text_size = gui_text_size((GuiHandle)userdata, text, s_fonts[color_index]); 74 | return *(DebuginatorVector2*)&text_size; 75 | } 76 | 77 | void log(const char* text, void* userdata) { 78 | (void)userdata; 79 | printf("[Debuginator] %s\n", text); 80 | } 81 | 82 | void play_sound(DebuginatorSoundEvent event, void* userdata) { 83 | (void)userdata; 84 | gui_play_sound(event); 85 | } 86 | 87 | struct SaveData { 88 | char* buffer; 89 | int buffer_size; 90 | int buffer_capacity; 91 | }; 92 | 93 | bool save_item(const char* key, const char* value, void* userdata) { 94 | SaveData* save_data = (SaveData*)userdata; 95 | if (save_data->buffer_capacity - save_data->buffer_size < 512) { 96 | return false; 97 | } 98 | 99 | int chars = sprintf_s(save_data->buffer + save_data->buffer_size, save_data->buffer_capacity - save_data->buffer_size, "%s=%s\n", key, value); 100 | save_data->buffer_size += chars; 101 | return true; 102 | } 103 | 104 | void save(TheDebuginator* debuginator) { 105 | SaveData save_data; 106 | save_data.buffer = NULL; 107 | save_data.buffer_size = 0; 108 | save_data.buffer_capacity = 1024; 109 | 110 | while (true) { 111 | save_data.buffer = (char*)malloc(save_data.buffer_capacity); 112 | memset(save_data.buffer, 0, save_data.buffer_capacity); 113 | bool saved = debuginator_save(debuginator, save_item, &save_data); 114 | if (saved) { 115 | break; 116 | } 117 | 118 | save_data.buffer_size = 0; 119 | save_data.buffer_capacity *= 16; 120 | free(save_data.buffer); 121 | } 122 | 123 | FILE* file = NULL; 124 | int error = fopen_s(&file, "DebuginatorConfig.txt", "w"); 125 | if (file == NULL || error < 0) { 126 | return; 127 | } 128 | 129 | fputs(save_data.buffer, file); 130 | fclose(file); 131 | free(save_data.buffer); 132 | } 133 | 134 | bool load(TheDebuginator* debuginator, char* loaded_data_buffer, int loaded_buffer_size) { 135 | FILE* file = NULL; 136 | int error = fopen_s(&file, "DebuginatorConfig.txt", "r"); 137 | if (file == NULL || error < 0) { 138 | return true; 139 | } 140 | 141 | bool result = true; 142 | char load_buffer[1024] = { 0 }; 143 | fread_s(load_buffer, 1024, 1, 1023, file); 144 | const char* data = load_buffer; 145 | while (*data != '\0') { 146 | if (loaded_buffer_size < 512) { 147 | result = false; 148 | break; 149 | } 150 | 151 | const char* key = data; 152 | const char* value = NULL; 153 | const char* loaded_data_buffer_key = NULL; 154 | const char* loaded_data_buffer_value = NULL; 155 | while (*data++ != '\0') { 156 | if (*data == '=') { 157 | memcpy(loaded_data_buffer, key, data - key); 158 | loaded_data_buffer[data - key] = 0; 159 | loaded_data_buffer_key = loaded_data_buffer; 160 | loaded_data_buffer += data - key + 1; 161 | loaded_buffer_size -= (int)(data - key + 1); 162 | value = (++data); 163 | } 164 | 165 | if (*data == '\n' && value != NULL) { 166 | memcpy(loaded_data_buffer, value, data - value); 167 | loaded_data_buffer[data - value] = 0; 168 | loaded_data_buffer_value = loaded_data_buffer; 169 | loaded_data_buffer += data - value + 1; 170 | loaded_buffer_size -= (int)(data - value + 1); 171 | // Debuginator needs to own this 172 | const char* value_owned = debuginator_copy_string(debuginator, loaded_data_buffer_value, 0); 173 | debuginator_load_item(debuginator, loaded_data_buffer_key, value_owned); 174 | ++data; 175 | break; 176 | } 177 | } 178 | } 179 | 180 | fclose(file); 181 | return result; 182 | } 183 | 184 | bool handle_debuginator_keyboard_input_event(SDL_Event* event, TheDebuginator* debuginator, DemoData* demodata) { 185 | if (event->type == SDL_MOUSEBUTTONDOWN) { 186 | DebuginatorItem* hot_mouse_item = debuginator_get_item_at_mouse_cursor(debuginator, NULL); 187 | if (hot_mouse_item == NULL) { 188 | demo_trigger_next(demodata); 189 | } 190 | } 191 | 192 | if (!debuginator_is_open(debuginator)) { 193 | if (event->type == SDL_TEXTINPUT) { 194 | int text_length = (int)strlen(event->text.text); 195 | if (text_length > 0) { 196 | char key[] = { event->text.text[0], 0 }; 197 | debuginator_activate_hot_key(debuginator, key); 198 | } 199 | 200 | return true; 201 | } 202 | 203 | if (event->type == SDL_KEYDOWN) { 204 | if (event->key.keysym.sym == SDLK_RIGHT || event->key.keysym.scancode == SDL_SCANCODE_GRAVE) { 205 | debuginator_set_open(debuginator, true); 206 | return true; 207 | } 208 | } 209 | 210 | return false; 211 | } 212 | 213 | int hot_item_index; 214 | DebuginatorItem* hot_item = debuginator_get_hot_item(debuginator, &hot_item_index); 215 | switch (event->type) { 216 | case SDL_KEYDOWN: 217 | { 218 | if (event->key.keysym.scancode == SDL_SCANCODE_LCTRL || event->key.keysym.scancode == SDL_SCANCODE_RCTRL) { 219 | return true; 220 | } 221 | 222 | if (event->key.keysym.sym == SDLK_UP) { 223 | debuginator_reset_scrolling(debuginator); 224 | bool long_move = (event->key.keysym.mod & SDLK_LCTRL) > 0; 225 | debuginator_move_to_prev_leaf(debuginator, long_move); 226 | return true; 227 | } 228 | else if (event->key.keysym.sym == SDLK_DOWN) { 229 | debuginator_reset_scrolling(debuginator); 230 | bool long_move = (event->key.keysym.mod & SDLK_LCTRL) > 0; 231 | debuginator_move_to_next_leaf(debuginator, long_move); 232 | return true; 233 | } 234 | else if (event->key.keysym.sym == SDLK_LEFT || event->key.keysym.sym == SDLK_ESCAPE || event->key.keysym.scancode == SDL_SCANCODE_GRAVE) { 235 | debuginator_reset_scrolling(debuginator); 236 | if (debuginator->is_open && (debuginator_is_folder(hot_item) || !hot_item->leaf.is_expanded)) { 237 | debuginator_set_open(debuginator, false); 238 | save(debuginator); 239 | return true; 240 | } 241 | else if (!debuginator_is_folder(hot_item) && hot_item->leaf.is_expanded) { 242 | debuginator_move_to_parent(debuginator); 243 | return true; 244 | } 245 | } 246 | else if (event->key.keysym.sym == SDLK_RIGHT) { 247 | debuginator_reset_scrolling(debuginator); 248 | if (debuginator_is_folder(hot_item)) { 249 | debuginator_set_collapsed(debuginator, hot_item, !debuginator_is_collapsed(hot_item)); 250 | return true; 251 | } 252 | else { 253 | bool direct_activate = (event->key.keysym.mod & SDLK_LCTRL) > 0; 254 | debuginator_move_to_child(debuginator, direct_activate); 255 | return true; 256 | } 257 | } 258 | else if (event->key.keysym.sym == SDLK_BACKSPACE) { 259 | if (debuginator->filter_length > 0) { 260 | char filter[64] = { 0 }; 261 | strcpy_s(filter, 64, debuginator->filter); 262 | filter[--debuginator->filter_length] = '\0'; 263 | debuginator_update_filter(debuginator, filter); 264 | //debuginator->filter[--debuginator->filter_length] = '\0'; 265 | } 266 | else if (debuginator->filter_enabled) { 267 | debuginator_set_filtering_enabled(debuginator, false); 268 | } 269 | else { 270 | debuginator_set_filtering_enabled(debuginator, true); 271 | } 272 | } 273 | else if (event->key.keysym.sym >= 'a' && event->key.keysym.sym <= 'z') { 274 | int hot_mouse_item_index; 275 | DebuginatorItem* hot_mouse_item = debuginator_get_item_at_mouse_cursor(debuginator, &hot_mouse_item_index); 276 | if (hot_mouse_item != NULL) { 277 | hot_item = hot_mouse_item; 278 | hot_item_index = hot_mouse_item_index; 279 | } 280 | 281 | int buffer_size = 256; 282 | char path[256]; 283 | debuginator_get_path(debuginator, hot_item, path, &buffer_size); 284 | ASSERT(buffer_size == 256); 285 | char key[] = { (char)event->key.keysym.sym, 0 }; 286 | 287 | DebuginatorItem* prev_hot_key_item = debuginator_get_first_assigned_hot_key_item(debuginator, key); 288 | if ( prev_hot_key_item == hot_item ) { 289 | // Key was already assigned to this item, toggle it off 290 | debuginator_unassign_hot_key(debuginator, key); 291 | break; 292 | } 293 | 294 | bool multi_add_key_held = SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL); 295 | if (prev_hot_key_item && !multi_add_key_held) { 296 | // Item was already assigned to something and user just wants the new one assigned, 297 | // so unassign the old one first. 298 | debuginator_unassign_hot_key(debuginator, key); 299 | } 300 | 301 | debuginator_assign_hot_key(debuginator, key, path, hot_item_index, NULL); 302 | } 303 | break; 304 | } 305 | case SDL_TEXTINPUT: 306 | { 307 | int new_text_length = (int)strlen(event->text.text); 308 | if (debuginator->filter_enabled) { 309 | if (debuginator->filter_length + new_text_length >= sizeof(debuginator->filter)) { 310 | break; 311 | } 312 | 313 | char filter[64] = { 0 }; 314 | ASSERT(sizeof(filter) >= sizeof(debuginator->filter)); 315 | strcpy_s(filter, sizeof(debuginator->filter), debuginator->filter); 316 | strcat_s(filter, sizeof(debuginator->filter), event->text.text); 317 | // if (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL);) 318 | debuginator->filter_length = (int)strlen(filter); 319 | debuginator_update_filter(debuginator, filter); 320 | } 321 | break; 322 | } 323 | case SDL_MOUSEMOTION: { 324 | DebuginatorVector2 mouse_cursor_pos = { (float)event->motion.x, (float)event->motion.y }; 325 | debuginator_set_mouse_cursor_pos(debuginator, &mouse_cursor_pos); 326 | 327 | break; 328 | } 329 | case SDL_MOUSEWHEEL: { 330 | debuginator_apply_scroll(debuginator, event->wheel.y * debuginator->item_height * 1); 331 | 332 | break; 333 | } 334 | case SDL_MOUSEBUTTONDOWN: { 335 | DebuginatorVector2 mouse_cursor_pos = { (float)event->button.x, (float)event->button.y }; 336 | debuginator_set_mouse_cursor_pos(debuginator, &mouse_cursor_pos); 337 | if (event->button.button == SDL_BUTTON_LEFT && event->button.state == SDL_PRESSED) { 338 | debuginator_activate_item_at_mouse_cursor(debuginator); 339 | } 340 | else if (event->button.button == SDL_BUTTON_RIGHT && event->button.state == SDL_PRESSED) { 341 | debuginator_expand_item_at_mouse_cursor(debuginator, DEBUGINATOR_Toggle); 342 | } 343 | 344 | break; 345 | } 346 | case SDL_WINDOWEVENT: { 347 | if (event->window.event == SDL_WINDOWEVENT_LEAVE) { 348 | DebuginatorVector2 mouse_cursor_pos = { -1.f, -1.f }; 349 | debuginator_set_mouse_cursor_pos(debuginator, &mouse_cursor_pos); 350 | } 351 | 352 | break; 353 | } 354 | } 355 | 356 | 357 | return false; 358 | } 359 | 360 | bool handle_debuginator_gamepad_input_event(SDL_Event* event, TheDebuginator* debuginator, SDL_GameControllerButton& current_button, double& time_since_button_pressed, float& scroll_speed) { 361 | switch (event->type) { 362 | case SDL_CONTROLLERBUTTONDOWN: 363 | { 364 | SDL_ControllerButtonEvent& button_ev = event->cbutton; 365 | current_button = (SDL_GameControllerButton)button_ev.button; 366 | time_since_button_pressed = 0; 367 | 368 | debuginator_reset_scrolling(debuginator); 369 | 370 | if (button_ev.button == SDL_CONTROLLER_BUTTON_DPAD_UP) { 371 | bool long_move = (event->key.keysym.mod & SDLK_LCTRL) > 0; 372 | debuginator_move_to_prev_leaf(debuginator, long_move); 373 | return true; 374 | } 375 | else if (button_ev.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { 376 | bool long_move = (event->key.keysym.mod & SDLK_LCTRL) > 0; 377 | debuginator_move_to_next_leaf(debuginator, long_move); 378 | return true; 379 | } 380 | else if (current_button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { 381 | bool long_move = true; 382 | debuginator_move_to_prev_leaf(debuginator, long_move); 383 | return true; 384 | } 385 | else if (current_button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { 386 | bool long_move = true; 387 | debuginator_move_to_next_leaf(debuginator, long_move); 388 | return true; 389 | } 390 | else if (button_ev.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT || button_ev.button == SDL_CONTROLLER_BUTTON_BACK) { 391 | if (debuginator->is_open && !debuginator->hot_item->leaf.is_expanded) { 392 | debuginator_set_open(debuginator, false); 393 | save(debuginator); 394 | return true; 395 | } 396 | else if (!debuginator->hot_item->is_folder && debuginator->hot_item->leaf.is_expanded) { 397 | debuginator_move_to_parent(debuginator); 398 | return true; 399 | } 400 | } 401 | else if (button_ev.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { 402 | if (!debuginator->is_open) { 403 | debuginator_set_open(debuginator, true); 404 | return true; 405 | } 406 | else { 407 | bool direct_activate = (event->key.keysym.mod & SDLK_LCTRL) > 0; 408 | debuginator_move_to_child(debuginator, direct_activate); 409 | return true; 410 | } 411 | } 412 | else if (button_ev.button == SDL_CONTROLLER_BUTTON_A) { 413 | bool direct_activate = true; 414 | debuginator_move_to_child(debuginator, direct_activate); 415 | return true; 416 | } 417 | 418 | } 419 | break; 420 | case SDL_CONTROLLERBUTTONUP: 421 | { 422 | current_button = SDL_CONTROLLER_BUTTON_INVALID; 423 | //in_repeat_mode = false; 424 | } 425 | break; 426 | case SDL_CONTROLLERAXISMOTION: 427 | { 428 | SDL_ControllerAxisEvent& motion_ev = event->caxis; 429 | if (motion_ev.axis == SDL_CONTROLLER_AXIS_LEFTY) { 430 | scroll_speed = 0; 431 | if (motion_ev.value < -5000 || motion_ev.value > 5000) { 432 | scroll_speed = (float)(-3000 * (motion_ev.value / 32767.0) * (motion_ev.value / 32767.0) * (motion_ev.value / 32767.0)); 433 | } 434 | } 435 | } 436 | break; 437 | } 438 | 439 | return false; 440 | } 441 | 442 | void handle_debuginator_gamepad_input(TheDebuginator* debuginator, SDL_GameControllerButton current_button, double& time_since_button_pressed) { 443 | if (current_button == SDL_CONTROLLER_BUTTON_INVALID) { 444 | return; 445 | } 446 | 447 | if (time_since_button_pressed < 0.3) { 448 | return; 449 | } 450 | 451 | time_since_button_pressed = 0.25; 452 | if (current_button == SDL_CONTROLLER_BUTTON_DPAD_UP) { 453 | bool long_move = false; 454 | debuginator_move_to_prev_leaf(debuginator, long_move); 455 | return; 456 | } 457 | else if (current_button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) { 458 | bool long_move = false; 459 | debuginator_move_to_next_leaf(debuginator, long_move); 460 | return; 461 | } 462 | else if (current_button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { 463 | bool long_move = true; 464 | debuginator_move_to_prev_leaf(debuginator, long_move); 465 | return; 466 | } 467 | else if (current_button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { 468 | bool long_move = true; 469 | debuginator_move_to_next_leaf(debuginator, long_move); 470 | return; 471 | } 472 | } 473 | void handle_debuginator_gamepad_input_immediate(TheDebuginator* debuginator, GuiHandle gui) { 474 | SDL_GameController** controller = gui_get_controllers(gui); 475 | while(*controller) { 476 | int axis_value = SDL_GameControllerGetAxis(*controller, SDL_CONTROLLER_AXIS_RIGHTY); 477 | if (axis_value * axis_value > 8000 * 8000) { 478 | float change = 0.1f * (axis_value / 32767.0f) * (axis_value / 32767.0f) * (axis_value / 32767.0f); 479 | DebuginatorItem* hot_item = debuginator_get_hot_item(debuginator, NULL); 480 | debuginator_modify_value(debuginator, hot_item, 0, change, false); 481 | } 482 | 483 | ++controller; 484 | } 485 | } 486 | 487 | int main(int argc, char **argv) 488 | { 489 | (void)(argc, argv); 490 | 491 | int res_x = 1280; 492 | int res_y = 720; 493 | bool vsync_on = true; 494 | GuiHandle gui = gui_create_gui(res_x, res_y, "The Debuginator - SDL Demo", vsync_on); 495 | if (gui == 0) { 496 | return 1; 497 | } 498 | 499 | if (!theme_setup(gui)) { 500 | gui_destroy_gui(gui); 501 | return 1; 502 | } 503 | 504 | TextureHandle colorpicker_image = gui_load_texture(gui, "color_picker.png"); 505 | 506 | int memory_arena_capacity = 1024 * 1024 * 1; 507 | char* memory_arena = (char*)malloc(memory_arena_capacity); 508 | TheDebuginatorConfig config; 509 | debuginator_get_default_config(&config); 510 | config.memory_arena = memory_arena; 511 | config.memory_arena_capacity = memory_arena_capacity; 512 | config.draw_image = draw_image; 513 | config.draw_rect = draw_rect; 514 | config.draw_text = draw_text; 515 | config.word_wrap = word_wrap; 516 | config.text_size = text_size; 517 | config.log = log; 518 | config.play_sound = play_sound; 519 | config.app_user_data = (void*)gui; 520 | config.size.x = 500; 521 | config.size.y = (float)res_y; 522 | config.screen_resolution.x = (float)res_x; 523 | config.screen_resolution.y = (float)res_y; 524 | //config.open_direction = -1; // To show it on the right side of the screen 525 | config.create_default_debuginator_items = true; 526 | config.colorpicker_image.h.ull_value = colorpicker_image; 527 | 528 | TheDebuginator debuginator; 529 | debuginator_create(&config, &debuginator); 530 | 531 | char* loaded_data_buffer = (char*)malloc(10 * 1024 * 1024); 532 | bool load_result = load(&debuginator, loaded_data_buffer, 10 * 1024 * 1024); 533 | if (!load_result) { 534 | return 1; 535 | } 536 | free(loaded_data_buffer); 537 | 538 | GameData* gamedata = game_init(gui, &debuginator); 539 | DemoData* demodata = demo_init(gui, &debuginator); 540 | 541 | bool limit_framerate = false; 542 | debuginator_create_bool_item(&debuginator, "SDL Demo/Throttle framerate", "Disables sleeping between frames.", &limit_framerate); 543 | 544 | bool show_framerate = false; 545 | debuginator_create_bool_item(&debuginator, "SDL Demo/Show framerate", "Shows framerate and frame time in ms.", &show_framerate); 546 | 547 | const char* preset_paths[2] = { "SDL Demo/Throttle framerate", "SDL Demo/Show framerate" }; 548 | const char* preset_value_titles[2] = { "True", "False" }; 549 | debuginator_create_preset_item(&debuginator, "SDL Demo/Edit types/Preset", preset_paths, preset_value_titles, NULL, 2); 550 | 551 | float demo_y_offset = 0; 552 | debuginator_create_numberrange_float_item(&debuginator, "SDL Demo/Edit types/Number slider", "Example of the NumberRange edit type", &demo_y_offset, -100, 200); 553 | 554 | bool show_background = true; 555 | debuginator_create_bool_item(&debuginator, "SDL Demo/Show background image", "", &show_background); 556 | debuginator_create_array_item(&debuginator, NULL, "SDL Demo/Attributions", "Background image: sk5.jpg. See sk5.txt.", NULL, NULL, NULL, NULL, 0, 0); 557 | 558 | TextureHandle bg_texture = gui_load_texture(gui, "sk5.jpg"); 559 | 560 | Uint64 START = SDL_GetPerformanceCounter(); 561 | Uint64 NOW = START; 562 | Uint64 LAST = 0; 563 | 564 | SDL_Event event; 565 | bool quit = false; 566 | double time_since_button_pressed = 0; 567 | SDL_GameControllerButton current_button = SDL_CONTROLLER_BUTTON_INVALID; 568 | float gamepad_scroll_speed = 0; 569 | double time_now = 0; 570 | double bg_height = 0; 571 | while (!quit) { 572 | LAST = NOW; 573 | NOW = SDL_GetPerformanceCounter(); 574 | Uint64 freq = SDL_GetPerformanceFrequency(); 575 | double dt = (double)((NOW - LAST) * 1.0 / freq); 576 | time_now += dt; 577 | time_since_button_pressed += dt; 578 | 579 | while (SDL_PollEvent(&event) != 0) 580 | { 581 | if (handle_debuginator_keyboard_input_event(&event, &debuginator, demodata)) { 582 | continue; 583 | } 584 | 585 | if (handle_debuginator_gamepad_input_event(&event, &debuginator, current_button, time_since_button_pressed, gamepad_scroll_speed)) { 586 | continue; 587 | } 588 | 589 | switch (event.type) { 590 | case SDL_KEYDOWN: 591 | { 592 | if (event.key.keysym.sym == SDLK_ESCAPE) { 593 | quit = true; 594 | } 595 | break; 596 | } 597 | case SDL_QUIT: 598 | { 599 | quit = true; 600 | break; 601 | } 602 | case SDL_WINDOWEVENT : { 603 | switch (event.window.event) { 604 | case SDL_WINDOWEVENT_RESIZED: { 605 | debuginator_set_size(&debuginator, (int)debuginator.size.x, event.window.data2 ); 606 | debuginator_set_screen_resolution(&debuginator, event.window.data1, event.window.data2 ); 607 | res_x = event.window.data1; 608 | res_y = event.window.data2; 609 | } break; 610 | } 611 | 612 | break; 613 | } 614 | } 615 | } 616 | 617 | handle_debuginator_gamepad_input(&debuginator, current_button, time_since_button_pressed); 618 | handle_debuginator_gamepad_input_immediate(&debuginator, gui); 619 | if (gamepad_scroll_speed != 0) { 620 | debuginator_apply_scroll(&debuginator, (int)(gamepad_scroll_speed * dt)); 621 | } 622 | 623 | debuginator_update(&debuginator, (float)dt); 624 | 625 | gui_frame_begin(gui); 626 | 627 | if (show_background) { 628 | bg_height = bg_height * (1 - 0.02) - 200 * 0.02; 629 | Vector2 bg_texture_pos = {0, (float)bg_height}; 630 | Vector2 bg_texture_size = {(float)res_x, 0}; 631 | gui_draw_texture(gui, bg_texture, bg_texture_pos, bg_texture_size); 632 | } 633 | 634 | Vector2 main_text_size = gui_text_size(gui, "The Debuginator", s_fonts[FONT_DemoHeader]); 635 | Vector2 main_text_pos(res_x / 2 - main_text_size.x / 2, res_y / 4 - main_text_size.y / 2 + demo_y_offset); 636 | float main_text_width = res_x - debuginator.openness * debuginator.size.x; 637 | float main_text_offset = debuginator.open_direction == 1 ? debuginator.openness * debuginator.size.x : 0; 638 | main_text_pos.x = main_text_offset + main_text_width / 2 - main_text_size.x / 2; 639 | unsigned char main_text_brightness = 80 + (unsigned char)(50*sin((double)(NOW-START) * 1 / freq)); 640 | Color main_text_color(30 + main_text_brightness, 30 + main_text_brightness, main_text_brightness, 255); 641 | gui_draw_text(gui, "The Debuginator", main_text_pos, s_fonts[FONT_DemoHeader], main_text_color); 642 | 643 | game_update(gamedata, (float)dt); 644 | 645 | Vector2 demo_pos = main_text_pos; 646 | demo_pos.y += 100; 647 | demo_update(demodata, (float)dt, demo_pos); 648 | 649 | debuginator_draw(&debuginator, (float)dt); 650 | 651 | // Not a good way to enforce a framerate due to delay being inprecise but 652 | // its purpose is to save some battery, not to get exactly X fps. 653 | float fps = 30; 654 | float frame_time = 1 / fps; 655 | if (limit_framerate && frame_time > dt) { 656 | SDL_Delay((Uint32)(1000 * (frame_time - dt))); 657 | } 658 | 659 | if (show_framerate && dt > 0) { 660 | char fpsstr[64] = { 0 }; 661 | sprintf_s(fpsstr, 64, "FPS: %.2lf / ms: %.15lf", 1/dt, dt * 1000); 662 | gui_draw_text(gui, fpsstr, Vector2(res_x * 0.5f, 20.f), s_fonts[FONT_ItemDescription], Color(255, 255, 0, 255)); 663 | } 664 | 665 | gui_frame_end(gui); 666 | } 667 | 668 | for (size_t i = 0; i < 16; i++) { // TODO unhardcode 669 | if (s_fonts[i] != 0x0) { // TODO invalid handle 670 | gui_unregister_font_template(gui, s_fonts[i]); 671 | } 672 | } 673 | 674 | free(memory_arena); 675 | gui_destroy_gui(gui); 676 | return 0; 677 | } 678 | -------------------------------------------------------------------------------- /tests/sdl/sdl.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {56259B2E-7D95-4AB0-83E4-369B9361F8A2} 36 | sdl 37 | 10.0 38 | 39 | 40 | 41 | Application 42 | true 43 | v142 44 | MultiByte 45 | 46 | 47 | Application 48 | false 49 | v142 50 | true 51 | MultiByte 52 | 53 | 54 | Application 55 | true 56 | v142 57 | MultiByte 58 | 59 | 60 | Application 61 | false 62 | v142 63 | true 64 | MultiByte 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | $(IncludePath) 86 | 87 | 88 | $(IncludePath) 89 | $(SolutionDir)\Build\$(Platform)_$(Configuration)\ 90 | $(SolutionDir)\Build\_$(ProjectName)\$(Platform)_$(Configuration)\ 91 | TheDebuginator_SDLDemo_Debug 92 | 93 | 94 | $(SolutionDir)\Build\$(Platform)_$(Configuration)\ 95 | $(SolutionDir)\Build\_$(ProjectName)\$(Platform)_$(Configuration)\ 96 | TheDebuginator_SDLDemo 97 | 98 | 99 | 100 | Level4 101 | Disabled 102 | true 103 | $(SolutionDir)..\..\external\SDL2-2.0.9\include;%(AdditionalIncludeDirectories) 104 | 105 | 106 | SDL2.lib;SDL2main.lib;%(AdditionalDependencies) 107 | D:\projects\external\sdl\SDL2-2.0.8\lib\x86;%(AdditionalLibraryDirectories) 108 | Console 109 | 110 | 111 | 112 | 113 | Level4 114 | Disabled 115 | true 116 | $(SolutionDir)..\..\external\SDL2-2.0.9\include;$(SolutionDir)..\..\external\SDL2_image-2.0.2\include;$(SolutionDir)..\..\external\SDL2_ttf-2.0.14\include;$(SolutionDir)3rdparty\Simple-SDL2-Audio\src;%(AdditionalIncludeDirectories) 117 | true 118 | 119 | 120 | SDL2.lib;SDL2main.lib;SDL2_image.lib;SDL2_ttf.lib;%(AdditionalDependencies) 121 | $(SolutionDir)..\..\external\SDL2-2.0.9\lib\x64;$(SolutionDir)..\..\external\SDL2_ttf-2.0.14\lib\x64;$(SolutionDir)..\..\external\SDL2_image-2.0.2\lib\x64 122 | Console 123 | 124 | 125 | 126 | 127 | 128 | 129 | copy $(SolutionDir)..\..\external\SDL2-2.0.9\lib\x64\SDL2.dll $(OutputPath) 130 | 131 | copy $(SolutionDir)..\..\external\SDL2_ttf-2.0.14\lib\x64\*.dll $(OutputPath) 132 | 133 | copy $(SolutionDir)..\..\external\SDL2_image-2.0.2\lib\x64\*.dll $(OutputPath) 134 | 135 | copy $(SolutionDir)3rdparty\liberation-fonts-ttf-2.00.1\*.ttf $(OutputPath) 136 | 137 | copy $(SolutionDir)..\color_picker.png $(OutputPath) 138 | copy $(SolutionDir)sk5.jpg $(OutputPath) 139 | copy $(SolutionDir)sk5.txt $(OutputPath) 140 | 141 | copy $(SolutionDir)3rdparty\SDL_GameControllerDB-master\gamecontrollerdb.txt $(OutputPath) 142 | 143 | copy $(SolutionDir)3rdparty\kenney_interfacesounds\*.wav $(OutputPath) 144 | 145 | 146 | 147 | 148 | 149 | 150 | Level4 151 | MaxSpeed 152 | true 153 | true 154 | true 155 | $(SolutionDir)..\..\external\SDL2-2.0.9\include;%(AdditionalIncludeDirectories) 156 | 157 | 158 | true 159 | true 160 | 161 | 162 | 163 | 164 | Level4 165 | MaxSpeed 166 | true 167 | true 168 | true 169 | $(SolutionDir)..\..\external\SDL2-2.0.9\include;$(SolutionDir)..\..\external\SDL2_image-2.0.2\include;$(SolutionDir)..\..\external\SDL2_ttf-2.0.14\include;$(SolutionDir)3rdparty\Simple-SDL2-Audio\src;%(AdditionalIncludeDirectories) 170 | 171 | 172 | true 173 | true 174 | Console 175 | SDL2.lib;SDL2main.lib;SDL2_image.lib;SDL2_ttf.lib;%(AdditionalDependencies) 176 | $(SolutionDir)..\..\external\SDL2-2.0.9\lib\x64;$(SolutionDir)..\..\external\SDL2_ttf-2.0.14\lib\x64;$(SolutionDir)..\..\external\SDL2_image-2.0.2\lib\x64 177 | 178 | 179 | copy $(SolutionDir)..\..\external\SDL2-2.0.9\lib\x64\SDL2.dll $(OutputPath) 180 | 181 | copy $(SolutionDir)..\..\external\SDL2_ttf-2.0.14\lib\x64\*.dll $(OutputPath) 182 | 183 | copy $(SolutionDir)..\..\external\SDL2_image-2.0.2\lib\x64\*.dll $(OutputPath) 184 | 185 | copy $(SolutionDir)3rdparty\liberation-fonts-ttf-2.00.1\*.ttf $(OutputPath) 186 | 187 | copy $(SolutionDir)..\color_picker.png $(OutputPath) 188 | 189 | copy $(SolutionDir)3rdparty\SDL_GameControllerDB-master\gamecontrollerdb.txt $(OutputPath) 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /tests/sdl/sdl.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/sk5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Srekel/the-debuginator/15614a72c26ef15db55b6c715dea6bfe2deb5b3e/tests/sk5.jpg -------------------------------------------------------------------------------- /tests/sk5.txt: -------------------------------------------------------------------------------- 1 | For sk5.jpg 2 | Licensed under CC BY 4.0 https://creativecommons.org/licenses/by/4.0/ 3 | Source: https://opengameart.org/content/kujasa-the-beginning 4 | 5 | Copyright/Attribution Notice: 6 | Sergei Churbanov, aka CatBlack, graphic artist 7 | William Thompson, contributor 8 | -------------------------------------------------------------------------------- /tests/the-debuginator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittest", "unittest\unittest.vcxproj", "{97FE62A5-44C3-4741-882F-FC515FDC8A86}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl", "sdl\sdl.vcxproj", "{56259B2E-7D95-4AB0-83E4-369B9361F8A2}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Debug|x64.ActiveCfg = Debug|x64 19 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Debug|x64.Build.0 = Debug|x64 20 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Debug|x86.ActiveCfg = Debug|Win32 21 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Debug|x86.Build.0 = Debug|Win32 22 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Release|x64.ActiveCfg = Release|x64 23 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Release|x64.Build.0 = Release|x64 24 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Release|x86.ActiveCfg = Release|Win32 25 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Release|x86.Build.0 = Release|Win32 26 | {56259B2E-7D95-4AB0-83E4-369B9361F8A2}.Debug|x64.ActiveCfg = Debug|x64 27 | {56259B2E-7D95-4AB0-83E4-369B9361F8A2}.Debug|x64.Build.0 = Debug|x64 28 | {56259B2E-7D95-4AB0-83E4-369B9361F8A2}.Debug|x86.ActiveCfg = Debug|Win32 29 | {56259B2E-7D95-4AB0-83E4-369B9361F8A2}.Debug|x86.Build.0 = Debug|Win32 30 | {56259B2E-7D95-4AB0-83E4-369B9361F8A2}.Release|x64.ActiveCfg = Release|x64 31 | {56259B2E-7D95-4AB0-83E4-369B9361F8A2}.Release|x64.Build.0 = Release|x64 32 | {56259B2E-7D95-4AB0-83E4-369B9361F8A2}.Release|x86.ActiveCfg = Release|Win32 33 | {56259B2E-7D95-4AB0-83E4-369B9361F8A2}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /tests/the-debuginator_solution_suppressions.cfg: -------------------------------------------------------------------------------- 1 | [cppcheck] 2 | cstyleCast 3 | memsetClassFloat 4 | missingInclude 5 | missingIncludeSystem 6 | variableScope:*main.cpp 7 | constStatement 8 | [cppcheck_files] 9 | [cppcheck_includes] 10 | -------------------------------------------------------------------------------- /tests/unittest/unittest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef __cplusplus 7 | #include 8 | #endif 9 | 10 | #ifdef _MSC_VER 11 | #pragma warning( disable: 4464 4548 4710 4820 ) 12 | #endif 13 | 14 | #ifdef __clang__ 15 | 16 | #endif 17 | typedef struct UnitTestData 18 | { 19 | char errors[256][256]; 20 | unsigned error_index; 21 | unsigned num_tests; 22 | 23 | bool simplebool_target; 24 | bool generatedbool_target; 25 | 26 | char stringtest[256]; 27 | } UnitTestData; 28 | 29 | static UnitTestData g_testdata; 30 | 31 | static void unittest_debuginator_assert(bool test) { 32 | ++g_testdata.num_tests; 33 | if (g_testdata.error_index == 256) { 34 | assert(false); 35 | } 36 | if (!test) { 37 | memcpy(g_testdata.errors[g_testdata.error_index++], "LOL", 4); 38 | } 39 | //DebugBreak(); 40 | } 41 | 42 | #define DEBUGINATOR_assert unittest_debuginator_assert 43 | #define DEBUGINATOR_static_assert unittest_debuginator_assert 44 | #define ASSERT unittest_debuginator_assert 45 | 46 | #define DEBUGINATOR_debug_print printf 47 | #define DEBUGINATOR_IMPLEMENTATION 48 | 49 | #include "../../the_debuginator.h" 50 | 51 | // Debuginator Callbacks 52 | #pragma warning(suppress: 4100) // Unreferenced param 53 | void draw_text(const char* text, DebuginatorVector2* position, DebuginatorColor* color, DebuginatorFont* font, void* userdata) { 54 | } 55 | 56 | #pragma warning(suppress: 4100) // Unreferenced param 57 | void draw_rect(DebuginatorVector2* position, DebuginatorVector2* size, DebuginatorColor* color, void* userdata) { 58 | } 59 | 60 | #pragma warning(suppress: 4100) // Unreferenced param 61 | void word_wrap(const char* text, DebuginatorFont font, float max_width, int* row_count, int* row_lengths, int row_lengths_buffer_size, void* app_userdata) { 62 | *row_count = 0; 63 | } 64 | 65 | #pragma warning(suppress: 4100) // Unreferenced param 66 | DebuginatorVector2 text_size(const char* text, DebuginatorFont* font, void* userdata) { 67 | DebuginatorVector2 size = {0, 0}; 68 | return size; 69 | } 70 | 71 | // Item callback 72 | static void unittest_on_item_changed_stringtest(DebuginatorItem* item, void* value, const char* value_title, void* app_userdata) { 73 | (void)value_title; 74 | (void)app_userdata; 75 | const char** string_ptr = (const char**)value; 76 | UnitTestData* callback_data = (UnitTestData*)item->user_data; // same as &g_testdata 77 | 78 | #ifdef __cplusplus 79 | strncpy_s(callback_data->stringtest, *string_ptr, strlen(*string_ptr)); 80 | #else 81 | strncpy_s(callback_data->stringtest, sizeof(callback_data->stringtest), *string_ptr, strlen(*string_ptr)); 82 | #endif 83 | } 84 | 85 | static void unittest_debug_menu_setup(TheDebuginator* debuginator) { 86 | debuginator_create_bool_item(debuginator, "SimpleBool 1", "Change a bool.", &g_testdata.simplebool_target); 87 | debuginator_create_bool_item(debuginator, "Folder/SimpleBool 2", "Change a bool.", &g_testdata.simplebool_target); 88 | debuginator_create_bool_item(debuginator, "Folder/SimpleBool 3", "Change a bool.", &g_testdata.simplebool_target); 89 | debuginator_create_bool_item(debuginator, "Folder/SimpleBool 4 with a really really long title", "Change a bool.", &g_testdata.simplebool_target); 90 | 91 | debuginator_create_folder_item(debuginator, NULL, "Folder 2"); 92 | 93 | static const char* string_values[3] = { "gamestring 1", "gamestring 2", "gamestring 3"}; 94 | static const char* string_titles[3] = { "First value", "Second one", "This is the third." }; 95 | debuginator_create_array_item(debuginator, NULL, "Folder 2/String item", 96 | "Do it", unittest_on_item_changed_stringtest, &g_testdata, 97 | string_titles, (void*)string_values, 3, sizeof(string_values[0])); 98 | } 99 | 100 | static void unittest_debug_menu_run(void) { 101 | 102 | memset(&g_testdata, 0, sizeof(g_testdata)); 103 | UnitTestData* testdata = &g_testdata; 104 | 105 | 106 | int memory_arena_capacity = 1024 * 1024 * 1; 107 | char* memory_arena = (char*)malloc(memory_arena_capacity); 108 | TheDebuginatorConfig config; 109 | debuginator_get_default_config(&config); 110 | config.memory_arena = memory_arena; 111 | config.memory_arena_capacity = memory_arena_capacity; 112 | config.draw_rect = draw_rect; 113 | config.draw_text = draw_text; 114 | config.word_wrap = word_wrap; 115 | config.text_size = text_size; 116 | config.app_user_data = &g_testdata; 117 | config.size.x = 500; 118 | config.size.y = 1000; 119 | config.screen_resolution.x = 500; 120 | config.screen_resolution.y = 1000; 121 | config.focus_height = 0.3f; 122 | config.create_default_debuginator_items = false; 123 | 124 | TheDebuginator debuginator; 125 | TheDebuginator* thed = &debuginator; // Lazy shorthand 126 | debuginator_create(&config, thed); 127 | 128 | unittest_debug_menu_setup(thed); 129 | 130 | printf("\n"); 131 | printf("Setup errors found: %u/%u\n", 132 | testdata->error_index, testdata->num_tests); 133 | 134 | if (testdata->error_index > 0) { 135 | printf("Errors found during setup, exiting.\n"); 136 | return; 137 | } 138 | 139 | testdata->num_tests = 0; 140 | 141 | DebuginatorItem* sb1_item = debuginator_get_item(thed, NULL, "SimpleBool 1", false); 142 | DebuginatorItem* sb2_item = debuginator_get_item(thed, NULL, "Folder/SimpleBool 2", false); 143 | DebuginatorItem* sb3_item = debuginator_get_item(thed, NULL, "Folder/SimpleBool 3", false); 144 | DebuginatorItem* sb4_item = debuginator_get_item(thed, NULL, "Folder/SimpleBool 4 with a really really long title", false); 145 | DebuginatorItem* str_item = debuginator_get_item(thed, NULL, "Folder 2/String item", false); 146 | 147 | { 148 | // Are our expectations after setup correct? 149 | ASSERT(sb1_item != NULL); 150 | ASSERT(sb2_item != NULL); 151 | ASSERT(sb3_item != NULL); 152 | ASSERT(sb4_item != NULL); 153 | ASSERT(str_item != NULL); 154 | 155 | ASSERT(debuginator.root->folder.num_visible_children = 5); 156 | 157 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "SimpleBool 1", false); 158 | ASSERT(expected_hot_item == debuginator.hot_item); 159 | ASSERT(expected_hot_item->leaf.is_expanded == false); 160 | 161 | ASSERT(testdata->simplebool_target == false); 162 | ASSERT(testdata->generatedbool_target == false); 163 | } 164 | { 165 | // Can we add and remove stuff? 166 | char item_name[64] = { 0 }; 167 | for (int i = 0; i < 10; i++) { 168 | for (int j = 0; j < 10; j++) { 169 | sprintf_s(item_name, 64, "Game/Test%02d/GameBool%02d", i, j); 170 | debuginator_create_bool_item(thed, item_name, "Generated bool item.", &g_testdata.generatedbool_target); 171 | } 172 | } 173 | 174 | ASSERT(debuginator.root->folder.num_visible_children == 6); 175 | 176 | DebuginatorItem* game_item = debuginator_get_item(thed, NULL, "Game", false); 177 | ASSERT(game_item->folder.num_visible_children == 10); 178 | 179 | DebuginatorItem* remove_item = debuginator_get_item(thed, NULL, "Game/Test01", false); 180 | debuginator_remove_item(thed, remove_item); 181 | ASSERT(game_item->folder.num_visible_children == 9); 182 | 183 | for (int i = 0; i < 10; i++) { 184 | for (int j = 0; j < 10; j++) { 185 | sprintf_s(item_name, 64, "Game/Test%02d/GameBool%02d", i, j); 186 | debuginator_create_bool_item(thed, item_name, "Generated bool item.", &g_testdata.generatedbool_target); 187 | } 188 | } 189 | 190 | ASSERT(game_item->folder.num_visible_children == 10); 191 | 192 | remove_item = debuginator_get_item(thed, NULL, "Game/Test01", false); 193 | debuginator_remove_item(thed, remove_item); 194 | 195 | ASSERT(game_item->folder.num_visible_children == 9); 196 | } 197 | 198 | /* 199 | { 200 | debuginator_move_to_next_leaf(thed, false); 201 | // Going to child activates SimpleBool 1 202 | DebuginatorInput input = {0}; 203 | input.move_to_child = true; 204 | debug_menu_handle_input(thed, &input); 205 | ASSERT(debuginator.hot_item->leaf.is_expanded == true); 206 | } 207 | { 208 | // Going to child changes SimpleBool 1 bool 209 | DebuginatorInput input = {0}; 210 | input.move_to_child = true; 211 | debug_menu_handle_input(thed, &input); 212 | ASSERT(testdata->simplebool_target == false); 213 | } 214 | { 215 | // Going to child and sibling at the same time changes SimpleBool 1's to second option and sets bool to true 216 | DebuginatorInput input = {0}; 217 | input.move_to_child = true; 218 | input.move_sibling_next = true; 219 | debug_menu_handle_input(thed, &input); 220 | ASSERT(testdata->simplebool_target == true); 221 | } 222 | { 223 | // Going to child SimpleBool 1's first option changes bool to false 224 | DebuginatorInput input = {0}; 225 | input.move_to_child = true; 226 | input.move_sibling_next = true; 227 | debug_menu_handle_input(thed, &input); 228 | ASSERT(testdata->simplebool_target == false); 229 | } 230 | { 231 | // Going to parent inactivates item 232 | DebuginatorInput input = {0}; 233 | input.move_to_parent = true; 234 | debug_menu_handle_input(thed, &input); 235 | ASSERT(debuginator.hot_item->leaf.is_expanded == false); 236 | } 237 | { 238 | // Going to parent does nothing 239 | DebuginatorInput input = {0}; 240 | input.move_to_parent = true; 241 | debug_menu_handle_input(thed, &input); 242 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "SimpleBool 1", false); 243 | ASSERT(expected_hot_item == debuginator.hot_item); 244 | } 245 | { 246 | // Going down goes to Folder 247 | DebuginatorInput input = {0}; 248 | input.move_sibling_next = true; 249 | debug_menu_handle_input(thed, &input); 250 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "Folder", false); 251 | ASSERT(expected_hot_item == debuginator.hot_item); 252 | } 253 | { 254 | // Going down goes to Folder 2 255 | DebuginatorInput input = {0}; 256 | input.move_sibling_next = true; 257 | debug_menu_handle_input(thed, &input); 258 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "Folder 2", false); 259 | ASSERT(expected_hot_item == debuginator.hot_item); 260 | } 261 | { 262 | // Going down wraps to SimpleBool 1 263 | DebuginatorInput input = {0}; 264 | input.move_sibling_next = true; 265 | debug_menu_handle_input(thed, &input); 266 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "SimpleBool 1", false); 267 | ASSERT(expected_hot_item == debuginator.hot_item); 268 | } 269 | { 270 | // Go to Folder 271 | DebuginatorInput input = {0}; 272 | input.move_sibling_next = true; 273 | debug_menu_handle_input(thed, &input); 274 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "Folder", false); 275 | ASSERT(expected_hot_item == debuginator.hot_item); 276 | } 277 | { 278 | // Going to child goes to SimpleBool 2 279 | DebuginatorInput input = {0}; 280 | input.move_to_child = true; 281 | debug_menu_handle_input(thed, &input); 282 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "Folder/SimpleBool 2", false); 283 | ASSERT(expected_hot_item == debuginator.hot_item); 284 | } 285 | { 286 | // Going to child activates SimpleBool 2 287 | DebuginatorInput input = {0}; 288 | input.move_to_child = true; 289 | debug_menu_handle_input(thed, &input); 290 | ASSERT(debuginator.hot_item->leaf.is_expanded == true); 291 | } 292 | { 293 | // Going to child changes SimpleBool 2 bool 294 | DebuginatorInput input = {0}; 295 | input.move_to_child = true; 296 | debug_menu_handle_input(thed, &input); 297 | ASSERT(testdata->simplebool_target == false); 298 | } 299 | { 300 | // Going to parent inactivates item 301 | DebuginatorInput input = {0}; 302 | input.move_to_parent = true; 303 | debug_menu_handle_input(thed, &input); 304 | ASSERT(debuginator.hot_item->leaf.is_expanded == false); 305 | } 306 | { 307 | // Going to sibling works as expected 308 | DebuginatorInput input = {0}; 309 | input.move_sibling_next = true; 310 | debug_menu_handle_input(thed, &input); 311 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "Folder/SimpleBool 3", false); 312 | ASSERT(expected_hot_item == debuginator.hot_item); 313 | 314 | debug_menu_handle_input(thed, &input); 315 | expected_hot_item = debuginator_get_item(thed, NULL, "Folder/SimpleBool 4 with a really long long title", false); 316 | ASSERT(expected_hot_item == debuginator.hot_item); 317 | 318 | debug_menu_handle_input(thed, &input); 319 | expected_hot_item = debuginator_get_item(thed, NULL, "Folder/SimpleBool 2", false); 320 | ASSERT(expected_hot_item == debuginator.hot_item); 321 | 322 | debug_menu_handle_input(thed, &input); 323 | expected_hot_item = debuginator_get_item(thed, NULL, "Folder/SimpleBool 3", false); 324 | ASSERT(expected_hot_item == debuginator.hot_item); 325 | } 326 | { 327 | // Go to Folder 2/String item 328 | DebuginatorInput input = {0}; 329 | input.move_to_parent = true; 330 | debug_menu_handle_input(thed, &input); 331 | debug_menu_handle_input(thed, &input); 332 | input.move_to_parent = false; 333 | input.move_sibling_next = true; 334 | input.move_to_child = true; 335 | debug_menu_handle_input(thed, &input); 336 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "Folder 2/String item", false); 337 | ASSERT(expected_hot_item == debuginator.hot_item); 338 | } 339 | { 340 | // Going to child activates string item 341 | DebuginatorInput input = {0}; 342 | input.move_to_child = true; 343 | debug_menu_handle_input(thed, &input); 344 | ASSERT(debuginator.hot_item->leaf.is_expanded == true); 345 | } 346 | { 347 | // Going to child changes string 348 | DebuginatorInput input = {0}; 349 | input.move_to_child = true; 350 | debug_menu_handle_input(thed, &input); 351 | ASSERT(strcmp(testdata->stringtest, "gamestring 1") == 0); 352 | } 353 | { 354 | // Going down goes to next string 355 | DebuginatorInput input = {0}; 356 | input.move_sibling_next = true; 357 | debug_menu_handle_input(thed, &input); 358 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "Folder 2/String item", false); 359 | ASSERT(expected_hot_item == debuginator.hot_item); 360 | } 361 | { 362 | // Going to child changes string 363 | DebuginatorInput input = {0}; 364 | input.move_to_child = true; 365 | debug_menu_handle_input(thed, &input); 366 | ASSERT(strcmp(testdata->stringtest, "gamestring 2") == 0); 367 | } 368 | { 369 | // Overwrite the item with another item 370 | debuginator_create_bool_item(thed, "Folder 2/String item", "Change a bool.", &testdata->simplebool_target); 371 | } 372 | { 373 | // Activate it, we should still be on the second value, so bool should turn to true 374 | ASSERT(testdata->simplebool_target == false); 375 | DebuginatorInput input = {0}; 376 | input.move_to_child = true; 377 | debug_menu_handle_input(thed, &input); 378 | ASSERT(testdata->simplebool_target == true); 379 | } 380 | { 381 | // Remove item 382 | debuginator_remove_item_by_path(thed, "Folder 2/String item"); 383 | DebuginatorItem* expected_null_item = debuginator_get_item(thed, NULL, "Folder 2/String item", false); 384 | ASSERT(expected_null_item == NULL); 385 | 386 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "Folder 2", false); 387 | ASSERT(expected_hot_item == debuginator.hot_item); 388 | } 389 | { 390 | // Set hot item 391 | debuginator_set_hot_item(thed, "Folder/SimpleBool 2"); 392 | 393 | DebuginatorItem* expected_hot_item = debuginator_get_item(thed, NULL, "Folder/SimpleBool 2", false); 394 | ASSERT(expected_hot_item == debuginator.hot_item); 395 | } 396 | 397 | */ 398 | 399 | printf("Run errors found: %u/%u\n", 400 | testdata->error_index, testdata->num_tests); 401 | 402 | printf("\n"); 403 | if (testdata->error_index == 0) { 404 | printf("No errors found, YAY!\n"); 405 | } 406 | else { 407 | printf("U are teh sux.\n"); 408 | } 409 | } 410 | 411 | int main(int argc, char **argv) 412 | { 413 | (void)(argc, argv); 414 | unittest_debug_menu_run(); 415 | 416 | while (true) 417 | { 418 | 419 | } 420 | return 0; 421 | } 422 | -------------------------------------------------------------------------------- /tests/unittest/unittest.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {97FE62A5-44C3-4741-882F-FC515FDC8A86} 23 | Win32Proj 24 | thedebuginator 25 | 10.0.14393.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v141 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | $(SolutionDir)\Build\$(Platform)_$(Configuration)\ 78 | $(SolutionDir)\Build\_$(ProjectName)\$(Platform)_$(Configuration)\ 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | Level3 91 | Disabled 92 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | true 94 | 95 | 96 | Console 97 | true 98 | 99 | 100 | 101 | 102 | 103 | 104 | EnableAllWarnings 105 | Disabled 106 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 107 | true 108 | true 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | MaxSpeed 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | 126 | 127 | Console 128 | true 129 | true 130 | true 131 | 132 | 133 | 134 | 135 | Level3 136 | 137 | 138 | MaxSpeed 139 | true 140 | true 141 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 142 | true 143 | 144 | 145 | Console 146 | true 147 | true 148 | true 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /tests/unittest/unittest.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/zig-minimal/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | cls 4 | cd src 5 | zig build run 6 | cd .. -------------------------------------------------------------------------------- /tests/zig-minimal/src/build.zig: -------------------------------------------------------------------------------- 1 | 2 | 3 | const std = @import("std"); 4 | const builtin = @import("builtin"); 5 | 6 | pub fn build(b: *std.build.Builder) anyerror!void { 7 | b.release_mode = builtin.Mode.Debug; 8 | const mode = b.standardReleaseOptions(); 9 | 10 | const mainFile = "main.zig"; 11 | var exe = b.addExecutable("debuginator-zig-demo", "../src/" ++ mainFile); 12 | exe.addIncludeDir("../src/"); 13 | exe.addIncludeDir("../../.."); 14 | exe.setBuildMode(mode); 15 | 16 | const cFlags = [_][]const u8{"-std=c99"}; 17 | exe.addCSourceFile("the_debuginator_wrapper.c", &cFlags); 18 | 19 | exe.linkLibC(); 20 | exe.linkSystemLibrary("user32"); 21 | exe.linkSystemLibrary("gdi32"); 22 | 23 | const run_cmd = exe.run(); 24 | const run_step = b.step("run", "Run 'The Debuginator Zig Demo'"); 25 | run_step.dependOn(&run_cmd.step); 26 | 27 | b.default_step.dependOn(&exe.step); 28 | b.installArtifact(exe); 29 | } 30 | -------------------------------------------------------------------------------- /tests/zig-minimal/src/c.zig: -------------------------------------------------------------------------------- 1 | pub usingnamespace @cImport({ 2 | @cInclude("the_debuginator_wrapper.h"); 3 | }); 4 | 5 | // pub usingnamespace @cImport("c.c"); 6 | 7 | // const local = struct { 8 | // export fn debuginator_assert(condition: bool) void { 9 | // if (!condition) unreachable; 10 | // } 11 | // export fn de buginator_sprintf_s(varargs: anytype) void {} 12 | // export fn debuginator_strcpy_s(varargs: anytype) void {} 13 | // }; 14 | -------------------------------------------------------------------------------- /tests/zig-minimal/src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const debuginator_c = @import("c.zig"); 3 | 4 | pub export fn draw_rect(position: [*c]debuginator_c.DebuginatorVector2, size: [*c]debuginator_c.DebuginatorVector2, color: [*c]debuginator_c.DebuginatorColor, userdata: ?*c_void) void {} 5 | pub export fn draw_text(text: [*c]const u8, position: [*c]debuginator_c.DebuginatorVector2, color: [*c]debuginator_c.DebuginatorColor, font: [*c]debuginator_c.DebuginatorFont, userdata: ?*c_void) void {} 6 | pub export fn text_size(text: [*c]const u8, font: [*c]debuginator_c.DebuginatorFont, userdata: ?*c_void) debuginator_c.DebuginatorVector2 { 7 | var size = debuginator_c.DebuginatorVector2{ 8 | .x = 30, 9 | .y = 10, 10 | }; 11 | return size; 12 | } 13 | 14 | pub export fn word_wrap(text: [*c]const u8, font: [*c]debuginator_c.DebuginatorFont, max_width: f32, row_count: [*c]c_int, row_lengths: [*c]c_int, row_lengths_buffer_size: c_int, userdata: ?*c_void) void { 15 | // std.debug.print("WW {}\n", .{max_width}); 16 | } 17 | 18 | pub export fn log(text: [*c]const u8, userdata: ?*c_void) void { 19 | std.debug.print("LOG {}\n", .{text}); 20 | } 21 | 22 | 23 | pub fn main() void { 24 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); 25 | defer arena.deinit(); 26 | 27 | const buffer_size = 1024 * 1024; 28 | var buffer = arena.allocator.alloc(u8, buffer_size) catch unreachable; 29 | 30 | var config = std.mem.zeroes(debuginator_c.TheDebuginatorConfig); 31 | debuginator_c.debuginator_get_default_config(&config); 32 | config.memory_arena = @ptrCast([*c]u8, buffer); 33 | config.memory_arena_capacity = buffer_size; 34 | config.draw_rect = draw_rect; 35 | config.draw_text = draw_text; 36 | config.text_size = text_size; 37 | config.word_wrap = word_wrap; 38 | config.log = log; 39 | config.size.x = 300; 40 | config.size.y = 1000; 41 | config.screen_resolution.x = 1200; 42 | config.screen_resolution.y = config.size.y; 43 | config.app_user_data = &config; 44 | 45 | 46 | std.debug.print("BEGIN {}\n", .{config.create_default_debuginator_items}); 47 | var debuginator: ?*debuginator_c.TheDebuginator = debuginator_c.alloc_debuginator(); 48 | std.debug.assert(debuginator != null); 49 | 50 | std.debug.print("CREATE {}\n", .{config.create_default_debuginator_items}); 51 | debuginator_c.debuginator_create(&config, debuginator); 52 | 53 | std.debug.print("UPDATE {}\n", .{config.create_default_debuginator_items}); 54 | debuginator_c.debuginator_update(debuginator, 0.1); 55 | debuginator_c.debuginator_draw(debuginator, 0.1); 56 | 57 | std.debug.print("END {}\n", .{config.create_default_debuginator_items}); 58 | } 59 | -------------------------------------------------------------------------------- /tests/zig-minimal/src/the_debuginator_wrapper.c: -------------------------------------------------------------------------------- 1 | 2 | #define DEBUGINATOR_IMPLEMENTATION 3 | #include 4 | 5 | 6 | struct TheDebuginator* alloc_debuginator() { 7 | static struct TheDebuginator debuginator; 8 | memset(&debuginator, 0, sizeof(debuginator)); 9 | return &debuginator; 10 | } 11 | -------------------------------------------------------------------------------- /tests/zig-minimal/src/the_debuginator_wrapper.h: -------------------------------------------------------------------------------- 1 | 2 | #include "the_debuginator.h" 3 | 4 | struct TheDebuginator* alloc_debuginator(); 5 | -------------------------------------------------------------------------------- /the_debuginator_queue.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | the_debuginator_queue.h - v0.01 - public domain - Anders Elfgren @srekel, 2018 4 | 5 | # THE DEBUGINATOR QUEUE 6 | 7 | A utility library to enable simple multi-threading support for The Debuginator. 8 | 9 | See github for latest version: https://github.com/Srekel/the-debuginator 10 | 11 | ## Usage 12 | 13 | In *ONE* source file, put: 14 | 15 | ```C 16 | #define DEBUGINATOR_QUEUE_IMPLEMENTATION 17 | 18 | // Define any of these if you wish to override them. 19 | // (There are more. Find them in the beginning of the code.) 20 | #define DEBUGINATOR_assert 21 | #define DEBUGINATOR_memcpy 22 | #define DEBUGINATOR_fabs 23 | 24 | #include "the_debuginator.h" 25 | #include "the_debuginator_queue.h" 26 | ``` 27 | 28 | Depending on if its in a binary that has actual access to the debuginator, 29 | also #define DEBUGINATOR_QUEUE_CAN_PROCEES 30 | 31 | Other source files should just include the_debuginator_queue.h 32 | 33 | ## Notes 34 | 35 | See the documentation on the github page. 36 | 37 | ## License 38 | 39 | Basically Public Domain / MIT. 40 | See end of file for license information. 41 | 42 | */ 43 | 44 | #ifndef INCLUDE_THE_DEBUGINATOR_QUEUE_H 45 | #define INCLUDE_THE_DEBUGINATOR_QUEUE_H 46 | 47 | #include "the_debuginator.h" 48 | 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | 53 | #ifndef DEBUGINATOR_ENABLE_WARNINGS 54 | #ifdef _MSC_VER 55 | #pragma warning( push, 0 ) 56 | // #pragma warning( disable: 4820 4201) 57 | #endif 58 | 59 | #ifdef __clang__ 60 | #pragma clang diagnostic push 61 | #pragma clang diagnostic ignored "-Wold-style-cast" 62 | #pragma clang diagnostic ignored "-Wsign-conversion" 63 | #pragma clang diagnostic ignored "-Wunused-value" 64 | #pragma clang diagnostic ignored "-Wcomma" 65 | #pragma clang diagnostic ignored "-Wcast-align" 66 | #pragma clang diagnostic ignored "-Wswitch-enum" 67 | #endif 68 | #endif 69 | 70 | #define DEBUGINATOR_QUEUE_MAX_PATH_LENGTH 128 71 | #define DEBUGINATOR_QUEUE_MAX_DESCRIPTION_LENGTH 512 72 | 73 | typedef enum DebuginatorQueueItemTypes { 74 | DEBUGINATOR_QUEUE_CreateItem, 75 | DEBUGINATOR_QUEUE_NumItemTypes 76 | } DebuginatorQueueItemTypes; 77 | 78 | typedef struct { 79 | char path[DEBUGINATOR_QUEUE_MAX_PATH_LENGTH]; 80 | char description[DEBUGINATOR_QUEUE_MAX_DESCRIPTION_LENGTH]; 81 | void* userdata; 82 | DebuginatorOnItemChangedCallback callback; 83 | } DebuginatorQueue_CreateItemData; 84 | 85 | typedef struct DebuginatorQueueItem { 86 | DebuginatorQueueItemTypes type; 87 | union { 88 | DebuginatorQueue_CreateItemData create_item; 89 | } data; 90 | } DebuginatorQueueItem; 91 | 92 | typedef struct TheDebuginatorQueue TheDebuginatorQueue; 93 | 94 | typedef void* ( *DebuginatorQueueAllocateFunc )( void* userdata, int bytes ); 95 | typedef void ( *DebuginatorQueueDellocateFunc )( void* userdata, void* ptr ); 96 | typedef void ( *DebuginatorQueueItemCleanupFunc )( TheDebuginatorQueue* queue, 97 | DebuginatorQueueItem* item ); 98 | 99 | typedef struct TheDebuginatorQueue { 100 | void* userdata; 101 | DebuginatorQueueItem* items; 102 | int num_items; 103 | int capacity; 104 | DebuginatorQueueAllocateFunc allocate; 105 | DebuginatorQueueDellocateFunc deallocate; 106 | DebuginatorQueueItemCleanupFunc cleanup_funcs[DEBUGINATOR_QUEUE_NumItemTypes]; 107 | } TheDebuginatorQueue; 108 | 109 | TheDebuginatorQueue* debuginator_queue_create( int initial_size, 110 | DebuginatorQueueAllocateFunc allocate_func, 111 | DebuginatorQueueDellocateFunc deallocate_func, 112 | void* userdata ); 113 | 114 | unsigned char* debuginator_queue_data( TheDebuginatorQueue* queue, int* out_size ); 115 | void debuginator_queue_clear( TheDebuginatorQueue* queue ); 116 | void debuginator_queue_process( const unsigned char* data, int size, TheDebuginator* debuginator ); 117 | void debuginator_queue_create_bool_item( TheDebuginatorQueue* queue, 118 | const char* path, 119 | const char* description, 120 | void* userdata ); 121 | void debuginator_queue_create_bool_item_with_callback( TheDebuginatorQueue* queue, 122 | const char* path, 123 | const char* description, 124 | void* userdata, 125 | DebuginatorOnItemChangedCallback callback ); 126 | 127 | #ifdef __cplusplus 128 | } 129 | #endif 130 | 131 | #ifdef DEBUGINATOR_QUEUE_IMPLEMENTATION 132 | 133 | #ifndef DEBUGINATOR_QUEUE_assert 134 | #include 135 | #define DEBUGINATOR_QUEUE_assert assert; 136 | #endif 137 | 138 | #ifndef DEBUGINATOR_QUEUE_memcpy 139 | #include 140 | #define DEBUGINATOR_QUEUE_memcpy memcpy 141 | #endif 142 | 143 | #ifndef DEBUGINATOR_QUEUE_strcpy_s 144 | #include 145 | #define DEBUGINATOR_QUEUE_strcpy_s strcpy_s 146 | #endif 147 | 148 | static void 149 | debuginator_queue__ensure_capacity( TheDebuginatorQueue* queue ) { 150 | if ( queue->capacity + 1 == queue->num_items ) { 151 | int bytes = sizeof( DebuginatorQueueItem ) * queue->num_items * 2; 152 | void* items = queue->allocate( queue->userdata, bytes ); 153 | DEBUGINATOR_QUEUE_memcpy( items, queue->items, bytes ); 154 | queue->deallocate( queue->userdata, queue->items ); 155 | queue->items = (DebuginatorQueueItem*)items; 156 | queue->capacity = queue->num_items * 2; 157 | } 158 | } 159 | 160 | // static const char* 161 | // debuginator_queue__copy_string( TheDebuginatorQueue* queue, const char* str ) { 162 | // int bytes = strlen( str ); 163 | // void* buffer = queue->allocate( queue->userdata, bytes ); 164 | // DEBUGINATOR_QUEUE_memcpy( buffer, str, bytes ); 165 | // return (const char*)buffer; 166 | // } 167 | 168 | void 169 | debuginator_queue_clear( TheDebuginatorQueue* queue ) { 170 | // for ( int i_item = 0; i_item < queue->num_items; ++i_item ) { 171 | // DebuginatorQueueItem* item = &queue->items[i_item]; 172 | // queue->cleanup_funcs[item->type]( queue, item ); 173 | // } 174 | queue->num_items = 0; 175 | } 176 | 177 | unsigned char* 178 | debuginator_queue_data( TheDebuginatorQueue* queue, int* out_size ) { 179 | *out_size = queue->num_items * sizeof( DebuginatorQueueItem ); 180 | return (unsigned char*)queue->items; 181 | } 182 | 183 | void 184 | debuginator_queue_create_bool_item( TheDebuginatorQueue* queue, 185 | const char* path, 186 | const char* description, 187 | void* userdata ) { 188 | 189 | debuginator_queue__ensure_capacity( queue ); 190 | 191 | DebuginatorQueue_CreateItemData data; 192 | DEBUGINATOR_QUEUE_strcpy_s( data.path, DEBUGINATOR_QUEUE_MAX_PATH_LENGTH, path ); 193 | DEBUGINATOR_QUEUE_strcpy_s( 194 | data.description, DEBUGINATOR_QUEUE_MAX_DESCRIPTION_LENGTH, description ); 195 | data.userdata = userdata; 196 | 197 | DebuginatorQueueItem* item = &queue->items[queue->num_items++]; 198 | item->type = DEBUGINATOR_QUEUE_CreateItem; 199 | item->data.create_item = data; 200 | } 201 | 202 | void 203 | debuginator_queue_create_bool_item_with_callback( TheDebuginatorQueue* queue, 204 | const char* path, 205 | const char* description, 206 | void* userdata, 207 | DebuginatorOnItemChangedCallback callback ) { 208 | 209 | debuginator_queue__ensure_capacity( queue ); 210 | 211 | DebuginatorQueue_CreateItemData data; 212 | DEBUGINATOR_QUEUE_strcpy_s( data.path, DEBUGINATOR_QUEUE_MAX_PATH_LENGTH, path ); 213 | DEBUGINATOR_QUEUE_strcpy_s( 214 | data.description, DEBUGINATOR_QUEUE_MAX_DESCRIPTION_LENGTH, description ); 215 | data.userdata = userdata; 216 | data.callback = callback; 217 | 218 | DebuginatorQueueItem* item = &queue->items[queue->num_items++]; 219 | item->type = DEBUGINATOR_QUEUE_CreateItem; 220 | item->data.create_item = data; 221 | } 222 | 223 | // void 224 | // debuginator_queue__create_bool_item_cleanup( TheDebuginatorQueue* queue, 225 | // DebuginatorQueueItem* item ) { 226 | 227 | // DebuginatorQueue_CreateItemData* data = (DebuginatorQueue_CreateItemData*)item->data; 228 | // queue->deallocate( queue->userdata, (void*)data->path ); 229 | // queue->deallocate( queue->userdata, (void*)data->description ); 230 | // queue->deallocate( queue->userdata, (void*)item->data ); 231 | // } 232 | 233 | void 234 | debuginator_queue_process( const unsigned char* data, int size, TheDebuginator* debuginator ) { 235 | (void)( data, size, debuginator ); 236 | #ifdef DEBUGINATOR_QUEUE_CAN_PROCEES 237 | const void* data_end = data + size; 238 | const DebuginatorQueueItem* item = (DebuginatorQueueItem*)(unsigned long long)data; 239 | while ( item < data_end ) { 240 | switch ( item->type ) { 241 | case DEBUGINATOR_QUEUE_CreateItem: { 242 | // debuginator_create_array_item(debuginator, NULL, item->data.create_item.path, 243 | // description, item->data.create_item.callback, item->data.create_item.userdata, 244 | // debuginator->bool_titles, debuginator->bool_values, 2, 245 | // sizeof(debuginator->bool_values[0])); 246 | if ( item->data.create_item.callback == NULL ) { 247 | debuginator_create_bool_item( debuginator, 248 | item->data.create_item.path, 249 | item->data.create_item.description, 250 | item->data.create_item.userdata ); 251 | } 252 | else { 253 | debuginator_create_bool_item_with_callback( debuginator, 254 | item->data.create_item.path, 255 | item->data.create_item.description, 256 | item->data.create_item.userdata, 257 | item->data.create_item.callback ); 258 | } 259 | } break; 260 | default: 261 | break; 262 | } 263 | 264 | item++; 265 | } 266 | 267 | DEBUGINATOR_QUEUE_assert( item == data_end ); 268 | #endif 269 | } 270 | 271 | TheDebuginatorQueue* 272 | debuginator_queue_create( int initial_size, 273 | DebuginatorQueueAllocateFunc allocate_func, 274 | DebuginatorQueueDellocateFunc deallocate_func, 275 | void* userdata ) { 276 | 277 | int buffer_bytes = sizeof( TheDebuginatorQueue ); 278 | int item_bytes = sizeof( DebuginatorQueueItem ) * initial_size; 279 | void* queue_buffer = allocate_func( userdata, buffer_bytes ); 280 | void* item_buffer = allocate_func( userdata, item_bytes ); 281 | TheDebuginatorQueue* queue = (TheDebuginatorQueue*)queue_buffer; 282 | queue->num_items = 0; 283 | queue->capacity = initial_size; 284 | queue->userdata = userdata; 285 | queue->allocate = allocate_func; 286 | queue->deallocate = deallocate_func; 287 | queue->items = (DebuginatorQueueItem*)item_buffer; 288 | 289 | // queue->cleanup_funcs[DEBUGINATOR_QUEUE_CreateItem] = 290 | // debuginator_queue__create_bool_item_cleanup; 291 | 292 | return queue; 293 | } 294 | 295 | #endif // DEBUGINATOR_QUEUE_IMPLEMENTATION 296 | 297 | #ifndef DEBUGINATOR_ENABLE_WARNINGS 298 | #ifdef _MSC_VER 299 | #pragma warning( pop ) 300 | #endif 301 | 302 | #ifdef __clang__ 303 | #pragma clang diagnostic pop 304 | #endif 305 | #endif 306 | 307 | #endif // INCLUDE_THE_DEBUGINATOR_H 308 | 309 | /* 310 | ------------------------------------------------------------------------------ 311 | This software is available under 2 licenses -- choose whichever you prefer. 312 | ------------------------------------------------------------------------------ 313 | ALTERNATIVE A - MIT License 314 | Copyright (c) 2017 Anders Elfgren 315 | Permission is hereby granted, free of charge, to any person obtaining a copy of 316 | this software and associated documentation files (the "Software"), to deal in 317 | the Software without restriction, including without limitation the rights to 318 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 319 | of the Software, and to permit persons to whom the Software is furnished to do 320 | so, subject to the following conditions: 321 | The above copyright notice and this permission notice shall be included in all 322 | copies or substantial portions of the Software. 323 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 324 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 325 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 326 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 327 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 328 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 329 | SOFTWARE. 330 | ------------------------------------------------------------------------------ 331 | ALTERNATIVE B - Public Domain (www.unlicense.org) 332 | This is free and unencumbered software released into the public domain. 333 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 334 | software, either in source code form or as a compiled binary, for any purpose, 335 | commercial or non-commercial, and by any means. 336 | In jurisdictions that recognize copyright laws, the author or authors of this 337 | software dedicate any and all copyright interest in the software to the public 338 | domain. We make this dedication for the benefit of the public at large and to 339 | the detriment of our heirs and successors. We intend this dedication to be an 340 | overt act of relinquishment in perpetuity of all present and future rights to 341 | this software under copyright law. 342 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 343 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 344 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 345 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 346 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 347 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 348 | ------------------------------------------------------------------------------ 349 | */ 350 | -------------------------------------------------------------------------------- /tools/stub_gen.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | class Param: 5 | def __init__(self): 6 | self.type = None 7 | self.name = None 8 | def __repr__(self): 9 | # return "%s %s" % (self.type, self.name) 10 | return "%s" % (self.type) 11 | 12 | 13 | class Function: 14 | def __init__(self): 15 | self.return_type = None 16 | self.name = None 17 | self.params = [] 18 | def __repr__(self): 19 | return "%-20s %-50s(%s) {}" % (self.return_type, self.name, ", ".join([str(p) for p in self.params])) 20 | 21 | if __name__ == "__main__": 22 | 23 | os.chdir(os.path.dirname(sys.argv[0])) 24 | functions = [] 25 | 26 | with open("../the_debuginator.h") as file: 27 | state = "finding_start" 28 | curr_func = None 29 | for line in file: 30 | line = line.strip() 31 | if state == "finding_start": 32 | if "API START" in line: 33 | state = "parsing_api" 34 | elif state == "parsing_api": 35 | if "API END" in line: 36 | break 37 | if line == "": 38 | continue 39 | if line[:2] == "//": 40 | continue 41 | 42 | if curr_func == None: 43 | curr_func = Function() 44 | firstsplit = line.find(" ") 45 | curr_func.return_type = line[:firstsplit] 46 | parambegin = line.find("(", firstsplit) 47 | curr_func.name = line[firstsplit + 1:parambegin] 48 | parambegin += 1 49 | 50 | while True: 51 | type_end = line.find(" ", parambegin) 52 | p = Param() 53 | p.type = line[parambegin:type_end] 54 | name_begin = type_end + 1 55 | name_end = line.find(",", name_begin) 56 | if name_end == -1: 57 | name_end = line.find(")", name_begin) 58 | p.name = line[name_begin:name_end] 59 | curr_func.params.append(p) 60 | 61 | if name_end == -1: 62 | break 63 | 64 | if line[name_end:] == ",": 65 | break 66 | 67 | parambegin = name_end + 2 68 | 69 | if line[name_end:] == ");": 70 | print(curr_func) 71 | functions.append(curr_func) 72 | curr_func = None 73 | break 74 | 75 | # for f in functions: 76 | # print(f.name) 77 | 78 | --------------------------------------------------------------------------------