Changeset View
Changeset View
Standalone View
Standalone View
backends/xrandr/xrandrconfig.cpp
Show First 20 Lines • Show All 102 Lines • ▼ Show 20 Line(s) | |||||
103 | { | 103 | { | ||
104 | delete m_outputs.take(id); | 104 | delete m_outputs.take(id); | ||
105 | } | 105 | } | ||
106 | 106 | | |||
107 | KScreen::ConfigPtr XRandRConfig::toKScreenConfig() const | 107 | KScreen::ConfigPtr XRandRConfig::toKScreenConfig() const | ||
108 | { | 108 | { | ||
109 | KScreen::ConfigPtr config(new KScreen::Config); | 109 | KScreen::ConfigPtr config(new KScreen::Config); | ||
110 | 110 | | |||
111 | auto features = Config::Feature::Writable | Config::Feature::PrimaryDisplay; | 111 | const Config::Features features = Config::Feature::Writable | Config::Feature::PrimaryDisplay | | ||
112 | Config::Feature::OutputReplication; | ||||
112 | config->setSupportedFeatures(features); | 113 | config->setSupportedFeatures(features); | ||
113 | 114 | | |||
114 | KScreen::OutputList kscreenOutputs; | 115 | KScreen::OutputList kscreenOutputs; | ||
115 | 116 | | |||
116 | for (auto iter = m_outputs.constBegin(); iter != m_outputs.constEnd(); ++iter) { | 117 | for (auto iter = m_outputs.constBegin(); iter != m_outputs.constEnd(); ++iter) { | ||
117 | KScreen::OutputPtr kscreenOutput = (*iter)->toKScreenOutput(); | 118 | KScreen::OutputPtr kscreenOutput = (*iter)->toKScreenOutput(); | ||
118 | kscreenOutputs.insert(kscreenOutput->id(), kscreenOutput); | 119 | kscreenOutputs.insert(kscreenOutput->id(), kscreenOutput); | ||
119 | } | 120 | } | ||
Show All 29 Lines | 129 | { | |||
149 | 150 | | |||
150 | for (const XRandROutput *xrandrOutput : m_outputs) { | 151 | for (const XRandROutput *xrandrOutput : m_outputs) { | ||
151 | if (xrandrOutput->isPrimary()) { | 152 | if (xrandrOutput->isPrimary()) { | ||
152 | oldPrimaryOutput = xrandrOutput->id(); | 153 | oldPrimaryOutput = xrandrOutput->id(); | ||
153 | break; | 154 | break; | ||
154 | } | 155 | } | ||
155 | } | 156 | } | ||
156 | 157 | | |||
157 | KScreen::OutputList toDisable, toEnable, toChange; | 158 | KScreen::OutputList toDisable, toEnable, toChange, toReplicate; | ||
158 | 159 | | |||
159 | for (const KScreen::OutputPtr &kscreenOutput : kscreenOutputs) { | 160 | for (const KScreen::OutputPtr &kscreenOutput : kscreenOutputs) { | ||
160 | xcb_randr_output_t outputId = kscreenOutput->id(); | 161 | xcb_randr_output_t outputId = kscreenOutput->id(); | ||
161 | XRandROutput *currentOutput = output(outputId); | 162 | XRandROutput *currentOutput = output(outputId); | ||
162 | //Only set the output as primary if it is enabled. | 163 | //Only set the output as primary if it is enabled. | ||
163 | if (kscreenOutput->isPrimary() && kscreenOutput->isEnabled()) { | 164 | if (kscreenOutput->isPrimary() && kscreenOutput->isEnabled()) { | ||
164 | primaryOutput = outputId; | 165 | primaryOutput = outputId; | ||
165 | } | 166 | } | ||
166 | 167 | | |||
167 | const bool currentEnabled = currentOutput->isEnabled(); | 168 | const bool currentEnabled = currentOutput->isEnabled(); | ||
168 | 169 | | |||
169 | if (!kscreenOutput->isEnabled() && currentEnabled) { | 170 | if (!kscreenOutput->isEnabled() && currentEnabled) { | ||
170 | toDisable.insert(outputId, kscreenOutput); | 171 | toDisable.insert(outputId, kscreenOutput); | ||
171 | continue; | 172 | continue; | ||
172 | } else if (kscreenOutput->isEnabled() && !currentEnabled) { | 173 | } else if (kscreenOutput->isEnabled() && !currentEnabled) { | ||
173 | toEnable.insert(outputId, kscreenOutput); | 174 | toEnable.insert(outputId, kscreenOutput); | ||
174 | ++neededCrtcs; | 175 | ++neededCrtcs; | ||
175 | continue; | 176 | continue; | ||
176 | } else if (!kscreenOutput->isEnabled() && !currentEnabled) { | 177 | } else if (!kscreenOutput->isEnabled() && !currentEnabled) { | ||
177 | continue; | 178 | continue; | ||
178 | } | 179 | } | ||
179 | 180 | | |||
180 | ++neededCrtcs; | 181 | ++neededCrtcs; | ||
181 | 182 | | |||
183 | // Update replication when it is supposed to be a replica or changes not to be anymore. | ||||
184 | if (kscreenOutput->replicationSource() || currentOutput->replicationSource()) { | ||||
185 | if (int sourceId = kscreenOutput->replicationSource()) { | ||||
186 | kscreenOutput->setPos(config->output(sourceId)->pos()); | ||||
187 | } | ||||
188 | if (!toReplicate.contains(outputId)) { | ||||
189 | toReplicate.insert(outputId, kscreenOutput); | ||||
190 | } | ||||
191 | } | ||||
192 | | ||||
182 | if (kscreenOutput->currentModeId() != currentOutput->currentModeId()) { | 193 | if (kscreenOutput->currentModeId() != currentOutput->currentModeId()) { | ||
183 | if (!toChange.contains(outputId)) { | 194 | if (!toChange.contains(outputId)) { | ||
184 | toChange.insert(outputId, kscreenOutput); | 195 | toChange.insert(outputId, kscreenOutput); | ||
185 | } | 196 | } | ||
186 | } | 197 | } | ||
187 | 198 | | |||
188 | if (kscreenOutput->pos() != currentOutput->position()) { | 199 | if (kscreenOutput->pos() != currentOutput->position()) { | ||
189 | if (!toChange.contains(outputId)) { | 200 | if (!toChange.contains(outputId)) { | ||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Line(s) | |||||
275 | } | 286 | } | ||
276 | 287 | | |||
277 | // Grab the server so that no-one else can do changes to XRandR and to block | 288 | // Grab the server so that no-one else can do changes to XRandR and to block | ||
278 | // change notifications until we are done | 289 | // change notifications until we are done | ||
279 | XCB::GrabServer grabber; | 290 | XCB::GrabServer grabber; | ||
280 | 291 | | |||
281 | //If there is nothing to do, not even bother | 292 | //If there is nothing to do, not even bother | ||
282 | if (oldPrimaryOutput == primaryOutput && toDisable.isEmpty() && | 293 | if (oldPrimaryOutput == primaryOutput && toDisable.isEmpty() && | ||
283 | toEnable.isEmpty() && toChange.isEmpty()) { | 294 | toEnable.isEmpty() && toChange.isEmpty() && toReplicate.isEmpty()) { | ||
284 | if (newScreenSize != currentScreenSize) { | 295 | if (newScreenSize != currentScreenSize) { | ||
285 | setScreenSize(newScreenSize); | 296 | setScreenSize(newScreenSize); | ||
286 | } | 297 | } | ||
287 | return; | 298 | return; | ||
288 | } | 299 | } | ||
289 | 300 | | |||
290 | for (const KScreen::OutputPtr &output : toDisable) { | 301 | for (const KScreen::OutputPtr &output : toDisable) { | ||
291 | disableOutput(output); | 302 | disableOutput(output); | ||
292 | } | 303 | } | ||
293 | 304 | | |||
294 | if (intermediateScreenSize != currentScreenSize) { | 305 | if (intermediateScreenSize != currentScreenSize) { | ||
295 | setScreenSize(intermediateScreenSize); | 306 | setScreenSize(intermediateScreenSize); | ||
296 | } | 307 | } | ||
297 | 308 | | |||
298 | bool forceScreenSizeUpdate = false; | 309 | bool forceScreenSizeUpdate = false; | ||
299 | 310 | | |||
300 | for (const KScreen::OutputPtr &output : toChange) { | 311 | for (const KScreen::OutputPtr &output : toChange) { | ||
301 | if (!changeOutput(output)) { | 312 | if (!changeOutput(output)) { | ||
302 | /* If we disabled the output before changing it and XRandR failed | 313 | /* If we disabled the output before changing it and XRandR failed | ||
303 | * to re-enable it, then update screen size too */ | 314 | * to re-enable it, then update screen size too */ | ||
304 | if (toDisable.contains(output->id())) { | 315 | if (toDisable.contains(output->id())) { | ||
305 | //output->setEnabled(false); | 316 | output->setEnabled(false); | ||
306 | qCDebug(KSCREEN_XRANDR) << "Output failed to change: " << output->name(); | 317 | qCDebug(KSCREEN_XRANDR) << "Output failed to change: " << output->name(); | ||
307 | forceScreenSizeUpdate = true; | 318 | forceScreenSizeUpdate = true; | ||
308 | } | 319 | } | ||
309 | } | 320 | } | ||
310 | } | 321 | } | ||
311 | 322 | | |||
312 | for (const KScreen::OutputPtr &output : toEnable) { | 323 | for (const KScreen::OutputPtr &output : toEnable) { | ||
313 | if (!enableOutput(output)) { | 324 | if (!enableOutput(output)) { | ||
314 | //output->setEnabled(false); | | |||
315 | qCDebug(KSCREEN_XRANDR) << "Output failed to be Enabled: " << output->name(); | 325 | qCDebug(KSCREEN_XRANDR) << "Output failed to be Enabled: " << output->name(); | ||
316 | forceScreenSizeUpdate = true; | 326 | forceScreenSizeUpdate = true; | ||
317 | } | 327 | } | ||
318 | } | 328 | } | ||
319 | 329 | | |||
330 | for (KScreen::OutputPtr &output : toReplicate) { | ||||
331 | if (replicateOutput(output)) { | ||||
332 | forceScreenSizeUpdate = true; | ||||
333 | } | ||||
334 | } | ||||
335 | | ||||
320 | if (oldPrimaryOutput != primaryOutput) { | 336 | if (oldPrimaryOutput != primaryOutput) { | ||
321 | setPrimaryOutput(primaryOutput); | 337 | setPrimaryOutput(primaryOutput); | ||
322 | } | 338 | } | ||
323 | 339 | | |||
324 | if (forceScreenSizeUpdate || intermediateScreenSize != newScreenSize) { | 340 | if (forceScreenSizeUpdate || intermediateScreenSize != newScreenSize) { | ||
325 | QSize newSize = newScreenSize; | 341 | QSize newSize = newScreenSize; | ||
davidedmundson: this goes against the pattern above where we build up a changeset list and then apply it
Also… | |||||
326 | if (forceScreenSizeUpdate) { | 342 | if (forceScreenSizeUpdate) { | ||
327 | newSize = screenSize(config); | 343 | newSize = screenSize(config); | ||
328 | qCDebug(KSCREEN_XRANDR) << "Forced to change screen size: " << newSize; | 344 | qCDebug(KSCREEN_XRANDR) << "Forced to change screen size: " << newSize; | ||
329 | } | 345 | } | ||
330 | setScreenSize(newSize); | 346 | setScreenSize(newSize); | ||
331 | } | 347 | } | ||
332 | } | 348 | } | ||
333 | 349 | | |||
▲ Show 20 Lines • Show All 188 Lines • ▼ Show 20 Line(s) | 537 | xOutput->isConnected() ? XCB_RANDR_CONNECTION_CONNECTED : | |||
522 | XCB_RANDR_CONNECTION_DISCONNECTED, | 538 | XCB_RANDR_CONNECTION_DISCONNECTED, | ||
523 | kscreenOutput->isPrimary()); | 539 | kscreenOutput->isPrimary()); | ||
524 | } | 540 | } | ||
525 | return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); | 541 | return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); | ||
526 | } | 542 | } | ||
527 | 543 | | |||
528 | bool XRandRConfig::enableOutput(const OutputPtr &kscreenOutput) const | 544 | bool XRandRConfig::enableOutput(const OutputPtr &kscreenOutput) const | ||
529 | { | 545 | { | ||
530 | xcb_randr_output_t outputs[1] { static_cast<xcb_randr_output_t>(kscreenOutput->id()) }; | | |||
531 | | ||||
532 | XRandRCrtc *freeCrtc = nullptr; | 546 | XRandRCrtc *freeCrtc = nullptr; | ||
533 | qCDebug(KSCREEN_XRANDR) << m_crtcs; | 547 | qCDebug(KSCREEN_XRANDR) << m_crtcs; | ||
534 | 548 | | |||
535 | for (XRandRCrtc *crtc : m_crtcs) { | 549 | for (XRandRCrtc *crtc : m_crtcs) { | ||
536 | crtc->update(); | 550 | crtc->update(); | ||
537 | qCDebug(KSCREEN_XRANDR) << "Testing CRTC" << crtc->crtc() << "\n" | 551 | qCDebug(KSCREEN_XRANDR) << "Testing CRTC" << crtc->crtc() << "\n" | ||
538 | << "\tFree:" << crtc->isFree() << "\n" | 552 | << "\tFree:" << crtc->isFree() << "\n" | ||
539 | << "\tMode:" << crtc->mode() << "\n" | 553 | << "\tMode:" << crtc->mode() << "\n" | ||
Show All 19 Lines | 572 | qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (enable output)" << "\n" | |||
559 | << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() | 573 | << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() | ||
560 | << ")" << "\n" | 574 | << ")" << "\n" | ||
561 | << "\tNew CRTC:" << freeCrtc->crtc() << "\n" | 575 | << "\tNew CRTC:" << freeCrtc->crtc() << "\n" | ||
562 | << "\tPos:" << kscreenOutput->pos() << "\n" | 576 | << "\tPos:" << kscreenOutput->pos() << "\n" | ||
563 | << "\tMode:" << kscreenOutput->currentMode() | 577 | << "\tMode:" << kscreenOutput->currentMode() | ||
564 | << "Preferred:" << kscreenOutput->preferredModeId() << "\n" | 578 | << "Preferred:" << kscreenOutput->preferredModeId() << "\n" | ||
565 | << "\tRotation:" << kscreenOutput->rotation(); | 579 | << "\tRotation:" << kscreenOutput->rotation(); | ||
566 | 580 | | |||
567 | auto cookie = xcb_randr_set_crtc_config(XCB::connection(), freeCrtc->crtc(), | 581 | if (!sendConfig(kscreenOutput, freeCrtc)) { | ||
568 | XCB_CURRENT_TIME, XCB_CURRENT_TIME, | | |||
569 | kscreenOutput->pos().rx(), kscreenOutput->pos().ry(), | | |||
570 | modeId, | | |||
571 | kscreenOutput->rotation(), | | |||
572 | 1, outputs); | | |||
573 | | ||||
574 | XCB::ScopedPointer<xcb_randr_set_crtc_config_reply_t> | | |||
575 | reply(xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr)); | | |||
576 | | ||||
577 | if (!reply) { | | |||
578 | qCDebug(KSCREEN_XRANDR) << "Result: unknown (error)"; | | |||
579 | return false; | 582 | return false; | ||
580 | } | 583 | } | ||
581 | qCDebug(KSCREEN_XRANDR) << "\tResult:" << reply->status; | | |||
582 | 584 | | |||
583 | if (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS) { | 585 | output(kscreenOutput->id())->update(freeCrtc->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED, | ||
584 | XRandROutput *xOutput = output(kscreenOutput->id()); | | |||
585 | xOutput->update(freeCrtc->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED, | | |||
586 | kscreenOutput->isPrimary()); | 586 | kscreenOutput->isPrimary()); | ||
587 | } | 587 | return true; | ||
588 | return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); | | |||
589 | } | 588 | } | ||
590 | 589 | | |||
591 | bool XRandRConfig::changeOutput(const OutputPtr &kscreenOutput) const | 590 | bool XRandRConfig::changeOutput(const KScreen::OutputPtr &kscreenOutput) const | ||
592 | { | 591 | { | ||
593 | XRandROutput *xOutput = output(kscreenOutput->id()); | 592 | XRandROutput *xOutput = output(kscreenOutput->id()); | ||
594 | Q_ASSERT(xOutput); | 593 | Q_ASSERT(xOutput); | ||
594 | | ||||
595 | if (!xOutput->crtc()) { | 595 | if (!xOutput->crtc()) { | ||
596 | qCDebug(KSCREEN_XRANDR) << "Output" << kscreenOutput->id() | 596 | qCDebug(KSCREEN_XRANDR) << "Output" << kscreenOutput->id() | ||
597 | << "has no CRTC, falling back to enableOutput()"; | 597 | << "has no CRTC, falling back to enableOutput()"; | ||
598 | return enableOutput(kscreenOutput); | 598 | return enableOutput(kscreenOutput); | ||
599 | } | 599 | } | ||
600 | 600 | | |||
601 | int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : | 601 | int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : | ||
602 | kscreenOutput->preferredModeId().toInt(); | 602 | kscreenOutput->preferredModeId().toInt(); | ||
603 | 603 | | |||
604 | qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (change output)" << "\n" | 604 | qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (change output)" << "\n" | ||
605 | << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() | 605 | << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() | ||
606 | << ")" << "\n" | 606 | << ")" << "\n" | ||
607 | << "\tCRTC:" << xOutput->crtc()->crtc() << "\n" | 607 | << "\tCRTC:" << xOutput->crtc()->crtc() << "\n" | ||
608 | << "\tPos:" << kscreenOutput->pos() << "\n" | 608 | << "\tPos:" << kscreenOutput->pos() << "\n" | ||
609 | << "\tMode:" << modeId << kscreenOutput->currentMode() << "\n" | 609 | << "\tMode:" << modeId << kscreenOutput->currentMode() << "\n" | ||
610 | << "\tRotation:" << kscreenOutput->rotation(); | 610 | << "\tRotation:" << kscreenOutput->rotation(); | ||
611 | 611 | | |||
612 | if (!sendConfig(kscreenOutput, xOutput->crtc())) { | ||||
613 | return false; | ||||
614 | } | ||||
615 | | ||||
616 | xOutput->update(xOutput->crtc()->crtc(), modeId, | ||||
617 | XCB_RANDR_CONNECTION_CONNECTED, kscreenOutput->isPrimary()); | ||||
618 | return true; | ||||
619 | } | ||||
620 | | ||||
621 | bool XRandRConfig::replicateOutput(const KScreen::OutputPtr &kscreenOutput) const | ||||
622 | { | ||||
623 | XRandROutput *xOutput = output(kscreenOutput->id()); | ||||
624 | Q_ASSERT(xOutput); | ||||
625 | | ||||
626 | if (!xOutput->crtc()) { | ||||
627 | qCDebug(KSCREEN_XRANDR) << "Output" << kscreenOutput->id() | ||||
628 | << "has no CRTC, falling back to enableOutput()"; | ||||
629 | enableOutput(kscreenOutput); | ||||
630 | } | ||||
631 | | ||||
632 | int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : | ||||
633 | kscreenOutput->preferredModeId().toInt(); | ||||
634 | | ||||
635 | qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (change output)" << "\n" | ||||
636 | << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() | ||||
637 | << ")" << "\n" | ||||
638 | << "\tCRTC:" << xOutput->crtc()->crtc() << "\n" | ||||
639 | << "\tPos:" << kscreenOutput->pos() << "\n" | ||||
640 | << "\tMode:" << modeId << kscreenOutput->currentMode() << "\n" | ||||
641 | << "\tRotation:" << kscreenOutput->rotation(); | ||||
642 | | ||||
643 | if (!xOutput->setReplicationSource(kscreenOutput->replicationSource())) { | ||||
644 | return false; | ||||
645 | } | ||||
646 | if (!sendConfig(kscreenOutput, xOutput->crtc())) { | ||||
647 | return false; | ||||
648 | } | ||||
649 | | ||||
650 | xOutput->update(xOutput->crtc()->crtc(), modeId, | ||||
651 | XCB_RANDR_CONNECTION_CONNECTED, kscreenOutput->isPrimary()); | ||||
652 | return true; | ||||
653 | } | ||||
654 | | ||||
655 | bool XRandRConfig::sendConfig(const KScreen::OutputPtr &kscreenOutput, XRandRCrtc *crtc) const | ||||
656 | { | ||||
612 | xcb_randr_output_t outputs[1] { static_cast<xcb_randr_output_t>(kscreenOutput->id()) }; | 657 | xcb_randr_output_t outputs[1] { static_cast<xcb_randr_output_t>(kscreenOutput->id()) }; | ||
658 | const int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : | ||||
659 | kscreenOutput->preferredModeId().toInt(); | ||||
613 | 660 | | |||
614 | auto cookie = xcb_randr_set_crtc_config(XCB::connection(), xOutput->crtc()->crtc(), | 661 | auto cookie = xcb_randr_set_crtc_config(XCB::connection(), crtc->crtc(), | ||
615 | XCB_CURRENT_TIME, XCB_CURRENT_TIME, | 662 | XCB_CURRENT_TIME, XCB_CURRENT_TIME, | ||
616 | kscreenOutput->pos().rx(), kscreenOutput->pos().ry(), | 663 | kscreenOutput->pos().rx(), kscreenOutput->pos().ry(), | ||
617 | modeId, | 664 | modeId, | ||
618 | kscreenOutput->rotation(), | 665 | kscreenOutput->rotation(), | ||
619 | 1, outputs); | 666 | 1, outputs); | ||
620 | 667 | | |||
621 | XCB::ScopedPointer<xcb_randr_set_crtc_config_reply_t> | 668 | XCB::ScopedPointer<xcb_randr_set_crtc_config_reply_t> | ||
622 | reply(xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr)); | 669 | reply(xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr)); | ||
623 | | ||||
624 | if (!reply) { | 670 | if (!reply) { | ||
625 | qCDebug(KSCREEN_XRANDR) << "\tResult: unknown (error)"; | 671 | qCDebug(KSCREEN_XRANDR) << "\tResult: unknown (error)"; | ||
626 | return false; | 672 | return false; | ||
627 | } | 673 | } | ||
628 | qCDebug(KSCREEN_XRANDR) << "\tResult: " << reply->status; | 674 | qCDebug(KSCREEN_XRANDR) << "\tResult: " << reply->status; | ||
629 | | ||||
630 | if (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS) { | | |||
631 | xOutput->update(xOutput->crtc()->crtc(), modeId, | | |||
632 | XCB_RANDR_CONNECTION_CONNECTED, kscreenOutput->isPrimary()); | | |||
633 | } | | |||
634 | return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); | 675 | return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); | ||
635 | } | 676 | } | ||
davidedmundson: why do we send config twice? | |||||
davidedmundson: this method returns an important bool that's not checked |
this goes against the pattern above where we build up a changeset list and then apply it
Also potentially we call changeOutput twice if it's in the toChange above. We want to avoid that