From f863872591f787f81a9b574aae9a8ecc6b8ba8f5 Mon Sep 17 00:00:00 2001 From: Vasilito Date: Sat, 18 Apr 2026 17:59:04 +0100 Subject: [PATCH] Update libdisplay-info and libudev stubs, fix Qt toolchain cmake Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../libs/libdisplay-info-stub/recipe.toml | 31 +- .../libs/libdisplay-info-stub/source/.gitkeep | 0 .../source/include/libdisplay-info/cta.h | 3 + .../include/libdisplay-info/displayid.h | 3 + .../source/include/libdisplay-info/edid.h | 3 + .../source/include/libdisplay-info/info.h | 108 ++ .../libdisplay-info-stub/source/stub_di.c | 401 +++++ local/recipes/libs/libudev-stub/recipe.toml | 68 +- .../libudev-stub/source/include/libudev.h | 71 + .../libs/libudev-stub/source/libudev.c | 1304 +++++++++++++++++ local/recipes/qt/redox-toolchain.cmake | 6 +- 11 files changed, 1926 insertions(+), 72 deletions(-) delete mode 100644 local/recipes/libs/libdisplay-info-stub/source/.gitkeep create mode 100644 local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/cta.h create mode 100644 local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/displayid.h create mode 100644 local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/edid.h create mode 100644 local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/info.h create mode 100644 local/recipes/libs/libdisplay-info-stub/source/stub_di.c create mode 100644 local/recipes/libs/libudev-stub/source/include/libudev.h create mode 100644 local/recipes/libs/libudev-stub/source/libudev.c diff --git a/local/recipes/libs/libdisplay-info-stub/recipe.toml b/local/recipes/libs/libdisplay-info-stub/recipe.toml index f977fc13..10d70b4b 100644 --- a/local/recipes/libs/libdisplay-info-stub/recipe.toml +++ b/local/recipes/libs/libdisplay-info-stub/recipe.toml @@ -1,4 +1,4 @@ -#TODO: libdisplay-info stub — provides libdisplay-info pkgconfig for KWin linking +#TODO: bounded libdisplay-info shim for the KWin reduced path — parses base EDID vendor/product, strings, physical size, chromaticity, detailed timings, and preferred-timing metadata; CTA/DisplayID remain unsupported [source] path = "source" @@ -8,27 +8,16 @@ script = """ DYNAMIC_INIT mkdir -p "${COOKBOOK_STAGE}/usr/include/libdisplay-info" +mkdir -p "${COOKBOOK_STAGE}/usr/lib" mkdir -p "${COOKBOOK_STAGE}/usr/lib/pkgconfig" -cat > "${COOKBOOK_STAGE}/usr/include/libdisplay-info/info.h" << 'EOF' -#pragma once -#include -struct di_info; -struct di_info *di_info_create(const void *data, size_t size) __attribute__((weak)); -void di_info_destroy(struct di_info *info) __attribute__((weak)); -const char *di_info_get_display_name(const struct di_info *info) __attribute__((weak)); -EOF +cp -R "${COOKBOOK_SOURCE}/include/libdisplay-info/." "${COOKBOOK_STAGE}/usr/include/libdisplay-info/" -cat > stub_di.c << 'CEOF' -#include - -struct di_info; -struct di_info *di_info_create(const void *d, size_t s) { (void)d; (void)s; return 0; } -void di_info_destroy(struct di_info *i) { (void)i; } -const char *di_info_get_display_name(const struct di_info *i) { (void)i; return 0; } -CEOF - -x86_64-unknown-redox-gcc -shared -fPIC -o "${COOKBOOK_STAGE}/usr/lib/libdisplay-info.so" stub_di.c +x86_64-unknown-redox-gcc -shared -fPIC \ + -Wl,-soname,libdisplay-info.so \ + -I"${COOKBOOK_SOURCE}/include" \ + -o "${COOKBOOK_STAGE}/usr/lib/libdisplay-info.so" \ + "${COOKBOOK_SOURCE}/stub_di.c" cat > "${COOKBOOK_STAGE}/usr/lib/pkgconfig/libdisplay-info.pc" << 'EOF' prefix=/usr @@ -37,9 +26,9 @@ libdir=${exec_prefix}/lib includedir=${prefix}/include Name: libdisplay-info -Description: libdisplay-info stub for Redox +Description: libdisplay-info compatibility shim for Redox Version: 0.2.0 Libs: -L${libdir} -ldisplay-info -Cflags: -I${includedir}/libdisplay-info +Cflags: -I${includedir} EOF """ diff --git a/local/recipes/libs/libdisplay-info-stub/source/.gitkeep b/local/recipes/libs/libdisplay-info-stub/source/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/cta.h b/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/cta.h new file mode 100644 index 00000000..d62bf07b --- /dev/null +++ b/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/cta.h @@ -0,0 +1,3 @@ +#pragma once + +#include "info.h" diff --git a/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/displayid.h b/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/displayid.h new file mode 100644 index 00000000..d62bf07b --- /dev/null +++ b/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/displayid.h @@ -0,0 +1,3 @@ +#pragma once + +#include "info.h" diff --git a/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/edid.h b/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/edid.h new file mode 100644 index 00000000..d62bf07b --- /dev/null +++ b/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/edid.h @@ -0,0 +1,3 @@ +#pragma once + +#include "info.h" diff --git a/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/info.h b/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/info.h new file mode 100644 index 00000000..523fa73d --- /dev/null +++ b/local/recipes/libs/libdisplay-info-stub/source/include/libdisplay-info/info.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct di_info; +struct di_edid; +struct di_edid_ext; +struct di_edid_cta; +struct di_displayid; +struct di_cta_data_block; +struct di_displayid_data_block; + +struct di_edid_vendor_product { + char manufacturer[4]; + int product; + int serial; + int manufacture_week; + int manufacture_year; + int model_year; +}; + +struct di_edid_chromaticity_coords { + float red_x; + float red_y; + float green_x; + float green_y; + float blue_x; + float blue_y; + float white_x; + float white_y; +}; + +struct di_cta_hdr_static_metadata_eotfs { + bool pq; +}; + +struct di_cta_hdr_static_metadata_block { + int desired_content_min_luminance; + int desired_content_max_luminance; + int desired_content_max_frame_avg_luminance; + const struct di_cta_hdr_static_metadata_eotfs *eotfs; +}; + +struct di_cta_colorimetry_block { + bool bt2020_rgb; +}; + +struct di_displayid_display_params { + int horiz_pixels; + int vert_pixels; +}; + +struct di_displayid_type_i_ii_vii_timing { + bool preferred; + int horiz_active; + int vert_active; +}; + +struct di_edid_misc_features { + bool preferred_timing_is_native; +}; + +struct di_edid_detailed_timing_def { + int horiz_image_mm; + int vert_image_mm; + int horiz_video; + int vert_video; +}; + +struct di_edid_screen_size { + int width_cm; + int height_cm; +}; + +const struct di_info *di_info_parse_edid(const void *data, size_t size); +void di_info_destroy(const struct di_info *info); +const struct di_edid *di_info_get_edid(const struct di_info *info); +char *di_info_get_model(const struct di_info *info); +char *di_info_get_serial(const struct di_info *info); + +const struct di_edid_detailed_timing_def *const *di_edid_get_detailed_timing_defs(const struct di_edid *edid); +const struct di_edid_screen_size *di_edid_get_screen_size(const struct di_edid *edid); +const struct di_edid_vendor_product *di_edid_get_vendor_product(const struct di_edid *edid); +const struct di_edid_chromaticity_coords *di_edid_get_chromaticity_coords(const struct di_edid *edid); +const struct di_edid_ext *const *di_edid_get_extensions(const struct di_edid *edid); +const struct di_edid_misc_features *di_edid_get_misc_features(const struct di_edid *edid); + +const struct di_edid_cta *di_edid_ext_get_cta(const struct di_edid_ext *ext); +const struct di_displayid *di_edid_ext_get_displayid(const struct di_edid_ext *ext); + +const struct di_cta_data_block *const *di_edid_cta_get_data_blocks(const struct di_edid_cta *cta); +const struct di_cta_hdr_static_metadata_block *di_cta_data_block_get_hdr_static_metadata(const struct di_cta_data_block *block); +const struct di_cta_colorimetry_block *di_cta_data_block_get_colorimetry(const struct di_cta_data_block *block); + +const struct di_displayid_data_block *const *di_displayid_get_data_blocks(const struct di_displayid *displayid); +const struct di_displayid_display_params *di_displayid_data_block_get_display_params(const struct di_displayid_data_block *block); +const struct di_displayid_type_i_ii_vii_timing *const *di_displayid_data_block_get_type_i_timings(const struct di_displayid_data_block *block); +const struct di_displayid_type_i_ii_vii_timing *const *di_displayid_data_block_get_type_ii_timings(const struct di_displayid_data_block *block); + +#ifdef __cplusplus +} +#endif diff --git a/local/recipes/libs/libdisplay-info-stub/source/stub_di.c b/local/recipes/libs/libdisplay-info-stub/source/stub_di.c new file mode 100644 index 00000000..d8f04573 --- /dev/null +++ b/local/recipes/libs/libdisplay-info-stub/source/stub_di.c @@ -0,0 +1,401 @@ +#include +#include +#include +#include + +#include "include/libdisplay-info/info.h" + +#define EDID_BLOCK_SIZE 128 +#define EDID_DESCRIPTOR_COUNT 4 +#define EDID_DESCRIPTOR_OFFSET 54 + +struct di_edid { + struct di_edid_vendor_product vendor_product; + struct di_edid_chromaticity_coords chromaticity; + struct di_edid_screen_size screen_size; + struct di_edid_misc_features misc_features; + bool has_chromaticity; + struct di_edid_detailed_timing_def *detailed_timing_storage; + const struct di_edid_detailed_timing_def **detailed_timings; + const struct di_edid_ext **extensions; +}; + +struct di_info { + struct di_edid edid; + char *model; + char *serial; +}; + +static const uint8_t edid_header[8] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; +static const struct di_edid_detailed_timing_def *empty_detailed_timings[] = { NULL }; +static const struct di_edid_ext *empty_edid_exts[] = { NULL }; +static const struct di_cta_data_block *empty_cta_blocks[] = { NULL }; +static const struct di_displayid_data_block *empty_displayid_blocks[] = { NULL }; +static const struct di_displayid_type_i_ii_vii_timing *empty_timings[] = { NULL }; +static const struct di_edid_screen_size empty_screen_size = { 0, 0 }; + +static char *dup_string(const char *value) +{ + size_t len; + char *copy; + + if (!value) { + value = ""; + } + + len = strlen(value) + 1; + copy = malloc(len); + if (!copy) { + return NULL; + } + + memcpy(copy, value, len); + return copy; +} + +static bool has_nonzero_bytes(const uint8_t *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (data[i] != 0) { + return true; + } + } + + return false; +} + +static bool validate_edid_block(const uint8_t *block) +{ + uint8_t checksum = 0; + size_t i; + + for (i = 0; i < EDID_BLOCK_SIZE; i++) { + checksum = (uint8_t)(checksum + block[i]); + } + + return checksum == 0; +} + +static uint16_t read_le_u16(const uint8_t *data) +{ + return (uint16_t)data[0] | ((uint16_t)data[1] << 8); +} + +static uint32_t read_le_u32(const uint8_t *data) +{ + return (uint32_t)data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); +} + +static float decode_chromaticity_value(uint8_t high_bits, uint8_t low_bits) +{ + return (float)(((unsigned int)high_bits << 2) | low_bits) / 1024.0f; +} + +static void decode_manufacturer_id(const uint8_t *base, char manufacturer[4]) +{ + manufacturer[0] = (char)('A' + ((base[8] >> 2) & 0x1f) - 1); + manufacturer[1] = (char)('A' + (((base[8] & 0x03) << 3) | ((base[9] >> 5) & 0x07)) - 1); + manufacturer[2] = (char)('A' + (base[9] & 0x1f) - 1); + manufacturer[3] = '\0'; + + if (!isupper((unsigned char)manufacturer[0]) || !isupper((unsigned char)manufacturer[1]) || !isupper((unsigned char)manufacturer[2])) { + memcpy(manufacturer, "???", 4); + } +} + +static char *parse_descriptor_string(const uint8_t *descriptor) +{ + char buffer[14]; + size_t out_len = 0; + size_t i; + + for (i = 5; i < 18; i++) { + const uint8_t value = descriptor[i]; + + if (value == 0x00 || value == 0x0a || value == 0x0d) { + break; + } + if (value < 0x20 || value > 0x7e) { + continue; + } + + buffer[out_len++] = (char)value; + } + + while (out_len > 0 && isspace((unsigned char)buffer[out_len - 1])) { + out_len--; + } + buffer[out_len] = '\0'; + + return dup_string(buffer); +} + +static bool parse_detailed_timing(const uint8_t *descriptor, struct di_edid_detailed_timing_def *timing) +{ + const uint16_t pixel_clock = read_le_u16(descriptor); + + if (pixel_clock == 0) { + return false; + } + + timing->horiz_video = (int)(descriptor[2] | ((descriptor[4] & 0xf0) << 4)); + timing->vert_video = (int)(descriptor[5] | ((descriptor[7] & 0xf0) << 4)); + timing->horiz_image_mm = (int)(descriptor[12] | ((descriptor[14] & 0xf0) << 4)); + timing->vert_image_mm = (int)(descriptor[13] | ((descriptor[14] & 0x0f) << 8)); + + return timing->horiz_video > 0 && timing->vert_video > 0; +} + +static bool populate_detailed_timings(struct di_edid *edid, const uint8_t *base) +{ + struct di_edid_detailed_timing_def parsed[EDID_DESCRIPTOR_COUNT]; + size_t count = 0; + size_t i; + + edid->detailed_timings = empty_detailed_timings; + + for (i = 0; i < EDID_DESCRIPTOR_COUNT; i++) { + const uint8_t *descriptor = base + EDID_DESCRIPTOR_OFFSET + (i * 18); + + if (parse_detailed_timing(descriptor, &parsed[count])) { + count++; + } + } + + if (count == 0) { + return true; + } + + edid->detailed_timing_storage = calloc(count, sizeof(*edid->detailed_timing_storage)); + edid->detailed_timings = calloc(count + 1, sizeof(*edid->detailed_timings)); + if (!edid->detailed_timing_storage || !edid->detailed_timings) { + return false; + } + + for (i = 0; i < count; i++) { + edid->detailed_timing_storage[i] = parsed[i]; + edid->detailed_timings[i] = &edid->detailed_timing_storage[i]; + } + + edid->detailed_timings[count] = NULL; + return true; +} + +static void populate_vendor_product(struct di_edid *edid, const uint8_t *base) +{ + const int manufacture_year = (int)base[17] + 1990; + const bool has_model_year = base[16] == 0xff; + + decode_manufacturer_id(base, edid->vendor_product.manufacturer); + edid->vendor_product.product = (int)read_le_u16(base + 10); + edid->vendor_product.serial = (int)read_le_u32(base + 12); + edid->vendor_product.manufacture_week = has_model_year ? 0 : (int)base[16]; + edid->vendor_product.manufacture_year = has_model_year ? 0 : manufacture_year; + edid->vendor_product.model_year = has_model_year ? manufacture_year : 0; +} + +static void populate_chromaticity(struct di_edid *edid, const uint8_t *base) +{ + if (!has_nonzero_bytes(base + 25, 10)) { + edid->has_chromaticity = false; + return; + } + + edid->chromaticity.red_x = decode_chromaticity_value(base[27], (base[25] >> 6) & 0x03); + edid->chromaticity.red_y = decode_chromaticity_value(base[28], (base[25] >> 4) & 0x03); + edid->chromaticity.green_x = decode_chromaticity_value(base[29], (base[25] >> 2) & 0x03); + edid->chromaticity.green_y = decode_chromaticity_value(base[30], base[25] & 0x03); + edid->chromaticity.blue_x = decode_chromaticity_value(base[31], (base[26] >> 6) & 0x03); + edid->chromaticity.blue_y = decode_chromaticity_value(base[32], (base[26] >> 4) & 0x03); + edid->chromaticity.white_x = decode_chromaticity_value(base[33], (base[26] >> 2) & 0x03); + edid->chromaticity.white_y = decode_chromaticity_value(base[34], base[26] & 0x03); + edid->has_chromaticity = true; +} + +static bool populate_strings(struct di_info *info, const uint8_t *base) +{ + size_t i; + + for (i = 0; i < EDID_DESCRIPTOR_COUNT; i++) { + const uint8_t *descriptor = base + EDID_DESCRIPTOR_OFFSET + (i * 18); + + if (descriptor[0] != 0x00 || descriptor[1] != 0x00 || descriptor[2] != 0x00) { + continue; + } + + if (descriptor[3] == 0xfc && !info->model) { + info->model = parse_descriptor_string(descriptor); + } else if (descriptor[3] == 0xff && !info->serial) { + info->serial = parse_descriptor_string(descriptor); + } + } + + if (!info->model) { + info->model = dup_string(""); + } + if (!info->serial) { + if (info->edid.vendor_product.serial != 0) { + char serial_buffer[32]; + + snprintf(serial_buffer, sizeof(serial_buffer), "%u", (unsigned int)info->edid.vendor_product.serial); + info->serial = dup_string(serial_buffer); + } else { + info->serial = dup_string(""); + } + } + + return info->model && info->serial; +} + +const struct di_info *di_info_parse_edid(const void *data, size_t size) +{ + const uint8_t *base = data; + struct di_info *info; + + if (!base || size < EDID_BLOCK_SIZE) { + return NULL; + } + if (memcmp(base, edid_header, sizeof(edid_header)) != 0) { + return NULL; + } + if (!validate_edid_block(base)) { + return NULL; + } + + info = calloc(1, sizeof(*info)); + if (!info) { + return NULL; + } + + info->edid.screen_size.width_cm = (int)base[21]; + info->edid.screen_size.height_cm = (int)base[22]; + info->edid.misc_features.preferred_timing_is_native = (base[24] & 0x02) != 0; + info->edid.extensions = empty_edid_exts; + + populate_vendor_product(&info->edid, base); + populate_chromaticity(&info->edid, base); + if (!populate_detailed_timings(&info->edid, base) || !populate_strings(info, base)) { + di_info_destroy(info); + return NULL; + } + + return info; +} + +void di_info_destroy(const struct di_info *info) +{ + struct di_info *mutable_info = (struct di_info *)info; + + if (!mutable_info) { + return; + } + + free(mutable_info->model); + free(mutable_info->serial); + if (mutable_info->edid.detailed_timings != empty_detailed_timings) { + free((void *)mutable_info->edid.detailed_timings); + } + free(mutable_info->edid.detailed_timing_storage); + free(mutable_info); +} + +const struct di_edid *di_info_get_edid(const struct di_info *info) +{ + return info ? &info->edid : NULL; +} + +char *di_info_get_model(const struct di_info *info) +{ + return dup_string(info ? info->model : ""); +} + +char *di_info_get_serial(const struct di_info *info) +{ + return dup_string(info ? info->serial : ""); +} + +const struct di_edid_detailed_timing_def *const *di_edid_get_detailed_timing_defs(const struct di_edid *edid) +{ + return edid && edid->detailed_timings ? edid->detailed_timings : empty_detailed_timings; +} + +const struct di_edid_screen_size *di_edid_get_screen_size(const struct di_edid *edid) +{ + return edid ? &edid->screen_size : &empty_screen_size; +} + +const struct di_edid_vendor_product *di_edid_get_vendor_product(const struct di_edid *edid) +{ + return edid ? &edid->vendor_product : NULL; +} + +const struct di_edid_chromaticity_coords *di_edid_get_chromaticity_coords(const struct di_edid *edid) +{ + return edid && edid->has_chromaticity ? &edid->chromaticity : NULL; +} + +const struct di_edid_ext *const *di_edid_get_extensions(const struct di_edid *edid) +{ + return edid && edid->extensions ? edid->extensions : empty_edid_exts; +} + +const struct di_edid_misc_features *di_edid_get_misc_features(const struct di_edid *edid) +{ + return edid ? &edid->misc_features : NULL; +} + +const struct di_edid_cta *di_edid_ext_get_cta(const struct di_edid_ext *ext) +{ + (void)ext; + return NULL; +} + +const struct di_displayid *di_edid_ext_get_displayid(const struct di_edid_ext *ext) +{ + (void)ext; + return NULL; +} + +const struct di_cta_data_block *const *di_edid_cta_get_data_blocks(const struct di_edid_cta *cta) +{ + (void)cta; + return empty_cta_blocks; +} + +const struct di_cta_hdr_static_metadata_block *di_cta_data_block_get_hdr_static_metadata(const struct di_cta_data_block *block) +{ + (void)block; + return NULL; +} + +const struct di_cta_colorimetry_block *di_cta_data_block_get_colorimetry(const struct di_cta_data_block *block) +{ + (void)block; + return NULL; +} + +const struct di_displayid_data_block *const *di_displayid_get_data_blocks(const struct di_displayid *displayid) +{ + (void)displayid; + return empty_displayid_blocks; +} + +const struct di_displayid_display_params *di_displayid_data_block_get_display_params(const struct di_displayid_data_block *block) +{ + (void)block; + return NULL; +} + +const struct di_displayid_type_i_ii_vii_timing *const *di_displayid_data_block_get_type_i_timings(const struct di_displayid_data_block *block) +{ + (void)block; + return empty_timings; +} + +const struct di_displayid_type_i_ii_vii_timing *const *di_displayid_data_block_get_type_ii_timings(const struct di_displayid_data_block *block) +{ + (void)block; + return empty_timings; +} diff --git a/local/recipes/libs/libudev-stub/recipe.toml b/local/recipes/libs/libudev-stub/recipe.toml index d2ce2d27..937685e9 100644 --- a/local/recipes/libs/libudev-stub/recipe.toml +++ b/local/recipes/libs/libudev-stub/recipe.toml @@ -1,4 +1,4 @@ -#TODO: libudev stub — provides UDev::UDev cmake target for KWin linking +#TODO: reduced libudev provider — exposes a real libudev.so / UDev::UDev surface for the current KWin path via scheme:udev and udev-shim; hotplug event delivery remains bounded [source] path = "source" @@ -11,60 +11,28 @@ mkdir -p "${COOKBOOK_STAGE}/usr/include" mkdir -p "${COOKBOOK_STAGE}/usr/lib/cmake/UDev" mkdir -p "${COOKBOOK_STAGE}/usr/lib/pkgconfig" -cat > "${COOKBOOK_STAGE}/usr/include/libudev.h" << 'EOF' -#pragma once -#include -void *udev_new(void) __attribute__((weak)); -void udev_unref(void *udev) __attribute__((weak)); -void *udev_enumerate_new(void *udev) __attribute__((weak)); -void udev_enumerate_unref(void *udev_enumerate) __attribute__((weak)); -int udev_enumerate_add_match_subsystem(void *udev_enumerate, const char *subsystem) __attribute__((weak)); -int udev_enumerate_scan_devices(void *udev_enumerate) __attribute__((weak)); -void *udev_enumerate_get_list_entry(void *udev_enumerate) __attribute__((weak)); -void *udev_list_entry_get_next(void *list_entry) __attribute__((weak)); -const char *udev_list_entry_get_name(void *list_entry) __attribute__((weak)); -void *udev_device_new_from_syspath(void *udev, const char *syspath) __attribute__((weak)); -void udev_device_unref(void *udev_device) __attribute__((weak)); -const char *udev_device_get_devnode(void *udev_device) __attribute__((weak)); -const char *udev_device_get_action(void *udev_device) __attribute__((weak)); -void *udev_monitor_new_from_netlink(void *udev, const char *name) __attribute__((weak)); -void udev_monitor_unref(void *udev_monitor) __attribute__((weak)); -int udev_monitor_enable_receiving(void *udev_monitor) __attribute__((weak)); -int udev_monitor_get_fd(void *udev_monitor) __attribute__((weak)); -void *udev_monitor_receive_device(void *udev_monitor) __attribute__((weak)); -int udev_monitor_filter_add_match_subsystem_devtype(void *udev_monitor, const char *subsystem, const char *devtype) __attribute__((weak)); -EOF +cp "${COOKBOOK_SOURCE}/include/libudev.h" "${COOKBOOK_STAGE}/usr/include/libudev.h" -cat > stub_udev.c << 'CEOF' -void *udev_new(void) { return 0; } -void udev_unref(void *u) { (void)u; } -void *udev_enumerate_new(void *u) { (void)u; return 0; } -void udev_enumerate_unref(void *e) { (void)e; } -int udev_enumerate_add_match_subsystem(void *e, const char *s) { (void)e; (void)s; return -1; } -int udev_enumerate_scan_devices(void *e) { (void)e; return 0; } -void *udev_enumerate_get_list_entry(void *e) { (void)e; return 0; } -void *udev_list_entry_get_next(void *l) { (void)l; return 0; } -const char *udev_list_entry_get_name(void *l) { (void)l; return 0; } -void *udev_device_new_from_syspath(void *u, const char *s) { (void)u; (void)s; return 0; } -void udev_device_unref(void *d) { (void)d; } -const char *udev_device_get_devnode(void *d) { (void)d; return 0; } -const char *udev_device_get_action(void *d) { (void)d; return 0; } -void *udev_monitor_new_from_netlink(void *u, const char *n) { (void)u; (void)n; return 0; } -void udev_monitor_unref(void *m) { (void)m; } -int udev_monitor_enable_receiving(void *m) { (void)m; return -1; } -int udev_monitor_get_fd(void *m) { (void)m; return -1; } -void *udev_monitor_receive_device(void *m) { (void)m; return 0; } -int udev_monitor_filter_add_match_subsystem_devtype(void *m, const char *s, const char *d) { (void)m; (void)s; (void)d; return -1; } -CEOF - -x86_64-unknown-redox-gcc -shared -fPIC -o "${COOKBOOK_STAGE}/usr/lib/libudev.so" stub_udev.c +x86_64-unknown-redox-gcc \ + -shared \ + -fPIC \ + -std=c11 \ + -Wall \ + -Wextra \ + -Wl,-soname,libudev.so \ + -I"${COOKBOOK_SOURCE}/include" \ + -o "${COOKBOOK_STAGE}/usr/lib/libudev.so" \ + "${COOKBOOK_SOURCE}/libudev.c" cat > "${COOKBOOK_STAGE}/usr/lib/cmake/UDev/UDevConfig.cmake" << 'EOF' +set(UDev_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/../../../include") +set(UDev_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/../../../lib/libudev.so") +set(UDev_VERSION "1.0.0") if(NOT TARGET UDev::UDev) add_library(UDev::UDev SHARED IMPORTED) set_target_properties(UDev::UDev PROPERTIES - IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/../../../lib/libudev.so" - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/../../../include" + IMPORTED_LOCATION "${UDev_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${UDev_INCLUDE_DIRS}" ) endif() set(UDev_FOUND TRUE) @@ -77,7 +45,7 @@ libdir=${exec_prefix}/lib includedir=${prefix}/include Name: libudev -Description: udev stub for Redox +Description: scheme-backed libudev provider for the reduced Red Bear path Version: 1.0.0 Libs: -L${libdir} -ludev Cflags: -I${includedir} diff --git a/local/recipes/libs/libudev-stub/source/include/libudev.h b/local/recipes/libs/libudev-stub/source/include/libudev.h new file mode 100644 index 00000000..8d65e35a --- /dev/null +++ b/local/recipes/libs/libudev-stub/source/include/libudev.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct udev; +struct udev_device; +struct udev_enumerate; +struct udev_list_entry; +struct udev_monitor; + +#define udev_list_entry_foreach(list_entry, first_entry) \ + for ((list_entry) = (first_entry); (list_entry) != NULL; (list_entry) = udev_list_entry_get_next(list_entry)) + +struct udev *udev_new(void); +struct udev *udev_ref(struct udev *udev); +struct udev *udev_unref(struct udev *udev); + +struct udev_enumerate *udev_enumerate_new(struct udev *udev); +struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate); +struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate); +struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); +int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); +int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname); +int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value); +int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); +int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); +struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate); + +struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry); +const char *udev_list_entry_get_name(struct udev_list_entry *list_entry); +const char *udev_list_entry_get_value(struct udev_list_entry *list_entry); + +struct udev_device *udev_device_ref(struct udev_device *udev_device); +struct udev_device *udev_device_unref(struct udev_device *udev_device); +struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath); +struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum); +struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname); +const char *udev_device_get_devnode(struct udev_device *udev_device); +dev_t udev_device_get_devnum(struct udev_device *udev_device); +const char *udev_device_get_action(struct udev_device *udev_device); +const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key); +struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device); +struct udev_device *udev_device_get_parent(struct udev_device *udev_device); +struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype); +const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr); +const char *udev_device_get_devpath(struct udev_device *udev_device); +const char *udev_device_get_syspath(struct udev_device *udev_device); +const char *udev_device_get_subsystem(struct udev_device *udev_device); +const char *udev_device_get_devtype(struct udev_device *udev_device); +const char *udev_device_get_sysname(struct udev_device *udev_device); +const char *udev_device_get_sysnum(struct udev_device *udev_device); +const char *udev_device_get_driver(struct udev_device *udev_device); +int udev_device_has_tag(struct udev_device *udev_device, const char *tag); + +struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name); +struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor); +struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor); +int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype); +int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); +int udev_monitor_get_fd(struct udev_monitor *udev_monitor); +struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor); + +#ifdef __cplusplus +} +#endif diff --git a/local/recipes/libs/libudev-stub/source/libudev.c b/local/recipes/libs/libudev-stub/source/libudev.c new file mode 100644 index 00000000..416c44bc --- /dev/null +++ b/local/recipes/libs/libudev-stub/source/libudev.c @@ -0,0 +1,1304 @@ +#define _POSIX_C_SOURCE 200809L + +#include "libudev.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct udev_list_entry { + char *name; + char *value; + struct udev_list_entry *next; +}; + +struct udev { + int refcount; +}; + +struct udev_match_property { + char *key; + char *value; + struct udev_match_property *next; +}; + +struct udev_device { + int refcount; + struct udev *udev; + char *syspath; + char *devpath; + char *devnode; + char *subsystem; + char *devtype; + char *sysname; + char *sysnum; + char *driver; + char *action; + dev_t devnum; + struct udev_list_entry *properties; + struct udev_list_entry *devlinks; + struct udev_list_entry *sysattrs; + struct udev_device *parent; +}; + +struct udev_enumerate { + int refcount; + struct udev *udev; + char *match_subsystem; + char *match_sysname; + struct udev_match_property *match_properties; + struct udev_list_entry *list; +}; + +struct udev_monitor_filter { + char *subsystem; + char *devtype; + struct udev_monitor_filter *next; +}; + +struct udev_monitor { + int refcount; + struct udev *udev; + int read_fd; + int write_fd; + bool enabled; + struct udev_monitor_filter *filters; + struct udev_monitor_event *pending_head; + struct udev_monitor_event *pending_tail; +}; + +struct udev_monitor_event { + struct udev_device *device; + struct udev_monitor_event *next; +}; + +static char *xstrdup(const char *value) +{ + if (!value) { + return NULL; + } + + size_t len = strlen(value) + 1; + char *copy = malloc(len); + if (!copy) { + return NULL; + } + + memcpy(copy, value, len); + return copy; +} + +static void free_list_entries(struct udev_list_entry *entry) +{ + while (entry) { + struct udev_list_entry *next = entry->next; + free(entry->name); + free(entry->value); + free(entry); + entry = next; + } +} + +static void free_match_properties(struct udev_match_property *entry) +{ + while (entry) { + struct udev_match_property *next = entry->next; + free(entry->key); + free(entry->value); + free(entry); + entry = next; + } +} + +static void free_monitor_filters(struct udev_monitor_filter *entry) +{ + while (entry) { + struct udev_monitor_filter *next = entry->next; + free(entry->subsystem); + free(entry->devtype); + free(entry); + entry = next; + } +} + +static void free_monitor_events(struct udev_monitor_event *entry) +{ + while (entry) { + struct udev_monitor_event *next = entry->next; + udev_device_unref(entry->device); + free(entry); + entry = next; + } +} + +static int list_entry_append(struct udev_list_entry **head, const char *name, const char *value) +{ + struct udev_list_entry *entry = calloc(1, sizeof(*entry)); + if (!entry) { + return -1; + } + + entry->name = xstrdup(name); + entry->value = xstrdup(value); + if ((name && !entry->name) || (value && !entry->value)) { + free(entry->name); + free(entry->value); + free(entry); + return -1; + } + + if (!*head) { + *head = entry; + return 0; + } + + struct udev_list_entry *tail = *head; + while (tail->next) { + tail = tail->next; + } + tail->next = entry; + return 0; +} + +static const char *list_entry_find_value(const struct udev_list_entry *entry, const char *name) +{ + while (entry) { + if (entry->name && name && strcmp(entry->name, name) == 0) { + return entry->value; + } + entry = entry->next; + } + return NULL; +} + +static bool list_entry_has_name(const struct udev_list_entry *entry, const char *name) +{ + while (entry) { + if (entry->name && name && strcmp(entry->name, name) == 0) { + return true; + } + entry = entry->next; + } + return false; +} + +static bool string_matches(const char *left, const char *right) +{ + return left && right && strcmp(left, right) == 0; +} + +static char *read_text_file(const char *path) +{ + FILE *file = fopen(path, "r"); + if (!file) { + return NULL; + } + + size_t cap = 1024; + size_t len = 0; + char *buffer = malloc(cap); + if (!buffer) { + fclose(file); + return NULL; + } + + for (;;) { + if (len + 512 >= cap) { + size_t next_cap = cap * 2; + char *next = realloc(buffer, next_cap); + if (!next) { + free(buffer); + fclose(file); + return NULL; + } + buffer = next; + cap = next_cap; + } + + size_t read_bytes = fread(buffer + len, 1, cap - len - 1, file); + len += read_bytes; + if (read_bytes == 0) { + if (ferror(file)) { + free(buffer); + fclose(file); + return NULL; + } + break; + } + } + + buffer[len] = '\0'; + fclose(file); + return buffer; +} + +static char *dup_basename(const char *path) +{ + if (!path) { + return NULL; + } + + const char *base = strrchr(path, '/'); + base = base ? base + 1 : path; + return xstrdup(base); +} + +static char *dup_sysnum(const char *sysname) +{ + if (!sysname) { + return NULL; + } + + const char *end = sysname + strlen(sysname); + const char *start = end; + while (start > sysname && start[-1] >= '0' && start[-1] <= '9') { + start--; + } + + if (start == end) { + return NULL; + } + + size_t len = (size_t)(end - start); + char *copy = malloc(len + 1); + if (!copy) { + return NULL; + } + + memcpy(copy, start, len); + copy[len] = '\0'; + return copy; +} + +static int replace_string(char **slot, const char *value) +{ + char *copy = xstrdup(value); + if (value && !copy) { + return -1; + } + + free(*slot); + *slot = copy; + return 0; +} + +static dev_t devnum_from_node(const char *devnode) +{ + if (!devnode) { + return 0; + } + + struct stat st; + if (stat(devnode, &st) != 0) { + return 0; + } + + return st.st_rdev; +} + +static void udev_device_destroy(struct udev_device *device) +{ + if (!device) { + return; + } + + if (device->parent) { + udev_device_destroy(device->parent); + } + + udev_unref(device->udev); + free(device->syspath); + free(device->devpath); + free(device->devnode); + free(device->subsystem); + free(device->devtype); + free(device->sysname); + free(device->sysnum); + free(device->driver); + free(device->action); + free_list_entries(device->properties); + free_list_entries(device->devlinks); + free_list_entries(device->sysattrs); + free(device); +} + +static struct udev_device *udev_device_alloc(struct udev *udev) +{ + struct udev_device *device = calloc(1, sizeof(*device)); + if (!device) { + return NULL; + } + + device->refcount = 1; + device->udev = udev_ref(udev); + if (!device->udev) { + free(device); + return NULL; + } + + return device; +} + +static bool is_primary_drm_device(const struct udev_device *device) +{ + return device && device->subsystem && strcmp(device->subsystem, "drm") == 0 && device->sysname && strcmp(device->sysname, "card0") == 0; +} + +static struct udev_device *make_pci_parent(struct udev_device *child) +{ + if (!child || !child->syspath || strncmp(child->syspath, "/devices/pci/", 13) != 0) { + return NULL; + } + if (child->subsystem && strcmp(child->subsystem, "pci") == 0) { + return NULL; + } + + struct udev_device *parent = udev_device_alloc(child->udev); + if (!parent) { + return NULL; + } + + if (replace_string(&parent->syspath, child->syspath) != 0 || replace_string(&parent->devpath, child->devpath ? child->devpath : child->syspath) != 0 || replace_string(&parent->subsystem, "pci") != 0 || !(parent->sysname = dup_basename(child->syspath))) { + udev_device_destroy(parent); + return NULL; + } + + parent->sysnum = dup_sysnum(parent->sysname); + if (list_entry_append(&parent->properties, "DEVPATH", parent->devpath) != 0 || list_entry_append(&parent->properties, "SUBSYSTEM", "pci") != 0) { + udev_device_destroy(parent); + return NULL; + } + + const char *vendor_id = list_entry_find_value(child->properties, "PCI_VENDOR_ID"); + const char *device_id = list_entry_find_value(child->properties, "PCI_DEVICE_ID"); + const char *pci_class = list_entry_find_value(child->properties, "PCI_CLASS"); + + if ((vendor_id && list_entry_append(&parent->properties, "PCI_VENDOR_ID", vendor_id) != 0) || (device_id && list_entry_append(&parent->properties, "PCI_DEVICE_ID", device_id) != 0) || (pci_class && list_entry_append(&parent->properties, "PCI_CLASS", pci_class) != 0) || list_entry_append(&parent->sysattrs, "boot_vga", is_primary_drm_device(child) ? "1" : "0") != 0) { + udev_device_destroy(parent); + return NULL; + } + + return parent; +} + +static struct udev_device *parse_device_record(struct udev *udev, const char *content, const char *action) +{ + char *buffer = xstrdup(content); + if (!buffer) { + return NULL; + } + + struct udev_device *device = udev_device_alloc(udev); + if (!device) { + free(buffer); + return NULL; + } + + char *save = NULL; + for (char *line = strtok_r(buffer, "\n", &save); line; line = strtok_r(NULL, "\n", &save)) { + if (strncmp(line, "P=", 2) == 0) { + if (replace_string(&device->syspath, line + 2) != 0 || replace_string(&device->devpath, line + 2) != 0) { + udev_device_destroy(device); + free(buffer); + return NULL; + } + } else if (strncmp(line, "E=", 2) == 0) { + char *payload = line + 2; + char *separator = strchr(payload, '='); + if (!separator) { + continue; + } + + *separator = '\0'; + const char *key = payload; + const char *value = separator + 1; + if (list_entry_append(&device->properties, key, value) != 0) { + udev_device_destroy(device); + free(buffer); + return NULL; + } + + if (strcmp(key, "DEVPATH") == 0) { + if (replace_string(&device->devpath, value) != 0) { + udev_device_destroy(device); + free(buffer); + return NULL; + } + } else if (strcmp(key, "SUBSYSTEM") == 0) { + if (replace_string(&device->subsystem, value) != 0) { + udev_device_destroy(device); + free(buffer); + return NULL; + } + } else if (strcmp(key, "DEVNAME") == 0) { + if (replace_string(&device->devnode, value) != 0) { + udev_device_destroy(device); + free(buffer); + return NULL; + } + } + } else if (strncmp(line, "S=", 2) == 0) { + const char *value = line + 2; + if (*value == '\0') { + continue; + } + + char *devlink = NULL; + if (value[0] == '/') { + devlink = xstrdup(value); + } else { + size_t len = strlen(value) + 2; + devlink = malloc(len); + if (devlink) { + snprintf(devlink, len, "/%s", value); + } + } + + if (!devlink || list_entry_append(&device->devlinks, devlink, NULL) != 0) { + free(devlink); + udev_device_destroy(device); + free(buffer); + return NULL; + } + + free(devlink); + } + } + + if (!device->syspath && device->devpath && replace_string(&device->syspath, device->devpath) != 0) { + udev_device_destroy(device); + free(buffer); + return NULL; + } + + if (!device->sysname) { + device->sysname = dup_basename(device->devnode ? device->devnode : device->syspath); + } + if (device->sysname && !device->sysnum) { + device->sysnum = dup_sysnum(device->sysname); + } + if (device->devnode) { + device->devnum = devnum_from_node(device->devnode); + } + if (action && replace_string(&device->action, action) != 0) { + udev_device_destroy(device); + free(buffer); + return NULL; + } + if (!device->subsystem && replace_string(&device->subsystem, "unknown") != 0) { + udev_device_destroy(device); + free(buffer); + return NULL; + } + + device->parent = make_pci_parent(device); + free(buffer); + return device; +} + +typedef int (*device_callback_fn)(struct udev_device *device, void *ctx); + +static int scan_scheme_devices(struct udev *udev, device_callback_fn callback, void *ctx) +{ + char *listing = read_text_file("/scheme/udev/devices"); + if (!listing) { + return 0; + } + + int result = 0; + char *save = NULL; + for (char *line = strtok_r(listing, "\n", &save); line; line = strtok_r(NULL, "\n", &save)) { + if (*line == '\0') { + continue; + } + + char path[256]; + snprintf(path, sizeof(path), "/scheme/udev/devices/%s", line); + + char *content = read_text_file(path); + if (!content) { + continue; + } + + struct udev_device *device = parse_device_record(udev, content, NULL); + free(content); + if (!device) { + continue; + } + + result = callback(device, ctx); + udev_device_unref(device); + if (result != 0) { + break; + } + } + + free(listing); + return result; +} + +struct find_by_syspath_ctx { + struct udev_device *result; + const char *target; +}; + +static int find_by_syspath_cb(struct udev_device *device, void *ctx) +{ + struct find_by_syspath_ctx *state = ctx; + if (string_matches(device->syspath, state->target) || string_matches(device->devnode, state->target) || list_entry_has_name(device->devlinks, state->target)) { + state->result = udev_device_ref(device); + return 1; + } + return 0; +} + +struct find_by_devnum_ctx { + struct udev_device *result; + dev_t target; +}; + +static int find_by_devnum_cb(struct udev_device *device, void *ctx) +{ + struct find_by_devnum_ctx *state = ctx; + if (device->devnum != 0 && device->devnum == state->target) { + state->result = udev_device_ref(device); + return 1; + } + return 0; +} + +struct find_by_subsystem_sysname_ctx { + struct udev_device *result; + const char *subsystem; + const char *sysname; +}; + +static int find_by_subsystem_sysname_cb(struct udev_device *device, void *ctx) +{ + struct find_by_subsystem_sysname_ctx *state = ctx; + if (string_matches(device->subsystem, state->subsystem) && string_matches(device->sysname, state->sysname)) { + state->result = udev_device_ref(device); + return 1; + } + return 0; +} + +static bool device_matches_enumerate(const struct udev_enumerate *enumerate, const struct udev_device *device) +{ + if (enumerate->match_subsystem && !string_matches(enumerate->match_subsystem, device->subsystem)) { + return false; + } + + if (enumerate->match_sysname && (!device->sysname || fnmatch(enumerate->match_sysname, device->sysname, 0) != 0)) { + return false; + } + + for (const struct udev_match_property *entry = enumerate->match_properties; entry; entry = entry->next) { + const char *value = list_entry_find_value(device->properties, entry->key); + if (!value) { + return false; + } + if (entry->value && strcmp(value, entry->value) != 0) { + return false; + } + } + + return true; +} + +static bool device_matches_monitor_filters(const struct udev_monitor *monitor, const struct udev_device *device) +{ + const struct udev_monitor_filter *filter; + + if (!monitor->filters) { + return true; + } + + for (filter = monitor->filters; filter; filter = filter->next) { + if (!string_matches(filter->subsystem, device->subsystem)) { + continue; + } + if (filter->devtype && !string_matches(filter->devtype, device->devtype)) { + continue; + } + return true; + } + + return false; +} + +static int monitor_queue_device(struct udev_monitor *monitor, struct udev_device *device, const char *action) +{ + struct udev_monitor_event *event; + struct udev_device *copy; + + if (!monitor || !device || !device_matches_monitor_filters(monitor, device)) { + return 0; + } + + copy = udev_device_ref(device); + if (!copy) { + errno = ENOMEM; + return -1; + } + + free(copy->action); + copy->action = xstrdup(action ? action : "change"); + if (!copy->action) { + udev_device_unref(copy); + errno = ENOMEM; + return -1; + } + + event = calloc(1, sizeof(*event)); + if (!event) { + udev_device_unref(copy); + errno = ENOMEM; + return -1; + } + + event->device = copy; + if (monitor->pending_tail) { + monitor->pending_tail->next = event; + } else { + monitor->pending_head = event; + } + monitor->pending_tail = event; + return 0; +} + +static int monitor_emit_pending(struct udev_monitor *monitor) +{ + struct udev_monitor_event *event; + + if (!monitor || monitor->write_fd < 0) { + return 0; + } + + for (event = monitor->pending_head; event; event = event->next) { + if (write(monitor->write_fd, "u", 1) < 0) { + return -1; + } + } + + return 0; +} + +static int monitor_seed_existing_devices_cb(struct udev_device *device, void *ctx) +{ + return monitor_queue_device(ctx, device, "change"); +} + +static void clear_enumerate_list(struct udev_enumerate *enumerate) +{ + free_list_entries(enumerate->list); + enumerate->list = NULL; +} + +struct enumerate_devices_ctx { + struct udev_enumerate *enumerate; + int failed; +}; + +static int enumerate_devices_cb(struct udev_device *device, void *ctx) +{ + struct enumerate_devices_ctx *state = ctx; + if (!device_matches_enumerate(state->enumerate, device)) { + return 0; + } + + if (list_entry_append(&state->enumerate->list, device->syspath, NULL) != 0) { + state->failed = 1; + return 1; + } + + return 0; +} + +struct enumerate_subsystems_ctx { + struct udev_enumerate *enumerate; + int failed; +}; + +static int enumerate_subsystems_cb(struct udev_device *device, void *ctx) +{ + struct enumerate_subsystems_ctx *state = ctx; + if (!device->subsystem || list_entry_has_name(state->enumerate->list, device->subsystem)) { + return 0; + } + + if (list_entry_append(&state->enumerate->list, device->subsystem, NULL) != 0) { + state->failed = 1; + return 1; + } + + return 0; +} + +struct udev *udev_new(void) +{ + struct udev *udev = calloc(1, sizeof(*udev)); + if (!udev) { + return NULL; + } + + udev->refcount = 1; + return udev; +} + +struct udev *udev_ref(struct udev *udev) +{ + if (udev) { + udev->refcount++; + } + return udev; +} + +struct udev *udev_unref(struct udev *udev) +{ + if (!udev) { + return NULL; + } + + udev->refcount--; + if (udev->refcount <= 0) { + free(udev); + return NULL; + } + + return udev; +} + +struct udev_enumerate *udev_enumerate_new(struct udev *udev) +{ + if (!udev) { + errno = EINVAL; + return NULL; + } + + struct udev_enumerate *enumerate = calloc(1, sizeof(*enumerate)); + if (!enumerate) { + return NULL; + } + + enumerate->refcount = 1; + enumerate->udev = udev_ref(udev); + return enumerate; +} + +struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) +{ + if (udev_enumerate) { + udev_enumerate->refcount++; + } + return udev_enumerate; +} + +struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) +{ + if (!udev_enumerate) { + return NULL; + } + + udev_enumerate->refcount--; + if (udev_enumerate->refcount <= 0) { + udev_unref(udev_enumerate->udev); + free(udev_enumerate->match_subsystem); + free(udev_enumerate->match_sysname); + free_match_properties(udev_enumerate->match_properties); + clear_enumerate_list(udev_enumerate); + free(udev_enumerate); + return NULL; + } + + return udev_enumerate; +} + +struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) +{ + return udev_enumerate ? udev_enumerate->udev : NULL; +} + +int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) +{ + if (!udev_enumerate || !subsystem) { + errno = EINVAL; + return -1; + } + + return replace_string(&udev_enumerate->match_subsystem, subsystem); +} + +int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) +{ + if (!udev_enumerate || !sysname) { + errno = EINVAL; + return -1; + } + + return replace_string(&udev_enumerate->match_sysname, sysname); +} + +int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) +{ + if (!udev_enumerate || !property) { + errno = EINVAL; + return -1; + } + + struct udev_match_property *entry = calloc(1, sizeof(*entry)); + if (!entry) { + return -1; + } + + entry->key = xstrdup(property); + entry->value = xstrdup(value); + if (!entry->key || (value && !entry->value)) { + free(entry->key); + free(entry->value); + free(entry); + return -1; + } + + if (!udev_enumerate->match_properties) { + udev_enumerate->match_properties = entry; + return 0; + } + + struct udev_match_property *tail = udev_enumerate->match_properties; + while (tail->next) { + tail = tail->next; + } + tail->next = entry; + return 0; +} + +int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) +{ + if (!udev_enumerate) { + errno = EINVAL; + return -1; + } + + clear_enumerate_list(udev_enumerate); + struct enumerate_devices_ctx ctx = { + .enumerate = udev_enumerate, + .failed = 0, + }; + scan_scheme_devices(udev_enumerate->udev, enumerate_devices_cb, &ctx); + if (ctx.failed) { + errno = ENOMEM; + return -1; + } + return 0; +} + +int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) +{ + if (!udev_enumerate) { + errno = EINVAL; + return -1; + } + + clear_enumerate_list(udev_enumerate); + struct enumerate_subsystems_ctx ctx = { + .enumerate = udev_enumerate, + .failed = 0, + }; + scan_scheme_devices(udev_enumerate->udev, enumerate_subsystems_cb, &ctx); + if (ctx.failed) { + errno = ENOMEM; + return -1; + } + return 0; +} + +struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) +{ + return udev_enumerate ? udev_enumerate->list : NULL; +} + +struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) +{ + return list_entry ? list_entry->next : NULL; +} + +const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) +{ + return list_entry ? list_entry->name : NULL; +} + +const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) +{ + return list_entry ? list_entry->value : NULL; +} + +struct udev_device *udev_device_ref(struct udev_device *udev_device) +{ + if (udev_device) { + udev_device->refcount++; + } + return udev_device; +} + +struct udev_device *udev_device_unref(struct udev_device *udev_device) +{ + if (!udev_device) { + return NULL; + } + + udev_device->refcount--; + if (udev_device->refcount <= 0) { + udev_device_destroy(udev_device); + return NULL; + } + + return udev_device; +} + +struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) +{ + if (!udev || !syspath) { + errno = EINVAL; + return NULL; + } + + struct find_by_syspath_ctx ctx = { + .result = NULL, + .target = syspath, + }; + scan_scheme_devices(udev, find_by_syspath_cb, &ctx); + if (!ctx.result) { + errno = ENOENT; + } + return ctx.result; +} + +struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) +{ + (void)type; + if (!udev) { + errno = EINVAL; + return NULL; + } + + struct find_by_devnum_ctx ctx = { + .result = NULL, + .target = devnum, + }; + scan_scheme_devices(udev, find_by_devnum_cb, &ctx); + if (!ctx.result) { + errno = ENOENT; + } + return ctx.result; +} + +struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) +{ + if (!udev || !subsystem || !sysname) { + errno = EINVAL; + return NULL; + } + + struct find_by_subsystem_sysname_ctx ctx = { + .result = NULL, + .subsystem = subsystem, + .sysname = sysname, + }; + scan_scheme_devices(udev, find_by_subsystem_sysname_cb, &ctx); + if (!ctx.result) { + errno = ENOENT; + } + return ctx.result; +} + +const char *udev_device_get_devnode(struct udev_device *udev_device) +{ + return udev_device ? udev_device->devnode : NULL; +} + +dev_t udev_device_get_devnum(struct udev_device *udev_device) +{ + return udev_device ? udev_device->devnum : 0; +} + +const char *udev_device_get_action(struct udev_device *udev_device) +{ + return udev_device ? udev_device->action : NULL; +} + +const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) +{ + return udev_device ? list_entry_find_value(udev_device->properties, key) : NULL; +} + +struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) +{ + return udev_device ? udev_device->properties : NULL; +} + +struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) +{ + return udev_device ? udev_device->devlinks : NULL; +} + +struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device) +{ + return udev_device ? udev_device->sysattrs : NULL; +} + +struct udev_device *udev_device_get_parent(struct udev_device *udev_device) +{ + return udev_device ? udev_device->parent : NULL; +} + +struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) +{ + if (!udev_device || !subsystem) { + return NULL; + } + + if (string_matches(udev_device->subsystem, subsystem) && (!devtype || string_matches(udev_device->devtype, devtype))) { + return udev_device; + } + + if (udev_device->parent && string_matches(udev_device->parent->subsystem, subsystem) && (!devtype || string_matches(udev_device->parent->devtype, devtype))) { + return udev_device->parent; + } + + return NULL; +} + +const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) +{ + return udev_device ? list_entry_find_value(udev_device->sysattrs, sysattr) : NULL; +} + +const char *udev_device_get_devpath(struct udev_device *udev_device) +{ + return udev_device ? udev_device->devpath : NULL; +} + +const char *udev_device_get_syspath(struct udev_device *udev_device) +{ + return udev_device ? udev_device->syspath : NULL; +} + +const char *udev_device_get_subsystem(struct udev_device *udev_device) +{ + return udev_device ? udev_device->subsystem : NULL; +} + +const char *udev_device_get_devtype(struct udev_device *udev_device) +{ + return udev_device ? udev_device->devtype : NULL; +} + +const char *udev_device_get_sysname(struct udev_device *udev_device) +{ + return udev_device ? udev_device->sysname : NULL; +} + +const char *udev_device_get_sysnum(struct udev_device *udev_device) +{ + return udev_device ? udev_device->sysnum : NULL; +} + +const char *udev_device_get_driver(struct udev_device *udev_device) +{ + return udev_device ? udev_device->driver : NULL; +} + +int udev_device_has_tag(struct udev_device *udev_device, const char *tag) +{ + const char *tags = udev_device_get_property_value(udev_device, "TAGS"); + if (!tags || !tag || *tag == '\0') { + return 0; + } + + size_t tag_len = strlen(tag); + const char *cursor = tags; + while (*cursor) { + while (*cursor == ':' || *cursor == ' ') { + cursor++; + } + + const char *end = cursor; + while (*end && *end != ':') { + end++; + } + + if ((size_t)(end - cursor) == tag_len && strncmp(cursor, tag, tag_len) == 0) { + return 1; + } + + cursor = end; + } + + return 0; +} + +struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) +{ + int pipe_fds[2]; + + if (!udev || !name || strcmp(name, "udev") != 0) { + errno = EINVAL; + return NULL; + } + + struct udev_monitor *monitor = calloc(1, sizeof(*monitor)); + if (!monitor) { + return NULL; + } + + if (pipe(pipe_fds) != 0) { + free(monitor); + return NULL; + } + + int flags = fcntl(pipe_fds[0], F_GETFL); + if (flags >= 0) { + (void)fcntl(pipe_fds[0], F_SETFL, flags | O_NONBLOCK); + } + + monitor->refcount = 1; + monitor->udev = udev_ref(udev); + monitor->read_fd = pipe_fds[0]; + monitor->write_fd = pipe_fds[1]; + return monitor; +} + +struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor) +{ + if (udev_monitor) { + udev_monitor->refcount++; + } + return udev_monitor; +} + +struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) +{ + if (!udev_monitor) { + return NULL; + } + + udev_monitor->refcount--; + if (udev_monitor->refcount <= 0) { + if (udev_monitor->read_fd >= 0) { + close(udev_monitor->read_fd); + } + if (udev_monitor->write_fd >= 0) { + close(udev_monitor->write_fd); + } + free_monitor_filters(udev_monitor->filters); + free_monitor_events(udev_monitor->pending_head); + udev_unref(udev_monitor->udev); + free(udev_monitor); + return NULL; + } + + return udev_monitor; +} + +int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) +{ + if (!udev_monitor || !subsystem) { + errno = EINVAL; + return -1; + } + + struct udev_monitor_filter *filter = calloc(1, sizeof(*filter)); + if (!filter) { + return -1; + } + + filter->subsystem = xstrdup(subsystem); + filter->devtype = xstrdup(devtype); + if (!filter->subsystem || (devtype && !filter->devtype)) { + free(filter->subsystem); + free(filter->devtype); + free(filter); + return -1; + } + + if (!udev_monitor->filters) { + udev_monitor->filters = filter; + return 0; + } + + struct udev_monitor_filter *tail = udev_monitor->filters; + while (tail->next) { + tail = tail->next; + } + tail->next = filter; + return 0; +} + +int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) +{ + if (!udev_monitor) { + errno = EINVAL; + return -1; + } + + if (udev_monitor->enabled) { + return 0; + } + + if (udev_monitor->read_fd < 0 || udev_monitor->write_fd < 0) { + errno = EINVAL; + return -1; + } + + if (scan_scheme_devices(udev_monitor->udev, monitor_seed_existing_devices_cb, udev_monitor) != 0) { + return -1; + } + + if (monitor_emit_pending(udev_monitor) != 0) { + return -1; + } + + udev_monitor->enabled = true; + return 0; +} + +int udev_monitor_get_fd(struct udev_monitor *udev_monitor) +{ + return udev_monitor ? udev_monitor->read_fd : -1; +} + +struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) +{ + struct udev_monitor_event *event; + + if (!udev_monitor || udev_monitor->read_fd < 0) { + return NULL; + } + + char byte; + ssize_t read_bytes = read(udev_monitor->read_fd, &byte, sizeof(byte)); + if (read_bytes <= 0) { + return NULL; + } + + event = udev_monitor->pending_head; + if (!event) { + return NULL; + } + + udev_monitor->pending_head = event->next; + if (!udev_monitor->pending_head) { + udev_monitor->pending_tail = NULL; + } + + struct udev_device *device = event->device; + free(event); + return device; +} diff --git a/local/recipes/qt/redox-toolchain.cmake b/local/recipes/qt/redox-toolchain.cmake index 718a813b..e26e459e 100644 --- a/local/recipes/qt/redox-toolchain.cmake +++ b/local/recipes/qt/redox-toolchain.cmake @@ -65,7 +65,11 @@ set(CMAKE_CROSSCOMPILING TRUE) # COOKBOOK_HOST_SYSROOT is set to $(ROOT)/$(PREFIX_INSTALL) = prefix/x86_64-unknown-redox/sysroot # by mk/repo.mk and mk/prefix.mk. Fallback to hardcoded path if unset. if(NOT DEFINED COOKBOOK_HOST_SYSROOT OR COOKBOOK_HOST_SYSROOT STREQUAL "") - set(COOKBOOK_HOST_SYSROOT "/mnt/data/homes/kellito/Builds/rbos/prefix/x86_64-unknown-redox/sysroot") + if(EXISTS "$ENV{HOME}/.redoxer/x86_64-unknown-redox/toolchain/bin/x86_64-unknown-redox-gcc") + set(COOKBOOK_HOST_SYSROOT "$ENV{HOME}/.redoxer/x86_64-unknown-redox/toolchain") + else() + set(COOKBOOK_HOST_SYSROOT "/mnt/data/homes/kellito/Builds/rbos/prefix/x86_64-unknown-redox/sysroot") + endif() endif() set(CMAKE_C_COMPILER "${COOKBOOK_HOST_SYSROOT}/bin/x86_64-unknown-redox-gcc")