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.
This commit is contained in:
Bruno Herbelin
2023-02-26 23:04:38 +01:00
parent b11f6d5b3b
commit c3bb29182e
10 changed files with 961 additions and 684 deletions

View File

@@ -24,6 +24,7 @@
#include <iomanip> #include <iomanip>
#include <regex> #include <regex>
#include <GLFW/glfw3.h>
#include "osc/OscOutboundPacketStream.h" #include "osc/OscOutboundPacketStream.h"
#include "Log.h" #include "Log.h"
@@ -36,10 +37,7 @@
#include "ActionManager.h" #include "ActionManager.h"
#include "TransitionView.h" #include "TransitionView.h"
#include "NetworkToolkit.h" #include "NetworkToolkit.h"
#include "UserInterfaceManager.h" #include "UserInterfaceManager.h"
#include "RenderingManager.h"
#include <GLFW/glfw3.h>
#include "ControlManager.h" #include "ControlManager.h"
@@ -378,14 +376,6 @@ bool Control::init()
// //
terminate(); 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 // 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 ) 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 ) else if (_key == GLFW_KEY_ESCAPE && action == GLFW_PRESS )
{ {
static GLFWwindow *output = Rendering::manager().outputWindow().window(); Rendering::manager().window(w)->exitFullscreen();
if (window==output)
Rendering::manager().outputWindow().exitFullscreen();
} }
Control::manager().input_access_.unlock(); Control::manager().input_access_.unlock();
} }

View File

@@ -96,6 +96,8 @@ class GLFWwindow;
class Control class Control
{ {
friend class RenderingWindow;
// Private Constructor // Private Constructor
Control(); Control();
Control(Control const& copy) = delete; Control(Control const& copy) = delete;
@@ -151,6 +153,8 @@ protected:
void sendBatchStatus(const IpEndpointName& remoteEndpoint); void sendBatchStatus(const IpEndpointName& remoteEndpoint);
void sendOutputStatus(const IpEndpointName& remoteEndpoint); void sendOutputStatus(const IpEndpointName& remoteEndpoint);
static void keyboardCalback(GLFWwindow*, int, int, int, int);
private: private:
static void listen(); static void listen();
@@ -169,7 +173,6 @@ private:
int multitouch_active[INPUT_MULTITOUCH_COUNT]; int multitouch_active[INPUT_MULTITOUCH_COUNT];
glm::vec2 multitouch_values[INPUT_MULTITOUCH_COUNT]; glm::vec2 multitouch_values[INPUT_MULTITOUCH_COUNT];
static void keyboardCalback(GLFWwindow*, int, int, int, int);
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,48 @@
#include "View.h" #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 class DisplaysView : public View
{ {
public: public:
@@ -23,31 +65,43 @@ public:
void initiate () override; void initiate () override;
void terminate (bool force = false) override; void terminate (bool force = false) override;
Cursor grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick) override; Cursor grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick) override;
Cursor over (glm::vec2) override;
void arrow (glm::vec2) override; void arrow (glm::vec2) override;
bool doubleclic (glm::vec2) override; bool doubleclic (glm::vec2) override;
glm::ivec4 outputCoordinates() const; glm::ivec4 windowCoordinates(int index) const;
std::string outputFullscreenMonitor() const; std::string fullscreenMonitor(int index) const;
private: 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_; bool draw_pending_;
float output_ar;
std::vector<WindowPreview> 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_;
}; };

View File

