Linux 07 Wrzesień 2026

Cambio instantáneo de monitores en KDE (`kscreen-doctor` + alias)

También en:

La situación en mi escritorio es esta: un portátil conectado a una estación de acoplamiento, dos monitores externos enchufados a ella, más la pantalla del propio portátil — tres en total. A esos mismos monitores está conectado también un segundo ordenador, de sobremesa. A veces necesito trabajar en ambos a la vez, así que le cedo un monitor al PC (con el botón de selección de entrada) y el portátil tiene que adaptarse. El teclado y el ratón están emparejados con las dos máquinas, así que ahí basta con un botón.

Quedaba un cuello de botella: ir haciendo clics en la Configuración de pantalla de Plasma. Entrar en la configuración, desactivar un monitor, arrastrar los rectángulos, aplicar, confirmar. Una docena de segundos y algo de irritación, varias veces al día. Ahora abro un terminal, escribo d_major y asunto resuelto.

Qué aporta

Cuatro alias que cubren todas las disposiciones que uso:

Alias Efecto
d_set Disposición completa — las tres pantallas para el portátil.
d_major El ultrapanorámico se queda con el portátil, el monitor lateral pasa al PC.
d_minor Al revés — el ultrapanorámico va al PC, el portátil se queda el monitor lateral.
d_disable Ambos externos apagados, queda solo la pantalla del portátil.

Lo esencial es que, una vez cedido un monitor al otro ordenador, el portátil tiene que dejar de verlo. De lo contrario las ventanas se seguirán abriendo en una pantalla que no veo, el cursor se escapará hacia el vacío y el panel de Plasma se quedará en un monitor que ya no está. Cambiar la entrada del monitor no resuelve esto — el portátil sigue manteniendo allí un escritorio activo.

Por qué kscreen-doctor y no xrandr

El reflejo es echar mano de xrandr, pero es un callejón sin salida: xrandr solo funciona en X11, y Plasma 6 arranca en Wayland de forma predeterminada. Bajo Wayland xrandr ve como mucho la capa XWayland y no moverá las pantallas físicas.

kscreen-doctor, en cambio, es la herramienta CLI oficial de la biblioteca KScreen — habla directamente con KWin y funciona igual en X11 y en Wayland. Viene en el paquete libkscreen-bin, así que en la práctica ya lo tienes instalado junto con Plasma.

Su segunda ventaja importa más de lo que parece: aplica la configuración de forma atómica. Todos los ajustes pasados en una sola invocación entran en vigor juntos, en un único recálculo. Hacerlos uno a uno produciría estados intermedios con pantallas solapadas o descuadradas, y Plasma reubicaría las ventanas en cada paso.

Conoce tus salidas

El punto de partida siempre es el mismo:

kscreen-doctor -o

Obtienes una lista de salidas con su nombre, estado, lista de modos y geometría actual. La mía, resumida:

Output: 1 eDP-1     enabled connected priority 1  Panel        Geometry: 1520,1440 1920x1200
Output: 2 DP-5      enabled connected priority 2  DisplayPort  Geometry: 3440,0    1920x1080
Output: 3 DP-6      enabled connected priority 3  DisplayPort  Geometry: 0,0       3440x1440

Tres cosas que conviene extraer de ahí:

  • el nombre de la salida (eDP-1, DP-5, DP-6) — es con él como te diriges a una pantalla en los comandos,
  • Geometry — la posición de la esquina superior izquierda más la resolución,
  • la lista Modes — las resoluciones y frecuencias exactas que aceptará esa pantalla.

También existe kscreen-doctor -j, que devuelve lo mismo en JSON — vendrá bien al final.

Los alias

Toda la configuración cabe en una docena larga de líneas en ~/.bash_aliases:

# DP-6 = ultrapanorámico 3440x1440@100, DP-5 = lateral 1920x1080@60, eDP-1 = panel del portátil 1920x1200@60
alias d_set='kscreen-doctor \
output.DP-6.enable output.DP-6.position.0,0 output.DP-6.mode.3440x1440@100 \
output.DP-5.enable output.DP-5.position.3440,0 output.DP-5.mode.1920x1080@60 \
output.eDP-1.enable output.eDP-1.position.1520,1440 output.eDP-1.mode.1920x1200@60'

alias d_major='kscreen-doctor \
output.DP-6.enable output.DP-6.position.0,0 output.DP-6.mode.3440x1440@100 \
output.DP-5.disable \
output.eDP-1.enable output.eDP-1.position.1520,1440 output.eDP-1.mode.1920x1200@60'

alias d_minor='kscreen-doctor \
output.DP-6.disable \
output.DP-5.enable output.DP-5.position.0,0 output.DP-5.mode.1920x1080@60 \
output.eDP-1.enable output.eDP-1.position.0,1080 output.eDP-1.mode.1920x1200@60'

alias d_disable='kscreen-doctor output.DP-6.disable output.DP-5.disable'

alias d_off="echo 'Apago la pantalla en 2 s.'; sleep 2; kscreen-doctor --dpms off"

