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
This commit is contained in:
Saran Tunyasuvunakool
2022-08-25 15:41:14 -07:00
committed by Copybara-Service
parent d26501c0f3
commit 3240596c3a
7 changed files with 294 additions and 94 deletions
+16 -12
View File
@@ -97,25 +97,29 @@ if(NOT TARGET lodepng)
endif() endif()
endif() endif()
# mjsimulate library # Simulate library
add_library(mjsimulate STATIC) add_library(libsimulate STATIC)
add_library(mujoco::libsimulate ALIAS libsimulate)
target_sources( target_sources(
mjsimulate libsimulate
PUBLIC simulate.h PUBLIC simulate.h
array_safety.h array_safety.h
glfw_dispatch.h
glfw_dispatch.cc
simulate.cc simulate.cc
uitools.h uitools.h
uitools.c uitools.cc
) )
target_include_directories(mjsimulate PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(libsimulate PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(mjsimulate PUBLIC MJSIMULATE_STATIC) target_compile_definitions(libsimulate PUBLIC MJSIMULATE_STATIC)
target_compile_options(mjsimulate PUBLIC ${MUJOCO_SIMULATE_COMPILE_OPTIONS}) target_compile_options(libsimulate PUBLIC ${MUJOCO_SIMULATE_COMPILE_OPTIONS})
target_link_libraries(mjsimulate PUBLIC glfw lodepng mujoco::mujoco) target_link_libraries(libsimulate PUBLIC glfw lodepng mujoco::mujoco)
target_link_options(mjsimulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS}) target_link_options(libsimulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS})
if(APPLE) if(APPLE)
target_sources(mjsimulate PRIVATE macos_save.mm) target_sources(libsimulate PRIVATE macos_save.mm)
target_link_libraries(mjsimulate PUBLIC "-framework Cocoa") target_link_libraries(libsimulate PUBLIC "-framework Cocoa")
endif() endif()
# Build simulate executable # Build simulate executable
@@ -142,7 +146,7 @@ endif()
target_link_libraries( target_link_libraries(
simulate simulate
mjsimulate libsimulate
mujoco::mujoco mujoco::mujoco
glfw glfw
Threads::Threads Threads::Threads
+117
View File
@@ -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 <windows.h>
#include <libloaderapi.h>
#else
#include <dlfcn.h>
#endif
#endif
#include <cstdlib>
#include <iostream>
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<decltype(glfw.func)>( \
GetProcAddress(reinterpret_cast<HMODULE>(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<decltype(glfw.func)>(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
+66
View File
@@ -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 <GLFW/glfw3.h>
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_
+8 -5
View File
@@ -23,6 +23,7 @@
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#include <mujoco/mjxmacro.h> #include <mujoco/mjxmacro.h>
#include "glfw_dispatch.h"
#include "simulate.h" #include "simulate.h"
#include "array_safety.h" #include "array_safety.h"
@@ -30,6 +31,8 @@ namespace {
namespace mj = ::mujoco; namespace mj = ::mujoco;
namespace mju = ::mujoco::sample_util; namespace mju = ::mujoco::sample_util;
using ::mujoco::Glfw;
// constants // constants
const double syncmisalign = 0.1; // maximum time mis-alignment before re-sync const double syncmisalign = 0.1; // maximum time mis-alignment before re-sync
const double refreshfactor = 0.7; // fraction of refresh available for simulation const double refreshfactor = 0.7; // fraction of refresh available for simulation
@@ -158,7 +161,7 @@ void PhysicsLoop(mj::Simulate& sim) {
// running // running
if (sim.run) { if (sim.run) {
// record cpu time at start of iteration // record cpu time at start of iteration
double tmstart = glfwGetTime(); double tmstart = Glfw().glfwGetTime();
// inject noise // inject noise
if (sim.ctrlnoisestd) { if (sim.ctrlnoisestd) {
@@ -195,8 +198,8 @@ void PhysicsLoop(mj::Simulate& sim) {
// in-sync // in-sync
else { else {
// step while simtime lags behind cputime, and within safefactor // step while simtime lags behind cputime, and within safefactor
while ((d->time*sim.slow_down-simsync) < (glfwGetTime()-cpusync) && while ((d->time*sim.slow_down-simsync) < (Glfw().glfwGetTime()-cpusync) &&
(glfwGetTime()-tmstart) < refreshfactor/sim.vmode.refreshRate) { (Glfw().glfwGetTime()-tmstart) < refreshfactor/sim.vmode.refreshRate) {
// clear old perturbations, apply new // clear old perturbations, apply new
mju_zero(d->xfrc_applied, 6*m->nbody); mju_zero(d->xfrc_applied, 6*m->nbody);
sim.applyposepertubations(0); // move mocap bodies only sim.applyposepertubations(0); // move mocap bodies only
@@ -268,7 +271,7 @@ int main(int argc, const char** argv) {
auto sim = std::make_unique<mj::Simulate>(); auto sim = std::make_unique<mj::Simulate>();
// init GLFW // init GLFW
if (!glfwInit()) { if (!Glfw().glfwInit()) {
mju_error("could not initialize GLFW"); mju_error("could not initialize GLFW");
} }
@@ -286,7 +289,7 @@ int main(int argc, const char** argv) {
// terminate GLFW (crashes with Linux NVidia drivers) // terminate GLFW (crashes with Linux NVidia drivers)
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32)
glfwTerminate(); Glfw().glfwTerminate();
#endif #endif
return 0; return 0;
+34 -29
View File
@@ -23,9 +23,12 @@
#include <string> #include <string>
#include <thread> #include <thread>
#include <GLFW/glfw3.h>
#include "lodepng.h"
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjvisualize.h> #include <mujoco/mjvisualize.h>
#include <mujoco/mjxmacro.h> #include <mujoco/mjxmacro.h>
#include "glfw_dispatch.h"
#include "uitools.h" #include "uitools.h"
#include "array_safety.h" #include "array_safety.h"
@@ -46,6 +49,8 @@ namespace {
namespace mj = ::mujoco; namespace mj = ::mujoco;
namespace mju = ::mujoco::sample_util; namespace mju = ::mujoco::sample_util;
using ::mujoco::Glfw;
//------------------------------------------- global ----------------------------------------------- //------------------------------------------- global -----------------------------------------------
const int maxgeom = 5000; // preallocated geom array in mjvScene const int maxgeom = 5000; // preallocated geom array in mjvScene
@@ -925,12 +930,12 @@ void copykey(mj::Simulate* sim) {
mju::strcat_arr(clipboard, "'/>"); mju::strcat_arr(clipboard, "'/>");
// copy to clipboard // copy to clipboard
glfwSetClipboardString(sim->window, clipboard); Glfw().glfwSetClipboardString(sim->window, clipboard);
} }
// millisecond timer, for MuJoCo built-in profiler // millisecond timer, for MuJoCo built-in profiler
mjtNum timer() { mjtNum timer() {
return 1000*glfwGetTime(); return 1000*Glfw().glfwGetTime();
} }
// clear all times // clear all times
@@ -965,7 +970,7 @@ void copycamera(mj::Simulate* sim) {
camera[0].up[0], camera[0].up[1], camera[0].up[2]); camera[0].up[0], camera[0].up[1], camera[0].up[2]);
// copy spec into clipboard // copy spec into clipboard
glfwSetClipboardString(sim->window, clipboard); Glfw().glfwSetClipboardString(sim->window, clipboard);
} }
// update UI 0 when MuJoCo structures change (except for joint sliders) // 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: entire framebuffer
rect[0].left = 0; rect[0].left = 0;
rect[0].bottom = 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: UI 0
rect[1].left = 0; rect[1].left = 0;
@@ -1119,29 +1124,29 @@ void uiEvent(mjuiState* state) {
break; break;
case 9: // Full screen case 9: // Full screen
if (glfwGetWindowMonitor(sim->window)) { if (Glfw().glfwGetWindowMonitor(sim->window)) {
// restore window from saved data // restore window from saved data
glfwSetWindowMonitor(sim->window, nullptr, sim->windowpos[0], sim->windowpos[1], Glfw().glfwSetWindowMonitor(sim->window, nullptr, sim->windowpos[0], sim->windowpos[1],
sim->windowsize[0], sim->windowsize[1], 0); sim->windowsize[0], sim->windowsize[1], 0);
} }
// currently windowed: switch to full screen // currently windowed: switch to full screen
else { else {
// save window data // save window data
glfwGetWindowPos(sim->window, sim->windowpos, sim->windowpos+1); Glfw().glfwGetWindowPos(sim->window, sim->windowpos, sim->windowpos+1);
glfwGetWindowSize(sim->window, sim->windowsize, sim->windowsize+1); Glfw().glfwGetWindowSize(sim->window, sim->windowsize, sim->windowsize+1);
// switch // switch
glfwSetWindowMonitor(sim->window, glfwGetPrimaryMonitor(), 0, 0, Glfw().glfwSetWindowMonitor(sim->window, Glfw().glfwGetPrimaryMonitor(), 0, 0,
sim->vmode.width, sim->vmode.height, sim->vmode.refreshRate); sim->vmode.width, sim->vmode.height, sim->vmode.refreshRate);
} }
// reinstante vsync, just in case // reinstante vsync, just in case
glfwSwapInterval(sim->vsync); Glfw().glfwSwapInterval(sim->vsync);
break; break;
case 10: // Vertical sync case 10: // Vertical sync
glfwSwapInterval(sim->vsync); Glfw().glfwSwapInterval(sim->vsync);
break; break;
} }
@@ -1617,7 +1622,7 @@ void Simulate::loadmodel() {
if (this->window && this->m->names) { if (this->window && this->m->names) {
char title[200] = "Simulate : "; char title[200] = "Simulate : ";
mju::strcat_arr(title, this->m->names); mju::strcat_arr(title, this->m->names);
glfwSetWindowTitle(this->window, title); Glfw().glfwSetWindowTitle(this->window, title);
} }
// set keyframe range and divisions // set keyframe range and divisions
@@ -1648,7 +1653,7 @@ void Simulate::prepare() {
static double lastupdatetm = 0; static double lastupdatetm = 0;
// update interval, save update time // update interval, save update time
double tmnow = glfwGetTime(); double tmnow = Glfw().glfwGetTime();
double interval = tmnow - lastupdatetm; double interval = tmnow - lastupdatetm;
interval = mjMIN(1, mjMAX(0.0001, interval)); interval = mjMIN(1, mjMAX(0.0001, interval));
lastupdatetm = tmnow; lastupdatetm = tmnow;
@@ -1734,7 +1739,7 @@ void Simulate::render() {
} }
// finalize // finalize
glfwSwapBuffers(this->window); Glfw().glfwSwapBuffers(this->window);
return; return;
} }
@@ -1822,7 +1827,7 @@ void Simulate::render() {
} }
// finalize // finalize
glfwSwapBuffers(this->window); Glfw().glfwSwapBuffers(this->window);
} }
@@ -1836,27 +1841,27 @@ void Simulate::renderloop() {
mjcb_time = timer; mjcb_time = timer;
// multisampling // multisampling
glfwWindowHint(GLFW_SAMPLES, 4); Glfw().glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_VISIBLE, 1); Glfw().glfwWindowHint(GLFW_VISIBLE, 1);
// get videomode and save // get videomode and save
this->vmode = *glfwGetVideoMode(glfwGetPrimaryMonitor()); this->vmode = *Glfw().glfwGetVideoMode(Glfw().glfwGetPrimaryMonitor());
// create window // create window
this->window = glfwCreateWindow((2*this->vmode.width)/3, (2*this->vmode.height)/3, this->window = Glfw().glfwCreateWindow((2*this->vmode.width)/3, (2*this->vmode.height)/3,
"Simulate", nullptr, nullptr); "Simulate", nullptr, nullptr);
if (!this->window) { if (!this->window) {
glfwTerminate(); Glfw().glfwTerminate();
mju_error("could not create window"); mju_error("could not create window");
} }
// save window position and size // save window position and size
glfwGetWindowPos(this->window, this->windowpos, this->windowpos+1); Glfw().glfwGetWindowPos(this->window, this->windowpos, this->windowpos+1);
glfwGetWindowSize(this->window, this->windowsize, this->windowsize+1); Glfw().glfwGetWindowSize(this->window, this->windowsize, this->windowsize+1);
// make context current, set v-sync // make context current, set v-sync
glfwMakeContextCurrent(this->window); Glfw().glfwMakeContextCurrent(this->window);
glfwSwapInterval(this->vsync); Glfw().glfwSwapInterval(this->vsync);
// init abstract visualization // init abstract visualization
mjv_defaultCamera(&this->cam); mjv_defaultCamera(&this->cam);
@@ -1906,7 +1911,7 @@ void Simulate::renderloop() {
uiModify(this->window, &this->ui1, &this->uistate, &this->con); uiModify(this->window, &this->ui1, &this->uistate, &this->con);
// run event loop // run event loop
while (!glfwWindowShouldClose(this->window) && !this->exitrequest.load()) { while (!Glfw().glfwWindowShouldClose(this->window) && !this->exitrequest.load()) {
{ {
const std::lock_guard<std::mutex> lock(this->mtx); const std::lock_guard<std::mutex> lock(this->mtx);
@@ -1918,7 +1923,7 @@ void Simulate::renderloop() {
} }
// handle events (calls all callbacks) // handle events (calls all callbacks)
glfwPollEvents(); Glfw().glfwPollEvents();
// prepare to render // prepare to render
this->prepare(); this->prepare();
-1
View File
@@ -20,7 +20,6 @@
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include "lodepng.h"
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
+53 -47
View File
@@ -17,27 +17,33 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <GLFW/glfw3.h>
#include "glfw_dispatch.h"
namespace {
using ::mujoco::Glfw;
}
//------------------------------------ internal GLFW callbacks ------------------------------------- //------------------------------------ internal GLFW callbacks -------------------------------------
// update state // update state
static void uiUpdateState(GLFWwindow* wnd) { static void uiUpdateState(GLFWwindow* wnd) {
// extract data from user pointer // extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state; mjuiState* state = ptr->state;
// mouse buttons // mouse buttons
state->left = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS); state->left = (Glfw().glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS);
state->right = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_RIGHT)==GLFW_PRESS); state->right = (Glfw().glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_RIGHT)==GLFW_PRESS);
state->middle = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_MIDDLE)==GLFW_PRESS); state->middle = (Glfw().glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_MIDDLE)==GLFW_PRESS);
// keyboard modifiers // keyboard modifiers
state->control = (glfwGetKey(wnd, GLFW_KEY_LEFT_CONTROL)==GLFW_PRESS || state->control = (Glfw().glfwGetKey(wnd, GLFW_KEY_LEFT_CONTROL)==GLFW_PRESS ||
glfwGetKey(wnd, GLFW_KEY_RIGHT_CONTROL)==GLFW_PRESS); Glfw().glfwGetKey(wnd, GLFW_KEY_RIGHT_CONTROL)==GLFW_PRESS);
state->shift = (glfwGetKey(wnd, GLFW_KEY_LEFT_SHIFT)==GLFW_PRESS || state->shift = (Glfw().glfwGetKey(wnd, GLFW_KEY_LEFT_SHIFT)==GLFW_PRESS ||
glfwGetKey(wnd, GLFW_KEY_RIGHT_SHIFT)==GLFW_PRESS); Glfw().glfwGetKey(wnd, GLFW_KEY_RIGHT_SHIFT)==GLFW_PRESS);
state->alt = (glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS || state->alt = (Glfw().glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS ||
glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS); Glfw().glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS);
// swap left and right if Alt // swap left and right if Alt
if (state->alt) { if (state->alt) {
@@ -48,7 +54,7 @@ static void uiUpdateState(GLFWwindow* wnd) {
// get mouse position, scale by buffer-to-window ratio // get mouse position, scale by buffer-to-window ratio
double x, y; double x, y;
glfwGetCursorPos(wnd, &x, &y); Glfw().glfwGetCursorPos(wnd, &x, &y);
x *= ptr->buffer2window; x *= ptr->buffer2window;
y *= 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 // extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state; mjuiState* state = ptr->state;
// update state // update state
@@ -84,7 +90,7 @@ static void uiKeyboard(GLFWwindow* wnd, int key, int scancode, int act, int mods
// set key info // set key info
state->type = mjEVENT_KEY; state->type = mjEVENT_KEY;
state->key = key; state->key = key;
state->keytime = glfwGetTime(); state->keytime = Glfw().glfwGetTime();
// application-specific processing // application-specific processing
ptr->uiEvent(state); ptr->uiEvent(state);
@@ -95,7 +101,7 @@ static void uiKeyboard(GLFWwindow* wnd, int key, int scancode, int act, int mods
// mouse button // mouse button
static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) { static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) {
// extract data from user pointer // extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state; mjuiState* state = ptr->state;
// update state // update state
@@ -111,8 +117,8 @@ static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) {
} }
// swap left and right if Alt // swap left and right if Alt
if (glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS || if (Glfw().glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS ||
glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS) { Glfw().glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS) {
if (button==mjBUTTON_LEFT) { if (button==mjBUTTON_LEFT) {
button = mjBUTTON_RIGHT; button = mjBUTTON_RIGHT;
} else if (button==mjBUTTON_RIGHT) { } else if (button==mjBUTTON_RIGHT) {
@@ -123,7 +129,7 @@ static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) {
// press // press
if (act==GLFW_PRESS) { if (act==GLFW_PRESS) {
// detect doubleclick: 250 ms // 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; state->doubleclick = 1;
} else { } else {
state->doubleclick = 0; state->doubleclick = 0;
@@ -132,7 +138,7 @@ static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) {
// set info // set info
state->type = mjEVENT_PRESS; state->type = mjEVENT_PRESS;
state->button = button; state->button = button;
state->buttontime = glfwGetTime(); state->buttontime = Glfw().glfwGetTime();
// start dragging // start dragging
if (state->mouserect) { if (state->mouserect) {
@@ -161,7 +167,7 @@ static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) {
// mouse move // mouse move
static void uiMouseMove(GLFWwindow* wnd, double xpos, double ypos) { static void uiMouseMove(GLFWwindow* wnd, double xpos, double ypos) {
// extract data from user pointer // extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state; mjuiState* state = ptr->state;
// no buttons down: nothing to do // no buttons down: nothing to do
@@ -184,7 +190,7 @@ static void uiMouseMove(GLFWwindow* wnd, double xpos, double ypos) {
// scroll // scroll
static void uiScroll(GLFWwindow* wnd, double xoffset, double yoffset) { static void uiScroll(GLFWwindow* wnd, double xoffset, double yoffset) {
// extract data from user pointer // extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state; mjuiState* state = ptr->state;
// update state // update state
@@ -204,7 +210,7 @@ static void uiScroll(GLFWwindow* wnd, double xoffset, double yoffset) {
// resize // resize
static void uiResize(GLFWwindow* wnd, int width, int height) { static void uiResize(GLFWwindow* wnd, int width, int height) {
// extract data from user pointer // extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state; mjuiState* state = ptr->state;
// set layout // set layout
@@ -228,13 +234,13 @@ static void uiResize(GLFWwindow* wnd, int width, int height) {
static void uiRender(GLFWwindow* wnd) { static void uiRender(GLFWwindow* wnd) {
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd); uiUserPointer* ptr = (uiUserPointer*)Glfw().glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state; mjuiState* state = ptr->state;
ptr->uiRender(state); ptr->uiRender(state);
} }
static void uiDrop(GLFWwindow* wnd, int count, const char** paths) { 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; mjuiState* state = ptr->state;
ptr->uiDrop(state, count, paths); ptr->uiDrop(state, count, paths);
} }
@@ -245,14 +251,14 @@ static void uiDrop(GLFWwindow* wnd, int count, const char** paths) {
int uiFontScale(GLFWwindow* wnd) { int uiFontScale(GLFWwindow* wnd) {
// compute framebuffer-to-window ratio // compute framebuffer-to-window ratio
int width_win, width_buf, height; int width_win, width_buf, height;
glfwGetWindowSize(wnd, &width_win, &height); Glfw().glfwGetWindowSize(wnd, &width_win, &height);
glfwGetFramebufferSize(wnd, &width_buf, &height); Glfw().glfwGetFramebufferSize(wnd, &width_buf, &height);
double b2w = (double)width_buf / (double)width_win; double b2w = (double)width_buf / (double)width_win;
// compute PPI // compute PPI
int width_MM, height_MM; int width_MM, height_MM;
glfwGetMonitorPhysicalSize(glfwGetPrimaryMonitor(), &width_MM, &height_MM); Glfw().glfwGetMonitorPhysicalSize(Glfw().glfwGetPrimaryMonitor(), &width_MM, &height_MM);
int width_vmode = glfwGetVideoMode(glfwGetPrimaryMonitor())->width; int width_vmode = Glfw().glfwGetVideoMode(Glfw().glfwGetPrimaryMonitor())->width;
double PPI = 25.4 * b2w * (double)width_vmode / (double)width_MM; double PPI = 25.4 * b2w * (double)width_vmode / (double)width_MM;
// estimate font scaling, guard against unrealistic PPI // estimate font scaling, guard against unrealistic PPI
@@ -283,22 +289,22 @@ void uiSetCallback(GLFWwindow* wnd, mjuiState* state,
ptr->uiLayout = uiLayout; ptr->uiLayout = uiLayout;
ptr->uiRender = uiUserRender; ptr->uiRender = uiUserRender;
ptr->uiDrop = uiUserDrop; ptr->uiDrop = uiUserDrop;
glfwSetWindowUserPointer(wnd, ptr); Glfw().glfwSetWindowUserPointer(wnd, ptr);
// compute framebuffer-to-window pixel ratio // compute framebuffer-to-window pixel ratio
int width_win, width_buf, height; int width_win, width_buf, height;
glfwGetWindowSize(wnd, &width_win, &height); Glfw().glfwGetWindowSize(wnd, &width_win, &height);
glfwGetFramebufferSize(wnd, &width_buf, &height); Glfw().glfwGetFramebufferSize(wnd, &width_buf, &height);
ptr->buffer2window = (double)width_buf / (double)width_win; ptr->buffer2window = (double)width_buf / (double)width_win;
// set internal callbacks // set internal callbacks
glfwSetKeyCallback(wnd, uiKeyboard); Glfw().glfwSetKeyCallback(wnd, uiKeyboard);
glfwSetCursorPosCallback(wnd, uiMouseMove); Glfw().glfwSetCursorPosCallback(wnd, uiMouseMove);
glfwSetMouseButtonCallback(wnd, uiMouseButton); Glfw().glfwSetMouseButtonCallback(wnd, uiMouseButton);
glfwSetScrollCallback(wnd, uiScroll); Glfw().glfwSetScrollCallback(wnd, uiScroll);
glfwSetWindowSizeCallback(wnd, uiResize); Glfw().glfwSetWindowSizeCallback(wnd, uiResize);
glfwSetWindowRefreshCallback(wnd, uiRender); Glfw().glfwSetWindowRefreshCallback(wnd, uiRender);
glfwSetDropCallback(wnd, uiDrop); Glfw().glfwSetDropCallback(wnd, uiDrop);
} }
@@ -306,19 +312,19 @@ void uiSetCallback(GLFWwindow* wnd, mjuiState* state,
// Clear UI callbacks in GLFW window. // Clear UI callbacks in GLFW window.
void uiClearCallback(GLFWwindow* wnd) { void uiClearCallback(GLFWwindow* wnd) {
// clear container // clear container
if (glfwGetWindowUserPointer(wnd)) { if (Glfw().glfwGetWindowUserPointer(wnd)) {
mju_free(glfwGetWindowUserPointer(wnd)); mju_free(Glfw().glfwGetWindowUserPointer(wnd));
glfwSetWindowUserPointer(wnd, NULL); Glfw().glfwSetWindowUserPointer(wnd, NULL);
} }
// clear internal callbacks // clear internal callbacks
glfwSetKeyCallback(wnd, NULL); Glfw().glfwSetKeyCallback(wnd, NULL);
glfwSetCursorPosCallback(wnd, NULL); Glfw().glfwSetCursorPosCallback(wnd, NULL);
glfwSetMouseButtonCallback(wnd, NULL); Glfw().glfwSetMouseButtonCallback(wnd, NULL);
glfwSetScrollCallback(wnd, NULL); Glfw().glfwSetScrollCallback(wnd, NULL);
glfwSetWindowSizeCallback(wnd, NULL); Glfw().glfwSetWindowSizeCallback(wnd, NULL);
glfwSetWindowRefreshCallback(wnd, NULL); Glfw().glfwSetWindowRefreshCallback(wnd, NULL);
glfwSetDropCallback(wnd, NULL); Glfw().glfwSetDropCallback(wnd, NULL);
} }