Qt для встраиваемой Linux-системы
С момента выпуска Qt 5.0, Qt больше не содержит собственной реализации системы окон (QWS). Для однопроцессных случаев использования абстракция платформы Qt является лучшим решением. Несколько графических процессов могут поддерживаться через Wayland.
Существует множество плагинов платформы, которые потенциально могут быть использованы на встраиваемых Linux-системах: EGLFS, LinuxFB, DirectFB, Wayland. Доступность этих плагинов зависит от конфигурации Qt. На многих платах eglfs выбирается в качестве значения по умолчанию. Если значение по умолчанию не подходит, параметр переменной окружения QT_QPA_PLATFORM может быть использован для запроса другого плагина. В качестве альтернативы, для быстрых тестов, можно использовать -platform команду с тем же синтаксисом.
Настройка конкретного устройства
Компиляция Qt для данного устройства требует инструментальной цепочки и sysroot. Кроме того, некоторые устройства требуют кода адаптации, специфичного для поставщика, для поддержки EGL и OpenGL ES 2.0. Это не относится к платформам без ускорения, например, к тем, которые используют плагин LinuxFB, который предназначен только для рендеринга на основе программного обеспечения.
Директория qtbase/mkspecs/devices содержит конфигурацию и код адаптации графики для ряда устройств. Например, mkspec linux-rasp-pi2-g++ содержит настройки сборки, такие как оптимальные флаги компилятора и линковщика для устройства Raspberry Pi 2. Mkspec также содержит информацию о реализации eglfs-хуков (код адаптации, специфичный для поставщика), или ссылку на подходящий плагин интеграции устройств eglfs. Устройство выбирается с помощью параметра -device инструмента конфигурации configure. Имя, которое следует после этого аргумента, должно хотя бы частично совпадать с одним из подкаталогов в директории devices.
Следующий пример конфигурации для Raspberry Pi 2. Для большинства встраиваемых Linux-платформ команда конфигурации выглядит аналогично:
./configure -release -opengl es2 -device linux-rasp-pi2-g++ -device-option CROSS_COMPILE=$TOOLCHAIN/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf- -sysroot $ROOTFS -prefix /usr/local/qt5
Самые важные параметры - -device и -sysroot. Указав -sysroot, файлы заголовков и библиотеки, используемые тестами обнаружения функций configure, а также Qt, берутся из указанного расположения, а не из стандартных расположений на хост-компьютере. Это означает, что установка пакетов разработки на хост-машине не имеет значения. Например, для получения поддержки libinput недостаточно или не нужно иметь заголовки и библиотеки разработки libinput установленные на хост-системе. Вместо этого заголовки и библиотеки для целевой архитектуры (например, ARM) должны присутствовать в sysroot.
pkg-config поддерживается также при выполнении кросс-компиляции. configure автоматически устанавливает PKG_CONFIG_LIBDIR для того, чтобы pkg-config отчитывался о настройках компилятора и линковщика, основанных на sysroot вместо хост-машины. Это обычно работает без дополнительных корректировок. Однако переменные окружения, такие как PKG_CONFIG_PATH, должны быть удалены с хост-машины перед запуском configure. В противном случае процесс сборки Qt может попытаться использовать неподходящие заголовки и библиотеки с хост-системы.
Указание -sysroot приводит к автоматической установке аргумента --sysroot при вызове компилятора. В некоторых случаях это нежелательно и может быть отключено, передав -no-gcc-sysroot в configure.
-prefix, -extprefix и -hostprefix управляют предполагаемой целевой директорией сборки Qt. В приведенном выше примере ожидается, что ARM-сборка Qt будет помещена в /usr/local/qt5 на целевом устройстве. Обратите внимание, что запуск make install ничего не развернёт на устройстве. Вместо этого шаг install нацелен на директорию, указанную параметром extprefix, которая по умолчанию равна sysroot + prefix, и, следовательно, является необязательной. Однако во многих случаях «загрязнение» sysroot нежелательно, и поэтому указание -extprefix становится важным. Наконец, -hostprefix позволяет отделить инструменты хоста, такие как qmake, rcc, uic, от библиотек для целевой системы. При указании таких инструментов они будут установлены в указанной директории вместо extprefix.
Для получения дополнительной информации см. Параметры конфигурации Qt.
Плагины платформы для встраиваемых Linux-устройств
EGLFS
EGL — это интерфейс между OpenGL и родной системой окон. Qt может использовать EGL для управления контекстом и поверхностью, однако API не содержит специфики платформы: создание родного окна (которое не обязательно будет реальным окном на экране) всё равно должно выполняться с помощью платформо-специфических средств. Отсюда и необходимость кода адаптации, специфичного для устройства или графического процессора. Такие адаптации предоставляются либо как eglfs-хуки, которые могут быть единственным исходным файлом, скомпилированным в плагин платформы, либо как динамически загружаемые плагины интеграции устройств EGL.
EGLFS — это плагин платформы для запуска приложений Qt5 поверх EGL и OpenGL ES 2.0 без реальной системы окон (такой как X11 или Wayland). Помимо Qt Quick 2 и приложений на базе native OpenGL, он также поддерживает окна с рендерингом на основе программного обеспечения (например, QWidget). В последнем случае содержимое виджетов рендерится процессором в изображения, которые затем загружаются в текстуры и компонуются плагином.
Это рекомендуемый плагин для современных встраиваемых Linux-устройств, которые включают графический процессор.
EGLFS принуждает первое окно верхнего уровня (будь то QWidget или QQuickView) стать полноэкранным. Это окно также выбирается в качестве корневого окна виджета, в которое компонуются все остальные окна верхнего уровня (например, диалоговые окна, всплывающие меню или раскрывающиеся списки). Это необходимо, потому что с EGLFS всегда есть ровно одно родное окно и поверхность EGL окна, и они принадлежат виджету или окну, созданному первым. Этот подход хорошо работает, когда существует главное окно, которое существует на протяжении всего срока службы приложения, а все остальные виджеты являются либо не верхнего уровня, либо создаются позже, после отображения главного окна.
Существуют дополнительные ограничения для окон на основе OpenGL. Начиная с Qt 5.3, eglfs поддерживает одно полноэкранное окно GL (например, OpenGL-основанное QWindow, QQuickView или QGLWidget). Открытие дополнительных окон OpenGL или смешивание таких окон с контентом на основе QWidget не поддерживается и завершает приложение с сообщением об ошибке.
При необходимости eglfs можно настроить с помощью следующих переменных среды:
-
QT_QPA_EGLFS_INTEGRATION- В дополнение к скомпилированным хукам, также можно предоставить адаптацию, специфичную для устройства или поставщика, в виде динамически загружаемых плагинов. Эта переменная среды навязывает определённый плагин. Например, установка значения eglfs_kms использует бэкенд KMS/DRM. Это опция только тогда, когда в device makespecs не были указаны статические или скомпилированные хуки. На практике, традиционные скомпилированные хуки редко используются, почти все бэкенды сейчас мигрированы в плагины. Device makespecs по-прежнему содержат соответствующуюEGLFS_DEVICE_INTEGRATIONзапись: имя предпочтительного бэкенда для конкретного устройства. Это необязательно, но очень полезно, чтобы избежать необходимости устанавливать эту переменную среды в случае наличия более одного плагина в целевой системе. В настольной среде бэкенды KMS или X11 имеют приоритет в зависимости от наличия переменной средыDISPLAY. Обратите внимание, что на некоторых платах вместо фактического плагина используется специальное значениеnone. Это указывает на то, что для использования EGL с буфером кадров не требуется специальная интеграция, и, следовательно, плагины не должны загружаться. -
QT_QPA_EGLFS_PHYSICAL_WIDTHиQT_QPA_EGLFS_PHYSICAL_HEIGHT- Физическая ширина и высота экрана в миллиметрах. На платформах, где значение не может быть запрошено из устройства буфера кадров /dev/fb0 или другими способами, используется значение DPI по умолчанию 100. Эту переменную можно использовать для переопределения таких значений по умолчанию. Установка этого значения важна, потому что приложения на основе QWidget или Qt Quick Controls зависят от этих значений. Запуск с жёстко заданными настройками может привести к неподходящим размерам элементов пользовательского интерфейса для используемого дисплея. -
QT_QPA_EGLFS_ROTATION- Указывает поворот, применяемый к контенту, отрисованному на основе программного обеспечения, в приложениях на основе QWidget. Поддерживаемые значения равны 180, 90 и -90. Это не относится к окнам на основе OpenGL, включая Qt Quick. Приложения Qt Quick могут применять преобразования в сцене QML вместо этого. Стандартный курсор мыши eglfs всегда учитывает это значение, используя соответствующее размещённое и повернутое изображение указателя, независимо от типа приложения. Специальные реализации курсора, такие как аппаратный курсор бэкенда KMS/DRM, могут не поддерживать поворот. -
QT_QPA_EGLFS_FORCEVSYNC- При установкеeglfsзапрашиваетFBIO_WAITFORVSYNCна устройстве буфера кадров после каждого вызова eglSwapBuffers(). Это актуально только для бэкендов, использующих устаревшую подсистемуfbdevLinux. Обычно, с интервалом обмена по умолчанию 1, Qt предполагает, что вызов eglSwapBuffers() заботится о vsync; если это не так (например, из-за проблем с драйверами), попробуйте установитьQT_QPA_EGLFS_FORCEVSYNCна ненулевое значение. -
QT_QPA_EGLFS_FORCE888- При установке значения, размеры каналов цветов красный, зелёный и синий игнорируются при создании нового контекста, окна или внеэкранной поверхности. Вместо этого плагин запрашивает конфигурацию с 8 битами на канал. Это может быть полезно на устройствах, где конфигурации с менее чем 32 или 24 битами на пиксель (например, 5-6-5 или 4-4-4) выбираются по умолчанию, но не являются оптимальными, например, из-за эффектов полос. Вместо изменения кода приложения, эта переменная предоставляет более лёгкий способ принудительно установить конфигурации 24 или 32 бита на пиксель.
Кроме того, доступны и следующие, менее часто используемые, переменные:
-
QT_QPA_EGLFS_FB— Переопределяет устройство буфера кадра. По умолчанию значение/dev/fb0. На большинстве встроенных платформ это не очень актуально, так как буфер кадра используется только для запроса настроек, таких как размеры экрана. Однако на некоторых устройствах этот параметр позволяет указать используемый дисплей в нескольких конфигурациях дисплеев, аналогично параметруfbв LinuxFB. -
QT_QPA_EGLFS_WIDTHиQT_QPA_EGLFS_HEIGHT— Содержат ширину и высоту экрана в пикселях. Хотяeglfsпытается определить размеры по устройству буфера кадра /dev/fb0, но это не всегда работает, и может потребоваться ручное указание размеров. -
QT_QPA_EGLFS_DEPTH— Переопределяет цветовое разрешение экрана. На платформах, где устройство буфера кадра /dev/fb0 недоступно или запрос не успешен, используется значение по умолчанию32. Эта переменная может использоваться для переопределения таких значений по умолчанию. Обратите внимание, что это влияет только на значение цветового разрешения, сообщаемое QScreen. Оно не связано с конфигурациями EGL и цветовым разрешением, используемым для рендеринга OpenGL. -
QT_QPA_EGLFS_SWAPINTERVAL— По умолчанию запрашивается интервал переключения1. Это позволяет синхронизироваться с вертикальной частотой обновления дисплея. Значение может быть переопределено с помощью этой переменной окружения. Например, передача 0 отключит блокировку при переключении, что позволит работать как можно быстрее без синхронизации. -
QT_QPA_EGLFS_DEBUG— При установке выводятся некоторые отладочные данные на вывод отладки. Например, входной QSurfaceFormat и свойства выбранной конфигурации EGL выводятся при создании нового контекста. Вместе с переменнойQSG_INFOQt Quick, это может предоставить полезную информацию для устранения неполадок, связанных с конфигурацией EGL.
В дополнение к QT_QPA_EGLFS_DEBUG, eglfs также поддерживает более современную систему категорированного логирования Qt. Доступны следующие категории логирования:
-
qt.qpa.egldeviceintegration— Включает логирование для динамически загруженных бэкэндов. Очень полезно для проверки, какой бэкенд используется. -
qt.qpa.input— Включает вывод отладки как от обработчиков вводаevdevиlibinput. Очень полезно для проверки, было ли распознано и открыто данное устройство ввода. -
qt.qpa.eglfs.kms— Включает подробное логирование в бэкэнде KMS/DRM.
После запуска configure, убедитесь, что вы проверили его вывод. Отсутствие необходимого бэкенда eglfs, libudev или libinput из-за сбоя соответствующих тестов конфигурации — довольно распространённые проблемы, которые можно легко и быстро распознать таким образом. При получении нежелательного результата «нет», запустите configure с -v, чтобы включить подробный вывод, чтобы увидеть вызовы компилятора и компоновщика для каждого теста конфигурации.
Примечание: Ошибки о недостающих заголовках, библиотеках или, казалось бы, загадочных ошибках компоновщика часто являются признаком неполного или повреждённого sysroot и не связаны с Qt, и не могут быть решены с помощью Qt.
В качестве примера, при нацеливании на Raspberry Pi с собственными графическими драйверами Broadcom, вывод должен содержать что-то вроде следующего. Если это не так, нет смысла продолжать сборку, так как ускоренная графика не будет функциональной без специфичного для Raspberry Pi бэкенда, даже если остальная часть Qt успешно скомпилируется.
QPA backends: EGLFS ................................ yes EGLFS details: EGLFS i.Mx6 ........................ no EGLFS i.Mx6 Wayland ................ no EGLFS EGLDevice .................... no EGLFS GBM .......................... no EGLFS Mali ......................... no EGLFS Rasberry Pi .................. yes EGL on X11 ......................... no
LinuxFB
Этот плагин записывает данные напрямую в буфер кадра через подсистему fbdev Linux. Поддерживается только программный рендеринг. Обратите внимание, что на некоторых конфигурациях производительность отображения может быть ограничена.
Начиная с Qt 5.9, также доступна поддержка буферов DRM «глупых» буферов, так как fbdev устарел в ядре Linux. Это должно быть запрошено путем установки переменной среды QT_QPA_FB_DRM в ненулевое значение. При установке, при условии, что система поддерживает «глупые» буферы, к устаревшим устройствам буфера кадра, таким как /dev/fb0, никогда не обращаются. Вместо этого рендеринг настраивается через API DRM, аналогично бэкенду eglfs_kms eglfs. Вывод будет двухбуферным и с перелистыванием страниц, обеспечивая надлежащий vsync для программного рендеринга.
Примечание: когда используются «глупые» буферы, ни один из описанных ниже параметров не применим, так как такие свойства, как физические и логические размеры экрана, запрошиваются автоматически.
Плагин linuxfb позволяет задавать дополнительные настройки, передавая их в переменную среды QT_QPA_PLATFORM или опцию командной строки -platform. Например, QT_QPA_PLATFORM=linuxfb:fb=/dev/fb1 указывает, что устройство буфера кадра /dev/fb1 должно использоваться вместо значения по умолчанию fb0. Несколько настроек можно указать, разделив их двоеточием.
-
fb=/dev/fbN— Указывает устройства буфера кадра. В конфигурациях с несколькими дисплеями это обычно позволяет запускать приложение на разных дисплеях. Пока нет способа использовать несколько буферов кадра из одного приложения Qt. -
size=<width>x<height> — Указывает размер экрана в пикселях. Плагин пытается запросить размеры отображения, как физические, так и логические, из устройства буфера кадра. Однако это может не всегда привести к правильным результатам, и поэтому может потребоваться явно указать значения. -
mmsize=<width>x<height> — Физическая ширина и высота в миллиметрах. -
offset=<width>x<height> — Смещение в пикселях, указывающее верхний левый угол экрана. По умолчанию положение —(0, 0). -
nographicsmodeswitch— Не переключать виртуальный терминал в графический режим (KD_GRAPHICS). В дополнение к переключению в графический режим, обычно отключаются также мигающий курсор и выключение экрана. При установке этого параметра они также пропустятся. -
tty=/dev/ttyN— Переопределяет виртуальную консоль. Используется только тогда, когдаnographicsmodeswitchне установлено.
Начиная с Qt 5.9, поведение eglfs и linuxfb было синхронизировано в отношении политики изменения размера окна: первое окно верхнего уровня принудительно занимает весь экран в обоих платформах-плагинах. Если этого не требуется, установите переменную среды QT_QPA_FB_FORCE_FULLSCREEN в 0, чтобы восстановить поведение предыдущих версий Qt.
Ввод
При отсутствии оконной системы мышь, клавиатура и сенсорный ввод считываются напрямую через evdev или с помощью вспомогательных библиотек, таких как libinput или tslib. Обратите внимание, что это требует, чтобы узлы устройств /dev/input/event* были доступны для чтения пользователем. eglfs и linuxfb содержат весь код обработки ввода.
Использование libinput
libinput — это библиотека для обработки устройств ввода. Она предлагает альтернативу собственной поддержке ввода Qt evdev. Чтобы включить использование libinput, убедитесь, что файлы разработки для libudev и libinput доступны при конфигурации и компиляции Qt. xkbcommon также необходимо, если требуется поддержка клавиатуры. С eglfs и linuxfb дополнительных действий не требуется, так как эти плагины по умолчанию используют libinput. Если поддержка libinput недоступна или переменная среды QT_QPA_EGLFS_NO_LIBINPUT установлена, вступают в действие собственные обработчики evdev Qt.
Ввод на eglfs и linuxfb без libinput
Параметры, такие как имя узла устройства, могут быть установлены в переменных окружения QT_QPA_EVDEV_MOUSE_PARAMETERS, QT_QPA_EVDEV_KEYBOARD_PARAMETERS и QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS. Разделяйте записи двоеточием. Эти параметры действуют как альтернатива передаче настроек в аргументе командной строки -plugin, и в некоторых бэкендах они являются необходимыми: eglfs и linuxfb используют встроенные обработчики ввода, поэтому отдельный аргумент -plugin не используется.
Кроме того, встроенные обработчики ввода можно отключить, установив QT_QPA_EGLFS_DISABLE_INPUT или QT_QPA_FB_DISABLE_INPUT в 1.
Мышь
Курсор мыши отображается, когда QT_QPA_EGLFS_HIDECURSOR (для eglfs) или QT_QPA_FB_HIDECURSOR (для linuxfb) не установлены, а основанная на libudev система обнаружения устройств Qt сообщает, что доступна хотя бы одна мышь. Когда поддержка libudev отсутствует, курсор мыши всегда отображается, если явно не отключен с помощью переменной среды.
Поддержка подключения по «горячим» подключаемым устройствам поддерживается, но только если Qt был сконфигурирован с поддержкой libudev (то есть если заголовки разработки libudev присутствуют в sysroot во время конфигурации). Это позволяет подключать или отключать устройство ввода во время выполнения приложения.
Обработчик мыши evdev поддерживает следующие дополнительные параметры:
-
/dev/input/...— Указывает имя устройства ввода. Если не указано, Qt ищет подходящее устройство либо через libudev, либо, проходя по доступным узлам. -
nocompress— По умолчанию события ввода, которые не приводят к изменению позиции по сравнению с последним событием мыши Qt, сжимаются; новое событие мыши Qt отправляется только после изменения позиции или состояния кнопки. Это можно отключить, установив параметрnocompress. -
dejitter— Устанавливает предел джиттера. По умолчанию джиттеринг отключен. -
grab— При значении 1 Qt захватывает устройство для эксклюзивного использования. -
abs— Некоторые сенсорные экраны сообщают абсолютные координаты и не могут быть отличимы от тачпадов. В этой специальной ситуации передайтеabs, чтобы указать, что устройство использует абсолютные события.
Клавиатура
Обработчик клавиатуры evdev поддерживает следующие дополнительные параметры:
-
/dev/input/...— Указывает имя устройства ввода. Если не указано, Qt ищет подходящее устройство либо через libudev, либо, проходя по доступным узлам. -
grab— Включает захват устройства ввода. -
keymap— Указывает имя файла пользовательской карты клавиатуры. -
enable-compose— Включает композитинг. -
repeat-delay— Устанавливает пользовательскую задержку повтора клавиш. -
repeat-rate— Устанавливает пользовательскую частоту повтора клавиш.
На системах Embedded Linux, у которых не отключены сессии терминала, поведение при нажатии клавиши может быть запутанным, так как событие ввода обрабатывается приложением Qt и tty. Для решения этой проблемы доступны следующие параметры:
-
EGLFS и LinuxFB пытаются отключить клавиатуру терминала при запуске приложения, установив режим клавиатуры tty в
K_OFF. Это предотвращает попадание нажатий клавиш в терминал. Если по какой-то причине необходимо восстановить стандартное поведение, установите переменную окруженияQT_QPA_ENABLE_TERMINAL_KEYBOARDв значение1. Обратите внимание, что это работает только при запуске приложения из удалённой консоли (например, черезssh) и вход с клавиатуры терминала остаётся включённым. - Альтернативный подход — использовать параметр обработчика клавиатуры evdev
grab, передав grab=1 вQT_QPA_EVDEV_KEYBOARD_PARAMETERS. Это приводит к попытке захвата устройства ввода. Еслиgrabбудет успешным, никакие другие компоненты системы не получат события от него, пока приложение Qt работает. Этот подход более подходит для приложений, запускаемых удалённо, поскольку он не требует доступа к устройству tty. - Наконец, для многих специализированных образов Embedded Linux нет смысла вообще включать стандартные сеансы терминала. Обратитесь к документации вашей среды сборки, чтобы узнать, как их отключить. Например, при создании образов с помощью Yocto Project, сброс значения
SYSVINIT_ENABLED_GETTYSприводит к отсутствию процессаgettyи, следовательно, отсутствию ввода на любом из виртуальных терминалов.
Если встроенная по умолчанию раскладка клавиатуры недостаточна, можно указать другую, либо через параметр keymap, либо используя функцию eglfs loadKeymap(). Последний вариант позволяет переключать раскладку во время выполнения. Однако обратите внимание, что это требует использования встроенного обработчика клавиатуры eglfs; он не поддерживается, когда обработчик клавиатуры загружается через параметр командной строки -plugin.
Примечание: Специальные системные сочетания клавиш, такие как переключение консолей (Ctrl+Alt+Fx) или удаление (Ctrl+Alt+Backspace), в настоящее время не поддерживаются и игнорируются.
Для создания пользовательской раскладки можно использовать утилиту kmap2qmap. Она находится в модуле qttools. Исходные файлы должны быть в стандартном Linux kmap формате, который понимает команда ядра loadkeys. Это означает, что можно использовать следующие источники для создания файлов qmap:
- Проект Linux Console Tools (LCT).
-
Раскладки X11 Xorg могут быть преобразованы в формат
kmapс помощью утилитыckbcomp. - Поскольку файлы
kmapявляются текстовыми файлами, их также можно создать вручную.
kmap2qmap — это командная программа, которой для работы необходимо как минимум 2 файла в качестве параметров. Последний — сгенерированный файл .qmap, а все остальные — входные файлы .kmap. Например:
kmap2qmap i386/qwertz/de-latin1-nodeadkeys.kmap include/compose.latin1.inc de-latin1-nodeadkeys.qmap
Примечание: kmap2qmap не поддерживает все (псевдо)символы, которые поддерживает ядро Linux. При преобразовании стандартной раскладки будет отображено несколько предупреждений о Show_Registers, Hex_A и т. д.; эти сообщения можно безопасно игнорировать.
Touch
Для некоторых резистивных сенсорных экранов с одним касанием может потребоваться вернуться к использованию tslib вместо использования протокола Linux multi-touch и устройств событий. Для современных сенсорных экранов это не требуется. Поддержка tslib может быть включена путём установки переменной окружения QT_QPA_EGLFS_TSLIB или QT_QPA_FB_TSLIB в 1. Для изменения устройства установите переменную окружения TSLIB_TSDEVICE или передайте имя устройства в командной строке. Обратите внимание, что обработчик ввода tslib генерирует события мыши и поддерживает только одно касание, в отличие от evdevtouch, который также генерирует истинные многосенсорные события QTouchEvent.
Обработчик сенсорного ввода evdev поддерживает следующие дополнительные параметры:
-
/dev/input/...— Указывает имя устройства ввода. Если параметр не указан, Qt ищет подходящее устройство с помощью libudev или перебирая доступные узлы. -
rotate— На некоторых сенсорных экранах координаты необходимо вращать, что выполняется путём установкиrotateв 90, 180 или 270. -
invertxиinverty— Для инвертирования координат X или Y в событиях ввода передайтеinvertxилиinverty.
Например, выполнение export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/event5:rotate=180 перед запуском приложений приводит к явно указанному устройству сенсорного ввода и инвертированию координат — полезно, когда ориентация фактического экрана и сенсорного экрана не совпадают.
Планшеты с ручкой
Плагин evdevtablet предоставляет базовую поддержку планшетов с ручкой, таких как Wacom, и аналогичных устройств. Он генерирует события QTabletEvent только. Для его включения передайте QT_QPA_GENERIC_PLUGINS=evdevtablet в переменной окружения или, как альтернатива, передайте параметр -plugin evdevtablet в командной строке. Плагин может принимать параметр узла устройства, например QT_QPA_GENERIC_PLUGINS=evdevtablet:/dev/event1, если автоматическое обнаружение устройства Qt (основанное на libudev или обходе /dev/input/event*) не работает или работает неправильно.
Отладка устройств ввода
Можно вывести некоторую информацию в выходные данные отладки, включив правило регистрации qt.qpa.input, например, установив переменную окружения QT_LOGGING_RULES в значение qt.qpa.input=true. Это полезно для определения используемого устройства или для устранения неполадок при обнаружении устройства.
Использование пользовательских изображений курсора мыши
eglfs поставляется со своим набором изображений курсора мыши размером 32x32. Если этого недостаточно, можно предоставить пользовательский атлас курсора, установив переменную окружения QT_QPA_EGLFS_CURSOR в имя файла JSON. Файл также можно встроить в приложение с помощью системы ресурсов Qt.
Например, встроенный атлас курсора с 8 изображениями курсора в строке можно указать следующим образом:
{
"image": ":/cursor-atlas.png",
"cursorsPerRow": 8,
"hotSpots": [
[7, 2],
[12, 3],
[12, 12],
...
]
} Обратите внимание, что изображения должны быть плотно упакованы в атласе: ширина и высота курсоров определяются на основе общего размера изображения и параметра cursorsPerRow. Атласы должны предоставлять изображение для всех поддерживаемых курсоров.
Вывод на экран
При подключении нескольких дисплеев уровень поддержки привязки к одному или нескольким из них из одного приложения Qt варьируется между плагинами платформы и часто зависит от устройства и его графического стека.
eglfs с eglfs_kms бэкэндом
Когда используется бэкэнд KMS/DRM, eglfs сообщает обо всех доступных экранах в QGuiApplication::screens(). Приложения могут назначать различные окна различным экранам с помощью QWindow::setScreen().
Примечание: Ограничение одного полноэкранного окна на экран по-прежнему действует. Изменение экранов после того, как окно QWindow стало видимым, также не поддерживается. Поэтому крайне важно, чтобы встроенные приложения выполнили все необходимые вызовы QWindow::setScreen() перед вызовом QWindow::show().
Приступая к разработке на конкретном встроенном устройстве, часто необходимо проверить поведение устройства и драйверов, а также убедиться, что подключённые дисплеи работают должным образом. Один из простых способов — использовать пример hellowindow. Запуск его с аргументами -platform eglfs --multiscreen --timeout отображает вращающийся логотип Qt на каждом подключённом экране в течение нескольких секунд.
Примечание: Большинство параметров конфигурации, описанных ниже, относятся ко всем бэкэндам, основанным на KMS/DRM, независимо от технологии управления буферами (GBM или EGLStreams).
Бэкэнд KMS/DRM также поддерживает пользовательские конфигурации с помощью файла JSON. Установите переменную окружения QT_QPA_EGLFS_KMS_CONFIG в имя файла, чтобы включить эту функцию. Файл также можно встроить в приложение с помощью системы ресурсов Qt. Пример конфигурации приведён ниже:
{
"device": "/dev/dri/card1",
"hwcursor": false,
"pbuffers": true,
"outputs": [
{
"name": "VGA1",
"mode": "off"
},
{
"name": "HDMI1",
"mode": "1024x768"
}
]
} Здесь мы настраиваем указанное устройство так, что:
- не будет использовать аппаратный курсор (вернётся к отрисовке курсора мыши с помощью OpenGL; по умолчанию аппаратные курсоры включены, так как они более эффективны),
- будет использовать QOffscreenSurface со стандартными поверхностями EGL pbuffer (по умолчанию это отключено и вместо этого используется поверхность gbm),
- выход на VGA-разъём отключён, а HDMI активен с разрешением 1024x768.
Кроме того, такая конфигурация также отключает поиск устройства с помощью libudev и вместо этого использует указанное устройство.
Если mode не определено, выбирается режим, который система сообщает как предпочтительный. Допустимые значения для mode: off, current, preferred, widthxheight, widthxheight@vrefresh или строка модолинии.
Указание current выберет режим с разрешением, соответствующим текущему. Поскольку изменение режима осуществляется только тогда, когда желаемый режим отличается от активного (если не принудительно установлено через переменную среды QT_QPA_EGLFS_ALWAYS_SET_MODE), эта опция полезна для сохранения текущего режима и любого содержимого в плоскостях, не затронутых Qt.
Все экраны, сообщённые слоем DRM, по умолчанию обрабатываются как один большой виртуальный рабочий стол. Реализация курсора мыши учтёт это и будет перемещаться по экранам как ожидается. Хотя не рекомендуется, режим виртуального рабочего стола можно отключить, установив separateScreens в false в конфигурации, если это необходимо.
По умолчанию виртуальный рабочий стол формируется слева направо, в порядке подключения разъёмов, как сообщается системой. Это можно изменить, установив virtualIndex в значение, начиная с 0. Например, следующая конфигурация использует предпочтительное разрешение, но гарантирует, что левая сторона виртуального рабочего стола — экран, подключённый к порту HDMI, а правая — экран, подключённый к порту DisplayPort:
{
"device": "drm-nvdc",
"outputs": [
{
"name": "HDMI1",
"virtualIndex": 0
},
{
"name": "DP1",
"virtualIndex": 1
}
]
} Порядок элементов в массиве не имеет значения. Выводы без указанного виртуального индекса будут размещены после других, сохраняя исходный порядок в списке разъёмов DRM.
Для создания вертикального рабочего пространства (то есть для расположения сверху вниз вместо слева направо) добавьте свойство virtualDesktopLayout после device со значением vertical.
Примечание: Рекомендуется, чтобы все экраны на виртуальном рабочем столе имели одинаковое разрешение, в противном случае элементы, такие как курсор мыши, могут вести себя непредсказуемым образом при переходе в области, существующие только на одном конкретном экране.
Если virtualIndex недостаточно, можно использовать свойство virtualPos для явного указания верхнего левого положения рассматриваемого экрана. Принимая предыдущий пример и предполагая разрешение 1080p для HDMI1, следующее помещает второй HDMI-экран ниже первого:
{
...
"outputs": [
...
{
"name": "HDMI2",
"virtualPos": "0, 1080"
}
]
} Примечание: Избегайте таких конфигураций, если требуется поддержка мыши. Поведение курсора мыши может быть непредсказуемым при использовании нелинейных макетов. Однако с сенсорным вводом проблем не должно возникнуть.
В некоторых случаях автоматическое определение размера физического экрана с помощью DRM может завершиться неудачей. Обычно переменные среды QT_QPA_EGLFS_PHYSICAL_WIDTH и QT_QPA_EGLFS_PHYSICAL_HEIGHT использовались для предоставления недостающих значений, однако это больше не подходит при наличии нескольких экранов. Вместо этого используйте свойства physicalWidth и physicalHeight в списке outputs для указания размеров в миллиметрах.
Примечание: Не рекомендуется использовать различные физические размеры и, следовательно, различные логические DPI, поскольку это может привести к непредвиденным проблемам из-за того, что некоторые компоненты графического стека не знают о нескольких экранах и полагаются только на значения первого экрана.
Каждый активный элемент массива outputs соответствует одному экземпляру QScreen, сообщённому из QGuiApplication::screens(). Основной экран, сообщённый функцией QGuiApplication::primaryScreen(), по умолчанию — это экран, который регистрируется первым. При отсутствии использования virtualIndex, это означает, что решение основано на порядке подключения DRM. Чтобы переопределить это, установите свойство primary в значение true для нужного элемента в списке outputs. Например, чтобы гарантировать, что экран, соответствующий выводу VGA, будет основным, даже если система сообщает о выводе HDMI первым, можно сделать следующее:
{
"device": "/dev/dri/card0",
"outputs": [
{ "name": "HDMI1" },
{ "name": "VGA1", "mode": "1280x720", "primary": true },
{ "name": "LVDS1", "mode": "off" }
]
} Для устранения неполадок может быть полезно включить журналы отладки из бэкенда KMS/DRM. Для этого включите правило категорированного логирования qt.qpa.eglfs.kms.
Примечание: В интегрированной среде виртуальные рабочие столы ограничены по сравнению с полной системой окон. Следует избегать перекрытия окон на нескольких экранах, окон, не занимающих весь экран, и перемещения окон между экранами, так как их поведение может быть непредсказуемым.
Наиболее распространенный и наиболее поддерживаемый сценарий использования многоэкранной настройки — это открытие выделенного QQuickWindow или QQuickView для каждого экрана. Благодаря по умолчанию циклу отрисовки threaded графика Qt Quick, каждое из этих окон получит свою отдельную нить отрисовки. Это хорошо, потому что нити можно ограничивать независимо, основываясь на vsync, и они не будут мешать друг другу. При использовании цикла basic это может стать проблематичным, и в результате качество анимации может ухудшиться.
Например, обнаружение всех подключённых экранов и создание QQuickView для каждого из них можно выполнить следующим образом:
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QVector<QQuickView *> views;
for (QScreen *screen : app.screens()) {
QQuickView *view = new QQuickView;
view->setScreen(screen);
view->setResizeMode(QQuickView::SizeRootObjectToView);
view->setSource(QUrl("qrc:/main.qml"));
QObject::connect(view->engine(), &QQmlEngine::quit, qGuiApp, &QCoreApplication::quit);
views.append(view);
view->showFullScreen();
}
int result = app.exec();
qDeleteAll(views);
return result;
} Дополнительные возможности eglfs_kms
Клонирование экранов (отображение) поддерживается с Qt 5.11. Его можно включить свойством clones.
{
"device": "/dev/dri/card0",
"outputs": [
{ "name": "HDMI1", "mode": "1920x1080" },
{ "name": "DP1", "mode": "1920x1080", "clones": "HDMI1" }
]
} Здесь содержимое на дисплее, подключённом через DisplayPort, будет таким же, как и на HDMI. Это обеспечивается простым сканированием одного и того же буфера на обоих.
Примечание: Это может работать только если разрешения одинаковые, нет несовместимостей с форматами буфера, и приложение не имеет вывода на QScreen, связанный с клонируемым целевым экраном. На практике это означает, что ни одно QWindow, связанное с QScreen (DP1 в примере), не должно выполнять операцию QOpenGLContext::swapBuffers(). Это должно быть учтено в конфигурации и приложении.
Режим без графического интерфейса с помощью узлов отрисовки DRM поддерживается с Qt 5.11. Это позволяет выполнять вычисления на GPU (шейдеры OpenGL для вычислений, OpenCL) или отрисовку вне экрана OpenGL без необходимости привилегий DRM-мастера. В этом режиме приложения могут работать даже в том случае, если существует другой процесс, выводящий изображение на экран.
Простое переключение device с /dev/dri/card0 на /dev/dri/renderD128 само по себе бесполезно, так как ряд операций не может быть выполнен в режиме без графического интерфейса. Поэтому это необходимо комбинировать со свойством headless, например:
{
"device": "/dev/dri/renderD128",
"headless": "1024x768"
} Обратите внимание, что окна по-прежнему масштабируются для соответствия размеру экрана — теперь виртуальному, поэтому необходимо указать размер в свойстве headless. Также отсутствует регулирование частоты кадров с учётом vsync.
После включения у приложений есть два типичных варианта выполнения отрисовки вне экрана в режиме без графического интерфейса:
Использование обычного окна, например, подкласса QOpenGLWindow, нацеленного на буфер кадра по умолчанию окна, что по сути является gbm_surface.
MyOpenGLWindow w;
w.show(); // will not actually show up on screen
w.grabFramebuffer().save("output.png"); Или типичный подход вне экрана с дополнительным FBO:
QOffscreenSurface s;
s.setFormat(ctx.format());
s.create();
ctx.makeCurrent(&s);
QOpenGLFramebufferObject fbo(1024, 768);
fbo.bind();
ctx.functions()->glClearColor(1, 0, 0, 1);
ctx.functions()->glClear(GL_COLOR_BUFFER_BIT);
fbo.toImage().save("output.png");
ctx.doneCurrent(); eglfs с бэкендом eglfs_kms_egldevice
Этот бэкенд, обычно используемый на устройствах Tegra, похож на бэкенд KMS/DRM, упомянутый выше, за исключением того, что он использует расширения EGLDevice и EGLStream вместо GBM.
Дополнительные технические сведения об этом подходе см. в этой презентации.
С Qt 5.7 этот бэкенд разделяет многие из своих внутренних реализаций с бэкендом на основе GBM. Это означает, что поддерживаются несколько экранов и расширенная конфигурация с помощью QT_QPA_EGLFS_KMS_CONFIG. Однако некоторые настройки, такие как hwcursor и pbuffers, неприменимы.
По умолчанию бэкенд автоматически выбирает правильный EGL-уровень для планов вывода по умолчанию. При необходимости это можно переопределить, установив переменную окружения QT_QPA_EGLFS_LAYER_INDEX в индекс нужного уровня. Этот подход в настоящее время не поддерживает несколько выходов, поэтому его использование следует ограничить системами с одним экраном. Чтобы увидеть доступные уровни и отладить возможные проблемы при запуске, включите категорию логирования qt.qpa.eglfs.kms.
В некоторых случаях может потребоваться выполнить установку режима видео при запуске приложения, даже если экран сообщает, что желаемое разрешение уже установлено. Обычно это оптимизируется, но если экран остается выключенным, попробуйте установить переменную окружения QT_QPA_EGLFS_ALWAYS_SET_MODE в ненулевое значение и запустить приложение заново.
Для настройки поведения объекта EGLStream, используемого бэкендом, используйте переменную среды QT_QPA_EGLFS_STREAM_FIFO_LENGTH. Предполагается, что KHR_stream_fifo поддерживается целевой системой. По умолчанию поток работает в режиме буфера обмена. Для переключения на режим FIFO установите значение 1 или больше. Значение указывает максимальное количество кадров, которое может хранить поток.
На некоторых системах может потребоваться нацеливание на определённый слой перекрытия через предварительно определённый разъем. Простое принудительное задание индекса слоя с помощью QT_QPA_EGLFS_LAYER_INDEX не выполняет конфигурацию слоя и поэтому не подходит само по себе. Вместо этого в таких особых случаях используйте переменные среды QT_QPA_EGLFS_KMS_CONNECTOR_INDEX и QT_QPA_EGLFS_KMS_PLANE_INDEX. При установке этих переменных будут использоваться только указанный разъем и слой, все остальные выходы будут игнорироваться. Бэкенд позаботится о выборе EGL-уровня, соответствующего нужному слою, и о настройке слоя.
Ввод сенсорного экрана в системах с несколькими экранами на KMS/DRM
Сенсорные экраны требуют дополнительных соображений в системах с несколькими дисплеями, поскольку события сенсорного ввода должны быть перенаправлены на правильный виртуальный экран, и для этого требуется правильное сопоставление между сенсорными экранами и дисплейными выходами.
Сопоставление выполняется с помощью файла конфигурации JSON, указанного в QT_QPA_EGLFS_KMS_CONFIG, и описанного в предыдущих разделах. Когда свойство touchDevice присутствует в элементе массива outputs, значение обрабатывается как узел устройства, и сенсорное устройство связывается с дисплейным выходом, о котором идет речь.
Например, предположим, что наш сенсорный экран имеет узел устройства /dev/input/event5 и интегрирован в монитор, подключенный через HDMI как дополнительный экран. Следующая конфигурация гарантирует правильное преобразование событий сенсорного ввода (и синтезированных событий мыши):
{
"device": "drm-nvdc",
"outputs": [
{
"name": "HDMI1",
"touchDevice": "/dev/input/event5",
"virtualIndex": 1
},
{
"name": "DP1",
"virtualIndex": 0
}
]
} Примечание: В случае сомнений включите логирование как из графических, так и из входных подсистем, установив переменную среды QT_LOGGING_RULES=qt.qpa.*=true перед запуском приложения. Это поможет определить правильные узлы входных устройств и может выявить проблемы с конфигурацией вывода, которые в противном случае могут быть сложными для отладки.
Примечание: Начиная с Qt 5.8, вышеуказанное поддерживается только для бэкенда evdevtouch. Другие варианты, такие как базирующийся на libinput, будут по-прежнему перенаправлять события на основной экран. Чтобы принудительно использовать evdevtouch на системах, где доступно несколько входных бэкендов, установите переменную среды QT_QPA_EGLFS_NO_LIBINPUT в значение 1.
eglfs с другими бэкендами
Другие бэкенды, которые, как правило, основаны на непосредственной работе с буфером кадра или API композиции через реализацию EGL поставщика, обычно предоставляют ограниченную или вообще никакую поддержку нескольких дисплеев. На платах на базе i.MX6 с графическими процессорами Vivante переменную среды QT_QPA_EGLFS_FB можно использовать для указания целевого буфера кадра, аналогично linuxfb. На Raspberry Pi переменную среды QT_QPA_EGLFS_DISPMANX_ID можно использовать для указания экрана для вывода. Значение соответствует одному из констант DISPMANX_ID_, см. документацию Dispmanx. Обратите внимание, что в отличие от KMS/DRM, эти подходы обычно не позволят выводить изображение на несколько экранов из одного приложения. В качестве альтернативы могут быть доступны и зависящие от драйвера переменные среды или параметры ядра для управления используемым буфером кадра. См. документацию по конкретной встроенной плате.
Видеопамять
Системы с фиксированным объемом выделенной видеопамяти могут потребовать дополнительных мер предосторожности перед запуском приложения Qt, основанного на Qt Quick или классах, таких как QOpenGLWidget. Значение по умолчанию может быть недостаточным для таких приложений, особенно при отображении на экране с высоким разрешением (например, Full HD). В этом случае они могут начать сбоить непредсказуемым образом. Рекомендуется обеспечить наличие не менее 128 МБ памяти GPU. Для систем, не имеющих фиксированного объема памяти, выделенного для графического процессора, это не проблема.
linuxfb
Используйте параметр плагина fb для указания устройства буфера кадра для использования.
Обработчики сигналов Unix
Плагины платформы для консольных систем, такие как eglfs и linuxfb, по умолчанию устанавливают обработчики сигналов для захвата прерывания (SIGINT), приостановки и продолжения (SIGTSTP, SIGCONT) и завершения (SIGTERM). Таким образом, клавиатура, курсор терминала и, возможно, другое состояние графики могут восстанавливаться при завершении работы приложения или его приостановке из-за kill, Ctrl+C или Ctrl+Z. (хотя завершение или приостановку через клавиатуру можно выполнить только при установке QT_QPA_ENABLE_TERMINAL_KEYBOARD, как указано выше в разделе Ввод). Однако в некоторых случаях захват SIGINT может быть нежелателен, так как это может конфликтовать, например, с удаленной отладкой. Поэтому предоставлена переменная среды QT_QPA_NO_SIGNAL_HANDLER для отказа от всех встроенных обработчиков сигналов.
Шрифты
Qt обычно использует fontconfig для доступа к системным шрифтам. Если fontconfig недоступен, Qt переключится на использование QBasicFontDatabase. В этом случае приложения Qt будут искать шрифты в каталоге lib/fonts Qt. Qt автоматически обнаружит предварительно рендеренные шрифты и TrueType шрифты. Этот каталог можно переопределить, установив переменную окружения QT_QPA_FONTDIR.
Дополнительную информацию о поддерживаемых форматах см. в разделе Шрифты Qt для встраиваемых систем Linux.
Примечание: Qt больше не поставляет шрифты в каталоге lib/fonts. Это означает, что платформа (образ системы) должна предоставлять необходимые шрифты.
Плагины платформы для систем оконного интерфейса на встраиваемых устройствах Linux
XCB
Это плагин X11, используемый на обычных платформах Linux для настольных компьютеров. В некоторых встраиваемых средах, которые предоставляют X и необходимые файлы разработки для xcb, этот плагин функционирует так же, как и на обычном настольном ПК.
Примечание: На некоторых устройствах нет поддержки EGL и OpenGL под X, поскольку реализация EGL несовместима с Xlib. В этом случае плагин XCB создаётся без поддержки EGL, что означает, что приложения Qt Quick 2 или другие приложения, основанные на OpenGL, не работают с этим плагином платформы. Однако его можно использовать для запуска приложений с программной отрисовкой (например, на основе QWidget).
Как правило, использование XCB на встраиваемых устройствах не рекомендуется. Плагины, такие как eglfs, скорее всего, обеспечат лучшую производительность и аппаратное ускорение.
Wayland
Wayland — это лёгкая система оконного интерфейса; точнее, это протокол для взаимодействия клиентов с сервером отображения.
Модуль Qt Wayland предоставляет плагин платформы wayland, который позволяет приложениям Qt подключаться к композитору Wayland.
Примечание: Могут возникнуть проблемы с вводом сенсорного экрана при использовании эталонного композитора Weston. Для получения дополнительной информации обратитесь к Вики Qt.
Qt Quick WebGL
Плагин платформы Qt Quick WebGL позволяет осуществлять удаленный доступ путём потоковой передачи пользовательских интерфейсов Qt Quick по сети. Интерфейс пользователя рендерится в веб-браузере клиента, поддерживающем WebGL™.
Примечание: Плагин Qt Quick WebGL в настоящее время предоставляется как Технологический предварительный просмотр.
Настройка и использование
Для использования плагина Qt Quick WebGL Qt необходимо настроить с поддержкой OpenGL ES 2:
./configure [...] -opengl es2
Плагин зависит от Qt WebSockets.
Запуск приложения Qt Quick с плагином платформы webgl выполняется следующим образом:
./qmlapplication -platform webgl
Это запускает лёгкий веб-сервер на порту 8080, к которому клиент может подключиться с помощью веб-браузера, поддерживающего WebGL. Порт прослушивания можно настроить следующим образом:
./qmlapplication -platform webgl:port=80
Поддерживаются события клавиатуры, мыши, касания и многократного касания от клиента.
Ограничения
- Потоковая передача команд OpenGL® по сети приводит к задержке по сравнению с запуском приложения локально.
- Плагин не поддерживает приложения для настольных компьютеров, использующие Qt Widgets.
- Элементы текста могут отображаться неправильно, если параметр рендеринга Text.NativeRendering задан.
- Допускается только один активный клиент на процесс. Попытки последующих клиентов подключиться к серверу будут отображать индикатор загрузки до отключения предыдущего клиента.
- Потоковая передача аудио не поддерживается.
Примечание: Плагин webgl требует потоковую обработку отрисовки. На Windows и других платформах, использующих по умолчанию другой цикл рендеринга, установите переменную окружения QSG_RENDER_LOOP соответственно:
set QSG_RENDER_LOOP=threaded
Связанные темы
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.11/embedded-linux.html