#include #include #include #include #include #include #include "DisplayManagerRPI.h" #include "display/DisplayComponent.h" /////////////////////////////////////////////////////////////////////////////////////////////////// void DisplayManagerRPI::tv_callback(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2) { DisplayManagerRPI* obj = (DisplayManagerRPI *)callback_data; emit obj->onTvChange(reason); } /////////////////////////////////////////////////////////////////////////////////////////////////// DisplayManagerRPI::DisplayManagerRPI(QObject* parent) : DisplayManager(parent) { connect(this, &DisplayManagerRPI::onTvChange, this, &DisplayManagerRPI::handleTvChange, Qt::QueuedConnection); vc_tv_register_callback(&tv_callback, (void *)this); } /////////////////////////////////////////////////////////////////////////////////////////////////// DisplayManagerRPI::~DisplayManagerRPI() { vc_tv_unregister_callback_full(&tv_callback, (void *)this); } /////////////////////////////////////////////////////////////////////////////////////////////////// void DisplayManagerRPI::handleTvChange(uint32_t reason) { qInfo() << "tv_service notification:" << reason; if (reason & (VC_HDMI_DVI | VC_HDMI_HDMI)) { // Looks like a mode change - Qt's dispmanx state is probably trashed, recreate it. resetRendering(); } else if (reason & VC_HDMI_ATTACHED) { // Plugged in, but is in standby mode. May happen when reconnecting a monitor via HDMI. qInfo() << "Powering on screen."; initialize(); DisplayComponent::Get().switchToBestOverallVideoMode(0); } else if (reason & VC_HDMI_UNPLUGGED) { qInfo() << "Screen was unplugged."; } } /////////////////////////////////////////////////////////////////////////////////////////////////// static void get_modes(std::vector& modes, HDMI_RES_GROUP_T group) { const int max_modes = 256; size_t count = modes.size(); modes.resize(count + max_modes); HDMI_RES_GROUP_T preferred_group; uint32_t preferred_mode; int got = vc_tv_hdmi_get_supported_modes_new(group, &modes[count], max_modes, &preferred_group, &preferred_mode); modes.resize(count + got); } /////////////////////////////////////////////////////////////////////////////////////////////////// bool DisplayManagerRPI::initialize() { bcm_host_init(); m_displays.clear(); // create the main display DMDisplayPtr display = DMDisplayPtr(new DMDisplay); display->m_id = 0; display->m_name = "Display"; m_displays[display->m_id] = display; // fills mode array with both CEA and DMT m_modes.resize(0); get_modes(m_modes, HDMI_RES_GROUP_CEA); // TV get_modes(m_modes, HDMI_RES_GROUP_DMT); // PC for (size_t n = 0; n < m_modes.size(); n++) { TV_SUPPORTED_MODE_NEW_T* tvmode = &m_modes[n]; DMVideoModePtr mode = DMVideoModePtr(new DMVideoMode); mode->m_id = n * 2 + 0; display->m_videoModes[mode->m_id] = mode; mode->m_height = tvmode->height; mode->m_width = tvmode->width; mode->m_refreshRate = tvmode->frame_rate; mode->m_interlaced = (tvmode->scan_mode == 1); mode->m_bitsPerPixel = 32; mode = DMVideoModePtr(new DMVideoMode(*mode)); mode->m_id = n * 2 + 1; display->m_videoModes[mode->m_id] = mode; mode->m_refreshRate /= 1.001; } if (m_modes.size() == 0) return false; else return DisplayManager::initialize(); } /////////////////////////////////////////////////////////////////////////////////////////////////// bool DisplayManagerRPI::setDisplayMode(int display, int mode) { if (!isValidDisplayMode(display, mode)) return false; HDMI_PROPERTY_PARAM_T property; property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE; property.param1 = (mode % 2) ? HDMI_PIXEL_CLOCK_TYPE_NTSC : HDMI_PIXEL_CLOCK_TYPE_PAL; property.param2 = 0; vc_tv_hdmi_set_property(&property); TV_SUPPORTED_MODE_NEW_T* tvmode = &m_modes[mode / 2]; bool ret = vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, (HDMI_RES_GROUP_T)tvmode->group, tvmode->code) == 0; if (!ret) { qCritical() << "Failed to switch display mode" << ret; } return ret; } /////////////////////////////////////////////////////////////////////////////////////////////////// int DisplayManagerRPI::getCurrentDisplayMode(int display) { if (!isValidDisplay(display)) return -1; TV_GET_STATE_RESP_T tvstate; if (vc_tv_get_state(&tvstate)) return -1; for (int mode = 0; mode < m_displays[display]->m_videoModes.size(); mode++) { TV_SUPPORTED_MODE_NEW_T* tvmode = &m_modes[mode]; if (tvmode->width == tvstate.width && tvmode->height == tvstate.height && tvmode->frame_rate == tvstate.frame_rate && tvmode->scan_mode == tvstate.scan_mode) { bool ntsc = false; HDMI_PROPERTY_PARAM_T property; property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE; property.param1 = 0; property.param2 = 0; if (!vc_tv_hdmi_get_property(&property)) ntsc = property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC; return mode * 2 + (ntsc ? 1 : 0); } } return -1; } /////////////////////////////////////////////////////////////////////////////////////////////////// void DisplayManagerRPI::resetRendering() { QGuiApplication *guiApp = (QGuiApplication*)QGuiApplication::instance(); QQuickWindow *window = (QQuickWindow*)guiApp->focusWindow(); if (window) { qInfo() << "Recreating Qt UI renderer"; // destroy the window to reset OpenGL context window->setPersistentOpenGLContext(false); window->setPersistentSceneGraph(false); window->destroy(); // Grab the Platform integration private object and recreate it // this allows to clean / recreate the dispmanx objects QGuiApplicationPrivate *privateApp = (QGuiApplicationPrivate *)QGuiApplicationPrivate::get(guiApp); QPlatformIntegration *integration = privateApp->platformIntegration(); if (integration) { integration->destroy(); QThread::msleep(500); integration->initialize(); } else { qCritical() << "Failed to retrieve platform integration"; } // now recreate the window OpenGL context window->setScreen(QGuiApplication::primaryScreen()); window->create(); } }