Files
Mujoco_WASM/include/uitools.c
T
Saran Tunyasuvunakool 1f7eaae62e Version 2.1: documentation, public API headers, and sample programs.
PiperOrigin-RevId: 403900419
2021-10-18 12:35:33 +01:00

323 lines
8.7 KiB
C
Executable File

// Copyright 2021 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 "uitools.h"
#include "stdio.h"
#include "string.h"
//-------------------------------- Internal GLFW callbacks ------------------------------
// update state
static void uiUpdateState(GLFWwindow* wnd)
{
// extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)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);
// 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);
// swap left and right if Alt
if( state->alt )
{
int tmp = state->left;
state->left = state->right;
state->right = tmp;
}
// get mouse position, scale by buffer-to-window ratio
double x, y;
glfwGetCursorPos(wnd, &x, &y);
x *= ptr->buffer2window;
y *= ptr->buffer2window;
// invert y to match OpenGL convention
y = state->rect[0].height - y;
// save
state->dx = x - state->x;
state->dy = y - state->y;
state->x = x;
state->y = y;
// find mouse rectangle
state->mouserect = mjr_findRect(mju_round(x), mju_round(y),
state->nrect-1, state->rect+1) + 1;
}
// keyboard
static void uiKeyboard(GLFWwindow* wnd, int key, int scancode, int act, int mods)
{
// release: nothing to do
if( act==GLFW_RELEASE )
return;
// extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state;
// update state
uiUpdateState(wnd);
// set key info
state->type = mjEVENT_KEY;
state->key = key;
state->keytime = glfwGetTime();
// application-specific processing
ptr->uiEvent(state);
}
// mouse button
static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods)
{
// extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state;
// update state
uiUpdateState(wnd);
// translate button
if( button==GLFW_MOUSE_BUTTON_LEFT )
button = mjBUTTON_LEFT;
else if( button==GLFW_MOUSE_BUTTON_RIGHT )
button = mjBUTTON_RIGHT;
else
button = mjBUTTON_MIDDLE;
// swap left and right if Alt
if( glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS ||
glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS )
{
if( button==mjBUTTON_LEFT )
button = mjBUTTON_RIGHT;
else if( button==mjBUTTON_RIGHT )
button = mjBUTTON_LEFT;
}
// press
if( act==GLFW_PRESS )
{
// detect doubleclick: 250 ms
if( button==state->button && glfwGetTime()-state->buttontime<0.25 )
state->doubleclick = 1;
else
state->doubleclick = 0;
// set info
state->type = mjEVENT_PRESS;
state->button = button;
state->buttontime = glfwGetTime();
// start dragging
if( state->mouserect )
{
state->dragbutton = state->button;
state->dragrect = state->mouserect;
}
}
// release
else
state->type = mjEVENT_RELEASE;
// application-specific processing
ptr->uiEvent(state);
// stop dragging after application processing
if( state->type==mjEVENT_RELEASE )
{
state->dragrect = 0;
state->dragbutton = 0;
}
}
// mouse move
static void uiMouseMove(GLFWwindow* wnd, double xpos, double ypos)
{
// extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state;
// no buttons down: nothing to do
if( !state->left && !state->right && !state->middle )
return;
// update state
uiUpdateState(wnd);
// set move info
state->type = mjEVENT_MOVE;
// application-specific processing
ptr->uiEvent(state);
}
// scroll
static void uiScroll(GLFWwindow* wnd, double xoffset, double yoffset)
{
// extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state;
// update state
uiUpdateState(wnd);
// set scroll info, scale by buffer-to-window ratio
state->type = mjEVENT_SCROLL;
state->sx = xoffset * ptr->buffer2window;
state->sy = yoffset * ptr->buffer2window;
// application-specific processing
ptr->uiEvent(state);
}
// resize
static void uiResize(GLFWwindow* wnd, int width, int height)
{
// extract data from user pointer
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
mjuiState* state = ptr->state;
// set layout
ptr->uiLayout(state);
// update state
uiUpdateState(wnd);
// set resize info
state->type = mjEVENT_RESIZE;
// stop dragging
state->dragbutton = 0;
state->dragrect = 0;
// application-specific processing (unless called with 0,0 from uiModify)
if( width && height )
ptr->uiEvent(state);
}
//----------------------------------- Public API ----------------------------------------
// Compute suitable font scale.
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);
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;
double PPI = 25.4 * b2w * (double)width_vmode / (double)width_MM;
// estimate font scaling, guard against unrealistic PPI
int fs;
if( width_buf>width_win )
fs = mju_round(b2w * 100);
else if( PPI>50 && PPI<350 )
fs = mju_round(PPI);
else
fs = 150;
fs = mju_round(fs * 0.02) * 50;
fs = mjMIN(300, mjMAX(100, fs));
return fs;
}
// Set internal and user-supplied UI callbacks in GLFW window.
void uiSetCallback(GLFWwindow* wnd, mjuiState* state,
uiEventFn uiEvent, uiLayoutFn uiLayout)
{
// make container with user-supplied objects and set window pointer
uiUserPointer* ptr = (uiUserPointer*) mju_malloc(sizeof(uiUserPointer));
ptr->state = state;
ptr->uiEvent = uiEvent;
ptr->uiLayout = uiLayout;
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);
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);
}
// Clear UI callbacks in GLFW window.
void uiClearCallback(GLFWwindow* wnd)
{
// clear container
if( glfwGetWindowUserPointer(wnd) )
{
mju_free(glfwGetWindowUserPointer(wnd));
glfwSetWindowUserPointer(wnd, NULL);
}
// clear internal callbacks
glfwSetKeyCallback(wnd, NULL);
glfwSetCursorPosCallback(wnd, NULL);
glfwSetMouseButtonCallback(wnd, NULL);
glfwSetScrollCallback(wnd, NULL);
glfwSetWindowSizeCallback(wnd, NULL);
}
// Modify UI structure.
void uiModify(GLFWwindow* wnd, mjUI* ui, mjuiState* state, mjrContext* con)
{
mjui_resize(ui, con);
mjr_addAux(ui->auxid, ui->width, ui->maxheight, ui->spacing.samples, con);
uiResize(wnd, 0, 0);
mjui_update(-1, -1, ui, state, con);
}