From c3bb29182efb1858cf80c77815e24c49f262ce67 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Sun, 26 Feb 2023 23:04:38 +0100 Subject: [PATCH] Multi Window support in Rendering Manager and Display View Important reshape of Rendering Manager to support creation of multiple output windows. The Display View is now designed to allow creating and manipulating output windows. Settings are incompatible with previous version. --- src/ControlManager.cpp | 18 +- src/ControlManager.h | 5 +- src/DisplaysView.cpp | 1142 ++++++++++++++++++---------------- src/DisplaysView.h | 88 ++- src/RenderingManager.cpp | 278 ++++++--- src/RenderingManager.h | 66 +- src/Settings.cpp | 10 +- src/Settings.h | 17 +- src/UserInterfaceManager.cpp | 14 +- src/defines.h | 7 +- 10 files changed, 961 insertions(+), 684 deletions(-) diff --git a/src/ControlManager.cpp b/src/ControlManager.cpp index c2b1412..91b31b9 100644 --- a/src/ControlManager.cpp +++ b/src/ControlManager.cpp @@ -24,6 +24,7 @@ #include #include +#include #include "osc/OscOutboundPacketStream.h" #include "Log.h" @@ -36,10 +37,7 @@ #include "ActionManager.h" #include "TransitionView.h" #include "NetworkToolkit.h" - #include "UserInterfaceManager.h" -#include "RenderingManager.h" -#include #include "ControlManager.h" @@ -378,14 +376,6 @@ bool Control::init() // terminate(); - // - // set keyboard callback - // - GLFWwindow *main = Rendering::manager().mainWindow().window(); - GLFWwindow *output = Rendering::manager().outputWindow().window(); - glfwSetKeyCallback( main, Control::keyboardCalback); - glfwSetKeyCallback( output, Control::keyboardCalback); - // // load OSC Translator // @@ -1181,7 +1171,7 @@ void Control::sendOutputStatus(const IpEndpointName &remoteEndpoint) } -void Control::keyboardCalback(GLFWwindow* window, int key, int, int action, int mods) +void Control::keyboardCalback(GLFWwindow* w, int key, int, int action, int mods) { if (UserInterface::manager().keyboardAvailable() && !mods ) { @@ -1197,9 +1187,7 @@ void Control::keyboardCalback(GLFWwindow* window, int key, int, int action, int } else if (_key == GLFW_KEY_ESCAPE && action == GLFW_PRESS ) { - static GLFWwindow *output = Rendering::manager().outputWindow().window(); - if (window==output) - Rendering::manager().outputWindow().exitFullscreen(); + Rendering::manager().window(w)->exitFullscreen(); } Control::manager().input_access_.unlock(); } diff --git a/src/ControlManager.h b/src/ControlManager.h index 9fdbc1b..7010db7 100644 --- a/src/ControlManager.h +++ b/src/ControlManager.h @@ -96,6 +96,8 @@ class GLFWwindow; class Control { + friend class RenderingWindow; + // Private Constructor Control(); Control(Control const& copy) = delete; @@ -151,6 +153,8 @@ protected: void sendBatchStatus(const IpEndpointName& remoteEndpoint); void sendOutputStatus(const IpEndpointName& remoteEndpoint); + static void keyboardCalback(GLFWwindow*, int, int, int, int); + private: static void listen(); @@ -169,7 +173,6 @@ private: int multitouch_active[INPUT_MULTITOUCH_COUNT]; glm::vec2 multitouch_values[INPUT_MULTITOUCH_COUNT]; - static void keyboardCalback(GLFWwindow*, int, int, int, int); }; diff --git a/src/DisplaysView.cpp b/src/DisplaysView.cpp index aac6a32..fb1aa5e 100644 --- a/src/DisplaysView.cpp +++ b/src/DisplaysView.cpp @@ -28,6 +28,7 @@ #include #include "ImGuiToolkit.h" +#include "imgui_internal.h" #include "Mixer.h" #include "defines.h" @@ -41,6 +42,8 @@ #include "DisplaysView.h" +#define WINDOW_TITLEBAR_HEIGHT 0.03f + DisplaysView::DisplaysView() : View(DISPLAYS) { @@ -54,68 +57,75 @@ DisplaysView::DisplaysView() : View(DISPLAYS) restoreSettings(); Settings::application.views[mode_].name = "Displays"; - // stored status of output_ - output_status_ = new Group; + // create and attach all window manipulation objects + windows_ = std::vector(MAX_OUTPUT_WINDOW); + for (auto w = windows_.begin(); w != windows_.end(); ++w){ + // root node + w->root_ = new Group; + scene.ws()->attach(w->root_); + w->root_->visible_ = false; - // Geometry Scene workspace - output_ = new Group; - scene.ws()->attach(output_); - output_surface_ = new Surface; - output_->attach(output_surface_); - output_render_ = new Surface; - output_->attach(output_render_); + // surface background and texture + w->surface_ = new Surface; + w->root_->attach(w->surface_); + w->render_ = new Surface; + w->root_->attach(w->render_); + // icon if disabled + w->icon_ = new Handles(Handles::EYESLASHED); + w->icon_->visible_ = false; + w->icon_->color = glm::vec4( COLOR_WINDOW, 1.f ); + w->root_->attach(w->icon_); + // overlays for selected and not selected + w->overlays_ = new Switch; + w->root_->attach(w->overlays_); + // overlays_ [0] is for not active frame + Frame *frame = new Frame(Frame::SHARP, Frame::THIN, Frame::DROP); + frame->color = glm::vec4( COLOR_WINDOW, 1.f ); + w->overlays_->attach(frame); + // overlays_ [1] is for active frame + Group *g = new Group; + w->overlays_->attach(g); + // Overlay menu icon + w->menu_ = new Handles(Handles::MENU); + w->menu_->color = glm::vec4( COLOR_WINDOW, 1.f ); + g->attach(w->menu_); + // selected frame + frame = new Frame(Frame::SHARP, Frame::LARGE, Frame::NONE); + frame->color = glm::vec4( COLOR_WINDOW, 1.f ); + g->attach(frame); + // Overlay has two modes : window or fullscreen + w->mode_ = new Switch; + g->attach(w->mode_); + // mode_ [0] is for WINDOWED + w->handles_ = new Handles(Handles::SCALE); + w->handles_->color = glm::vec4( COLOR_WINDOW, 1.f ); + w->mode_->attach(w->handles_); + // mode_ [1] is for FULLSCREEN + w->fullscreen_ = new Symbol(Symbol::TELEVISION); + w->fullscreen_->scale_ = glm::vec3(2.f, 2.f, 1.f); + w->fullscreen_->color = glm::vec4( COLOR_WINDOW, 1.f ); + w->mode_->attach(w->fullscreen_); + // title bar + w->title_ = new Surface (new Shader); + w->title_->shader()->color = glm::vec4( COLOR_WINDOW, 1.f ); + w->title_->scale_ = glm::vec3(1.002f, WINDOW_TITLEBAR_HEIGHT, 1.f); + w->title_->translation_ = glm::vec3(0.f, 1.f + WINDOW_TITLEBAR_HEIGHT, 0.f); + w->root_->attach(w->title_); + // default to not active & window overlay frame + w->overlays_->setActive(0); + w->mode_->setActive(0); + } - output_visible_ = new Handles(Handles::EYESLASHED); - output_visible_->visible_ = false; - output_visible_->color = glm::vec4( COLOR_FRAME, 1.f ); - output_->attach(output_visible_); + // initial behavior: no window selected, no menu + show_window_menu_ = false; + current_window_ = -1; + current_window_status_ = new Group; - // overlays for selected and not selected - output_overlays_ = new Switch; - output_->attach(output_overlays_); - - // output_overlays_ [0] is for not active output frame - Frame *frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE); - frame->color = glm::vec4( COLOR_FRAME, 1.f ); - output_overlays_->attach(frame); - - // output_overlays_ [1] is for active frame - Group *g = new Group; - output_overlays_->attach(g); - // Overlay menu icon - output_menu_ = new Handles(Handles::MENU); - output_menu_->color = glm::vec4( COLOR_FRAME, 1.f ); - g->attach(output_menu_); - // selected frame - frame = new Frame(Frame::SHARP, Frame::LARGE, Frame::NONE); - frame->color = glm::vec4( COLOR_FRAME, 1.f ); - g->attach(frame); - - // Overlay has two modes : window or fullscreen - output_mode_ = new Switch; - g->attach(output_mode_); - - // output_mode_ [0] is for WINDOWED - output_handles_ = new Handles(Handles::RESIZE); - output_handles_->color = glm::vec4( COLOR_FRAME, 1.f ); - output_mode_->attach(output_handles_); - - // output_mode_ [1] is for FULLSCREEN - output_fullscreen_ = new Symbol(Symbol::TELEVISION); - output_fullscreen_->scale_ = glm::vec3(2.f, 2.f, 1.f); - output_fullscreen_->color = glm::vec4( COLOR_FRAME, 1.f ); - output_mode_->attach(output_fullscreen_); - - // default behavior : selected output in windowed mode - show_output_menu_ = false; - output_selected_=true; - output_overlays_->setActive(1); - output_mode_->setActive(0); - output_monitor_ = ""; draw_pending_ = false; // display actions : 0 = move output, 1 paint, 2 erase display_action_ = 0; + output_ar = 1.f; } void DisplaysView::update(float dt) @@ -127,10 +137,17 @@ void DisplaysView::update(float dt) // update rendering of render frame FrameBuffer *render = Mixer::manager().session()->frame(); - if (render) - output_render_->setTextureIndex( render->texture() ); - } + if (render) { + output_ar = render->aspectRatio(); + + for (int i = 0; i < MAX_OUTPUT_WINDOW; ++i) { + windows_[i].render_->setTextureIndex( render->texture() ); + } + } + else + output_ar = 1.f; + } } @@ -150,17 +167,18 @@ void DisplaysView::recenter () // get coordinates of monitor in Display units glm::vec4 rect = DISPLAYS_UNIT * glm::vec4(monitor_iter->second); - // add a background black surface with glow shadow + // add a background dark surface with glow shadow Group *m = new Group; m->scale_ = glm::vec3( 0.5f * rect.p, 0.5f * rect.q, 1.f ); m->translation_ = glm::vec3( rect.x + m->scale_.x, -rect.y - m->scale_.y, 0.f ); Surface *surf = new Surface( new Shader); surf->shader()->color = glm::vec4( 0.1f, 0.1f, 0.1f, 1.f ); - m->attach(surf); + // cyan color frame Frame *frame = new Frame(Frame::SHARP, Frame::THIN, Frame::GLOW); frame->color = glm::vec4( COLOR_MONITOR, 1.f); m->attach(frame); + // central label Glyph *label = new Glyph(4); label->setChar( std::to_string(index).back() ); label->color = glm::vec4( COLOR_MONITOR, 1.f ); @@ -169,7 +187,7 @@ void DisplaysView::recenter () m->attach(label); scene.bg()->attach( m ); - // add a foreground color frame + // add a foreground color frame (semi transparent for overlay) Group *f = new Group; f->copyTransform(m); frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE); @@ -202,7 +220,7 @@ void DisplaysView::resize ( int scale ) float z = CLAMP(0.01f * (float) scale, 0.f, 1.f); - z *= z; + z *= z; // square z *= DISPLAYS_MAX_SCALE - DISPLAYS_MIN_SCALE; z += DISPLAYS_MIN_SCALE; scene.root()->scale_.x = z; @@ -234,318 +252,240 @@ int DisplaysView::size () void DisplaysView::draw() { - // update visible flag - output_render_->visible_ = !Settings::application.render.disabled; - output_visible_->visible_ = Settings::application.render.disabled; + // draw all windows + int i = 0; + for (; i < Settings::application.num_output_windows; ++i) { + + // update visible flag + windows_[i].root_->visible_ = true; + windows_[i].render_->visible_ = !Settings::application.render.disabled; + windows_[i].icon_->visible_ = Settings::application.render.disabled; + + if (windows_[i].render_->visible_) { + // rendering of framebuffer in window + if (Settings::application.windows[1].scaled) { + windows_[i].render_->scale_ = glm::vec3(1.f, 1.f, 1.f); + } + else { + float out_ar = windows_[i].root_->scale_.x / windows_[i].root_->scale_.y; + if (output_ar < out_ar) + windows_[i].render_->scale_ = glm::vec3(output_ar / out_ar, 1.f, 1.f); + else + windows_[i].render_->scale_ = glm::vec3(1.f, out_ar / output_ar, 1.f); + } + } + + // update overlay + if ( Settings::application.windows[i+1].fullscreen ) { + // output overlay for fullscreen + windows_[i].mode_->setActive( 1 ); + windows_[i].title_->visible_ = false; + + glm::ivec4 rect = Rendering::manager().monitors()[Settings::application.windows[i+1].monitor]; + glm::vec2 TopLeft = glm::vec2(rect.x * DISPLAYS_UNIT, -rect.y * DISPLAYS_UNIT); + TopLeft = Rendering::manager().project(glm::vec3(TopLeft, 0.f), scene.root()->transform_, false); + ImGui::SetNextWindowPos( ImVec2( TopLeft.x, TopLeft.y), ImGuiCond_Always); + + glm::vec2 BottomRight = glm::vec2( (rect.x + rect.p) * DISPLAYS_UNIT, -(rect.y + rect.q) * DISPLAYS_UNIT); + BottomRight = Rendering::manager().project(glm::vec3(BottomRight, 0.f), scene.root()->transform_, false); + ImGui::SetNextWindowSize( ImVec2( BottomRight.x - TopLeft.x, BottomRight.y - TopLeft.y), ImGuiCond_Always); + + ImGui::SetNextWindowBgAlpha(0.0f); // Transparent background + if (ImGui::Begin(Settings::application.windows[i+1].name.c_str(), NULL, ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing)) + { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRectFilled(ImVec2(TopLeft.x, TopLeft.y + 4), ImVec2(BottomRight.x, TopLeft.y + ImGui::GetTextLineHeightWithSpacing()), IMGUI_COLOR_OVERLAY); + + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); + ImGui::TextColored(ImVec4(COLOR_WINDOW, 1.0f), ICON_FA_TV " %d %s %d x %d px", i+1, + Settings::application.windows[i+1].monitor.c_str(), rect.p, rect.q); + ImGui::PopFont(); + + ImGui::End(); + } - if (output_render_->visible_) { - // rendering of framebuffer in window - if (Settings::application.windows[1].scaled) { - output_render_->scale_ = glm::vec3(1.f, 1.f, 1.f); } else { - FrameBuffer *output = Mixer::manager().session()->frame(); - if (output){ - float out_ar = output_->scale_.x / output_->scale_.y; - if (output->aspectRatio() < out_ar) - output_render_->scale_ = glm::vec3(output->aspectRatio() / out_ar, 1.f, 1.f); - else - output_render_->scale_ = glm::vec3(1.f, out_ar / output->aspectRatio(), 1.f); + // output overlay for window + windows_[i].mode_->setActive( 0 ); + windows_[i].title_->visible_ = Settings::application.windows[i+1].decorated; + + glm::vec2 TopLeft = glm::vec2(Settings::application.windows[i+1].x * DISPLAYS_UNIT, -Settings::application.windows[i+1].y * DISPLAYS_UNIT); + TopLeft = Rendering::manager().project(glm::vec3(TopLeft, 0.f), scene.root()->transform_, false); + ImGui::SetNextWindowPos( ImVec2( TopLeft.x, TopLeft.y), ImGuiCond_Always); + + glm::vec2 BottomRight = glm::vec2( (Settings::application.windows[i+1].x + Settings::application.windows[i+1].w) * DISPLAYS_UNIT, + -(Settings::application.windows[i+1].y + Settings::application.windows[i+1].h) * DISPLAYS_UNIT); + BottomRight = Rendering::manager().project(glm::vec3(BottomRight, 0.f), scene.root()->transform_, false); + ImGui::SetNextWindowSize( ImVec2( BottomRight.x - TopLeft.x, BottomRight.y - TopLeft.y), ImGuiCond_Always); + + ImGui::SetNextWindowBgAlpha(0.0f); // Transparent background + if (ImGui::Begin(Settings::application.windows[i+1].name.c_str(), NULL, ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing)) + { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRectFilled(ImVec2(TopLeft.x, TopLeft.y + 4), ImVec2(BottomRight.x, TopLeft.y + ImGui::GetTextLineHeightWithSpacing()), IMGUI_COLOR_OVERLAY); + + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); + ImGui::TextColored(ImVec4(COLOR_WINDOW, 1.0f), ICON_FA_WINDOW_MAXIMIZE " %d (%d,%d) %d x %d px", i+1, + Settings::application.windows[i+1].x, Settings::application.windows[i+1].y, + Settings::application.windows[i+1].w, Settings::application.windows[i+1].h); + ImGui::PopFont(); + + ImGui::End(); + } + } + } + for (; i < MAX_OUTPUT_WINDOW; ++i) + windows_[i].root_->visible_ = false; + + // if user is not manipulating output frame + // update the output frame to match the window dimensions + // TODO Mutex for multithread access with changed flag + // TODO less busy update? + if (!current_action_ongoing_ && !draw_pending_) { + + for (int i = 0; i < Settings::application.num_output_windows; ++i) { + + if ( Settings::application.windows[i+1].fullscreen ) { + glm::ivec4 rect = Rendering::manager().monitors()[Settings::application.windows[i+1].monitor]; + windows_[i].root_->scale_.x = rect.p * 0.5f * DISPLAYS_UNIT; + windows_[i].root_->scale_.y = rect.q * 0.5f * DISPLAYS_UNIT; + windows_[i].root_->translation_.x = rect.x * DISPLAYS_UNIT + windows_[i].root_->scale_.x; + windows_[i].root_->translation_.y = -rect.y * DISPLAYS_UNIT - windows_[i].root_->scale_.y; + } + else { + windows_[i].root_->scale_.x = Settings::application.windows[i+1].w * 0.5f * DISPLAYS_UNIT; + windows_[i].root_->scale_.y = Settings::application.windows[i+1].h * 0.5f * DISPLAYS_UNIT; + windows_[i].root_->translation_.x = Settings::application.windows[i+1].x * DISPLAYS_UNIT + windows_[i].root_->scale_.x; + windows_[i].root_->translation_.y = -Settings::application.windows[i+1].y * DISPLAYS_UNIT - windows_[i].root_->scale_.y; + windows_[i].title_->scale_.y = WINDOW_TITLEBAR_HEIGHT / windows_[i].root_->scale_.y; + windows_[i].title_->translation_.y = 1.f + windows_[i].title_->scale_.y; } } } - // Window overlay - if ( Settings::application.windows[1].fullscreen ) { - // output overlay for fullscreen - output_mode_->setActive( 1 ); - - glm::ivec4 rect = Rendering::manager().monitors()[Settings::application.windows[1].monitor]; - glm::vec2 TopLeft = glm::vec2(rect.x * DISPLAYS_UNIT, -rect.y * DISPLAYS_UNIT); - TopLeft = Rendering::manager().project(glm::vec3(TopLeft, 0.f), scene.root()->transform_, false); - ImGui::SetNextWindowPos( ImVec2( TopLeft.x, TopLeft.y), ImGuiCond_Always); - - glm::vec2 BottomRight = glm::vec2( (rect.x + rect.p) * DISPLAYS_UNIT, -(rect.y + rect.q) * DISPLAYS_UNIT); - BottomRight = Rendering::manager().project(glm::vec3(BottomRight, 0.f), scene.root()->transform_, false); - ImGui::SetNextWindowSize( ImVec2( BottomRight.x - TopLeft.x, BottomRight.y - TopLeft.y), ImGuiCond_Always); - - ImGui::SetNextWindowBgAlpha(0.0f); // Transparent background - if (ImGui::Begin("InfoOutputFullscreen", NULL, ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing)) - { - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->AddRectFilled(ImVec2(TopLeft.x, TopLeft.y + 4), ImVec2(BottomRight.x, TopLeft.y + ImGui::GetTextLineHeightWithSpacing()), IMGUI_COLOR_OVERLAY); - - ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); - ImGui::TextColored(ImVec4(COLOR_FRAME_LIGHT, 1.0f), ICON_FA_TV " %s %d x %d px", Settings::application.windows[1].monitor.c_str(), rect.p, rect.q); - ImGui::PopFont(); - - ImGui::End(); - } - - } - else { - // output overlay for window - output_mode_->setActive( 0 ); - - glm::vec2 TopLeft = glm::vec2(Settings::application.windows[1].x * DISPLAYS_UNIT, -Settings::application.windows[1].y * DISPLAYS_UNIT); - TopLeft = Rendering::manager().project(glm::vec3(TopLeft, 0.f), scene.root()->transform_, false); - ImGui::SetNextWindowPos( ImVec2( TopLeft.x, TopLeft.y), ImGuiCond_Always); - - glm::vec2 BottomRight = glm::vec2( (Settings::application.windows[1].x + Settings::application.windows[1].w) * DISPLAYS_UNIT, - -(Settings::application.windows[1].y + Settings::application.windows[1].h) * DISPLAYS_UNIT); - BottomRight = Rendering::manager().project(glm::vec3(BottomRight, 0.f), scene.root()->transform_, false); - ImGui::SetNextWindowSize( ImVec2( BottomRight.x - TopLeft.x, BottomRight.y - TopLeft.y), ImGuiCond_Always); - - ImGui::SetNextWindowBgAlpha(0.0f); // Transparent background - if (ImGui::Begin("InfoOutputWindow", NULL, ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing)) - { - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->AddRectFilled(ImVec2(TopLeft.x, TopLeft.y + 4), ImVec2(BottomRight.x, TopLeft.y + ImGui::GetTextLineHeightWithSpacing()), IMGUI_COLOR_OVERLAY); - - ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); - ImGui::TextColored(ImVec4(COLOR_FRAME_LIGHT, 1.0f), ICON_FA_WINDOW_RESTORE " (%d,%d) %d x %d px", Settings::application.windows[1].x, Settings::application.windows[1].y, - Settings::application.windows[1].w, Settings::application.windows[1].h); - ImGui::PopFont(); - - ImGui::End(); - } - } - - // if user is not manipulating output frame - // update the output frame to match the window dimensions - if (!current_action_ongoing_ && !draw_pending_) { - // TODO Mutex for multithread access with changed flag - if ( Settings::application.windows[1].fullscreen ) { - glm::ivec4 rect = Rendering::manager().monitors()[Settings::application.windows[1].monitor]; - output_->scale_.x = rect.p * 0.5f * DISPLAYS_UNIT; - output_->scale_.y = rect.q * 0.5f * DISPLAYS_UNIT; - output_->translation_.x = rect.x * DISPLAYS_UNIT + output_->scale_.x; - output_->translation_.y = -rect.y * DISPLAYS_UNIT - output_->scale_.y; - } - else { - output_->scale_.x = Settings::application.windows[1].w * 0.5f * DISPLAYS_UNIT; - output_->scale_.y = Settings::application.windows[1].h * 0.5f * DISPLAYS_UNIT; - output_->translation_.x = Settings::application.windows[1].x * DISPLAYS_UNIT + output_->scale_.x; - output_->translation_.y = -Settings::application.windows[1].y * DISPLAYS_UNIT - output_->scale_.y; - } - } - - // main call to draw the view View::draw(); -// // Render the UI -// if (output_render_ != nullptr){ + // display interface + // Locate window at upper left corner + glm::vec2 P = glm::vec2(0.01f, 0.01 ); + P = Rendering::manager().project(glm::vec3(P, 0.f), scene.root()->transform_, false); + // Set window position depending on icons size + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); + ImGui::SetNextWindowPos(ImVec2(P.x, P.y - 1.5f * ImGui::GetFrameHeight() ), ImGuiCond_Always); + if (ImGui::Begin("##DisplaysMaskOptions", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground + | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus )) + { + // colors for UI + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.0f)); // 1 + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f)); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.24f, 0.24f, 0.24f, 0.46f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.85f, 0.85f, 0.85f, 0.86f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.95f, 0.95f, 0.95f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.4f, 0.4f, 0.56f)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.36f, 0.36f, 0.36f, 0.9f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.36f, 0.36f, 0.36f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.88f, 0.88f, 0.88f, 0.73f)); + ImGui::PushStyleColor(ImGuiCol_Tab, ImVec4(0.83f, 0.83f, 0.84f, 0.78f)); + ImGui::PushStyleColor(ImGuiCol_TabHovered, ImVec4(0.43f, 0.43f, 0.43f, 0.60f)); + ImGui::PushStyleColor(ImGuiCol_TabActive, ImVec4(0.40f, 0.40f, 0.40f, 1.00f)); // 14 colors -// // display interface -// // Locate window at upper left corner -// glm::vec2 P = glm::vec2(- 0.02f, 0.01 ); -// P = Rendering::manager().project(glm::vec3(P, 0.f), scene.root()->transform_, false); -// // Set window position depending on icons size -// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); -// ImGui::SetNextWindowPos(ImVec2(P.x, P.y - 1.5f * ImGui::GetFrameHeight() ), ImGuiCond_Always); -// if (ImGui::Begin("##DisplaysMaskOptions", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground -// | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings -// | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus )) -// { -// // colors for UI -// ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.0f)); // 1 -// ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f)); -// ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.f)); -// ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.24f, 0.24f, 0.24f, 0.46f)); -// ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.85f, 0.85f, 0.85f, 0.86f)); -// ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.95f, 0.95f, 0.95f, 1.00f)); -// ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); -// ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.4f, 0.4f, 0.56f)); -// ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.36f, 0.36f, 0.36f, 0.9f)); -// ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.36f, 0.36f, 0.36f, 0.5f)); -// ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.88f, 0.88f, 0.88f, 0.73f)); -// ImGui::PushStyleColor(ImGuiCol_Tab, ImVec4(0.83f, 0.83f, 0.84f, 0.78f)); -// ImGui::PushStyleColor(ImGuiCol_TabHovered, ImVec4(0.43f, 0.43f, 0.43f, 0.60f)); -// ImGui::PushStyleColor(ImGuiCol_TabActive, ImVec4(0.40f, 0.40f, 0.40f, 1.00f)); // 14 colors + // + // Buttons on top + // -// // select cursor -// static bool on = true; -// on = display_action_ == 0; -// if (ImGuiToolkit::ButtonToggle(ICON_FA_MOUSE_POINTER, &on)) { -// output_selected_=true; -// output_overlays_->setActive(1); -// display_action_ = 0; -// } + // Disable output + ImGuiToolkit::ButtonToggle(ICON_FA_EYE_SLASH, &Settings::application.render.disabled, MENU_OUTPUTDISABLE); -// ImGui::SameLine(); -// on = display_action_ == 1; -// if (ImGuiToolkit::ButtonToggle(ICON_FA_PAINT_BRUSH, &on)) { -// output_selected_=false; -// output_overlays_->setActive(0); -// display_action_ = 1; -// } + // Add / Remove windows + ImGui::SameLine(); + if ( Settings::application.num_output_windows < MAX_OUTPUT_WINDOW) { + if (ImGuiToolkit::IconButton(18, 4, "Less windows")) + ++Settings::application.num_output_windows; + } + else + ImGuiToolkit::Icon(18, 4, false); + ImGui::SameLine(); + if ( Settings::application.num_output_windows > 0 ) { + if (ImGuiToolkit::IconButton(19, 4, "More windows")) + --Settings::application.num_output_windows; + } + else + ImGuiToolkit::Icon(19, 4, false); -// ImGui::SameLine(); -// on = display_action_ == 2; -// if (ImGuiToolkit::ButtonToggle(ICON_FA_ERASER, &on)) { -// output_selected_=false; -// output_overlays_->setActive(0); -// display_action_ = 2; -// } + ImGui::PopStyleColor(14); // 14 colors + ImGui::End(); + } + ImGui::PopFont(); -// if (display_action_ > 0) { - -// ImGui::SameLine(0, 50); -// ImGui::SetNextItemWidth( ImGui::GetTextLineHeight() * 2.6); -// const char* items[] = { ICON_FA_CIRCLE, ICON_FA_SQUARE }; -// static int item = 0; -// item = (int) round(Settings::application.brush.z); -// if(ImGui::Combo("##DisplayBrushShape", &item, items, IM_ARRAYSIZE(items))) { -// Settings::application.brush.z = float(item); -// } - -// ImGui::SameLine(); -//// show_cursor_forced_ = false; -// if (ImGui::Button(ICON_FA_DOT_CIRCLE ICON_FA_SORT_DOWN )) -// ImGui::OpenPopup("display_brush_size_popup"); -// if (ImGui::BeginPopup("display_brush_size_popup", ImGuiWindowFlags_NoMove)) -// { -//// int pixel_size_min = int(0.05 * edit_source_->frame()->height() ); -//// int pixel_size_max = int(2.0 * edit_source_->frame()->height() ); -//// int pixel_size = int(Settings::application.brush.x * edit_source_->frame()->height() ); -//// show_cursor_forced_ = true; -//// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT); -//// ImGuiToolkit::Indication("Large ", 16, 1, ICON_FA_ARROW_RIGHT); -//// if (ImGui::VSliderInt("##BrushSize", ImVec2(30,260), &pixel_size, pixel_size_min, pixel_size_max, "") ){ -//// Settings::application.brush.x = CLAMP(float(pixel_size) / edit_source_->frame()->height(), BRUSH_MIN_SIZE, BRUSH_MAX_SIZE); -//// } -//// if (ImGui::IsItemHovered() || ImGui::IsItemActive() ) { -//// ImGui::BeginTooltip(); -//// ImGui::Text("%d px", pixel_size); -//// ImGui::EndTooltip(); -//// } -//// ImGuiToolkit::Indication("Small ", 15, 1, ICON_FA_ARROW_LEFT); -//// ImGui::PopFont(); -// ImGui::EndPopup(); -// } -// // make sure the visual brush is up to date -//// glm::vec2 s = glm::vec2(Settings::application.brush.x); -//// mask_cursor_circle_->scale_ = glm::vec3(s * 1.16f, 1.f); -//// mask_cursor_square_->scale_ = glm::vec3(s * 1.75f, 1.f); - -// ImGui::SameLine(); -// if (ImGui::Button(ICON_FA_FEATHER_ALT ICON_FA_SORT_DOWN )) -// ImGui::OpenPopup("display_brush_pressure_popup"); -// if (ImGui::BeginPopup("display_brush_pressure_popup", ImGuiWindowFlags_NoMove)) -// { -//// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT); -//// ImGuiToolkit::Indication("Light ", ICON_FA_FEATHER_ALT, ICON_FA_ARROW_UP); -//// ImGui::VSliderFloat("##BrushPressure", ImVec2(30,260), &Settings::application.brush.y, BRUSH_MAX_PRESS, BRUSH_MIN_PRESS, "", 0.3f); -//// if (ImGui::IsItemHovered() || ImGui::IsItemActive() ) { -//// ImGui::BeginTooltip(); -//// ImGui::Text("%.1f%%", Settings::application.brush.y * 100.0); -//// ImGui::EndTooltip(); -//// } -//// ImGuiToolkit::Indication("Heavy ", ICON_FA_WEIGHT_HANGING, ICON_FA_ARROW_DOWN); -//// ImGui::PopFont(); -// ImGui::EndPopup(); -// } - -//// // store mask if changed, reset after applied -//// if (edit_source_->maskShader()->effect > 0) -//// edit_source_->storeMask(); -//// edit_source_->maskShader()->effect = 0; - -// // menu for effects -// ImGui::SameLine(0, 60); -// if (ImGui::Button(ICON_FA_MAGIC ICON_FA_SORT_DOWN )) -// ImGui::OpenPopup( "display_brush_menu_popup" ); -// if (ImGui::BeginPopup( "display_brush_menu_popup" )) -// { -//// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT); -//// std::ostringstream oss; -//// oss << edit_source_->name(); -//// int e = 0; -//// if (ImGui::Selectable( ICON_FA_BACKSPACE "\tClear")) { -//// e = 1; -//// oss << ": Clear " << MASK_PAINT_ACTION_LABEL; -//// } -//// if (ImGui::Selectable( ICON_FA_ADJUST "\tInvert")) { -//// e = 2; -//// oss << ": Invert " << MASK_PAINT_ACTION_LABEL; -//// } -//// if (ImGui::Selectable( ICON_FA_WAVE_SQUARE "\tEdge")) { -//// e = 3; -//// oss << ": Edge " << MASK_PAINT_ACTION_LABEL; -//// } -//// if (e>0) { -//// edit_source_->maskShader()->effect = e; -//// edit_source_->maskShader()->cursor = glm::vec4(100.0, 100.0, 0.f, 0.f); -//// edit_source_->touch(); -//// Action::manager().store(oss.str()); -//// } -//// ImGui::PopFont(); -// ImGui::EndPopup(); -// } - -// static DialogToolkit::OpenImageDialog display_mask_dialog("Select Image"); - -// ImGui::SameLine(); -// if (ImGui::Button(ICON_FA_FOLDER_OPEN)) -// display_mask_dialog.open(); -// if (display_mask_dialog.closed() && !display_mask_dialog.path().empty()) -// { -// FrameBufferImage *img = new FrameBufferImage(display_mask_dialog.path()); -//// if (edit_source_->maskbuffer_->fill( img )) { -//// // apply mask filled -//// edit_source_->storeMask(); -//// // store history -//// std::ostringstream oss; -//// oss << edit_source_->name() << ": Mask fill with " << maskdialog.path(); -//// Action::manager().store(oss.str()); -//// } -// } - - -// } - -// ImGui::PopStyleColor(14); // 14 colors -// ImGui::End(); -// } -// ImGui::PopFont(); - -// } // display popup menu - if (show_output_menu_) { + if (show_window_menu_ && current_window_ > -1) { ImGui::OpenPopup( "DisplaysOutputContextMenu" ); - show_output_menu_ = false; + show_window_menu_ = false; } if (ImGui::BeginPopup("DisplaysOutputContextMenu")) { - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_FRAME_LIGHT, 1.f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_WINDOW, 1.f)); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(COLOR_MENU_HOVERED, 0.5f)); - ImGui::MenuItem( MENU_OUTPUTDISABLE, SHORTCUT_OUTPUTDISABLE, &Settings::application.render.disabled); + // FULLSCREEN selection: list of monitors + int index = 1; + std::map _monitors = Rendering::manager().monitors(); + for (auto monitor_iter = _monitors.begin(); + monitor_iter != _monitors.end(); ++monitor_iter, ++index) { - ImGui::MenuItem( ICON_FA_EXPAND_ARROWS_ALT " Scaled", nullptr, &Settings::application.windows[1].scaled ); - - ImGui::Separator(); - bool _windowed = !Settings::application.windows[1].fullscreen; - if (ImGui::MenuItem( ICON_FA_WINDOW_RESTORE " Window", nullptr, &_windowed)){ - Rendering::manager().outputWindow().exitFullscreen(); - // not fullscreen on a monitor - output_monitor_ = ""; + bool _fullscreen = Settings::application.windows[current_window_+1].fullscreen && + Settings::application.windows[current_window_+1].monitor == monitor_iter->first; + std::string menutext = std::string( ICON_FA_TV " Fullscreen on Display ") + std::to_string(index); + if (ImGui::MenuItem( menutext.c_str(), nullptr, _fullscreen )){ + windows_[current_window_].monitor_ = monitor_iter->first; + Rendering::manager().outputWindow(current_window_).setFullscreen( windows_[current_window_].monitor_ ); + } } - if (ImGui::MenuItem( ICON_FA_LAPTOP " Reset size", nullptr, false, _windowed )){ + // WINDOW mode : set size + bool _windowed = !Settings::application.windows[current_window_+1].fullscreen; + if (ImGui::MenuItem( ICON_FA_WINDOW_MAXIMIZE " Window", nullptr, &_windowed)){ + Rendering::manager().outputWindow(current_window_).exitFullscreen(); + // not fullscreen on a monitor + windows_[current_window_].monitor_ = ""; + } + ImGui::Separator(); + + bool _borderless = !Settings::application.windows[current_window_+1].decorated; + if (ImGui::MenuItem( ICON_FA_SQUARE_FULL " Borderless", nullptr, &_borderless, _windowed)){ + Rendering::manager().outputWindow(current_window_).setDecoration(!_borderless); + } + + if (ImGui::MenuItem( ICON_FA_EXPAND_ALT " Buffer aspect ratio" , nullptr, false, _windowed )){ + // reset aspect ratio + glm::ivec4 rect = windowCoordinates(current_window_); + float ar = Mixer::manager().session()->frame()->aspectRatio(); + if ( rect.p / rect.q > ar) + rect.p = ar * rect.q; + else + rect.q = rect.p / ar; + Rendering::manager().outputWindow(current_window_).setCoordinates( rect ); + } + + if (ImGui::MenuItem( ICON_FA_COMPRESS " Buffer size", nullptr, false, _windowed )){ // reset resolution to 1:1 - glm::ivec4 rect = outputCoordinates(); + glm::ivec4 rect = windowCoordinates(current_window_); rect.p = Mixer::manager().session()->frame()->width(); rect.q = Mixer::manager().session()->frame()->height(); - Rendering::manager().outputWindow().setCoordinates( rect ); + Rendering::manager().outputWindow(current_window_).setCoordinates( rect ); } - if (ImGui::MenuItem( ICON_FA_EXPAND " Fit all screens", nullptr, false, _windowed )){ + if (ImGui::MenuItem( ICON_FA_EXPAND " Fit all Displays", nullptr, false, _windowed )){ + Rendering::manager().outputWindow(current_window_).setDecoration(false); glm::ivec4 rect (INT_MAX, INT_MAX, 0, 0); std::map _monitors = Rendering::manager().monitors(); for (auto monitor_iter = _monitors.begin(); @@ -555,23 +495,12 @@ void DisplaysView::draw() rect.p = MAX(rect.p, monitor_iter->second.x+monitor_iter->second.p); rect.q = MAX(rect.q, monitor_iter->second.y+monitor_iter->second.q); } - Rendering::manager().outputWindow().setCoordinates( rect ); + Rendering::manager().outputWindow(current_window_).setCoordinates( rect ); } ImGui::Separator(); - int index = 1; - std::map _monitors = Rendering::manager().monitors(); - for (auto monitor_iter = _monitors.begin(); - monitor_iter != _monitors.end(); ++monitor_iter, ++index) { + ImGui::MenuItem( ICON_FA_EXPAND_ARROWS_ALT " Scaled content", nullptr, &Settings::application.windows[1].scaled ); - bool _fullscreen = Settings::application.windows[1].fullscreen && - Settings::application.windows[1].monitor == monitor_iter->first; - std::string menutext = std::string( ICON_FA_TV " Fullscreen ") + std::to_string(index); - if (ImGui::MenuItem( menutext.c_str(), nullptr, _fullscreen )){ - output_monitor_ = monitor_iter->first; - Rendering::manager().outputWindow().setFullscreen( output_monitor_ ); - } - } ImGui::PopStyleColor(2); ImGui::EndPopup(); } @@ -581,26 +510,49 @@ void DisplaysView::draw() std::pair DisplaysView::pick(glm::vec2 P) { - // get picking from generic View - std::pair pick = View::pick(P); + // prepare empty return value + std::pair pick = { nullptr, glm::vec2(0.f) }; - // ignore pick on render surface: it's the same as output surface - if (pick.first == output_render_ || pick.first == output_fullscreen_) - pick.first = output_surface_; + // mode placement output window + if ( display_action_ == 0 ) { + // get picking from generic View + pick = View::pick(P); - // detect clic on menu - if (pick.first == output_menu_) - show_output_menu_ = true; + // test all windows + current_window_ = -1; + for (int i = 0; i < Settings::application.num_output_windows; ++i) { - // activate / deactivate output if clic on any element of it - output_selected_ = (pick.first == output_surface_) || - (pick.first == output_handles_) || - (pick.first == output_menu_); - output_overlays_->setActive(output_selected_ ? 1 : 0); + // ignore pick on render surface: it's the same as output surface + if (pick.first == windows_[i].render_ || + pick.first == windows_[i].fullscreen_ || + pick.first == windows_[i].title_ ) + pick.first = windows_[i].surface_; - // ignore anything else than selected output - if (!output_selected_) - pick.first = nullptr; + // detect clic on menu + if (pick.first == windows_[i].menu_) + show_window_menu_ = true; + + // activate / deactivate window if clic on any element of it + if ( (pick.first == windows_[i].surface_) || + (pick.first == windows_[i].handles_) || + (pick.first == windows_[i].menu_) ) { + current_window_ = i; + windows_[i].overlays_->setActive(1); + } + else + windows_[i].overlays_->setActive(0); + + } + + // ignore anything else than selected window + if (current_window_ < 0) + pick.first = nullptr; + } + // mode other + else // if ( display_action_ == 1 ) + { + + } return pick; } @@ -616,21 +568,45 @@ void DisplaysView::select(glm::vec2 A, glm::vec2 B) glm::vec3 scene_point_A = Rendering::manager().unProject(A); glm::vec3 scene_point_B = Rendering::manager().unProject(B); - // picking visitor traverses the scene - PickingVisitor pv(scene_point_A, scene_point_B, true); - scene.accept(pv); + // select area in window placement mode + if ( display_action_ == 0 ) { + // picking visitor traverses the scene + PickingVisitor pv(scene_point_A, scene_point_B, true); + scene.accept(pv); + + // TODO Multiple window selection? + + if (!pv.empty()) { + + // find which window was picked + auto itp = pv.rbegin(); + for (; itp != pv.rend(); ++itp){ + // search for WindowPreview + auto w = std::find_if(windows_.begin(), windows_.end(), WindowPreview::hasNode(itp->first)); + if (w != windows_.end()) { + // cancel previous current + if (current_window_>-1) + windows_[current_window_].overlays_->setActive(0); + // set current + current_window_ = (int) std::distance(windows_.begin(), w); + windows_[current_window_].overlays_->setActive(1); + } + + } + + } + + } - output_selected_ = !pv.empty(); - output_overlays_->setActive(output_selected_ ? 1 : 0); } void DisplaysView::initiate() { // initiate pending action - if (!current_action_ongoing_) { + if (!current_action_ongoing_ && current_window_ > -1) { // store status - output_status_->copyTransform(output_); + current_window_status_->copyTransform(windows_[current_window_].root_); // initiated current_action_ = ""; @@ -643,18 +619,19 @@ void DisplaysView::terminate(bool force) // terminate pending action if (current_action_ongoing_ || force) { - if (Settings::application.windows[1].fullscreen) { - // Apply change of fullscreen monitor - if ( output_monitor_.compare(Settings::application.windows[1].monitor) != 0 ) - Rendering::manager().outputWindow().setFullscreen( output_monitor_ ); - } - else { - // Apply coordinates to actual output window - Rendering::manager().outputWindow().setCoordinates( outputCoordinates() ); - } + if (current_window_ > -1) { - // reset indicators - output_handles_->overlayActiveCorner(glm::vec2(0.f, 0.f)); + if (Settings::application.windows[current_window_+1].fullscreen) { + // Apply change of fullscreen monitor + if ( windows_[current_window_].monitor_.compare(Settings::application.windows[current_window_+1].monitor) != 0 ) + Rendering::manager().outputWindow(current_window_).setFullscreen( windows_[current_window_].monitor_ ); + } + else { + // Apply coordinates to actual output window + Rendering::manager().outputWindow(current_window_).setCoordinates( windowCoordinates(current_window_) ); + } + + } // terminated current_action_ = ""; @@ -666,21 +643,21 @@ void DisplaysView::terminate(bool force) } -glm::ivec4 DisplaysView::outputCoordinates() const +glm::ivec4 DisplaysView::windowCoordinates(int index) const { glm::ivec4 rect; - rect.x = (output_->translation_.x - output_->scale_.x) / DISPLAYS_UNIT; - rect.y = (output_->translation_.y + output_->scale_.y) / - DISPLAYS_UNIT; - rect.p = 2.f * output_->scale_.x / DISPLAYS_UNIT; - rect.q = 2.f * output_->scale_.y / DISPLAYS_UNIT; + rect.x = (windows_[index].root_->translation_.x - windows_[index].root_->scale_.x) / DISPLAYS_UNIT; + rect.y = (windows_[index].root_->translation_.y + windows_[index].root_->scale_.y) / - DISPLAYS_UNIT; + rect.p = 2.f * windows_[index].root_->scale_.x / DISPLAYS_UNIT; + rect.q = 2.f * windows_[index].root_->scale_.y / DISPLAYS_UNIT; return rect; } -std::string DisplaysView::outputFullscreenMonitor() const +std::string DisplaysView::fullscreenMonitor(int index) const { - return output_monitor_; + return windows_[index].monitor_; } View::Cursor DisplaysView::grab (Source *, glm::vec2 from, glm::vec2 to, std::pair pick) @@ -693,134 +670,138 @@ View::Cursor DisplaysView::grab (Source *, glm::vec2 from, glm::vec2 to, std::pa glm::vec3 scene_to = Rendering::manager().unProject(to, scene.root()->transform_); glm::vec3 scene_translation = scene_to - scene_from; - // grab only works if not fullscreen - if (!Settings::application.windows[1].fullscreen) { + if ( current_window_ > -1 ) { - // grab surface to move - if ( pick.first == output_surface_ ){ + // grab window not fullscreen : move or resizes + if (!Settings::application.windows[current_window_+1].fullscreen) { - // apply translation - output_->translation_ = output_status_->translation_ + scene_translation; - glm::ivec4 r = outputCoordinates(); + // grab surface to move + if ( pick.first == windows_[current_window_].surface_ ){ - // discretized translation with ALT - if (UserInterface::manager().altModifier()) { - r.x = ROUND(r.x, 0.01f); - r.y = ROUND(r.y, 0.01f); - output_->translation_.x = (r.x * DISPLAYS_UNIT) + output_->scale_.x; - output_->translation_.y = (r.y * - DISPLAYS_UNIT) - output_->scale_.y; + // apply translation + windows_[current_window_].root_->translation_ = current_window_status_->translation_ + scene_translation; + glm::ivec4 r = windowCoordinates(current_window_); + + // discretized translation with ALT + if (UserInterface::manager().altModifier()) { + r.x = ROUND(r.x, 0.01f); + r.y = ROUND(r.y, 0.01f); + windows_[current_window_].root_->translation_.x = (r.x * DISPLAYS_UNIT) + windows_[current_window_].root_->scale_.x; + windows_[current_window_].root_->translation_.y = (r.y * - DISPLAYS_UNIT) - windows_[current_window_].root_->scale_.y; + } + + // Show move cursor + ret.type = Cursor_ResizeAll; + info << "Window position " << r.x << ", " << r.y << " px"; } + // grab handle to resize + else if ( pick.first == windows_[current_window_].handles_ ){ - // Show move cursor - output_handles_->overlayActiveCorner(glm::vec2(-1.f, 1.f)); - ret.type = Cursor_ResizeAll; - info << "Window position " << r.x << ", " << r.y << " px"; + // which corner was picked ? + glm::vec2 corner = glm::round(pick.second); + + // transform from source center to corner + glm::mat4 T = GlmToolkit::transform(glm::vec3(corner.x, corner.y, 0.f), glm::vec3(0.f, 0.f, 0.f), + glm::vec3(1.f, 1.f, 1.f)); + + // transformation from scene to corner: + glm::mat4 scene_to_corner_transform = T * glm::inverse(current_window_status_->transform_); + glm::mat4 corner_to_scene_transform = glm::inverse(scene_to_corner_transform); + + // compute cursor movement in corner reference frame + glm::vec4 corner_from = scene_to_corner_transform * glm::vec4( scene_from, 1.f ); + glm::vec4 corner_to = scene_to_corner_transform * glm::vec4( scene_to, 1.f ); + + // operation of scaling in corner reference frame + glm::vec3 corner_scaling = glm::vec3(corner_to) / glm::vec3(corner_from); + glm::ivec4 rect; + + // RESIZE CORNER + // proportional SCALING with SHIFT + if (UserInterface::manager().shiftModifier()) { + // calculate proportional scaling factor + float factor = glm::length( glm::vec2( corner_to ) ) / glm::length( glm::vec2( corner_from ) ); + // scale node + windows_[current_window_].root_->scale_ = current_window_status_->scale_ * glm::vec3(factor, factor, 1.f); + } + // non-proportional CORNER RESIZE (normal case) + else { + // scale node + windows_[current_window_].root_->scale_ = current_window_status_->scale_ * corner_scaling; + } + + // discretized scaling with ALT + if (UserInterface::manager().altModifier()) { + // calculate ratio of scaling modulo the output resolution + glm::vec3 outputsize = windows_[current_window_].root_->scale_ / DISPLAYS_UNIT; + glm::vec3 framesize = Mixer::manager().session()->frame()->resolution(); + glm::vec3 ra = outputsize / framesize; + ra.x = ROUND(ra.x, 20.f); + ra.y = ROUND(ra.y, 20.f); + outputsize = ra * framesize; + windows_[current_window_].root_->scale_.x = outputsize.x * DISPLAYS_UNIT; + windows_[current_window_].root_->scale_.y = outputsize.y * DISPLAYS_UNIT; + } + // update corner scaling to apply to center coordinates + corner_scaling = windows_[current_window_].root_->scale_ / current_window_status_->scale_; + + // TRANSLATION CORNER + // convert source position in corner reference frame + glm::vec4 center = scene_to_corner_transform * glm::vec4( current_window_status_->translation_, 1.f); + // transform source center (in corner reference frame) + center = glm::scale(glm::identity(), corner_scaling) * center; + // convert center back into scene reference frame + center = corner_to_scene_transform * center; + // apply to node + windows_[current_window_].root_->translation_ = glm::vec3(center); + + // rescale title bar + windows_[current_window_].title_->scale_.y = WINDOW_TITLEBAR_HEIGHT / windows_[current_window_].root_->scale_.y; + windows_[current_window_].title_->translation_.y = 1.f + windows_[current_window_].title_->scale_.y; + + // show cursor depending on diagonal (corner picked) + T = glm::rotate(glm::identity(), current_window_status_->rotation_.z, glm::vec3(0.f, 0.f, 1.f)); + T = glm::scale(T, current_window_status_->scale_); + corner = T * glm::vec4( corner, 0.f, 0.f ); + ret.type = corner.x * corner.y > 0.f ? Cursor_ResizeNESW : Cursor_ResizeNWSE; + + rect = windowCoordinates(current_window_); + info << "Window size " << rect.p << " x " << rect.q << " px"; + } } - // grab handle to resize - else if ( pick.first == output_handles_ ){ + // grab fullscreen window : change monitor + else { - // which corner was picked ? - glm::vec2 corner = glm::round(pick.second); - // inform on which corner should be overlayed (opposite) - output_handles_->overlayActiveCorner(-corner); + if ( pick.first == windows_[current_window_].surface_ ){ - // transform from source center to corner - glm::mat4 T = GlmToolkit::transform(glm::vec3(corner.x, corner.y, 0.f), glm::vec3(0.f, 0.f, 0.f), - glm::vec3(1.f, 1.f, 1.f)); + // convert mouse cursor coordinates to displays coordinates + scene_to *= glm::vec3(1.f/DISPLAYS_UNIT, -1.f/DISPLAYS_UNIT, 1.f); - // transformation from scene to corner: - glm::mat4 scene_to_corner_transform = T * glm::inverse(output_status_->transform_); - glm::mat4 corner_to_scene_transform = glm::inverse(scene_to_corner_transform); + // loop over all monitors + std::map _monitors = Rendering::manager().monitors(); + int index = 1; + for (auto monitor_iter = _monitors.begin(); + monitor_iter != _monitors.end(); ++monitor_iter, ++index) { - // compute cursor movement in corner reference frame - glm::vec4 corner_from = scene_to_corner_transform * glm::vec4( scene_from, 1.f ); - glm::vec4 corner_to = scene_to_corner_transform * glm::vec4( scene_to, 1.f ); + // if the mouse cursor is over a monitor + glm::ivec4 r = monitor_iter->second; + if (scene_to.x > r.x && scene_to.x < r.x + r.p + && scene_to.y > r.y && scene_to.y < r.y + r.q) { - // operation of scaling in corner reference frame - glm::vec3 corner_scaling = glm::vec3(corner_to) / glm::vec3(corner_from); - glm::ivec4 rect; + // show output frame on top of that monitor + windows_[current_window_].root_->scale_.x = r.p * 0.5f * DISPLAYS_UNIT; + windows_[current_window_].root_->scale_.y = r.q * 0.5f * DISPLAYS_UNIT; + windows_[current_window_].root_->translation_.x = r.x * DISPLAYS_UNIT + windows_[current_window_].root_->scale_.x; + windows_[current_window_].root_->translation_.y = -r.y * DISPLAYS_UNIT - windows_[current_window_].root_->scale_.y; - // RESIZE CORNER - // proportional SCALING with SHIFT - if (UserInterface::manager().shiftModifier()) { - // calculate proportional scaling factor - float factor = glm::length( glm::vec2( corner_to ) ) / glm::length( glm::vec2( corner_from ) ); - // scale node - output_->scale_ = output_status_->scale_ * glm::vec3(factor, factor, 1.f); - } - // non-proportional CORNER RESIZE (normal case) - else { - // scale node - output_->scale_ = output_status_->scale_ * corner_scaling; - } + // remember the output monitor selected + windows_[current_window_].monitor_ = monitor_iter->first; - // discretized scaling with ALT - if (UserInterface::manager().altModifier()) { - // calculate ratio of scaling modulo the output resolution - glm::vec3 outputsize = output_->scale_ / DISPLAYS_UNIT; - glm::vec3 framesize = Mixer::manager().session()->frame()->resolution(); - glm::vec3 ra = outputsize / framesize; - ra.x = ROUND(ra.x, 20.f); - ra.y = ROUND(ra.y, 20.f); - outputsize = ra * framesize; - output_->scale_.x = outputsize.x * DISPLAYS_UNIT; - output_->scale_.y = outputsize.y * DISPLAYS_UNIT; - } - // update corner scaling to apply to center coordinates - corner_scaling = output_->scale_ / output_status_->scale_; - - // TRANSLATION CORNER - // convert source position in corner reference frame - glm::vec4 center = scene_to_corner_transform * glm::vec4( output_status_->translation_, 1.f); - // transform source center (in corner reference frame) - center = glm::scale(glm::identity(), corner_scaling) * center; - // convert center back into scene reference frame - center = corner_to_scene_transform * center; - // apply to node - output_->translation_ = glm::vec3(center); - - // show cursor depending on diagonal (corner picked) - T = glm::rotate(glm::identity(), output_status_->rotation_.z, glm::vec3(0.f, 0.f, 1.f)); - T = glm::scale(T, output_status_->scale_); - corner = T * glm::vec4( corner, 0.f, 0.f ); - ret.type = corner.x * corner.y > 0.f ? Cursor_ResizeNESW : Cursor_ResizeNWSE; - - rect = outputCoordinates(); - info << "Window size " << rect.p << " x " << rect.q << " px"; - } - } - else { - - // grab fullscreen output - if ( pick.first == output_surface_ ){ - - // convert mouse cursor coordinates to displays coordinates - scene_to *= glm::vec3(1.f/DISPLAYS_UNIT, -1.f/DISPLAYS_UNIT, 1.f); - - // loop over all monitors - std::map _monitors = Rendering::manager().monitors(); - int index = 1; - for (auto monitor_iter = _monitors.begin(); - monitor_iter != _monitors.end(); ++monitor_iter, ++index) { - - // if the mouse cursor is over a monitor - glm::ivec4 r = monitor_iter->second; - if (scene_to.x > r.x && scene_to.x < r.x + r.p - && scene_to.y > r.y && scene_to.y < r.y + r.q) { - - // show output frame on top of that monitor - output_->scale_.x = r.p * 0.5f * DISPLAYS_UNIT; - output_->scale_.y = r.q * 0.5f * DISPLAYS_UNIT; - output_->translation_.x = r.x * DISPLAYS_UNIT + output_->scale_.x; - output_->translation_.y = -r.y * DISPLAYS_UNIT - output_->scale_.y; - - // remember the output monitor selected - output_monitor_ = monitor_iter->first; - - // Show cursor - ret.type = Cursor_Hand; - info << "Fullscreen Monitor " << index << " (" << output_monitor_ << ")\n "<< - r.p << " x " << r.q << " px"; + // Show cursor + ret.type = Cursor_Hand; + info << "Fullscreen Monitor " << index << " (" << windows_[current_window_].monitor_ << ")\n "<< + r.p << " x " << r.q << " px"; + } } } } @@ -832,11 +813,89 @@ View::Cursor DisplaysView::grab (Source *, glm::vec2 from, glm::vec2 to, std::pa return ret; } +View::Cursor DisplaysView::over (glm::vec2 pos) +{ +// output_zoomarea_->visible_ = false; + +// if ( display_action_ == 2 ) { + +// // utility +// bool _over_render = false; +// glm::vec2 _uv_cursor_center(0.f, 0.f); +// glm::vec3 scene_pos = Rendering::manager().unProject(pos); + +// // find if the output_render surface is under the cursor at scene_pos +// _over_render = set_UV_output_render_from_pick(scene_pos, &_uv_cursor_center); + +// // set coordinates of zoom area indicator under cursor at position 'pos' +// output_zoomarea_->translation_ = glm::inverse(scene.root()->transform_) * glm::vec4(scene_pos, 1.f); +// output_zoomarea_->visible_ = _over_render; +// output_zoomarea_->scale_ = glm::vec3(output_scale * output_->scale_.x, output_scale * output_->scale_.x, 1.f); + +// // mouse cursor is over the rendering surface +// if (_over_render) { + +// // get the imgui screen area to render the zoom window into +// const ImGuiIO& io = ImGui::GetIO(); +// const ImVec2 margin(100.f, 50.f); +// ImVec2 screen = io.DisplaySize - margin * 2.f; +// ImVec2 win_pos = margin; +// ImVec2 win_size = screen * 0.5f; + +// // vimix window is horizontal +// if ( screen.x > screen.y){ +// // set zoom window to square +// win_size.y = win_size.x; +// // show the zoom window left and right of window +// win_pos.y += (screen.y - win_size.y) * 0.5f; +// win_pos.x += (pos.x > io.DisplaySize.x * 0.5f) ? 0 : win_size.x; +// } +// // vimix window is vertical +// else { +// // set zoom window to square +// win_size.x = win_size.y; +// // show the zoom window top and bottom of window +// win_pos.x += (screen.x - win_size.x) * 0.5f; +// win_pos.y += (pos.y > io.DisplaySize.y * 0.5f) ? 0 : win_size.y; +// } + +// // DRAW window without decoration, 100% opaque, with 2 pixels on the border +// ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.f); +// ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.f); +// ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(2.f, 2.f)); + +// ImGui::SetNextWindowPos(win_pos, ImGuiCond_Always); +// ImGui::SetNextWindowSize(win_size, ImGuiCond_Always); +// if (ImGui::Begin("##DisplaysMacro", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration +// | ImGuiWindowFlags_NoSavedSettings )) +// { +// ImVec2 _zoom(zoom_factor, zoom_factor * output_ar); +// ImVec2 _uv1( _uv_cursor_center.x - _zoom.x * 0.5f, _uv_cursor_center.y - _zoom.y * 0.5f); +// _uv1 = ImClamp( _uv1, ImVec2(0.f, 0.f), ImVec2(1.f, 1.f)); + +// ImVec2 rect = _uv1 + _zoom; +// ImVec2 _uv2 = ImClamp( rect, ImVec2(0.f, 0.f), ImVec2(1.f, 1.f)); +// _uv1 = _uv2 - _zoom; + + +// ImGui::Image((void*)(intptr_t) output_render_->textureIndex(), win_size, _uv1, _uv2); + +// ImGui::End(); +// } +// ImGui::PopStyleVar(3); + +// } + +// } + + return Cursor(); +} bool DisplaysView::doubleclic (glm::vec2 P) { + // TODO find which window? if ( pick(P).first != nullptr) { - Rendering::manager().outputWindow().show(); + Rendering::manager().outputWindow(current_window_).show(); return true; } @@ -845,8 +904,8 @@ bool DisplaysView::doubleclic (glm::vec2 P) void DisplaysView::arrow (glm::vec2 movement) { - // grab only works on selected output if not fullscreen - if (output_selected_ && !Settings::application.windows[1].fullscreen) { + // grab only works on current window if not fullscreen + if (current_window_ > -1 && !Settings::application.windows[current_window_+1].fullscreen) { // operate in pixel coordinates static glm::vec2 p; @@ -855,8 +914,8 @@ void DisplaysView::arrow (glm::vec2 movement) if (!current_action_ongoing_) { // initial position - p.x = (output_->translation_.x - output_->scale_.x) / DISPLAYS_UNIT; - p.y = (output_->translation_.y + output_->scale_.y) / - DISPLAYS_UNIT; + p.x = (windows_[current_window_].root_->translation_.x - windows_[current_window_].root_->scale_.x) / DISPLAYS_UNIT; + p.y = (windows_[current_window_].root_->translation_.y + windows_[current_window_].root_->scale_.y) / - DISPLAYS_UNIT; // initiate (terminated at key release) current_action_ongoing_ = true; @@ -871,16 +930,59 @@ void DisplaysView::arrow (glm::vec2 movement) q.x = ROUND(p.x, 0.05f); // 20 pix precision q.y = ROUND(p.y, 0.05f); // convert back to output-frame coordinates - output_->translation_.x = (q.x * DISPLAYS_UNIT) + output_->scale_.x; - output_->translation_.y = (q.y * - DISPLAYS_UNIT) - output_->scale_.y; + windows_[current_window_].root_->translation_.x = (q.x * DISPLAYS_UNIT) + windows_[current_window_].root_->scale_.x; + windows_[current_window_].root_->translation_.y = (q.y * - DISPLAYS_UNIT) - windows_[current_window_].root_->scale_.y; } else { // convert back to output-frame coordinates - output_->translation_.x = (p.x * DISPLAYS_UNIT) + output_->scale_.x; - output_->translation_.y = (p.y * - DISPLAYS_UNIT) - output_->scale_.y; + windows_[current_window_].root_->translation_.x = (p.x * DISPLAYS_UNIT) + windows_[current_window_].root_->scale_.x; + windows_[current_window_].root_->translation_.y = (p.y * - DISPLAYS_UNIT) - windows_[current_window_].root_->scale_.y; } } } + + +bool WindowPreview::hasNode::operator()(WindowPreview elem) const +{ + if (_n) + { + if (_n == elem.render_ || + _n == elem.surface_ || + _n == elem.title_) + return true; + } + + return false; +} + + + + +//// from scene coordinates pos, get the UV of point in the output render +//// returns true if on output_render +//bool DisplaysView::get_UV_window_render_from_pick(const glm::vec3 &pos, glm::vec2 *uv) +//{ +// bool ret = false; + +// // perform picking at pos +// PickingVisitor pv(pos); +// scene.accept(pv); + +// // find if the output_render surface is at pos +// for(std::vector< std::pair >::const_reverse_iterator p = pv.rbegin(); p != pv.rend(); ++p){ +// // if found the output rendering surface at pos +// if ( p->first == window_render_) { +// // set return value to true +// ret = true; +// // convert picking coordinates into UV +// // This works only as we are sure that the output_render_ UV mapping is fixed +// *uv = (p->second + glm::vec2(1.f, -1.f)) * glm::vec2(0.5f, -0.5f); +// break; +// } +// } + +// return ret; +//} diff --git a/src/DisplaysView.h b/src/DisplaysView.h index 52cb1f9..c34e214 100644 --- a/src/DisplaysView.h +++ b/src/DisplaysView.h @@ -3,6 +3,48 @@ #include "View.h" +struct WindowPreview +{ + Group *root_; + Group *status_; + Surface *surface_; + Surface *render_; + Switch *overlays_; + Switch *mode_; + Handles *handles_; + Handles *menu_; + Handles *icon_; + Surface *title_; + Symbol *fullscreen_; + std::string monitor_; + + WindowPreview() { + root_ = nullptr; + status_ = nullptr; + surface_ = nullptr; + render_ = nullptr; + overlays_ = nullptr; + status_ = nullptr; + mode_ = nullptr; + handles_ = nullptr; + menu_ = nullptr; + icon_ = nullptr; + title_ = nullptr; + fullscreen_ = nullptr; + monitor_ = ""; + } + + struct hasNode + { + bool operator()(WindowPreview elem) const; + hasNode(Node *n) : _n(n) { } + private: + Node *_n; + }; + +}; + + class DisplaysView : public View { public: @@ -23,31 +65,43 @@ public: void initiate () override; void terminate (bool force = false) override; Cursor grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair pick) override; + Cursor over (glm::vec2) override; void arrow (glm::vec2) override; bool doubleclic (glm::vec2) override; - glm::ivec4 outputCoordinates() const; - std::string outputFullscreenMonitor() const; + glm::ivec4 windowCoordinates(int index) const; + std::string fullscreenMonitor(int index) const; private: - Group *output_; - Group *output_status_; - Surface *output_surface_; - Surface *output_render_; - Switch *output_overlays_; - Switch *output_mode_; - Handles *output_handles_; - Handles *output_menu_; - Handles *output_visible_; - Symbol *output_fullscreen_; - - bool output_selected_; - bool show_output_menu_; - int display_action_; bool draw_pending_; + float output_ar; + + std::vector windows_; + int current_window_; + Group *current_window_status_; + bool show_window_menu_; + +// Group *window_; +// Group *window_status_; +// Surface *window_surface_; +// Surface *window_render_; +// Switch *window_overlays_; +// Switch *window_mode_; +// Handles *window_handles_; +// Handles *window_menu_; +// Handles *window_icon_; +// Surface *window_title_; +// Symbol *window_fullscreen_; +//// Surface *preview_surface_; +// std::string window_monitor_; + +// bool window_selected_; + int display_action_; + + +// bool get_UV_window_render_from_pick(const glm::vec3 &pos, glm::vec2 *uv); - std::string output_monitor_; }; diff --git a/src/RenderingManager.cpp b/src/RenderingManager.cpp index 19a3997..1a796be 100644 --- a/src/RenderingManager.cpp +++ b/src/RenderingManager.cpp @@ -67,6 +67,7 @@ #include "SystemToolkit.h" #include "GstToolkit.h" #include "UserInterfaceManager.h" +#include "ControlManager.h" #include "RenderingManager.h" @@ -120,8 +121,6 @@ void Rendering::LinkPipeline( GstPipeline *pipeline ) #endif -std::map GLFW_window_; - static void glfw_error_callback(int error, const char* description) { g_printerr("Glfw Error %d: %s\n", error, description); @@ -131,11 +130,11 @@ static void WindowResizeCallback( GLFWwindow *w, int width, int height) { if (Rendering::manager().mainWindow().window() == w) { // UI manager tries to keep windows in the workspace - WorkspaceWindow::notifyWorkspaceSizeChanged(GLFW_window_[w]->previous_size.x, GLFW_window_[w]->previous_size.y, width, height); - GLFW_window_[w]->previous_size = glm::vec2(width, height); + WorkspaceWindow::notifyWorkspaceSizeChanged(Rendering::manager().mainWindow().previous_size.x, Rendering::manager().mainWindow().previous_size.y, width, height); + Rendering::manager().mainWindow().previous_size = glm::vec2(width, height); } - int id = GLFW_window_[w]->index(); + int id = Rendering::manager().window(w)->index(); Settings::application.windows[id].fullscreen = glfwGetWindowMonitor(w) != nullptr; if (!Settings::application.windows[id].fullscreen) { Settings::application.windows[id].w = width; @@ -149,7 +148,7 @@ static void WindowResizeCallback( GLFWwindow *w, int width, int height) static void WindowMoveCallback( GLFWwindow *w, int x, int y) { - int id = GLFW_window_[w]->index(); + int id = Rendering::manager().window(w)->index(); if (!Settings::application.windows[id].fullscreen) { Settings::application.windows[id].x = x; Settings::application.windows[id].y = y; @@ -167,7 +166,7 @@ static void OutputWindowEvent( GLFWwindow *w, int button, int action, int) // exit fullscreen if its the case if (glfwGetWindowMonitor(w) != nullptr) - Rendering::manager().outputWindow().exitFullscreen(); + Rendering::manager().window(w)->exitFullscreen(); // show main window in DISPLAYS view to // indicate how to manipulate output window @@ -203,9 +202,6 @@ void Rendering::MonitorConnect(GLFWmonitor* monitor, int event) std::string n = glfwGetMonitorName(monitors[i]); // add Rendering::manager().monitors_geometry_[n] = glm::ivec4(x, y, vm->width, vm->height); - - g_printerr("Monitor %d : %s, %d Hz, %d x %d px\n", i, glfwGetMonitorName(monitors[i]), - vm->refreshRate, vm->width, vm->height); } // inform Displays View that monitors changed @@ -285,33 +281,15 @@ Rendering::Rendering() bool Rendering::init() { - // Setup window + // + // Setup GLFW + // glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()){ g_printerr("Failed to Initialize GLFW.\n"); return false; } - // Decide GL+GLSL versions GL 3.3 + GLSL 150 - glsl_version = "#version 150"; - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only -#if __APPLE__ - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac -#endif - - // - // OpenGL Multisampling main window - // - glfwWindowHint(GLFW_SAMPLES, Settings::application.render.multisampling); - main_.init(0); - // set application icon - main_.setIcon("images/vimix_256x256.png"); - // additional window callbacks for main window - glfwSetWindowCloseCallback( main_.window(), WindowCloseCallback ); - glfwSetDropCallback( main_.window(), Rendering::FileDropped); - // // Gstreamer setup // @@ -349,7 +327,9 @@ bool Rendering::init() Log::Info("Found Frei0r plugins in %s", frei0r_path.c_str()); g_setenv ("FREI0R_PATH", frei0r_path.c_str(), TRUE); } - g_setenv ("GST_GL_API", "opengl3", TRUE); + + // init gstreamer with opengl API + g_setenv ("GST_GL_API", VIMIX_GL_VERSION, TRUE); gst_init (NULL, NULL); // increase selection rank for GPU decoding plugins @@ -368,6 +348,7 @@ bool Rendering::init() else { Log::Info("No hardware decoding plugin found."); } + #ifdef SYNC_GSTREAMER_OPENGL_CONTEXT #if GST_GL_HAVE_PLATFORM_WGL global_gl_context = gst_gl_context_new_wrapped (display, (guintptr) wglGetCurrentContext (), @@ -388,29 +369,48 @@ bool Rendering::init() #endif // - // output window - // - glfwWindowHint(GLFW_SAMPLES, 0); // no need for multisampling in displaying output - output_.init(1, main_.window()); - output_.setIcon("images/vimix_256x256.png"); - // special callbacks for user input in output window - glfwSetMouseButtonCallback( output_.window(), OutputWindowEvent); - - // - // Monitors configuration + // Monitors // Rendering::MonitorConnect(nullptr, GLFW_DONT_CARE); // automatic detection of monitor connect & disconnect glfwSetMonitorCallback(Rendering::MonitorConnect); + // + // Main window + // + if ( !main_.init(0) ) + return false; + + // + // Output windows will be initialized in draw + // + outputs_ = std::vector(MAX_OUTPUT_WINDOW); + return true; } +RenderingWindow* Rendering::window(GLFWwindow *w) +{ + if (windows_.count(w)) + return windows_[w]; + else + return &main_; +} + +RenderingWindow* Rendering::window(int index) +{ + if (index > 0 && index <= MAX_OUTPUT_WINDOW ) + return &outputs_[index - 1]; + else + return &main_; +} + void Rendering::show() { - // show output window - output_.show(); + // show output windows + for (auto it = outputs_.begin(); it != outputs_.end(); ++it) + it->show(); // show main window main_.show(); @@ -440,7 +440,8 @@ void Rendering::draw() // change windows fullscreen mode if requested main_.changeFullscreen_(); - output_.changeFullscreen_(); + for (auto it = outputs_.begin(); it != outputs_.end(); ++it) + it->changeFullscreen_(); // change main window title if requested if (!main_new_title_.empty()) { @@ -464,6 +465,20 @@ void Rendering::draw() request_screenshot_ = false; } + // draw output windows and count number of success + int count = 0; + for (auto it = outputs_.begin(); it != outputs_.end(); ++it) { + if ( it->draw( Mixer::manager().session()->frame() ) ) + ++count; + } + // terminate or initialize output windows to match number of output windows + if (count > Settings::application.num_output_windows) + outputs_[count-1].terminate(); + else if (count < Settings::application.num_output_windows) { + outputs_[count].init( count+1, main_.window()); + outputs_[count].show(); + } + // software framerate limiter < 62 FPS { static GTimer *timer = g_timer_new (); @@ -474,18 +489,19 @@ void Rendering::draw() } // swap GL buffers - glfwSwapBuffers(main_.window()); - - // draw output window (and swap buffer output) - output_.draw( Mixer::manager().session()->frame() ); - + main_.swap(); + for (auto it = outputs_.begin(); it != outputs_.end(); ++it) + it->swap(); } void Rendering::terminate() { - // close window - glfwDestroyWindow(output_.window()); - glfwDestroyWindow(main_.window()); + // terminate all windows + for (auto it = outputs_.begin(); it != outputs_.end(); ++it) + it->terminate(); + + main_.terminate(); + // glfwTerminate(); } @@ -674,17 +690,15 @@ WindowSurface::WindowSurface(Shader *s) : Primitive(s) } -RenderingWindow::RenderingWindow() : window_(nullptr), master_(nullptr), +RenderingWindow::RenderingWindow() : window_(NULL), master_(NULL), index_(-1), dpi_scale_(1.f), textureid_(0), fbo_(0), surface_(nullptr), request_change_fullscreen_(false) { } RenderingWindow::~RenderingWindow() { - if (surface_ != nullptr) - delete surface_; - if (fbo_ != 0) - glDeleteFramebuffers(1, &fbo_); + if (window_ != NULL) + terminate(); } void RenderingWindow::setTitle(const std::string &title) @@ -695,7 +709,7 @@ void RenderingWindow::setTitle(const std::string &title) else fulltitle = title + std::string(" - " APP_NAME); - if (window_ != nullptr) + if (window_ != NULL) glfwSetWindowTitle(window_, fulltitle.c_str()); } @@ -703,7 +717,7 @@ void RenderingWindow::setIcon(const std::string &resource) { size_t fpsize = 0; const char *fp = Resource::getData(resource, &fpsize); - if (fp != nullptr && window_ != nullptr) { + if (fp != nullptr && window_) { GLFWimage icon; icon.pixels = stbi_load_from_memory( (const stbi_uc*)fp, fpsize, &icon.width, &icon.height, nullptr, 4 ); glfwSetWindowIcon( window_, 1, &icon ); @@ -715,7 +729,7 @@ GLFWmonitor *RenderingWindow::monitor() { // get monitor at the center of the window int x = 0, y = 0, w = 2, h = 2; - if (window_ != nullptr) { + if (window_) { glfwGetWindowSize(window_, &w, &h); glfwGetWindowPos(window_, &x, &y); } @@ -724,7 +738,7 @@ GLFWmonitor *RenderingWindow::monitor() void RenderingWindow::setFullscreen_(GLFWmonitor *mo) { - if (window_ == nullptr) + if (!window_) return; // disable fullscreen mode @@ -734,7 +748,7 @@ void RenderingWindow::setFullscreen_(GLFWmonitor *mo) // set to window mode glfwSetInputMode( window_, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - glfwSetWindowMonitor( window_, nullptr, Settings::application.windows[index_].x, + glfwSetWindowMonitor( window_, NULL, Settings::application.windows[index_].x, Settings::application.windows[index_].y, Settings::application.windows[index_].w, Settings::application.windows[index_].h, 0 ); @@ -809,9 +823,26 @@ void RenderingWindow::changeFullscreen_() } } +void RenderingWindow::setDecoration (bool on) +{ + if (window_ == NULL) + return; + + Settings::application.windows[index_].decorated = on; + glfwSetWindowAttrib( window_, GLFW_RESIZABLE, on ? GLFW_TRUE : GLFW_FALSE); + glfwSetWindowAttrib( window_, GLFW_DECORATED, on ? GLFW_TRUE : GLFW_FALSE); +} + void RenderingWindow::setCoordinates(glm::ivec4 rect) { - glfwSetWindowSize( window_, rect.p, rect.q); + if (window_ == NULL) + return; + + // restore maximized window to be able to change its coordinates + if (glfwGetWindowAttrib(window_, GLFW_MAXIMIZED)) + glfwRestoreWindow(window_); + + glfwSetWindowSize( window_, glm::max(50, rect.p), glm::max(50, rect.q)); glfwSetWindowPos( window_, rect.x, rect.y); } @@ -849,19 +880,38 @@ float RenderingWindow::aspectRatio() bool RenderingWindow::init(int index, GLFWwindow *share) { + if (window_) + return false; + + glfwMakeContextCurrent(NULL); + + /// + /// Settings + /// index_ = index; master_ = share; - - // access Settings Settings::WindowConfig winset = Settings::application.windows[index_]; + /// + /// GLFW window creation parameters + /// + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only +#if __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac +#endif + + // multisampling in main window + glfwWindowHint(GLFW_SAMPLES, master_ == NULL ? Settings::application.render.multisampling : 0); + // do not show at creation glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE); - if (master_ != nullptr) { - // special window type for output + // restore decoration state + if (master_ != NULL && !winset.decorated) { glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); } @@ -875,25 +925,49 @@ bool RenderingWindow::init(int index, GLFWwindow *share) // ensure minimal window size glfwSetWindowSizeLimits(window_, 500, 500, GLFW_DONT_CARE, GLFW_DONT_CARE); - previous_size = glm::vec2(winset.w, winset.h); // set initial position glfwSetWindowPos(window_, winset.x, winset.y); + // set icon + setIcon("images/vimix_256x256.png"); + + /// /// CALLBACKS + /// // store global ref to pointers (used by callbacks) - GLFW_window_[window_] = this; + Rendering::manager().windows_[window_] = this; + + // // window position and resize callbacks + // glfwSetWindowPosCallback( window_, WindowMoveCallback ); glfwSetWindowSizeCallback( window_, WindowResizeCallback ); + // + // set keyboard callback + // + // all windows capture keys + glfwSetKeyCallback( window_, Control::keyboardCalback); + + if (master_ != NULL) { + // additional window callbacks for user input in output windows + glfwSetMouseButtonCallback( window_, OutputWindowEvent); + } + else { + // additional window callbacks for main window + glfwSetWindowCloseCallback( window_, WindowCloseCallback ); + glfwSetDropCallback( window_, Rendering::FileDropped); + } + + // + // Initialize OpenGL + // // take opengl context ownership glfwMakeContextCurrent(window_); - // // Initialize OpenGL loader on first call - // static bool glad_initialized = false; if ( !glad_initialized ) { bool err = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress) == 0; @@ -920,19 +994,17 @@ bool RenderingWindow::init(int index, GLFWwindow *share) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // if not main window - if ( master_ != nullptr ) { - // Enable vsync on output window - glfwSwapInterval(Settings::application.render.vsync); + if ( master_ != NULL ) { + // NO vsync on output windows + glfwSwapInterval(0); // no need for multisampling glDisable(GL_MULTISAMPLE); // clear to black window_attributes_.clear_color = glm::vec4(0.f, 0.f, 0.f, 1.f); - // give back context ownership - glfwMakeContextCurrent(master_); } else { - // Disable vsync on main window - glfwSwapInterval(0); + // vsync on main window + glfwSwapInterval(Settings::application.render.vsync); // Enable Antialiasing multisampling if (Settings::application.render.multisampling > 0) { glEnable(GL_MULTISAMPLE); @@ -945,20 +1017,46 @@ bool RenderingWindow::init(int index, GLFWwindow *share) return true; } +void RenderingWindow::terminate() +{ + // cleanup + if (surface_ != nullptr) + delete surface_; + if (fbo_ != 0) + glDeleteFramebuffers(1, &fbo_); + if (window_ != NULL) { + // remove global ref to pointers + Rendering::manager().windows_.erase(window_); + // delete window + glfwDestroyWindow(window_); + } + + // invalidate + window_ = NULL; + surface_ = nullptr; + fbo_ = 0; + index_ = -1; +} + void RenderingWindow::show() { + if (!window_) + return; + glfwShowWindow(window_); if ( Settings::application.windows[index_].fullscreen ) { GLFWmonitor *mo = Rendering::manager().monitorNamed(Settings::application.windows[index_].monitor); setFullscreen_(mo); } - } void RenderingWindow::makeCurrent() { + if (!window_) + return; + // handle window resize glfwGetFramebufferSize(window_, &(window_attributes_.viewport.x), &(window_attributes_.viewport.y)); @@ -972,12 +1070,18 @@ void RenderingWindow::makeCurrent() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } -// TODO update parameters for draw on resize event (not every frame) +void RenderingWindow::swap() +{ + if (window_) + // swap buffer + glfwSwapBuffers(window_); -void RenderingWindow::draw(FrameBuffer *fb) +} + +bool RenderingWindow::draw(FrameBuffer *fb) { if (!window_ || !fb) - return; + return false; // only draw if window is not iconified if( !glfwGetWindowAttrib(window_, GLFW_ICONIFIED ) ) { @@ -1058,7 +1162,7 @@ void RenderingWindow::draw(FrameBuffer *fb) { // VAO is not shared between multiple contexts of different windows // so we have to create a new VAO for rendering the surface in this window - if (surface_ == 0) + if (surface_ == nullptr) surface_ = new WindowSurface; // calculate scaling factor of frame buffer inside window @@ -1089,12 +1193,8 @@ void RenderingWindow::draw(FrameBuffer *fb) // restore attribs Rendering::manager().popAttrib(); - - // swap buffer - glfwSwapBuffers(window_); } - // give back context ownership - glfwMakeContextCurrent(master_); + return true; } diff --git a/src/RenderingManager.h b/src/RenderingManager.h index c2a4f5d..62a2f83 100644 --- a/src/RenderingManager.h +++ b/src/RenderingManager.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,9 @@ class RenderingWindow uint fbo_; class WindowSurface *surface_; +protected: + void setTitle(const std::string &title = ""); + void setIcon(const std::string &resource); bool request_change_fullscreen_; void changeFullscreen_ (); void setFullscreen_(GLFWmonitor *mo); @@ -51,8 +55,7 @@ public: inline GLFWwindow *window() const { return window_; } bool init(int index, GLFWwindow *share = NULL); - void setIcon(const std::string &resource); - void setTitle(const std::string &title = ""); + void terminate(); // show window (fullscreen if needed) void show(); @@ -61,16 +64,20 @@ public: void makeCurrent(); // draw a framebuffer - void draw(FrameBuffer *fb); + bool draw(FrameBuffer *fb); + void swap(); // fullscreen bool isFullscreen (); void exitFullscreen (); void setFullscreen (std::string monitorname); void toggleFullscreen (); + // get monitor in which the window is + GLFWmonitor *monitor(); - // set geometry + // set geometry and decoration void setCoordinates(glm::ivec4 rect); + void setDecoration (bool on); // get width of rendering area int width(); @@ -78,20 +85,18 @@ public: int height(); // get aspect ratio of rendering area float aspectRatio(); + // high dpi monitor scaling + inline float dpiScale() const { return dpi_scale_; } // get number of pixels to render X milimeters in height int pixelsforRealHeight(float milimeters); - inline float dpiScale() const { return dpi_scale_; } - - // get monitor in which the window is - GLFWmonitor *monitor(); - glm::vec2 previous_size; }; class Rendering { friend class UserInterface; + friend class RenderingWindow; // Private Constructor Rendering(); @@ -120,7 +125,7 @@ public: // Post-loop termination void terminate(); - // add function to call during Draw + // add function to call during draw typedef void (* RenderingCallback)(void); void pushBackDrawCallback(RenderingCallback function); @@ -129,16 +134,6 @@ public: void popAttrib(); RenderingAttrib currentAttrib(); - // get hold on the windows - inline RenderingWindow& mainWindow() { return main_; } - inline RenderingWindow& outputWindow() { return output_; } - inline void setMainWindowTitle(const std::string t) { main_new_title_ = t; } - - // request screenshot - void requestScreenshot(); - // get Screenshot - class Screenshot *currentScreenshot(); - // get projection matrix (for sharers) => Views glm::mat4 Projection(); // unproject from window coordinate to scene @@ -146,6 +141,21 @@ public: // project from scene coordinate to window glm::vec2 project(glm::vec3 scene_coordinate, glm::mat4 modelview = glm::mat4(1.f), bool to_framebuffer = true); + // Application main window management + inline RenderingWindow& mainWindow() { return main_; } + inline void setMainWindowTitle(const std::string t) { main_new_title_ = t; } + // request screenshot + void requestScreenshot(); + // get Screenshot + class Screenshot *currentScreenshot(); + + // Rendering output windows management + inline RenderingWindow& outputWindow(size_t i) { return outputs_[i]; } + + // windows access (cannot fail; defaults to main window on invalid argument) + RenderingWindow* window(GLFWwindow *w); + RenderingWindow* window(int index); + // get hold on the monitors inline std::map monitors() { return monitors_geometry_; } // get which monitor contains this point @@ -162,9 +172,14 @@ public: static void LinkPipeline( GstPipeline *pipeline ); #endif -private: +protected: + // GLFW windows management + std::map windows_; - std::string glsl_version; + // file drop callback + static void FileDropped(GLFWwindow* main_window_, int path_count, const char* paths[]); + +private: // list of rendering attributes std::list draw_attributes_; @@ -172,20 +187,17 @@ private: // list of functions to call at each Draw std::list draw_callbacks_; + // windows RenderingWindow main_; std::string main_new_title_; - RenderingWindow output_; + std::vector outputs_; // monitors std::map monitors_geometry_; static void MonitorConnect(GLFWmonitor* monitor, int event); - // file drop callback - static void FileDropped(GLFWwindow* main_window_, int path_count, const char* paths[]); - Screenshot screenshot_; bool request_screenshot_; - }; diff --git a/src/Settings.cpp b/src/Settings.cpp index 00aa356..16781f9 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -94,6 +94,7 @@ void Settings::Save(uint64_t runtime) // Windows { XMLElement *windowsNode = xmlDoc.NewElement( "Windows" ); + windowsNode->SetAttribute("num_output_windows", application.num_output_windows); for (int i = 0; i < (int) application.windows.size(); ++i) { @@ -108,6 +109,7 @@ void Settings::Save(uint64_t runtime) window->SetAttribute("h", w.h); window->SetAttribute("f", w.fullscreen); window->SetAttribute("s", w.scaled); + window->SetAttribute("d", w.decorated); window->SetAttribute("m", w.monitor.c_str()); windowsNode->InsertEndChild(window); } @@ -494,6 +496,8 @@ void Settings::Load() XMLElement * pElement = pRoot->FirstChildElement("Windows"); if (pElement) { + pElement->QueryIntAttribute("num_output_windows", &application.num_output_windows); + XMLElement* windowNode = pElement->FirstChildElement("Window"); for( ; windowNode ; windowNode=windowNode->NextSiblingElement()) { @@ -504,13 +508,17 @@ void Settings::Load() windowNode->QueryIntAttribute("h", &w.h); windowNode->QueryBoolAttribute("f", &w.fullscreen); windowNode->QueryBoolAttribute("s", &w.scaled); + windowNode->QueryBoolAttribute("d", &w.decorated); const char *text = windowNode->Attribute("m"); if (text) w.monitor = std::string(text); int i = 0; windowNode->QueryIntAttribute("id", &i); - w.name = application.windows[i].name; // keep only original name + if (i > 0) + w.name = APP_NAME " output " + std::to_string(i); + else + w.name = APP_TITLE; application.windows[i] = w; } } diff --git a/src/Settings.h b/src/Settings.h index f627c96..679aefe 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -61,9 +61,11 @@ struct WindowConfig int x,y,w,h; bool fullscreen; bool scaled; + bool decorated; std::string monitor; - WindowConfig() : name(""), x(15), y(15), w(1280), h(720), fullscreen(false), scaled(false), monitor("") { } + WindowConfig() : name(APP_TITLE), x(15), y(15), w(1280), h(720), + fullscreen(false), scaled(false), decorated(true), monitor("") { } }; @@ -299,6 +301,7 @@ struct Application ControllerConfig control; // multiple windows handling + int num_output_windows; std::vector windows; // recent files histories @@ -334,12 +337,16 @@ struct Application current_view = 1; current_workspace= 1; brush = glm::vec3(0.5f, 0.1f, 0.f); - windows = std::vector(3); - windows[0].name = APP_TITLE; + num_output_windows = 1; + windows = std::vector(1+MAX_OUTPUT_WINDOW); windows[0].w = 1600; windows[0].h = 900; - windows[1].name = "Output " APP_TITLE; - windows[2].name = "Fullscreen " APP_TITLE; + windows[1].w = 1270; + windows[1].h = 720; + windows[2].w = 1270; + windows[2].h = 720; + windows[3].w = 1270; + windows[3].h = 720; } }; diff --git a/src/UserInterfaceManager.cpp b/src/UserInterfaceManager.cpp index 8b0d438..7038026 100644 --- a/src/UserInterfaceManager.cpp +++ b/src/UserInterfaceManager.cpp @@ -162,7 +162,7 @@ bool UserInterface::Init() // Setup Platform/Renderer bindings ImGui_ImplGlfw_InitForOpenGL(Rendering::manager().mainWindow().window(), true); - ImGui_ImplOpenGL3_Init(Rendering::manager().glsl_version.c_str()); + ImGui_ImplOpenGL3_Init(VIMIX_GLSL_VERSION); // hack to change keys according to keyboard layout io.KeyMap[ImGuiKey_A] = Control::layoutKey(GLFW_KEY_A); @@ -1497,7 +1497,7 @@ void ToolBox::Render() // init if (refresh_rate < 0.f) { - const GLFWvidmode* mode = glfwGetVideoMode(Rendering::manager().outputWindow().monitor()); + const GLFWvidmode* mode = glfwGetVideoMode(Rendering::manager().mainWindow().monitor()); refresh_rate = float(mode->refreshRate); if (Settings::application.render.vsync > 0) refresh_rate /= Settings::application.render.vsync; @@ -4137,8 +4137,9 @@ void OutputPreview::Render() if (ImGui::BeginMenu(IMGUI_TITLE_PREVIEW)) { // Output window menu - if ( ImGui::MenuItem( ICON_FA_WINDOW_RESTORE " Show window") ) - Rendering::manager().outputWindow().show(); +// TODO Menu options for output windows creation / show ? +// if ( ImGui::MenuItem( ICON_FA_WINDOW_RESTORE " Show window") ) +// Rendering::manager().outputWindow().show(); ImGui::MenuItem( MENU_OUTPUTDISABLE, SHORTCUT_OUTPUTDISABLE, &Settings::application.render.disabled); @@ -4368,9 +4369,6 @@ void OutputPreview::Render() ImGui::PopStyleVar(); // mouse over the image if ( ImGui::IsItemHovered() ) { - // raise window on double clic - if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) ) - Rendering::manager().outputWindow().show(); // show magnifying glass if active if (magnifying_glass) DrawInspector(output->texture(), imagesize, imagesize, draw_pos); @@ -4467,7 +4465,7 @@ void OutputPreview::Render() if (Settings::application.render.disabled) { ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + r, draw_pos.y + imagesize.y - 2.f*r)); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_FRAME, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_WINDOW, 0.8f)); ImGui::Text(ICON_FA_EYE_SLASH); ImGui::PopStyleColor(1); } diff --git a/src/defines.h b/src/defines.h index 27deead..4d408f6 100644 --- a/src/defines.h +++ b/src/defines.h @@ -8,6 +8,10 @@ #define XML_VERSION_MINOR 3 #define MAX_RECENT_HISTORY 20 #define MAX_SESSION_LEVEL 3 +#define MAX_OUTPUT_WINDOW 3 + +#define VIMIX_GL_VERSION "opengl3" +#define VIMIX_GLSL_VERSION "#version 150" #define VIMIX_FILE_EXT "mix" #define VIMIX_FILE_PATTERN "*.mix" @@ -110,7 +114,8 @@ #define COLOR_LIMBO_CIRCLE 0.173f, 0.173f, 0.173f #define COLOR_SLIDER_CIRCLE 0.11f, 0.11f, 0.11f #define COLOR_STASH_CIRCLE 0.06f, 0.06f, 0.06f -#define COLOR_MONITOR 0.2f, 0.85f, 0.85f +#define COLOR_MONITOR 0.90f, 0.90f, 0.90f +#define COLOR_WINDOW 0.2f, 0.85f, 0.85f #define COLOR_MENU_HOVERED 0.3f, 0.3f, 0.3f #define OSC_PORT_RECV_DEFAULT 7000