|
- #include <map>
- #include <queue>
- #include <thread>
-
- #include <stb_image_write.h>
- #include <osdialog.h>
-
- #include <window/Window.hpp>
- #include <asset.hpp>
- #include <widget/Widget.hpp>
- #include <app/Scene.hpp>
- #include <keyboard.hpp>
- #include <context.hpp>
- #include <patch.hpp>
- #include <settings.hpp>
- #include <plugin.hpp> // used in Window::screenshot
- #include <system.hpp> // used in Window::screenshot
-
- #include "DistrhoUI.hpp"
-
- namespace rack {
- namespace window {
-
- extern DISTRHO_NAMESPACE::UI* lastUI;
-
- static const math::Vec minWindowSize = math::Vec(640, 480);
-
-
- void Font::loadFile(const std::string& filename, NVGcontext* vg) {
- this->vg = vg;
- handle = nvgCreateFont(vg, filename.c_str(), filename.c_str());
- if (handle < 0)
- throw Exception("Failed to load font %s", filename.c_str());
- INFO("Loaded font %s", filename.c_str());
- }
-
-
- Font::~Font() {
- // There is no NanoVG deleteFont() function yet, so do nothing
- }
-
-
- std::shared_ptr<Font> Font::load(const std::string& filename) {
- return APP->window->loadFont(filename);
- }
-
-
- void Image::loadFile(const std::string& filename, NVGcontext* vg) {
- this->vg = vg;
- handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
- if (handle <= 0)
- throw Exception("Failed to load image %s", filename.c_str());
- INFO("Loaded image %s", filename.c_str());
- }
-
-
- Image::~Image() {
- // TODO What if handle is invalid?
- if (handle >= 0)
- nvgDeleteImage(vg, handle);
- }
-
-
- std::shared_ptr<Image> Image::load(const std::string& filename) {
- return APP->window->loadImage(filename);
- }
-
-
- struct Window::Internal {
- DISTRHO_NAMESPACE::UI* ui;
-
- std::string lastWindowTitle;
-
- int lastWindowX = 0;
- int lastWindowY = 0;
- int lastWindowWidth = 0;
- int lastWindowHeight = 0;
-
- int frame = 0;
- bool ignoreNextMouseDelta = false;
- int frameSwapInterval = -1;
- double monitorRefreshRate = 0.0;
- double frameTime = 0.0;
- double lastFrameDuration = 0.0;
-
- math::Vec lastMousePos;
-
- std::map<std::string, std::shared_ptr<Font>> fontCache;
- std::map<std::string, std::shared_ptr<Image>> imageCache;
-
- bool fbDirtyOnSubpixelChange = true;
- };
-
- Window::Window() {
- internal = new Internal;
- internal->ui = lastUI;
-
- vg = lastUI->getContext();
-
- int err;
-
- const GLubyte* vendor = glGetString(GL_VENDOR);
- const GLubyte* renderer = glGetString(GL_RENDERER);
- const GLubyte* version = glGetString(GL_VERSION);
- INFO("Renderer: %s %s", vendor, renderer);
- INFO("OpenGL: %s", version);
- INFO("UI pointer: %p", lastUI);
-
- // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here.
- glGetError();
-
- // Load default Blendish font
- uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf"));
- bndSetFont(uiFont->handle);
-
- if (APP->scene) {
- widget::Widget::ContextCreateEvent e;
- APP->scene->onContextCreate(e);
- }
- }
-
-
- Window::~Window() {
- if (APP->scene) {
- widget::Widget::ContextDestroyEvent e;
- APP->scene->onContextDestroy(e);
- }
-
- // Fonts and Images in the cache must be deleted before the NanoVG context is deleted
- internal->fontCache.clear();
- internal->imageCache.clear();
-
- // nvgDeleteClone(fbVg);
-
- delete internal;
- }
-
-
- math::Vec Window::getSize() {
- return math::Vec(1280, 720);
- }
-
-
- void Window::setSize(math::Vec size) {
- size = size.max(minWindowSize);
- }
-
-
- void Window::run() {
- internal->frame = 0;
- }
-
-
- void Window::step() {
- double frameTime = system::getTime();
- double lastFrameTime = internal->frameTime;
- internal->frameTime = frameTime;
- internal->lastFrameDuration = frameTime - lastFrameTime;
- // DEBUG("%.2lf Hz", 1.0 / internal->lastFrameDuration);
- double t1 = 0.0, t2 = 0.0, t3 = 0.0, t4 = 0.0, t5 = 0.0;
-
- // Make event handlers and step() have a clean NanoVG context
- nvgReset(vg);
-
- bndSetFont(uiFont->handle);
- nvgFillColor(vg, nvgRGBf(1, 1, 1));
- nvgStrokeColor(vg, nvgRGBf(1, 1, 1));
-
- // Poll events
- // Save and restore context because event handler set their own context based on which window they originate from.
- Context* context = contextGet();
- // glfwPollEvents();
- contextSet(context);
-
- // Set window title
- std::string windowTitle = APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION;
- if (APP->patch->path != "") {
- windowTitle += " - ";
- if (!APP->history->isSaved())
- windowTitle += "*";
- windowTitle += system::getFilename(APP->patch->path);
- }
- if (windowTitle != internal->lastWindowTitle) {
- internal->ui->getWindow().setTitle(windowTitle.c_str());
- internal->lastWindowTitle = windowTitle;
- }
-
- // Get desired pixel ratio
- float newPixelRatio = internal->ui->getScaleFactor();
- if (newPixelRatio != pixelRatio) {
- pixelRatio = newPixelRatio;
- APP->event->handleDirty();
- }
-
- // Get framebuffer/window ratio
- int winWidth = internal->ui->getWidth();
- int winHeight = internal->ui->getHeight();
- int fbWidth = winWidth;// * newPixelRatio;
- int fbHeight = winHeight;// * newPixelRatio;
- windowRatio = (float)fbWidth / winWidth;
- t1 = system::getTime();
-
- if (APP->scene) {
- // DEBUG("%f %f %d %d", pixelRatio, windowRatio, fbWidth, winWidth);
- // Resize scene
- APP->scene->box.size = math::Vec(fbWidth, fbHeight).div(pixelRatio);
-
- // Step scene
- APP->scene->step();
- t2 = system::getTime();
-
- // Render scene
- bool visible = true;
- if (visible) {
- // Update and render
- nvgBeginFrame(vg, fbWidth, fbHeight, pixelRatio);
- nvgScale(vg, pixelRatio, pixelRatio);
-
- // Draw scene
- widget::Widget::DrawArgs args;
- args.vg = vg;
- args.clipBox = APP->scene->box.zeroPos();
- APP->scene->draw(args);
- t3 = system::getTime();
-
- // glViewport(0, -winHeight, fbWidth, fbHeight);
- // glClearColor(0.0, 0.0, 0.0, 1.0);
- // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
- nvgEndFrame(vg);
- t4 = system::getTime();
- }
- }
-
- t5 = system::getTime();
-
- // DEBUG("pre-step %6.1f step %6.1f draw %6.1f nvgEndFrame %6.1f glfwSwapBuffers %6.1f total %6.1f",
- // (t1 - frameTime) * 1e3f,
- // (t2 - t1) * 1e3f,
- // (t3 - t2) * 1e3f,
- // (t4 - t2) * 1e3f,
- // (t5 - t4) * 1e3f,
- // (t5 - frameTime) * 1e3f
- // );
- internal->frame++;
- }
-
-
- void Window::screenshot(const std::string&) {
- }
-
-
- void Window::screenshotModules(const std::string&, float) {
- }
-
-
- void Window::close() {
- internal->ui->getWindow().close();
- }
-
-
- void Window::cursorLock() {
- if (!settings::allowCursorLock)
- return;
-
- internal->ignoreNextMouseDelta = true;
- }
-
-
- void Window::cursorUnlock() {
- if (!settings::allowCursorLock)
- return;
-
- internal->ignoreNextMouseDelta = true;
- }
-
-
- bool Window::isCursorLocked() {
- return false;
- }
-
-
- int Window::getMods() {
- int mods = 0;
- /*
- if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)
- mods |= GLFW_MOD_SHIFT;
- if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)
- mods |= GLFW_MOD_CONTROL;
- if (glfwGetKey(win, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)
- mods |= GLFW_MOD_ALT;
- if (glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)
- mods |= GLFW_MOD_SUPER;
- */
- return mods;
- }
-
-
- void Window::setFullScreen(bool) {
- }
-
-
- bool Window::isFullScreen() {
- return false;
- }
-
-
- double Window::getMonitorRefreshRate() {
- return internal->monitorRefreshRate;
- }
-
-
- double Window::getFrameTime() {
- return internal->frameTime;
- }
-
-
- double Window::getLastFrameDuration() {
- return internal->lastFrameDuration;
- }
-
-
- double Window::getFrameDurationRemaining() {
- double frameDurationDesired = internal->frameSwapInterval / internal->monitorRefreshRate;
- return frameDurationDesired - (system::getTime() - internal->frameTime);
- }
-
-
- std::shared_ptr<Font> Window::loadFont(const std::string& filename) {
- const auto& pair = internal->fontCache.find(filename);
- if (pair != internal->fontCache.end())
- return pair->second;
-
- // Load font
- std::shared_ptr<Font> font;
- try {
- font = std::make_shared<Font>();
- font->loadFile(filename, vg);
- }
- catch (Exception& e) {
- WARN("%s", e.what());
- font = NULL;
- }
- internal->fontCache[filename] = font;
- return font;
- }
-
-
- std::shared_ptr<Image> Window::loadImage(const std::string& filename) {
- const auto& pair = internal->imageCache.find(filename);
- if (pair != internal->imageCache.end())
- return pair->second;
-
- // Load image
- std::shared_ptr<Image> image;
- try {
- image = std::make_shared<Image>();
- image->loadFile(filename, vg);
- }
- catch (Exception& e) {
- WARN("%s", e.what());
- image = NULL;
- }
- internal->imageCache[filename] = image;
- return image;
- }
-
-
- bool& Window::fbDirtyOnSubpixelChange() {
- return internal->fbDirtyOnSubpixelChange;
- }
-
-
- void init() {
- }
-
- void destroy() {
- }
-
-
- } // namespace window
- } // namespace rack
|