Параметры настройки менеджера окон fly-wm#

Важно

Документация дорабатывается по мере развития продуктов Группы Астра и по пожеланиям пользователей.

Ваши пожелания и замечания направляйте на почту docs@astralinux.ru

Общие сведения#

Процесс настройки взаимодействия приложения с пользовательской средой Fly (менеджер окон fly-wm — менеджер окон, рабочий стол, классическое меню «Пуск», панель инструментов, переключатель рабочих столов, блокировщик экрана), включает:

  • создание и регистрацию *.desktop-файла (ярлыка приложения),

  • создание *.directory-файла с собственной категорией (для кастомизации ярлыков в меню приложений),

  • настройку ассоциаций MIME-типов (открытие файлов нужным приложением).

Описание функций fly-wmfunc — инструмента редактирования оконного менеджера fly-wm — представлено в статье.

Рабочие каталоги менеджера окон fly-wm (для настройки приложения)#

Системные конфигурационные файлы:

  • /usr/share/applications/ — каталог объектов меню Пуск;

  • /usr/share/fly-wm/shared/flydesktop — размещение в этом каталоге ярлыков (файлов *.desktop), файлов и каталогов приводит к их немедленному появлению на первых рабочих столах всех пользователей (и наоборот, удаление объекта из каталога ведет к удалению объекта с рабочих столов);

  • /usr/share/fly-wm/startmenu — каталог для объектов меню «Пуск» (файлы *.directory — описатель категории меню);

  • /usr/share/fly-wm/shared/flystartmenu — администраторская настройка объектов меню «Пуск» (не рекомендуется использовать);

  • /usr/share/fly-wm/shared/flytoolbar — каталог для объектов панели инструментов (файлы *.desktop);

  • /usr/share/mime/packages/ — каталог для XML-файлов регистрации MIME-типов;

  • /usr/share/icons/hicolor — каталог иконок приложения.

Системные файлы применяются ко всем пользователям. Пользовательские настройки, заданные в ~/.fly, имеют приоритет и переопределяют системные.

Примечание

Описанные выше каталоги flydesktop, flystartmenu и flytoolbar задаются переменными окружения FLY_SHARED_DESKTOP_DIR, FLY_SHARED_STARTMENU_DIR, FLY_SHARED_TOOLBAR_DIR. Директории должны быть созданы заранее (если переменная пустая, то используется каталог по умолчанию из /usr/share/fly-wm/shared/). Задание значений переменных определяется до запуска службы fly-wm. Это можно сделать, например, при аутентификации пользователя в правилах PAM или создав сценарий в каталоге /etc/X11/Xsession.d/. После обновления значения переменной необходимо перезапустить сессию. Нежелательно использовать стандартные пути для нестандартных целей.

Предупреждение

В более ранних обновлениях:

  • /usr/share/applications/flydesktop — каталог для объектов рабочего стола;

  • /usr/share/applications/flystartmenu — каталог для объектов меню «Пуск»;

  • каталог для объектов панели инструментов отсутствует.

Использование данных каталогов для настройки собственного ПО в последних обновлениях ALSE не рекомендуется.

Конфигурационные файлы#

.desktop-файл — текстовый файл с расширением .desktop, соответствующий стандарту Freedesktop.org. Он содержит метаданные о приложении и используется для интеграции программы в графические среды рабочего стола Linux. Основные поля .desktop-файла включают:

  • название приложения (Name, поддерживает локализацию через Name[ru]) — обязательное поле,

  • тип объекта (Type=Application или Type=Links — для ссылок на сайты/документацию, открываются в браузере) — обязательное поле,

  • команда, запускающая приложение (Exec) — обязательное поле для Type=Application,

  • ссылка (URL) — обязательное поле для Type=Links,

  • описание (Comment),

  • рабочая директория, из которой следует запускать команду (Path),

  • иконка (Icon),

  • версия (Version),

  • ключевые слова для поиска из меню (Keywords),

  • запуск команды из терминала (Terminal=true или Terminal=false),

  • категория (Categories, например, «Разработка», «Интернет»),

  • список MIME-типов, с которыми может работать приложение (MimeType).

.directory-файл — это специальный файл, описывающий категорию (или подменю) в меню приложений «Пуск». Он используется для группировки .desktop-файлов по логическим разделам. Содержит следующие ключевые поля:

  • Name (отображаемое имя категории, поддерживает локализацию через Name[ru]) — обязательное поле,

  • Categories (имя, по которому *.desktop-файлы ассоциируются с этой категорией),

  • Comment (краткое описание категории, появляется при наведении),

  • Icon (иконка категории),

  • Type=Directory (обязательное поле, указывающее, что это файл категории).

Категория ярлыка — логическая группа, к которой относится программа, связанная с этим ярлыком. Может использоваться для создания кастомизированной группы в меню Пуск, например, категория «ППО» и входящие в ее состав «SDK ППО», «Информация о версии ППО», «Лицензия ППО», «Справочник ППО» и др.

Совет

Стандартные категории — Инструменты («Utility»), Научные («Education»), Разработка («Development»), Игры («Game»), Графика («Graphics»), Мобильные («Mobile»), Мультимедиа («Multimedia»), Интернет («Network»), Офис («Office»), Параметры («Settings»), Системные («System»).

MIME (Multipurpose Internet Mail Extensions) — стандарт, описывающий типы файлов (обычно по расширению, определяется glob-паттерном) и способы их обработки. В контексте Linux-десктопов MIME-типы используются для определения, какое приложение должно открывать файл определённого типа (в .desktop файле в поле MimeType указываются MIME-типы, с которыми приложение умеет работать).

