Skip to content

Latest commit

 

History

History
255 lines (190 loc) · 18.5 KB

File metadata and controls

255 lines (190 loc) · 18.5 KB
SleepSwitch

SleepSwitch

Один клик в строке меню — и MacBook перестаёт засыпать, в том числе с закрытой крышкой.

Релиз Сборка Загрузки macOS Лицензия

English version


caffeinate держит Mac бодрым ровно до закрытия крышки — дальше он всё равно засыпает, потому что сон по крышке это отдельная настройка, спрятанная за sudo. SleepSwitch переключает обе сразу, одной иконкой в строке меню.

Пригождается, когда надо закрыть ноутбук, но оставить работать сборку, закачку, рендер или SSH-сессию — без внешнего монитора, без заглушки в HDMI и без приоткрытой крышки.

Что умеет

  • Не спит при закрытой крышке. Захлопнули — продолжает работать.
  • Не спит по бездействию. Пока режим включён, таймер сна игнорируется.
  • Не гасит экран, пока режим включён — отдельной настройки нет и думать не о чем.
  • Пароль спрашивается один раз. Установщик ставит узкое правило sudo, дальше переключение идёт без запросов.
  • Не высадит батарею. Режим сам выключается ниже выбранного заряда, а по желанию — сразу при отключении от сети.
  • Даёт знать, что сработало. Тихий тон при закрытии крышки и другой при открытии.
  • Ничего не остаётся висеть. Выход, принудительное завершение, падение системы — запрет сна снимется в любом случае.
  • Обновляется с GitHub — проверяет раз в сутки, отдаёт установщик и никогда не подменяет бинарники втихаря.
  • Русский и английский, и приложение, и установщик, по языку системы.
  • Чистый Swift, без зависимостей, без фонового демона, без телеметрии.
  • Universal binary, Apple Silicon и Intel.

Установка

Скачайте .pkg из последнего релиза и откройте.

Note

Приложение не подписано сертификатом Apple, поэтому первый раз macOS откажется его открывать. Начиная с macOS 15 Sequoia правая кнопка это больше не обходит: откройте .pkg, дайте себя заблокировать, затем зайдите в Системные настройки → Конфиденциальность и безопасность — там в разделе «Безопасность» появится кнопка «Всё равно открыть». На macOS 14 и старше — правой кнопкой по файлу → «Открыть» → в диалоге ещё раз «Открыть». В любом случае один раз.

Установщик кладёт приложение в /Applications, запускает его и — если не снять галочку на шаге «Настроить» — ставит правило sudo для переключения без пароля.

Управление

Действие Результат
Левый клик по иконке Включить или выключить режим
Правый клик Меню: автозапуск, вход в систему, обновления, правило sudo

Иконка и есть индикатор:

Значок Что означает
🌙 Обычный режим — Mac засыпает как настроено
☕️ Режим включён — сон полностью запрещён
⚠️ Частично — сон по бездействию запрещён, но крышка усыпит

Как устроено

Два независимых слоя, потому что macOS считает это разными вещами:

Слой Что запрещает Нужен root
pmset -a disablesleep 1 Сон по крышке и вообще любой сон Да
Ассершен PreventUserIdleSystemSleep Сон по бездействию Нет
Ассершен PreventUserIdleDisplaySleep Гашение экрана Нет

Ассершены держит сам процесс, поэтому они исчезают вместе с ним — залипнуть не могут. Настройка pmset живёт дольше процесса, поэтому приложение снимает её при выходе и по SIGTERM, а при запуске показывает реальное положение дел, читая SleepDisabled прямо из IOPMrootDomain. Отказы, до которых не доходит ни один обработчик, закрыты отдельно — см. Если приложение умерло с включённым запретом.

Про правило sudo

pmset disablesleep требует root. Спрашивать пароль на каждое переключение невозможно, поэтому установщик пишет /etc/sudoers.d/sleepswitch:

вы ALL=(root) NOPASSWD: /usr/bin/pmset -a disablesleep 0
вы ALL=(root) NOPASSWD: /usr/bin/pmset -a disablesleep 1

Две команды, без шаблонов. sudo сверяет аргументы точно, так что правило даёт ровно одну возможность — переключить настройку сна. pmset -a sleep 0 по-прежнему просит пароль.

Файл готовится, проверяется через visudo и ставится целиком под root во временной папке с правами 0700 — подменить его между проверкой и установкой некому. Имя учётной записи проверяется до подстановки в sudoers, и правило выдаётся только администратору.

Отказаться можно на шаге установки, потом через меню — или руками:

sudo rm /etc/sudoers.d/sleepswitch

Звук крышки

При включённом режиме закрытие крышки играет приглушённый фарфоровый тон, открытие — тон повыше: достаточно, чтобы убедиться, что Mac не уснул, ничего не открывая для проверки. Выключается в меню, а при выключенном режиме не звучит вовсе — закрытие крышки тогда просто означает сон.

Тоны синтезируются во время сборки скриптом Tools/make-sounds.swift, так что аудиофайлов в репозитории нет. На Mac без крышки нет ни звуков, ни самой настройки.

Если приложение умерло с включённым запретом

IOKit-ассершены залипнуть не могут — они принадлежат процессу и исчезают вместе с ним. С настройкой pmset иначе: она переживает SIGKILL, принудительное завершение и панику ядра, а до обработчиков приложения ни один из этих случаев не доходит. Без присмотра это означает Mac, который не засыпает, без иконки в строке меню, объясняющей почему, и без предохранителя по батарее — он работал в том самом процессе, который только что умер.

