1 /* 2 * Copyright (c) 2012-2016, Bruno Levy All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * * Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright notice, 10 * this list of conditions and the following disclaimer in the documentation 11 * and/or other materials provided with the distribution. 12 * * Neither the name of the ALICE Project-Team nor the names of its 13 * contributors may be used to endorse or promote products derived from this 14 * software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 * 28 * If you modify this software, you should include a notice giving the 29 * name of the person performing the modification, the date of modification, 30 * and the reason for such modification. 31 * 32 * Contact: Bruno Levy 33 * 34 * Bruno.Levy@inria.fr 35 * http://www.loria.fr/~levy 36 * 37 * ALICE Project 38 * LORIA, INRIA Lorraine, 39 * Campus Scientifique, BP 239 40 * 54506 VANDOEUVRE LES NANCY CEDEX 41 * FRANCE 42 * 43 */ 44 45 #include <geogram_gfx/gui/application.h> 46 47 #include <geogram_gfx/third_party/ImGui/imgui.h> 48 49 #include <geogram_gfx/third_party/ImGui/imgui_impl_opengl3.h> 50 #include <geogram_gfx/ImGui_ext/icon_font.h> 51 52 #include <geogram_gfx/lua/lua_glup.h> 53 #include <geogram_gfx/lua/lua_imgui.h> 54 55 #include <geogram_gfx/third_party/imgui_fonts/cousine_regular.h> 56 #include <geogram_gfx/third_party/imgui_fonts/roboto_medium.h> 57 #include <geogram_gfx/third_party/imgui_fonts/fa_solid.h> 58 59 #include <geogram/image/image.h> 60 #include <geogram/basic/command_line.h> 61 #include <geogram/basic/command_line_args.h> 62 #include <geogram/basic/logger.h> 63 #include <geogram/basic/file_system.h> 64 65 66 #if defined(GEO_GLFW) 67 # include <geogram_gfx/third_party/ImGui/imgui_impl_glfw.h> 68 // Too many documentation warnings in glfw 69 // (glfw uses tags that clang does not understand). 70 # ifdef __clang__ 71 # pragma GCC diagnostic ignored "-Wdocumentation" 72 # endif 73 74 # if defined(GEO_USE_SYSTEM_GLFW3) || defined(GEO_OS_EMSCRIPTEN) 75 # include <GLFW/glfw3.h> 76 # else 77 # include <third_party/glfw/include/GLFW/glfw3.h> 78 # endif 79 80 #ifdef GEO_OS_EMSCRIPTEN 81 # include <emscripten.h> 82 #endif 83 84 85 #endif 86 87 88 namespace GEO { 89 90 void StyleColorsCorporateGrey(bool threeD); 91 /** 92 * \brief Computes the pixel ratio for hidpi devices. 93 * \details Uses the current GLFW window. 94 */ 95 double compute_pixel_ratio(); 96 97 /** 98 * \brief Computes the scaling factor for hidpi devices. 99 * \details Uses the current GLFW window. 100 */ 101 double compute_hidpi_scaling(); 102 103 104 /** 105 * \brief If nothing happens during 100 frames, then 106 * we (micro)-sleep instead of redrawing the 107 * window. 108 */ 109 const index_t NB_FRAMES_UPDATE_INIT = 100; 110 111 #if defined(GEO_GLFW) 112 class ApplicationData { 113 public: ApplicationData()114 ApplicationData() { 115 window_ = nullptr; 116 GLFW_callbacks_initialized_ = false; 117 ImGui_callback_mouse_button = nullptr; 118 ImGui_callback_cursor_pos = nullptr; 119 ImGui_callback_scroll = nullptr; 120 ImGui_callback_key = nullptr; 121 ImGui_callback_char = nullptr; 122 ImGui_callback_drop = nullptr; 123 ImGui_callback_refresh = nullptr; 124 } 125 GLFWwindow* window_; 126 bool GLFW_callbacks_initialized_; 127 GLFWmousebuttonfun ImGui_callback_mouse_button; 128 GLFWcursorposfun ImGui_callback_cursor_pos; 129 GLFWscrollfun ImGui_callback_scroll; 130 GLFWkeyfun ImGui_callback_key; 131 GLFWcharfun ImGui_callback_char; 132 GLFWdropfun ImGui_callback_drop; 133 GLFWwindowrefreshfun ImGui_callback_refresh; 134 }; 135 136 #else 137 # error "No windowing system" 138 #endif 139 140 } 141 142 namespace GEO { 143 144 Application* Application::instance_ = nullptr; 145 Application(const std::string & name)146 Application::Application(const std::string& name) { 147 geo_assert(instance_ == nullptr); 148 GEO::initialize(); 149 instance_ = this; 150 name_ = name; 151 data_ = new ApplicationData(); 152 ImGui_restart_ = false; 153 ImGui_reload_font_ = false; 154 ImGui_initialized_ = false; 155 ImGui_firsttime_init_ = false; 156 width_ = 800; 157 height_ = 800; 158 frame_buffer_width_ = 800; 159 frame_buffer_height_ = 800; 160 in_main_loop_ = false; 161 accept_drops_ = true; 162 scaling_ = 1.0; 163 font_size_ = 18; 164 nb_update_locks_ = 0; 165 nb_frames_update_ = NB_FRAMES_UPDATE_INIT; 166 hidpi_scaling_ = 1.0; 167 pixel_ratio_ = 1.0; 168 currently_drawing_gui_ = false; 169 animate_ = false; 170 menubar_visible_ = true; 171 phone_screen_ = false; 172 soft_keyboard_visible_ = false; 173 } 174 ~Application()175 Application::~Application() { 176 delete_window(); 177 geo_assert(instance_ == this); 178 delete data_; 179 data_ = nullptr; 180 instance_ = nullptr; 181 } 182 start(int argc,char ** argv)183 void Application::start(int argc, char** argv) { 184 if(argc != 0 && argv != nullptr) { 185 geogram_initialize(argc, argv); 186 } 187 if(CmdLine::arg_is_declared("gui:font_size")) { 188 set_font_size(CmdLine::get_arg_uint("gui:font_size")); 189 } 190 create_window(); 191 main_loop(); 192 } 193 stop()194 void Application::stop() { 195 in_main_loop_ = false; 196 } 197 get_styles()198 std::string Application::get_styles() { 199 #ifdef GEO_OS_ANDROID 200 return "Light;Dark"; 201 #else 202 return "Light;Dark;CorporateGrey"; 203 #endif 204 } 205 set_style(const std::string & style_name)206 void Application::set_style(const std::string& style_name) { 207 style_ = style_name; 208 209 if(style_name == "Light") { 210 ImGui::StyleColorsLight(); 211 ImGuiStyle& style = ImGui::GetStyle(); 212 style.WindowRounding = 10.0f; 213 style.FrameRounding = 5.0f; 214 style.GrabRounding = 10.0f; 215 style.WindowBorderSize = 1.5f; 216 style.FrameBorderSize = 1.0f; 217 style.PopupBorderSize = 1.0f; 218 ImVec4* colors = style.Colors; 219 colors[ImGuiCol_Text] = ImVec4(0.0f, 0.0f, 0.25f, 1.00f); 220 colors[ImGuiCol_TextDisabled] = ImVec4(0.25f, 0.25f, 0.75f, 1.00f); 221 colors[ImGuiCol_Separator] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f); 222 } else if(style_name == "Dark") { 223 ImGuiStyle& style = ImGui::GetStyle(); 224 style.WindowRounding = 10.0f; 225 style.FrameRounding = 5.0f; 226 style.GrabRounding = 10.0f; 227 style.WindowBorderSize = 1.5f; 228 style.FrameBorderSize = 0.0f; 229 style.PopupBorderSize = 1.0f; 230 ImGui::StyleColorsDark(); 231 } else if(style_name == "CorporateGrey") { 232 StyleColorsCorporateGrey(true); 233 } else { 234 set_style("Light"); 235 Logger::err("Skin") << style_name << ": no such style" 236 << std::endl; 237 } 238 239 ImGuiStyle& style = ImGui::GetStyle(); 240 if(CmdLine::get_arg_bool("gfx:transparent")) { 241 // Make ImGui windows opaque if background is 242 // transparent (else it becomes difficult to 243 // distinguish anything...) 244 style.Alpha = 1.0f; 245 } else { 246 style.Alpha = 0.90f; 247 } 248 249 if(phone_screen_) { 250 style.ScrollbarSize = 10.0f * float(scaling_); 251 style.GrabMinSize = 15.0f * float(scaling_); 252 } 253 } 254 set_font_size(index_t value)255 void Application::set_font_size(index_t value) { 256 font_size_ = index_t(value); 257 scaling_ = double(font_size_)/16.0; 258 if(phone_screen_) { 259 scaling_ *= double(std::max(get_width(), get_height()))/600.0; 260 } 261 if(CmdLine::arg_is_declared("gui:font_size")) { 262 CmdLine::set_arg("gui:font_size", String::to_string(value)); 263 } 264 if(ImGui_initialized_) { 265 ImGui_reload_font_ = true; 266 } 267 } 268 scaling() const269 double Application::scaling() const { 270 return scaling_ * hidpi_scaling_ / pixel_ratio_; 271 } 272 resize(index_t w,index_t h,index_t fb_w,index_t fb_h)273 void Application::resize( 274 index_t w, index_t h, index_t fb_w, index_t fb_h 275 ) { 276 width_ = w; 277 height_ = h; 278 frame_buffer_width_ = fb_w; 279 frame_buffer_height_ = fb_h; 280 scaling_ = double(font_size_)/16.0; 281 if(phone_screen_) { 282 scaling_ *= double(std::max(get_width(), get_height()))/600.0; 283 } 284 update(); 285 } 286 draw_gui()287 void Application::draw_gui() { 288 } 289 draw_graphics()290 void Application::draw_graphics() { 291 } 292 mouse_button_callback(int button,int action,int mods,int source)293 void Application::mouse_button_callback( 294 int button, int action, int mods, int source 295 ) { 296 geo_argused(button); 297 geo_argused(action); 298 geo_argused(mods); 299 geo_argused(source); 300 } 301 scroll_callback(double xoffset,double yoffset)302 void Application::scroll_callback(double xoffset, double yoffset) { 303 geo_argused(xoffset); 304 geo_argused(yoffset); 305 } 306 cursor_pos_callback(double x,double y,int source)307 void Application::cursor_pos_callback(double x, double y, int source) { 308 geo_argused(x); 309 geo_argused(y); 310 geo_argused(source); 311 } 312 drop_callback(int nb,const char ** f)313 void Application::drop_callback(int nb, const char** f) { 314 geo_argused(nb); 315 geo_argused(f); 316 } 317 char_callback(unsigned int c)318 void Application::char_callback(unsigned int c) { 319 geo_argused(c); 320 } 321 key_callback(int key,int scancode,int action,int mods)322 void Application::key_callback( 323 int key, int scancode, int action, int mods 324 ) { 325 geo_argused(key); 326 geo_argused(scancode); 327 geo_argused(action); 328 geo_argused(mods); 329 } 330 draw_dock_space()331 void Application::draw_dock_space() { 332 ImGuiIO& io = ImGui::GetIO(); 333 // Create window and dockspace for docking. 334 if((io.ConfigFlags & ImGuiConfigFlags_DockingEnable) != 0) { 335 ImGuiViewport* viewport = ImGui::GetMainViewport(); 336 ImGui::SetNextWindowPos(viewport->Pos); 337 ImGui::SetNextWindowSize(viewport->Size); 338 ImGui::SetNextWindowViewport(viewport->ID); 339 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); 340 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); 341 ImGui::PushStyleVar( 342 ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f) 343 ); 344 static bool open = true; 345 ImGui::Begin( 346 "DockSpace", &open, 347 ImGuiWindowFlags_NoDocking | 348 ImGuiWindowFlags_NoTitleBar | 349 ImGuiWindowFlags_NoCollapse | 350 ImGuiWindowFlags_NoResize | 351 ImGuiWindowFlags_NoMove | 352 ImGuiWindowFlags_NoBringToFrontOnFocus | 353 ImGuiWindowFlags_NoNavFocus | 354 ImGuiWindowFlags_NoBackground | 355 (menubar_visible_ ? ImGuiWindowFlags_MenuBar : 0) 356 ); 357 ImGui::PopStyleVar(3); 358 ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); 359 ImGui::DockSpace( 360 dockspace_id, ImVec2(0.0f, 0.0f), 361 ImGuiDockNodeFlags_None | 362 ImGuiDockNodeFlags_PassthruCentralNode | 363 ImGuiDockNodeFlags_AutoHideTabBar 364 ); 365 ImGui::End(); 366 } 367 } 368 draw()369 void Application::draw() { 370 if(!ImGui_initialized_) { 371 return; 372 } 373 update(); 374 if(nb_update_locks_ == 0 && !Process::is_running_threads()) { 375 one_frame(); 376 } 377 } 378 GL_initialize()379 void Application::GL_initialize() { 380 GEO::Graphics::initialize(); 381 geo_assert(glupCurrentContext() == nullptr); 382 glupMakeCurrent(glupCreateContext()); 383 if(glupCurrentContext() == nullptr) { 384 Logger::err("Skin") << "Could not create GLUP context" 385 << std::endl; 386 exit(-1); 387 } 388 } 389 GL_terminate()390 void Application::GL_terminate() { 391 glupDeleteContext(glupCurrentContext()); 392 glupMakeCurrent(nullptr); 393 GEO::Graphics::terminate(); 394 } 395 ImGui_initialize()396 void Application::ImGui_initialize() { 397 geo_assert(!ImGui_initialized_ ); 398 ImGui::CreateContext(); 399 { 400 std::string state = CmdLine::get_arg("gui:state"); 401 for(size_t i=0; i<state.length(); ++i) { 402 if(state[i] == '\t') { 403 state[i] = '\n'; 404 } 405 } 406 ImGui::LoadIniSettingsFromMemory(state.c_str()); 407 } 408 ImGuiIO& io = ImGui::GetIO(); 409 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; 410 411 // Viewports allow to drag ImGui windows outside the app's window, 412 // but it is still a bit unstable, so deactivated it for now. 413 if( 414 CmdLine::arg_is_declared("gui:viewports") && 415 CmdLine::get_arg_bool("gui:viewports") 416 ) { 417 io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; 418 } 419 420 // Note: NavKeyboard sets WantsCaptureKeyboard all the time and 421 // thus prevents from nanosleeping ! 422 if( 423 CmdLine::arg_is_declared("gui:keyboard_nav") && 424 CmdLine::get_arg_bool("gui:keyboard_nav") 425 ) { 426 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; 427 } 428 429 #if defined(GEO_GLFW) 430 431 // Second argument to true = install callbacks 432 // (note that this function can be called multiple times, 433 // e.g. when ImGui is restarted after re-loading a saved 434 // window state, this mechanism prevents the callbacks to 435 // be installed multiple times, which would be an error since 436 // they are chained). 437 ImGui_ImplGlfw_InitForOpenGL( 438 data_->window_, !data_->GLFW_callbacks_initialized_ 439 ); 440 #endif 441 442 #if defined(GEO_OS_APPLE) 443 ImGui_ImplOpenGL3_Init("#version 330"); 444 #else 445 ImGui_ImplOpenGL3_Init("#version 100"); 446 #endif 447 callbacks_initialize(); 448 449 if(style_ != "") { 450 set_style(style_); 451 } else if(Environment::instance()->has_value("gui:style")) { 452 std::string style = Environment::instance()->get_value("gui:style"); 453 set_style(style); 454 } else { 455 set_style("Light"); 456 } 457 458 ImGui_load_fonts(); 459 ImGui_initialized_ = true; 460 ImGui_firsttime_init_ = true; 461 } 462 ImGui_load_fonts()463 void Application::ImGui_load_fonts() { 464 ImGuiIO& io = ImGui::GetIO(); 465 io.IniFilename = nullptr; 466 467 float s = 1.0f; 468 if(phone_screen_) { 469 s = float(std::max(get_width(), get_height())) / 600.0f; 470 } 471 472 float font_size = s * float(double(font_size_) * hidpi_scaling_); 473 474 // Default font 475 io.FontDefault = io.Fonts->AddFontFromMemoryCompressedTTF( 476 roboto_medium_compressed_data, 477 roboto_medium_compressed_size, font_size 478 ); 479 480 // Add icons to default font. 481 { 482 #define ICON_MIN_FA 0xf000 483 #define ICON_MAX_FA 0xf63c 484 485 ImFontConfig config; 486 config.MergeMode = true; 487 488 // Make the icon monospaced 489 config.GlyphMinAdvanceX = 1.5f*font_size; 490 config.GlyphOffset.y += 2.0f; 491 492 static const ImWchar icon_ranges[] = { 493 ICON_MIN_FA, ICON_MAX_FA, 0 494 }; 495 496 io.Fonts->AddFontFromMemoryCompressedTTF( 497 fa_solid_compressed_data, 498 fa_solid_compressed_size, font_size, 499 &config, icon_ranges 500 ); 501 502 init_icon_table(); 503 } 504 505 // Fixed font for console and editor 506 io.Fonts->AddFontFromMemoryCompressedTTF( 507 cousine_regular_compressed_data, 508 cousine_regular_compressed_size, font_size 509 ); 510 511 // Larger font 512 io.Fonts->AddFontFromMemoryCompressedTTF( 513 roboto_medium_compressed_data, 514 roboto_medium_compressed_size, font_size*1.5f 515 ); 516 517 if(phone_screen_) { 518 // Smaller fixed font for console 519 io.Fonts->AddFontFromMemoryCompressedTTF( 520 cousine_regular_compressed_data, 521 cousine_regular_compressed_size, font_size*0.5f 522 ); 523 } 524 525 io.FontGlobalScale = float(1.0 / pixel_ratio_); 526 } 527 ImGui_terminate()528 void Application::ImGui_terminate() { 529 geo_assert(ImGui_initialized_); 530 ImGui_ImplOpenGL3_Shutdown(); 531 #if defined(GEO_GLFW) 532 // Note: normally, with the new ImGui (1.74), this 533 // desinstalls user callbacks and reinstalls the previous 534 // callbacks. I deactivated this mechanism to have the same 535 // behavior as in ImGui 1.72. TODO: do that properly ! 536 ImGui_ImplGlfw_Shutdown(); 537 #endif 538 ImGui::DestroyContext(); 539 ImGui_initialized_ = false; 540 } 541 ImGui_new_frame()542 void Application::ImGui_new_frame() { 543 ImGui_ImplOpenGL3_NewFrame(); 544 #if defined(GEO_GLFW) 545 ImGui_ImplGlfw_NewFrame(); 546 #endif 547 ImGui::NewFrame(); 548 549 } 550 geogram_initialize(int argc,char ** argv)551 void Application::geogram_initialize(int argc, char** argv) { 552 GEO::initialize(); 553 CmdLine::import_arg_group("standard"); 554 CmdLine::import_arg_group("algo"); 555 CmdLine::import_arg_group("gfx"); 556 CmdLine::import_arg_group("gui"); 557 CmdLine::parse(argc, argv, filenames_); 558 phone_screen_ = CmdLine::get_arg_bool("gui:phone_screen"); 559 #ifndef GEO_OS_ANDROID 560 if(phone_screen_ && CmdLine::get_arg("gfx:geometry") == "1024x1024") { 561 CmdLine::set_arg("gfx:geometry", "768x1024"); 562 } 563 #endif 564 } 565 needs_to_redraw() const566 bool Application::needs_to_redraw() const { 567 return 568 animate_ || 569 ImGui::GetIO().WantCaptureMouse || 570 ImGui::GetIO().WantCaptureKeyboard || 571 (nb_frames_update_ > 0); 572 } 573 update()574 void Application::update() { 575 // We redraw several frames, in order to make 576 // sure all events are properly processed. 577 nb_frames_update_ = NB_FRAMES_UPDATE_INIT; 578 } 579 set_gui_state(std::string x)580 void Application::set_gui_state(std::string x) { 581 CmdLine::set_arg("gui:state", x); 582 if(!ImGui_initialized_) { 583 return; 584 } 585 ImGui_restart_ = true; 586 } 587 get_gui_state() const588 std::string Application::get_gui_state() const { 589 std::string state; 590 if(ImGui_initialized_) { 591 state = std::string(ImGui::SaveIniSettingsToMemory()); 592 for(size_t i=0; i<state.length(); ++i) { 593 if(state[i] == '\n') { 594 state[i] = '\t'; 595 } 596 } 597 } 598 return state; 599 } 600 601 /**************************** GLFW-specific code *********************/ 602 #if defined(GEO_GLFW) 603 604 pre_draw()605 void Application::pre_draw() { 606 } 607 post_draw()608 void Application::post_draw() { 609 } 610 create_window()611 void Application::create_window() { 612 if(!glfwInit()) { 613 Logger::err("Skin") 614 << "Could not initialize GLFW" << std::endl; 615 exit(-1); 616 } 617 618 if(CmdLine::get_arg("gfx:GL_profile") == "core") { 619 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 620 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 621 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 622 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 623 } 624 625 if(CmdLine::get_arg_bool("gfx:GL_debug")) { 626 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); 627 } 628 629 const char* title = name_.c_str(); 630 631 { 632 std::string geometry = CmdLine::get_arg("gfx:geometry"); 633 std::vector<std::string> words; 634 String::split_string(geometry, 'x', words); 635 if(words.size() != 2) { 636 Logger::err("Skin") 637 << "Invalid gfx:geometry:" << geometry << std::endl; 638 exit(-1); 639 } 640 if( 641 !String::string_to_unsigned_integer(words[0].c_str(), width_) || 642 !String::string_to_unsigned_integer(words[1].c_str(), height_) 643 ) { 644 Logger::err("Skin") 645 << "Invalid gfx:geometry:" << geometry << std::endl; 646 exit(-1); 647 } 648 } 649 650 if(CmdLine::get_arg_bool("gfx:transparent")) { 651 #ifdef GLFW_TRANSPARENT_FRAMEBUFFER 652 glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); 653 #else 654 Logger::warn("Skin") 655 << "Transparent not supported by this version of GLFW" 656 << std::endl; 657 #endif 658 } 659 660 if(CmdLine::get_arg_bool("gfx:full_screen")) { 661 GLFWmonitor* monitor = glfwGetPrimaryMonitor(); 662 const GLFWvidmode* vidmode = glfwGetVideoMode(monitor); 663 width_ = index_t(vidmode->width); 664 height_ = index_t(vidmode->height); 665 666 bool no_decoration = CmdLine::get_arg_bool("gfx:no_decoration"); 667 668 if(no_decoration) { 669 glfwWindowHint(GLFW_FOCUSED,GL_TRUE); 670 glfwWindowHint(GLFW_DECORATED,GL_FALSE); 671 glfwWindowHint(GLFW_RESIZABLE,GL_FALSE); 672 glfwWindowHint(GLFW_AUTO_ICONIFY,GL_FALSE); 673 glfwWindowHint(GLFW_FLOATING,GL_FALSE); 674 glfwWindowHint(GLFW_MAXIMIZED,GL_TRUE); 675 } 676 677 data_->window_ = glfwCreateWindow( 678 int(width_), int(height_), title, 679 no_decoration ? glfwGetPrimaryMonitor() : nullptr, 680 nullptr 681 ); 682 683 } else { 684 data_->window_ = glfwCreateWindow( 685 int(width_), int(height_), title, nullptr, nullptr 686 ); 687 } 688 689 if(data_->window_ == nullptr) { 690 Logger::err("Skin") 691 << "Could not create GLFW window" << std::endl; 692 exit(-1); 693 } 694 695 glfwSetWindowUserPointer(data_->window_, this); 696 697 glfwMakeContextCurrent(data_->window_); 698 glfwSwapInterval(1); 699 700 hidpi_scaling_ = compute_hidpi_scaling(); 701 pixel_ratio_ = compute_pixel_ratio(); 702 703 Logger::out("Skin") 704 << "hidpi_scaling=" << hidpi_scaling_ << std::endl; 705 Logger::out("Skin") 706 << "pixel_ratio=" << pixel_ratio_ << std::endl; 707 } 708 delete_window()709 void Application::delete_window() { 710 glfwDestroyWindow(data_->window_); 711 data_->window_ = nullptr; 712 glfwTerminate(); 713 in_main_loop_ = false; 714 } 715 716 one_frame()717 void Application::one_frame() { 718 // Avoid nested ImGui calls 719 // (due to calling draw()) 720 if(currently_drawing_gui_) { 721 return; 722 } 723 724 // Can happen when ImGui Graphite application 725 // triggers update too soon. 726 if(data_->window_ == nullptr) { 727 return; 728 } 729 730 if(glfwWindowShouldClose(data_->window_) || !in_main_loop_) { 731 return; 732 } 733 734 735 { 736 int cur_width, cur_height; 737 int cur_fb_width, cur_fb_height; 738 739 glfwGetWindowSize(data_->window_, &cur_width, &cur_height); 740 glfwGetFramebufferSize( 741 data_->window_, &cur_fb_width, &cur_fb_height 742 ); 743 744 if( 745 int(width_) != cur_width || 746 int(height_) != cur_height || 747 int(frame_buffer_width_) != cur_fb_width || 748 int(frame_buffer_height_) != cur_fb_height 749 ) { 750 resize( 751 index_t(cur_width), index_t(cur_height), 752 index_t(cur_fb_width), index_t(cur_fb_height) 753 ); 754 } 755 } 756 757 { 758 // Detect if hidpi scaling changed. This can happen when 759 // dragging the window from the laptop screen to an external 760 // monitor. 761 if( 762 glfwGetCurrentContext() != nullptr && 763 compute_hidpi_scaling() != hidpi_scaling_ 764 ) { 765 hidpi_scaling_ = compute_hidpi_scaling(); 766 pixel_ratio_ = compute_pixel_ratio(); 767 set_font_size(font_size_); // This reloads the font. 768 } 769 } 770 771 772 glfwPollEvents(); 773 774 if(needs_to_redraw()) { 775 pre_draw(); 776 currently_drawing_gui_ = true; 777 ImGui_new_frame(); 778 draw_graphics(); 779 draw_gui(); 780 ImGui::Render(); 781 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 782 783 #ifndef GEO_OS_EMSCRIPTEN 784 // Update and Render additional Platform Windows 785 // (see ImGui demo app). 786 if(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { 787 GLFWwindow* backup_current_context = glfwGetCurrentContext(); 788 ImGui::UpdatePlatformWindows(); 789 ImGui::RenderPlatformWindowsDefault(); 790 glfwMakeContextCurrent(backup_current_context); 791 } 792 #endif 793 794 currently_drawing_gui_ = false; 795 796 #ifdef GEO_OS_EMSCRIPTEN 797 // Set alpha channel to 1 else the image is composited 798 // with the background 799 glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 800 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); 801 glClear(GL_COLOR_BUFFER_BIT); 802 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 803 #endif 804 805 glfwSwapBuffers(data_->window_); 806 post_draw(); 807 if( 808 nb_frames_update_ > 0 && !animate_ && 809 !(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) 810 ) { 811 --nb_frames_update_; 812 } 813 } else { 814 // Sleep for 0.2 seconds, to let the processor cold-down 815 // instead of actively waiting (be a good citizen for the 816 // other processes. 817 Process::sleep(20000); 818 } 819 820 // ImGui needs to be restarted whenever docking state is reloaded. 821 if(ImGui_restart_) { 822 ImGui_restart_ = false; 823 ImGui_terminate(); 824 if(CmdLine::arg_is_declared("gui:font_size")) { 825 set_font_size(CmdLine::get_arg_uint("gui:font_size")); 826 ImGui_reload_font_ = false; 827 } 828 ImGui_initialize(); 829 } else if(ImGui_reload_font_) { 830 ImGuiIO& io = ImGui::GetIO(); 831 io.Fonts->Clear(); 832 ImGui_load_fonts(); 833 ImGui_ImplOpenGL3_DestroyDeviceObjects(); 834 ImGui_reload_font_ = false; 835 } 836 } 837 838 #ifdef GEO_OS_EMSCRIPTEN 839 840 void emscripten_load_latest_file(); 841 842 /** 843 * \brief This function is called by the HTML shell each 844 * time a file is loaded. 845 */ emscripten_load_latest_file()846 void emscripten_load_latest_file() { 847 if(Application::instance() == nullptr) { 848 return; 849 } 850 std::vector<std::string> all_files; 851 GEO::FileSystem::get_directory_entries("/",all_files); 852 std::string filename = ""; 853 Numeric::uint64 timestamp = 0; 854 for(auto f: all_files) { 855 if(GEO::FileSystem::is_file(f)) { 856 Numeric::uint64 f_timestamp = 857 GEO::FileSystem::get_time_stamp(f); 858 if(f_timestamp > timestamp) { 859 timestamp = f_timestamp; 860 filename = f; 861 } 862 } 863 } 864 if(filename != "") { 865 const char* filename_str = filename.c_str(); 866 Application::instance()->drop_callback(1, &filename_str); 867 } 868 } 869 870 871 /** 872 * \brief The job to be done for each frame when running 873 * in Emscripten. 874 * \details The browser keeps control of the main loop 875 * in Emscripten. This function is a callback passed to 876 * the Emscripten runtime. 877 */ emscripten_one_frame()878 void emscripten_one_frame() { 879 if(Application::instance() == nullptr) { 880 return; 881 } 882 Application::instance()->one_frame(); 883 } 884 #endif 885 main_loop()886 void Application::main_loop() { 887 in_main_loop_ = true; 888 #ifdef GEO_OS_EMSCRIPTEN 889 FileSystem::set_file_system_changed_callback( 890 emscripten_load_latest_file 891 ); 892 GL_initialize(); 893 ImGui_initialize(); 894 emscripten_load_latest_file(); 895 emscripten_set_main_loop(emscripten_one_frame, 0, 1); 896 #else 897 bool initialized = false; 898 while (!glfwWindowShouldClose(data_->window_) && in_main_loop_) { 899 if(!initialized) { 900 GL_initialize(); 901 ImGui_initialize(); 902 initialized = true; 903 } 904 one_frame(); 905 } 906 if(initialized) { 907 ImGui_terminate(); 908 GL_terminate(); 909 } 910 #endif 911 } 912 913 namespace { GLFW_callback_mouse_button(GLFWwindow * w,int button,int action,int mods)914 void GLFW_callback_mouse_button( 915 GLFWwindow* w, int button, int action, int mods 916 ) { 917 Application* app = static_cast<Application*>( 918 glfwGetWindowUserPointer(w) 919 ); 920 app->update(); 921 if(!ImGui::GetIO().WantCaptureMouse) { 922 app->mouse_button_callback(button,action,mods); 923 } 924 // Note: when a menu is open and you click elsewhere, the 925 // WantCaptureMouse flag is still set, and the framework 926 // misses the "mouse button up" event. If a translation is 927 // active, it remains active later ("sticky translation" bug). 928 // The following code always generates a "mouse button up" event 929 // to solve this problem. 930 if(ImGui::GetIO().WantCaptureMouse && action==EVENT_ACTION_UP) { 931 ImVec2 mouse_pos = ImGui::GetIO().MousePos; 932 app->cursor_pos_callback( 933 double(mouse_pos.x), double(mouse_pos.y) 934 ); 935 app->mouse_button_callback(button,action,mods); 936 } 937 if(app->impl_data()->ImGui_callback_mouse_button != nullptr) { 938 app->impl_data()->ImGui_callback_mouse_button( 939 w, button, action, mods 940 ); 941 } 942 } 943 GLFW_callback_cursor_pos(GLFWwindow * w,double xf,double yf)944 void GLFW_callback_cursor_pos( 945 GLFWwindow* w, double xf, double yf 946 ) { 947 Application* app = static_cast<Application*>( 948 glfwGetWindowUserPointer(w) 949 ); 950 app->update(); 951 if(app->impl_data()->ImGui_callback_cursor_pos != nullptr) { 952 app->impl_data()->ImGui_callback_cursor_pos(w, xf, yf); 953 } 954 if(!ImGui::GetIO().WantCaptureMouse) { 955 app->cursor_pos_callback(xf, yf); 956 } 957 } 958 GLFW_callback_scroll(GLFWwindow * w,double xoffset,double yoffset)959 void GLFW_callback_scroll( 960 GLFWwindow* w, double xoffset, double yoffset 961 ) { 962 Application* app = static_cast<Application*>( 963 glfwGetWindowUserPointer(w) 964 ); 965 app->update(); 966 if(!ImGui::GetIO().WantCaptureMouse) { 967 #if defined(GEO_OS_EMSCRIPTEN) || defined(GEO_OS_APPLE) 968 app->scroll_callback(xoffset,-yoffset); 969 #else 970 app->scroll_callback(xoffset, yoffset); 971 #endif 972 } 973 if(app->impl_data()->ImGui_callback_scroll != nullptr) { 974 app->impl_data()->ImGui_callback_scroll(w, xoffset, yoffset); 975 } 976 } 977 GLFW_callback_drop(GLFWwindow * w,int nb,const char ** p)978 void GLFW_callback_drop( 979 GLFWwindow* w, int nb, const char** p 980 ) { 981 Application* app = static_cast<Application*>( 982 glfwGetWindowUserPointer(w) 983 ); 984 app->update(); 985 if(app->impl_data()->ImGui_callback_drop != nullptr) { 986 app->impl_data()->ImGui_callback_drop(w, nb, p); 987 } 988 app->drop_callback(nb, p); 989 } 990 GLFW_callback_char(GLFWwindow * w,unsigned int c)991 void GLFW_callback_char(GLFWwindow* w, unsigned int c) { 992 Application* app = static_cast<Application*>( 993 glfwGetWindowUserPointer(w) 994 ); 995 app->update(); 996 if(app->impl_data()->ImGui_callback_char != nullptr) { 997 app->impl_data()->ImGui_callback_char(w, c); 998 } 999 if(!ImGui::GetIO().WantCaptureKeyboard) { 1000 app->char_callback(c); 1001 } 1002 } 1003 GLFW_callback_key(GLFWwindow * w,int key,int scancode,int action,int mods)1004 void GLFW_callback_key( 1005 GLFWwindow* w, int key, int scancode, int action, int mods 1006 ) { 1007 Application* app = static_cast<Application*>( 1008 glfwGetWindowUserPointer(w) 1009 ); 1010 app->update(); 1011 if(app->impl_data()->ImGui_callback_key != nullptr) { 1012 app->impl_data()->ImGui_callback_key( 1013 w, key, scancode, action, mods 1014 ); 1015 } 1016 if(!ImGui::GetIO().WantCaptureKeyboard) { 1017 app->key_callback(key,scancode,action,mods); 1018 } 1019 } 1020 GLFW_callback_refresh(GLFWwindow * w)1021 void GLFW_callback_refresh(GLFWwindow* w) { 1022 Application* app = static_cast<Application*>( 1023 glfwGetWindowUserPointer(w) 1024 ); 1025 app->update(); 1026 if(app->impl_data()->ImGui_callback_refresh != nullptr) { 1027 app->impl_data()->ImGui_callback_refresh(w); 1028 } 1029 } 1030 } 1031 callbacks_initialize()1032 void Application::callbacks_initialize() { 1033 if(!data_->GLFW_callbacks_initialized_) { 1034 GEO::Logger::out("ImGui") << "Viewer GUI init (GL3)" 1035 << std::endl; 1036 } 1037 1038 if(!data_->GLFW_callbacks_initialized_) { 1039 // Get previous callbacks so that we can call them if ImGui 1040 // wants to handle user input. 1041 data_->ImGui_callback_mouse_button = glfwSetMouseButtonCallback( 1042 data_->window_,GLFW_callback_mouse_button 1043 ); 1044 data_->ImGui_callback_cursor_pos = glfwSetCursorPosCallback( 1045 data_->window_,GLFW_callback_cursor_pos 1046 ); 1047 data_->ImGui_callback_scroll = glfwSetScrollCallback( 1048 data_->window_,GLFW_callback_scroll 1049 ); 1050 data_->ImGui_callback_char = glfwSetCharCallback( 1051 data_->window_,GLFW_callback_char 1052 ); 1053 data_->ImGui_callback_key = glfwSetKeyCallback( 1054 data_->window_,GLFW_callback_key 1055 ); 1056 data_->ImGui_callback_drop = glfwSetDropCallback( 1057 data_->window_,GLFW_callback_drop 1058 ); 1059 data_->ImGui_callback_refresh = glfwSetWindowRefreshCallback( 1060 data_->window_,GLFW_callback_refresh 1061 ); 1062 1063 #ifdef GEO_OS_EMSCRIPTEN 1064 // It seems that Emscripten's implementation of 1065 // glfwSetXXXCallback() does not always return previous 1066 // callback bindings. 1067 data_->ImGui_callback_mouse_button = 1068 ImGui_ImplGlfw_MouseButtonCallback; 1069 data_->ImGui_callback_char = ImGui_ImplGlfw_CharCallback; 1070 data_->ImGui_callback_key = ImGui_ImplGlfw_KeyCallback; 1071 data_->ImGui_callback_scroll = ImGui_ImplGlfw_ScrollCallback; 1072 #endif 1073 data_->GLFW_callbacks_initialized_ = true; 1074 } 1075 } 1076 set_window_icon(Image * icon_image)1077 void Application::set_window_icon(Image* icon_image) { 1078 GLFWimage glfw_image; 1079 glfw_image.width = int(icon_image->width()); 1080 glfw_image.height = int(icon_image->height()); 1081 glfw_image.pixels = icon_image->base_mem(); 1082 glfwSetWindowIcon(data_->window_, 1, &glfw_image); 1083 } 1084 set_full_screen_mode(index_t w,index_t h,index_t Hz,index_t monitor)1085 void Application::set_full_screen_mode( 1086 index_t w, index_t h, index_t Hz, index_t monitor 1087 ) { 1088 if(data_->window_ == nullptr) { 1089 return; 1090 } 1091 int count; 1092 GLFWmonitor** monitors = glfwGetMonitors(&count); 1093 if(int(monitor) >= count) { 1094 Logger::err("Application") << monitor << ": no such monitor" 1095 << std::endl; 1096 } 1097 if((w == 0) || (h == 0) || (Hz == 0)) { 1098 Logger::out("Application") 1099 << "Using default video mode" << std::endl; 1100 const GLFWvidmode* mode = glfwGetVideoMode(monitors[monitor]); 1101 w = index_t(mode->width); 1102 h = index_t(mode->height); 1103 Hz = index_t(mode->refreshRate); 1104 } 1105 glfwSetWindowMonitor( 1106 data_->window_, monitors[monitor], 0, 0, int(w), int(h), int(Hz) 1107 ); 1108 update(); 1109 } 1110 set_windowed_mode(index_t w,index_t h)1111 void Application::set_windowed_mode(index_t w, index_t h) { 1112 if(w != 0 && h != 0) { 1113 width_ = w; 1114 height_ = w; 1115 } 1116 glfwSetWindowMonitor( 1117 data_->window_, nullptr, 0, 0, int(width_), int(height_), 50 1118 ); 1119 update(); 1120 } 1121 1122 list_video_modes()1123 void Application::list_video_modes() { 1124 int nb_monitors = 0; 1125 GLFWmonitor** monitors = glfwGetMonitors(&nb_monitors); 1126 Logger::out("Application") << "Detected " << nb_monitors 1127 << " monitor(s)" 1128 << std::endl; 1129 for(int m=0; m<nb_monitors; ++m) { 1130 GLFWmonitor* monitor = monitors[m]; 1131 Logger::out("Application") << "Monitor " << m << ":" 1132 << glfwGetMonitorName(monitor) 1133 << std::endl; 1134 int nb_modes = 0; 1135 const GLFWvidmode* modes = glfwGetVideoModes(monitor, &nb_modes); 1136 for(int mm=0; mm<nb_modes; ++mm) { 1137 const GLFWvidmode& mode = modes[mm]; 1138 Logger::out("Application") << " mode " << mm << ":" 1139 << mode.width << "x" << mode.height 1140 << " " << mode.refreshRate << "Hz " 1141 << "R" << mode.redBits 1142 << "G" << mode.greenBits 1143 << "B" << mode.blueBits 1144 << std::endl; 1145 } 1146 } 1147 } 1148 1149 iconify()1150 void Application::iconify() { 1151 if(data_->window_ == nullptr ){ 1152 return ; 1153 } 1154 glfwIconifyWindow(data_->window_); 1155 } 1156 restore()1157 void Application::restore() { 1158 if(data_->window_ == nullptr ){ 1159 return ; 1160 } 1161 1162 // In full screen mode, glfwRestoreWindow() 1163 // does not seem to work, so we switch to 1164 // windowed mode, deiconify, then switch back 1165 // to full screen mode. 1166 if(get_full_screen()) { 1167 set_full_screen(false); 1168 glfwRestoreWindow(data_->window_); 1169 set_full_screen(true); 1170 } else { 1171 glfwRestoreWindow(data_->window_); 1172 } 1173 } 1174 get_full_screen() const1175 bool Application::get_full_screen() const { 1176 return (data_->window_ != nullptr && 1177 glfwGetWindowMonitor(data_->window_) != nullptr); 1178 } 1179 set_full_screen(bool x)1180 void Application::set_full_screen(bool x) { 1181 if(x != get_full_screen()) { 1182 if(x) { 1183 set_full_screen_mode(); 1184 } else { 1185 set_windowed_mode(); 1186 } 1187 } 1188 } 1189 impl_window()1190 void* Application::impl_window() { 1191 return data_->window_; 1192 } 1193 1194 #else 1195 # error "No windowing system" 1196 #endif 1197 1198 1199 #ifdef GEO_GLFW key_to_string(int key)1200 const char* Application::key_to_string(int key) { 1201 if(key == GLFW_KEY_LEFT) { 1202 return "left"; 1203 } 1204 if(key == GLFW_KEY_RIGHT) { 1205 return "right"; 1206 } 1207 if(key == GLFW_KEY_UP) { 1208 return "up"; 1209 } 1210 if(key == GLFW_KEY_DOWN) { 1211 return "down"; 1212 } 1213 if(key == GLFW_KEY_F1) { 1214 return "F1"; 1215 } 1216 if(key == GLFW_KEY_F2) { 1217 return "F2"; 1218 } 1219 if(key == GLFW_KEY_F3) { 1220 return "F3"; 1221 } 1222 if(key == GLFW_KEY_F4) { 1223 return "F4"; 1224 } 1225 if(key == GLFW_KEY_F5) { 1226 return "F5"; 1227 } 1228 if(key == GLFW_KEY_F6) { 1229 return "F6"; 1230 } 1231 if(key == GLFW_KEY_F7) { 1232 return "F7"; 1233 } 1234 if(key == GLFW_KEY_F8) { 1235 return "F8"; 1236 } 1237 if(key == GLFW_KEY_F9) { 1238 return "F9"; 1239 } 1240 if(key == GLFW_KEY_F10) { 1241 return "F10"; 1242 } 1243 if(key == GLFW_KEY_F11) { 1244 return "F11"; 1245 } 1246 if(key == GLFW_KEY_F12) { 1247 return "F12"; 1248 } 1249 if(key == GLFW_KEY_LEFT_CONTROL) { 1250 return "left_control"; 1251 } 1252 if(key == GLFW_KEY_RIGHT_CONTROL) { 1253 return "right_control"; 1254 } 1255 if(key == GLFW_KEY_LEFT_ALT) { 1256 return "left_alt"; 1257 } 1258 if(key == GLFW_KEY_RIGHT_ALT) { 1259 return "right_alt"; 1260 } 1261 if(key == GLFW_KEY_LEFT_SHIFT) { 1262 return "left_shift"; 1263 } 1264 if(key == GLFW_KEY_RIGHT_SHIFT) { 1265 return "right_shift"; 1266 } 1267 if(key == GLFW_KEY_ESCAPE) { 1268 return "escape"; 1269 } 1270 if(key == GLFW_KEY_TAB) { 1271 return "tab"; 1272 } 1273 if(key == GLFW_KEY_BACKSPACE) { 1274 return "backspace"; 1275 } 1276 return ""; 1277 } 1278 #else key_to_string(int key)1279 const char* Application::key_to_string(int key) { 1280 return ""; 1281 } 1282 #endif 1283 1284 } 1285 1286 /************************ Utilities *************************************/ 1287 1288 namespace GEO { 1289 StyleColorsCorporateGrey(bool threeD)1290 void StyleColorsCorporateGrey(bool threeD) { 1291 ImGuiStyle & style = ImGui::GetStyle(); 1292 ImVec4 * colors = style.Colors; 1293 1294 /// 0 = FLAT APPEARENCE 1295 /// 1 = MORE "3D" LOOK 1296 float is3D = threeD ? 1.0f : 0.0f; 1297 1298 colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); 1299 colors[ImGuiCol_TextDisabled] = ImVec4(0.40f, 0.40f, 0.40f, 1.00f); 1300 colors[ImGuiCol_ChildBg] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f); //BL orig=0.25 1301 colors[ImGuiCol_WindowBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); //BL orig=0.25 1302 colors[ImGuiCol_PopupBg] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f); 1303 colors[ImGuiCol_Border] = ImVec4(0.12f, 0.12f, 0.12f, 0.71f); 1304 colors[ImGuiCol_BorderShadow] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); 1305 colors[ImGuiCol_FrameBg] = ImVec4(0.42f, 0.42f, 0.42f, 0.54f); 1306 colors[ImGuiCol_FrameBgHovered] = ImVec4(0.42f, 0.42f, 0.42f, 0.40f); 1307 colors[ImGuiCol_FrameBgActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.67f); 1308 colors[ImGuiCol_TitleBg] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f); 1309 colors[ImGuiCol_TitleBgActive] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f); 1310 colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.17f, 0.17f, 0.17f, 0.90f); 1311 colors[ImGuiCol_MenuBarBg] = ImVec4(0.335f, 0.335f, 0.335f, 1.000f); 1312 colors[ImGuiCol_ScrollbarBg] = ImVec4(0.24f, 0.24f, 0.24f, 0.53f); 1313 colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); 1314 colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); 1315 colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.76f, 0.76f, 0.76f, 1.00f); 1316 colors[ImGuiCol_CheckMark] = ImVec4(0.65f, 0.65f, 0.65f, 1.00f); 1317 colors[ImGuiCol_SliderGrab] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); 1318 colors[ImGuiCol_SliderGrabActive] = ImVec4(0.64f, 0.64f, 0.64f, 1.00f); 1319 colors[ImGuiCol_Button] = ImVec4(0.54f, 0.54f, 0.54f, 0.35f); 1320 colors[ImGuiCol_ButtonHovered] = ImVec4(0.52f, 0.52f, 0.52f, 0.59f); 1321 colors[ImGuiCol_ButtonActive] = ImVec4(0.76f, 0.76f, 0.76f, 1.00f); 1322 colors[ImGuiCol_Header] = ImVec4(0.38f, 0.38f, 0.38f, 1.00f); 1323 colors[ImGuiCol_HeaderHovered] = ImVec4(0.47f, 0.47f, 0.47f, 1.00f); 1324 colors[ImGuiCol_HeaderActive] = ImVec4(0.76f, 0.76f, 0.76f, 0.77f); 1325 colors[ImGuiCol_Separator] = ImVec4(0.000f, 0.000f, 0.000f, 0.137f); 1326 colors[ImGuiCol_SeparatorHovered] = ImVec4(0.700f, 0.671f, 0.600f, 0.290f); 1327 colors[ImGuiCol_SeparatorActive] = ImVec4(0.702f, 0.671f, 0.600f, 0.674f); 1328 colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f); 1329 colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); 1330 colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); 1331 colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); 1332 colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); 1333 colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); 1334 colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); 1335 colors[ImGuiCol_TextSelectedBg] = ImVec4(0.73f, 0.73f, 0.73f, 0.35f); 1336 colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); 1337 colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); 1338 colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); 1339 colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); 1340 colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); 1341 1342 style.PopupRounding = 3; 1343 1344 style.WindowPadding = ImVec2(4, 4); 1345 style.FramePadding = ImVec2(6, 4); 1346 style.ItemSpacing = ImVec2(6, 2); 1347 1348 style.ScrollbarSize = 18; 1349 1350 style.WindowBorderSize = 1; 1351 style.ChildBorderSize = 1; 1352 style.PopupBorderSize = 1; 1353 style.FrameBorderSize = is3D; 1354 1355 style.WindowRounding = 3; 1356 style.ChildRounding = 3; 1357 style.FrameRounding = 3; 1358 style.ScrollbarRounding = 2; 1359 style.GrabRounding = 3; 1360 1361 #ifdef IMGUI_HAS_DOCK 1362 style.TabBorderSize = is3D; 1363 style.TabRounding = 3; 1364 1365 colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.38f, 0.38f, 0.38f, 1.00f); 1366 colors[ImGuiCol_Tab] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f); 1367 colors[ImGuiCol_TabHovered] = ImVec4(0.40f, 0.40f, 0.40f, 1.00f); 1368 colors[ImGuiCol_TabActive] = ImVec4(0.33f, 0.33f, 0.33f, 1.00f); 1369 colors[ImGuiCol_TabUnfocused] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f); 1370 colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.33f, 0.33f, 0.33f, 1.00f); 1371 colors[ImGuiCol_DockingPreview] = ImVec4(0.85f, 0.85f, 0.85f, 0.28f); 1372 1373 if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) 1374 { 1375 style.WindowRounding = 0.0f; 1376 style.Colors[ImGuiCol_WindowBg].w = 1.0f; 1377 } 1378 #endif 1379 } 1380 1381 1382 #if defined(GEO_GLFW) && !defined(GEO_OS_EMSCRIPTEN) 1383 compute_pixel_ratio()1384 double compute_pixel_ratio() { 1385 int buf_size[2]; 1386 int win_size[2]; 1387 GLFWwindow* window = glfwGetCurrentContext(); 1388 glfwGetFramebufferSize(window, &buf_size[0], &buf_size[1]); 1389 glfwGetWindowSize(window, &win_size[0], &win_size[1]); 1390 // The window may be iconified. 1391 if(win_size[0] == 0) { 1392 return 1.0; 1393 } 1394 return double(buf_size[0]) / double(win_size[0]); 1395 } 1396 compute_hidpi_scaling()1397 double compute_hidpi_scaling() { 1398 float xscale, yscale; 1399 GLFWwindow* window = glfwGetCurrentContext(); 1400 glfwGetWindowContentScale(window, &xscale, &yscale); 1401 return 0.5 * double(xscale + yscale); 1402 } 1403 1404 #else 1405 compute_pixel_ratio()1406 double compute_pixel_ratio() { 1407 return 1.0; 1408 } 1409 compute_hidpi_scaling()1410 double compute_hidpi_scaling() { 1411 return 1.0; 1412 } 1413 1414 #endif 1415 1416 } 1417 1418