@@ -67,6 +67,7 @@
#include "SystemToolkit.h" #include "SystemToolkit.h"
#include "GstToolkit.h" #include "GstToolkit.h"
#include "UserInterfaceManager.h" #include "UserInterfaceManager.h"
#include "ControlManager.h"
#include "RenderingManager.h" #include "RenderingManager.h"
@@ -120,8 +121,6 @@ void Rendering::LinkPipeline( GstPipeline *pipeline )
#endif #endif
std::map<GLFWwindow *, RenderingWindow*> GLFW_window_;
static void glfw_error_callback(int error, const char* description) static void glfw_error_callback(int error, const char* description)
{ {
g_printerr("Glfw Error %d: %s\n", error, 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) { if (Rendering::manager().mainWindow().window() == w) {
// UI manager tries to keep windows in the workspace // 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); WorkspaceWindow::notifyWorkspaceSizeChanged(Rendering::manager().mainWindow().previous_size.x, Rendering::manager().mainWindow().previous_size.y, width, height);
GLFW_window_[w]->previous_size = glm::vec2(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; Settings::application.windows[id].fullscreen = glfwGetWindowMonitor(w) != nullptr;
if (!Settings::application.windows[id].fullscreen) { if (!Settings::application.windows[id].fullscreen) {
Settings::application.windows[id].w = width; 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) 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) { if (!Settings::application.windows[id].fullscreen) {
Settings::application.windows[id].x = x; Settings::application.windows[id].x = x;
Settings::application.windows[id].y = y; 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 // exit fullscreen if its the case
if (glfwGetWindowMonitor(w) != nullptr) if (glfwGetWindowMonitor(w) != nullptr)
Rendering::manager().outputWindow().exitFullscreen(); Rendering::manager().window(w)->exitFullscreen();
// show main window in DISPLAYS view to // show main window in DISPLAYS view to
// indicate how to manipulate output window // indicate how to manipulate output window
@@ -203,9 +202,6 @@ void Rendering::MonitorConnect(GLFWmonitor* monitor, int event)
std::string n = glfwGetMonitorName(monitors[i]); std::string n = glfwGetMonitorName(monitors[i]);
// add // add
Rendering::manager().monitors_geometry_[n] = glm::ivec4(x, y, vm->width, vm->height); 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 // inform Displays View that monitors changed
@@ -285,33 +281,15 @@ Rendering::Rendering()
bool Rendering::init() bool Rendering::init()
{ {
// Setup window //
// Setup GLFW
//
glfwSetErrorCallback(glfw_error_callback); glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()){ if (!glfwInit()){
g_printerr("Failed to Initialize GLFW.\n"); g_printerr("Failed to Initialize GLFW.\n");
return false; 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 // Gstreamer setup
// //
@@ -349,7 +327,9 @@ bool Rendering::init()
Log::Info("Found Frei0r plugins in %s", frei0r_path.c_str()); Log::Info("Found Frei0r plugins in %s", frei0r_path.c_str());
g_setenv ("FREI0R_PATH", frei0r_path.c_str(), TRUE); 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); gst_init (NULL, NULL);
// increase selection rank for GPU decoding plugins // increase selection rank for GPU decoding plugins
@@ -368,6 +348,7 @@ bool Rendering::init()
else { else {
Log::Info("No hardware decoding plugin found."); Log::Info("No hardware decoding plugin found.");
} }
#ifdef SYNC_GSTREAMER_OPENGL_CONTEXT #ifdef SYNC_GSTREAMER_OPENGL_CONTEXT
#if GST_GL_HAVE_PLATFORM_WGL #if GST_GL_HAVE_PLATFORM_WGL
global_gl_context = gst_gl_context_new_wrapped (display, (guintptr) wglGetCurrentContext (), global_gl_context = gst_gl_context_new_wrapped (display, (guintptr) wglGetCurrentContext (),
@@ -388,29 +369,48 @@ bool Rendering::init()
#endif #endif
// //
// output window // Monitors
//
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
// //
Rendering::MonitorConnect(nullptr, GLFW_DONT_CARE); Rendering::MonitorConnect(nullptr, GLFW_DONT_CARE);
// automatic detection of monitor connect & disconnect // automatic detection of monitor connect & disconnect
glfwSetMonitorCallback(Rendering::MonitorConnect); glfwSetMonitorCallback(Rendering::MonitorConnect);
//
// Main window
//
if ( !main_.init(0) )
return false;
//
// Output windows will be initialized in draw
//
outputs_ = std::vector<RenderingWindow>(MAX_OUTPUT_WINDOW);
return true; 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() void Rendering::show()
{ {
// show output window // show output windows
output_.show(); for (auto it = outputs_.begin(); it != outputs_.end(); ++it)
it->show();
// show main window // show main window
main_.show(); main_.show();
@@ -440,7 +440,8 @@ void Rendering::draw()
// change windows fullscreen mode if requested // change windows fullscreen mode if requested
main_.changeFullscreen_(); main_.changeFullscreen_();
output_.changeFullscreen_(); for (auto it = outputs_.begin(); it != outputs_.end(); ++it)
it->changeFullscreen_();
// change main window title if requested // change main window title if requested
if (!main_new_title_.empty()) { if (!main_new_title_.empty()) {
@@ -464,6 +465,20 @@ void Rendering::draw()
request_screenshot_ = false; 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 // software framerate limiter < 62 FPS
{ {
static GTimer *timer = g_timer_new (); static GTimer *timer = g_timer_new ();
@@ -474,18 +489,19 @@ void Rendering::draw()
} }
// swap GL buffers // swap GL buffers
glfwSwapBuffers(main_.window()); main_.swap();
for (auto it = outputs_.begin(); it != outputs_.end(); ++it)
// draw output window (and swap buffer output) it->swap();
output_.draw( Mixer::manager().session()->frame() );
} }
void Rendering::terminate() void Rendering::terminate()
{ {
// close window // terminate all windows
glfwDestroyWindow(output_.window()); for (auto it = outputs_.begin(); it != outputs_.end(); ++it)
glfwDestroyWindow(main_.window()); it->terminate();
main_.terminate();
// glfwTerminate(); // 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) index_(-1), dpi_scale_(1.f), textureid_(0), fbo_(0), surface_(nullptr), request_change_fullscreen_(false)
{ {
} }
RenderingWindow::~RenderingWindow() RenderingWindow::~RenderingWindow()
{ {
if (surface_ != nullptr) if (window_ != NULL)
delete surface_; terminate();
if (fbo_ != 0)
glDeleteFramebuffers(1, &fbo_);
} }
void RenderingWindow::setTitle(const std::string &title) void RenderingWindow::setTitle(const std::string &title)
@@ -695,7 +709,7 @@ void RenderingWindow::setTitle(const std::string &title)
else else
fulltitle = title + std::string(" - " APP_NAME); fulltitle = title + std::string(" - " APP_NAME);
if (window_ != nullptr) if (window_ != NULL)
glfwSetWindowTitle(window_, fulltitle.c_str()); glfwSetWindowTitle(window_, fulltitle.c_str());
} }
@@ -703,7 +717,7 @@ void RenderingWindow::setIcon(const std::string &resource)
{ {
size_t fpsize = 0; size_t fpsize = 0;
const char *fp = Resource::getData(resource, &fpsize); const char *fp = Resource::getData(resource, &fpsize);
if (fp != nullptr && window_ != nullptr) { if (fp != nullptr && window_) {
GLFWimage icon; GLFWimage icon;
icon.pixels = stbi_load_from_memory( (const stbi_uc*)fp, fpsize, &icon.width, &icon.height, nullptr, 4 ); icon.pixels = stbi_load_from_memory( (const stbi_uc*)fp, fpsize, &icon.width, &icon.height, nullptr, 4 );
glfwSetWindowIcon( window_, 1, &icon ); glfwSetWindowIcon( window_, 1, &icon );
@@ -715,7 +729,7 @@ GLFWmonitor *RenderingWindow::monitor()
{ {
// get monitor at the center of the window // get monitor at the center of the window
int x = 0, y = 0, w = 2, h = 2; int x = 0, y = 0, w = 2, h = 2;
if (window_ != nullptr) { if (window_) {
glfwGetWindowSize(window_, &w, &h); glfwGetWindowSize(window_, &w, &h);
glfwGetWindowPos(window_, &x, &y); glfwGetWindowPos(window_, &x, &y);
} }
@@ -724,7 +738,7 @@ GLFWmonitor *RenderingWindow::monitor()
void RenderingWindow::setFullscreen_(GLFWmonitor *mo) void RenderingWindow::setFullscreen_(GLFWmonitor *mo)
{ {
if (window_ == nullptr) if (!window_)
return; return;
// disable fullscreen mode // disable fullscreen mode
@@ -734,7 +748,7 @@ void RenderingWindow::setFullscreen_(GLFWmonitor *mo)
// set to window mode // set to window mode
glfwSetInputMode( window_, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 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_].y,
Settings::application.windows[index_].w, Settings::application.windows[index_].w,
Settings::application.windows[index_].h, 0 ); 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) 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); glfwSetWindowPos( window_, rect.x, rect.y);
} }
@@ -849,19 +880,38 @@ float RenderingWindow::aspectRatio()
bool RenderingWindow::init(int index, GLFWwindow *share) bool RenderingWindow::init(int index, GLFWwindow *share)
{ {
if (window_)
return false;
glfwMakeContextCurrent(NULL);
///
/// Settings
///
index_ = index; index_ = index;
master_ = share; master_ = share;
// access Settings
Settings::WindowConfig winset = Settings::application.windows[index_]; 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 // do not show at creation
glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE); glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE);
if (master_ != nullptr) { // restore decoration state
// special window type for output if (master_ != NULL && !winset.decorated) {
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
} }
@@ -875,25 +925,49 @@ bool RenderingWindow::init(int index, GLFWwindow *share)
// ensure minimal window size // ensure minimal window size
glfwSetWindowSizeLimits(window_, 500, 500, GLFW_DONT_CARE, GLFW_DONT_CARE); glfwSetWindowSizeLimits(window_, 500, 500, GLFW_DONT_CARE, GLFW_DONT_CARE);
previous_size = glm::vec2(winset.w, winset.h); previous_size = glm::vec2(winset.w, winset.h);
// set initial position // set initial position
glfwSetWindowPos(window_, winset.x, winset.y); glfwSetWindowPos(window_, winset.x, winset.y);
// set icon
setIcon("images/vimix_256x256.png");
///
/// CALLBACKS /// CALLBACKS
///
// store global ref to pointers (used by callbacks) // store global ref to pointers (used by callbacks)
GLFW_window_[window_] = this; Rendering::manager().windows_[window_] = this;
//
// window position and resize callbacks // window position and resize callbacks
//
glfwSetWindowPosCallback( window_, WindowMoveCallback ); glfwSetWindowPosCallback( window_, WindowMoveCallback );
glfwSetWindowSizeCallback( window_, WindowResizeCallback ); 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 // take opengl context ownership
glfwMakeContextCurrent(window_); glfwMakeContextCurrent(window_);
//
// Initialize OpenGL loader on first call // Initialize OpenGL loader on first call
//
static bool glad_initialized = false; static bool glad_initialized = false;
if ( !glad_initialized ) { if ( !glad_initialized ) {
bool err = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress) == 0; bool err = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress) == 0;
@@ -920,19 +994,17 @@ bool RenderingWindow::init(int index, GLFWwindow *share)
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
// if not main window // if not main window
if ( master_ != nullptr ) { if ( master_ != NULL ) {
// Enable vsync on output window // NO vsync on output windows
glfwSwapInterval(Settings::application.render.vsync); glfwSwapInterval(0);
// no need for multisampling // no need for multisampling
glDisable(GL_MULTISAMPLE); glDisable(GL_MULTISAMPLE);
// clear to black // clear to black
window_attributes_.clear_color = glm::vec4(0.f, 0.f, 0.f, 1.f); window_attributes_.clear_color = glm::vec4(0.f, 0.f, 0.f, 1.f);
// give back context ownership
glfwMakeContextCurrent(master_);
} }
else { else {
// Disable vsync on main window // vsync on main window
glfwSwapInterval(0); glfwSwapInterval(Settings::application.render.vsync);
// Enable Antialiasing multisampling // Enable Antialiasing multisampling
if (Settings::application.render.multisampling > 0) { if (Settings::application.render.multisampling > 0) {
glEnable(GL_MULTISAMPLE); glEnable(GL_MULTISAMPLE);
@@ -945,20 +1017,46 @@ bool RenderingWindow::init(int index, GLFWwindow *share)
return true; 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() void RenderingWindow::show()
{ {
if (!window_)
return;
glfwShowWindow(window_); glfwShowWindow(window_);
if ( Settings::application.windows[index_].fullscreen ) { if ( Settings::application.windows[index_].fullscreen ) {
GLFWmonitor *mo = Rendering::manager().monitorNamed(Settings::application.windows[index_].monitor); GLFWmonitor *mo = Rendering::manager().monitorNamed(Settings::application.windows[index_].monitor);
setFullscreen_(mo); setFullscreen_(mo);
} }
} }
void RenderingWindow::makeCurrent() void RenderingWindow::makeCurrent()
{ {
if (!window_)
return;
// handle window resize // handle window resize
glfwGetFramebufferSize(window_, &(window_attributes_.viewport.x), &(window_attributes_.viewport.y)); 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); 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) if (!window_ || !fb)
return; return false;
// only draw if window is not iconified // only draw if window is not iconified
if( !glfwGetWindowAttrib(window_, GLFW_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 // 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 // so we have to create a new VAO for rendering the surface in this window
if (surface_ == 0) if (surface_ == nullptr)
surface_ = new WindowSurface; surface_ = new WindowSurface;
// calculate scaling factor of frame buffer inside window // calculate scaling factor of frame buffer inside window
@@ -1089,12 +1193,8 @@ void RenderingWindow::draw(FrameBuffer *fb)
// restore attribs // restore attribs
Rendering::manager().popAttrib(); Rendering::manager().popAttrib();
// swap buffer
glfwSwapBuffers(window_);
} }
// give back context ownership return true;
glfwMakeContextCurrent(master_);
} }

View File

@@ -4,6 +4,7 @@
#include <string> #include <string>
#include <list> #include <list>
#include <map> #include <map>
#include <vector>
#include <gst/gl/gl.h> #include <gst/gl/gl.h>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@@ -38,6 +39,9 @@ class RenderingWindow
uint fbo_; uint fbo_;
class WindowSurface *surface_; class WindowSurface *surface_;
protected:
void setTitle(const std::string &title = "");
void setIcon(const std::string &resource);
bool request_change_fullscreen_; bool request_change_fullscreen_;
void changeFullscreen_ (); void changeFullscreen_ ();
void setFullscreen_(GLFWmonitor *mo); void setFullscreen_(GLFWmonitor *mo);
@@ -51,8 +55,7 @@ public:
inline GLFWwindow *window() const { return window_; } inline GLFWwindow *window() const { return window_; }
bool init(int index, GLFWwindow *share = NULL); bool init(int index, GLFWwindow *share = NULL);
void setIcon(const std::string &resource); void terminate();
void setTitle(const std::string &title = "");
// show window (fullscreen if needed) // show window (fullscreen if needed)
void show(); void show();
@@ -61,16 +64,20 @@ public:
void makeCurrent(); void makeCurrent();
// draw a framebuffer // draw a framebuffer
void draw(FrameBuffer *fb); bool draw(FrameBuffer *fb);
void swap();
// fullscreen // fullscreen
bool isFullscreen (); bool isFullscreen ();
void exitFullscreen (); void exitFullscreen ();
void setFullscreen (std::string monitorname); void setFullscreen (std::string monitorname);
void toggleFullscreen (); void toggleFullscreen ();
// get monitor in which the window is
GLFWmonitor *monitor();
// set geometry // set geometry and decoration
void setCoordinates(glm::ivec4 rect); void setCoordinates(glm::ivec4 rect);
void setDecoration (bool on);
// get width of rendering area // get width of rendering area
int width(); int width();
@@ -78,20 +85,18 @@ public:
int height(); int height();
// get aspect ratio of rendering area // get aspect ratio of rendering area
float aspectRatio(); float aspectRatio();
// high dpi monitor scaling
inline float dpiScale() const { return dpi_scale_; }
// get number of pixels to render X milimeters in height // get number of pixels to render X milimeters in height
int pixelsforRealHeight(float milimeters); int pixelsforRealHeight(float milimeters);
inline float dpiScale() const { return dpi_scale_; }
// get monitor in which the window is
GLFWmonitor *monitor();
glm::vec2 previous_size; glm::vec2 previous_size;
}; };
class Rendering class Rendering
{ {
friend class UserInterface; friend class UserInterface;
friend class RenderingWindow;
// Private Constructor // Private Constructor
Rendering(); Rendering();
@@ -120,7 +125,7 @@ public:
// Post-loop termination // Post-loop termination
void terminate(); void terminate();
// add function to call during Draw // add function to call during draw
typedef void (* RenderingCallback)(void); typedef void (* RenderingCallback)(void);
void pushBackDrawCallback(RenderingCallback function); void pushBackDrawCallback(RenderingCallback function);
@@ -129,16 +134,6 @@ public:
void popAttrib(); void popAttrib();
RenderingAttrib currentAttrib(); 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 // get projection matrix (for sharers) => Views
glm::mat4 Projection(); glm::mat4 Projection();
// unproject from window coordinate to scene // unproject from window coordinate to scene
@@ -146,6 +141,21 @@ public:
// project from scene coordinate to window // project from scene coordinate to window
glm::vec2 project(glm::vec3 scene_coordinate, glm::mat4 modelview = glm::mat4(1.f), bool to_framebuffer = true); 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 // get hold on the monitors
inline std::map<std::string, glm::ivec4> monitors() { return monitors_geometry_; } inline std::map<std::string, glm::ivec4> monitors() { return monitors_geometry_; }
// get which monitor contains this point // get which monitor contains this point
@@ -162,9 +172,14 @@ public:
static void LinkPipeline( GstPipeline *pipeline ); static void LinkPipeline( GstPipeline *pipeline );
#endif #endif
private: protected:
// GLFW windows management
std::map<GLFWwindow *, RenderingWindow*> 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 // list of rendering attributes
std::list<RenderingAttrib> draw_attributes_; std::list<RenderingAttrib> draw_attributes_;
@@ -172,20 +187,17 @@ private:
// list of functions to call at each Draw // list of functions to call at each Draw
std::list<RenderingCallback> draw_callbacks_; std::list<RenderingCallback> draw_callbacks_;
// windows
RenderingWindow main_; RenderingWindow main_;
std::string main_new_title_; std::string main_new_title_;
RenderingWindow output_; std::vector<RenderingWindow> outputs_;
// monitors // monitors
std::map<std::string, glm::ivec4> monitors_geometry_; std::map<std::string, glm::ivec4> monitors_geometry_;
static void MonitorConnect(GLFWmonitor* monitor, int event); static void MonitorConnect(GLFWmonitor* monitor, int event);
// file drop callback
static void FileDropped(GLFWwindow* main_window_, int path_count, const char* paths[]);
Screenshot screenshot_; Screenshot screenshot_;
bool request_screenshot_; bool request_screenshot_;
}; };

View File

@@ -94,6 +94,7 @@ void Settings::Save(uint64_t runtime)
// Windows // Windows
{ {
XMLElement *windowsNode = xmlDoc.NewElement( "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) 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("h", w.h);
window->SetAttribute("f", w.fullscreen); window->SetAttribute("f", w.fullscreen);
window->SetAttribute("s", w.scaled); window->SetAttribute("s", w.scaled);
window->SetAttribute("d", w.decorated);
window->SetAttribute("m", w.monitor.c_str()); window->SetAttribute("m", w.monitor.c_str());
windowsNode->InsertEndChild(window); windowsNode->InsertEndChild(window);
} }
@@ -494,6 +496,8 @@ void Settings::Load()
XMLElement * pElement = pRoot->FirstChildElement("Windows"); XMLElement * pElement = pRoot->FirstChildElement("Windows");
if (pElement) if (pElement)
{ {
pElement->QueryIntAttribute("num_output_windows", &application.num_output_windows);
XMLElement* windowNode = pElement->FirstChildElement("Window"); XMLElement* windowNode = pElement->FirstChildElement("Window");
for( ; windowNode ; windowNode=windowNode->NextSiblingElement()) for( ; windowNode ; windowNode=windowNode->NextSiblingElement())
{ {
@@ -504,13 +508,17 @@ void Settings::Load()
windowNode->QueryIntAttribute("h", &w.h); windowNode->QueryIntAttribute("h", &w.h);
windowNode->QueryBoolAttribute("f", &w.fullscreen); windowNode->QueryBoolAttribute("f", &w.fullscreen);
windowNode->QueryBoolAttribute("s", &w.scaled); windowNode->QueryBoolAttribute("s", &w.scaled);
windowNode->QueryBoolAttribute("d", &w.decorated);
const char *text = windowNode->Attribute("m"); const char *text = windowNode->Attribute("m");
if (text) if (text)
w.monitor = std::string(text); w.monitor = std::string(text);
int i = 0; int i = 0;
windowNode->QueryIntAttribute("id", &i); 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; application.windows[i] = w;
} }
} }

View File

@@ -61,9 +61,11 @@ struct WindowConfig
int x,y,w,h; int x,y,w,h;
bool fullscreen; bool fullscreen;
bool scaled; bool scaled;
bool decorated;
std::string monitor; 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; ControllerConfig control;
// multiple windows handling // multiple windows handling
int num_output_windows;
std::vector<WindowConfig> windows; std::vector<WindowConfig> windows;
// recent files histories // recent files histories
@@ -334,12 +337,16 @@ struct Application
current_view = 1; current_view = 1;
current_workspace= 1; current_workspace= 1;
brush = glm::vec3(0.5f, 0.1f, 0.f); brush = glm::vec3(0.5f, 0.1f, 0.f);
windows = std::vector<WindowConfig>(3); num_output_windows = 1;
windows[0].name = APP_TITLE; windows = std::vector<WindowConfig>(1+MAX_OUTPUT_WINDOW);
windows[0].w = 1600; windows[0].w = 1600;
windows[0].h = 900; windows[0].h = 900;
windows[1].name = "Output " APP_TITLE; windows[1].w = 1270;
windows[2].name = "Fullscreen " APP_TITLE; windows[1].h = 720;
windows[2].w = 1270;
windows[2].h = 720;
windows[3].w = 1270;
windows[3].h = 720;
} }
}; };

View File

@@ -162,7 +162,7 @@ bool UserInterface::Init()
// Setup Platform/Renderer bindings // Setup Platform/Renderer bindings
ImGui_ImplGlfw_InitForOpenGL(Rendering::manager().mainWindow().window(), true); 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 // hack to change keys according to keyboard layout
io.KeyMap[ImGuiKey_A] = Control::layoutKey(GLFW_KEY_A); io.KeyMap[ImGuiKey_A] = Control::layoutKey(GLFW_KEY_A);
@@ -1497,7 +1497,7 @@ void ToolBox::Render()
// init // init
if (refresh_rate < 0.f) { 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); refresh_rate = float(mode->refreshRate);
if (Settings::application.render.vsync > 0) if (Settings::application.render.vsync > 0)
refresh_rate /= Settings::application.render.vsync; refresh_rate /= Settings::application.render.vsync;
@@ -4137,8 +4137,9 @@ void OutputPreview::Render()
if (ImGui::BeginMenu(IMGUI_TITLE_PREVIEW)) if (ImGui::BeginMenu(IMGUI_TITLE_PREVIEW))
{ {
// Output window menu // Output window menu
if ( ImGui::MenuItem( ICON_FA_WINDOW_RESTORE " Show window") ) // TODO Menu options for output windows creation / show ?
Rendering::manager().outputWindow().show(); // if ( ImGui::MenuItem( ICON_FA_WINDOW_RESTORE " Show window") )
// Rendering::manager().outputWindow().show();
ImGui::MenuItem( MENU_OUTPUTDISABLE, SHORTCUT_OUTPUTDISABLE, &Settings::application.render.disabled); ImGui::MenuItem( MENU_OUTPUTDISABLE, SHORTCUT_OUTPUTDISABLE, &Settings::application.render.disabled);
@@ -4368,9 +4369,6 @@ void OutputPreview::Render()
ImGui::PopStyleVar(); ImGui::PopStyleVar();
// mouse over the image // mouse over the image
if ( ImGui::IsItemHovered() ) { if ( ImGui::IsItemHovered() ) {
// raise window on double clic
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) )
Rendering::manager().outputWindow().show();
// show magnifying glass if active // show magnifying glass if active
if (magnifying_glass) if (magnifying_glass)
DrawInspector(output->texture(), imagesize, imagesize, draw_pos); DrawInspector(output->texture(), imagesize, imagesize, draw_pos);
@@ -4467,7 +4465,7 @@ void OutputPreview::Render()
if (Settings::application.render.disabled) if (Settings::application.render.disabled)
{ {
ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + r, draw_pos.y + imagesize.y - 2.f*r)); 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::Text(ICON_FA_EYE_SLASH);
ImGui::PopStyleColor(1); ImGui::PopStyleColor(1);
} }

View File

@@ -8,6 +8,10 @@
#define XML_VERSION_MINOR 3 #define XML_VERSION_MINOR 3
#define MAX_RECENT_HISTORY 20 #define MAX_RECENT_HISTORY 20
#define MAX_SESSION_LEVEL 3 #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_EXT "mix"
#define VIMIX_FILE_PATTERN "*.mix" #define VIMIX_FILE_PATTERN "*.mix"
@@ -110,7 +114,8 @@
#define COLOR_LIMBO_CIRCLE 0.173f, 0.173f, 0.173f #define COLOR_LIMBO_CIRCLE 0.173f, 0.173f, 0.173f
#define COLOR_SLIDER_CIRCLE 0.11f, 0.11f, 0.11f #define COLOR_SLIDER_CIRCLE 0.11f, 0.11f, 0.11f
#define COLOR_STASH_CIRCLE 0.06f, 0.06f, 0.06f #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 COLOR_MENU_HOVERED 0.3f, 0.3f, 0.3f
#define OSC_PORT_RECV_DEFAULT 7000 #define OSC_PORT_RECV_DEFAULT 7000