1 /***************************************************************************
2 **
3 ** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig <tobias.koenig@kdab.com>
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qhaikuwindow.h"
41 
42 #include "private/qguiapplication_p.h"
43 
44 #include <QCoreApplication>
45 #include <QThread>
46 #include <QWindow>
47 #include <qpa/qwindowsysteminterface.h>
48 
49 #include <Rect.h>
50 #include <Window.h>
51 
52 QT_BEGIN_NAMESPACE
53 
54 enum {
55     DefaultWindowWidth = 160,
56     DefaultWindowHeight = 160
57 };
58 
HaikuWindowProxy(QWindow * window,const QRect & rect,QObject * parent)59 HaikuWindowProxy::HaikuWindowProxy(QWindow *window, const QRect &rect, QObject *parent)
60     : QObject(parent)
61     , BWindow(BRect(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1),
62               window->title().toUtf8(), B_NO_BORDER_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 0)
63     , m_qtCalledZoom(false)
64     , m_zoomInProgress(false)
65 {
66 }
67 
FrameMoved(BPoint pos)68 void HaikuWindowProxy::FrameMoved(BPoint pos)
69 {
70     Q_EMIT moved(QPoint(pos.x, pos.y));
71 }
72 
FrameResized(float width,float height)73 void HaikuWindowProxy::FrameResized(float width, float height)
74 {
75     Q_EMIT resized(QSize(static_cast<int>(width), static_cast<int>(height)), m_zoomInProgress);
76 
77     m_zoomInProgress = false;
78 }
79 
WindowActivated(bool activated)80 void HaikuWindowProxy::WindowActivated(bool activated)
81 {
82     Q_EMIT windowActivated(activated);
83 }
84 
Minimize(bool minimize)85 void HaikuWindowProxy::Minimize(bool minimize)
86 {
87     BWindow::Minimize(minimize);
88 
89     Q_EMIT minimized(minimize);
90 }
91 
Zoom(BPoint pos,float width,float height)92 void HaikuWindowProxy::Zoom(BPoint pos, float width, float height)
93 {
94     m_zoomInProgress = true;
95     BWindow::Zoom(pos, width, height);
96 
97     // Only notify about Zoom invocations from the Haiku windowing system
98     if (!m_qtCalledZoom)
99         Q_EMIT zoomed();
100 }
101 
QuitRequested()102 bool HaikuWindowProxy::QuitRequested()
103 {
104     Q_EMIT quitRequested();
105 
106     // Return false to prevent Haiku windowing system to clean up
107     // the BWindow and BView instances. We will do that ourself when
108     // Qt invokes the dtor of QHaikuWindow
109     return false;
110 }
111 
zoomByQt()112 void HaikuWindowProxy::zoomByQt()
113 {
114     m_qtCalledZoom = true;
115     BWindow::Zoom();
116     m_qtCalledZoom = false;
117 }
118 
QHaikuWindow(QWindow * window)119 QHaikuWindow::QHaikuWindow(QWindow *window)
120     : QPlatformWindow(window)
121     , m_window(nullptr)
122     , m_windowState(Qt::WindowNoState)
123 {
124     const QRect rect = initialGeometry(window, window->geometry(), DefaultWindowWidth, DefaultWindowHeight);
125 
126     HaikuWindowProxy *haikuWindow = new HaikuWindowProxy(window, rect, nullptr);
127     connect(haikuWindow, SIGNAL(moved(QPoint)), SLOT(haikuWindowMoved(QPoint)));
128     connect(haikuWindow, SIGNAL(resized(QSize,bool)), SLOT(haikuWindowResized(QSize,bool)));
129     connect(haikuWindow, SIGNAL(windowActivated(bool)), SLOT(haikuWindowActivated(bool)));
130     connect(haikuWindow, SIGNAL(minimized(bool)), SLOT(haikuWindowMinimized(bool)));
131     connect(haikuWindow, SIGNAL(zoomed()), SLOT(haikuWindowZoomed()));
132     connect(haikuWindow, SIGNAL(quitRequested()), SLOT(haikuWindowQuitRequested()), Qt::BlockingQueuedConnection);
133 
134     m_window = haikuWindow;
135 
136     if (Q_UNLIKELY(!m_window))
137         qFatal("QHaikuWindow: failed to create window");
138 
139     setGeometry(rect);
140     setWindowFlags(window->flags());
141 }
142 
~QHaikuWindow()143 QHaikuWindow::~QHaikuWindow()
144 {
145     m_window->LockLooper();
146     m_window->Quit();
147 
148     m_window = nullptr;
149 }
150 
setGeometry(const QRect & rect)151 void QHaikuWindow::setGeometry(const QRect &rect)
152 {
153     QPlatformWindow::setGeometry(rect);
154 
155     m_window->MoveTo(rect.x(), rect.y());
156     m_window->ResizeTo(rect.width(), rect.height());
157 }
158 
frameMargins() const159 QMargins QHaikuWindow::frameMargins() const
160 {
161     const BRect decoratorFrame = m_window->DecoratorFrame();
162     const BRect frame = m_window->Frame();
163 
164     return QMargins(frame.left - decoratorFrame.left,
165                     frame.top - decoratorFrame.top,
166                     decoratorFrame.right - frame.right,
167                     decoratorFrame.bottom - frame.bottom);
168 }
169 
setVisible(bool visible)170 void QHaikuWindow::setVisible(bool visible)
171 {
172     if (visible) {
173         m_window->Show();
174 
175         window()->requestActivate();
176 
177         QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), window()->geometry().size()));
178     } else {
179         m_window->Hide();
180     }
181 }
182 
isExposed() const183 bool QHaikuWindow::isExposed() const
184 {
185     return !m_window->IsHidden();
186 }
187 
isActive() const188 bool QHaikuWindow::isActive() const
189 {
190     return m_window->IsActive();
191 }
192 
winId() const193 WId QHaikuWindow::winId() const
194 {
195     return (WId)static_cast<BWindow*>(m_window);
196 }
197 
nativeHandle() const198 BWindow* QHaikuWindow::nativeHandle() const
199 {
200     return m_window;
201 }
202 
requestActivateWindow()203 void QHaikuWindow::requestActivateWindow()
204 {
205     m_window->Activate(true);
206 }
207 
setWindowState(Qt::WindowStates state)208 void QHaikuWindow::setWindowState(Qt::WindowStates state)
209 {
210     if (m_windowState == state)
211         return;
212 
213     const Qt::WindowStates oldState = m_windowState;
214 
215     m_windowState = state;
216 
217     if (m_windowState & Qt::WindowMinimized)
218         m_window->Minimize(true);
219     else if (m_windowState & Qt::WindowMaximized)
220         m_window->zoomByQt();
221     else if (oldState & Qt::WindowMinimized)
222         m_window->Minimize(false); // undo minimize
223     else if (oldState & Qt::WindowMaximized)
224         m_window->zoomByQt(); // undo zoom
225 }
226 
setWindowFlags(Qt::WindowFlags flags)227 void QHaikuWindow::setWindowFlags(Qt::WindowFlags flags)
228 {
229     const Qt::WindowType type = static_cast<Qt::WindowType>(static_cast<int>(flags & Qt::WindowType_Mask));
230 
231     const bool isPopup = (type == Qt::Popup);
232     const bool isSplashScreen = (type == Qt::SplashScreen);
233     const bool isDialog = ((type == Qt::Dialog) || (type == Qt::Sheet) || (type == Qt::MSWindowsFixedSizeDialogHint));
234     const bool isTool = ((type == Qt::Tool) || (type == Qt::Drawer));
235     const bool isToolTip = (type == Qt::ToolTip);
236 
237     window_look wlook = B_TITLED_WINDOW_LOOK;
238     window_feel wfeel = B_NORMAL_WINDOW_FEEL;
239     uint32 wflag = (B_NO_WORKSPACE_ACTIVATION | B_NOT_ANCHORED_ON_ACTIVATE);
240 
241     if (isTool) {
242         wlook = B_FLOATING_WINDOW_LOOK;
243         wflag |= B_WILL_ACCEPT_FIRST_CLICK;
244     }
245 
246     if (isSplashScreen) {
247         wlook = B_NO_BORDER_WINDOW_LOOK;
248     }
249 
250     if (isPopup) {
251         wlook = B_NO_BORDER_WINDOW_LOOK;
252         wflag |= (B_WILL_ACCEPT_FIRST_CLICK | B_AVOID_FRONT | B_AVOID_FOCUS);
253         flags |= Qt::WindowStaysOnTopHint;
254     }
255 
256     if (isDialog) {
257         if (window()->modality() == Qt::WindowModal)
258             wfeel = B_MODAL_SUBSET_WINDOW_FEEL;
259         else if (window()->modality() == Qt::ApplicationModal)
260             wfeel = B_MODAL_APP_WINDOW_FEEL;
261     }
262 
263     if (isToolTip) {
264         wlook = B_NO_BORDER_WINDOW_LOOK;
265         wflag |= (B_WILL_ACCEPT_FIRST_CLICK | B_AVOID_FOCUS);
266         flags |= Qt::WindowStaysOnTopHint;
267     }
268 
269     if (flags & Qt::FramelessWindowHint)
270         wlook = B_NO_BORDER_WINDOW_LOOK;
271 
272     if (flags & Qt::MSWindowsFixedSizeDialogHint)
273         wflag |= (B_NOT_RESIZABLE | B_NOT_ZOOMABLE);
274 
275     if (flags & Qt::CustomizeWindowHint) {
276         if (!(flags & Qt::WindowMinimizeButtonHint))
277             wflag |= B_NOT_MINIMIZABLE;
278         if (!(flags & Qt::WindowMaximizeButtonHint))
279             wflag |= B_NOT_ZOOMABLE;
280         if (!(flags & Qt::WindowCloseButtonHint))
281             wflag |= B_NOT_CLOSABLE;
282     }
283 
284     if (flags & Qt::WindowStaysOnTopHint)
285         wfeel = B_FLOATING_ALL_WINDOW_FEEL;
286 
287     m_window->SetLook(wlook);
288     m_window->SetFeel(wfeel);
289     m_window->SetFlags(wflag);
290 }
291 
setWindowTitle(const QString & title)292 void QHaikuWindow::setWindowTitle(const QString &title)
293 {
294     m_window->SetTitle(title.toLocal8Bit().constData());
295 }
296 
propagateSizeHints()297 void QHaikuWindow::propagateSizeHints()
298 {
299     m_window->SetSizeLimits(window()->minimumSize().width(),
300                             window()->maximumSize().width(),
301                             window()->minimumSize().height(),
302                             window()->maximumSize().height());
303 
304     m_window->SetZoomLimits(window()->maximumSize().width(),
305                             window()->maximumSize().height());
306 }
307 
haikuWindowMoved(const QPoint & pos)308 void QHaikuWindow::haikuWindowMoved(const QPoint &pos)
309 {
310     const QRect newGeometry(pos, geometry().size());
311 
312     QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
313     QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), newGeometry.size()));
314 }
315 
haikuWindowResized(const QSize & size,bool zoomInProgress)316 void QHaikuWindow::haikuWindowResized(const QSize &size, bool zoomInProgress)
317 {
318     const QRect newGeometry(geometry().topLeft(), size);
319 
320     QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
321     QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), newGeometry.size()));
322 
323     if ((m_windowState == Qt::WindowMaximized) && !zoomInProgress) {
324         // the user has resized the window while maximized -> reset maximized flag
325         m_windowState = Qt::WindowNoState;
326 
327         QWindowSystemInterface::handleWindowStateChanged(window(), m_windowState);
328     }
329 }
330 
haikuWindowActivated(bool activated)331 void QHaikuWindow::haikuWindowActivated(bool activated)
332 {
333     QWindowSystemInterface::handleWindowActivated(activated ? window() : nullptr);
334 }
335 
haikuWindowMinimized(bool minimize)336 void QHaikuWindow::haikuWindowMinimized(bool minimize)
337 {
338     m_windowState = (minimize ? Qt::WindowMinimized : Qt::WindowNoState);
339 
340     QWindowSystemInterface::handleWindowStateChanged(window(), m_windowState);
341 }
342 
haikuWindowZoomed()343 void QHaikuWindow::haikuWindowZoomed()
344 {
345     m_windowState = (m_windowState == Qt::WindowMaximized ? Qt::WindowNoState : Qt::WindowMaximized);
346 
347     QWindowSystemInterface::handleWindowStateChanged(window(), m_windowState);
348 }
349 
haikuWindowQuitRequested()350 void QHaikuWindow::haikuWindowQuitRequested()
351 {
352     QWindowSystemInterface::handleCloseEvent(window());
353 }
354 
haikuMouseEvent(const QPoint & localPosition,const QPoint & globalPosition,Qt::MouseButtons buttons,Qt::KeyboardModifiers modifiers,Qt::MouseEventSource source)355 void QHaikuWindow::haikuMouseEvent(const QPoint &localPosition, const QPoint &globalPosition, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source)
356 {
357     QWindowSystemInterface::handleMouseEvent(window(), localPosition, globalPosition,
358                                              buttons, modifiers, source);
359 }
360 
haikuWheelEvent(const QPoint & localPosition,const QPoint & globalPosition,int delta,Qt::Orientation orientation,Qt::KeyboardModifiers modifiers)361 void QHaikuWindow::haikuWheelEvent(const QPoint &localPosition, const QPoint &globalPosition, int delta, Qt::Orientation orientation, Qt::KeyboardModifiers modifiers)
362 {
363     QWindowSystemInterface::handleWheelEvent(window(), localPosition, globalPosition, delta, orientation, modifiers);
364 }
365 
haikuKeyEvent(QEvent::Type type,int key,Qt::KeyboardModifiers modifiers,const QString & text)366 void QHaikuWindow::haikuKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text)
367 {
368     QWindowSystemInterface::handleKeyEvent(window(), type, key, modifiers, text);
369 }
370 
haikuEnteredView()371 void QHaikuWindow::haikuEnteredView()
372 {
373     QWindowSystemInterface::handleEnterEvent(window());
374 }
375 
haikuExitedView()376 void QHaikuWindow::haikuExitedView()
377 {
378     QWindowSystemInterface::handleLeaveEvent(window());
379 }
380 
haikuDrawRequest(const QRect & rect)381 void QHaikuWindow::haikuDrawRequest(const QRect &rect)
382 {
383     QWindowSystemInterface::handleExposeEvent(window(), QRegion(rect));
384 }
385 
386 QT_END_NAMESPACE
387