KonvergoWindow.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. #include "KonvergoWindow.h"
  2. #include <QTimer>
  3. #include <QJsonObject>
  4. #include <QScreen>
  5. #include <QQuickItem>
  6. #include <QGuiApplication>
  7. #include "input/InputKeyboard.h"
  8. #include "settings/SettingsComponent.h"
  9. #include "settings/SettingsSection.h"
  10. #include "system/SystemComponent.h"
  11. #include "player/PlayerComponent.h"
  12. #include "player/PlayerQuickItem.h"
  13. #include "display/DisplayComponent.h"
  14. #include "QsLog.h"
  15. #include "power/PowerComponent.h"
  16. #include "utils/Utils.h"
  17. #include "KonvergoEngine.h"
  18. ///////////////////////////////////////////////////////////////////////////////////////////////////
  19. bool MouseEventFilter::eventFilter(QObject* watched, QEvent* event)
  20. {
  21. SystemComponent& system = SystemComponent::Get();
  22. // ignore mouse events if mouse is disabled
  23. if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "disablemouse").toBool() &&
  24. ((event->type() == QEvent::MouseMove) ||
  25. (event->type() == QEvent::MouseButtonPress) ||
  26. (event->type() == QEvent::MouseButtonRelease) ||
  27. (event->type() == QEvent::MouseButtonDblClick)))
  28. {
  29. return true;
  30. }
  31. if (event->type() == QEvent::KeyPress)
  32. {
  33. // In konvergo we intercept all keyboard events and translate them
  34. // into web client actions. We need to do this so that we can remap
  35. // keyboard buttons to different events.
  36. //
  37. QKeyEvent* kevent = dynamic_cast<QKeyEvent*>(event);
  38. if (kevent)
  39. {
  40. system.setCursorVisibility(false);
  41. if (kevent->spontaneous())
  42. {
  43. InputKeyboard::Get().keyPress(QKeySequence(kevent->key() | kevent->modifiers()));
  44. return true;
  45. }
  46. }
  47. }
  48. else if (event->type() == QEvent::MouseMove)
  49. {
  50. system.setCursorVisibility(true);
  51. }
  52. else if (event->type() == QEvent::Wheel)
  53. {
  54. return true;
  55. }
  56. else if (event->type() == QEvent::MouseButtonPress)
  57. {
  58. // ignore right clicks that would show context menu
  59. QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
  60. if ((mouseEvent) && (mouseEvent->button() == Qt::RightButton))
  61. return true;
  62. }
  63. return QObject::eventFilter(watched, event);
  64. }
  65. ///////////////////////////////////////////////////////////////////////////////////////////////////
  66. KonvergoWindow::KonvergoWindow(QWindow* parent) : QQuickWindow(parent), m_debugLayer(false)
  67. {
  68. // NSWindowCollectionBehaviorFullScreenPrimary is only set on OSX if Qt::WindowFullscreenButtonHint is set on the window.
  69. setFlags(flags() | Qt::WindowFullscreenButtonHint);
  70. m_eventFilter = new MouseEventFilter(this);
  71. installEventFilter(m_eventFilter);
  72. m_infoTimer = new QTimer(this);
  73. m_infoTimer->setInterval(1000);
  74. connect(m_infoTimer, &QTimer::timeout, this, &KonvergoWindow::updateDebugInfo);
  75. InputComponent::Get().registerHostCommand("close", this, "close");
  76. InputComponent::Get().registerHostCommand("toggleDebug", this, "toggleDebug");
  77. InputComponent::Get().registerHostCommand("reload", this, "reloadWeb");
  78. InputComponent::Get().registerHostCommand("fullscreen", this, "toggleFullscreen");
  79. #ifdef TARGET_RPI
  80. // On RPI, we use dispmanx layering - the video is on a layer below Konvergo,
  81. // and during playback the Konvergo window is partially transparent. The OSD
  82. // will be visible on top of the video as part of the Konvergo window.
  83. setColor(QColor("transparent"));
  84. #else
  85. setColor(QColor("#111111"));
  86. #endif
  87. loadGeometry();
  88. connect(SettingsComponent::Get().getSection(SETTINGS_SECTION_MAIN), &SettingsSection::valuesUpdated,
  89. this, &KonvergoWindow::updateMainSectionSettings);
  90. connect(this, &KonvergoWindow::visibilityChanged,
  91. this, &KonvergoWindow::onVisibilityChanged);
  92. connect(this, &KonvergoWindow::enableVideoWindowSignal,
  93. this, &KonvergoWindow::enableVideoWindow, Qt::QueuedConnection);
  94. // connect(QGuiApplication::desktop(), &QDesktopWidget::screenCountChanged,
  95. // this, &KonvergoWindow::onScreenCountChanged);
  96. connect(&PlayerComponent::Get(), &PlayerComponent::windowVisible,
  97. this, &KonvergoWindow::playerWindowVisible);
  98. connect(&PlayerComponent::Get(), &PlayerComponent::playbackStarting,
  99. this, &KonvergoWindow::playerPlaybackStarting);
  100. // this is using old syntax because ... reasons. QQuickCloseEvent is not public class
  101. connect(this, SIGNAL(closing(QQuickCloseEvent*)), this, SLOT(closingWindow()));
  102. connect(qApp, &QCoreApplication::aboutToQuit, this, &KonvergoWindow::saveGeometry);
  103. if (!SystemComponent::Get().isOpenELEC())
  104. {
  105. // this is such a hack. But I could not get it to enter into fullscreen
  106. // mode if I didn't trigger this after a while.
  107. //
  108. QTimer::singleShot(500, [=]() {
  109. updateFullscreenState();
  110. });
  111. }
  112. else
  113. {
  114. setWindowState(Qt::WindowFullScreen);
  115. }
  116. emit enableVideoWindowSignal();
  117. }
  118. /////////////////////////////////////////////////////////////////////////////////////////
  119. void KonvergoWindow::closingWindow()
  120. {
  121. if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "fullscreen").toBool() == false)
  122. saveGeometry();
  123. qApp->quit();
  124. }
  125. ///////////////////////////////////////////////////////////////////////////////////////////////////
  126. KonvergoWindow::~KonvergoWindow()
  127. {
  128. removeEventFilter(m_eventFilter);
  129. DisplayComponent::Get().setApplicationWindow(0);
  130. }
  131. ///////////////////////////////////////////////////////////////////////////////////////////////////
  132. void KonvergoWindow::saveGeometry()
  133. {
  134. QRect rc = geometry();
  135. QJsonObject obj;
  136. obj.insert("x", rc.x());
  137. obj.insert("y", rc.y());
  138. obj.insert("width", rc.width());
  139. obj.insert("height", rc.height());
  140. SettingsComponent::Get().setValue(SETTINGS_SECTION_STATE, "geometry", obj.toVariantMap());
  141. }
  142. ///////////////////////////////////////////////////////////////////////////////////////////////////
  143. void KonvergoWindow::loadGeometry()
  144. {
  145. QRect rc = loadGeometryRect();
  146. if (rc.isValid())
  147. setGeometry(rc);
  148. }
  149. ///////////////////////////////////////////////////////////////////////////////////////////////////
  150. QRect KonvergoWindow::loadGeometryRect()
  151. {
  152. QJsonObject obj = QJsonObject::fromVariantMap(SettingsComponent::Get().value(SETTINGS_SECTION_STATE, "geometry").toMap());
  153. if (obj.isEmpty())
  154. return QRect();
  155. QRect rc(obj["x"].toInt(), obj["y"].toInt(), obj["width"].toInt(), obj["height"].toInt());
  156. if (rc.width() < 1280)
  157. rc.setWidth(1280);
  158. if (rc.height() < 720)
  159. rc.setHeight(720);
  160. // make sure our poisition is contained in one of the current screens
  161. foreach (QScreen *screen, QGuiApplication::screens())
  162. {
  163. if (screen->availableGeometry().contains(rc))
  164. return rc;
  165. }
  166. // otherwise default to center of current screen
  167. return QRect((screen()->geometry().width() - geometry().width()) / 2,
  168. (screen()->geometry().height() - geometry().height()) / 2,
  169. geometry().width(),
  170. geometry().height());
  171. }
  172. ///////////////////////////////////////////////////////////////////////////////////////////////////
  173. void KonvergoWindow::enableVideoWindow()
  174. {
  175. PlayerComponent::Get().setWindow(this);
  176. DisplayComponent::Get().setApplicationWindow(this);
  177. }
  178. ///////////////////////////////////////////////////////////////////////////////////////////////////
  179. void KonvergoWindow::setFullScreen(bool enable)
  180. {
  181. QLOG_DEBUG() << "setting fullscreen = " << enable;
  182. SettingsComponent::Get().setValue(SETTINGS_SECTION_MAIN, "fullscreen", enable);
  183. }
  184. ///////////////////////////////////////////////////////////////////////////////////////////////////
  185. void KonvergoWindow::playerWindowVisible(bool visible)
  186. {
  187. // adjust webengineview transparecy depending on player visibility
  188. QQuickItem *web = findChild<QQuickItem *>("web");
  189. if (web)
  190. web->setProperty("backgroundColor", visible ? "transparent" : "#111111");
  191. }
  192. ///////////////////////////////////////////////////////////////////////////////////////////////////
  193. void KonvergoWindow::updateMainSectionSettings(const QVariantMap& values)
  194. {
  195. // update mouse visibility if needed
  196. if (values.find("disablemouse") != values.end())
  197. {
  198. SystemComponent::Get().setCursorVisibility(!SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "disablemouse").toBool());
  199. }
  200. if (values.find("fullscreen") == values.end())
  201. return;
  202. updateFullscreenState();
  203. }
  204. ///////////////////////////////////////////////////////////////////////////////////////////////////
  205. void KonvergoWindow::updateFullscreenState()
  206. {
  207. if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "fullscreen").toBool() || SystemComponent::Get().isOpenELEC())
  208. {
  209. // if we were go from windowed to fullscreen
  210. // we want to stor our current windowed position
  211. if (!isFullScreen())
  212. saveGeometry();
  213. setVisibility(QWindow::FullScreen);
  214. }
  215. else
  216. {
  217. setVisibility(QWindow::Windowed);
  218. loadGeometry();
  219. }
  220. }
  221. ///////////////////////////////////////////////////////////////////////////////////////////////////
  222. void KonvergoWindow::onVisibilityChanged(QWindow::Visibility visibility)
  223. {
  224. QLOG_DEBUG() << (visibility == QWindow::FullScreen ? "FullScreen" : "Windowed") << "visbility set to " << visibility;
  225. if (visibility == QWindow::Windowed)
  226. loadGeometry();
  227. if (visibility == QWindow::FullScreen)
  228. PowerComponent::Get().setFullscreenState(true);
  229. else if (visibility == QWindow::Windowed)
  230. PowerComponent::Get().setFullscreenState(false);
  231. }
  232. /////////////////////////////////////////////////////////////////////////////////////////
  233. void KonvergoWindow::focusOutEvent(QFocusEvent * ev)
  234. {
  235. #ifdef Q_OS_WIN32
  236. // Do this to workaround DWM compositor bugs with fullscreened OpenGL applications.
  237. // The compositor will not properly redraw anything when focusing other windows.
  238. if (visibility() == QWindow::FullScreen && SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "minimizeOnDefocus").toBool())
  239. {
  240. QLOG_DEBUG() << "minimizing window";
  241. showMinimized();
  242. }
  243. #endif
  244. }
  245. /////////////////////////////////////////////////////////////////////////////////////////
  246. void KonvergoWindow::playerPlaybackStarting()
  247. {
  248. #if defined(Q_OS_MAC)
  249. // On OSX, initializing VideoTooolbox (hardware decoder API) will mysteriously
  250. // show the hidden mouse pointer again. The VTDecompressionSessionCreate API
  251. // function does this, and we have no influence over its behavior. To make sure
  252. // the cursor is gone again when starting playback, listen to the player's
  253. // playbackStarting signal, at which point decoder initialization is guaranteed
  254. // to be completed. Then we just have to set the cursor again on the Cocoa level.
  255. if (QGuiApplication::overrideCursor())
  256. QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
  257. #endif
  258. }
  259. /////////////////////////////////////////////////////////////////////////////////////////
  260. void KonvergoWindow::RegisterClass()
  261. {
  262. qmlRegisterType<KonvergoWindow>("Konvergo", 1, 0, "KonvergoWindow");
  263. }
  264. /////////////////////////////////////////////////////////////////////////////////////////
  265. void KonvergoWindow::onScreenCountChanged(int newCount)
  266. {
  267. updateFullscreenState();
  268. }
  269. /////////////////////////////////////////////////////////////////////////////////////////
  270. void KonvergoWindow::updateDebugInfo()
  271. {
  272. if (m_systemDebugInfo.size() == 0)
  273. m_systemDebugInfo = SystemComponent::Get().debugInformation();
  274. m_debugInfo = m_systemDebugInfo;
  275. m_debugInfo += DisplayComponent::Get().debugInformation();
  276. PlayerQuickItem* video = findChild<PlayerQuickItem*>("video");
  277. if (video)
  278. m_debugInfo += video->debugInfo();
  279. m_videoInfo = PlayerComponent::Get().videoInformation();
  280. emit debugInfoChanged();
  281. }
  282. /////////////////////////////////////////////////////////////////////////////////////////
  283. void KonvergoWindow::toggleDebug()
  284. {
  285. if (property("showDebugLayer").toBool())
  286. {
  287. m_infoTimer->stop();
  288. setProperty("showDebugLayer", false);
  289. }
  290. else
  291. {
  292. m_infoTimer->start();
  293. updateDebugInfo();
  294. setProperty("showDebugLayer", true);
  295. }
  296. }