MIME-файл содержит описание новых или пользовательских MIME-типов, включая:

  • <mime-type type="application/x-myformat"> — объявление типа,

  • <glob pattern="*.myext"/> — сопоставление файла с MIME-типом (задается glob-паттерном, обычно указывается расширение файла),

  • <comment> — описание типа.

Примечание

Редактор меню Пуск и панели быстрого запуска (fly-menuedit) позволяет графически редактировать ярлыки и категории программ.

Интеграция ППО в ALSE#

Описанные ниже способы настройки рекомендуется использовать в постинсталяционном скрипте.

Предупреждение

Данные настройки актуальны для обновления ALSE 1.8.5.

Предупреждение

Пользовательская настройка имеет приоритет над системной. При возникновении ошибок с системной настройкой рекомендуется проверить нет ли конфликта с пользовательскими настройками.

Добавление приложения в меню Пуск#

  1. Создать myapp.desktop файл для приложения myapp:

[Desktop Entry]
Name=My App
Name[ru]=Мое приложение
Exec=/opt/myapp
Icon=myappicon
Type=Application
Categories=Utility;
Terminal=false

Совет

Иконка задается png или svg файлом либо по указанному пути (например, /home/user/myicon.svg), либо ищется сопоставление по имени в каталоге /usr/share/icons/hicolor (например, если задана ассоциация myappicon с файлом /usr/share/icons/hicolor/scalable/apps/myappicon.svg).

Примечание

Согласно спецификации Freedesktop.org, если в *.desktop указано несколько категорий (например, «Categories=Office;Utility;»), ярлык добавится только в первую соответствующую категорию (т.е. «Офис»).

  1. Проверить валидность файла:

desktop-file-validate myapp.desktop

Если есть ошибки — будут показаны, если всё хорошо — вывода не будет.

  1. Добавить ярлык в меню (для всех пользователей, требуются права администратора):

sudo mv myapp.desktop /usr/share/applications

Результат — после добавления файла myapp.desktop в директорию /usr/share/applications иконка приложения автоматически появится у всех пользователей в меню Пуск (появится в пользовательских каталогах ~/.fly/startmenu2 и ~/.fly/startmenu).

Примечание

Удаление файла myapp.desktop из директории /usr/share/fly-wm/startmenu приведет к автоматическому удалению ярлыка приложения из меню Пуск у всех пользователей.

Совет

Для добавления приложения в меню Пуск от пользователя рекомендуется выполнить графическую настройку из меню Пуск ‣ Настройки меню Пуск ‣ Редактировать меню Пуск

Примечание

При появлении ярлыка приложения в меню Пуск пользователю будет удобно самостоятельно добавить ярлык на рабочий стол, панель инструментов, избранное или автозапуск. Для этого необходимо нажать правой кнопкой мыши на ярлык в меню и нажать кнопку «Добавить».

Создание ярлыка на рабочем столе#

Создать валидный файл myapp.desktop из предыдущего примера в директории /usr/share/fly-wm/shared/flydesktop (для всех пользователей, требуются права администратора).

Результат — после создания файла в директории ярлык автоматически появится на первых рабочих столах всех пользователей (появится в пользовательском каталоге ~/Desktops/Desktop1).

Примечание

Удаление файла myapp.desktop из директории /usr/share/fly-wm/shared/flydesktop приведет к автоматическому удалению ярлыка приложения со всех рабочих столов у всех пользователей.

Совет

Для добавления приложения на рабочий стол от пользователя рекомендуется выполнить графическую настройку с рабочего стола: правая кнопка мыши ‣ Создать ‣ Ярлык и заполнить обязательные поля «Имя» и «Команда».

Добавление ярлыка на панель инструментов#

  1. Создать myapp.desktop как в примере выше.

  2. Добавить ярлык на панели инструментов (для всех пользователей, требуются права администратора):

sudo mv myapp.desktop /usr/share/fly-wm/shared/flytoolbar

Результат — файл myapp.desktop появится в каталоге /usr/share/fly-wm/shared/flytoolbar и автоматически появится на панели инструментов всех пользователей (появится в пользовательском каталоге ~/.fly/toolbar/).

Примечание

Удаление файла myapp.desktop из директории /usr/share/fly-wm/shared/flytoolbar приведет к автоматическому удалению ярлыка приложения с панели инструментов у всех пользователей.

Совет

Для добавления приложения на панель управления пользователю необходимо выполнить следующие действия: правая кнопка мыши по значку меню Пуск ‣ Настроить панель быстрого запуска ‣ Новый ярлык.

Задание иконки#

Иконка задается файлами png или svg, который необходимо поместить в соответствующую директорию (для png файла — /usr/share/icons/hicolor/<размер>/apps/имя_иконки.png, для svg — /usr/share/icons/hicolor/scalable/apps/имя_иконки.svg):

sudo mv myicon.png /usr/share/icons/hicolor/48x48/apps/myicon.png

Совет

Рекомендуется использовать несколько размеров (16x16, 32x32, 48x48, 64x64, 128x128), но достаточно хотя бы одного — система подберёт ближайший.

Результат — иконка появится в реестре иконок и может быть использована по имени как значок ярлыка.

Настройка ассоциации MIME-типа#

