Как стать автором
Поиск
Написать публикацию
Обновить

История о том, как одна ошибка драйвера поменяла моё взаимодействие с системой (и не только)

Время на прочтение6 мин
Количество просмотров1.3K

Несколько лет назад я решил полностью обновить компьютер. Интереса ради (а ещё из-за урезанного бюджета) в качестве основы была выбрана пара зеонов на относительно неплохой материнке. Не буду расписывать все комплектующие, т.к. для истории они не так важны и, вероятно, для этого понадобится ещё одна статья.

После сборки на сие чудо было решено накатить 11 винду, т.к. было интересно потыкать, по мощности железа хватало за глаза, а ограничения обходились парой записей в реестре. После возни с настройками и выпиливания тогда ещё не очень большого количества мусора система пару лет стабильно работала: тянула базовые задачи, разработку на Python, Java и Wiring, игры, какое-то время была хостом для нескольких ботов, иногда обновлялась. Система была прекрасным инструментом и всё было хорошо... до поры.

Однажды во время разработки и тестирования мода для Minecraft, который я тогда писал, экран потух. Сначала подумал на видеокарту, но после небольшого расследования заметил, что в системных журналах уже несколько дней повторяется предупреждение об ошибках в работе драйвера PCI Express. Там же была серия ошибок, примерно после которой я и застал отвал графики. Ещё какое-то время я игнорировал ошибки и просто перезагружался, когда картинка исчезала. Но когда я словил отвал трижды за час, терпение лопнуло.

Примерно в то же время на GitHub'е я наткнулся на Hyprland, тайлинговый оконный менеджер для Linux. Мне понравилась идея тайлинга и биндов на все случаи жизни, т.к. на винде мне их постоянно не хватало (PowerToys чуть закрывал потребности, но не полностью). Но повода для перехода не было, т.к. текущая система меня вполне устраивала, а опыта взаимодействия с Linux у меня не было. Но после инцидента с 3 отвалами за час я всерьёз задумался о переходе.

Тогда (да и на момент написания статьи) Hyprland официально поддерживал только 2 дистрибутива: Arch и NixOS. Остальные вроде как поддерживались сообществом, но
рисковать тогда не очень хотелось. Мне было примерно всё равно, какой выбрать. О первом я был наслышан из-за множества различных шуток и не только, а про второй мне как-то рассказывал друг, который когда-то меня втянул в программирование. В итоге он всё-таки склонил меня на сторону NixOS.

Систему я решил поставить на довольно старый ноут (i3 2 поколения, 8 ГБ оперативной памяти, дискретная видеокарта от AMD), который был проапгрейжен до i7 того же поколения и 16 ГБ оперативки.

Т.к. опыта с NixOS было 0, а документации тогда было крайне мало (да и сейчас её не так много, но, как мне кажется, достаточно), моё путешествие в увлекательный мир Nix началось с изучения чужих конфигов на GitHub. Тогда Nix для меня был чем-то вроде JSON, но с приколами. Система была полигоном для экспериментов и изучения.


Примерно так я бы себе объяснял Nix в то время

# Есть комментарии

# Используем pkgs
{pkgs, ...}: {
    # "Ключи" можно писать без кавычек и вкладывать. Вместо двоеточия используется "="
    # Можно "добавить ключ" к каждому элементу 
    environmemt.systemPackages = with pkgs; [
        vscodium # Элементы списка разделяются пробелами, табуляцией или переносами строк
        kitty
    ]; # В конце пары "ключ-значение" не просто запятая, а точка с запятой

    programs.thunar.enable = true; # Если в https://search.nixos.org/options есть вариант для пакета, используем его
}

