Decide en qué monitor se abre cada aplicación y con qué estado, en Windows 11.
Windows no permite configurar esto. Sólo tiene una heurística de "recordar la última posición" que es poco fiable y no cubre el estado maximizado. MonSelect lo reemplaza por reglas que definís vos.
Descargá MonSelect.App.exe de la última release, dejalo donde quieras y ejecutalo. No necesita instalador ni tener .NET instalado.
Para que arranque con Windows:
MonSelect.App.exe --install-autostart
Registra una tarea programada al iniciar sesión, con privilegios elevados — sin eso no puede mover ventanas de aplicaciones que corren como administrador. Se revierte con --uninstall-autostart.
Al arrancar aparece un icono en la bandeja. Desde ahí se abre la ventana de gestión, o directamente:
MonSelect.App.exe --gui
Para crear una regla: en Ventanas abiertas, elegí la aplicación, acomodá su ventana donde la querés, y confirmá. La regla se escribe desde donde quedó.
La pestaña Reglas muestra cuántas ventanas abiertas matchea cada regla. Un cero significa que esa regla no le aplica a nada — es la forma rápida de ver que algo quedó mal escrito.
| Comando | Qué hace |
|---|---|
--gui |
Abre la ventana de gestión al arrancar |
--apply-now |
Reubica las ventanas abiertas y termina |
--diagnose |
Imprime exe, command line, clase y estado de cada ventana que aparece |
--install-autostart |
Arranca con Windows |
--uninstall-autostart |
Deja de arrancar con Windows |
Cada regla combina los criterios que definas. Todos son opcionales y se aplican en conjunto:
| Criterio | Qué distingue |
|---|---|
exe |
Qué programa |
class |
Qué tipo de ventana dentro del programa — separa la ventana real de las auxiliares |
cmdline |
Qué instancia — permite mandar dos sesiones del mismo programa a monitores distintos |
title |
Qué documento o sesión, cuando lo anterior no alcanza |
Los tres primeros no cambian mientras el proceso viva. El título sí cambia — notificaciones, nombres de archivo, banners — así que la captura no lo incluye salvo que se lo pidas.
exe compara el path completo; class también es exacto. cmdline y title matchean por substring, y los tres menos class aceptan una expresión regular si el valor viene envuelto entre barras:
match:
exe: /\\Discord\\app-[\d.]+\\Discord\.exe$/Eso hace falta para las aplicaciones que se autoactualizan, porque guardan la versión en el path: Discord vive en AppData\Local\Discord\app-1.0.9256\, Claude Desktop y WhatsApp en WindowsApps\Nombre_1.2.3.0_x64__…. Con el path exacto, la regla se rompe sola en el próximo update de esa aplicación. El regex se evalúa contra el path como lo muestra --diagnose, con barras invertidas.
No hace falta escribirlo a mano. La captura reconoce esas dos formas de carpeta versionada y escribe el comodín sola, dejando el resto del path anclado y el nombre del ejecutable intacto — una carpeta de Squirrel tiene varios exe adentro y la regla es para uno. Lo ves en la vista previa antes de confirmar, y el campo exe se edita después desde la pestaña Reglas. Un path sin versión adentro se guarda literal.
Cuando varias reglas matchean, gana la primera del archivo. Sin puntajes ni desempates: el orden lo decidís vos y se ve en la pestaña Reglas.
| Estado | Resultado |
|---|---|
normal |
Rect exacto, en coordenadas visibles y relativas al monitor |
maximized |
Maximizada respetando la taskbar |
minimized |
Arranca minimizada |
borderless |
Sin bordes, cubriendo el monitor completo, tapando la taskbar |
Exclusive fullscreen real no está: requiere que la propia aplicación se lo pida al driver, y ningún programa externo puede forzarlo. borderless es lo que la mayoría de las aplicaciones llaman "pantalla completa".
%APPDATA%\MonSelect\rules.yaml. Se puede editar a mano y se recarga sola al guardar. Si tiene un error de sintaxis, las reglas anteriores siguen en vigor y el error aparece en la ventana.
version: 2
rules:
- name: "Discord"
match:
exe: /\\Discord\\app-[\d.]+\\Discord\.exe$/
title: "Discord$"
place:
monitor: display3
state: normal
rect: [0, 0, 1080, 944]
bleed: 1El rect va en coordenadas del monitor de la regla, no del escritorio: [0, 0, 1080, 944] es la franja de arriba de ese monitor, mida lo que mida el resto y esté donde esté el monitor. Windows reacomoda el origen de las pantallas por su cuenta — al cambiar el driver de video, al conectar o desconectar algo — y un rect en coordenadas del escritorio se queda con los números de la disposición anterior: la ventana aparece corrida unos píxeles y deja ver el fondo de escritorio en el borde.
Un archivo version: 1 (los que escribió MonSelect antes de esto) tiene los rect en coordenadas del escritorio. Se convierten solos en el próximo arranque, sin mover ninguna ventana, y el archivo pasa a version: 2; la conversión queda en el log. Si en ese momento falta un monitor que alguna regla nombra, se espera al arranque siguiente: convertir sin conocer el origen de esa pantalla pondría la ventana en el lugar equivocado cuando vuelva.
Los monitores se referencian por alias. El bloque monitors: se genera solo en el primer arranque, con un identificador estable por pantalla — no el índice, que Windows reasigna al reconectar.
Ese identificador lleva adentro un tramo que Windows reescribe al reinstalar el driver de video o al cambiar la GPU. MonSelect reconoce igual a cada pantalla por su panel y su salida del adaptador, y actualiza los identificadores viejos de rules.yaml en el próximo arranque, dejando la línea en el log. Las reglas siguen funcionando; no hay que reescribirlas. Lo que sí puede quedar viejo es el label, que se genera con el número de DISPLAY — es texto para leer, no afecta a dónde va la ventana.
Algunas aplicaciones dibujan su propio borde de 1px dentro de su ventana, y eso deja ver el escritorio entre dos ventanas pegadas. bleed lo compensa: auto lo mide solo, 0 no compensa, o un número fijo. Así la coordenada del rect sigue diciendo la verdad y la compensación queda declarada aparte.
Requiere el SDK de .NET 10.
dotnet test
dotnet publish src/MonSelect.App/MonSelect.App.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish/win-x64
El documento de diseño está en docs/superpowers/specs/, con las mediciones sobre las que se apoya y las correcciones de lo que resultó estar mal. Los hallazgos empíricos — en qué coordenadas vive WINDOWPLACEMENT, por qué el serial EDID no sirve como identificador de monitor — están en docs/superpowers/findings/.
Conviene saberlo antes de ejecutarlo, porque no es una aplicación corriente:
- Instala un hook global de Windows (
SetWinEventHook) para enterarse cuando aparece una ventana. Recibe eventos de todas las aplicaciones, no sólo de las que tienen regla. - Lee la línea de comandos de otros procesos, leyendo su PEB. Es lo que permite mandar dos sesiones del mismo programa a monitores distintos. Los procesos elevados o de otro usuario no se pueden leer, y sus reglas por
cmdlinesimplemente no matchean. - Mueve y redimensiona ventanas ajenas, y en estado
borderlessles modifica el estilo. El estilo original se guarda para poder revertirlo. - Escribe en
%APPDATA%\MonSelect\— reglas, log y estilos originales. Nada sale de la máquina: no hay telemetría ni conexiones de red. - Con
--install-autostartregistra una tarea programada con privilegios elevados, necesaria para tocar ventanas de aplicaciones que corren como administrador.
El ejecutable no está firmado, así que Windows SmartScreen lo va a marcar como desconocido la primera vez: Más información → Ejecutar de todas formas. Si preferís no confiar en un binario sin firmar, compilalo vos con las instrucciones de arriba.
MIT — ver LICENSE.
El ejecutable publicado es autocontenido e incluye el runtime de .NET y YamlDotNet, ambos MIT. Sus avisos de copyright están en THIRD-PARTY-NOTICES.md.
Sin garantía de ningún tipo, como dice la licencia. Es una herramienta que manipula ventanas de otros programas; probala con algo que no te importe antes de confiarle tu escritorio de trabajo.