Существуют ли макросы для определения того, использую ли я Wayland или X11?

В настоящее время я работаю над проектом и для обучения хочу написать себе оконную систему с кодом, специфичным для платформы.

Итак, я использую препроцессор, чтобы во время компиляции узнать, на какой платформе я работаю, но я не знаю, как определить оконную систему в Linux. Мне это очень нужно, потому что приложения X11 и Wayland несовместимы; поэтому мне придется писать разные файлы.

Вот мой файл с опциями препроцессора, который я нашел на этом сайте:

// Windows OS
#if defined(_WIN32) || defined(_WIN64)
  #define PLATFORM_WINDOWS 1
// Linux OS
#elif defined(__linux__)
  #define PLATFORM_LINUX 1
  #if defined(WAYLAND)
    #define LINUX_ON_WAYLAND
  #elif defined(X11)
    #define LINUX_ON_X11
  #endif
// Unix OS
#elif defined(__unix) || defined(__unix__)
  #define PLATFORM_UNIX 1
// Mac OS
#elif defined(__APPLE__) || defined(__MACH__)
  #define PLATFORM_MACOS
// Unknown OS
#else
  #error "Unknown platform."
#endif

Как видите, я добавил заполнители для WAYLAND и X11, но есть ли способ заставить такую ​​систему работать?

Главное — абстракции. Поместите все, что не зависит от базовой системы отображения (X11 или Wayland), в отдельную библиотеку, все, что зависит от X11, в одну библиотеку и все, что зависит от Wayland, в третью библиотеку. Сделайте интерфейсы (API) одинаковыми для обеих библиотек. Затем вы создаете небольшую и простую связующую программу и настраиваете систему сборки для связи с библиотекой X11 или с библиотекой Wayland. При желании сделайте библиотеки системы отображения динамическими (файлы .so), и программа склеивания загрузит их во время выполнения. Использование подобных макросов просто превратит ваш код в беспорядок, который невозможно поддерживать.

Some programmer dude 07.05.2024 05:01

Откуда макрос мог знать? Они оцениваются во время компиляции, но один или другой используется во время выполнения. Обычно системы сборки работают в изолированных средах и используются в серьезных средах (например, фермах сборки дистрибутивов Linux), которые вообще не имеют доступа к графическому интерфейсу. Решения о том, какие дополнительные зависимости включить, затем принимаются с помощью таких инструментов, как autoconf; существует некоторый автоматический выбор, основанный на том, какие заголовки установлены, но помимо этого у вас есть параметры, передаваемые инструментам командной строки, где люди создают пакеты с конфигурацией, явно выбирающей параметры.

Charles Duffy 07.05.2024 05:38

Вы можете использовать систему, подобную autoconf, чтобы определить, доступны ли заголовки X11 или Wayland (отдельно от запуска сборки и до него). Если у вас есть доступ к C23 везде, где это актуально, возможно, вы можете использовать новый __has_include(headername), чтобы решить, что вы используете — возможно, тестируя основной заголовок Wayland и предполагая, что Wayland присутствует, и тестируя основной заголовок X11, предполагая X11. если он присутствует, и жалуется, если ни один из них недоступен в Linux.

Jonathan Leffler 07.05.2024 05:47

То, что вы представляете себе выбор между Wayland и X11 во время сборки, означает, что вы должны планировать наличие источника, поддерживающего оба. В этом случае создайте оба и выберите между ними во время выполнения. Существует множество способов сделать это. Альтернативно, просто выберите один. Не существует закона, согласно которому ваше программное обеспечение должно поддерживать все мыслимые конфигурации системы. Особенно если это личный проект, как кажется. И если ваш выбор — X, то вы, вероятно, все равно сможете запустить его поверх Wayland, используя соответствующий X-сервер.

John Bollinger 07.05.2024 06:55

@Someprogrammerdude Я уже это сделал. Вдохновленный Трэвисом Вроманом и его игровым движком Kohi (ссылка), у меня есть интерфейс в шапке platform.h, а реализация для каждой ОС имеет свою собственную подпапку. Но чтобы знать, в какой подпапке использовать определения кода для компиляции, я использую защиту заголовков, например #ifdef PLATFORM_*** [...] #endif. Если у вас есть идеи получше, добро пожаловать.