Tras editarlo basta con source ~/.bash_aliases o un terminal nuevo.

Cómo funciona

La sintaxis es uniforme: output.<nombre>.<ajuste>[.<valor>], y pasas tantos ajustes como haga falta en una sola invocación.

Las posiciones viven en un espacio compartido

position.X,Y son las coordenadas de la esquina superior izquierda de la pantalla en el espacio de coordenadas común a todos los monitores. El origen 0,0 es arbitrario — es simplemente la esquina de la pantalla que coloques primero. Mi disposición predeterminada es esta:

x=0                               x=3440         x=5360
+---------------------------------+--------------+  y=0
|                                 | DP-5         |
|  DP-6   3440x1440 @100          | 1920x1080    |
|  (ultrapanorámico)              | @60          |
|                                 +--------------+  y=1080
|                                 |
+----------------+----------------+                 y=1440
                 | eDP-1          |
                 | 1920x1200 @60  |
                 | (portátil)     |
                 +----------------+                 y=2640
                x=1520        x=3440

El portátil está físicamente debajo del ultrapanorámico, de ahí y=1440 — exactamente la altura de la pantalla que tiene encima. x=1520 lo alinea con el borde derecho del ultrapanorámico (1520 + 1920 = 3440), porque así está colocado en el escritorio; si lo quisieras centrado, sería (3440 − 1920) / 2 = 760. Conviene calcular estos valores de forma consciente: si dejas un hueco entre pantallas, el cursor se detendrá en su borde.

Ese fue, por cierto, un error real en mi versión antigua de los alias. En d_minor desactivaba el ultrapanorámico, pero el monitor lateral se quedaba en la posición 3440,0 — es decir, en el vacío dejado por una pantalla que ya no está. De ahí que en la versión de arriba el lateral acabe en 0,0, con el portátil debajo.

Los modos aceptan frecuencias redondeadas

mode.3440x1440@100 funciona aunque -o informe de ese modo como 3440x1440@99.98. kscreen-doctor empareja los modos por la frecuencia redondeada, así que no hace falta copiar decimales. Si no encuentra el modo, el comando simplemente da un error y no cambia nada — la atomicidad también juega a tu favor con las erratas.

enable hay que indicarlo explícitamente

Fijar la posición y el modo por sí solo no encenderá una pantalla desactivada. Por eso cada variante lleva un output.<nombre>.enable explícito — también para el panel del portátil. Sin él, tras d_disable (o tras apagar el panel a mano estando acoplado) los alias no serían capaces de recuperarlo.

Apagado de pantalla

kscreen-doctor --dpms off apaga las pantallas hasta el primer movimiento del ratón. Ojo: --dpms solo funciona en Wayland. En X11 hay que hacerlo a la antigua, con xset dpms force off.

La trampa: los nombres de las salidas pueden cambiar

Con esta me estrellé. Mis alias dejaron de funcionar de un día para otro porque los monitores habían cambiado de nombre: DP-3-2 pasó a ser DP-6, y DP-3-1-6 pasó a ser DP-5.

El nombre de una salida es el nombre del conector asignado por el controlador gráfico, no un identificador del monitor. Puede cambiar al pasar a otra estación de acoplamiento o a otro puerto, tras una actualización del núcleo o del controlador, y con monitores encadenados por DisplayPort MST (lo típico en los docks) se añade además la numeración de las ramas de la cadena — de ahí esos nombres de varios segmentos como DP-3-1-6. El arreglo es trivial, pero hay que saber dónde mirar: kscreen-doctor -o y reescribir los nombres.

Si prefieres inmunizarte contra eso, puedes deducir el nombre a partir de la resolución nativa del monitor, que es bastante más estable que el nombre del conector:

