DisplayManagerRPI.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #include <QGuiApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QQuickWindow>
  4. #include <QDebug>
  5. #include <private/qguiapplication_p.h>
  6. #include <qpa/qplatformintegration.h>
  7. #include "DisplayManagerRPI.h"
  8. #include "display/DisplayComponent.h"
  9. ///////////////////////////////////////////////////////////////////////////////////////////////////
  10. void DisplayManagerRPI::tv_callback(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
  11. {
  12. DisplayManagerRPI* obj = (DisplayManagerRPI *)callback_data;
  13. emit obj->onTvChange(reason);
  14. }
  15. ///////////////////////////////////////////////////////////////////////////////////////////////////
  16. DisplayManagerRPI::DisplayManagerRPI(QObject* parent) : DisplayManager(parent)
  17. {
  18. connect(this, &DisplayManagerRPI::onTvChange,
  19. this, &DisplayManagerRPI::handleTvChange,
  20. Qt::QueuedConnection);
  21. vc_tv_register_callback(&tv_callback, (void *)this);
  22. }
  23. ///////////////////////////////////////////////////////////////////////////////////////////////////
  24. DisplayManagerRPI::~DisplayManagerRPI()
  25. {
  26. vc_tv_unregister_callback_full(&tv_callback, (void *)this);
  27. }
  28. ///////////////////////////////////////////////////////////////////////////////////////////////////
  29. void DisplayManagerRPI::handleTvChange(uint32_t reason)
  30. {
  31. qInfo() << "tv_service notification:" << reason;
  32. if (reason & (VC_HDMI_DVI | VC_HDMI_HDMI))
  33. {
  34. // Looks like a mode change - Qt's dispmanx state is probably trashed, recreate it.
  35. resetRendering();
  36. }
  37. else if (reason & VC_HDMI_ATTACHED)
  38. {
  39. // Plugged in, but is in standby mode. May happen when reconnecting a monitor via HDMI.
  40. qInfo() << "Powering on screen.";
  41. initialize();
  42. DisplayComponent::Get().switchToBestOverallVideoMode(0);
  43. }
  44. else if (reason & VC_HDMI_UNPLUGGED)
  45. {
  46. qInfo() << "Screen was unplugged.";
  47. }
  48. }
  49. ///////////////////////////////////////////////////////////////////////////////////////////////////
  50. static void get_modes(std::vector<TV_SUPPORTED_MODE_NEW_T>& modes, HDMI_RES_GROUP_T group)
  51. {
  52. const int max_modes = 256;
  53. size_t count = modes.size();
  54. modes.resize(count + max_modes);
  55. HDMI_RES_GROUP_T preferred_group;
  56. uint32_t preferred_mode;
  57. int got = vc_tv_hdmi_get_supported_modes_new(group, &modes[count], max_modes, &preferred_group, &preferred_mode);
  58. modes.resize(count + got);
  59. }
  60. ///////////////////////////////////////////////////////////////////////////////////////////////////
  61. bool DisplayManagerRPI::initialize()
  62. {
  63. bcm_host_init();
  64. m_displays.clear();
  65. // create the main display
  66. DMDisplayPtr display = DMDisplayPtr(new DMDisplay);
  67. display->m_id = 0;
  68. display->m_name = "Display";
  69. m_displays[display->m_id] = display;
  70. // fills mode array with both CEA and DMT
  71. m_modes.resize(0);
  72. get_modes(m_modes, HDMI_RES_GROUP_CEA); // TV
  73. get_modes(m_modes, HDMI_RES_GROUP_DMT); // PC
  74. for (size_t n = 0; n < m_modes.size(); n++)
  75. {
  76. TV_SUPPORTED_MODE_NEW_T* tvmode = &m_modes[n];
  77. DMVideoModePtr mode = DMVideoModePtr(new DMVideoMode);
  78. mode->m_id = n * 2 + 0;
  79. display->m_videoModes[mode->m_id] = mode;
  80. mode->m_height = tvmode->height;
  81. mode->m_width = tvmode->width;
  82. mode->m_refreshRate = tvmode->frame_rate;
  83. mode->m_interlaced = (tvmode->scan_mode == 1);
  84. mode->m_bitsPerPixel = 32;
  85. mode = DMVideoModePtr(new DMVideoMode(*mode));
  86. mode->m_id = n * 2 + 1;
  87. display->m_videoModes[mode->m_id] = mode;
  88. mode->m_refreshRate /= 1.001;
  89. }
  90. if (m_modes.size() == 0)
  91. return false;
  92. else
  93. return DisplayManager::initialize();
  94. }
  95. ///////////////////////////////////////////////////////////////////////////////////////////////////
  96. bool DisplayManagerRPI::setDisplayMode(int display, int mode)
  97. {
  98. if (!isValidDisplayMode(display, mode))
  99. return false;
  100. HDMI_PROPERTY_PARAM_T property;
  101. property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
  102. property.param1 = (mode % 2) ? HDMI_PIXEL_CLOCK_TYPE_NTSC : HDMI_PIXEL_CLOCK_TYPE_PAL;
  103. property.param2 = 0;
  104. vc_tv_hdmi_set_property(&property);
  105. TV_SUPPORTED_MODE_NEW_T* tvmode = &m_modes[mode / 2];
  106. bool ret = vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, (HDMI_RES_GROUP_T)tvmode->group, tvmode->code) == 0;
  107. if (!ret)
  108. {
  109. qCritical() << "Failed to switch display mode" << ret;
  110. }
  111. return ret;
  112. }
  113. ///////////////////////////////////////////////////////////////////////////////////////////////////
  114. int DisplayManagerRPI::getCurrentDisplayMode(int display)
  115. {
  116. if (!isValidDisplay(display))
  117. return -1;
  118. TV_GET_STATE_RESP_T tvstate;
  119. if (vc_tv_get_state(&tvstate))
  120. return -1;
  121. for (int mode = 0; mode < m_displays[display]->m_videoModes.size(); mode++)
  122. {
  123. TV_SUPPORTED_MODE_NEW_T* tvmode = &m_modes[mode];
  124. if (tvmode->width == tvstate.width &&
  125. tvmode->height == tvstate.height &&
  126. tvmode->frame_rate == tvstate.frame_rate &&
  127. tvmode->scan_mode == tvstate.scan_mode)
  128. {
  129. bool ntsc = false;
  130. HDMI_PROPERTY_PARAM_T property;
  131. property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
  132. property.param1 = 0;
  133. property.param2 = 0;
  134. if (!vc_tv_hdmi_get_property(&property))
  135. ntsc = property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC;
  136. return mode * 2 + (ntsc ? 1 : 0);
  137. }
  138. }
  139. return -1;
  140. }
  141. ///////////////////////////////////////////////////////////////////////////////////////////////////
  142. void DisplayManagerRPI::resetRendering()
  143. {
  144. QGuiApplication *guiApp = (QGuiApplication*)QGuiApplication::instance();
  145. QQuickWindow *window = (QQuickWindow*)guiApp->focusWindow();
  146. if (window)
  147. {
  148. qInfo() << "Recreating Qt UI renderer";
  149. // destroy the window to reset OpenGL context
  150. window->setPersistentOpenGLContext(false);
  151. window->setPersistentSceneGraph(false);
  152. window->destroy();
  153. // Grab the Platform integration private object and recreate it
  154. // this allows to clean / recreate the dispmanx objects
  155. QGuiApplicationPrivate *privateApp = (QGuiApplicationPrivate *)QGuiApplicationPrivate::get(guiApp);
  156. QPlatformIntegration *integration = privateApp->platformIntegration();
  157. if (integration)
  158. {
  159. integration->destroy();
  160. QThread::msleep(500);
  161. integration->initialize();
  162. }
  163. else
  164. {
  165. qCritical() << "Failed to retrieve platform integration";
  166. }
  167. // now recreate the window OpenGL context
  168. window->setScreen(QGuiApplication::primaryScreen());
  169. window->create();
  170. }
  171. }