Files
jellyfin-desktop/src/display/rpi/DisplayManagerRPI.cpp
T
Andrew Rabert c7e9cf7486 Change to Qt 6
Co-authored by: Izzie Walton <ian@iwalton.com>
Co-authored by: Satadru Pramanik <satadru@gmail.com>
Co-authored by: sevenrats <sevenrats@tutanota.com>
Co-authored by: xB <65196493+xBiei@users.noreply.github.com>
Co-authored by: zjeffer <4633209+zjeffer@users.noreply.github.com>
2025-11-22 18:16:00 -05:00

204 lines
6.3 KiB
C++

#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->setPersistentGraphics(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();
}
}