Change udev detect mothod. Signed-off-by: Windsome Zeng diff -purN a/src/native-state-drm.cpp b/src/native-state-drm.cpp --- a/src/native-state-drm.cpp 2022-08-11 16:49:38.274490554 +0800 +++ b/src/native-state-drm.cpp 2022-08-11 16:48:41.759513413 +0800 @@ -29,6 +29,7 @@ #include #include #include +#include /****************** * Public methods * @@ -157,180 +158,72 @@ inline static bool invalid_drm_node_path return !(valid_drm_node_path(provided_node_path)); } -/* Udev methods */ -// Udev detection functions -#define UDEV_TEST_FUNC_SIGNATURE(udev_identifier, device_identifier, syspath_identifier) \ - struct udev * __restrict const udev_identifier, \ - struct udev_device * __restrict const device_identifier, \ - char const * __restrict syspath_identifier - -/* Omitting the parameter names is kind of ugly but is the only way - * to force G++ to forget about the unused parameters. - * Having big warnings during the compilation isn't very nice. - * - * These functions will be used as function pointers and should have - * the same signature to avoid weird stack related issues. - * - * Another way to deal with that issue will be to mark unused parameters - * with __attribute__((unused)) - */ -static bool udev_drm_test_virtual( - UDEV_TEST_FUNC_SIGNATURE(,,tested_node_syspath)) -{ - return strstr(tested_node_syspath, "virtual") != NULL; -} - -static bool udev_drm_test_not_virtual( - UDEV_TEST_FUNC_SIGNATURE(udev, current_device, tested_node_syspath)) -{ - return !udev_drm_test_virtual(udev, - current_device, - tested_node_syspath); -} - static bool -udev_drm_test_primary_gpu(UDEV_TEST_FUNC_SIGNATURE(, current_device,)) -{ - bool is_main_gpu = false; - - auto const drm_node_parent = udev_device_get_parent(current_device); - - /* While tempting, using udev_device_unref will generate issues - * when unreferencing the child in udev_get_node_that_pass_in_enum - * - * udev_device_unref WILL unreference the parent, so avoid doing - * that here. - * - * ( See udev sources : src/libudev/libudev-device.c ) - */ - if (drm_node_parent != NULL) { - is_main_gpu = - (udev_device_get_sysattr_value(drm_node_parent, "boot_vga") - != NULL); - } - - return is_main_gpu; -} - -/* Test if the drm-device is actually modeset capable. - * Render-only devices cannot drive an actual display, - * so the GETRESOURCES ioctl will fail in that case. - */ -static bool udev_drm_test_modeset(std::string const& dev_path) -{ - struct drm_mode_card_res res {}; - int fd, ret; - - fd = open(dev_path.c_str(), O_RDWR); - if (!valid_fd(fd)) - return false; - - ret = drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res); - drmClose(fd); - - return !ret; -} - -static std::string -udev_get_node_that_pass_in_enum( - struct udev * __restrict const udev, - struct udev_enumerate * __restrict const dev_enum, - bool (* check_function)(UDEV_TEST_FUNC_SIGNATURE(,,))) +drm_device_is_kms(struct udev_device *device) { - std::string result; + const char *filename = NULL; + drmModeRes *res = NULL; + int fd = -1; + bool ret = false; - auto current_element = udev_enumerate_get_list_entry(dev_enum); + if (!device) + goto out_fail; - while (current_element && result.empty()) { - char const * __restrict current_element_sys_path = - udev_list_entry_get_name(current_element); + filename = udev_device_get_devnode(device); + if (!filename) + goto out_fail; - if (current_element_sys_path) { - struct udev_device * current_device = - udev_device_new_from_syspath(udev, - current_element_sys_path); - auto check_passed = check_function( - udev, current_device, current_element_sys_path); + fd = open(filename, O_RDWR); + if (fd < 0) + goto out_fail; - if (check_passed) { - const char * device_node_path = - udev_device_get_devnode(current_device); + res = drmModeGetResources(fd); + if (!res) + goto out_fail; - if (device_node_path && - udev_drm_test_modeset(device_node_path)) { - result = device_node_path; - } + if (res->count_crtcs <= 0 || res->count_connectors <= 0 || + res->count_encoders <= 0) + goto out_fail; - } + ret = true; - udev_device_unref(current_device); - } - - current_element = udev_list_entry_get_next(current_element); - } +out_fail: + if (res) + drmModeFreeResources(res); + if (fd >= 0) + close(fd); - return result; + return ret; } -/* Inspired by KWin detection mechanism */ -/* And yet KWin got it wrong too, it seems. - * 1 - Looking for the primary GPU by checking the flag 'boot_vga' - * won't get you far with some embedded chipsets, like Rockchip. - * 2 - Looking for a GPU plugged in PCI will fail on various embedded - * devices ! - * 3 - Looking for a render node is not guaranteed to work on some - * poorly maintained DRM drivers, which plague some embedded - * devices too... - * - * So, we won't play too smart here. - * - We first check for a primary GPU plugged in PCI with the 'boot_vga' - * attribute, to take care of Desktop users using multiple GPU. - * - Then, we just check for a DRM node that is not virtual - * - At least, we use the first virtual node we get, if we didn't find - * anything yet. - * This should take care of almost every potential use case. - * - * The remaining ones will be dealt with an additional option to - * specify the DRM dev node manually. - */ +/* Inspired by Wayland's Weston detection mechanism */ static std::string udev_main_gpu_drm_node_path() { - Log::debug("Using Udev to detect the right DRM node to use\n"); auto udev = udev_new(); - auto dev_enumeration = udev_enumerate_new(udev); - - udev_enumerate_add_match_subsystem(dev_enumeration, "drm"); - udev_enumerate_add_match_sysname(dev_enumeration, "card[0-9]*"); - udev_enumerate_scan_devices(dev_enumeration); - - Log::debug("Looking for the main GPU DRM node...\n"); - std::string node_path = udev_get_node_that_pass_in_enum( - udev, dev_enumeration, udev_drm_test_primary_gpu); - - if (invalid_drm_node_path(node_path)) { - Log::debug("Not found!\n"); - Log::debug("Looking for a concrete GPU DRM node...\n"); - node_path = udev_get_node_that_pass_in_enum( - udev, dev_enumeration, udev_drm_test_not_virtual); - } - if (invalid_drm_node_path(node_path)) { - Log::debug("Not found!?\n"); - Log::debug("Looking for a virtual GPU DRM node...\n"); - node_path = udev_get_node_that_pass_in_enum( - udev, dev_enumeration, udev_drm_test_virtual); - } - if (invalid_drm_node_path(node_path)) { - Log::debug("Not found.\n"); - Log::debug("Cannot find a single DRM node using UDEV...\n"); + struct udev_enumerate *e; + struct udev_list_entry *entry; + const char *path; + struct udev_device *device; + std::string node_path; + + e = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(e, "drm"); + udev_enumerate_add_match_sysname(e, "card[0-9]*"); + + udev_enumerate_scan_devices(e); + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { + path = udev_list_entry_get_name(entry); + device = udev_device_new_from_syspath(udev, path); + if (drm_device_is_kms(device)) + node_path = udev_device_get_devnode(device); + + udev_device_unref(device); + if (!node_path.empty()) + break; } - if (valid_drm_node_path(node_path)) { - Log::debug("Success!\n"); - } - - udev_enumerate_unref(dev_enumeration); + udev_enumerate_unref(e); udev_unref(udev); - return node_path; }