lordeji 07.05.2024 12:56

@JohnBollinger Как объяснено в моем комментарии выше, я использую своего рода интерфейс для своей реализации. Следовательно, как я могу выбрать во время выполнения, какую реализацию? Я знаю, что есть переменная среды под названием XDG_SESSION_TYPE, достаточно ли будет использования такой команды, как system("echo $XDG_SESSION_TYPE")?

lordeji 07.05.2024 12:56

@JonathanLeffler Я нахожусь на Arch (кстати), так что да, сейчас я работаю с C23 (потому что я знаю, что этот проект будет хобби на долгие годы, поэтому я хочу использовать C23, потому что в долгосрочной перспективе я буду рад иметь его все эти новые функции). Это будет инструмент, работающий во время компиляции или во время выполнения? Есть ли какое-либо преимущество между выполнением этого во время компиляции или во время выполнения? И будет ли мое решение выше с использованием XDG_SESSION_TYPE менее подвержено ошибкам?

lordeji 07.05.2024 12:57

Я не уверен, что у механизма __has_include есть преимущество; Я еще ни для чего его не использовал, но он существует. Я склонен предпочесть какой-то этап настройки (autoconf — самый известный — и, вероятно, самый ненавистный — но он далеко не единственный и может быть для вас излишним). Я не уверен в относительных преимуществах времени компиляции и определения того, какую оконную систему использовать во время выполнения. В переходный период (следующее десятилетие или около того), пока Wayland борется за использование X11, время выполнения может быть лучше — создайте оба, если на ваших машинах есть обе установки для разработки.

Jonathan Leffler 07.05.2024 16:28

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

Jonathan Leffler 07.05.2024 16:29

Цель немного расплывчата: x11 можно использовать с разными API (привязки xlib или xcb), есть также xwayland для запуска приложений x11 под Wayland и так далее.

yvs2014 09.05.2024 09:05
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
10
142
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Думаю, я нашел два достаточно хороших решения.

Первый работает во время компиляции: просто вручную установите флаг типа -DBUILDING_WITH_X11 или -DBUILDING_WITH_WAYLAND, а затем проверьте, что при компиляции определен только один флаг. Пример моего предыдущего кода с изменением:

[...]
// Linux OS
#elif defined(__linux__)
  #define PLATFORM_LINUX 1
  #if defined(BUILDING_WITH_X11) && defined(BUILDING_WITH_WAYLAND)
    #error "You can't build for x11 and Wayland at the same time."
  #elif !defined(BUILDING_WITH_X11) && !defined(BUILDING_WITH_WAYLAND)
    #error "You need to define at least one windowing protocol to use."
  #endif
[...]

Второй работает во время выполнения. Он использует команду secure_getenv() из stdlib.h (с расширением _GNU_SOURCE), которая извлекает значение переменной среды в виде строки. Поэтому я могу использовать это с условиями, чтобы выбрать, какие функции я использую:

if (strcmp(secure_getenv("XDG_SESSION_TYPE"), "wayland") == 0) {
    printf("We're on wayland.\n");
} else if (strcmp(secure_getenv("XDG_SESSION_TYPE"), "x11") == 0) {
    printf("We're on X11.\n");
} else {
    printf("wtf ?\n");
}

(Примечание: прежде чем комментировать это, я предпочитаю явно указывать значение, которое мы хотим получить, если это не просто true false, и здесь, как и во многих std-библиотеках, 0 является эквивалентом true, что раздражает)

Я действительно не знаю, какой из них лучший. Инстинктивно первый кажется самым простым и я видел большие программы (вроде glfw) имеют разные сборки для x11 и wayland (теперь, начиная с glfw3.4, нет glfw-x11 или glfw-wayland, это просто один и тот же пакет) так что мне не нужно стыдиться того, что я не сделал самую большую кроссплатформенную вещь всех времен.

В любом случае, если у людей есть замечания, я буду очень рад изменить свое мнение и, наоборот, отмечу это как свое решение.

Другие вопросы по теме