diff --git a/src/backend/backend.h b/src/backend/backend.h index fded6ff5..45980e12 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -5,6 +5,7 @@ #include +#include "driver.h" #include "compiler.h" #include "kernel.h" #include "region.h" @@ -176,6 +177,9 @@ struct backend_operations { // =========== Hooks ============ /// Let the backend hook into the event handling queue + // =========== Misc ============ + /// Return the driver that is been used by the backend + enum driver (*detect_driver)(backend_t *backend_data); }; typedef backend_t *(*backend_init_fn)(session_t *ps)attr_nonnull(1); diff --git a/src/backend/driver.c b/src/backend/driver.c new file mode 100644 index 00000000..d4f86cfd --- /dev/null +++ b/src/backend/driver.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +#include +#include + +#include "backend/backend.h" +#include "backend/driver.h" +#include "common.h" + +enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_window_t window) { + enum driver ret = 0; + // First we try doing backend agnostic detection using RANDR + // There's no way to query the X server about what driver is loaded, so RANDR is + // our best shot. + auto randr_version = xcb_randr_query_version_reply( + c, xcb_randr_query_version(c, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), + NULL); + if (randr_version && + (randr_version->major_version > 1 || randr_version->minor_version >= 4)) { + auto r = xcb_randr_get_providers_reply( + c, xcb_randr_get_providers(c, window), NULL); + if (r == NULL) { + log_warn("Failed to get RANDR providers"); + return 0; + } + + auto providers = xcb_randr_get_providers_providers(r); + for (auto i = 0; i < xcb_randr_get_providers_providers_length(r); i++) { + auto r2 = xcb_randr_get_provider_info_reply( + c, xcb_randr_get_provider_info(c, providers[i], r->timestamp), NULL); + if (r2 == NULL) { + continue; + } + if (r2->num_outputs == 0) { + free(r2); + continue; + } + + auto name = xcb_randr_get_provider_info_name(r2); + if (strcasestr(name, "modesetting") != NULL) { + ret |= DRIVER_MODESETTING; + } else if (strcasestr(name, "Radeon") != NULL) { + // Be conservative, add both radeon drivers + ret |= DRIVER_AMDGPU | DRIVER_RADEON; + } else if (strcasestr(name, "NVIDIA") != NULL) { + ret |= DRIVER_NVIDIA; + } else if (strcasestr(name, "nouveau") != NULL) { + ret |= DRIVER_NOUVEAU; + } else if (strcasestr(name, "Intel") != NULL) { + ret |= DRIVER_INTEL; + } + } + free(r); + } + + // If the backend supports driver detection, use that as well + if (backend_data && backend_data->ops->detect_driver) { + ret |= backend_data->ops->detect_driver(backend_data); + } + return ret; +} diff --git a/src/backend/driver.h b/src/backend/driver.h new file mode 100644 index 00000000..fb100347 --- /dev/null +++ b/src/backend/driver.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +#pragma once + +#include +#include + +struct session; +struct backend_base; + +/// A list of possible drivers. +/// The driver situation is a bit complicated. There are two drivers we care about: the +/// DDX, and the OpenGL driver. They are usually paired, but not always, since there is +/// also the generic modesetting driver. +/// This enum represents _both_ drivers. +enum driver { + DRIVER_AMDGPU = 1, // AMDGPU for DDX, radeonsi for OpenGL + DRIVER_RADEON = 2, // ATI for DDX, mesa r600 for OpenGL + DRIVER_FGLRX = 4, + DRIVER_NVIDIA = 8, + DRIVER_NOUVEAU = 16, + DRIVER_INTEL = 32, + DRIVER_MODESETTING = 64, +}; + +/// Return a list of drivers currently in use by the X server. +/// Note, this is a best-effort test, so no guarantee all drivers will be detected. +enum driver detect_driver(xcb_connection_t *, struct backend_base *, xcb_window_t); + +// Print driver names to stdout, for diagnostics +static inline void print_drivers(enum driver drivers) { + if (drivers & DRIVER_AMDGPU) { + printf("AMDGPU, "); + } + if (drivers & DRIVER_RADEON) { + printf("Radeon, "); + } + if (drivers & DRIVER_FGLRX) { + printf("fglrx, "); + } + if (drivers & DRIVER_NVIDIA) { + printf("NVIDIA, "); + } + if (drivers & DRIVER_NOUVEAU) { + printf("nouveau, "); + } + if (drivers & DRIVER_INTEL) { + printf("Intel, "); + } + if (drivers & DRIVER_MODESETTING) { + printf("modesetting, "); + } + printf("\b\b \n"); +} diff --git a/src/backend/meson.build b/src/backend/meson.build index d8f7bc1b..9318b78f 100644 --- a/src/backend/meson.build +++ b/src/backend/meson.build @@ -1,5 +1,5 @@ # enable xrender -srcs += [ files('backend_common.c', 'xrender/xrender.c', 'backend.c') ] +srcs += [ files('backend_common.c', 'xrender/xrender.c', 'backend.c', 'driver.c') ] # enable opengl if get_option('opengl') diff --git a/src/common.h b/src/common.h index 93ab2a88..4a4af280 100644 --- a/src/common.h +++ b/src/common.h @@ -78,6 +78,7 @@ // FIXME This list of includes should get shorter #include "backend/backend.h" +#include "backend/driver.h" #include "compiler.h" #include "config.h" #include "kernel.h" @@ -294,6 +295,7 @@ typedef struct session { ev_signal int_signal; /// backend data backend_t *backend_data; + enum driver drivers; /// libev mainloop struct ev_loop *loop; diff --git a/src/compton.c b/src/compton.c index 2c71854f..ebfd4888 100644 --- a/src/compton.c +++ b/src/compton.c @@ -1311,6 +1311,9 @@ static bool redir_start(session_t *ps) { ps->redirected = true; + // Re-detect driver since we now have a backend + ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root); + root_damaged(ps); // Repaint the whole screen @@ -1985,6 +1988,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy, // of OpenGL context. init_overlay(ps); + ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root); + // Initialize filters, must be preceded by OpenGL context creation if (!ps->o.experimental_backends && !init_render(ps)) { log_fatal("Failed to initialize the backend"); diff --git a/src/diagnostic.c b/src/diagnostic.c index 7e26d9f5..ae31005b 100644 --- a/src/diagnostic.c +++ b/src/diagnostic.c @@ -4,6 +4,7 @@ #include #include +#include "backend/driver.h" #include "diagnostic.h" #include "config.h" #include "common.h" @@ -21,6 +22,8 @@ void print_diagnostics(session_t *ps, const char *config_file) { printf("* Fast Math: Yes\n"); #endif printf("* Config file used: %s\n", config_file ?: "None"); + printf("\n### Drivers (inaccurate):\n\n"); + print_drivers(ps->drivers); } // vim: set noet sw=8 ts=8 :