# Devuelve el nombre de la salida cuyo modo nativo tiene la resolución indicada.
d_out() {
  kscreen-doctor -j | jq -r --arg res "$1" '
    .outputs[] | select(.connected) | . as $o
    | ($o.preferredModes[0] // $o.currentModeId) as $pm
    | select([$o.modes[] | select(.id == $pm) | "\(.size.width)x\(.size.height)"] | index($res))
    | $o.name' | head -1
}

El alias se convierte entonces en una función que resuelve los nombres solo en el momento de la llamada — y por eso sobrevive a un cambio de nombre del conector:

d_major() {
  local uw lcd side
  uw=$(d_out 3440x1440); lcd=$(d_out 1920x1200); side=$(d_out 1920x1080)
  [ -n "$uw" ] && [ -n "$lcd" ] || { echo "display: falta una pantalla" >&2; return 1; }
  kscreen-doctor \
    output."$uw".enable output."$uw".position.0,0 output."$uw".mode.3440x1440@100 \
    ${side:+output."$side".disable} \
    output."$lcd".enable output."$lcd".position.1520,1440 output."$lcd".mode.1920x1200@60
}

La comprobación de nombres vacíos no es un adorno. Si d_out no encontrara nada, sin ella la llamada llevaría un argumento output..enable y kscreen-doctor rechazaría toda la invocación con un error de análisis poco esclarecedor. Mejor decir sin rodeos qué pantalla falta. El monitor lateral queda opcional gracias a ${side:+...} — si no está enchufado, sencillamente no hay nada que desactivar.

Una función no puede llamarse igual que un alias

Caí en esta trampa al migrar de alias a funciones. Si en el archivo se queda alias d_major=... y debajo añades una función con el mismo nombre, el source revienta en un punto que explica bien poco:

bash: .bashrc: línea 136: error sintáctico cerca del elemento inesperado `('
bash: .bashrc: línea 136: `d_major() {'

El motivo es que bash expande los alias antes de analizar la sintaxis. El token d_major queda sustituido por el cuerpo del alias, así que el analizador ve algo como kscreen-doctor output... () { — y protesta con razón en el paréntesis. El mensaje señala a la función, cuando el culpable es un alias decenas de líneas más arriba.

Y aquí viene la segunda capa de esa trampa, con la que me topé justo después. Borrar los alias del archivo no basta, porque los alias cargados por una versión anterior del archivo siguen vivos en la sesión ya abierta. El archivo estaba limpio y, aun así, source ~/.bashrc en la misma terminal seguía reventando — esta vez en d_set() {. Una terminal nueva funcionaba sin problemas, rasgo engañoso donde los haya, porque apunta a un problema del archivo y no del estado de la sesión.

Por eso la línea de limpieza debe quedarse en el archivo de forma permanente, por encima de las definiciones, y no solo mientras dura la migración:

unalias d_set d_major d_minor d_disable d_off d_show 2>/dev/null

Funciona porque bash lee y ejecuta los comandos uno a uno al hacer source — el unalias llega a ejecutarse antes de que el analizador alcance las definiciones de más abajo. Un efecto secundario agradable es la idempotencia: el archivo se puede cargar tantas veces como quieras en la misma sesión.

Conviene además prefijar los nombres auxiliares con un guion bajo (_d_out) para que no choquen con nada en el futuro.

Una pequeña curiosidad para terminar: el número de línea del mensaje puede estar desplazado. Los alias antiguos eran multilínea (con \ al final), y su expansión inyecta esas líneas en el flujo que bash cuenta — en mi caso el error se informó tres líneas por debajo de donde estaba realmente la definición de la función. Busca por el nombre que aparece en el mensaje, no por el número.

Comparo deliberadamente solo el modo nativo (preferredModes) y no toda la lista de modos. Casi cualquier monitor admite 1920x1080, así que buscar por un modo cualquiera acertaría en la primera pantalla que apareciera. La resolución nativa es, en la práctica, única para cada una de mis tres pantallas. El precio es que dos modelos idénticos uno al lado del otro habrá que distinguirlos de otra manera — por ejemplo por sizeMM, o sencillamente por el nombre del conector.

Limitaciones (compromisos conscientes)

  • Los nombres de las salidas no son estables. La versión sencilla con alias es legible, pero requiere un arreglo puntual tras cambiar de dock o de controlador. La versión con d_out es robusta, pero menos evidente de leer.
  • Fuera de una sesión gráfica hay que aportar el entorno. Ejecutado desde cron, desde una unidad de systemd o desde una herramienta agéntica, kscreen-doctor revienta por falta de wl_display. Necesita XDG_RUNTIME_DIR, WAYLAND_DISPLAY y DBUS_SESSION_BUS_ADDRESS de una sesión viva. En un terminal normal todo eso ya está definido.
  • Algunas opciones son exclusivas de Wayland — --dpms y el escalado fraccionario. En X11 hay que buscar sustitutos.
  • Esto no sustituye a los perfiles de KScreen. Plasma recuerda por su cuenta la disposición para cada conjunto de monitores y la restaura al acoplar. Los alias sirven para el cambio manual y deliberado dentro del mismo conjunto — justo donde el automatismo no tiene forma de adivinar lo que quiero.

Personalización

  • Elegir la pantalla principal — output.DP-6.priority.1 traslada allí el panel y las ventanas predeterminadas; el resto de salidas reciben los números siguientes.
  • Escalado y rotación — output.DP-5.scale.1.5, output.DP-5.rotation.left. Útil con un monitor puesto en vertical.
  • HDR y brillo — output.DP-6.hdr.enable, output.DP-6.brightness.60.
  • Sin terminal — los mismos comandos se pueden asociar a atajos de teclado en Preferencias del sistema → Atajos → Añadir orden. Así la disposición de pantallas cambia con una sola pulsación.

kscreen-doctor --help ofrece la lista completa de ajustes con ejemplos.

Resumen

Una docena larga de líneas en ~/.bash_aliases convierte «entra en la configuración y arrastra rectángulos» en una sola palabra en el terminal. Trabajando en dos ordenadores que comparten los mismos monitores, esa es la diferencia entre un pequeño ritual varias veces al día y algo en lo que dejas de pensar. Y kscreen-doctor ya está en el disco de cualquiera que use Plasma — con kscreen-doctor -o basta para empezar.