No se puede ocultar una ventana de Zoom en macOS 15
En macOS moderno, no es posible ocultar de forma confiable una ventana de captura de pantalla — ni desde otra aplicación, ni siquiera desde su propia aplicación. Esta es la historia técnica de la eliminación de CGSSetWindowCaptureExcluded, del silencioso ignorado de SLSSetWindowSharingState entre procesos, y de por qué NSWindow.sharingType = .none es una sugerencia suave para Zoom y QuickTime, y no una garantía dura.
Un breve post-mortem de ingeniería sobre la construcción de una aplicación de "ventana privada" que no funciona del todo.
La idea
Como muchas personas, trabajo con una ventana de Claude abierta durante las reuniones. Y como muchas personas, a veces comparto mi pantalla en Zoom y prefiero que esa ventana no sea visible para todos los participantes. No por nada sensible — simplemente porque es un estado de trabajo privado.
Sabía que aplicaciones como 1Password y Hand Mirror pueden ocultar sus propias ventanas de grabaciones. Pensé que podía construir una pequeña utilidad de barra de menú que me permitiera seleccionar cualquiera aplicación — Claude, Notas, cualquier otra — y alternar su visibilidad para que no aparezca en grabaciones, manteniéndola completamente visible e interactiva en mi pantalla.
Proyecto de fin de semana. Dos horas, como máximo.
Fue más largo. Y la conclusión es: en macOS moderno, no se puede hacer esto. No para otra aplicación, y — como resulta— tampoco de forma confiable para tu propia aplicación.
Esta es la historia de ingeniería de por qué.
Intento 1: ocultar la ventana de otra aplicación
La forma clásica de hacer que una ventana de macOS no sea visible en grabaciones es NSWindow.sharingType = .none. Pero este es un indicador por ventana que solo puede ser establecido por el proceso propietario sobre sus propias ventanas. AppKit no permite que accedas a la lista de ventanas de otra aplicación.
La solución bien conocida, utilizada durante años por herramientas como Hand Mirror y diversos utilidades de bloqueo de pantalla, es la función privada SkyLight:
OSStatus CGSSetWindowCaptureExcluded(CGSConnectionID cid, CGWindowID wid, bool excluded);
Enumeras las ventanas mediante CGWindowListCopyWindowInfo, filtras para el PID de la aplicación objetivo y llamas a esta función en cada ID de ventana. Como la llamada pasa por el WindowServer (no por el proceso propietario), puede excluir cualquier ventana de la grabación.
Conecté esto mediante dlsym en /System/Library/PrivateFrameworks/SkyLight.framework/..., construí la herramienta, la ejecuté en macOS 15.3.1 y obtuve:
[InvisibleApp] symbol not found: CGSSetWindowCaptureExcluded
[InvisibleApp] symbol not found: SLSSetWindowCaptureExcluded
La función es desaparecida. Apple la eliminó. Busqué una versión renombrada (SLSSetWindowExcludedFromCapture, CGSSetWindowSharingState, todas las variantes evidentes). La mayoría no existen, pero encontré dos que sí:
SLSSetWindowSharingState(CGSConnectionID, CGWindowID, int sharingState)— la llamada subyacente queNSWindow.sharingType = .noneusa internamente. Compartir estado0esNSWindowSharingNone.SLSGetWindowOwner— te da el ID de conexión del proceso propietario de la ventana.
Así que rehice el puente para llamar a SLSSetWindowSharingState en la ventana objetivo, probando tanto mi conexión principal como la conexión del proceso propietario de la ventana.
Construí, ejecuté, alterné, el registro indica que devolvió noErr, tomé una captura de pantalla — Claude sigue en la captura. Intenté Zoom — Claude sigue en la compartición.
SLSSetWindowSharingState Éxito cuando se llama entre procesos, pero el WindowServer lo ignora silenciosamente. Solo el proceso propietario puede cambiar el estado de su propia ventana en macOS 15.
También confirmé que la salida obvia está cerrada: DYLD_INSERT_LIBRARIES la inyección en el proceso de Claude está bloqueada porque Claude (como la mayoría de las aplicaciones modernas) tiene el entorno hardening habilitado y sin el disable-library-validation entitlement.
Así que eso está resuelto: ninguna aplicación de terceros puede hacer que la ventana de otra aplicación sea invisible para grabaciones en macOS 15. La conclusión coincide con lo que dice Apple: el estado de grabación por ventana es exclusión del propio proceso, punto final.
Intento 2: ocultar nuestra propia ventana
Plan B: cambiar el producto. En lugar de una herramienta que oculte a Claude, construiré una pequeña ventana de chat propia — misma función, forma diferente. Mi ventana, mi sharingType = .none. Este es el mecanismo bien conocido que utilizan todas las herramientas de ocultación de capturas, y es una línea de código:
window.sharingType = .none
Conectar un chat en streaming con la API Anthropic Messages (o con el CLI local para autenticación por suscripción), establecer claude en la ventana de chat, entregarla. Esta es una API pública, clara y soportada. sharingType = .none Construida. Registré el valor real de exclusión en tiempo de ejecución para asegurarme de que macOS lo aceptó:
Tomé una captura de pantalla — la ventana de chat desapareció correctamente. ✅
[InvisibleApp] window 151526 sharingType=0
Grabé la pantalla con QuickTime — la ventana de chat apareció en la grabación. ❌
Compartí la pantalla en Zoom — la ventana de chat era visible para el otro lado. ❌
Esto fue inesperado. El objetivo principal es que esta ventana no aparezca en estos flujos. Así que construí una prueba interna en la aplicación que realice una captura de pantalla usando ScreenCaptureKit (la API moderna y pública de captura), el mismo marco que utiliza QuickTime en el fondo. Probé tanto captura de un solo momento (
Esto fue inesperado. El todo punto de NSWindowSharingNone es obtenerlo fuera de estos flujos. Así que construí una prueba interna dentro de la aplicación que realiza una captura de pantalla utilizando ScreenCaptureKit (la API de captura pública moderna), el mismo marco que utiliza QuickTime en el interior. Intenté tanto la captura en un solo disparo (SCScreenshotManager.captureImage) como captura continua (SCStream) — ambas produjeron una imagen PNG en la que correctamente se excluyó la ventana de chat.
Así que:
| Método de captura | Oculta nuestra ventana? |
|---|---|
| Cmd-Shift-3/4/5 (captura de sistema) | ✅ |
| Captura de un solo momento de SCK de nuestra app | ✅ |
| Captura continua de SCK de nuestra app | ✅ |
| QuickTime “Nueva grabación de pantalla” | ❌ |
| Zoom “Compartir pantalla → Escritorio” | ❌ |
Mismo sistema, misma máquina, misma ventana, mismo sharingType = .none. Lo único que cambia es quién realiza la captura.
Rutas privilegiadas de captura
Esta es la parte que la mayoría de los ingenieros no entienden: en macOS 15, la captura de pantalla no es una sola API; es al menos dos — una pública y otra privada con entitidades adicionales.
Extraí las entidades de QuickTime Player:
codesign -d --entitlements - /System/Applications/QuickTime\ Player.app
com.apple.private.screencapturekit.noprompt = true
com.apple.private.tcc.allow = [
kTCCServiceMicrophone,
kTCCServiceCamera,
kTCCServiceScreenCapture,
]
Esas com.apple.private.* claves solo pueden ser otorgadas por Apple a sus binarios de primeros tercios. Usted y yo no podemos solicitarlas. Serían rechazadas por la notarización. El App Store, sin duda, también lo haría.
Y lo que otorgan, además de omitir el aviso de permiso de captura, es la capacidad de capturar una imagen de la pantalla antes de que el WindowServer aplique el filtro de exclusión. La ventana está en el framebuffer que sale — las herramientas de Apple simplemente ven todo.
Zoom utiliza un camino análogo privilegiado. Podría ser una extensión del kernel, una extensión del sistema o una API privada obtenida al ser una aplicación de conferencia aprobada por mucho tiempo — no he investigado lo suficiente para decir cuál. Pero el comportamiento es el mismo: el mecanismo estándar de exclusión no se aplica a él.
Lo que esto significa
Si eres un desarrollador de terceros y tu objetivo es “esta ventana nunca debe aparecer en una grabación en cualquier Mac”, no puedes garantizarlo en macOS 15. Tu ventana está oculta de todos los métodos de captura que puedes escribir tú mismo. Está oculta de Google Meet (navegador → getDisplayMedia → SCK), Microsoft Teams (SCK), OBS (SCK), cada herramienta de terceros que cumpla. Pero Apple y sus aplicaciones de conferencia aprobadas aún pueden verla.
La característica de "protección de captura de pantalla" de 1Password / Hand Mirror tiene la misma limitación. La gente simplemente no graba su cofre de 1Password con QuickTime, así que el problema no se nota.
La síntesis honesta que daría a Apple sobre esto: publique una entidad pública equivalente a com.apple.private.screencapturekit.noprompt, o comprometa que NSWindow.sharingType = .none sea una garantía firme contra todas rutas de captura. Hasta macOS 15.3.1, no es así — es una garantía firme contra la mayoría de rutas y una sugerencia suave que las herramientas privilegiadas pueden ignorar. El estado actual crea una postura de seguridad en la que los usuarios creen que una ventana está invisible durante la captura cuando en realidad no lo está, lo cual es peor que no tener protección en absoluto.
Las soluciones alternativas, ordenadas por cuánto dañan
Para mi caso — mantener una ventana de chat oculta durante una compartición en Zoom — todas funcionan y ninguna es ideal:
- Compartir una sola ventana en Zoom, no toda la pantalla. El diálogo de compartir pantalla tiene una pestaña “Ventana”. Zoom captura solo los píxeles de esa ventana y nada más, así que mi chat — y todo lo demás en pantalla — desaparece automáticamente. Esta es la solución más confiable y requiere cero código. Inconveniente: los espectadores no ven el contexto general de la pantalla.
- Usar Meet o Teams en lugar de Zoom. Ambas respetan
sharingType = .none. Esto es excelente si controlas la herramienta de reunión, terrible si no. - Colocar el chat en un espacio separado. Mission Control te da múltiples escritorios. Comparte el Espacio 1, mantén el chat en el Espacio 2. Desliza para interactuar. Lento, rompe el flujo, pero funciona.
- Usar un segundo dispositivo. Un teléfono o iPad junto a tu laptop es imbatible por cualquier herramienta de captura, por definición.
Opté por la opción 1 más la aplicación de chat que ya construí. El chat está invisible para aproximadamente 95% de rutas de captura, y en el caso de compartir pantalla en Zoom, simplemente no uso compartir pantalla en el escritorio.
Lo que desearía tener a continuación
- Una entidad pública que permita a una aplicación notarizada declarar “esta ventana mía está excluida de todas captura, incluso para llamadores privilegiados”. Hoy en día, eso no es posible.
- Honestidad en
NSWindow.sharingTypedocumentación. Las documentaciones hacen que suene como una garantía firme. No lo es — al menos deben mencionar que las herramientas privilegiadas de Apple y las aplicaciones con entitidades privadas pueden capturar sin importar. Hoy en día no lo hacen. - Una forma de inspeccionar cuáles son las aplicaciones en un sistema que tienen acceso privilegiado a captura — análoga a “Archivos y Carpetas” en Privacidad y Seguridad. Hoy en día no hay ninguna superficie para que los usuarios vean quién puede ignorar su privacidad.
Hasta que cualquiera de eso se implemente, la recomendación práctica es: asume que cualquier ventana en tu pantalla es capturable por el sistema y por las principales aplicaciones de conferencia, independientemente de lo que sharingType establezcas. Los APIs de exclusión son reales — funcionan contra la mayoría del mundo — pero no son una frontera de seguridad que puedas confiar frente a las partes del mundo que importan más.
Código
La fuente completa de la aplicación de barra de menú de chat — incluyendo el puente SkyLight, la prueba interna basada en flujo de SCK y los experimentos que confirman la conclusión — está disponible aquí (este directorio). Es aproximadamente 700 líneas de Swift, binario único, sin dependencias. Útil como referencia para cualquiera que esté buscando NSWindow.sharingType = .none y se pregunte qué es lo que realmente obtienen.
La versión resumida para ese público:
NSWindow.sharingType = .nonees la API correcta. Solo que no es tan fuerte como crees. Prueba contra la específica herramienta que usan tus usuarios para compartir, no contra capturas y tu propio código de SCK, antes de prometer cualquier cosa.
Instalar extensiones
Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas
恵 ¡El marcador ha llegado!
Marcador es una forma divertida de llevar un registro de tus juegos, todos los datos se almacenan en tu navegador. ¡Próximamente habrá más funciones!
Herramientas clave
Ver todo Los recién llegados
Ver todoActualizar: Nuestro última herramienta se agregó el 26 abr. 2026