Поэтому запрет сверяет launch-агент: при входе в систему и раз в минуту. Пока запрет принадлежит приложению, оно обновляет файл аренды; протухшая аренда означает, что приложение умерло, держа запрет, — агент его снимает. Аренды нет вовсе — значит запрет ставили не мы, например руками через терминал, и агент его не трогает.

Аренда пишется до того, как запрет взведён. Записанная после, она оставляла бы окно: падение между двумя вызовами дало бы запрет без аренды, а это агент читает как «ставили не мы» — единственное прочтение, которое не должно доставаться нашему же падению. Плата за такой порядок — аренда, которая недолго может существовать без запрета за ней; поэтому приложение убирает её сразу, как только выясняется, что запрета нет, а агент удаляет протухшую одинокую аренду. Именно протухшую: только что записанную и ещё не подкреплённую запретом из-под живого приложения никто не выдернет.

Работает от вашего имени, а не от root, и снимает запрет тем же узким правилом sudo, которым пользуется приложение, — новых прав себе не выдаёт. Без этого правила тихо снять запрет он тоже не сможет: ещё один довод в пользу того, что установщик ставит правило по умолчанию.

Предохранитель по батарее

Запрет сна делает лёгкой ровно одну ошибку: включить режим, закрыть крышку, убрать ноутбук в рюкзак — и он греется там, пока батарея не сядет в ноль. Поэтому режим сам выключается, когда заряд опускается до выбранного порога (по умолчанию 20%), а при желании — сразу при отключении от сети. Обе настройки лежат в меню, а уведомление сообщает, что именно сработало.

На Mac без встроенной батареи обе настройки просто не показываются — неработающий переключатель хуже его отсутствия. Проверка идёт по типу источника питания, а не по принципу «источник вообще есть»: подключённый к настольному Mac ИБП — тоже источник. А если уровень заряда почему-то не читается, не срабатывает ничего: выключить режим по причине, которую приложение не может назвать, хуже, чем не трогать его.

Обновления

Раз в сутки приложение спрашивает у GitHub, нет ли релиза новее. Если есть — приходит системное уведомление с кнопками «Скачать» и «Пропустить эту версию»: модальное окно поверх вашей работы не появляется. А проверку, запущенную вручную из меню, приложение отвечает окном — вы её ждёте. Проверку можно выключить в меню: тогда в сеть ничего не уходит, пока вы сами не попросите.

Себя приложение не подменяет. Оно скачивает .pkg из релиза и открывает его системным установщиком — обновление проходит ту же авторизацию, что и первая установка. Запросы разрешены только по https и только на хосты GitHub, включая промежуточные редиректы; всё остальное отклоняется.

Блокировка экрана

Сон и блокировка экрана — разные настройки macOS, SleepSwitch трогает только сон. Если при открытии крышки Mac просит пароль, это блокировка: macOS намеренно требует пароль учётной записи, чтобы её изменить, и ни одно приложение не может выключить её молча.

sysadminctl -screenLock off -password -        # выключить
sysadminctl -screenLock immediate -password -  # вернуть
sysadminctl -screenLock status                 # проверить

В меню есть пункт, открывающий нужную панель системных настроек.

Сборка из исходников

./make-installer.sh          # → dist/SleepSwitch-<версия>.pkg
./install.sh                 # или сразу в /Applications, без установщика
./Tools/run-tests.sh         # тесты; с --network ходит на настоящий GitHub

Note

После локальной сборки остаётся build/SleepSwitch.app, и Spotlight его индексирует — в поиске приложение начинает двоиться рядом с установленной копией. Лечится rm -rf build dist. Файл .metadata_never_index тут не помогает: для обычных подпапок Spotlight его игнорирует.

Нужен только Xcode или Command Line Tools. Иконка рисуется кодом Tools/make-icon.swift во время сборки — бинарных ассетов в репозитории нет. Забытый перевод ломает сборку: Tools/check-localization.sh сверяет ключи L("…") из исходников со всеми Localizable.strings.

Релиз выпускается тегом:

git tag v1.0.1 && git push origin v1.0.1

Требования

macOS 13 Ventura или новее. Apple Silicon и Intel. Интерфейс следует языку системы — русский или английский.

Обновление и удаление

Для обновления удалять ничего не нужно. Откройте новый .pkg — установщик сам погасит запущенную копию и заменит бандл. Настройки и правило sudo сохранятся.

Чтобы удалить совсем, выберите в меню «Удалить SleepSwitch…». Сначала снимется запрет сна — пока правило sudo ещё на месте и это можно сделать тихо, — затем удалятся приложение, правило, запись об установке и настройки, после чего программа закроется. Один пароль, одно подтверждение.

Из склонированного репозитория можно запустить uninstall.sh или сделать то же руками:

sudo pmset -a disablesleep 0          # сначала это — см. предупреждение ниже
sudo rm -rf /Applications/SleepSwitch.app
sudo rm -f /etc/sudoers.d/sleepswitch
sudo pkgutil --forget com.ganin.sleepswitch.app
defaults delete com.ganin.sleepswitch

Warning

Снимите запрет сна до удаления приложения. Это системная настройка, она переживает процесс: если перетащить приложение в корзину при включённом режиме, Mac перестанет засыпать, а выключателя уже не будет. Достаточно заранее выключить режим в меню.

Просто перетащить в корзину можно, только если режим выключен и вас не смущает, что правило sudo и запись об установке останутся в системе.

Лицензия

MIT