До настройки ассоциации (связи *.xml с *.desktop) необходимо убедиться, что *.desktop имеет поле MimeType и находится в ~/.fly/startmenu или ~/.fly/startmenu2 (пользовательские приложения) или /usr/share/applications/ (стандартные приложения Astra Linux Special Edition).

Пусть задан файл myapp.desktop:

[Desktop Entry]
Name=My app
Name[ru]=Мое приложение
Exec=/usr/bin/myapp
Icon=myapplication
Type=Application
Categories=Utility;
Terminal=false
MimeType=application/x-myapp;

Предупреждение

Согласно Freedesktop.org Desktop Entry Specification, поле MimeType должно содержать полные имена MIME-типов в формате type/subtype, где type может быть application, text, video или image, а subtypex-myapp, pdf, png или др. Использование неправильных типов может приводить к ошибкам связывания.

1. Для работы приложения с определёнными файлами (например, *.myapp) необходимо зарегистрировать MIME-тип в системе, создав файл application-x-myapp.xml:

<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
  <mime-type type="application/x-myapp">
    <comment>MyApp Data File</comment>
    <glob pattern="*.myapp"/> <!-- glob-паттерн для файлов, например, config_*.cfg -->
  </mime-type>
</mime-info>
  1. Установить новый MIME-тип в системе (требуются права администратора):

sudo mv application-x-myapp.xml /usr/share/mime/packages/
sudo update-mime-database /usr/share/mime
  1. Связать его с приложением:

# Задание однозначного выбора для открытия файлов приложением (если не задано, будет предлагаться список подходящих приложений, работающих с данным mime-типом)
xdg-mime default myapp.desktop application/x-myapp

Результат — описанные в mime файлы (заданные glob-паттерном, например, .myapp) открываются системой в заданном приложении. Проверить работоспособность можно, открыв файл new.myapp командой xdg-open new.myapp.

Создание новой категории#

1. Создать папку MyApp с описателем категории .directory (обязательные поля - Type=Directory, Name, Categories) следующего содержания:

Совет

Иконка (icon) должна быть задана предварительно, если используется нестандартная. В примере folder - стандартная иконка каталогов.

[Desktop Entry]
Name=MyApp Tools
Name[ru]=Средства MyApp
Icon=folder
Type=Directory
Categories=X-MyCategoryName
  1. Скопировать каталог MyApp, содержащий описатель .directory, в /usr/share/fly-wm/startmenu (содержат описание нестандартных категорий меню):

sudo mv MyApp /usr/share/fly-wm/startmenu

Примечание

Новая категория появится в меню Пуск у всех пользователей.

3. Создать файл MyApp.desktop с новой категорией и добавить его в меню Пуск (если *.desktop с новой категорией был создан до выполнения шагов 1-2, он находился в категории меню «Прочее», а после создания новой категории меню переместится в нее).

[Desktop Entry]
Name=MyApp
Name[ru]=MyApp
Exec=/usr/bin/myapp
Icon=myapp
Terminal=false
Type=Application
Categories=X-MyCategoryName;

Совет

Согласно спецификации Freedesktop.org, собственная категория должна начинаться с префикса «X-«.

Примечание

В новой категории меню Пуск у всех пользователей появится ярлык приложения.

Результат — ярлыки приложений (*.desktop) с заданной категорией X-MyCategoryName отобразятся в меню «Пуск» в новой категории меню.

Предупреждение

Изменение категории в файле *.desktop, уже находящегося в каталоге /usr/share/fly-wm/startmenu/, не приведет к перемещению ярлыка приложения в новую категорию меню (даже после ручного обновления оконного менеджера с помощью fly-wmfunc).

Обновление оконного менеджера#

При изменении файлов в /usr/share/ системным администратором обновление происходит автоматически. Ниже приведены команды fly-wmfunc, с помощью которых можно применить изменения вручную.

  1. Принудительное обновление стартового меню (применение - после добавления новой категории, добавление MimeType):

fly-wmfunc FLYWM_CREATE_STARTMENU

Предупреждение

Изменение полей Name, Exec, Icon, Comment, MimeType (требуется также перезадание с помощью xdg-mime), Path, Terminal, Version, Keywords в файле *.desktop приведёт к автоматическому изменению ярлыка. Изменение поля Type может сломать ярлык. Изменение поля Categories в уже установленном файле не перемещает ярлык в меню. Переименование файла *.desktop может сломать системные ассоциации.

  1. Обновление значков на рабочем столе:

fly-wmfunc FLYWM_UPDATE_SHORTCUT

Совет

Принудительное обновление FLYWM_FORCE_UPDATE_SHORTCUT.

  1. Обновление панели быстрого запуска:

fly-wmfunc FLYWM_UPDATE_TOOLBAR
  1. Перезагрузка графической сессии пользователя без перезагрузки сессии:

fly-wmfunc FLYWM_RESTART

Предупреждение

Не выполнять в скриптах deb пакета.

  1. Перезапуск меню у всех пользователей:

sudo killall fly-start-menu

Предупреждение

Изменение в файлах каталога /usr/share/fly-wm может не привести к изменению даже после выполнения команд выше. Рабочим вариантом является удаление изменяемого файла и последующее его создание с изменениями.

Настройка простого Python-приложения#

Рекомендуемая структура deb-пакета проприетарного ПО:

