О desktop

2018-10-27

Помните ярлыки в Windows. Файлики с расширением .lnk. Классическим фейлом было скопировать на дискетку ярлык вместо самого вордовского файла, а потом удивляться, почему это оно не открывается на другом компьютере.

В наших юниксах, оказывается, тоже есть ярлыки. Freedesktop.org определяет формат файла .desktop, который определяет то, что называется Desktop Entry. Это и ярлыки на рабочем столе, и пункты меню. И это работает и в GNOME, и в KDE, и во всех прочих юниксовых DE. Очень удобно.

Зачем мне ярлыки понадобились? Потому что, как оказалось, в KDE нельзя просто так запустить Java приложение, каким-нибудь java -jar app.jar, а потом закрепить (pin) его в каком-нибудь доке, чтобы потом быстро запускать. Иконка-то появляется, но запускает она просто /usr/bin/java, без параметров. И pin, в результате, не работает.

Чтобы pin заработал, нужно запускать правильный .desktop файл. Он может называться java-RedmineTimeTracker.RedmineTimeTracker.desktop и выглядеть как-то так:

[Desktop Entry]
Type=Application
Name=Redmine Time Tracker
Icon=java-RedmineTimeTracker.RedmineTimeTracker
Exec=java -XX:MaxRAM=256m -jar "/home/gelin/opt/RedmineTimeTracker/Redmine Time Tracker - 0.3.6.jar"
StartupWMClass=RedmineTimeTracker.RedmineTimeTracker
Terminal=false
Categories=Development

Обычный текстовый ini-подобный файл.

Type — это тип. "Application" — это значит, что это "ярлык" для приложения. Ещё есть "Link" и "Directory".

Name — это имя. Как оно будет называться в меню или искаться в dashboard.

Icon — это путь до файла с иконкой. В формате PNG или SVG. Или же имя зарегистрированной иконки. Об этом чуть позже.

Exec — это команда, которая запускает приложение. В данном случае — обычный java -jar, но с ограничением по памяти.

StartupWMClass — это то, ради чего мы вообще этот .desktop файл создавали. В X Window, также как и в Windows, у окна есть класс. Строка. И всякие доки определяют уникальную кнопочку для приложения как раз по этому классу. Из-за отсутствия класса и возникает проблема с Java приложениями. Поэтому мы здесь имя класса и указываем явно.

Есть небольшая проблемка, как определить класс окна для Java приложения. В этом нам поможет программка xprop. Запускаем нужное нам приложение. Запускаем xprop в консоли. Тыкаем по интересующему нас окну. Видим в консоли что-то вроде:

$ xprop | grep WM_CLASS
WM_CLASS(STRING) = "RedmineTimeTracker.RedmineTimeTracker", "RedmineTimeTracker.RedmineTimeTracker"

В данном случае класс совпадает с именем главного класса Java приложения. Я не нашёл, является ли это нормой для десктопных Java приложений. В любом случае, просто прописываем, что обнаружили, в StartupWMClass.

Terminal — флаг, нужно ли запускать приложение в терминале. В данном случае — нет.

Categories — список категорий, через точку с запятой. Это — стандартные категории, на которые делится главное меню. В данном случае это приложение связано с разработкой.

Иконка. Иконка — это ресурс, который можно зарегистрировать в системе, назначить ему имя, и использовать это имя в .desktop файле.

Регистрация производится с помощью программки xdg-icon-resource. XDG — это "X Desktop Group", так раньше назывался freedesktop.org. Сама эта программка — даже не программка, а здоровенный шелловый скрипт, внутри которого есть функции с интересными названиями вроде detectDE(), find_gtk_update_icon_cache() или need_kde_icon_path(). Этот скриптик очень много знает про всякие разные desktop environment.

$ xdg-icon-resource install \
--size 128 \
redmine_logo_green.png \
"java-RedmineTimeTracker.RedmineTimeTracker"

