mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-08 08:50:00 +01:00
Added Rendering Window class and output window in Rendering. Cleanup and
refactoring.
This commit is contained in:
@@ -112,14 +112,14 @@ void FrameBuffer::begin()
|
|||||||
{
|
{
|
||||||
bind();
|
bind();
|
||||||
|
|
||||||
Rendering::manager().PushAttrib(attrib_);
|
Rendering::manager().pushAttrib(attrib_);
|
||||||
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameBuffer::end()
|
void FrameBuffer::end()
|
||||||
{
|
{
|
||||||
Rendering::manager().PopAttrib();
|
Rendering::manager().popAttrib();
|
||||||
|
|
||||||
FrameBuffer::release();
|
FrameBuffer::release();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,16 +52,49 @@
|
|||||||
static GstGLContext *global_gl_context = NULL;
|
static GstGLContext *global_gl_context = NULL;
|
||||||
static GstGLDisplay *global_display = NULL;
|
static GstGLDisplay *global_display = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
void updateSettings(int id, GLFWwindow *w)
|
||||||
|
{
|
||||||
|
if ( !Settings::application.windows[id].fullscreen) {
|
||||||
|
int x, y;
|
||||||
|
glfwGetWindowPos(w, &x, &y);
|
||||||
|
Settings::application.windows[id].x = x;
|
||||||
|
Settings::application.windows[id].y = y;
|
||||||
|
glfwGetWindowSize(w, &x, &y);
|
||||||
|
Settings::application.windows[id].w = x;
|
||||||
|
Settings::application.windows[id].h = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void glfw_error_callback(int error, const char* description)
|
static void glfw_error_callback(int error, const char* description)
|
||||||
{
|
{
|
||||||
Log::Error("Glfw Error %d: %s", error, description);
|
Log::Error("Glfw Error %d: %s", error, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WindowRefreshCallback( GLFWwindow* )
|
static void WindowRefreshCallback( GLFWwindow *w )
|
||||||
{
|
{
|
||||||
Rendering::manager().Draw();
|
Rendering::manager().draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void WindowResizeCallback( GLFWwindow *w, int width, int height)
|
||||||
|
{
|
||||||
|
int id = Rendering::manager().getWindowId(w);
|
||||||
|
if ( id > 0 && !Settings::application.windows[id].fullscreen) {
|
||||||
|
Settings::application.windows[id].w = width;
|
||||||
|
Settings::application.windows[id].h = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WindowMoveCallback( GLFWwindow *w, int x, int y)
|
||||||
|
{
|
||||||
|
int id = Rendering::manager().getWindowId(w);
|
||||||
|
if ( id > 0 && !Settings::application.windows[id].fullscreen) {
|
||||||
|
Settings::application.windows[id].x = x;
|
||||||
|
Settings::application.windows[id].y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Rendering::Rendering()
|
Rendering::Rendering()
|
||||||
{
|
{
|
||||||
main_window_ = nullptr;
|
main_window_ = nullptr;
|
||||||
@@ -69,7 +102,7 @@ Rendering::Rendering()
|
|||||||
dpi_scale_ = 1.f;
|
dpi_scale_ = 1.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Rendering::Init()
|
bool Rendering::init()
|
||||||
{
|
{
|
||||||
// Setup window
|
// Setup window
|
||||||
glfwSetErrorCallback(glfw_error_callback);
|
glfwSetErrorCallback(glfw_error_callback);
|
||||||
@@ -86,16 +119,14 @@ bool Rendering::Init()
|
|||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
Settings::WindowConfig winset = Settings::application.windows.front();
|
|
||||||
|
|
||||||
// Create window with graphics context
|
|
||||||
// GL Multisampling #3
|
// GL Multisampling #3
|
||||||
glfwWindowHint(GLFW_SAMPLES, 3);
|
glfwWindowHint(GLFW_SAMPLES, 3);
|
||||||
|
|
||||||
|
// Create window with graphics context
|
||||||
|
Settings::WindowConfig winset = Settings::application.windows[0];
|
||||||
|
|
||||||
// do not show at creation
|
// do not show at creation
|
||||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||||
// glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
|
|
||||||
main_window_ = glfwCreateWindow(winset.w, winset.h, winset.name.c_str(), NULL, NULL);
|
main_window_ = glfwCreateWindow(winset.w, winset.h, winset.name.c_str(), NULL, NULL);
|
||||||
if (main_window_ == NULL){
|
if (main_window_ == NULL){
|
||||||
Log::Error("Failed to Create GLFW Window.");
|
Log::Error("Failed to Create GLFW Window.");
|
||||||
@@ -112,10 +143,14 @@ bool Rendering::Init()
|
|||||||
free( icon.pixels );
|
free( icon.pixels );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// callbacks
|
||||||
|
glfwSetWindowRefreshCallback( main_window_, WindowRefreshCallback );
|
||||||
|
glfwSetFramebufferSizeCallback( main_window_, WindowResizeCallback );
|
||||||
|
glfwSetWindowPosCallback( main_window_, WindowMoveCallback );
|
||||||
|
|
||||||
glfwSetWindowPos(main_window_, winset.x, winset.y);
|
glfwSetWindowPos(main_window_, winset.x, winset.y);
|
||||||
glfwMakeContextCurrent(main_window_);
|
glfwMakeContextCurrent(main_window_);
|
||||||
glfwSwapInterval(1); // Enable vsync3
|
glfwSwapInterval(1); // Enable vsync
|
||||||
glfwSetWindowRefreshCallback( main_window_, WindowRefreshCallback );
|
|
||||||
|
|
||||||
// Initialize OpenGL loader
|
// Initialize OpenGL loader
|
||||||
bool err = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress) == 0;
|
bool err = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress) == 0;
|
||||||
@@ -128,7 +163,7 @@ bool Rendering::Init()
|
|||||||
glfwShowWindow(main_window_);
|
glfwShowWindow(main_window_);
|
||||||
// restore fullscreen
|
// restore fullscreen
|
||||||
if (winset.fullscreen)
|
if (winset.fullscreen)
|
||||||
ToggleFullscreen();
|
toggleFullscreen();
|
||||||
|
|
||||||
// Rendering area (here same as window)
|
// Rendering area (here same as window)
|
||||||
glfwGetFramebufferSize(main_window_, &(main_window_attributes_.viewport.x), &(main_window_attributes_.viewport.y));
|
glfwGetFramebufferSize(main_window_, &(main_window_attributes_.viewport.x), &(main_window_attributes_.viewport.y));
|
||||||
@@ -191,6 +226,11 @@ bool Rendering::Init()
|
|||||||
// file drop callback
|
// file drop callback
|
||||||
glfwSetDropCallback(main_window_, Rendering::FileDropped);
|
glfwSetDropCallback(main_window_, Rendering::FileDropped);
|
||||||
|
|
||||||
|
// output window
|
||||||
|
output.init(main_window_, 1);
|
||||||
|
|
||||||
|
glfwMakeContextCurrent(main_window_);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,12 +239,22 @@ bool Rendering::isActive()
|
|||||||
return !glfwWindowShouldClose(main_window_);
|
return !glfwWindowShouldClose(main_window_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rendering::GrabWindow(int dx, int dy)
|
//void Rendering::GrabWindow(int dx, int dy)
|
||||||
|
//{
|
||||||
|
// int xpos = 0;
|
||||||
|
// int ypos = 0;
|
||||||
|
// glfwGetWindowPos(main_window_, &xpos, &ypos);
|
||||||
|
// glfwSetWindowPos(main_window_, xpos + dx, ypos + dy);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
int Rendering::getWindowId(GLFWwindow *w)
|
||||||
{
|
{
|
||||||
int xpos = 0;
|
if (w == main_window_)
|
||||||
int ypos = 0;
|
return 0;
|
||||||
glfwGetWindowPos(main_window_, &xpos, &ypos);
|
else
|
||||||
glfwSetWindowPos(main_window_, xpos + dx, ypos + dy);
|
return 1;
|
||||||
|
// TODO manage more than one output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -214,18 +264,25 @@ void Rendering::setWindowTitle(std::string title)
|
|||||||
glfwSetWindowTitle(main_window_, window_title.c_str());
|
glfwSetWindowTitle(main_window_, window_title.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rendering::PushFrontDrawCallback(RenderingCallback function)
|
void Rendering::pushFrontDrawCallback(RenderingCallback function)
|
||||||
{
|
{
|
||||||
draw_callbacks_.push_front(function);
|
draw_callbacks_.push_front(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rendering::PushBackDrawCallback(RenderingCallback function)
|
void Rendering::pushBackDrawCallback(RenderingCallback function)
|
||||||
{
|
{
|
||||||
draw_callbacks_.push_back(function);
|
draw_callbacks_.push_back(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rendering::Draw()
|
void Rendering::draw()
|
||||||
{
|
{
|
||||||
|
// Poll and handle events (inputs, window resize, etc.)
|
||||||
|
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||||
|
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
|
||||||
|
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
|
||||||
|
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||||
|
glfwPollEvents();
|
||||||
|
|
||||||
if ( Begin() )
|
if ( Begin() )
|
||||||
{
|
{
|
||||||
UserInterface::manager().NewFrame();
|
UserInterface::manager().NewFrame();
|
||||||
@@ -243,23 +300,21 @@ void Rendering::Draw()
|
|||||||
// no g_main_loop_run(loop) : update global GMainContext
|
// no g_main_loop_run(loop) : update global GMainContext
|
||||||
g_main_context_iteration(NULL, FALSE);
|
g_main_context_iteration(NULL, FALSE);
|
||||||
|
|
||||||
|
// draw output window
|
||||||
|
output.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Rendering::Begin()
|
bool Rendering::Begin()
|
||||||
{
|
{
|
||||||
// Poll and handle events (inputs, window resize, etc.)
|
// ensure main context is current
|
||||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
|
||||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
|
|
||||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
|
|
||||||
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
|
||||||
glfwPollEvents();
|
|
||||||
|
|
||||||
glfwMakeContextCurrent(main_window_);
|
glfwMakeContextCurrent(main_window_);
|
||||||
if( glfwGetWindowAttrib( main_window_, GLFW_ICONIFIED ) )
|
|
||||||
{
|
// TODO : optimize if window is minimized? (i.e. render output only)
|
||||||
std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
|
// if( glfwGetWindowAttrib( main_window_, GLFW_ICONIFIED ) )
|
||||||
return false;
|
// {
|
||||||
}
|
// std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
// handle window resize
|
// handle window resize
|
||||||
glfwGetFramebufferSize(main_window_, &(main_window_attributes_.viewport.x), &(main_window_attributes_.viewport.y));
|
glfwGetFramebufferSize(main_window_, &(main_window_attributes_.viewport.x), &(main_window_attributes_.viewport.y));
|
||||||
@@ -275,7 +330,7 @@ bool Rendering::Begin()
|
|||||||
|
|
||||||
void Rendering::End()
|
void Rendering::End()
|
||||||
{
|
{
|
||||||
glfwMakeContextCurrent(main_window_);
|
// glfwMakeContextCurrent(main_window_);
|
||||||
|
|
||||||
// perform screenshot if requested
|
// perform screenshot if requested
|
||||||
if (request_screenshot_) {
|
if (request_screenshot_) {
|
||||||
@@ -288,18 +343,10 @@ void Rendering::End()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Rendering::Terminate()
|
void Rendering::terminate()
|
||||||
{
|
{
|
||||||
// settings
|
// save pos
|
||||||
if ( !Settings::application.windows.front().fullscreen) {
|
updateSettings(0, main_window_);
|
||||||
int x, y;
|
|
||||||
glfwGetWindowPos(main_window_, &x, &y);
|
|
||||||
Settings::application.windows.front().x = x;
|
|
||||||
Settings::application.windows.front().y = y;
|
|
||||||
glfwGetWindowSize(main_window_,&x, &y);
|
|
||||||
Settings::application.windows.front().w = x;
|
|
||||||
Settings::application.windows.front().h = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// close window
|
// close window
|
||||||
glfwDestroyWindow(main_window_);
|
glfwDestroyWindow(main_window_);
|
||||||
@@ -307,13 +354,13 @@ void Rendering::Terminate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Rendering::Close()
|
void Rendering::close()
|
||||||
{
|
{
|
||||||
glfwSetWindowShouldClose(main_window_, true);
|
glfwSetWindowShouldClose(main_window_, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Rendering::PushAttrib(RenderingAttrib ra)
|
void Rendering::pushAttrib(RenderingAttrib ra)
|
||||||
{
|
{
|
||||||
// push it to top of pile
|
// push it to top of pile
|
||||||
draw_attributes_.push_front(ra);
|
draw_attributes_.push_front(ra);
|
||||||
@@ -323,7 +370,7 @@ void Rendering::PushAttrib(RenderingAttrib ra)
|
|||||||
glClearColor(ra.clear_color.r, ra.clear_color.g, ra.clear_color.b, ra.clear_color.a);
|
glClearColor(ra.clear_color.r, ra.clear_color.g, ra.clear_color.b, ra.clear_color.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rendering::PopAttrib()
|
void Rendering::popAttrib()
|
||||||
{
|
{
|
||||||
// pops the top of the pile
|
// pops the top of the pile
|
||||||
if (draw_attributes_.size() > 0)
|
if (draw_attributes_.size() > 0)
|
||||||
@@ -350,7 +397,7 @@ RenderingAttrib Rendering::currentAttrib()
|
|||||||
glm::mat4 Rendering::Projection()
|
glm::mat4 Rendering::Projection()
|
||||||
{
|
{
|
||||||
static glm::mat4 projection = glm::ortho(-SCENE_UNIT, SCENE_UNIT, -SCENE_UNIT, SCENE_UNIT, -SCENE_DEPTH, 1.f);
|
static glm::mat4 projection = glm::ortho(-SCENE_UNIT, SCENE_UNIT, -SCENE_UNIT, SCENE_UNIT, -SCENE_DEPTH, 1.f);
|
||||||
glm::mat4 scale = glm::scale(glm::identity<glm::mat4>(), glm::vec3(1.f, AspectRatio(), 1.f));
|
glm::mat4 scale = glm::scale(glm::identity<glm::mat4>(), glm::vec3(1.f, aspectRatio(), 1.f));
|
||||||
|
|
||||||
return projection * scale;
|
return projection * scale;
|
||||||
}
|
}
|
||||||
@@ -368,10 +415,10 @@ glm::vec3 Rendering::unProject(glm::vec2 screen_coordinate, glm::mat4 modelview)
|
|||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Rendering::Width() { return main_window_attributes_.viewport.x; }
|
float Rendering::width() { return main_window_attributes_.viewport.x; }
|
||||||
float Rendering::Height() { return main_window_attributes_.viewport.y; }
|
float Rendering::height() { return main_window_attributes_.viewport.y; }
|
||||||
|
|
||||||
float Rendering::MonitorWidth()
|
float Rendering::monitorWidth()
|
||||||
{
|
{
|
||||||
GLFWmonitor *monitor = glfwGetWindowMonitor (main_window_);
|
GLFWmonitor *monitor = glfwGetWindowMonitor (main_window_);
|
||||||
if (!monitor)
|
if (!monitor)
|
||||||
@@ -382,7 +429,7 @@ float Rendering::MonitorWidth()
|
|||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Rendering::MonitorHeight()
|
float Rendering::monitorHeight()
|
||||||
{
|
{
|
||||||
GLFWmonitor *monitor = glfwGetWindowMonitor (main_window_);
|
GLFWmonitor *monitor = glfwGetWindowMonitor (main_window_);
|
||||||
if (!monitor)
|
if (!monitor)
|
||||||
@@ -396,32 +443,26 @@ float Rendering::MonitorHeight()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool Rendering::IsFullscreen ()
|
bool Rendering::isFullscreen ()
|
||||||
{
|
{
|
||||||
return (glfwGetWindowMonitor(main_window_) != nullptr);
|
return (glfwGetWindowMonitor(main_window_) != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rendering::ToggleFullscreen()
|
void Rendering::toggleFullscreen()
|
||||||
{
|
{
|
||||||
// if in fullscreen mode
|
// if in fullscreen mode
|
||||||
if (IsFullscreen()) {
|
if (isFullscreen()) {
|
||||||
// set to window mode
|
// set to window mode
|
||||||
glfwSetWindowMonitor( main_window_, nullptr, Settings::application.windows.front().x,
|
glfwSetWindowMonitor( main_window_, nullptr, Settings::application.windows[0].x,
|
||||||
Settings::application.windows.front().y,
|
Settings::application.windows[0].y,
|
||||||
Settings::application.windows.front().w,
|
Settings::application.windows[0].w,
|
||||||
Settings::application.windows.front().h, 0 );
|
Settings::application.windows[0].h, 0 );
|
||||||
Settings::application.windows.front().fullscreen = false;
|
Settings::application.windows[0].fullscreen = false;
|
||||||
}
|
}
|
||||||
// not in fullscreen mode
|
// not in fullscreen mode
|
||||||
else {
|
else {
|
||||||
// remember window geometry
|
// remember window geometry
|
||||||
int x, y;
|
updateSettings(0, main_window_);
|
||||||
glfwGetWindowPos(main_window_, &x, &y);
|
|
||||||
Settings::application.windows.front().x = x;
|
|
||||||
Settings::application.windows.front().y = y;
|
|
||||||
glfwGetWindowSize(main_window_,&x, &y);
|
|
||||||
Settings::application.windows.front().w = x;
|
|
||||||
Settings::application.windows.front().h = y;
|
|
||||||
|
|
||||||
// select monitor
|
// select monitor
|
||||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||||
@@ -429,12 +470,12 @@ void Rendering::ToggleFullscreen()
|
|||||||
|
|
||||||
// set to fullscreen mode
|
// set to fullscreen mode
|
||||||
glfwSetWindowMonitor( main_window_, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
|
glfwSetWindowMonitor( main_window_, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
|
||||||
Settings::application.windows.front().fullscreen = true;
|
Settings::application.windows[0].fullscreen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Rendering::AspectRatio()
|
float Rendering::aspectRatio()
|
||||||
{
|
{
|
||||||
return static_cast<float>(main_window_attributes_.viewport.x) / static_cast<float>(main_window_attributes_.viewport.y);
|
return static_cast<float>(main_window_attributes_.viewport.x) / static_cast<float>(main_window_attributes_.viewport.y);
|
||||||
}
|
}
|
||||||
@@ -451,18 +492,95 @@ void Rendering::FileDropped(GLFWwindow *, int path_count, const char* paths[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Screenshot *Rendering::CurrentScreenshot()
|
Screenshot *Rendering::currentScreenshot()
|
||||||
{
|
{
|
||||||
return &screenshot_;
|
return &screenshot_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rendering::RequestScreenshot()
|
void Rendering::requestScreenshot()
|
||||||
{
|
{
|
||||||
screenshot_.Clear();
|
screenshot_.Clear();
|
||||||
request_screenshot_ = true;
|
request_screenshot_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
RenderingWindow::RenderingWindow() : window_(nullptr), frame_buffer_(nullptr), id_(-1)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderingWindow::~RenderingWindow()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenderingWindow::init(GLFWwindow *share, int id)
|
||||||
|
{
|
||||||
|
id_ = id;
|
||||||
|
master_ = share;
|
||||||
|
|
||||||
|
Settings::WindowConfig winset = Settings::application.windows[id_];
|
||||||
|
|
||||||
|
// do not show at creation
|
||||||
|
glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
|
||||||
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||||
|
window_ = glfwCreateWindow(winset.w, winset.h, winset.name.c_str(), NULL, master_);
|
||||||
|
if (window_ == NULL){
|
||||||
|
Log::Error("Failed to create GLFW Window %d", id_);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
glfwSetWindowPos(window_, winset.x, winset.y);
|
||||||
|
|
||||||
|
// callbacks
|
||||||
|
glfwSetFramebufferSizeCallback( window_, WindowResizeCallback );
|
||||||
|
glfwSetWindowPosCallback( window_, WindowMoveCallback );
|
||||||
|
|
||||||
|
// take context ownership
|
||||||
|
glfwMakeContextCurrent(window_);
|
||||||
|
glfwSwapInterval(0); // Disable vsync
|
||||||
|
|
||||||
|
// setup endering area
|
||||||
|
window_attributes_.viewport.x = winset.w;
|
||||||
|
window_attributes_.viewport.y = winset.h;
|
||||||
|
window_attributes_.clear_color = glm::vec4(0.f, 0.f, 0.f, 1.0);
|
||||||
|
|
||||||
|
glfwShowWindow(window_);
|
||||||
|
|
||||||
|
// give back context ownership
|
||||||
|
glfwMakeContextCurrent(master_);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderingWindow::draw()
|
||||||
|
{
|
||||||
|
if (!window_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// only draw if window is not iconified
|
||||||
|
if( !glfwGetWindowAttrib(window_, GLFW_ICONIFIED ) ) {
|
||||||
|
|
||||||
|
// take context ownership
|
||||||
|
glfwMakeContextCurrent(window_);
|
||||||
|
|
||||||
|
// render some stuff
|
||||||
|
glfwGetFramebufferSize(window_, &(window_attributes_.viewport.x), &(window_attributes_.viewport.y));
|
||||||
|
glViewport(0, 0, window_attributes_.viewport.x, window_attributes_.viewport.y);
|
||||||
|
|
||||||
|
glClearColor(window_attributes_.clear_color.r, window_attributes_.clear_color.g,
|
||||||
|
window_attributes_.clear_color.b, window_attributes_.clear_color.a);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
// swap buffer
|
||||||
|
glfwSwapBuffers(window_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// give back context ownership
|
||||||
|
glfwMakeContextCurrent(master_);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Discarded because not working under OSX - kept in case it would become useful
|
// Discarded because not working under OSX - kept in case it would become useful
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
|
|
||||||
#include "Screenshot.h"
|
#include "Screenshot.h"
|
||||||
|
|
||||||
|
class GLFWwindow;
|
||||||
|
class FrameBuffer;
|
||||||
|
|
||||||
struct RenderingAttrib
|
struct RenderingAttrib
|
||||||
{
|
{
|
||||||
RenderingAttrib() {}
|
RenderingAttrib() {}
|
||||||
@@ -16,16 +19,28 @@ struct RenderingAttrib
|
|||||||
glm::vec4 clear_color;
|
glm::vec4 clear_color;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RenderingWindow
|
||||||
|
{
|
||||||
|
GLFWwindow *window_, *master_;
|
||||||
|
RenderingAttrib window_attributes_;
|
||||||
|
FrameBuffer *frame_buffer_;
|
||||||
|
int id_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RenderingWindow();
|
||||||
|
~RenderingWindow();
|
||||||
|
|
||||||
|
void setFrameBuffer(FrameBuffer *fb) { frame_buffer_ = fb; }
|
||||||
|
|
||||||
|
bool init(GLFWwindow *share, int id);
|
||||||
|
void draw();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class Rendering
|
class Rendering
|
||||||
{
|
{
|
||||||
friend class UserInterface;
|
friend class UserInterface;
|
||||||
|
|
||||||
// GLFW integration in OS window management
|
|
||||||
class GLFWwindow* main_window_;
|
|
||||||
std::string glsl_version;
|
|
||||||
float dpi_scale_;
|
|
||||||
|
|
||||||
// Private Constructor
|
// Private Constructor
|
||||||
Rendering();
|
Rendering();
|
||||||
Rendering(Rendering const& copy); // Not Implemented
|
Rendering(Rendering const& copy); // Not Implemented
|
||||||
@@ -41,47 +56,46 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialization OpenGL and GLFW window creation
|
// Initialization OpenGL and GLFW window creation
|
||||||
bool Init();
|
bool init();
|
||||||
// true if active rendering window
|
// true if active rendering window
|
||||||
bool isActive();
|
bool isActive();
|
||||||
// draw one frame
|
// draw one frame
|
||||||
void Draw();
|
void draw();
|
||||||
// request close of the UI (Quit the program)
|
// request close of the UI (Quit the program)
|
||||||
void Close();
|
void close();
|
||||||
// Post-loop termination
|
// Post-loop termination
|
||||||
void Terminate();
|
void terminate();
|
||||||
|
|
||||||
void GrabWindow(int dx, int dy);
|
|
||||||
|
|
||||||
// add function to call during Draw
|
// add function to call during Draw
|
||||||
typedef void (* RenderingCallback)(void);
|
typedef void (* RenderingCallback)(void);
|
||||||
void PushFrontDrawCallback(RenderingCallback function);
|
void pushFrontDrawCallback(RenderingCallback function);
|
||||||
void PushBackDrawCallback(RenderingCallback function);
|
void pushBackDrawCallback(RenderingCallback function);
|
||||||
|
|
||||||
// push and pop rendering attributes
|
// push and pop rendering attributes
|
||||||
void PushAttrib(RenderingAttrib ra);
|
void pushAttrib(RenderingAttrib ra);
|
||||||
void PopAttrib();
|
void popAttrib();
|
||||||
RenderingAttrib currentAttrib();
|
RenderingAttrib currentAttrib();
|
||||||
|
|
||||||
// request screenshot
|
// request screenshot
|
||||||
void RequestScreenshot();
|
void requestScreenshot();
|
||||||
// get Screenshot
|
// get Screenshot
|
||||||
class Screenshot *CurrentScreenshot();
|
class Screenshot *currentScreenshot();
|
||||||
|
|
||||||
// window management
|
// window management
|
||||||
void setWindowTitle(std::string title);
|
void setWindowTitle(std::string title);
|
||||||
// request fullscreen
|
// request fullscreen
|
||||||
bool IsFullscreen ();
|
bool isFullscreen ();
|
||||||
void ToggleFullscreen ();
|
void toggleFullscreen ();
|
||||||
// get width of rendering area
|
// get width of rendering area
|
||||||
float Width();
|
float width();
|
||||||
// get height of rendering area
|
// get height of rendering area
|
||||||
float Height();
|
float height();
|
||||||
// get aspect ratio of rendering area
|
// get aspect ratio of rendering area
|
||||||
float AspectRatio();
|
float aspectRatio();
|
||||||
|
|
||||||
// monitor management
|
// monitor management
|
||||||
float MonitorWidth();
|
float monitorWidth();
|
||||||
float MonitorHeight();
|
float monitorHeight();
|
||||||
inline float DPIScale() const { return dpi_scale_; }
|
inline float DPIScale() const { return dpi_scale_; }
|
||||||
|
|
||||||
// get projection matrix (for sharers) => Views
|
// get projection matrix (for sharers) => Views
|
||||||
@@ -89,13 +103,23 @@ public:
|
|||||||
// unproject from window coordinate
|
// unproject from window coordinate
|
||||||
glm::vec3 unProject(glm::vec2 screen_coordinate, glm::mat4 modelview = glm::mat4(1.f));
|
glm::vec3 unProject(glm::vec2 screen_coordinate, glm::mat4 modelview = glm::mat4(1.f));
|
||||||
|
|
||||||
|
// utility for settings
|
||||||
|
int getWindowId(GLFWwindow *w);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
// GLFW integration in OS window management
|
||||||
|
GLFWwindow *main_window_;
|
||||||
|
std::string glsl_version;
|
||||||
|
float dpi_scale_;
|
||||||
|
|
||||||
// loop update to begin new frame
|
// loop update to begin new frame
|
||||||
bool Begin();
|
bool Begin();
|
||||||
// loop update end frame
|
// loop update end frame
|
||||||
void End();
|
void End();
|
||||||
|
|
||||||
|
// void GrabWindow(int dx, int dy);
|
||||||
|
|
||||||
// list of rendering attributes
|
// list of rendering attributes
|
||||||
std::list<RenderingAttrib> draw_attributes_;
|
std::list<RenderingAttrib> draw_attributes_;
|
||||||
RenderingAttrib main_window_attributes_;
|
RenderingAttrib main_window_attributes_;
|
||||||
@@ -103,6 +127,8 @@ 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_;
|
||||||
|
|
||||||
|
RenderingWindow output;
|
||||||
|
|
||||||
// file drop callback
|
// file drop callback
|
||||||
static void FileDropped(GLFWwindow* main_window_, int path_count, const char* paths[]);
|
static void FileDropped(GLFWwindow* main_window_, int path_count, const char* paths[]);
|
||||||
|
|
||||||
|
|||||||
29
Settings.cpp
29
Settings.cpp
@@ -32,18 +32,19 @@ void Settings::Save()
|
|||||||
{
|
{
|
||||||
XMLElement *windowsNode = xmlDoc.NewElement( "Windows" );
|
XMLElement *windowsNode = xmlDoc.NewElement( "Windows" );
|
||||||
|
|
||||||
vector<Settings::WindowConfig>::iterator iter;
|
for (int i = 0; i < application.windows.size(); i++)
|
||||||
for (iter=application.windows.begin(); iter != application.windows.end(); iter++)
|
{
|
||||||
{
|
const Settings::WindowConfig& w = application.windows[i];
|
||||||
const Settings::WindowConfig& w=*iter;
|
|
||||||
|
|
||||||
XMLElement *window = xmlDoc.NewElement( "Window" );
|
XMLElement *window = xmlDoc.NewElement( "Window" );
|
||||||
window->SetAttribute("name", w.name.c_str());
|
window->SetAttribute("id", i);
|
||||||
|
window->SetAttribute("name", w.name.c_str());
|
||||||
window->SetAttribute("x", w.x);
|
window->SetAttribute("x", w.x);
|
||||||
window->SetAttribute("y", w.y);
|
window->SetAttribute("y", w.y);
|
||||||
window->SetAttribute("w", w.w);
|
window->SetAttribute("w", w.w);
|
||||||
window->SetAttribute("h", w.h);
|
window->SetAttribute("h", w.h);
|
||||||
window->SetAttribute("f", w.fullscreen);
|
window->SetAttribute("f", w.fullscreen);
|
||||||
|
window->SetAttribute("m", w.monitor);
|
||||||
windowsNode->InsertEndChild(window);
|
windowsNode->InsertEndChild(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,25 +168,25 @@ void Settings::Load()
|
|||||||
pElement->QueryIntAttribute("framebuffer_h", &application.framebuffer_h);
|
pElement->QueryIntAttribute("framebuffer_h", &application.framebuffer_h);
|
||||||
|
|
||||||
// bloc windows
|
// bloc windows
|
||||||
{
|
{
|
||||||
application.windows.clear(); // trash existing list
|
|
||||||
|
|
||||||
XMLElement * pElement = pRoot->FirstChildElement("Windows");
|
XMLElement * pElement = pRoot->FirstChildElement("Windows");
|
||||||
if (pElement)
|
if (pElement)
|
||||||
{
|
{
|
||||||
XMLElement* windowNode = pElement->FirstChildElement("Window");
|
XMLElement* windowNode = pElement->FirstChildElement("Window");
|
||||||
for( ; windowNode ; windowNode=windowNode->NextSiblingElement())
|
for( ; windowNode ; windowNode=windowNode->NextSiblingElement())
|
||||||
{
|
{
|
||||||
const char *pName = windowNode->Attribute("name");
|
Settings::WindowConfig w;
|
||||||
Settings::WindowConfig w(pName);
|
w.name = std::string(windowNode->Attribute("name"));
|
||||||
|
|
||||||
windowNode->QueryIntAttribute("x", &w.x); // If this fails, original value is left as-is
|
windowNode->QueryIntAttribute("x", &w.x); // If this fails, original value is left as-is
|
||||||
windowNode->QueryIntAttribute("y", &w.y);
|
windowNode->QueryIntAttribute("y", &w.y);
|
||||||
windowNode->QueryIntAttribute("w", &w.w);
|
windowNode->QueryIntAttribute("w", &w.w);
|
||||||
windowNode->QueryIntAttribute("h", &w.h);
|
windowNode->QueryIntAttribute("h", &w.h);
|
||||||
windowNode->QueryBoolAttribute("f", &w.fullscreen);
|
windowNode->QueryBoolAttribute("f", &w.fullscreen);
|
||||||
|
windowNode->QueryIntAttribute("m", &w.monitor);
|
||||||
|
|
||||||
application.windows.push_back(w);
|
int i = 0;
|
||||||
|
windowNode->QueryIntAttribute("id", &i);
|
||||||
|
application.windows[i] = w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ struct WindowConfig
|
|||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
int x,y,w,h;
|
int x,y,w,h;
|
||||||
|
int monitor;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
|
|
||||||
WindowConfig(std::string n) : name(n), x(15), y(15), w(1280), h(720), fullscreen(false) { }
|
WindowConfig() : name(""), x(15), y(15), w(1280), h(720), monitor(0), fullscreen(false) { }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,7 +98,10 @@ struct Application
|
|||||||
current_view = 1;
|
current_view = 1;
|
||||||
framebuffer_ar = 3;
|
framebuffer_ar = 3;
|
||||||
framebuffer_h = 1;
|
framebuffer_h = 1;
|
||||||
windows.push_back(WindowConfig(APP_NAME APP_TITLE));
|
std::vector<int> second (4,100);
|
||||||
|
windows = std::vector<WindowConfig>(3);
|
||||||
|
windows[0].name = APP_NAME APP_TITLE;
|
||||||
|
windows[1].name = APP_NAME " Output";
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ bool UserInterface::Init()
|
|||||||
ImGuiToolkit::SetAccentColor(static_cast<ImGuiToolkit::accent_color>(Settings::application.accent_color));
|
ImGuiToolkit::SetAccentColor(static_cast<ImGuiToolkit::accent_color>(Settings::application.accent_color));
|
||||||
|
|
||||||
// Estalish the base size from the resolution of the monitor
|
// Estalish the base size from the resolution of the monitor
|
||||||
float base_font_size = (Rendering::manager().MonitorHeight() * Rendering::manager().DPIScale()) / 100.f ;
|
float base_font_size = (Rendering::manager().monitorHeight() * Rendering::manager().DPIScale()) / 100.f ;
|
||||||
// Load Fonts (using resource manager, NB: a temporary copy of the raw data is necessary)
|
// Load Fonts (using resource manager, NB: a temporary copy of the raw data is necessary)
|
||||||
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_DEFAULT, "Roboto-Regular", int(base_font_size) );
|
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_DEFAULT, "Roboto-Regular", int(base_font_size) );
|
||||||
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_BOLD, "Roboto-Bold", int(base_font_size) );
|
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_BOLD, "Roboto-Bold", int(base_font_size) );
|
||||||
@@ -169,7 +169,7 @@ bool UserInterface::Init()
|
|||||||
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_LARGE, "Hack-Regular", MIN(int(base_font_size * 1.5f), 50), 1 );
|
ImGuiToolkit::SetFont(ImGuiToolkit::FONT_LARGE, "Hack-Regular", MIN(int(base_font_size * 1.5f), 50), 1 );
|
||||||
|
|
||||||
// info
|
// info
|
||||||
Log::Info("Monitor (%.1f,%.1f)", Rendering::manager().MonitorWidth(), Rendering::manager().MonitorHeight());
|
Log::Info("Monitor (%.1f,%.1f)", Rendering::manager().monitorWidth(), Rendering::manager().monitorHeight());
|
||||||
Log::Info("Font size %d", int(base_font_size) );
|
Log::Info("Font size %d", int(base_font_size) );
|
||||||
|
|
||||||
// Style
|
// Style
|
||||||
@@ -214,7 +214,7 @@ void UserInterface::handleKeyboard()
|
|||||||
|
|
||||||
if (ImGui::IsKeyPressed( GLFW_KEY_Q )) {
|
if (ImGui::IsKeyPressed( GLFW_KEY_Q )) {
|
||||||
// Quit
|
// Quit
|
||||||
Rendering::manager().Close();
|
Rendering::manager().close();
|
||||||
}
|
}
|
||||||
else if (ImGui::IsKeyPressed( GLFW_KEY_O )) {
|
else if (ImGui::IsKeyPressed( GLFW_KEY_O )) {
|
||||||
// Open session
|
// Open session
|
||||||
@@ -262,7 +262,7 @@ void UserInterface::handleKeyboard()
|
|||||||
else if (ImGui::IsKeyPressed( GLFW_KEY_F3 ))
|
else if (ImGui::IsKeyPressed( GLFW_KEY_F3 ))
|
||||||
Mixer::manager().setCurrentView(View::LAYER);
|
Mixer::manager().setCurrentView(View::LAYER);
|
||||||
else if (ImGui::IsKeyPressed( GLFW_KEY_F11 ))
|
else if (ImGui::IsKeyPressed( GLFW_KEY_F11 ))
|
||||||
Rendering::manager().ToggleFullscreen();
|
Rendering::manager().toggleFullscreen();
|
||||||
else if (ImGui::IsKeyPressed( GLFW_KEY_F12 ))
|
else if (ImGui::IsKeyPressed( GLFW_KEY_F12 ))
|
||||||
StartScreenshot();
|
StartScreenshot();
|
||||||
// normal keys // make sure no entry / window box is active
|
// normal keys // make sure no entry / window box is active
|
||||||
@@ -275,8 +275,8 @@ void UserInterface::handleKeyboard()
|
|||||||
navigator.toggleMenu();
|
navigator.toggleMenu();
|
||||||
// esc to exit fullscreen
|
// esc to exit fullscreen
|
||||||
else if (ImGui::IsKeyPressed( GLFW_KEY_ESCAPE )){
|
else if (ImGui::IsKeyPressed( GLFW_KEY_ESCAPE )){
|
||||||
if (Rendering::manager().IsFullscreen())
|
if (Rendering::manager().isFullscreen())
|
||||||
Rendering::manager().ToggleFullscreen();
|
Rendering::manager().toggleFullscreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -550,7 +550,7 @@ void UserInterface::showMenuFile()
|
|||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::MenuItem( ICON_FA_POWER_OFF " Quit", CTRL_MOD "Q")) {
|
if (ImGui::MenuItem( ICON_FA_POWER_OFF " Quit", CTRL_MOD "Q")) {
|
||||||
Rendering::manager().Close();
|
Rendering::manager().close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,15 +581,15 @@ void UserInterface::handleScreenshot()
|
|||||||
screenshot_step = 2;
|
screenshot_step = 2;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
Rendering::manager().RequestScreenshot();
|
Rendering::manager().requestScreenshot();
|
||||||
screenshot_step = 3;
|
screenshot_step = 3;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
if ( Rendering::manager().CurrentScreenshot()->IsFull() ){
|
if ( Rendering::manager().currentScreenshot()->IsFull() ){
|
||||||
std::string filename = SystemToolkit::home_path() + SystemToolkit::date_time_string() + "_vmixcapture.png";
|
std::string filename = SystemToolkit::home_path() + SystemToolkit::date_time_string() + "_vmixcapture.png";
|
||||||
Rendering::manager().CurrentScreenshot()->SaveFile( filename.c_str() );
|
Rendering::manager().currentScreenshot()->SaveFile( filename.c_str() );
|
||||||
Rendering::manager().CurrentScreenshot()->Clear();
|
Rendering::manager().currentScreenshot()->Clear();
|
||||||
Log::Notify("Screenshot saved %s", filename.c_str() );
|
Log::Notify("Screenshot saved %s", filename.c_str() );
|
||||||
}
|
}
|
||||||
screenshot_step = 4;
|
screenshot_step = 4;
|
||||||
|
|||||||
2
View.cpp
2
View.cpp
@@ -361,7 +361,9 @@ View::Cursor GeometryView::grab (glm::vec2 from, glm::vec2 to, Source *s, std::p
|
|||||||
|
|
||||||
View::Cursor GeometryView::over (glm::vec2, Source*, std::pair<Node *, glm::vec2>)
|
View::Cursor GeometryView::over (glm::vec2, Source*, std::pair<Node *, glm::vec2>)
|
||||||
{
|
{
|
||||||
|
View::Cursor ret = Cursor_Arrow;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerView::LayerView() : View(LAYER), aspect_ratio(1.f)
|
LayerView::LayerView() : View(LAYER), aspect_ratio(1.f)
|
||||||
|
|||||||
8
main.cpp
8
main.cpp
@@ -47,7 +47,7 @@ int main(int, char**)
|
|||||||
///
|
///
|
||||||
/// RENDERING INIT
|
/// RENDERING INIT
|
||||||
///
|
///
|
||||||
if ( !Rendering::manager().Init() )
|
if ( !Rendering::manager().init() )
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -71,7 +71,7 @@ int main(int, char**)
|
|||||||
// UserInterface::manager().fillShaderEditor( Resource::getText("shaders/image.fs") );
|
// UserInterface::manager().fillShaderEditor( Resource::getText("shaders/image.fs") );
|
||||||
|
|
||||||
// draw the scene
|
// draw the scene
|
||||||
Rendering::manager().PushFrontDrawCallback(drawScene);
|
Rendering::manager().pushFrontDrawCallback(drawScene);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Main LOOP
|
/// Main LOOP
|
||||||
@@ -80,7 +80,7 @@ int main(int, char**)
|
|||||||
{
|
{
|
||||||
Mixer::manager().update();
|
Mixer::manager().update();
|
||||||
|
|
||||||
Rendering::manager().Draw();
|
Rendering::manager().draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -91,7 +91,7 @@ int main(int, char**)
|
|||||||
///
|
///
|
||||||
/// RENDERING TERMINATE
|
/// RENDERING TERMINATE
|
||||||
///
|
///
|
||||||
Rendering::manager().Terminate();
|
Rendering::manager().terminate();
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Settings
|
/// Settings
|
||||||
|
|||||||
Reference in New Issue
Block a user