1 /// DDUI SDL2 OpenGL1.1 example 2 module demo; 3 4 import core.stdc.stdio : printf, sprintf, snprintf; 5 import core.stdc.string; 6 import core.stdc.ctype; 7 import core.stdc.stdarg; 8 import bindbc.opengl; 9 import bindbc.sdl, bindbc.sdl.dynload; 10 import ddui, stopwatch; 11 version (Demo_GL33) 12 import renderer.sdl2.gl33; 13 else 14 import renderer.sdl2.gl11; 15 16 extern (C): 17 18 // Start and runtime window dimension. 19 __gshared int window_width = 700; 20 __gshared int window_height = 550; 21 __gshared SDL_Window *window; 22 __gshared SDL_GLContext glctx; 23 __gshared mu_Context uictx; 24 25 void main(int argc, const(char) **args) 26 { 27 bool cli_debug; 28 29 for (int argi = 1; argi < argc; ++argi) 30 { 31 const(char) *arg = args[argi]; 32 33 if (strcmp(arg, "--debug") == 0) 34 cli_debug = true; 35 } 36 37 if (cli_debug) 38 { 39 printf("* mu_Context.sizeof: %zu\n", mu_Context.sizeof); 40 printf("* mu_Command.sizeof: %zu\n", mu_Command.sizeof); 41 } 42 43 import std.compiler : version_major, version_minor; 44 printf("* COMPILER : "~__VENDOR__~" v%u.%03u\n", version_major, version_minor); 45 46 // Comment this section if you plan to use 47 // the bindbc-sdl:staticBC configuration. 48 version (Windows) 49 { 50 SDLSupport sdlstatus = loadSDL("sdl2.dll"); 51 } 52 else 53 { 54 SDLSupport sdlstatus = loadSDL(); 55 } 56 57 switch (sdlstatus) with (SDLSupport) { 58 case noLibrary: assert(0, "No SDL libraries found on system, aborting."); 59 case badLibrary: assert(0, "SDL library older than configuration, aborting."); 60 default: 61 } 62 63 // Setup SDL 64 SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0"); 65 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS); 66 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 67 version (GL33) 68 { 69 //SDL_GL_SetAttribute (SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); 70 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 71 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 72 } 73 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 74 75 // Initiate SDL window 76 window = SDL_CreateWindow("Demo", 77 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 78 window_width, window_height, 79 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); 80 glctx = SDL_GL_CreateContext(window); 81 82 // Print SDL version 83 SDL_version sdlverconf = void, sdlverrt = void; 84 SDL_VERSION(&sdlverconf); 85 SDL_GetVersion(&sdlverrt); 86 printf("* SDL_VERSION : %u.%u.%u configured, %u.%u.%u running\n", 87 sdlverconf.major, sdlverconf.minor, sdlverconf.patch, 88 sdlverrt.major, sdlverrt.minor, sdlverrt.patch); 89 90 // OpenGL setup 91 printf("* CONFIG : "~CONFIGURATION~"\n"); 92 initiate_renderer(); 93 printf("* GL_RENDERER : %s\n", glGetString(GL_RENDERER)); 94 printf("* GL_VERSION : %s\n", glGetString(GL_VERSION)); 95 96 // Init UI 97 mu_init(&uictx); 98 uictx.text_width = &text_width; 99 uictx.text_height = &text_height; 100 101 stopwatch_t.setup(); 102 103 GAME: while (true) 104 { 105 // Transmit SDL input events to UI 106 SDL_Event e = void; 107 sw_input.start; 108 while (SDL_PollEvent(&e)) 109 { 110 switch (e.type) 111 { 112 case SDL_QUIT: break GAME; 113 case SDL_MOUSEMOTION: 114 mu_input_mousemove(&uictx, e.motion.x, e.motion.y); 115 continue; 116 case SDL_MOUSEWHEEL: 117 mu_input_scroll(&uictx, 0, e.wheel.y * -30); 118 continue; 119 case SDL_TEXTINPUT: 120 mu_input_text(&uictx, e.text.text.ptr); 121 continue; 122 123 case SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP: 124 int b = button_map[e.button.button & 0xff]; 125 if (!b) continue; 126 switch (e.type) { 127 case SDL_MOUSEBUTTONDOWN: 128 mu_input_mousedown(&uictx, e.button.x, e.button.y, b); 129 continue; 130 case SDL_MOUSEBUTTONUP: 131 mu_input_mouseup(&uictx, e.button.x, e.button.y, b); 132 continue; 133 default: continue; 134 } 135 136 case SDL_KEYDOWN, SDL_KEYUP: 137 int k = key_map[e.key.keysym.sym & 0xff]; 138 if (!k) continue; 139 switch (e.type) { 140 case SDL_KEYDOWN: mu_input_keydown(&uictx, k); continue; 141 case SDL_KEYUP: mu_input_keyup(&uictx, k); continue; 142 default: 143 } 144 continue; 145 146 default: 147 } 148 } 149 sw_input.stop; 150 151 // Process UI 152 sw_ui.stop; 153 mu_begin(&uictx); 154 if (cli_debug) 155 { 156 debug_window(&uictx); 157 log_window(&uictx); 158 style_window(&uictx); 159 } 160 else 161 { 162 log_window(&uictx); 163 test_window(&uictx); 164 style_window(&uictx); 165 } 166 mu_end(&uictx); 167 sw_ui.start; 168 169 // Clear screen and process rendering commands from UI 170 stat_commands = uictx.command_list.idx; 171 stat_id = uictx.id_stack.idx; 172 sw_commands.start; 173 r_clear(mu_Color(cast(ubyte)bg[0], cast(ubyte)bg[1], cast(ubyte)bg[2], 255)); 174 foreach (ref mu_Command cmd ; mu_command_range(&uictx)) 175 { 176 switch (cmd.type) 177 { 178 case MU_COMMAND_TEXT: r_draw_text(cmd.text.str.ptr, cmd.text.pos, cmd.text.color); continue; 179 case MU_COMMAND_RECT: r_draw_rect(cmd.rect.rect, cmd.rect.color); continue; 180 case MU_COMMAND_ICON: r_draw_icon(cmd.icon.id, cmd.icon.rect, cmd.icon.color); continue; 181 case MU_COMMAND_CLIP: r_set_clip_rect(cmd.clip.rect); continue; 182 default: continue; 183 } 184 } 185 sw_commands.stop; 186 187 // Render screen 188 sw_render.start; 189 r_present(); 190 sw_render.stop; 191 } 192 193 destroy_renderer(); 194 SDL_GL_DeleteContext(glctx); 195 SDL_DestroyWindow(window); 196 SDL_Quit(); 197 } 198 199 private: 200 201 __gshared stopwatch_t sw_input; 202 __gshared stopwatch_t sw_ui; 203 __gshared stopwatch_t sw_commands; 204 __gshared stopwatch_t sw_render; 205 206 __gshared size_t stat_commands; 207 __gshared size_t stat_id; 208 209 void debug_window(mu_Context *ctx) 210 { 211 if (mu_begin_window(ctx, "Debug", mu_Rect(40, 40, 300, 450))) 212 { 213 if (mu_header_ex(ctx, "Times", MU_OPT_EXPANDED)) 214 { 215 enum bufsz = 32; 216 char[bufsz] buf = void; 217 static immutable const(char)* fmt = "%.3f ms"; 218 219 static immutable int[2] cols = [ 80, -1 ]; 220 mu_layout_row(ctx, 2, cols.ptr, 0); 221 222 snprintf(buf.ptr, bufsz, fmt, sw_input.ms); 223 mu_label(ctx, "Input:"); 224 mu_label(ctx, buf.ptr); 225 226 snprintf(buf.ptr, bufsz, fmt, sw_ui.ms); 227 mu_label(ctx, "UI:"); 228 mu_label(ctx, buf.ptr); 229 230 snprintf(buf.ptr, bufsz, fmt, sw_commands.ms); 231 mu_label(ctx, "Commands:"); 232 mu_label(ctx, buf.ptr); 233 234 snprintf(buf.ptr, bufsz, fmt, sw_render.ms); 235 mu_label(ctx, "Render:"); 236 mu_label(ctx, buf.ptr); 237 } 238 239 if (mu_header_ex(ctx, "Stats", MU_OPT_EXPANDED)) 240 { 241 enum bufsz = 32; 242 char[bufsz] buf = void; 243 244 static immutable int[2] cols = [ 80, -1 ]; 245 mu_layout_row(ctx, 2, cols.ptr, 0); 246 247 snprintf(buf.ptr, 32, "%zu", stat_commands); 248 mu_label(ctx, "Commands:"); 249 mu_label(ctx, buf.ptr); 250 251 snprintf(buf.ptr, 32, "%zu", stat_id); 252 mu_label(ctx, "IDs:"); 253 mu_label(ctx, buf.ptr); 254 } 255 256 mu_end_window(ctx); 257 } 258 } 259 260 void test_window(mu_Context *ctx) 261 { 262 if (mu_begin_window(ctx, "Demo Window", mu_Rect(40, 40, 300, 450))) 263 { 264 mu_Container *win = mu_get_current_container(ctx); 265 win.rect.w = mu_max(win.rect.w, 240); 266 win.rect.h = mu_max(win.rect.h, 300); 267 268 // window info 269 if (mu_header(ctx, "Window Info")) 270 { 271 mu_Container *win2 = mu_get_current_container(ctx); 272 char[64] buf = void; 273 static immutable int[2] cols = [ 54, -1 ]; 274 mu_layout_row(ctx, 2, cols.ptr, 0); 275 mu_label(ctx, "Position:"); 276 sprintf(buf.ptr, "%d, %d", win2.rect.x, win2.rect.y); mu_label(ctx, buf.ptr); 277 mu_label(ctx, "Size:"); 278 sprintf(buf.ptr, "%d, %d", win2.rect.w, win2.rect.h); mu_label(ctx, buf.ptr); 279 } 280 281 // labels + buttons 282 if (mu_header_ex(ctx, "Test Buttons", MU_OPT_EXPANDED)) 283 { 284 static immutable int[3] cols = [ 86, -110, -1 ]; 285 mu_layout_row(ctx, 3, cols.ptr, 0); 286 mu_label(ctx, "Test buttons 1:"); 287 if (mu_button(ctx, "Button 1")) { write_log("Pressed button 1"); } 288 if (mu_button(ctx, "Button 2")) { write_log("Pressed button 2"); } 289 mu_label(ctx, "Test buttons 2:"); 290 if (mu_button(ctx, "Button 3")) { write_log("Pressed button 3"); } 291 if (mu_button(ctx, "Popup")) { mu_open_popup(ctx, "Test Popup"); } 292 if (mu_begin_popup(ctx, "Test Popup")) { 293 mu_button(ctx, "Hello"); 294 mu_button(ctx, "World"); 295 mu_end_popup(ctx); 296 } 297 } 298 299 // tree 300 if (mu_header_ex(ctx, "Tree and Text", MU_OPT_EXPANDED)) 301 { 302 static immutable int[2] cols1 = [ 140, -1 ]; 303 mu_layout_row(ctx, 2, cols1.ptr, 0); 304 mu_layout_begin_column(ctx); 305 if (mu_begin_treenode(ctx, "Test 1")) { 306 if (mu_begin_treenode(ctx, "Test 1a")) { 307 mu_label(ctx, "Hello"); 308 mu_label(ctx, "world"); 309 mu_end_treenode(ctx); 310 } 311 if (mu_begin_treenode(ctx, "Test 1b")) { 312 if (mu_button(ctx, "Button 1")) { write_log("Pressed button 1"); } 313 if (mu_button(ctx, "Button 2")) { write_log("Pressed button 2"); } 314 mu_end_treenode(ctx); 315 } 316 mu_end_treenode(ctx); 317 } 318 if (mu_begin_treenode(ctx, "Test 2")) 319 { 320 static immutable int[2] cols2 = [ 54, 54 ]; 321 mu_layout_row(ctx, 2, cols2.ptr, 0); 322 if (mu_button(ctx, "Button 3")) { write_log("Pressed button 3"); } 323 if (mu_button(ctx, "Button 4")) { write_log("Pressed button 4"); } 324 if (mu_button(ctx, "Button 5")) { write_log("Pressed button 5"); } 325 if (mu_button(ctx, "Button 6")) { write_log("Pressed button 6"); } 326 mu_end_treenode(ctx); 327 } 328 if (mu_begin_treenode(ctx, "Test 3")) 329 { 330 __gshared int[3] checks = [ 1, 0, 1 ]; // Mutable 331 mu_checkbox(ctx, "Checkbox 1", &checks[0]); 332 mu_checkbox(ctx, "Checkbox 2", &checks[1]); 333 mu_checkbox(ctx, "Checkbox 3", &checks[2]); 334 mu_end_treenode(ctx); 335 } 336 mu_layout_end_column(ctx); 337 338 mu_layout_begin_column(ctx); 339 static immutable int[1] cols3 = [ -1 ]; 340 mu_layout_row(ctx, 1, cols3.ptr, 0); 341 mu_text(ctx, 342 "Lorem ipsum dolor sit amet, consectetur adipiscing "~ 343 "elit. Maecenas lacinia, sem eu lacinia molestie, mi risus faucibus "~ 344 "ipsum, eu varius magna felis a nulla."); 345 mu_layout_end_column(ctx); 346 } 347 348 /* background color sliders */ 349 if (mu_header_ex(ctx, "Background Color", MU_OPT_EXPANDED)) 350 { 351 static immutable int[2] cols4 = [ -78, -1 ]; 352 mu_layout_row(ctx, 2, cols4.ptr, 74); 353 // sliders 354 mu_layout_begin_column(ctx); 355 static immutable int[2] cols5 = [ 46, -1 ]; 356 mu_layout_row(ctx, 2, cols5.ptr, 0); 357 mu_label(ctx, "Red:"); mu_slider(ctx, &bg[0], 0, 255); 358 mu_label(ctx, "Green:"); mu_slider(ctx, &bg[1], 0, 255); 359 mu_label(ctx, "Blue:"); mu_slider(ctx, &bg[2], 0, 255); 360 mu_layout_end_column(ctx); 361 // color preview 362 mu_Rect r = mu_layout_next(ctx); 363 mu_draw_rect(ctx, r, 364 mu_Color(cast(ubyte)bg[0], cast(ubyte)bg[1], cast(ubyte)bg[2], 255)); 365 char[32] buf = void; 366 sprintf(buf.ptr, "#%02X%02X%02X", 367 cast(int)bg[0], cast(int)bg[1], cast(int)bg[2]); 368 mu_draw_control_text(ctx, buf.ptr, r, MU_COLOR_TEXT, MU_OPT_ALIGNCENTER); 369 } 370 371 mu_end_window(ctx); 372 } 373 } 374 375 int uint8_slider(mu_Context *ctx, ubyte *value, int low, int high) 376 { 377 mu_push_id(ctx, &value, value.sizeof); 378 float tmp = *value; 379 int res = mu_slider_ex(ctx, &tmp, low, high, 0, "%.0f", MU_OPT_ALIGNCENTER); 380 *value = cast(ubyte)tmp; 381 mu_pop_id(ctx); 382 return res; 383 } 384 385 struct color_t 386 { 387 const(char) *label; 388 int idx; 389 } 390 391 immutable color_t[] colors = [ 392 { "text", MU_COLOR_TEXT }, 393 { "border", MU_COLOR_BORDER }, 394 { "windowbg", MU_COLOR_WINDOWBG }, 395 { "titlebg", MU_COLOR_TITLEBG }, 396 { "titletext", MU_COLOR_TITLETEXT }, 397 { "panelbg", MU_COLOR_PANELBG }, 398 { "button", MU_COLOR_BUTTON }, 399 { "buttonhover", MU_COLOR_BUTTONHOVER }, 400 { "buttonfocus", MU_COLOR_BUTTONFOCUS }, 401 { "base", MU_COLOR_BASE }, 402 { "basehover", MU_COLOR_BASEHOVER }, 403 { "basefocus", MU_COLOR_BASEFOCUS }, 404 { "scrollbase", MU_COLOR_SCROLLBASE }, 405 { "scrollthumb", MU_COLOR_SCROLLTHUMB }, 406 { null } 407 ]; 408 409 void style_window(mu_Context *ctx) 410 { 411 if (mu_begin_window(ctx, "Style Editor", mu_Rect(350, 250, 300, 240))) 412 { 413 int sw = cast(int)(mu_get_current_container(ctx).body_.w * 0.14f); // ~1/5 414 int[6] r = [ 80, sw, sw, sw, sw, -1 ]; 415 mu_layout_row(ctx, 6, r.ptr, 0); 416 for (size_t i; colors[i].label; ++i) 417 { 418 mu_label(ctx, colors[i].label); 419 uint8_slider(ctx, &ctx.style.colors[i].r, 0, 255); 420 uint8_slider(ctx, &ctx.style.colors[i].g, 0, 255); 421 uint8_slider(ctx, &ctx.style.colors[i].b, 0, 255); 422 uint8_slider(ctx, &ctx.style.colors[i].a, 0, 255); 423 mu_draw_rect(ctx, mu_layout_next(ctx), ctx.style.colors[i]); 424 } 425 mu_end_window(ctx); 426 } 427 } 428 429 __gshared char[64000] logbuf = 0; // char.init == 0xff, confuses strlen 430 __gshared size_t logi; 431 __gshared int logbuf_updated = 0; 432 __gshared float[3] bg = [ 90, 95, 100 ]; 433 434 void write_log(const(char) *text) 435 { 436 size_t l = strlen(text); 437 if (l == 0) return; 438 memcpy(logbuf.ptr + logi, text, l); 439 logi += l; 440 logbuf[logi] = '\n'; 441 logbuf[++logi] = 0; 442 logbuf_updated = 1; 443 } 444 445 void log_window(mu_Context *ctx) 446 { 447 if (mu_begin_window(ctx, "Console", mu_Rect(350, 40, 300, 200))) 448 { 449 /* output text panel */ 450 static immutable int[1] cols1 = [ -1 ]; // (int[]) { -1 } 451 mu_layout_row(ctx, 1, cols1.ptr, -25); 452 mu_begin_panel(ctx, "Log Output"); 453 mu_Container *panel = mu_get_current_container(ctx); 454 mu_layout_row(ctx, 1, cols1.ptr, -1); 455 mu_text(ctx, logbuf.ptr); 456 mu_end_panel(ctx); 457 if (logbuf_updated) { 458 panel.scroll.y = panel.content_size.y; 459 logbuf_updated = 0; 460 } 461 462 /* input textbox + submit button */ 463 static immutable int[2] cols2 = [ -70, -1 ]; 464 __gshared char[128] buf = [ 0 ]; // textbox buffer 465 int submitted = 0; 466 mu_layout_row(ctx, 2, cols2.ptr, 0); 467 if (mu_textbox(ctx, buf.ptr, 128) & MU_RES_SUBMIT) { 468 mu_set_focus(ctx, ctx.last_id); 469 submitted = 1; 470 } 471 if (mu_button(ctx, "Submit")) { submitted = 1; } 472 if (submitted) { 473 write_log(buf.ptr); 474 buf[0] = '\0'; 475 } 476 477 mu_end_window(ctx); 478 } 479 } 480 481 /// SDL-DDUI mouse button mapping 482 immutable const(ubyte)[256] button_map = [ 483 SDL_BUTTON_LEFT & 0xff : MU_MOUSE_LEFT, 484 SDL_BUTTON_RIGHT & 0xff : MU_MOUSE_RIGHT, 485 SDL_BUTTON_MIDDLE & 0xff : MU_MOUSE_MIDDLE, 486 ]; 487 488 /// SDL-DDUI keyboard key mapping 489 immutable const(ubyte)[256] key_map = [ 490 SDLK_LSHIFT & 0xff : MU_KEY_SHIFT, 491 SDLK_RSHIFT & 0xff : MU_KEY_SHIFT, 492 SDLK_LCTRL & 0xff : MU_KEY_CTRL, 493 SDLK_RCTRL & 0xff : MU_KEY_CTRL, 494 SDLK_LALT & 0xff : MU_KEY_ALT, 495 SDLK_RALT & 0xff : MU_KEY_ALT, 496 SDLK_RETURN & 0xff : MU_KEY_RETURN, 497 SDLK_KP_ENTER & 0xff : MU_KEY_RETURN, 498 SDLK_BACKSPACE & 0xff : MU_KEY_BACKSPACE, 499 ]; 500 501 int text_width(mu_Font font, const(char) *text, int len) 502 { 503 if (len == -1) { len = cast(int)strlen(text); } 504 return r_get_text_width(text, len); 505 } 506 507 int text_height(mu_Font font) 508 { 509 return r_get_text_height(); 510 }