Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "vstviewdialog_qwidget.h"
24 :
25 : #include <QWindow>
26 : #include <QTimer>
27 : #include <QKeyEvent>
28 : #include <QScreen>
29 :
30 : #include "ivstplugininstance.h"
31 : #include "vsttypes.h"
32 :
33 : #include "log.h"
34 :
35 : using namespace muse::vst;
36 : using namespace Steinberg;
37 :
38 0 : ::Steinberg::uint32 PLUGIN_API VstViewDialog::addRef()
39 : {
40 0 : return ::Steinberg::FUnknownPrivate::atomicAdd(__funknownRefCount, 1);
41 : }
42 :
43 0 : ::Steinberg::uint32 PLUGIN_API VstViewDialog::release()
44 : {
45 0 : if (::Steinberg::FUnknownPrivate::atomicAdd(__funknownRefCount, -1) == 0) {
46 0 : return 0;
47 : }
48 0 : return __funknownRefCount;
49 : }
50 :
51 0 : IMPLEMENT_QUERYINTERFACE(VstViewDialog, IPlugFrame, IPlugFrame::iid)
52 :
53 0 : VstViewDialog::VstViewDialog(QWidget* parent)
54 0 : : TopLevelDialog(parent)
55 : {
56 0 : setAttribute(Qt::WA_NativeWindow);
57 0 : }
58 :
59 0 : VstViewDialog::~VstViewDialog()
60 : {
61 0 : deinit();
62 0 : }
63 :
64 0 : void VstViewDialog::deinit()
65 : {
66 0 : if (m_view) {
67 0 : m_view->setFrame(nullptr);
68 0 : m_view->removed();
69 0 : m_view = nullptr;
70 : }
71 :
72 0 : if (m_instance) {
73 0 : m_instance->loadingCompleted().disconnect(this);
74 0 : m_instance->refreshConfig();
75 0 : m_instance = nullptr;
76 : }
77 0 : }
78 :
79 0 : tresult VstViewDialog::resizeView(IPlugView* view, ViewRect* newSize)
80 : {
81 0 : view->checkSizeConstraint(newSize);
82 :
83 0 : QScreen* screen = this->screen();
84 0 : QSize availableSize = screen->availableSize();
85 :
86 0 : int newWidth = newSize->getWidth();
87 0 : int newHeight = newSize->getHeight();
88 :
89 : //! NOTE: newSize already includes the UI scaling on Windows, so we have to remove it before setting the fixed size.
90 : //! Otherwise, the user will get an extremely large window and won't be able to resize it
91 : #ifdef Q_OS_WIN
92 : qreal scaling = screen->devicePixelRatio();
93 : newWidth = newWidth / scaling;
94 : newHeight = newHeight / scaling;
95 : #endif
96 :
97 0 : newWidth = std::min(newWidth, availableSize.width());
98 0 : newHeight = std::min(newHeight, availableSize.height());
99 :
100 0 : setFixedSize(newWidth, newHeight);
101 :
102 0 : view->onSize(newSize);
103 :
104 0 : update();
105 :
106 0 : return kResultTrue;
107 : }
108 :
109 0 : void VstViewDialog::wrapPluginView()
110 : {
111 0 : m_instance = instancesRegister()->instanceById(m_instanceId);
112 0 : if (!m_instance) {
113 0 : return;
114 : }
115 :
116 0 : setWindowTitle(QString::fromStdString(m_instance->name()));
117 :
118 0 : if (m_instance->isLoaded()) {
119 0 : attachView(m_instance);
120 : } else {
121 0 : m_instance->loadingCompleted().onNotify(this, [this]() {
122 0 : attachView(m_instance);
123 0 : });
124 : }
125 : }
126 :
127 0 : void VstViewDialog::attachView(IVstPluginInstancePtr instance)
128 : {
129 0 : if (!instance) {
130 0 : return;
131 : }
132 :
133 0 : m_view = instance->createView();
134 0 : if (!m_view) {
135 0 : return;
136 : }
137 :
138 0 : if (m_view->isPlatformTypeSupported(currentPlatformUiType()) != Steinberg::kResultTrue) {
139 0 : return;
140 : }
141 :
142 0 : m_view->setFrame(this);
143 :
144 : Steinberg::tresult attached;
145 0 : attached = m_view->attached(reinterpret_cast<void*>(windowHandle()->winId()), currentPlatformUiType());
146 0 : if (attached != kResultOk) {
147 0 : LOGE() << "Unable to attach vst plugin view to window"
148 0 : << ", m_instanceId: " << m_instanceId;
149 0 : return;
150 : }
151 :
152 0 : connect(windowHandle(), &QWindow::screenChanged, this, [this](QScreen*) {
153 0 : updateViewGeometry();
154 0 : });
155 :
156 0 : QTimer::singleShot(0, [this]() {
157 0 : updateViewGeometry();
158 0 : moveViewToMainWindowCenter();
159 0 : });
160 : }
161 :
162 0 : void VstViewDialog::updateViewGeometry()
163 : {
164 0 : IF_ASSERT_FAILED(m_view) {
165 0 : return;
166 : }
167 :
168 : #ifdef Q_OS_WIN
169 : FUnknownPtr<IPluginContentScaleHandler> scalingHandler(m_view);
170 : if (scalingHandler) {
171 : scalingHandler->setContentScaleFactor(screen()->devicePixelRatio());
172 : }
173 : #endif
174 :
175 0 : ViewRect size;
176 0 : m_view->getSize(&size);
177 :
178 0 : resizeView(m_view, &size);
179 : }
180 :
181 0 : void VstViewDialog::moveViewToMainWindowCenter()
182 : {
183 0 : QRectF mainWindowGeo = mainWindow()->qWindow()->geometry();
184 :
185 0 : int x = mainWindowGeo.x() + (mainWindowGeo.width() - width()) / 2;
186 0 : int y = mainWindowGeo.y() + (mainWindowGeo.height() - height()) / 2;
187 :
188 0 : move(x, y);
189 0 : }
190 :
191 0 : void VstViewDialog::showEvent(QShowEvent* ev)
192 : {
193 0 : moveViewToMainWindowCenter();
194 :
195 0 : TopLevelDialog::showEvent(ev);
196 0 : }
197 :
198 0 : void VstViewDialog::closeEvent(QCloseEvent* ev)
199 : {
200 0 : deinit();
201 :
202 0 : TopLevelDialog::closeEvent(ev);
203 0 : }
204 :
205 0 : bool VstViewDialog::event(QEvent* ev)
206 : {
207 0 : if (ev && ev->spontaneous() && ev->type() == QEvent::ShortcutOverride) {
208 0 : if (QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>(ev)) {
209 0 : int key = keyEvent->key();
210 :
211 0 : if (key == 0 || key == static_cast<int>(Qt::Key_unknown)) {
212 0 : keyEvent->accept();
213 0 : return true;
214 : }
215 : }
216 : }
217 :
218 0 : return TopLevelDialog::event(ev);
219 : }
220 :
221 0 : FIDString VstViewDialog::currentPlatformUiType() const
222 : {
223 : #ifdef Q_OS_MAC
224 : return Steinberg::kPlatformTypeNSView;
225 : #elif defined(Q_OS_IOS)
226 : return Steinberg::kPlatformTypeUIView;
227 : #elif defined(Q_OS_WIN)
228 : return Steinberg::kPlatformTypeHWND;
229 : #else
230 0 : return Steinberg::kPlatformTypeX11EmbedWindowID;
231 : #endif
232 : }
233 :
234 0 : int VstViewDialog::instanceId() const
235 : {
236 0 : return m_instanceId;
237 : }
238 :
239 0 : void VstViewDialog::setInstanceId(int newInstanceId)
240 : {
241 0 : if (m_instanceId == newInstanceId) {
242 0 : return;
243 : }
244 0 : m_instanceId = newInstanceId;
245 0 : emit instanceIdChanged();
246 :
247 0 : wrapPluginView();
248 : }
|