Notice: If you are member of any public project or group, please make sure that your GitLab username is not the same as the LRZ identifier/Kennung (see https://gitlab.lrz.de/profile/account). Please change your username if necessary. For more information see the section "Public projects / Öffentliche Projekte" at https://doku.lrz.de/display/PUBLIC/GitLab . Thank you!

Commit 03041535 authored by Jakob Weiss's avatar Jakob Weiss

Fullscreen and Thread Names on Windows

* Debug Feature: Thread names for pipelines and some important active threads show in debugger
* fullscreen can be toggled per pipeline via the pipeline property and via ALT+Return key combination
parent 69d7ea4d
......@@ -46,6 +46,7 @@
#include "core/pipeline/abstractworkflow.h"
#include "core/pipeline/pipelinefactory.h"
#include "core/pipeline/pipelinepainter.h"
#include "core/pipeline/visualizationprocessor.h"
#include "core/datastructures/imagerepresentationconverter.h"
#include "core/pipeline/visualizationprocessor.h"
......@@ -275,6 +276,7 @@ namespace campvis {
#endif
GLCtxtMgr.releaseContext(canvas, false);
s_PipelinesChanged.emitSignal();
startOpenGlThreadAndMoveQtThreadAffinity(pipeline, canvas);
......
......@@ -26,6 +26,9 @@
#include "mdidockarea.h"
#include <QAction>
#include <QLayout>
#include <QMainWindow>
#include <cgt/qt/qtcanvas.h>
namespace campvis {
......@@ -36,6 +39,8 @@ namespace campvis {
, _dockedWindow(0)
, _floatingWindow(0)
, _toggleViewAction(0)
, _widget(widget)
, _fullScreenWidget(0)
{
this->setWindowFlags(windowFlags);
_dockedWindow = this->newDockedWindow(widget);
......@@ -44,6 +49,11 @@ namespace campvis {
_toggleViewAction->setCheckable(true);
_toggleViewAction->setChecked(false);
this->connect(_toggleViewAction, SIGNAL(toggled(bool)), SLOT(toggleWindowVisibility(bool)));
if (dynamic_cast<cgt::QtCanvas*>(widget)) {
connect(widget, SIGNAL(fullScreenChanged(bool)), this, SLOT(handleFullScreenChanged(bool)));
}
}
void MdiDockableWindow::setWindowTitle(const QString& title) {
......@@ -163,5 +173,32 @@ namespace campvis {
SLOT(trackFloatingWindowPosition(const QPoint&)));
}
}
void MdiDockableWindow::handleFullScreenChanged(bool fullscreen) {
if (_docked && _dockedWindow) {
if (fullscreen) {
_fullScreenWidget = new QMainWindow();
_fullScreenWidget->setCentralWidget(_widget);
_fullScreenWidget->showFullScreen();
}
else {
if (_dockedWindow->layout()) {
_dockedWindow->layout()->addWidget(_widget);
}
else {
setParent(_dockedWindow);
}
delete _fullScreenWidget;
_fullScreenWidget = nullptr;
}
}
if (!_docked && _floatingWindow) {
if (fullscreen)
_floatingWindow->showFullScreen();
else
_floatingWindow->showNormal();
}
}
}
......@@ -31,6 +31,8 @@
#include "application/gui/qtcolortools.h"
#include <QWidget>
class QMainWindow;
namespace campvis {
class MdiDockArea;
......@@ -111,6 +113,11 @@ namespace campvis {
*/
void handleWindowClosing();
/**
* Handle changing fullscreen state requested by the QtCanvas widget
*/
void handleFullScreenChanged(bool fullscreen);
private:
/**
* Create and return an MdiDockedWindow wrapping the \p widget.
......@@ -124,7 +131,8 @@ namespace campvis {
MdiDockedWindow* _dockedWindow; ///< The window's docked representation.
MdiFloatingWindow* _floatingWindow; ///< The window's floating representation.
QAction* _toggleViewAction; ///< A checkable action that can be used to show or hide this window.
QWidget* _widget; ///< Widget that was wrapped
QMainWindow* _fullScreenWidget; ///< Container widget used to display docked canvas in fullscreen
};
}
......
......@@ -49,6 +49,7 @@ namespace campvis {
, _canvasSize("CanvasSize", "Canvas Size", cgt::ivec2(128, 128), cgt::ivec2(1, 1), cgt::ivec2(4096, 4096))
, _ignoreCanvasSizeUpdate(false)
, _renderTargetID("renderTargetID", "Render Target ID", "AbstractPipeline.renderTarget", DataNameProperty::READ)
, p_showFullscreen("ShowFullscreen", "Show Fullscreen", false)
{
cgtAssert(_dataContainer != nullptr, "Pointer to the DataContainer for this pipeline must not be 0!");
......@@ -59,12 +60,15 @@ namespace campvis {
addProperty(_renderTargetID);
addProperty(_canvasSize);
addProperty(p_showFullscreen);
}
AbstractPipeline::~AbstractPipeline() {
}
void AbstractPipeline::init() {
setThreadName(std::string("Pipeline ") + getName());
_dataContainer->s_dataAdded.connect(this, &AbstractPipeline::onDataContainerDataAdded);
_painter->init();
......@@ -80,6 +84,8 @@ namespace campvis {
}
}
p_showFullscreen.s_changed.connect(this, &AbstractPipeline::onFullscreenChanged);
// use trigger signal to enforce blocking call
s_init.triggerSignal();
}
......@@ -148,6 +154,13 @@ namespace campvis {
}
}
void AbstractPipeline::onFullscreenChanged(const AbstractProperty * prop)
{
if (_canvas) {
_canvas->toggleFullScreen();
}
}
void AbstractPipeline::paint() {
// render nothing. May be overridden in sub classes.
}
......
......@@ -215,6 +215,9 @@ namespace campvis {
*/
IVec2Property& getCanvasSize() { return _canvasSize; }
/** Property indicating whether this pipeline should display fullscreen */
BoolProperty p_showFullscreen;
/**
* Returns this pipelines PipelinePainter.
* \return _painter
......@@ -256,11 +259,16 @@ namespace campvis {
/**
* Slot getting called when one of the observed properties changed and notifies its observers.
* The default behaviour is just to set the invalidation level to invalid.
* The default behavior is just to set the invalidation level to invalid.
* \param prop Property that emitted the signal
*/
virtual void onPropertyChanged(const AbstractProperty* prop);
/**
* Slot getting called when p_showFullscreen changes. Triggers a switch between fullscreen and non-fullscreen mode
* \param fullscreen Flag indicating whether the pipeline should display fullscreen
*/
virtual void onFullscreenChanged(const AbstractProperty * prop);
/// Pointer to the DataContainer containing local working set of data for this Pipeline, must not be 0.
DataContainer* _dataContainer;
......
......@@ -60,6 +60,8 @@ SET(CGT_SOURCES
# Qt related stuff:
IF(CGT_WITH_QT)
QT5_WRAP_CPP(qt/qtcanvas.h)
LIST(APPEND CGT_HEADERS
qt/qtcanvas.h
qt/qtthreadedcanvas.h
......@@ -68,7 +70,8 @@ IF(CGT_WITH_QT)
LIST(APPEND CGT_SOURCES
qt/qtcanvas.cpp
qt/qtthreadedcanvas.cpp
qt/qttimer.cpp)
qt/qttimer.cpp
${CGT_MOC_CPPS})
ENDIF(CGT_WITH_QT)
......
......@@ -37,6 +37,7 @@
namespace cgt {
OpenGLJobProcessor::OpenGLJobProcessor()
: RunnableWithConditionalWait("OpenGLJobProcessor")
{
_pause = 0;
_context= nullptr;
......
......@@ -64,6 +64,7 @@ QtCanvas::QtCanvas(const std::string& title,
stereoViewing_ = format().stereo();
connect(this, SIGNAL(s_sizeChangedExternally(int, int)), this, SLOT(sizeChangedExternally(int, int)));
connect(this, SIGNAL(fullScreenChanged(bool)), this, SLOT(changeFullScreen(bool)));
}
QtCanvas::QtCanvas(QWidget* parent, bool shared, Qt::WindowFlags f, char* /*name*/)
......@@ -123,12 +124,13 @@ void QtCanvas::swap() {
void QtCanvas::toggleFullScreen() {
if (fullscreen_) {
fullscreen_ = false;
showNormal();
//showNormal();
}
else {
showFullScreen();
//showFullScreen();
fullscreen_ = !fullscreen_;
}
emit fullScreenChanged(fullscreen_); // emit a Qt signal to process the GUI changes
};
/*
......@@ -190,8 +192,14 @@ void QtCanvas::wheelEvent(QWheelEvent* e) {
// See mousePressEvent
void QtCanvas::keyPressEvent(QKeyEvent* event) {
cgt::KeyEvent* ke = new cgt::KeyEvent(getKey(event->key()), getModifier(event), true);
eventHandler_->broadcastEvent(ke);
if (event->key() == Qt::Key_Return && event->modifiers() == Qt::Modifier::ALT) {
toggleFullScreen();
event->accept();
}
else {
cgt::KeyEvent* ke = new cgt::KeyEvent(getKey(event->key()), getModifier(event), true);
eventHandler_->broadcastEvent(ke);
}
}
// See mousePressEvent
......@@ -601,5 +609,4 @@ void QtCanvas::sizeChangedExternally(int w, int h) {
QWidget::resize(w, h);
}
} // namespace
......@@ -162,6 +162,9 @@ protected:
static QGLWidget* shareWidget_; ///< widget that this canvas shares the OpenGL context with
signals:
void fullScreenChanged(bool fullscreen);
};
......
......@@ -28,6 +28,17 @@
#include "runnable.h"
#ifdef WIN32
#include <Windows.h>
namespace {
void SetThreadName(HANDLE hThread, const char* threadName);
}
#else // WIN32 -> must be POSIX
#include <pthread.h>
#endif // WIN32
namespace cgt {
void invokeThread(Runnable* r) {
r->_initFunction();
......@@ -37,9 +48,10 @@ namespace cgt {
r->_running = false;
}
Runnable::Runnable()
Runnable::Runnable(const std::string& threadName /*= ""*/)
: _stopExecution()
, _thread(0)
, _threadName(threadName)
{
_initFunction = [] () {};
_deinitFunction = [] () {};
......@@ -74,12 +86,28 @@ namespace cgt {
_initFunction = initFunction;
_thread = new std::thread(&invokeThread, this);
_running = true;
// set the platform native thread name
if (!_threadName.empty()) {
setThreadName(_threadName);
}
}
void Runnable::setThreadName(const std::string& threadName) {
_threadName = threadName;
if (_running) {
#ifdef WIN32
SetThreadName(HANDLE(_thread->native_handle()), _threadName.c_str());
#else // WIN32 -> must be POSIX
pthread_setname_np(_thread->native_handle(), _threadName.c_str()); // UNTESTED!!
#endif
}
}
// ================================================================================================
RunnableWithConditionalWait::RunnableWithConditionalWait()
: Runnable()
RunnableWithConditionalWait::RunnableWithConditionalWait(const std::string& threadName /*= ""*/)
: Runnable(threadName)
{}
RunnableWithConditionalWait::~RunnableWithConditionalWait() {
......@@ -99,3 +127,41 @@ namespace cgt {
}
}
// Platform dependent code for setting the native thread name
#ifdef WIN32
// Code for setting a thread name in Windows / VC from https://msdn.microsoft.com/de-de/library/xcb2z8hs.aspx
namespace {
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
void SetThreadName(HANDLE hThread, const char* threadName) {
DWORD dwThreadID = GetThreadId(hThread);
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = threadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
__try {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
}
#pragma warning(pop)
}
}
#endif //WIN32
......@@ -55,7 +55,7 @@ namespace cgt {
/**
* Creates a new Runnable object
*/
Runnable();
Runnable(const std::string& threadName = "");
/**
* Destructor, stops and waits for the thread to finish if the thread is still running.
......@@ -80,10 +80,16 @@ namespace cgt {
*/
virtual void run() = 0;
/**
* Sets the thread name of the native thread. Useful for debugging
*/
void setThreadName(const std::string& threadName);
protected:
tbb::atomic<bool> _stopExecution; ///< Flag whether the thread should stop
std::function<void(void)> _initFunction; ///< Function to execute in the thread as very first code.
std::function<void(void)> _deinitFunction; ///< Function to execute in the thread as very last code.
std::string _threadName; ///< Thread name of the runnable, useful for debugging
private:
/// Runnables are not copyable
......@@ -109,7 +115,7 @@ namespace cgt {
/**
* Creates a new RunnableWithConditionalWait object
*/
RunnableWithConditionalWait();
RunnableWithConditionalWait(const std::string& threadName = "");
/**
* Destructor, stops and waits for thread to finish if the thread is still running.
......
......@@ -34,7 +34,8 @@
namespace sigslot {
signal_manager::signal_manager()
: _handlingMode(DEFAULT)
: RunnableWithConditionalWait("sigslot::signal_manager")
, _handlingMode(DEFAULT)
{
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment