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