From 3240596c3ab17f7bcb47c3caa0d273a8623ccc48 Mon Sep 17 00:00:00 2001 From: Saran Tunyasuvunakool Date: Thu, 25 Aug 2022 15:41:14 -0700 Subject: [PATCH] Call GLFW functions via a dynamic dispatch table. This allows us to use GLFW without needing to introduce a link-time dependency on the library, which in turn enables us to get GLFW through Python. PiperOrigin-RevId: 470100999 Change-Id: I27c84ad3310afdbebd582ab0033b6a963ba6307e --- simulate/CMakeLists.txt | 28 ++++--- simulate/glfw_dispatch.cc | 117 +++++++++++++++++++++++++++++ simulate/glfw_dispatch.h | 66 ++++++++++++++++ simulate/main.cc | 13 ++-- simulate/simulate.cc | 63 +++++++++------- simulate/simulate.h | 1 - simulate/{uitools.c => uitools.cc} | 100 ++++++++++++------------ 7 files changed, 294 insertions(+), 94 deletions(-) create mode 100644 simulate/glfw_dispatch.cc create mode 100644 simulate/glfw_dispatch.h rename simulate/{uitools.c => uitools.cc} (68%) diff --git a/simulate/CMakeLists.txt b/simulate/CMakeLists.txt index f9ddc9b5..e0dec11f 100644 --- a/simulate/CMakeLists.txt +++ b/simulate/CMakeLists.txt @@ -97,25 +97,29 @@ if(NOT TARGET lodepng) endif() endif() -# mjsimulate library -add_library(mjsimulate STATIC) +# Simulate library +add_library(libsimulate STATIC) +add_library(mujoco::libsimulate ALIAS libsimulate) + target_sources( - mjsimulate + libsimulate PUBLIC simulate.h array_safety.h + glfw_dispatch.h + glfw_dispatch.cc simulate.cc uitools.h - uitools.c + uitools.cc ) -target_include_directories(mjsimulate PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_compile_definitions(mjsimulate PUBLIC MJSIMULATE_STATIC) -target_compile_options(mjsimulate PUBLIC ${MUJOCO_SIMULATE_COMPILE_OPTIONS}) -target_link_libraries(mjsimulate PUBLIC glfw lodepng mujoco::mujoco) -target_link_options(mjsimulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS}) +target_include_directories(libsimulate PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_definitions(libsimulate PUBLIC MJSIMULATE_STATIC) +target_compile_options(libsimulate PUBLIC ${MUJOCO_SIMULATE_COMPILE_OPTIONS}) +target_link_libraries(libsimulate PUBLIC glfw lodepng mujoco::mujoco) +target_link_options(libsimulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS}) if(APPLE) - target_sources(mjsimulate PRIVATE macos_save.mm) - target_link_libraries(mjsimulate PUBLIC "-framework Cocoa") + target_sources(libsimulate PRIVATE macos_save.mm) + target_link_libraries(libsimulate PUBLIC "-framework Cocoa") endif() # Build simulate executable @@ -142,7 +146,7 @@ endif() target_link_libraries( simulate - mjsimulate + libsimulate mujoco::mujoco glfw Threads::Threads diff --git a/simulate/glfw_dispatch.cc b/simulate/glfw_dispatch.cc new file mode 100644 index 00000000..21161163 --- /dev/null +++ b/simulate/glfw_dispatch.cc @@ -0,0 +1,117 @@ +// Copyright 2022 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "glfw_dispatch.h" + +#ifdef mjGLFW_DYNAMIC_SYMBOLS + #ifdef _MSC_VER + #include + #include + #else + #include + #endif +#endif + +#include +#include + +namespace mujoco { +const struct Glfw& Glfw(void* dlhandle) { + { + static const void* init_dlhandle = dlhandle; + if (dlhandle && dlhandle != init_dlhandle) { + std::cerr << "dlhandle is specified when GLFW dispatch table is already " + "initialized\n"; + abort(); + } + } + + static const struct Glfw glfw = [&]() { + struct Glfw glfw; + +#ifdef mjGLFW_DYNAMIC_SYMBOLS + #ifdef _MSC_VER + if (!dlhandle) dlhandle = LoadLibraryA("glfw3.dll"); + if (!dlhandle) { + std::cerr << "cannot obtain a shared object handle\n"; + abort(); + } + #define mjGLFW_RESOLVE_SYMBOL(func) \ + glfw.func = reinterpret_cast( \ + GetProcAddress(reinterpret_cast(dlhandle), #func)) + #else + if (!dlhandle) dlhandle = dlopen("nullptr", RTLD_GLOBAL | RTLD_NOW); + if (!dlhandle) { + std::cerr << "cannot obtain a shared object handle\n"; + abort(); + } + #define mjGLFW_RESOLVE_SYMBOL(func) \ + glfw.func = reinterpret_cast(dlsym(dlhandle, #func)) + #endif +#else + #define mjGLFW_RESOLVE_SYMBOL(func) glfw.func = &func +#endif + +#define mjGLFW_INITIALIZE_SYMBOL(func) \ + if (!(mjGLFW_RESOLVE_SYMBOL(func))) { \ + std::cerr << "cannot dlsym " #func "\n"; \ + abort(); \ + } + + // go/keep-sorted start + mjGLFW_INITIALIZE_SYMBOL(glfwCreateWindow); + mjGLFW_INITIALIZE_SYMBOL(glfwGetCursorPos); + mjGLFW_INITIALIZE_SYMBOL(glfwGetFramebufferSize); + mjGLFW_INITIALIZE_SYMBOL(glfwGetKey); + mjGLFW_INITIALIZE_SYMBOL(glfwGetMonitorPhysicalSize); + mjGLFW_INITIALIZE_SYMBOL(glfwGetMouseButton); + mjGLFW_INITIALIZE_SYMBOL(glfwGetPrimaryMonitor); + mjGLFW_INITIALIZE_SYMBOL(glfwGetTime); + mjGLFW_INITIALIZE_SYMBOL(glfwGetVideoMode); + mjGLFW_INITIALIZE_SYMBOL(glfwGetWindowMonitor); + mjGLFW_INITIALIZE_SYMBOL(glfwGetWindowPos); + mjGLFW_INITIALIZE_SYMBOL(glfwGetWindowSize); + mjGLFW_INITIALIZE_SYMBOL(glfwGetWindowUserPointer); + mjGLFW_INITIALIZE_SYMBOL(glfwInit); + mjGLFW_INITIALIZE_SYMBOL(glfwMakeContextCurrent); + mjGLFW_INITIALIZE_SYMBOL(glfwPollEvents); + mjGLFW_INITIALIZE_SYMBOL(glfwSetClipboardString); + mjGLFW_INITIALIZE_SYMBOL(glfwSetCursorPosCallback); + mjGLFW_INITIALIZE_SYMBOL(glfwSetDropCallback); + mjGLFW_INITIALIZE_SYMBOL(glfwSetKeyCallback); + mjGLFW_INITIALIZE_SYMBOL(glfwSetMouseButtonCallback); + mjGLFW_INITIALIZE_SYMBOL(glfwSetScrollCallback); + mjGLFW_INITIALIZE_SYMBOL(glfwSetWindowMonitor); + mjGLFW_INITIALIZE_SYMBOL(glfwSetWindowRefreshCallback); + mjGLFW_INITIALIZE_SYMBOL(glfwSetWindowSizeCallback); + mjGLFW_INITIALIZE_SYMBOL(glfwSetWindowTitle); + mjGLFW_INITIALIZE_SYMBOL(glfwSetWindowUserPointer); + mjGLFW_INITIALIZE_SYMBOL(glfwSwapBuffers); + mjGLFW_INITIALIZE_SYMBOL(glfwSwapInterval); + mjGLFW_INITIALIZE_SYMBOL(glfwTerminate); + mjGLFW_INITIALIZE_SYMBOL(glfwWindowHint); + mjGLFW_INITIALIZE_SYMBOL(glfwWindowShouldClose); + // go/keep-sorted end + +#undef mjGLFW_INITIALIZE_SYMBOL + +#if defined(mjGLFW_DYNAMIC_SYMBOLS) && !defined(_MSC_VER) + dlclose(dlhandle); +#endif + + return glfw; + }(); + return glfw; +} +} // namespace mujoco diff --git a/simulate/glfw_dispatch.h b/simulate/glfw_dispatch.h new file mode 100644 index 00000000..8052b7df --- /dev/null +++ b/simulate/glfw_dispatch.h @@ -0,0 +1,66 @@ +// Copyright 2022 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MUJOCO_SIMULATE_GLFW_DISPATCH_H_ +#define MUJOCO_SIMULATE_GLFW_DISPATCH_H_ + +#include + +namespace mujoco { +// Dynamic dispatch table for GLFW functions required by Simulate. +// This allows us to use GLFW without introducing a link-time dependency on the +// library, which is useful e.g. when using GLFW via Python. +struct Glfw { +#define mjGLFW_DECLARE_SYMBOL(func) decltype(&func) func + // go/keep-sorted start + mjGLFW_DECLARE_SYMBOL(glfwCreateWindow); + mjGLFW_DECLARE_SYMBOL(glfwGetCursorPos); + mjGLFW_DECLARE_SYMBOL(glfwGetFramebufferSize); + mjGLFW_DECLARE_SYMBOL(glfwGetKey); + mjGLFW_DECLARE_SYMBOL(glfwGetMonitorPhysicalSize); + mjGLFW_DECLARE_SYMBOL(glfwGetMouseButton); + mjGLFW_DECLARE_SYMBOL(glfwGetPrimaryMonitor); + mjGLFW_DECLARE_SYMBOL(glfwGetTime); + mjGLFW_DECLARE_SYMBOL(glfwGetVideoMode); + mjGLFW_DECLARE_SYMBOL(glfwGetWindowMonitor); + mjGLFW_DECLARE_SYMBOL(glfwGetWindowPos); + mjGLFW_DECLARE_SYMBOL(glfwGetWindowSize); + mjGLFW_DECLARE_SYMBOL(glfwGetWindowUserPointer); + mjGLFW_DECLARE_SYMBOL(glfwInit); + mjGLFW_DECLARE_SYMBOL(glfwMakeContextCurrent); + mjGLFW_DECLARE_SYMBOL(glfwPollEvents); + mjGLFW_DECLARE_SYMBOL(glfwSetClipboardString); + mjGLFW_DECLARE_SYMBOL(glfwSetCursorPosCallback); + mjGLFW_DECLARE_SYMBOL(glfwSetDropCallback); + mjGLFW_DECLARE_SYMBOL(glfwSetKeyCallback); + mjGLFW_DECLARE_SYMBOL(glfwSetMouseButtonCallback); + mjGLFW_DECLARE_SYMBOL(glfwSetScrollCallback); + mjGLFW_DECLARE_SYMBOL(glfwSetWindowMonitor); + mjGLFW_DECLARE_SYMBOL(glfwSetWindowRefreshCallback); + mjGLFW_DECLARE_SYMBOL(glfwSetWindowSizeCallback); + mjGLFW_DECLARE_SYMBOL(glfwSetWindowTitle); + mjGLFW_DECLARE_SYMBOL(glfwSetWindowUserPointer); + mjGLFW_DECLARE_SYMBOL(glfwSwapBuffers); + mjGLFW_DECLARE_SYMBOL(glfwSwapInterval); + mjGLFW_DECLARE_SYMBOL(glfwTerminate); + mjGLFW_DECLARE_SYMBOL(glfwWindowHint); + mjGLFW_DECLARE_SYMBOL(glfwWindowShouldClose); + // go/keep-sorted end +#undef mjGLFW_DECLARE_SYMBOL +}; + +const struct Glfw& Glfw(void* dlhandle = nullptr); +} // namespace mujoco + +#endif // MUJOCO_SIMULATE_GLFW_DISPATCH_H_ diff --git a/simulate/main.cc b/simulate/main.cc index b48ff5b8..44dedcbb 100644 --- a/simulate/main.cc +++ b/simulate/main.cc @@ -23,6 +23,7 @@ #include #include +#include "glfw_dispatch.h" #include "simulate.h" #include "array_safety.h" @@ -30,6 +31,8 @@ namespace { namespace mj = ::mujoco; namespace mju = ::mujoco::sample_util; +using ::mujoco::Glfw; + // constants const double syncmisalign = 0.1; // maximum time mis-alignment before re-sync const double refreshfactor = 0.7; // fraction of refresh available for simulation @@ -158,7 +161,7 @@ void PhysicsLoop(mj::Simulate& sim) { // running if (sim.run) { // record cpu time at start of iteration - double tmstart = glfwGetTime(); + double tmstart = Glfw().glfwGetTime(); // inject noise if (sim.ctrlnoisestd) { @@ -195,8 +198,8 @@ void PhysicsLoop(mj::Simulate& sim) { // in-sync else { // step while simtime lags behind cputime, and within safefactor - while ((d->time*sim.slow_down-simsync) < (glfwGetTime()-cpusync) && - (glfwGetTime()-tmstart) < refreshfactor/sim.vmode.refreshRate) { + while ((d->time*sim.slow_down-simsync) < (Glfw().glfwGetTime()-cpusync) && + (Glfw().glfwGetTime()-tmstart) < refreshfactor/sim.vmode.refreshRate) { // clear old perturbations, apply new mju_zero(d->xfrc_applied, 6*m->nbody); sim.applyposepertubations(0); // move mocap bodies only @@ -268,7 +271,7 @@ int main(int argc, const char** argv) { auto sim = std::make_unique(); // init GLFW - if (!glfwInit()) { + if (!Glfw().glfwInit()) { mju_error("could not initialize GLFW"); } @@ -286,7 +289,7 @@ int main(int argc, const char** argv) { // terminate GLFW (crashes with Linux NVidia drivers) #if defined(__APPLE__) || defined(_WIN32) - glfwTerminate(); + Glfw().glfwTerminate(); #endif return 0; diff --git a/simulate/simulate.cc b/simulate/simulate.cc index fd0209e9..9db1da17 100644 --- a/simulate/simulate.cc +++ b/simulate/simulate.cc @@ -23,9 +23,12 @@ #include #include +#include +#include "lodepng.h" #include #include #include +#include "glfw_dispatch.h" #include "uitools.h" #include "array_safety.h" @@ -46,6 +49,8 @@ namespace { namespace mj = ::mujoco; namespace mju = ::mujoco::sample_util; +using ::mujoco::Glfw; + //------------------------------------------- global ----------------------------------------------- const int maxgeom = 5000; // preallocated geom array in mjvScene @@ -925,12 +930,12 @@ void copykey(mj::Simulate* sim) { mju::strcat_arr(clipboard, "'/>"); // copy to clipboard - glfwSetClipboardString(sim->window, clipboard); + Glfw().glfwSetClipboardString(sim->window, clipboard); } // millisecond timer, for MuJoCo built-in profiler mjtNum timer() { - return 1000*glfwGetTime(); + return 1000*Glfw().glfwGetTime(); } // clear all times @@ -965,7 +970,7 @@ void copycamera(mj::Simulate* sim) { camera[0].up[0], camera[0].up[1], camera[0].up[2]); // copy spec into clipboard - glfwSetClipboardString(sim->window, clipboard); + Glfw().glfwSetClipboardString(sim->window, clipboard); } // update UI 0 when MuJoCo structures change (except for joint sliders) @@ -1026,7 +1031,7 @@ void uiLayout(mjuiState* state) { // rect 0: entire framebuffer rect[0].left = 0; rect[0].bottom = 0; - glfwGetFramebufferSize(sim->window, &rect[0].width, &rect[0].height); + Glfw().glfwGetFramebufferSize(sim->window, &rect[0].width, &rect[0].height); // rect 1: UI 0 rect[1].left = 0; @@ -1119,29 +1124,29 @@ void uiEvent(mjuiState* state) { break; case 9: // Full screen - if (glfwGetWindowMonitor(sim->window)) { + if (Glfw().glfwGetWindowMonitor(sim->window)) { // restore window from saved data - glfwSetWindowMonitor(sim->window, nullptr, sim->windowpos[0], sim->windowpos[1], - sim->windowsize[0], sim->windowsize[1], 0); + Glfw().glfwSetWindowMonitor(sim->window, nullptr, sim->windowpos[0], sim->windowpos[1], + sim->windowsize[0], sim->windowsize[1], 0); } // currently windowed: switch to full screen else { // save window data - glfwGetWindowPos(sim->window, sim->windowpos, sim->windowpos+1); - glfwGetWindowSize(sim->window, sim->windowsize, sim->windowsize+1); + Glfw().glfwGetWindowPos(sim->window, sim->windowpos, sim->windowpos+1); + Glfw().glfwGetWindowSize(sim->window, sim->windowsize, sim->windowsize+1); // switch - glfwSetWindowMonitor(sim->window, glfwGetPrimaryMonitor(), 0, 0, - sim->vmode.width, sim->vmode.height, sim->vmode.refreshRate); + Glfw().glfwSetWindowMonitor(sim->window, Glfw().glfwGetPrimaryMonitor(), 0, 0, + sim->vmode.width, sim->vmode.height, sim->vmode.refreshRate); } // reinstante vsync, just in case - glfwSwapInterval(sim->vsync); + Glfw().glfwSwapInterval(sim->vsync); break; case 10: // Vertical sync - glfwSwapInterval(sim->vsync); + Glfw().glfwSwapInterval(sim->vsync); break; } @@ -1617,7 +1622,7 @@ void Simulate::loadmodel() { if (this->window && this->m->names) { char title[200] = "Simulate : "; mju::strcat_arr(title, this->m->names); - glfwSetWindowTitle(this->window, title); + Glfw().glfwSetWindowTitle(this->window, title); } // set keyframe range and divisions @@ -1648,7 +1653,7 @@ void Simulate::prepare() { static double lastupdatetm = 0; // update interval, save update time - double tmnow = glfwGetTime(); + double tmnow = Glfw().glfwGetTime(); double interval = tmnow - lastupdatetm; interval = mjMIN(1, mjMAX(0.0001, interval)); lastupdatetm = tmnow; @@ -1734,7 +1739,7 @@ void Simulate::render() { } // finalize - glfwSwapBuffers(this->window); + Glfw().glfwSwapBuffers(this->window); return; } @@ -1822,7 +1827,7 @@ void Simulate::render() { } // finalize - glfwSwapBuffers(this->window); + Glfw().glfwSwapBuffers(this->window); } @@ -1836,27 +1841,27 @@ void Simulate::renderloop() { mjcb_time = timer; // multisampling - glfwWindowHint(GLFW_SAMPLES, 4); - glfwWindowHint(GLFW_VISIBLE, 1); + Glfw().glfwWindowHint(GLFW_SAMPLES, 4); + Glfw().glfwWindowHint(GLFW_VISIBLE, 1); // get videomode and save - this->vmode = *glfwGetVideoMode(glfwGetPrimaryMonitor()); + this->vmode = *Glfw().glfwGetVideoMode(Glfw().glfwGetPrimaryMonitor()); // create window - this->window = glfwCreateWindow((2*this->vmode.width)/3, (2*this->vmode.height)/3, - "Simulate", nullptr, nullptr); + this->window = Glfw().glfwCreateWindow((2*this->vmode.width)/3, (2*this->vmode.height)/3, + "Simulate", nullptr, nullptr); if (!this->window) { - glfwTerminate(); + Glfw().glfwTerminate(); mju_error("could not create window"); } // save window position and size - glfwGetWindowPos(this->window, this->windowpos, this->windowpos+1); - glfwGetWindowSize(this->window, this->windowsize, this->windowsize+1); + Glfw().glfwGetWindowPos(this->window, this->windowpos, this->windowpos+1); + Glfw().glfwGetWindowSize(this->window, this->windowsize, this->windowsize+1); // make context current, set v-sync - glfwMakeContextCurrent(this->window); - glfwSwapInterval(this->vsync); + Glfw().glfwMakeContextCurrent(this->window); + Glfw().glfwSwapInterval(this->vsync); // init abstract visualization mjv_defaultCamera(&this->cam); @@ -1906,7 +1911,7 @@ void Simulate::renderloop() { uiModify(this->window, &this->ui1, &this->uistate, &this->con); // run event loop - while (!glfwWindowShouldClose(this->window) && !this->exitrequest.load()) { + while (!Glfw().glfwWindowShouldClose(this->window) && !this->exitrequest.load()) { { const std::lock_guard lock(this->mtx); @@ -1918,7 +1923,7 @@ void Simulate::renderloop() { } // handle events (calls all callbacks) - glfwPollEvents(); + Glfw().glfwPollEvents(); // prepare to render this->prepare(); diff --git a/simulate/simulate.h b/simulate/simulate.h index eb540d2d..ec8fca34 100644 --- a/simulate/simulate.h +++ b/simulate/simulate.h @@ -20,7 +20,6 @@ #include #include -#include "lodepng.h" #include #include diff --git a/simulate/uitools.c b/simulate/uitools.cc similarity index 68% rename from simulate/uitools.c rename to simulate/uitools.cc index 6548bd0b..ef7bfa99 100644 --- a/simulate/uitools.c +++ b/simulate/uitools.cc @@ -17,27 +17,33 @@ #include #include +#include +#include "glfw_dispatch.h" + +namespace { +using ::mujoco::Glfw; +} //------------------------------------ internal GLFW callbacks ------------------------------------- // update state static void uiUpdateState(GLFWwindow* wnd) { // extract data from user pointer - uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); + uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd); mjuiState* state = ptr->state; // mouse buttons - state->left = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS); - state->right = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_RIGHT)==GLFW_PRESS); - state->middle = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_MIDDLE)==GLFW_PRESS); + state->left = (Glfw().glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS); + state->right = (Glfw().glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_RIGHT)==GLFW_PRESS); + state->middle = (Glfw().glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_MIDDLE)==GLFW_PRESS); // keyboard modifiers - state->control = (glfwGetKey(wnd, GLFW_KEY_LEFT_CONTROL)==GLFW_PRESS || - glfwGetKey(wnd, GLFW_KEY_RIGHT_CONTROL)==GLFW_PRESS); - state->shift = (glfwGetKey(wnd, GLFW_KEY_LEFT_SHIFT)==GLFW_PRESS || - glfwGetKey(wnd, GLFW_KEY_RIGHT_SHIFT)==GLFW_PRESS); - state->alt = (glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS || - glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS); + state->control = (Glfw().glfwGetKey(wnd, GLFW_KEY_LEFT_CONTROL)==GLFW_PRESS || + Glfw().glfwGetKey(wnd, GLFW_KEY_RIGHT_CONTROL)==GLFW_PRESS); + state->shift = (Glfw().glfwGetKey(wnd, GLFW_KEY_LEFT_SHIFT)==GLFW_PRESS || + Glfw().glfwGetKey(wnd, GLFW_KEY_RIGHT_SHIFT)==GLFW_PRESS); + state->alt = (Glfw().glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS || + Glfw().glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS); // swap left and right if Alt if (state->alt) { @@ -48,7 +54,7 @@ static void uiUpdateState(GLFWwindow* wnd) { // get mouse position, scale by buffer-to-window ratio double x, y; - glfwGetCursorPos(wnd, &x, &y); + Glfw().glfwGetCursorPos(wnd, &x, &y); x *= ptr->buffer2window; y *= ptr->buffer2window; @@ -75,7 +81,7 @@ static void uiKeyboard(GLFWwindow* wnd, int key, int scancode, int act, int mods } // extract data from user pointer - uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); + uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd); mjuiState* state = ptr->state; // update state @@ -84,7 +90,7 @@ static void uiKeyboard(GLFWwindow* wnd, int key, int scancode, int act, int mods // set key info state->type = mjEVENT_KEY; state->key = key; - state->keytime = glfwGetTime(); + state->keytime = Glfw().glfwGetTime(); // application-specific processing ptr->uiEvent(state); @@ -95,7 +101,7 @@ static void uiKeyboard(GLFWwindow* wnd, int key, int scancode, int act, int mods // mouse button static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) { // extract data from user pointer - uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); + uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd); mjuiState* state = ptr->state; // update state @@ -111,8 +117,8 @@ static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) { } // swap left and right if Alt - if (glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS || - glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS) { + if (Glfw().glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS || + Glfw().glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS) { if (button==mjBUTTON_LEFT) { button = mjBUTTON_RIGHT; } else if (button==mjBUTTON_RIGHT) { @@ -123,7 +129,7 @@ static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) { // press if (act==GLFW_PRESS) { // detect doubleclick: 250 ms - if (button==state->button && glfwGetTime()-state->buttontime<0.25) { + if (button==state->button && Glfw().glfwGetTime()-state->buttontime<0.25) { state->doubleclick = 1; } else { state->doubleclick = 0; @@ -132,7 +138,7 @@ static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) { // set info state->type = mjEVENT_PRESS; state->button = button; - state->buttontime = glfwGetTime(); + state->buttontime = Glfw().glfwGetTime(); // start dragging if (state->mouserect) { @@ -161,7 +167,7 @@ static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) { // mouse move static void uiMouseMove(GLFWwindow* wnd, double xpos, double ypos) { // extract data from user pointer - uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); + uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd); mjuiState* state = ptr->state; // no buttons down: nothing to do @@ -184,7 +190,7 @@ static void uiMouseMove(GLFWwindow* wnd, double xpos, double ypos) { // scroll static void uiScroll(GLFWwindow* wnd, double xoffset, double yoffset) { // extract data from user pointer - uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); + uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd); mjuiState* state = ptr->state; // update state @@ -204,7 +210,7 @@ static void uiScroll(GLFWwindow* wnd, double xoffset, double yoffset) { // resize static void uiResize(GLFWwindow* wnd, int width, int height) { // extract data from user pointer - uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); + uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd); mjuiState* state = ptr->state; // set layout @@ -228,13 +234,13 @@ static void uiResize(GLFWwindow* wnd, int width, int height) { static void uiRender(GLFWwindow* wnd) { - uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); + uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd); mjuiState* state = ptr->state; ptr->uiRender(state); } static void uiDrop(GLFWwindow* wnd, int count, const char** paths) { - uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); + uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd); mjuiState* state = ptr->state; ptr->uiDrop(state, count, paths); } @@ -245,14 +251,14 @@ static void uiDrop(GLFWwindow* wnd, int count, const char** paths) { int uiFontScale(GLFWwindow* wnd) { // compute framebuffer-to-window ratio int width_win, width_buf, height; - glfwGetWindowSize(wnd, &width_win, &height); - glfwGetFramebufferSize(wnd, &width_buf, &height); + Glfw().glfwGetWindowSize(wnd, &width_win, &height); + Glfw().glfwGetFramebufferSize(wnd, &width_buf, &height); double b2w = (double)width_buf / (double)width_win; // compute PPI int width_MM, height_MM; - glfwGetMonitorPhysicalSize(glfwGetPrimaryMonitor(), &width_MM, &height_MM); - int width_vmode = glfwGetVideoMode(glfwGetPrimaryMonitor())->width; + Glfw().glfwGetMonitorPhysicalSize(Glfw().glfwGetPrimaryMonitor(), &width_MM, &height_MM); + int width_vmode = Glfw().glfwGetVideoMode(Glfw().glfwGetPrimaryMonitor())->width; double PPI = 25.4 * b2w * (double)width_vmode / (double)width_MM; // estimate font scaling, guard against unrealistic PPI @@ -283,22 +289,22 @@ void uiSetCallback(GLFWwindow* wnd, mjuiState* state, ptr->uiLayout = uiLayout; ptr->uiRender = uiUserRender; ptr->uiDrop = uiUserDrop; - glfwSetWindowUserPointer(wnd, ptr); + Glfw().glfwSetWindowUserPointer(wnd, ptr); // compute framebuffer-to-window pixel ratio int width_win, width_buf, height; - glfwGetWindowSize(wnd, &width_win, &height); - glfwGetFramebufferSize(wnd, &width_buf, &height); + Glfw().glfwGetWindowSize(wnd, &width_win, &height); + Glfw().glfwGetFramebufferSize(wnd, &width_buf, &height); ptr->buffer2window = (double)width_buf / (double)width_win; // set internal callbacks - glfwSetKeyCallback(wnd, uiKeyboard); - glfwSetCursorPosCallback(wnd, uiMouseMove); - glfwSetMouseButtonCallback(wnd, uiMouseButton); - glfwSetScrollCallback(wnd, uiScroll); - glfwSetWindowSizeCallback(wnd, uiResize); - glfwSetWindowRefreshCallback(wnd, uiRender); - glfwSetDropCallback(wnd, uiDrop); + Glfw().glfwSetKeyCallback(wnd, uiKeyboard); + Glfw().glfwSetCursorPosCallback(wnd, uiMouseMove); + Glfw().glfwSetMouseButtonCallback(wnd, uiMouseButton); + Glfw().glfwSetScrollCallback(wnd, uiScroll); + Glfw().glfwSetWindowSizeCallback(wnd, uiResize); + Glfw().glfwSetWindowRefreshCallback(wnd, uiRender); + Glfw().glfwSetDropCallback(wnd, uiDrop); } @@ -306,19 +312,19 @@ void uiSetCallback(GLFWwindow* wnd, mjuiState* state, // Clear UI callbacks in GLFW window. void uiClearCallback(GLFWwindow* wnd) { // clear container - if (glfwGetWindowUserPointer(wnd)) { - mju_free(glfwGetWindowUserPointer(wnd)); - glfwSetWindowUserPointer(wnd, NULL); + if (Glfw().glfwGetWindowUserPointer(wnd)) { + mju_free(Glfw().glfwGetWindowUserPointer(wnd)); + Glfw().glfwSetWindowUserPointer(wnd, NULL); } // clear internal callbacks - glfwSetKeyCallback(wnd, NULL); - glfwSetCursorPosCallback(wnd, NULL); - glfwSetMouseButtonCallback(wnd, NULL); - glfwSetScrollCallback(wnd, NULL); - glfwSetWindowSizeCallback(wnd, NULL); - glfwSetWindowRefreshCallback(wnd, NULL); - glfwSetDropCallback(wnd, NULL); + Glfw().glfwSetKeyCallback(wnd, NULL); + Glfw().glfwSetCursorPosCallback(wnd, NULL); + Glfw().glfwSetMouseButtonCallback(wnd, NULL); + Glfw().glfwSetScrollCallback(wnd, NULL); + Glfw().glfwSetWindowSizeCallback(wnd, NULL); + Glfw().glfwSetWindowRefreshCallback(wnd, NULL); + Glfw().glfwSetDropCallback(wnd, NULL); }