Первый конфиг, который я написал, был в двух файлах, которые были автоматически созданы при установке, но с Hyprland и некоторыми сервисами для него вместо GNOME. Потом он оброс теми же базовыми программами и инструментами для разработки, что и на винде (мне повезло, что все они поддерживали Linux и были в репозитории Nixpkgs). Всё либо через заимствование с чужих конфигов, либо через search.nixos.org. Система работала так, как я ожидал. Все конфиги обитали в ~/.config. Переключение между окнами и тайлинг были гораздо удобнее, чем вечный поиск на панели винды или Alt-Tab. А ещё получилось подтянуть уровень английского за счёт эффекта погружения, т.к. полезной информации на русском было примерно нисколько.

Вскоре файл разросся, а я захотел поставить систему на другой ноут. Тогда было принято решение о переходе на флейки, которые до этого видел много где, и началось дробление на файлы. В качестве референса был взят конфиг XNM1 примерно с коммита
03fdb7e1bf630e003df0b78cda48518a43458fbb. Результат - первая версия конфига, которую сейчас можно публично найти на моём аккаунте GitHub (сейчас она лежит в ветке
old-dotfiles). Наверное, где-то можно откопать и более старые версии, но в них не было ничего интересного. Система была холстом для творчества, за которым можно было работать.

С тем конфигом я жил примерно полгода, иногда меняя его под свои нужды. А потом я узнал о home-manager'е... И увидел конфиг fufexan, в котором он используется. После попыток его адаптировать получилась первая ревизия с более чёткой структурой и практически полной синхронизацией между хостами. В новом конфиге уже во всю используются функции и флейки, структура уже гораздо ближе к макаронам, но, на мой взгляд, ещё поддерживаема.

После рефактора я уже знал и использовал Nix не просто как JSON с комментариями и плюшками, а как функциональный DSL; смог познать прелести Neovim и написать конфиг для него на Nix; начал активно использовать direnv, nix run и nix shell, попутно заражая репозитории, с которыми работаю, файлами .envrc, flake.nix, flake.lock, shell.nix и default.nix; пощупал Go, Rust, Kotlin, Elixir, Haskell и не только... В общем, довольно много изучал.


А вот так бы я себе объяснил Nix сейчас

# Каждый файл - функция от одного аргумента
# Распаковываем сет аттрибутов, извлекая pkgs и игнорируя остальное
{pkgs, ...}: {
    # Сокращаем получение значений по ключам с общим началом с помощью with
    environmemt.systemPackages = with pkgs; [
        vscodium 
        kitty
    ]; 
    
    programs.thunar.enable = true; # Используем модуль для Thunar, объявленный в Nixpkgs
} # Т.к. возвращаем значение (в нашем случае сет аттрибутов), точка с запятой не нужна

Пример из файла с вынесенными функциями для конфига Niri