Обязательно нужно указать размер иконки. Больше — лучше. Но не все размеры ваша DE сможет съесть. Хорошо работает 128 и 256 пикселей (128x128 и 256x256).

Последний параметр — имя иконки. Оно обязательно должно состоять из двух слов. Первое, до минуса — это вендор. В данном случае это Java приложение, так что пусть будет "java". Второе, после минуса — собственно идентификатор иконки, для данного вендора. Так как нужен уникальный идентификатор, удобно использовать тут имя класса окна.

На самом деле xdg-icon-resource копирует иконку в каталог вроде ~/.local/share/hicolor/128x128/apps/, или другой, специфичный для вашей DE, и делает магию по обновлению кэша иконок DE.

Сам .desktop файл "устанавливается" тоже своей командой xdg-desktop-menu. Это тоже здоровенный шелловый скрипт.

$ xdg-desktop-menu install \
"java-RedmineTimeTracker.RedmineTimeTracker.desktop"

Имя файла тоже должно содержать префикс вендора, то есть "java-" в данном случае. Скрипт копирует ваш .desktop файл куда-нибудь в ~/.local/share/applications/ и магическим образом обновляет список приложений в DE.

Берём xprop, определяем класс окна. Берём иконку и засовываем её как системный ресурс с помощью xdg-icon-resource. Пишем .desktop файл и добавляем его в систему с помощью xdg-desktop-menu. Всё. Теперь мы умеем создавать "ярлыки" для любых приложений и засовать их в доки и меню.

freedesktop.org logo

И тут выходит Chrome 70. Где отломали одну очень полезную фичу, которой я интенсивно пользовался.

Вы знали, что в Хроме можно было сделать: "Open Main menu -> More Tools -> Add to Desktop"? В результате создавался тот самый .desktop файл, который можно закрепить в доке, и который запускал тот самый сайт, который вы таким образом добавили на рабочий стол, в отдельном окне, без табов и адресной строки.

То есть любой сайт можно "запускать" как обычное приложение. С отдельной кнопкой на панели задач. С нормальным переключением между окнами по Alt+Tab. И, так как технически это просто вкладка того же Хрома, только запущенная в отдельном окне, это жрёт заметно меньше памяти, чем Electron приложение того же Slack (потому что в каждом Электроне — своя копия движка браузера). У меня так запущены кучка Слаков, Gmail, DevDocs и прочие. Мне — очень удобно.

Так вот, в Chrome 70 этот пункт меню убрали. А у сохранившихся ярлыков какого-то чёрта поломалось оформление окон. Теперь они отображаются с хромовыми заголовками окон, что категорически не совпадает по стилю и расположению кнопок с окнами моего KDE.

Но мы же теперь умеем делать .desktop файлы. А у Chrome всё еще остался ключик командной строки --app, который делает то, что нам нужно. То есть запускает указанный url в отдельном окне без табов и адресной строки, и с правильным оформлением самого окна.

Так что берём своё счастье в свои руки. Качаем иконку сайта и засовываем её в систему с помощью xdg-icon-resource. Берите только иконку 256x256, а не 192x192, не все размеры DE съест.

Пишем .desktop файл. Например, chrome-devdocs.io.desktop:

[Desktop Entry]
Type=Application
Name=DevDocs API Documentation
Icon=chrome-devdocs.io
Exec=/opt/google/chrome/google-chrome --profile-directory=Default --app=https://devdocs.io
StartupWMClass=devdocs.io
Terminal=false
Categories=Network

В качестве вендора я тут взял "chrome-", по аналогии со старыми ярлыками. В качестве класса окна Хром выставляет доменное имя из указанного url. Остальное, думаю, понятно.

Устанавливаем файл с помощью xdg-desktop-menu. И наслаждаемся новым удобным приложением, которое на самом деле сайт.

P.S. Я это дело даже немного автоматизировал.