Skip to content

Commit

Permalink
UI: Make window-projector its own top level QWidget
Browse files Browse the repository at this point in the history
Previously OBSProjector was a subclass of OBSQTDisplay, which is
intended to own the entire surface it draws on. On wayland some
compositors expect the client to draw decorations (window titlebar), but
because OBSQTDisplay draws on the entire surface directly the client
decorations from QT are overwritten.

Instead promote OBSProjector to a simple QWidget with a single layout
containing the OBSQTDisplay. This allows the OBSQTDisplay to own the
entire contents while OBSProjector draws the decorations.

fixes obsproject#6283
  • Loading branch information
kkartaltepe committed Apr 20, 2022
1 parent 4c96fea commit 9520336
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 21 deletions.
84 changes: 64 additions & 20 deletions UI/window-projector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@
#include "qt-wrappers.hpp"
#include "platform.hpp"

#ifdef ENABLE_WAYLAND
#include <obs-nix-platform.h>
#endif

#ifdef _WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN 1
#include <Windows.h>
#endif

static QList<OBSProjector *> multiviewProjectors;
static QList<OBSProjector *> allProjectors;

Expand All @@ -19,11 +29,16 @@ static size_t maxSrcs, numSrcs;

OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor,
ProjectorType type_)
: OBSQTDisplay(widget, Qt::Window),
: display(this),
source(source_),
removedSignal(obs_source_get_signal_handler(source), "remove",
OBSSourceRemoved, this)
{
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(&display);
layout->setContentsMargins(0, 0, 0, 0);
this->setLayout(layout);

isAlwaysOnTop = config_get_bool(GetGlobalConfig(), "BasicWindow",
"ProjectorAlwaysOnTop");

Expand All @@ -32,7 +47,7 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor,

// Mark the window as a projector so SetDisplayAffinity
// can skip it
windowHandle()->setProperty("isOBSProjectorWindow", true);
display.windowHandle()->setProperty("isOBSProjectorWindow", true);

#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
// Prevents resizing of projector windows
Expand Down Expand Up @@ -69,12 +84,13 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor,
auto addDrawCallback = [this]() {
bool isMultiview = type == ProjectorType::Multiview;
obs_display_add_draw_callback(
GetDisplay(),
display.GetDisplay(),
isMultiview ? OBSRenderMultiview : OBSRender, this);
obs_display_set_background_color(GetDisplay(), 0x000000);
obs_display_set_background_color(display.GetDisplay(),
0x000000);
};

connect(this, &OBSQTDisplay::DisplayCreated, addDrawCallback);
connect(&display, &OBSQTDisplay::DisplayCreated, addDrawCallback);
connect(App(), &QGuiApplication::screenRemoved, this,
&OBSProjector::ScreenRemoved);

Expand All @@ -97,17 +113,14 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor,
ready = true;

show();

// We need it here to allow keyboard input in X11 to listen to Escape
activateWindow();
}

OBSProjector::~OBSProjector()
{
bool isMultiview = type == ProjectorType::Multiview;
obs_display_remove_draw_callback(
GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender,
this);
display.GetDisplay(),
isMultiview ? OBSRenderMultiview : OBSRender, this);

if (source)
obs_source_dec_showing(source);
Expand Down Expand Up @@ -874,7 +887,7 @@ static int getSourceByPosition(int x, int y, float ratio)

void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event)
{
OBSQTDisplay::mouseDoubleClickEvent(event);
QWidget::mouseDoubleClickEvent(event);

if (!mouseSwitching)
return;
Expand All @@ -901,7 +914,7 @@ void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event)

void OBSProjector::mousePressEvent(QMouseEvent *event)
{
OBSQTDisplay::mousePressEvent(event);
QWidget::mousePressEvent(event);

if (event->button() == Qt::RightButton) {
OBSBasic *main =
Expand All @@ -922,15 +935,26 @@ void OBSProjector::mousePressEvent(QMouseEvent *event)
this, SLOT(ResizeToContent()));
}

QAction *alwaysOnTopButton =
new QAction(QTStr("Basic.MainMenu.AlwaysOnTop"), this);
alwaysOnTopButton->setCheckable(true);
alwaysOnTopButton->setChecked(isAlwaysOnTop);
#ifdef ENABLE_WAYLAND
// SetAlwaysOnTop recreates the window and desyncs qt-display, I think.
// Causes mis-rendering if toggled on wayland and is disabled in the preview
// and main windows.
bool isWayland = obs_get_nix_platform() ==
OBS_NIX_PLATFORM_WAYLAND;
#else
bool isWayland = false;
#endif
if (!isWayland) {
QAction *alwaysOnTopButton = new QAction(
QTStr("Basic.MainMenu.AlwaysOnTop"), this);
alwaysOnTopButton->setCheckable(true);
alwaysOnTopButton->setChecked(isAlwaysOnTop);

connect(alwaysOnTopButton, &QAction::toggled, this,
&OBSProjector::AlwaysOnTopToggled);
connect(alwaysOnTopButton, &QAction::toggled, this,
&OBSProjector::AlwaysOnTopToggled);

popup.addAction(alwaysOnTopButton);
popup.addAction(alwaysOnTopButton);
}

popup.addAction(QTStr("Close"), this, SLOT(EscapeTriggered()));
popup.exec(QCursor::pos());
Expand Down Expand Up @@ -1184,8 +1208,8 @@ void OBSProjector::OpenFullScreenProjector()
void OBSProjector::OpenWindowedProjector()
{
showFullScreen();
showNormal();
setCursor(Qt::ArrowCursor);
showNormal();

if (!prevGeometry.isNull())
setGeometry(prevGeometry);
Expand Down Expand Up @@ -1236,6 +1260,26 @@ void OBSProjector::closeEvent(QCloseEvent *event)
event->accept();
}

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool OBSProjector::nativeEvent(const QByteArray &, void *message, qintptr *)
#else
bool OBSProjector::nativeEvent(const QByteArray &, void *message, long *)
#endif
{
#ifdef _WIN32
const MSG &msg = *static_cast<MSG *>(message);
switch (msg.message) {
case WM_MOVE:
display.OnMove();
break;
case WM_DISPLAYCHANGE:
display.OnDisplayChange();
}
#endif

return false;
}

bool OBSProjector::IsAlwaysOnTop() const
{
return isAlwaysOnTop;
Expand Down
10 changes: 9 additions & 1 deletion UI/window-projector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ enum class MultiviewLayout : uint8_t {
SCENES_ONLY_25_SCENES = 9,
};

class OBSProjector : public OBSQTDisplay {
class OBSProjector : public QWidget {
Q_OBJECT

private:
OBSSource source;
OBSSignal removedSignal;
OBSQTDisplay display;

static void OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy);
static void OBSRender(void *data, uint32_t cx, uint32_t cy);
Expand All @@ -40,6 +41,13 @@ class OBSProjector : public OBSQTDisplay {
void mousePressEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void closeEvent(QCloseEvent *event) override;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
virtual bool nativeEvent(const QByteArray &eventType, void *message,
qintptr *result) override;
#else
virtual bool nativeEvent(const QByteArray &eventType, void *message,
long *result) override;
#endif

bool isAlwaysOnTop;
bool isAlwaysOnTopOverridden = false;
Expand Down

0 comments on commit 9520336

Please sign in to comment.