x11: Rework how X11 outputs are queried
Currently, there are a couple of issues with output querying on X11: (a) if an output is changed, for example its transform has been changed, then all outputs will be destroyed and created again (b) it's possible to encounter the case where the platform has no outputs. The X11Platform destroys all outputs, then queries new outputs. The Workspace and AbstractClient sub-classes handle having no outputs very poorly! It's even possible to hit a crash. With this change, outputs will be queried similar to how it's done on Wayland.
This commit is contained in:
parent
205fb923cb
commit
a8810e0c33
3 changed files with 124 additions and 104 deletions
|
@ -37,7 +37,10 @@ QRect X11Output::geometry() const
|
||||||
|
|
||||||
void X11Output::setGeometry(QRect set)
|
void X11Output::setGeometry(QRect set)
|
||||||
{
|
{
|
||||||
m_geometry = set;
|
if (m_geometry != set) {
|
||||||
|
m_geometry = set;
|
||||||
|
Q_EMIT geometryChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int X11Output::refreshRate() const
|
int X11Output::refreshRate() const
|
||||||
|
|
|
@ -461,121 +461,137 @@ void X11StandalonePlatform::updateOutputs()
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void X11StandalonePlatform::doUpdateOutputs()
|
void X11StandalonePlatform::doUpdateOutputs()
|
||||||
{
|
{
|
||||||
auto fallback = [this]() {
|
QVector<AbstractOutput *> changed;
|
||||||
X11PlaceholderOutput *dummyOutput = new X11PlaceholderOutput();
|
QVector<AbstractOutput *> added;
|
||||||
|
QVector<AbstractOutput *> removed = m_outputs;
|
||||||
|
|
||||||
|
if (Xcb::Extensions::self()->isRandrAvailable()) {
|
||||||
|
T resources(rootWindow());
|
||||||
|
if (!resources.isNull()) {
|
||||||
|
xcb_randr_crtc_t *crtcs = resources.crtcs();
|
||||||
|
const xcb_randr_mode_info_t *modes = resources.modes();
|
||||||
|
|
||||||
|
QVector<Xcb::RandR::CrtcInfo> infos(resources->num_crtcs);
|
||||||
|
for (int i = 0; i < resources->num_crtcs; ++i) {
|
||||||
|
infos[i] = Xcb::RandR::CrtcInfo(crtcs[i], resources->config_timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < resources->num_crtcs; ++i) {
|
||||||
|
Xcb::RandR::CrtcInfo info(infos.at(i));
|
||||||
|
|
||||||
|
const QRect geometry = info.rect();
|
||||||
|
if (!geometry.isValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_randr_output_t *outputs = info.outputs();
|
||||||
|
QVector<Xcb::RandR::OutputInfo> outputInfos(outputs ? resources->num_outputs : 0);
|
||||||
|
if (outputs) {
|
||||||
|
for (int i = 0; i < resources->num_outputs; ++i) {
|
||||||
|
outputInfos[i] = Xcb::RandR::OutputInfo(outputs[i], resources->config_timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float refreshRate = -1.0f;
|
||||||
|
for (int j = 0; j < resources->num_modes; ++j) {
|
||||||
|
if (info->mode == modes[j].id) {
|
||||||
|
if (modes[j].htotal != 0 && modes[j].vtotal != 0) { // BUG 313996
|
||||||
|
// refresh rate calculation - WTF was wikipedia 1998 when I needed it?
|
||||||
|
int dotclock = modes[j].dot_clock,
|
||||||
|
vtotal = modes[j].vtotal;
|
||||||
|
if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE)
|
||||||
|
dotclock *= 2;
|
||||||
|
if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
|
||||||
|
vtotal *= 2;
|
||||||
|
refreshRate = dotclock/float(modes[j].htotal*vtotal);
|
||||||
|
}
|
||||||
|
break; // found mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < info->num_outputs; ++j) {
|
||||||
|
Xcb::RandR::OutputInfo outputInfo(outputInfos.at(j));
|
||||||
|
if (outputInfo->crtc != crtcs[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
X11Output *output = findX11Output(outputInfo.name());
|
||||||
|
if (output) {
|
||||||
|
changed.append(output);
|
||||||
|
removed.removeOne(output);
|
||||||
|
} else {
|
||||||
|
output = new X11Output();
|
||||||
|
output->setName(outputInfo.name());
|
||||||
|
added.append(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Perhaps the output has to save the inherited gamma ramp and
|
||||||
|
// restore it during tear down. Currently neither standalone x11 nor
|
||||||
|
// drm platform do this.
|
||||||
|
Xcb::RandR::CrtcGamma gamma(crtcs[i]);
|
||||||
|
|
||||||
|
output->setCrtc(crtcs[i]);
|
||||||
|
output->setGammaRampSize(gamma.isNull() ? 0 : gamma->size);
|
||||||
|
output->setGeometry(geometry);
|
||||||
|
output->setRefreshRate(refreshRate * 1000);
|
||||||
|
|
||||||
|
QSize physicalSize(outputInfo->mm_width, outputInfo->mm_height);
|
||||||
|
switch (info->rotation) {
|
||||||
|
case XCB_RANDR_ROTATION_ROTATE_0:
|
||||||
|
case XCB_RANDR_ROTATION_ROTATE_180:
|
||||||
|
break;
|
||||||
|
case XCB_RANDR_ROTATION_ROTATE_90:
|
||||||
|
case XCB_RANDR_ROTATION_ROTATE_270:
|
||||||
|
physicalSize.transpose();
|
||||||
|
break;
|
||||||
|
case XCB_RANDR_ROTATION_REFLECT_X:
|
||||||
|
case XCB_RANDR_ROTATION_REFLECT_Y:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
output->setPhysicalSize(physicalSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The workspace handles having no outputs poorly. If the last output is about to be
|
||||||
|
// removed, create a dummy output to avoid crashing.
|
||||||
|
if (changed.isEmpty() && added.isEmpty()) {
|
||||||
|
auto dummyOutput = new X11PlaceholderOutput();
|
||||||
m_outputs << dummyOutput;
|
m_outputs << dummyOutput;
|
||||||
Q_EMIT outputAdded(dummyOutput);
|
Q_EMIT outputAdded(dummyOutput);
|
||||||
Q_EMIT outputEnabled(dummyOutput);
|
Q_EMIT outputEnabled(dummyOutput);
|
||||||
};
|
}
|
||||||
|
|
||||||
// TODO: instead of resetting all outputs, check if new output is added/removed
|
// Process new outputs. Note new outputs must be introduced before removing any other outputs.
|
||||||
// or still available and leave still available outputs in m_outputs
|
for (AbstractOutput *output : qAsConst(added)) {
|
||||||
// untouched (like in DRM backend)
|
m_outputs.append(output);
|
||||||
while (!m_outputs.isEmpty()) {
|
Q_EMIT outputAdded(output);
|
||||||
AbstractOutput *output = m_outputs.takeLast();
|
Q_EMIT outputEnabled(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outputs have to be removed last to avoid the case where there are no enabled outputs.
|
||||||
|
for (AbstractOutput *output : qAsConst(removed)) {
|
||||||
|
m_outputs.removeOne(output);
|
||||||
Q_EMIT outputDisabled(output);
|
Q_EMIT outputDisabled(output);
|
||||||
Q_EMIT outputRemoved(output);
|
Q_EMIT outputRemoved(output);
|
||||||
delete output;
|
delete output;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Xcb::Extensions::self()->isRandrAvailable()) {
|
|
||||||
fallback();
|
|
||||||
Q_EMIT screensQueried();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
T resources(rootWindow());
|
|
||||||
if (resources.isNull()) {
|
|
||||||
fallback();
|
|
||||||
Q_EMIT screensQueried();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
xcb_randr_crtc_t *crtcs = resources.crtcs();
|
|
||||||
xcb_randr_mode_info_t *modes = resources.modes();
|
|
||||||
|
|
||||||
QVector<Xcb::RandR::CrtcInfo> infos(resources->num_crtcs);
|
|
||||||
for (int i = 0; i < resources->num_crtcs; ++i) {
|
|
||||||
infos[i] = Xcb::RandR::CrtcInfo(crtcs[i], resources->config_timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < resources->num_crtcs; ++i) {
|
|
||||||
Xcb::RandR::CrtcInfo info(infos.at(i));
|
|
||||||
|
|
||||||
xcb_randr_output_t *outputs = info.outputs();
|
|
||||||
QVector<Xcb::RandR::OutputInfo> outputInfos(outputs ? resources->num_outputs : 0);
|
|
||||||
if (outputs) {
|
|
||||||
for (int i = 0; i < resources->num_outputs; ++i) {
|
|
||||||
outputInfos[i] = Xcb::RandR::OutputInfo(outputs[i], resources->config_timestamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float refreshRate = -1.0f;
|
|
||||||
for (int j = 0; j < resources->num_modes; ++j) {
|
|
||||||
if (info->mode == modes[j].id) {
|
|
||||||
if (modes[j].htotal != 0 && modes[j].vtotal != 0) { // BUG 313996
|
|
||||||
// refresh rate calculation - WTF was wikipedia 1998 when I needed it?
|
|
||||||
int dotclock = modes[j].dot_clock,
|
|
||||||
vtotal = modes[j].vtotal;
|
|
||||||
if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE)
|
|
||||||
dotclock *= 2;
|
|
||||||
if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
|
|
||||||
vtotal *= 2;
|
|
||||||
refreshRate = dotclock/float(modes[j].htotal*vtotal);
|
|
||||||
}
|
|
||||||
break; // found mode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const QRect geo = info.rect();
|
|
||||||
if (geo.isValid()) {
|
|
||||||
xcb_randr_crtc_t crtc = crtcs[i];
|
|
||||||
|
|
||||||
// TODO: Perhaps the output has to save the inherited gamma ramp and
|
|
||||||
// restore it during tear down. Currently neither standalone x11 nor
|
|
||||||
// drm platform do this.
|
|
||||||
Xcb::RandR::CrtcGamma gamma(crtc);
|
|
||||||
|
|
||||||
auto *o = new X11Output(this);
|
|
||||||
o->setCrtc(crtc);
|
|
||||||
o->setGammaRampSize(gamma.isNull() ? 0 : gamma->size);
|
|
||||||
o->setGeometry(geo);
|
|
||||||
o->setRefreshRate(refreshRate * 1000);
|
|
||||||
|
|
||||||
for (int j = 0; j < info->num_outputs; ++j) {
|
|
||||||
Xcb::RandR::OutputInfo outputInfo(outputInfos.at(j));
|
|
||||||
if (outputInfo->crtc != crtc) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QSize physicalSize(outputInfo->mm_width, outputInfo->mm_height);
|
|
||||||
switch (info->rotation) {
|
|
||||||
case XCB_RANDR_ROTATION_ROTATE_0:
|
|
||||||
case XCB_RANDR_ROTATION_ROTATE_180:
|
|
||||||
break;
|
|
||||||
case XCB_RANDR_ROTATION_ROTATE_90:
|
|
||||||
case XCB_RANDR_ROTATION_ROTATE_270:
|
|
||||||
physicalSize.transpose();
|
|
||||||
break;
|
|
||||||
case XCB_RANDR_ROTATION_REFLECT_X:
|
|
||||||
case XCB_RANDR_ROTATION_REFLECT_Y:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
o->setName(outputInfo.name());
|
|
||||||
o->setPhysicalSize(physicalSize);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_outputs << o;
|
|
||||||
Q_EMIT outputAdded(o);
|
|
||||||
Q_EMIT outputEnabled(o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_outputs.isEmpty()) {
|
|
||||||
fallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_EMIT screensQueried();
|
Q_EMIT screensQueried();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
X11Output *X11StandalonePlatform::findX11Output(const QString &name) const
|
||||||
|
{
|
||||||
|
for (AbstractOutput *output : m_outputs) {
|
||||||
|
if (output->name() == name) {
|
||||||
|
return qobject_cast<X11Output *>(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Outputs X11StandalonePlatform::outputs() const
|
Outputs X11StandalonePlatform::outputs() const
|
||||||
{
|
{
|
||||||
return m_outputs;
|
return m_outputs;
|
||||||
|
|
|
@ -84,6 +84,7 @@ private:
|
||||||
*/
|
*/
|
||||||
static bool hasGlx();
|
static bool hasGlx();
|
||||||
|
|
||||||
|
X11Output *findX11Output(const QString &name) const;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void doUpdateOutputs();
|
void doUpdateOutputs();
|
||||||
void updateRefreshRate();
|
void updateRefreshRate();
|
||||||
|
|
Loading…
Reference in a new issue