123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- #include <QGuiApplication>
- #include <QQmlApplicationEngine>
- #include <QQuickWindow>
- #include <QDebug>
- #include <private/qguiapplication_p.h>
- #include <qpa/qplatformintegration.h>
- #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<TV_SUPPORTED_MODE_NEW_T>& 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();
- }
- }
|