{
  lib,
  numbers,
  ...
}: let # Создаём "переменные" для переиспользования и не только
  # На самом деле это похоже на сет аттрибутов, так что тут синтаксис тот же

  # "Копируем" ключи с их значениями из lib и не только
  inherit (lib) nameValuePair pipe take;
  inherit (lib.lists) findFirstIndex;
  inherit (numbers) mod;

  # Мне не нравится, как делаются бинды в оригинальном модуле для Niri, так что почему бы не задать их так, как нравится?
  mkBinds = binds:
    # Пайпим наш список биндов по функциям (последовательно применяем их к аргументу внешней функции)
    # Относительно недавно появился отдельный оператор для этого, но я пока не рефакторил свой конфиг, чтобы его использовать
    pipe binds [
      # Применяем функцию к каждому элементу списка
      (map (
        {
          bind,
          desc,
          action,
          allow-when-locked ? false, # Не все ключи иногда надо задавать, поэтому пропишем для них дефолты, чтобы не писать явно каждый раз
          repeat ? false,
          cooldown-ms ? null,
        }:
          nameValuePair bind {
            hotkey-overlay.title = desc;
            inherit action allow-when-locked repeat cooldown-ms;
          }
      ))
      builtins.listToAttrs # Преобразуем список из сетов аттрибутов вида { name = "";, value = {}; } в один сет аттрибутов
    ];

  mkWorkspacesBinds = names: let
    workspaces = take 10 names;
  in
    pipe workspaces [
      (map (name: let
        nameIndex = findFirstIndex (w: w == name) null workspaces;
        wsIndex = mod (nameIndex + 1) 10;
        ws = toString wsIndex; # Некоторые функции из builtins доступны без добавления "builtins." в начало
      in [
        {
          bind = "Mod+${ws}"; # Автоматически в строку число не конвертируется, поэтому мы сделали это чуть выше
          desc = "Focus the `${name}` workspace";
          action.focus-workspace = name;
        }
        {
          bind = "Mod+Shift+${ws}";
          desc = "Move the focused window to the `${name}` workspace";
          action.move-window-to-workspace = name;
        }
        {
          bind = "Mod+Shift+Ctrl+Alt+${ws}";
          desc = "Move the focused column to the `${name}` workspace";
          action.move-column-to-workspace = name;
        }
      ]))
      builtins.concatLists # Объединяем вложенные списки в один большой
    ];

  mkColumnsBinds = n:
    pipe n [
      (builtins.genList (
        x: let
          ws = toString (mod (x + 1) 10);
          m = x + 1;
          mStr = toString m;
        in [
          {
            bind = "Mod+Alt+${ws}";
            desc = "Focus a column at index ${mStr}";
            action.focus-column = m;
          }
          {
            bind = "Mod+Alt+Shift+${ws}";
            desc = "Move column to the index ${mStr}";
            action.move-column-to-index = m;
          }
        ]
      ))
      builtins.concatLists
    ];
in {
  inherit mkBinds mkWorkspacesBinds mkColumnsBinds; # Можно было и без let-in отдать функции, но иногда надо "экспортировать" не все или переиспользовать одни в других (это можно сделать через rec сеты аттрибутов, но мне больше нравится let-in)
}

Спустя какое-то время пришло осознание, что использовать по одному рабочему пространству под каждое окно - так себе затея. Группы вкладок убивали простоту навигации между окнами, а места на дисплее ноутбука для более 2 окон не так много. Так путешествие довело меня до "скроллинга": окна не делят место на экране, а открываются в колонках, которые можно "прокручивать". При этом в каждой колонке можно держать несколько окон, и они уже делят место так же, как и при уже привычном тайлинге. Для этого есть плагин на Hyprland, hyprscrolling, но я решил вместо этого перейти на Niri.

После небольшого перелопачивания конфига получилось снизить максимально возможное количество рабочих пространств с 22 до 6, заменить несколько плагинов и сторонних программ. Система снова стала прекрасным инструментом, которым приятно
пользоваться, но теперь уже гораздо более стабильным и, как мне кажется, почти
незаменимым.

Смотря на себя в начале пути, удивляюсь, как много изменилось за эти 1.5 года. Даже не верится, что отправной точкой этого путешествия стала та ошибка драйвера... Думаю, её до сих пор не пофиксили, т.к. моё железо изначально не было совместимо с 11 виндой. На деле же я уже вряд ли узнаю, т.к. возвращаться обратно уже не хочется: текущая система работает в разы лучше и стабильнее, а ещё полностью подконтрольна мне и не обновляется, когда не просят)

Конфиг на данный момент выглядит вот так
Конфиг на данный момент выглядит вот так

Вот так благодаря одной ошибке драйвера получилось пройти путь от обычного пользователя винды, который даже терминалом не пользовался ни разу, до счастливого пользователя NixOS, в основном сидящего в терминале и почти не использующего мышку. А параллельно с этим улучшить навыки разработчика, поднять уровень английского и изучить много нового.


Репозиторий с конфигурацией можно найти на GitHub.

Буду рад вашим комментариям!

Теги:
Хабы:
+4
Комментарии3

Публикации

Ближайшие события