app-package/
├── debian/                    # Метаданные пакета (Содержимое представлено в тексте ниже)
├── opt/
│   └── app/                   # Все файлы приложения (распаковывается в /opt/app/)
│       ├── script             # Файл приложения (исходники/код)
│       └── lib/               # Библиотеки
├── integration/                 # Интеграционные файлы
│   ├── application-x-app.xml    # Задание MIME-тип (распаковывается в /usr/share/mime/packages/)
│   ├── app -> /opt/app/script   # Симлинк/скрипт-обертка для запуска из терминала (распаковывается в /usr/bin/)
│   ├── app.desktop              # Ярлык для меню/рабочего стола/панели инструментов (распаковывается в /usr/share/applications/ и папки flydesktop, flytoolbar каталога в /usr/share/fly-wm/shared/)
│   ├── app.svg или app.png      # Иконка для ярлыка/категории (распаковываются в /usr/share/icons/hicolor/)
│   └── AppCategory/
│       └── .directory           # Новая категория меню  (распаковывается в /usr/share/fly-wm/startmenu)
└── config.conf                # Конфигурационный файл приложения (распаковывается в /etc/app/, помечается как conffile при remove, удаляются при purge)

Ниже представлены шаги создания deb-пакета приложения с интеграцией в Astra Linux Special Edition.

Создание приложения#

Приложение myapp будет открывать файлы *.myapp и показывать их Содержимое.

  1. Установить python3 и pyqt5:

sudo apt install python3 python3-pyqt5
  1. Создать рабочую директорию myapp-1.0 и дерево папок с файлами (Содержимое файлов будет представлено ниже):

myapp-1.0/
 ├── opt/                     # Каталог пользовательских приложений
    └── myapp/               # Каталог приложения
        ├── myapp.py         # Скрипт приложения
        └── file.myapp       # Файл открываемый приложением
 └── integration/                   # Интеграционные файлы
     ├── application-x-myapp.xml    # Задание MIME-тип
     ├── cat_myapp.svg              # Иконка категории меню (опционально)
     ├── myapp                      # Скрипт-обертка для запуска из терминала
     ├── myapp.png                  # Иконка ярлыка приложения (опционально)
     ├── myapp.desktop              # Ярлык приложения для меню
     ├── myapp-desktop.desktop      # Ярлык приложения для рабочего стола (копия)
     ├── myapp-toolbar.desktop      # Ярлык приложения для панели инструментов (копия)
     └── MyAppCategory/.directory   # Категория меню
  1. Содержимое файла myapp-1.0/opt/myapp/myapp.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MyApp - PyQt5 приложение для демонстрации интеграции в ALSE/Fly-WM
"""

import sys
import os
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QWidget,
    QVBoxLayout,
    QLabel,
    QTextEdit,
    QMessageBox
)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont


class MyAppWindow(QMainWindow):
    def __init__(self, filepath=None):
        super().__init__()
        self.filepath = filepath
        self.init_ui()

        # Если файл передан при запуске - открываем его
        if filepath:
            self.open_file(filepath)

    def init_ui(self):
        """Инициализация интерфейса"""
        self.setWindowTitle("MyApp")
        self.setGeometry(100, 100, 600, 400)

        # Центральный виджет
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        # Основной layout
        layout = QVBoxLayout()
        central_widget.setLayout(layout)

        # Заголовок
        self.title_label = QLabel("Добро пожаловать в MyApp!")
        self.title_label.setFont(QFont("Arial", 14, QFont.Bold))
        self.title_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.title_label)

        # Текстовая область
        self.text_area = QTextEdit()
        self.text_area.setFont(QFont("Arial", 10))
        self.text_area.setReadOnly(True)  # Только для чтения
        self.text_area.setLineWrapMode(QTextEdit.WidgetWidth)
        layout.addWidget(self.text_area)

        # Приветственный текст
        welcome_text = (
            "Это приложение запущено из меню.\n\n"
            "Оно работает с файлами *.myapp и служит шаблоном для интеграции в ALSE.\n"
            "Для тестирования откройте файл file.myapp из меню двойным кликом."
        )
        self.text_area.setPlainText(welcome_text)

    def open_file(self, filepath):
        """Открытие файла"""
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                content = f.read()

            self.text_area.setPlainText(content)
            self.setWindowTitle(f"MyApp - {os.path.basename(filepath)}")
            self.title_label.setText(f"Открыт файл: {filepath}")

        except Exception as e:
            self.text_area.setPlainText(f"Ошибка при открытии файла:\n{str(e)}")
            self.title_label.setText("Ошибка открытия файла")
            QMessageBox.critical(self, "Ошибка", f"Не удалось открыть файл:\n{str(e)}")


def main():
    # Создание приложения
    app = QApplication(sys.argv)

    # Получение пути к файлу из аргументов командной строки
    filepath = sys.argv[1] if len(sys.argv) > 1 else None

    # Создание и показ окна
    window = MyAppWindow(filepath)
    window.show()

    # Запуск цикла событий
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
  1. Сделать приложение исполняемым:

sudo chmod +x myapp-1.0/opt/myapp/myapp.py
  1. Файл для тестирования myapp-1.0/opt/myapp/file.myapp:

Test string in file.

6. Сделать скрипт-обертку myapp-1.0/integration/myapp для запуска приложения из терминала (предпочтительнее симлинка, т.к. позволяет менять версию Python или добавлять переменные окружения без изменения основного скрипта приложения):

#!/bin/sh
exec /opt/my-company/myapp/myapp.py "$@"
  1. Содержимое файлов myapp-1.0/integration/myapp.desktop, myapp-1.0/integration/myapp-desktop.desktop, myapp-1.0/integration/myapp-toolbar.desktop:

[Desktop Entry]
Version=1.0
Type=Application
Name=MyApp
Name[ru]=Моё приложение
Comment=Просмотр .myapp файлов
Exec=/opt/my-company/myapp/myapp.py %f
# Альтернатива: Exec=/usr/bin/myapp %f
Icon=myapp
Terminal=false
Categories=X-MyAppCategory;
MimeType=application/x-myapp;
  1. Содержимое файла myapp-1.0/integration/MyAppCategory/.directory:

[Desktop Entry]
Name=MyApp Tools
Name[ru]=Инструменты MyApp
Comment=Приложения MyApp
Icon=cat_myapp
Type=Directory
Categories=X-MyAppCategory;

Совет

Для корректной отработки проверки *.desktop файла категория должна начинаться с префикса X-.

  1. Регистрация MIME-типа в файле myapp-1.0/integration/application-x-myapp.xml:

<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
  <mime-type type="application/x-myapp">
    <comment>MyApp Data File</comment>
    <glob pattern="*.myapp"/>
    <icon name="myapp"/>
  </mime-type>
</mime-info>
  1. Добавить файлы иконок по путям myapp-1.0/integration/myapp.svg и myapp-1.0/integration/myapp.png.

  2. Перейти в каталог myapp-1.0 и инициализировать файлы упаковки (в папке deb):

cd myapp-1.0
dh_make -n --createorig -p myapp -e test@email.com
  1. Редактировать Содержимое папки debian, оставив следующую структуру:

debian/              # Файлы упаковки пакета
 ├── source/
 │   └── format      # Формат исходного пакета (3.0 native или 3.0 quilt)
 ├── changelog       # История версий, версия пакета, контакты сопровождающего
 ├── control         # Метаданные пакета: зависимости, описание, секция
 ├── copyright       # Лицензионная информация и авторские права (текстовый файл)
 ├── myapp.install   # Список файлов для установки
 ├── postinst        # Скрипт, выполняемый после установки
 ├── postrm          # Скрипт, выполняемый после удаления
 ├── prerm           # Скрипт, выполняемый до удаления
 └── rules           # Скрипт сборки (Makefile), управляющий процессом компиляции и упаковки

Примечание

Шаблонные пользовательские конфигурационные файлы могут задаваться с помощью debian/conffiles, в котором записываются абсолютные пути пользовательских конфигурационных файлов (для БД, приложения и т.д.). Важно, deb-пакет должен содержать перечисленные файлы, правила распаковки которых должны быть заданы в „.install“. Пример содержания debian/conffiles:

/opt/my-company/myapp/config/app.conf
/opt/my-company/myapp/config/database.ini
/etc/my-company/myapp/myapp.conf
  1. Содержимое файла source/format:

3.0 (native)
  1. Содержимое файла changelog:

myapp (1.0-1) unstable; urgency=medium

  * Initial release
  * Integration with ALSE/Fly-WM
  * Support for .myapp file format

 -- Test Name <test@mail.ru>  Thu, 24 Mar 2026 15:00:00 +0300
  1. Содержимое файла control:

Source: myapp
Section: utils
Priority: optional
Maintainer: Test <test@email.com>
Build-Depends: debhelper-compat (= 13), desktop-file-utils, python3, python3-pyqt5, xdg-utils
Standards-Version: 4.5.0
Rules-Requires-Root: no

Package: myapp
Architecture: all
Depends: python3, python3-pyqt5, xdg-utils, ${misc:Depends}
Recommends: x11-utils, dbus-x11, fly-wm
Suggests: desktop-file-utils
Breaks: myapp-old (<< 1.0)
Replaces: myapp-old (<< 1.0)
Section: utils
Priority: optional
Description: Демо-приложение для интеграции в ALSE
 Это приложение служит шаблоном для создания .deb-пакетов для ALSE 1.8 (на базе Debian 12).
 Особенности:
  - Установка в /opt/my-company/myapp
  - Ярлык в меню Пуск
  - Поддержка открытия файлов *.myapp
  - Интеграция с Fly-WM (меню, рабочий стол, панель инструментов)
  1. Содержимое файла copyright:

Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: myapp
Upstream-Contact: Test Name <test@email.com>
Source: https://your-repo.com/myapp

Files: *
Copyright: 2025 Test Name
License: MIT
  1. Содержимое файла myapp.install:

# Приложение (копируется вся папка) - рекомендуется структура /opt/<vendor>/<package>/ для мультипакетных вендоров
opt/myapp /opt/my-company/

# Скрипт-обертка
integration/myapp /usr/bin/my-company/

# Интеграция Fly-WM (меню Пуск, рабочий стол, панель инструментов)
integration/myapp.desktop /usr/share/applications/
integration/MyAppCategory /usr/share/fly-wm/startmenu/
integration/myapp-desktop.desktop /usr/share/fly-wm/shared/flydesktop/
integration/myapp-toolbar.desktop /usr/share/fly-wm/shared/flytoolbar/

# MIME-типы
integration/application-x-myapp.xml /usr/share/mime/packages/

# Иконки
integration/cat_myapp.svg /usr/share/icons/hicolor/scalable/apps/
integration/myapp.png /usr/share/icons/hicolor/48x48/apps/
  1. Содержимое файла postinst:

#!/bin/bash
# debian/postinst
# Вызывается dpkg после установки пакета.

PACKAGE="myapp"

xdg-mime default "${PACKAGE}.desktop" "application/x-${PACKAGE}"
#DEBHELPER#

exit 0

При вызове команды установки пакета скрипт postinst вызывается пакетным менеджером dpkg после размещения файлов пакета в системе. В данном скрипте выполняется базовая подготовка окружения: финальное обновление системных кэшей (с помощью #DEBHELPER#) и регистрация ассоциаций файлов.

Совет

Файлы, ассоциированные для открытия установленным приложением, будут создаваться с ярлыком приложения. Для уже созданных файлов ярлыки в проводнике обновятся после перезапуска менеджера файлов в активной сессии. На рабочем столе ярлыки файлов обновятся после выполнения команды fly-wmfunc FLYWM_FORCE_UPDATE_SHORTCUT. Описанные выше две настройки применятся сразу для уже созданных файлов при перезагрузке сессии пользователя (или выполнении команды fly-wmfunc FLYWM_RESTART).

  1. Содержимое файла postrm:

#!/bin/bash
# debian/postrm
# Вызывается dpkg после удаления пакета.

ACTION="${1:-}"
case "$ACTION" in
    purge)
        [ -d /opt/my-company/myapp ] && rm -rf /opt/my-company/myapp 2>/dev/null || true
        ;;
esac
fly-wmfunc FLYWM_FORCE_UPDATE_SHORTCUT
#DEBHELPER#
exit 0

При вызове команды удаления пакета скрипт postrm вызывается пакетным менеджером dpkg после удаления отслеживаемых файлов пакета (перечисленных в myapp.install и debian/conffiles). При этом пакетный менеджер самостоятельно работает с конфигурационными файлами (conffiles): в сценарии remove они сохраняются в системе, а в сценарии purge — удаляются. После завершения этих операций запускается скрипт postrm:

  • В сценарии remove переменная ACTION равна remove. Блок case не срабатывает, управление сразу переходит к #DEBHELPER#, который обновляет системные кэши (иконки, MIME, desktop) и завершает работу скрипта.

  • В сценарии purge переменная ACTION равна purge. Выполняется принудительная очистка каталога /opt/my-company/myapp для удаления runtime-данных, кэша и логов, созданных приложением. После этого отрабатывает #DEBHELPER# и скрипт завершается.

Совет

  • Если /opt/my-company/myapp содержит только файлы, установленные пакетом (через debian/install), то dpkg удалит их сам до запуска postrm. В postrm эта папка уже будет пустой или удаленной.

  • Если /opt/my-company/myapp содержит данные, созданные пользователем во время работы программы (логи, кэш, базы данных), которые не входят в состав пакета, тогда при remove не будут удалены пользовательские данные из /opt/my-company/myapp, при purge будет удален весь каталог /opt/my-company/myapp.

  1. Содержимое файла prerm:

#!/bin/bash
# debian/prerm
# -*- coding: utf-8 -*-
# Вызывается dpkg перед удалением файлов пакета.

ACTION="$1"
PACKAGE="myapp"

echo "🛑 $PACKAGE prerm: Запуск действия '$ACTION'..."

terminate_processes() {
    # Ищем только точное совпадение с запуском python3
    local pids
    pids=$(pgrep -f "${PACKAGE}\.py" 2>/dev/null || true)

    [ -z "$pids" ] && { echo "ℹ️  Запущенных экземпляров ${PACKAGE} не обнаружено."; return 0; }

    echo "⚠️  SIGTERM для PID: $pids"
    kill -TERM $pids 2>/dev/null || true

    # Ждём до 3 секунд, проверяем каждую секунду
    for i in 1 2 3; do
        local alive=0
        for pid in $pids; do
            kill -0 "$pid" 2>/dev/null && alive=1
        done
        [ $alive -eq 0 ] && break
        sleep 1
    done

    # Если что-то зависло — добиваем
    if kill -0 $pids 2>/dev/null; then
        echo "⚠️  SIGKILL для неотвечающих PID: $pids"
        kill -KILL $pids 2>/dev/null || true
    fi
}

case "$ACTION" in
    upgrade|remove|purge|failed-upgrade|abort-install|abort-upgrade)
        terminate_processes
        ;;
    *)
        echo "ℹ️  Действие '$ACTION' не требует предварительной остановки процессов."
        ;;
esac

exit 0
Если приложение работает как сервис

Скрипт prerm (systemd сам реализует: SIGTERM → 90s timeout → SIGKILL):

#!/bin/bash
PACKAGE="myapp"
set -e
if [ "$1" = "remove" ] || [ "$1" = "upgrade" ]; then
    systemctl stop "${PACKAGE}.service" 2>/dev/null || true
fi
  1. Содержимое файла rules:

#!/usr/bin/make -f

%:
   dh $@
Скрипт rules для отладки
#!/usr/bin/make -f

# Включить verbose-режим для отладки
export DH_VERBOSE=1

PACKAGE=myapp

%:
   dh $@

# -----------------------------------------------------------------------------
# Проверка скриптов сопровождения (выполняется после dh_installdeb)
# -----------------------------------------------------------------------------
execute_after_dh_installdeb:
   @echo "🔍 Проверка скриптов сопровождения..."
   @echo "=========================================="
   @for script in postinst preinst postrm prerm; do \
      if [ -f debian/$$script ]; then \
         echo "✅ Проверка: $$script"; \
         \
         # 1. Проверка исполняемости \
         if [ ! -x debian/$$script ]; then \
            echo "❌ ОШИБКА: $$script не исполняемый!"; \
            echo "   Выполните: chmod +x debian/$$script"; \
            exit 1; \
         fi; \
         echo "   ✓ Исполняемый"; \
         \
         # 2. Проверка синтаксиса bash \
         if ! bash -n debian/$$script 2>/dev/null; then \
            echo "❌ ОШИБКА: $$script имеет синтаксические ошибки!"; \
            bash -n debian/$$script; \
            exit 1; \
         fi; \
         echo "   ✓ Синтаксис верен"; \
         \
         # 3. Проверка shebang \
         if ! head -1 debian/$$script | grep -q "^#!/bin/\(ba\|da\)sh"; then \
            echo "⚠️  ПРЕДУПРЕЖДЕНИЕ: $$script имеет нестандартный shebang"; \
         fi; \
         echo "   ✓ Shebang корректен"; \
         \
         # 4. Проверка наличия exit 0 в конце \
         if ! tail -5 debian/$$script | grep -q "exit 0"; then \
            echo "⚠️  ПРЕДУПРЕЖДЕНИЕ: $$script может не иметь 'exit 0' в конце"; \
         else \
            echo "   ✓ exit 0 присутствует"; \
         fi; \
         \
         # 5. Проверка #DEBHELPER# (для postinst/postrm/prerm) \
         if [ "$$script" != "preinst" ]; then \
            if grep -q "^#DEBHELPER#$$" debian/$$script; then \
               echo "   ✓ #DEBHELPER# найден"; \
            else \
               echo "⚠️  ПРЕДУПРЕЖДЕНИЕ: #DEBHELPER# не найден (не критично)"; \
            fi; \
         fi; \
         \
         # 6. Проверка на опасный set -e в скриптах удаления \
         if [ "$$script" = "postrm" ] || [ "$$script" = "prerm" ]; then \
            if head -10 debian/$$script | grep -q "^set -e"; then \
               echo "⚠️  ПРЕДУПРЕЖДЕНИЕ: set -e в $$script может прервать удаление!"; \
               echo "   Рекомендуется убрать set -e или добавить '|| true'"; \
            else \
               echo "   ✓ set -e не обнаружен (безопасно)"; \
            fi; \
         fi; \
         \
         echo ""; \
      else \
         echo "ℹ️  $$script отсутствует (не критично)"; \
      fi; \
   done
   @echo "=========================================="
   @echo "✅ Все проверки скриптов завершены"
   @echo ""

# -----------------------------------------------------------------------------
# Дополнительная проверка .desktop файлов (если есть)
# -----------------------------------------------------------------------------
execute_after_dh_install:
   @echo "🔍 Проверка .desktop файлов..."
   @if find debian/$(PACKAGE) -name "*.desktop" -type f 2>/dev/null | grep -q .; then \
      for file in $$(find debian/$(PACKAGE) -name "*.desktop" -type f); do \
         echo "   Проверка: $$file"; \
         if command -v desktop-file-validate >/dev/null 2>&1; then \
            if ! desktop-file-validate "$$file" 2>/dev/null; then \
               echo "❌ ОШИБКА: $$file не прошёл валидацию!"; \
               desktop-file-validate "$$file"; \
               exit 1; \
            fi; \
            echo "   ✓ Валидация пройдена"; \
         else \
            echo "   ⚠️  desktop-file-validate не установлен (пропуск)"; \
         fi; \
      done; \
   else \
      echo "   ℹ️  .desktop файлы не найдены"; \
   fi
   @echo ""

# -----------------------------------------------------------------------------
# Проверка файла debian/install
# -----------------------------------------------------------------------------
execute_before_dh_install:
   @echo "🔍 Проверка debian/install..."
   @if [ -f debian/$(PACKAGE).install ]; then \
      echo "   ✓ Файл install существует"; \
      while read -r src dst; do \
         case "$$src" in \
            \#*|"") continue ;; \
         esac; \
         if [ ! -e "$$src" ]; then \
            echo "❌ ОШИБКА: Файл '$$src' не найден!"; \
            exit 1; \
         fi; \
         echo "   ✓ Найден: $$src → $$dst"; \
      done < debian/$(PACKAGE).install; \
   else \
      echo "   ℹ️  debian/$(PACKAGE).install не найден"; \
   fi
   @echo ""
  1. Сделать скрипты исполняемыми:

chmod +x postinst
chmod +x prerm
chmod +x postrm
chmod +x rules
  1. Собрать пакет, перейдя в myapp-1.0:

# Сборка пакета
dpkg-buildpackage -us -uc -b

# Установка пакета
sudo dpkg -i myapp*.deb

# Удаление пакета (с сохранением логов и конфигов)
sudo dpkg -r myapp

# Полное удаление пакета
sudo dpkg -P myapp

Примечание

После установки пакета ярлыки приложения автоматически появятся в меню «Пуск», на рабочем столе и панели инструментов, а в файловом менеджере обновятся иконки ассоциированных файлов.

Проверка корректности remove

Файл-скрипт проверки:

#!/bin/bash
PACKAGE="myapp"
OPT_DIR="/opt/my-company/${PACKAGE}"
BIN="/usr/bin/${PACKAGE}"

echo "=========================================="
echo "🔍 Проверка состояния после 'dpkg -r ${PACKAGE}'"
echo "=========================================="

# 1. Статус пакета в реестре
STATUS=$(dpkg-query -W -f='${db:Status-Abbrev}' "${PACKAGE}" 2>/dev/null)
case "$STATUS" in
    rc)
        echo "✅ Статус: rc (удалён, конфиги сохранены) — КОРРЕКТНО для 'dpkg -r'"
        ;;
    ii)
        echo "❌ Статус: ii (пакет установлен) — УДАЛЕНИЕ НЕ ПРОИЗОШЛО"
        ;;
    *)
        echo "⚠️  Статус: '${STATUS:-отсутствует}' (ожидается 'rc')"
        ;;
esac

# 2. Удаление отслеживаемых файлов пакета
TRACKED_FILES_REMOVED=true
if [ -e "${OPT_DIR}/${PACKAGE}.py" ]; then echo "❌ Файл пакета остался: ${OPT_DIR}/${PACKAGE}.py"; TRACKED_FILES_REMOVED=false; fi
if [ -e "${BIN}" ]; then echo "❌ Файл/ссылка пакета осталась: ${BIN}"; TRACKED_FILES_REMOVED=false; fi
if [ "$TRACKED_FILES_REMOVED" = true ]; then
    echo "✅ Все отслеживаемые файлы пакета удалены"
fi

# 3. Сохранение пользовательских конфигов (нормально для remove)
if [ -d "${OPT_DIR}/config" ] || find "${OPT_DIR}" \( -name "*.conf" -o -name "*.ini" \) 2>/dev/null | grep -q .; then
    echo "✅ Конфигурационные файлы сохранены (штатное поведение remove)"
else
    echo "ℹ️  Конфигурационные файлы не обнаружены (возможно, их не было в пакете)"
fi

# 4. Метаданные dpkg (должны остаться!)
INFO_COUNT=$(ls -1 /var/lib/dpkg/info/"${PACKAGE}".* 2>/dev/null | wc -l)
if [ "$INFO_COUNT" -gt 0 ]; then
    echo "✅ Метаданные в /var/lib/dpkg/info/ сохранены ($INFO_COUNT файлов) — КОРРЕКТНО"
else
    echo "⚠️  Метаданные отсутствуют (неожиданно для 'dpkg -r', возможно был purge)"
fi

# 5. Процессы приложения
PIDS=$(pgrep -f "^python3 .*${PACKAGE}\.py" 2>/dev/null || true)
if [ -z "$PIDS" ]; then
    echo "✅ Процессы приложения завершены"
else
    echo "❌ Обнаружены запущенные процессы: $PIDS"
fi

# 6. Целостность базы пакетов
if dpkg --audit 2>/dev/null | grep -q "^${PACKAGE}"; then
    echo "❌ dpkg --audit сообщает о проблемах с пакетом"
else
    echo "✅ База dpkg консистентна (audit чист)"
fi

echo "=========================================="
Руководство по устранению неполадок (Troubleshooting Guide)

Восстановление после сбоев установки/удаления

Если при установке или удалении пакета произошли критические ошибки, перед повторной попыткой необходимо убедиться, что система находится в консистентном состоянии.

1. Стандартные средства восстановления

Штатные команды dpkg и apt:

sudo dpkg --configure -a
sudo apt --fix-broken install

Если эти команды завершились успешно, повторная установка должна пройти нормально.

2. Принудительная очистка (если п.1 не помог)

Если пакет «завис» в состоянии half-installed или half-configured:

# Проверка статуса
dpkg -l | grep myapp

# Если пакет виден — попробовать принудительное удаление
sudo dpkg --purge --force-all myapp

3. Ручная очистка

Используйте этот метод только если предыдущие шаги не сработали. Внимание: Неправильное редактирование базы dpkg может сделать систему неработоспособной!

# 1. Создайте резервную копию статуса!
sudo cp /var/lib/dpkg/status /var/lib/dpkg/status.backup

# 2. Проверьте, осталась ли запись о пакете
if dpkg -l | grep -q "^..  myapp "; then
    echo "⚠️  Запись о пакете найдена. Удаляем вручную..."
    # Удаляем блок, начинающийся с "Package: myapp" до следующей пустой строки или следующего "Package:"
    sudo sed -i '/^Package: myapp$/,/^\(Package:\|$\)/{ /^Package: myapp$/!{/^Package:/!d;};}' /var/lib/dpkg/status
    # Более безопасный вариант — использовать dpkg-remove-obsolete или dpkg --remove --force-remove-reinstreq
fi

# 3. Удалите файлы информации о пакете
sudo rm -f /var/lib/dpkg/info/myapp.*

# 4. Обновите кеш зависимостей
sudo apt update

Проверка системы

После выполнения процедур убедитесь, что следов пакета не осталось:

# Проверка реестра
dpkg -l | grep myapp && echo "❌ Остались записи" || echo "✅ Реестр чист"

# Проверка файлов информации
[ $(find /var/lib/dpkg/info -name 'myapp.*' 2>/dev/null | wc -l) -eq 0 ] && echo "✅ Файлы info удалены" || echo "❌ Файлы info остались"

# Проверка установленных файлов (опционально)
[ ! -d /opt/my-company/myapp ] && echo "✅ Приложение удалено" || echo "❌ Каталог приложения остался"

Только после прохождения всех проверок можно повторно устанавливать исправленный пакет.

Полезные ссылки:

Совет

Достаточным является добавление приложения в меню Пуск, т.к. из него удобно добавлять приложение на рабочий стол, в панель инструментов и автозапуск. Если в myapp.install файл myapp.desktop размещать в каталоге /usr/share/fly-wm/shared/flydesktop, то после распаковки ярлык приложения появится на рабочем столе всех пользователей. Если в myapp.install файл myapp.desktop размещать в каталоге /usr/share/fly-wm/shared/flytoolbar, то после распаковки ярлык приложения появится на панели инструментов всех пользователей.