From 7c2c631807b5262c09885063d0ff36788ea2ec22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 16 Apr 2024 16:19:53 +0800 Subject: [PATCH 01/40] README: remove AUR as it's very outdated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea6199edb8..cc8304efdc 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Some distros packaged a outdated fastfetch version. Older version is not support * Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (for Ubuntu 22.04 or above) * Debian / Ubuntu: Download `fastfetch--Linux.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and `dpkg -i fastfetch--Linux.deb` (for Ubuntu 22.04 or above and Debian 12 or above). -* Arch Linux: `sudo pacman -S fastfetch`. You can also find fastfetch [on the AUR](https://aur.archlinux.org/packages/fastfetch-git). +* Arch Linux: `sudo pacman -S fastfetch` * Fedora: `sudo dnf install fastfetch` * Gentoo: `sudo emerge --ask app-misc/fastfetch` * Alpine: `apk add --upgrade fastfetch` From ccfba6786c3f9a3752449b5d0001d8e6de62f0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 16 Apr 2024 16:24:30 +0800 Subject: [PATCH 02/40] README: metion linuxbrew --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cc8304efdc..cc40b2ee6a 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ There are [screenshots on different platforms](https://github.com/fastfetch-cli/ ### Linux -Some distros packaged a outdated fastfetch version. Older version is not supported, please always ensure that the latest version is used. +Some distros packaged an outdated fastfetch version. Older version is not supported, please always ensure that the latest version is used. * Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (for Ubuntu 22.04 or above) * Debian / Ubuntu: Download `fastfetch--Linux.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and `dpkg -i fastfetch--Linux.deb` (for Ubuntu 22.04 or above and Debian 12 or above). @@ -41,7 +41,9 @@ Some distros packaged a outdated fastfetch version. Older version is not support Replace sudo with doas depending on what you use. -[See also if fastfetch has been packaged for your favorite Linux distro](#Packaging) +[See also if fastfetch has been packaged for your favorite Linux distro](#Packaging). + +If fastfetch is not packaged for your distro or an outdated version is packaged, [linuxbrew](https://brew.sh/) is a good alternate: `brew install fastfetch` ### macOS From 636b6fc43dcff9be26985a4624be51f139ea32d3 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Tue, 16 Apr 2024 16:34:18 +0800 Subject: [PATCH 03/40] Packaging: update debian stuff --- debian/changelog | 12 ++++++++++++ debian/files | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 6c560750f7..ba25aafed8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +fastfetch (2.9.2) jammy; urgency=medium + + * Update to 2.9.2 + + -- Carter Li Tue, 16 Apr 2024 16:32:40 +0800 + +fastfetch (2.9.1) jammy; urgency=medium + + * Update to 2.9.1 + + -- Carter Li Mon, 08 Apr 2024 09:34:30 +0800 + fastfetch (2.8.10) jammy; urgency=medium * Update to 2.8.10 diff --git a/debian/files b/debian/files index 655eaefe59..be634b16a8 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -fastfetch_2.8.10_source.buildinfo universe/utils optional +fastfetch_2.9.2_source.buildinfo universe/utils optional From 07d3fe3044286f6b06ea28eb0e162821a5013e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 16 Apr 2024 21:54:52 +0800 Subject: [PATCH 04/40] CI: add musl build --- .github/workflows/ci.yml | 132 ++++++++++++++++++++++++--------------- CMakeLists.txt | 7 ++- 2 files changed, 86 insertions(+), 53 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94f3a74b72..6fc8338138 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,58 +19,6 @@ jobs: - name: Run Spellchecker run: codespell - linux-buildtest: - name: Linux build test - permissions: - contents: read - strategy: - fail-fast: false - matrix: - compiler: [ - { cc: gcc, cxx: "g++" }, - { cc: clang, cxx: "clang++" }, - { cc: "musl-gcc", cxx: "musl-g++" } - ] - enableFeatures: [ON, OFF] - exclude: - # The feature libraries are all build against glibc, so they can't be used with musl - - compiler: { cc: "musl-gcc", cxx: "musl-g++" } - enableFeatures: ON - runs-on: ubuntu-22.04 - steps: - - name: checkout repository - uses: actions/checkout@v4 - - - name: uname -a - run: uname -a - - - name: install required packages - run: sudo apt-get update && sudo apt-get install -y musl-dev musl-tools linux-headers-generic libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev libddcutil-dev libchafa-dev libdrm-dev directx-headers-dev - - - name: configure project - env: - CC: ${{ matrix.compiler.cc }} - CXX: ${{ matrix.compiler.cxx }} - run: cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_VULKAN=${{ matrix.enableFeatures }} -DENABLE_WAYLAND=${{ matrix.enableFeatures }} -DENABLE_XCB_RANDR=${{ matrix.enableFeatures }} -DENABLE_XCB=${{ matrix.enableFeatures }} -DENABLE_XRANDR=${{ matrix.enableFeatures }} -DENABLE_X11=${{ matrix.enableFeatures }} -DENABLE_GIO=${{ matrix.enableFeatures }} -DENABLE_DCONF=${{ matrix.enableFeatures }} -DENABLE_DBUS=${{ matrix.enableFeatures }} -DENABLE_XFCONF=${{ matrix.enableFeatures }} -DENABLE_SQLITE3=${{ matrix.enableFeatures }} -DENABLE_RPM=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK7=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK6=${{ matrix.enableFeatures }} -DENABLE_CHAFA=${{ matrix.enableFeatures }} -DENABLE_ZLIB=${{ matrix.enableFeatures }} -DENABLE_EGL=${{ matrix.enableFeatures }} -DENABLE_GLX=${{ matrix.enableFeatures }} -DENABLE_OSMESA=${{ matrix.enableFeatures }} -DENABLE_OPENCL=${{ matrix.enableFeatures }} -DENABLE_LIBNM=${{ matrix.enableFeatures }} -DENABLE_PULSE=${{ matrix.enableFeatures }} -DENABLE_DIRECTX_HEADERS=${{ matrix.enableFeatures }} -DENABLE_DRM=${{ matrix.enableFeatures }} -DENABLE_DDCUTIL=${{ matrix.enableFeatures }} -DENABLE_CHAFA=${{ matrix.enableFeatures }} . - - - name: build project - run: cmake --build . --verbose -j4 - - - name: run tests - run: ctest - - - name: run fastfetch --list-features - run: ./fastfetch --list-features - - - name: run fastfetch - run: time ./fastfetch -c presets/ci.jsonc - - - name: run fastfetch --format json - run: time ./fastfetch -c presets/ci.jsonc --format json - - - name: run flashfetch - run: time ./flashfetch - Linux-old-amd64: name: Linux-old-amd64 runs-on: ubuntu-20.04 @@ -230,6 +178,84 @@ jobs: name: fastfetch-linux-aarch64 path: ./fastfetch-*.* + musl-amd64: + name: Musl-amd64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: setup alpine linux + uses: jirutka/setup-alpine@master + + - name: install dependencies + run: | + cat /etc/alpine-release + uname -a + apk add cmake samurai vulkan-loader-dev libxcb-dev wayland-dev libdrm-dev dconf-dev imagemagick-dev chafa-dev zlib-dev dbus-dev mesa-dev opencl-dev xfconf-dev sqlite-dev networkmanager-dev pulseaudio-dev ddcutil-dev gcc g++ + shell: alpine.sh --root {0} + + - name: build + run: | + cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -GNinja . + cmake --build . --target package --verbose -j4 + shell: alpine.sh {0} + + - name: run + run: | + ./fastfetch --list-features + time ./fastfetch -c presets/ci.jsonc + time ./fastfetch -c presets/ci.jsonc --format json + time ./flashfetch + ldd fastfetch + ctest + shell: alpine.sh {0} + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: fastfetch-musl-amd64 + path: ./fastfetch-*.* + + musl-aarch64: + name: Musl-aarch64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: setup alpine linux + uses: jirutka/setup-alpine@master + with: + arch: aarch64 + + - name: install dependencies + run: | + cat /etc/alpine-release + uname -a + apk add cmake samurai vulkan-loader-dev libxcb-dev wayland-dev libdrm-dev dconf-dev imagemagick-dev chafa-dev zlib-dev dbus-dev mesa-dev opencl-dev xfconf-dev sqlite-dev networkmanager-dev pulseaudio-dev ddcutil-dev gcc g++ + shell: alpine.sh --root {0} + + - name: build + run: | + cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -GNinja . + cmake --build . --target package --verbose -j4 + shell: alpine.sh {0} + + - name: run + run: | + ./fastfetch --list-features + time ./fastfetch -c presets/ci.jsonc + time ./fastfetch -c presets/ci.jsonc --format json + time ./flashfetch + ldd fastfetch + ctest + shell: alpine.sh {0} + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: fastfetch-musl-aarch64 + path: ./fastfetch-*.* + macos-universal: name: macOS-universal runs-on: macos-latest @@ -495,6 +521,8 @@ jobs: needs: - linux-amd64 - linux-aarch64 + - musl-amd64 + - musl-aarch64 - macos-universal - freebsd-amd64 - freebsd-aarch64 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b315f5a38..bb40ea593f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ option(ENABLE_ASAN "Build fastfetch with ASAN (address sanitizer)" OFF) option(ENABLE_PROPRIETARY_GPU_DRIVER_API "Enable proprietary GPU driver API (NVML, IGCL and AGS)" ON) option(BUILD_TESTS "Build tests" OFF) # Also create test executables option(SET_TWEAK "Add tweak to project version" ON) # This is set to off by github actions for release builds +option(SET_TWEAK "Add tweak to project version" ON) # This is set to off by github actions for release builds if (LINUX) set(CUSTOM_PCI_IDS_PATH "" CACHE STRING "Custom path to file pci.ids, defaults to `/usr/share/hwdata/pci.ids`") @@ -1135,7 +1136,11 @@ else() # We don't use this in Windows endif() if(LINUX) - set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB;RPM") + find_program(HAVE_DPKG "dpkg") + find_program(HAVE_RPMBUILD "rpmbuild") + if(HAVE_DPKG AND HAVE_RPMBUILD) + set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB;RPM") + endif() set(CPACK_DEBIAN_PACKAGE_SECTION, "utils") set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") From f098cdbf6900a0cb627c03c6de966fc1f7206fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 16 Apr 2024 22:58:06 +0800 Subject: [PATCH 05/40] CI: update artifact name for musl builds --- .github/workflows/ci.yml | 4 ++-- CMakeLists.txt | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6fc8338138..f70de31aa0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -196,7 +196,7 @@ jobs: - name: build run: | - cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -GNinja . + cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DIS_MUSL=ON -GNinja . cmake --build . --target package --verbose -j4 shell: alpine.sh {0} @@ -236,7 +236,7 @@ jobs: - name: build run: | - cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -GNinja . + cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DIS_MUSL=ON -GNinja . cmake --build . --target package --verbose -j4 shell: alpine.sh {0} diff --git a/CMakeLists.txt b/CMakeLists.txt index bb40ea593f..34ca17a7eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ option(ENABLE_ASAN "Build fastfetch with ASAN (address sanitizer)" OFF) option(ENABLE_PROPRIETARY_GPU_DRIVER_API "Enable proprietary GPU driver API (NVML, IGCL and AGS)" ON) option(BUILD_TESTS "Build tests" OFF) # Also create test executables option(SET_TWEAK "Add tweak to project version" ON) # This is set to off by github actions for release builds -option(SET_TWEAK "Add tweak to project version" ON) # This is set to off by github actions for release builds +option(IS_MUSL "Build with musl libc" OFF) # Used by Github Actions if (LINUX) set(CUSTOM_PCI_IDS_PATH "" CACHE STRING "Custom path to file pci.ids, defaults to `/usr/share/hwdata/pci.ids`") @@ -1132,7 +1132,11 @@ else() # We don't use this in Windows if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") set(CMAKE_SYSTEM_PROCESSOR "amd64") endif() - string(TOLOWER "${CMAKE_PROJECT_NAME}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}" CPACK_PACKAGE_FILE_NAME) + if(IS_MUSL) + string(TOLOWER "${CMAKE_PROJECT_NAME}-musl-${CMAKE_SYSTEM_PROCESSOR}" CPACK_PACKAGE_FILE_NAME) + else() + string(TOLOWER "${CMAKE_PROJECT_NAME}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}" CPACK_PACKAGE_FILE_NAME) + endif() endif() if(LINUX) From 6d36ec55c5efd5ab773cb7eb087d92d93b239a59 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 17 Apr 2024 14:21:30 +0800 Subject: [PATCH 06/40] GPU (Linux): try detecting vmem with gpu kernel module --- src/detection/gpu/gpu_linux.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 3cc6bdcdd9..27a7d64a00 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -20,8 +20,9 @@ FF_MAYBE_UNUSED static void pciDetectTemp(FFGPUResult* gpu, uint32_t deviceClass FF_LIST_FOR_EACH(FFTempValue, tempValue, *tempsResult) { + // https://www.kernel.org/doc/html/v5.10/gpu/amdgpu.html#hwmon-interfaces // FIXME: this code doesn't take multiGPUs into count - //The kernel exposes the device class multiplied by 256 for some reason + // The kernel exposes the device class multiplied by 256 for some reason if(tempValue->deviceClass == deviceClass * 256) { gpu->temperature = tempValue->value; @@ -54,6 +55,31 @@ static void pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer } } +static void pciDetectVmem(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) +{ + // https://www.kernel.org/doc/html/v5.10/gpu/amdgpu.html#mem-info-vis-vram-total + ffStrbufAppendS(pciDir, "/mem_info_vis_vram_total"); + uint64_t size = 0; + if (ffReadFileBuffer(pciDir->chars, buffer) && (size = ffStrbufToUInt(buffer, 0))) + { + gpu->type = size > 1024UL * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + if (gpu->type == FF_GPU_TYPE_DISCRETE) + gpu->dedicated.total = size; + else + gpu->shared.total = size; + + ffStrbufSubstrBefore(pciDir, pciDir->length - (uint32_t) strlen("/mem_info_vis_vram_total")); + ffStrbufAppendS(pciDir, "/mem_info_vram_used"); + if (ffReadFileBuffer(pciDir->chars, buffer) && (size = ffStrbufToUInt(buffer, 0))) + { + if (gpu->type == FF_GPU_TYPE_DISCRETE) + gpu->dedicated.used = size; + else + gpu->shared.used = size; + } + } +} + static bool loadPciIds(FFstrbuf* pciids) { #ifdef FF_CUSTOM_PCI_IDS_PATH @@ -154,6 +180,9 @@ static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) ffGPUParsePciIds(&pciids, subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); } + pciDetectVmem(gpu, &pciDir, &buffer); + ffStrbufSubstrBefore(&pciDir, pciDevDirLength); + pciDetectDriver(gpu, &pciDir, &buffer); ffStrbufSubstrBefore(&pciDir, pciDevDirLength); From 03fe5803de7cfb960e4358bed119b6f1a441f334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Apr 2024 19:00:06 +0800 Subject: [PATCH 07/40] common/properties: we don't use threads any more --- src/common/properties.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/common/properties.c b/src/common/properties.c index f5180aa111..3dead50454 100644 --- a/src/common/properties.c +++ b/src/common/properties.c @@ -154,16 +154,13 @@ bool ffParsePropFileListValues(const FFlist* list, const char* relativeFile, uin { bool foundAFile = false; - FF_STRBUF_AUTO_DESTROY baseDir = ffStrbufCreateA(64); - FF_LIST_FOR_EACH(FFstrbuf, dirPrefix, *list) { - //We need to copy the dir each time, because it used by multiple threads, so we can't directly write to it. - ffStrbufSet(&baseDir, dirPrefix); - ffStrbufAppendS(&baseDir, relativeFile); - - if(ffParsePropFileValues(baseDir.chars, numQueries, queries)) + const uint32_t dirPrefixLength = dirPrefix->length; + ffStrbufAppendS(dirPrefix, relativeFile); + if(ffParsePropFileValues(dirPrefix->chars, numQueries, queries)) foundAFile = true; + ffStrbufSubstrBefore(dirPrefix, dirPrefixLength); bool allSet = true; for(uint32_t k = 0; k < numQueries; k++) From 5e3fdc6c335d934006c156fe0912424af4a19ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Apr 2024 19:00:45 +0800 Subject: [PATCH 08/40] DE (Linux): better Gnome version detection --- src/common/dbus.c | 10 ++++++---- src/common/dbus.h | 2 +- src/detection/de/de_linux.c | 13 ++++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/common/dbus.c b/src/common/dbus.c index cf94128c71..f5b40aee2a 100644 --- a/src/common/dbus.c +++ b/src/common/dbus.c @@ -181,22 +181,24 @@ DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char return reply; } -void ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result) +bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result) { DBusMessage* reply = ffDBusGetProperty(dbus, busName, objectPath, interface, property); if(reply == NULL) - return; + return false; DBusMessageIter rootIterator; if(!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator)) { dbus->lib->ffdbus_message_unref(reply); - return; + return false; } - ffDBusGetValue(dbus, &rootIterator, result); + bool ret = ffDBusGetValue(dbus, &rootIterator, result); dbus->lib->ffdbus_message_unref(reply); + + return ret; } #endif //FF_HAVE_DBUS diff --git a/src/common/dbus.h b/src/common/dbus.h index 8c9be36c2b..a327884d44 100644 --- a/src/common/dbus.h +++ b/src/common/dbus.h @@ -48,6 +48,6 @@ bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result); bool ffDBusGetByte(FFDBusData* dbus, DBusMessageIter* iter, uint8_t* result); DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method); DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property); -void ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result); +bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result); #endif // FF_HAVE_DBUS diff --git a/src/detection/de/de_linux.c b/src/detection/de/de_linux.c index 5f7fd88391..59a1b17a31 100644 --- a/src/detection/de/de_linux.c +++ b/src/detection/de/de_linux.c @@ -1,5 +1,6 @@ #include "de.h" +#include "common/dbus.h" #include "common/io/io.h" #include "common/library.h" #include "common/parsing.h" @@ -41,9 +42,19 @@ static void getKDE(FFstrbuf* result, FFDEOptions* options) } } +static const char* getGnomeBySo(FFstrbuf* result) +{ + FFDBusData dbus; + if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL) + return "ffDBusLoadData() failed"; + + ffDBusGetPropertyString(&dbus, "org.gnome.Shell", "/org/gnome/Shell", "org.gnome.Shell", "ShellVersion", result); + return NULL; +} + static void getGnome(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options) { - ffParsePropFileData("gnome-shell/org.gnome.Extensions", "version :", result); + getGnomeBySo(result); if (result->length == 0) { From 2862f466f3efc75cfbc9cca0a42cc1de95decebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Apr 2024 20:28:13 +0800 Subject: [PATCH 09/40] Terminal (Linux): improve performance of kitty version detection --- src/detection/terminalshell/terminalshell.c | 28 ++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 40f5129d3c..6c4456cbe6 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -469,6 +469,32 @@ static bool getTerminalVersionZellij(FFstrbuf* exe, FFstrbuf* version) return version->length > 0; } +static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) +{ + // kitty is written in python. `kitty --version` can be expensive + char buffer[1024] = {}; + if (ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) || + ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer)) + { + // Starts from version 0.17.0 + // https://github.com/kovidgoyal/kitty/blob/master/kitty/constants.py#L25 + const char* p = memmem(buffer, sizeof(buffer) - 1, "version: Version = Version(", strlen("version: Version = Version(")); + if (p) + { + p += strlen("version: Version = Version("); + int major, minor, patch; + if (sscanf(p, "%d,%d,%d", &major, &minor, &patch) == 3) + { + ffStrbufSetF(version, "%d.%d.%d", major, minor, patch); + return true; + } + } + } + + //kitty 0.21.2 created by Kovid Goyal + return getExeVersionGeneral(exe, version); +} + #ifdef _WIN32 static bool getTerminalVersionWindowsTerminal(FFstrbuf* exe, FFstrbuf* version) @@ -576,7 +602,7 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe #ifndef _WIN32 if(ffStrbufIgnCaseEqualS(processName, "kitty")) - return getExeVersionGeneral(exe, version); //kitty 0.21.2 created by Kovid Goyal + return getTerminalVersionKitty(exe, version); if (ffStrbufIgnCaseEqualS(processName, "Tabby") && getExeVersionRaw(exe, version)) return true; From 6851c9cc1ac5b43298e94e2f4073f579cdfed778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Apr 2024 20:58:49 +0800 Subject: [PATCH 10/40] DE (Linux): tag `gnome-shell --version` a slow operation --- src/detection/de/de_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detection/de/de_linux.c b/src/detection/de/de_linux.c index 59a1b17a31..cfe5e7cfca 100644 --- a/src/detection/de/de_linux.c +++ b/src/detection/de/de_linux.c @@ -56,7 +56,7 @@ static void getGnome(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options) { getGnomeBySo(result); - if (result->length == 0) + if (result->length == 0 && options->slowVersionDetection) { if (ffProcessAppendStdOut(result, (char* const[]){ "gnome-shell", From 073d6a9337547601a057a1740dd20b5477fe02f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Apr 2024 21:02:32 +0800 Subject: [PATCH 11/40] DE (Linux): improve kde version detection performance KDE6 uses wayland only --- src/detection/de/de_linux.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/detection/de/de_linux.c b/src/detection/de/de_linux.c index cfe5e7cfca..7fbb4652aa 100644 --- a/src/detection/de/de_linux.c +++ b/src/detection/de/de_linux.c @@ -13,19 +13,20 @@ static void getKDE(FFstrbuf* result, FFDEOptions* options) { - ffParsePropFileValues(FASTFETCH_TARGET_DIR_USR "/share/xsessions/plasmax11.desktop", 1, (FFpropquery[]) { + ffParsePropFileValues(FASTFETCH_TARGET_DIR_USR "/share/wayland-sessions/plasma.desktop", 1, (FFpropquery[]) { {"X-KDE-PluginInfo-Version =", result} }); - if(result->length == 0) - ffParsePropFileData("xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result); - if(result->length == 0) - ffParsePropFileData("xsessions/plasma5.desktop", "X-KDE-PluginInfo-Version =", result); if(result->length == 0) { - ffParsePropFileValues(FASTFETCH_TARGET_DIR_USR "/share/wayland-sessions/plasma.desktop", 1, (FFpropquery[]) { + ffParsePropFileValues(FASTFETCH_TARGET_DIR_USR "/share/xsessions/plasmax11.desktop", 1, (FFpropquery[]) { {"X-KDE-PluginInfo-Version =", result} }); } + if(result->length == 0) + ffParsePropFileData("xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result); + if(result->length == 0) + ffParsePropFileData("xsessions/plasma5.desktop", "X-KDE-PluginInfo-Version =", result); + if(result->length == 0) ffParsePropFileData("wayland-sessions/plasmawayland.desktop", "X-KDE-PluginInfo-Version =", result); if(result->length == 0) From a561a96a5f114a8f98fdbaeb9b9a3a57b2d82131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Apr 2024 22:09:43 +0800 Subject: [PATCH 12/40] Display (Linux): `wlr-randr` for hyprland which support reporting fractional scale factors --- CMakeLists.txt | 1 + .../displayserver/linux/displayserver_linux.c | 57 ++++++++++ .../displayserver/linux/displayserver_linux.h | 3 + src/detection/displayserver/linux/wayland.c | 44 +------- src/detection/displayserver/linux/wlroots.c | 105 ++++++++++++++++++ 5 files changed, 168 insertions(+), 42 deletions(-) create mode 100644 src/detection/displayserver/linux/wlroots.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 34ca17a7eb..be5a66486d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -400,6 +400,7 @@ if(LINUX) src/detection/diskio/diskio_linux.c src/detection/displayserver/linux/displayserver_linux.c src/detection/displayserver/linux/drm.c + src/detection/displayserver/linux/wlroots.c src/detection/displayserver/linux/wayland.c src/detection/displayserver/linux/wmde.c src/detection/displayserver/linux/xcb.c diff --git a/src/detection/displayserver/linux/displayserver_linux.c b/src/detection/displayserver/linux/displayserver_linux.c index 78483b7e45..713a2626a0 100644 --- a/src/detection/displayserver/linux/displayserver_linux.c +++ b/src/detection/displayserver/linux/displayserver_linux.c @@ -1,4 +1,7 @@ #include "displayserver_linux.h" +#include "common/io/io.h" +#include "util/edidHelper.h" +#include "util/stringUtils.h" #ifdef __FreeBSD__ #include "common/settings.h" @@ -8,6 +11,20 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) { if (instance.config.general.dsForceDrm == FF_DS_FORCE_DRM_TYPE_FALSE) { + #ifdef __linux__ + { + const char* desktopSession = getenv("DESKTOP_SESSION"); + if (desktopSession && ffStrEquals(desktopSession, "hyprland")) + { + ffStrbufSetStatic(&ds->wmProcessName, "xdg-desktop-portal-hyprland"); + ffStrbufSetStatic(&ds->wmPrettyName, FF_WM_PRETTY_HYPRLAND); + ffStrbufSetStatic(&ds->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); + if (ffdsConnectWlroots(ds) == NULL) + return; + } + } + #endif + //We try wayland as our preferred display server, as it supports the most features. //This method can't detect the name of our WM / DE ffdsConnectWayland(ds); @@ -57,3 +74,43 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) //This fills in missing information about WM / DE by using env vars and iterating processes ffdsDetectWMDE(ds); } + +bool ffdsMatchDrmConnector(const char* connName, FFstrbuf* edidName) +{ + // https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_output-event-name + // The doc says that "do not assume that the name is a reflection of an underlying DRM connector, X11 connection, etc." + // However I can't find a better method to get the edid data + const char* drmDirPath = "/sys/class/drm/"; + + DIR* dirp = opendir(drmDirPath); + if(dirp == NULL) + return false; + + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) + { + const char* plainName = entry->d_name; + if (ffStrStartsWith(plainName, "card")) + { + const char* tmp = strchr(plainName + strlen("card"), '-'); + if (tmp) plainName = tmp + 1; + } + if (ffStrEquals(plainName, connName)) + { + ffStrbufAppendF(edidName, "%s%s/edid", drmDirPath, entry->d_name); + + uint8_t edidData[128]; + if(ffReadFileData(edidName->chars, sizeof(edidData), edidData) == sizeof(edidData)) + { + ffStrbufClear(edidName); + ffEdidGetName(edidData, edidName); + closedir(dirp); + return true; + } + break; + } + } + ffStrbufClear(edidName); + closedir(dirp); + return false; +} diff --git a/src/detection/displayserver/linux/displayserver_linux.h b/src/detection/displayserver/linux/displayserver_linux.h index cefc8b55ad..4ad1928e60 100644 --- a/src/detection/displayserver/linux/displayserver_linux.h +++ b/src/detection/displayserver/linux/displayserver_linux.h @@ -5,6 +5,9 @@ #include "detection/displayserver/displayserver.h" +bool ffdsMatchDrmConnector(const char* connName, FFstrbuf* edidName); + +const char* ffdsConnectWlroots(FFDisplayServerResult* result); void ffdsConnectWayland(FFDisplayServerResult* result); void ffdsConnectXcbRandr(FFDisplayServerResult* result); diff --git a/src/detection/displayserver/linux/wayland.c b/src/detection/displayserver/linux/wayland.c index 3678e118cc..f987036170 100644 --- a/src/detection/displayserver/linux/wayland.c +++ b/src/detection/displayserver/linux/wayland.c @@ -8,7 +8,7 @@ #include "common/library.h" #include "common/io/io.h" #include "common/thread.h" -#include "util/edidHelper.h" + #include #include #include @@ -103,46 +103,6 @@ static void waylandOutputGeometryListener(void *data, } } -static bool matchDrmConnector(const char* wlName, FFstrbuf* edidName) -{ - // https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_output-event-name - // The doc says that "do not assume that the name is a reflection of an underlying DRM connector, X11 connection, etc." - // However I can't find a better method to get the edid data - const char* drmDirPath = "/sys/class/drm/"; - - DIR* dirp = opendir(drmDirPath); - if(dirp == NULL) - return false; - - struct dirent* entry; - while((entry = readdir(dirp)) != NULL) - { - const char* plainName = entry->d_name; - if (ffStrStartsWith(plainName, "card")) - { - const char* tmp = strchr(plainName + strlen("card"), '-'); - if (tmp) plainName = tmp + 1; - } - if (ffStrEquals(plainName, wlName)) - { - ffStrbufAppendF(edidName, "%s%s/edid", drmDirPath, entry->d_name); - - uint8_t edidData[128]; - if(ffReadFileData(edidName->chars, sizeof(edidData), edidData) == sizeof(edidData)) - { - ffStrbufClear(edidName); - ffEdidGetName(edidData, edidName); - closedir(dirp); - return true; - } - break; - } - } - ffStrbufClear(edidName); - closedir(dirp); - return false; -} - static void waylandOutputNameListener(void *data, FF_MAYBE_UNUSED struct wl_output *output, const char *name) { WaylandDisplay* display = data; @@ -150,7 +110,7 @@ static void waylandOutputNameListener(void *data, FF_MAYBE_UNUSED struct wl_outp display->type = FF_DISPLAY_TYPE_BUILTIN; else if(ffStrStartsWith(name, "HDMI-")) display->type = FF_DISPLAY_TYPE_EXTERNAL; - matchDrmConnector(name, &display->edidName); + ffdsMatchDrmConnector(name, &display->edidName); ffStrbufAppendS(&display->name, name); } diff --git a/src/detection/displayserver/linux/wlroots.c b/src/detection/displayserver/linux/wlroots.c new file mode 100644 index 0000000000..19fc21374a --- /dev/null +++ b/src/detection/displayserver/linux/wlroots.c @@ -0,0 +1,105 @@ +#include "displayserver_linux.h" +#include "util/stringUtils.h" +#include "common/processing.h" + +static inline void wrapYyjsonFree(yyjson_doc** doc) +{ + assert(doc); + if (*doc) + yyjson_doc_free(*doc); +} + +const char* ffdsConnectWlroots(FFDisplayServerResult* result) +{ + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffProcessAppendStdOut(&buffer, (char* const[]){ + "wlr-randr", + "--json", + NULL + }) != NULL || buffer.length == 0) + return "Running wlr-randr failed"; + + yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(buffer.chars, buffer.length, 0, NULL, NULL); + if (!doc) + return "Failed to parse wlr-randr info"; + + yyjson_val* root = yyjson_doc_get_root(doc); + if (!yyjson_is_arr(root)) + return "Battery info result is not a JSON array"; + + yyjson_val* device; + size_t idev, mdev; + yyjson_arr_foreach(root, idev, mdev, device) + { + yyjson_val* modes = yyjson_obj_get(device, "modes"); + if (!yyjson_is_arr(modes)) + continue; + + if (!yyjson_get_bool(yyjson_obj_get(device, "enabled"))) + continue; + + yyjson_val* mode; + size_t imode, mmode; + yyjson_arr_foreach(modes, imode, mmode, mode) + { + if (!yyjson_is_obj(mode)) + continue; + + if (!yyjson_get_bool(yyjson_obj_get(mode, "current"))) + continue; + + uint32_t width = (uint32_t) yyjson_get_uint(yyjson_obj_get(mode, "width")); + uint32_t height = (uint32_t) yyjson_get_uint(yyjson_obj_get(mode, "height")); + + if (width == 0 || height == 0) + continue; + + double refreshRate = yyjson_get_real(yyjson_obj_get(mode, "refresh")); + + double scale = (double) yyjson_get_real(yyjson_obj_get(device, "scale")); + const char* connName = yyjson_get_str(yyjson_obj_get(device, "name")); + FFDisplayType type = FF_DISPLAY_TYPE_UNKNOWN; + if(ffStrStartsWith(connName, "eDP-")) + type = FF_DISPLAY_TYPE_BUILTIN; + else if(ffStrStartsWith(connName, "HDMI-")) + type = FF_DISPLAY_TYPE_EXTERNAL; + + FF_STRBUF_AUTO_DESTROY displayName = ffStrbufCreate(); + if (!ffdsMatchDrmConnector(connName, &displayName)) + ffStrbufSetS(&displayName, yyjson_get_str(yyjson_obj_get(device, "description"))); + uint32_t rotation = 0; + const char* transform = yyjson_get_str(yyjson_obj_get(device, "transform")); + if (!ffStrEquals(transform, "normal")) + { + // 90 | flipped-90 + if (ffStrEndsWith(transform, "90")) + rotation = 90; + else if (ffStrEndsWith(transform, "180")) + rotation = 180; + else if (ffStrEndsWith(transform, "270")) + rotation = 270; + } + if (rotation % 180 != 0) + { + uint32_t temp = width; + width = height; + height = temp; + } + ffdsAppendDisplay(result, + width, + height, + refreshRate, + (uint32_t) (width / scale), + (uint32_t) (height / scale), + rotation, + &displayName, + type, + false, + 0 + ); + break; + } + } + + return NULL; +} From 095149a4e9dadfe7b23c288f8a17560d1da6a9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Apr 2024 23:18:32 +0800 Subject: [PATCH 13/40] TerminalShell (Windows): fix compiling --- src/detection/terminalshell/terminalshell.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 6c4456cbe6..7909fef269 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -469,6 +469,7 @@ static bool getTerminalVersionZellij(FFstrbuf* exe, FFstrbuf* version) return version->length > 0; } +#ifndef _WIN32 static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) { // kitty is written in python. `kitty --version` can be expensive @@ -494,6 +495,7 @@ static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) //kitty 0.21.2 created by Kovid Goyal return getExeVersionGeneral(exe, version); } +#endif #ifdef _WIN32 From 08cbba1d9dfc7472c6444fa2d048224b411fb6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 08:53:53 +0800 Subject: [PATCH 14/40] Terminal (Linux): optimise kitty version detection --- src/detection/terminalshell/terminalshell.c | 29 ++++++++++++--------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 7909fef269..af661a30c6 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -472,25 +472,30 @@ static bool getTerminalVersionZellij(FFstrbuf* exe, FFstrbuf* version) #ifndef _WIN32 static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) { + #ifdef __linux__ // kitty is written in python. `kitty --version` can be expensive - char buffer[1024] = {}; - if (ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) || - ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer)) + if (ffStrbufStartsWithS(exe, FASTFETCH_TARGET_DIR_USR "/bin/")) { - // Starts from version 0.17.0 - // https://github.com/kovidgoyal/kitty/blob/master/kitty/constants.py#L25 - const char* p = memmem(buffer, sizeof(buffer) - 1, "version: Version = Version(", strlen("version: Version = Version(")); - if (p) + char buffer[1024] = {}; + if (ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) || + ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer)) { - p += strlen("version: Version = Version("); - int major, minor, patch; - if (sscanf(p, "%d,%d,%d", &major, &minor, &patch) == 3) + // Starts from version 0.17.0 + // https://github.com/kovidgoyal/kitty/blob/master/kitty/constants.py#L25 + const char* p = memmem(buffer, sizeof(buffer) - 1, "version: Version = Version(", strlen("version: Version = Version(")); + if (p) { - ffStrbufSetF(version, "%d.%d.%d", major, minor, patch); - return true; + p += strlen("version: Version = Version("); + int major, minor, patch; + if (sscanf(p, "%d,%d,%d", &major, &minor, &patch) == 3) + { + ffStrbufSetF(version, "%d.%d.%d", major, minor, patch); + return true; + } } } } + #endif //kitty 0.21.2 created by Kovid Goyal return getExeVersionGeneral(exe, version); From 9860fb67d408dcdfa3cdefd3c6fbc59aa7ec44df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 09:22:08 +0800 Subject: [PATCH 15/40] TerminalShell (Linux): if `/bin/sh` is set to default shell, use it Fix #798 --- .../terminalshell/terminalshell_linux.c | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 6d46a552a7..e6e28794cd 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -214,31 +214,43 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid) pid_t ppid = 0; int32_t tty = -1; + const char* userShellName = NULL; + { + uint32_t index = ffStrbufLastIndexC(&instance.state.platform.userShell, '/'); + if (index == instance.state.platform.userShell.length) + userShellName = instance.state.platform.userShell.chars; + else + userShellName = instance.state.platform.userShell.chars + index + 1; + } + while (getProcessNameAndPpid(pid, name, &ppid, &tty) == NULL) { - //Common programs that are between terminal and own process, but are not the shell - if( - // tty < 0 || //A shell should connect to a tty - ffStrEquals(name, "sh") || //This prevents us from detecting things like pipes and redirects, i hope nobody uses plain `sh` as shell - ffStrEquals(name, "sudo") || - ffStrEquals(name, "su") || - ffStrEquals(name, "strace") || - ffStrEquals(name, "sshd") || - ffStrEquals(name, "gdb") || - ffStrEquals(name, "lldb") || - ffStrEquals(name, "lldb-mi") || - ffStrEquals(name, "login") || - ffStrEquals(name, "ltrace") || - ffStrEquals(name, "perf") || - ffStrEquals(name, "guake-wrapped") || - ffStrEquals(name, "time") || - ffStrContainsIgnCase(name, "debug") || - ffStrContainsIgnCase(name, "not-found") || - ffStrEndsWith(name, ".sh") - ) + if (!ffStrEquals(userShellName, name)) { - pid = ppid; - continue; + //Common programs that are between terminal and own process, but are not the shell + if( + // tty < 0 || //A shell should connect to a tty + ffStrEquals(name, "sh") || //This prevents us from detecting things like pipes and redirects, i hope nobody uses plain `sh` as shell + ffStrEquals(name, "sudo") || + ffStrEquals(name, "su") || + ffStrEquals(name, "strace") || + ffStrEquals(name, "sshd") || + ffStrEquals(name, "gdb") || + ffStrEquals(name, "lldb") || + ffStrEquals(name, "lldb-mi") || + ffStrEquals(name, "login") || + ffStrEquals(name, "ltrace") || + ffStrEquals(name, "perf") || + ffStrEquals(name, "guake-wrapped") || + ffStrEquals(name, "time") || + ffStrContainsIgnCase(name, "debug") || + ffStrContainsIgnCase(name, "not-found") || + ffStrEndsWith(name, ".sh") + ) + { + pid = ppid; + continue; + } } result->pid = (uint32_t) pid; From 64ec76f5a67ddc836d3d49d7800d27ce7eb20d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 09:52:55 +0800 Subject: [PATCH 16/40] Display (Linux): detect refresh rate for DRM method --- src/detection/displayserver/linux/drm.c | 59 ++++++++++++------------- src/util/edidHelper.c | 19 ++++++++ src/util/edidHelper.h | 1 + 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/detection/displayserver/linux/drm.c b/src/detection/displayserver/linux/drm.c index f76df154b2..3710f1db27 100644 --- a/src/detection/displayserver/linux/drm.c +++ b/src/detection/displayserver/linux/drm.c @@ -36,31 +36,28 @@ static const char* drmParseSysfs(FFDisplayServerResult* result) continue; } + unsigned width = 0, height = 0; + double refreshRate = 0; + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate(); + ffStrbufSubstrBefore(&drmDir, drmDirWithDnameLength); - ffStrbufAppendS(&drmDir, "/modes"); + ffStrbufAppendS(&drmDir, "/edid"); - char modes[32]; - if (ffReadFileData(drmDir.chars, sizeof(modes), modes) < 3) + uint8_t edidData[128]; + if(ffReadFileData(drmDir.chars, sizeof(edidData), edidData) == sizeof(edidData)) { - ffStrbufSubstrBefore(&drmDir, drmDirLength); - continue; + ffEdidGetName(edidData, &name); + ffEdidGetPreferredResolutionAndRefreshRate(edidData, &width, &height, &refreshRate); } - - unsigned width = 0, height = 0; - - int scanned = sscanf(modes, "%ux%u", &width, &height); - if(scanned == 2 && width > 0 && height > 0) + else { - ffStrbufSubstrBefore(&drmDir, drmDirLength); - ffStrbufAppendS(&drmDir, entry->d_name); - ffStrbufAppendS(&drmDir, "/edid"); - - FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate(); - uint8_t edidData[128]; - if(ffReadFileData(drmDir.chars, sizeof(edidData), edidData) == sizeof(edidData)) - ffEdidGetName(edidData, &name); - else + ffStrbufSubstrBefore(&drmDir, drmDirWithDnameLength); + ffStrbufAppendS(&drmDir, "/modes"); + + char modes[32]; + if (ffReadFileData(drmDir.chars, sizeof(modes), modes) >= 3) { + sscanf(modes, "%ux%u", &width, &height); const char* plainName = entry->d_name; if (ffStrStartsWith(plainName, "card")) { @@ -69,20 +66,20 @@ static const char* drmParseSysfs(FFDisplayServerResult* result) } ffStrbufAppendS(&name, plainName); } - - ffdsAppendDisplay( - result, - width, height, - 0, - 0, 0, - 0, - &name, - FF_DISPLAY_TYPE_UNKNOWN, - false, - 0 - ); } + ffdsAppendDisplay( + result, + width, height, + refreshRate, + 0, 0, + 0, + &name, + FF_DISPLAY_TYPE_UNKNOWN, + false, + 0 + ); + ffStrbufSubstrBefore(&drmDir, drmDirLength); } diff --git a/src/util/edidHelper.c b/src/util/edidHelper.c index 5fa2c662ba..09931d22da 100644 --- a/src/util/edidHelper.c +++ b/src/util/edidHelper.c @@ -7,6 +7,25 @@ void ffEdidGetPhysicalResolution(const uint8_t edid[128], uint32_t* width, uint3 *height = (((uint32_t) edid[dtd + 7] >> 4) << 8) | edid[dtd + 5]; } +void ffEdidGetPreferredResolutionAndRefreshRate(const uint8_t edid[128], uint32_t* width, uint32_t* height, double* refreshRate) +{ + for (uint32_t i = 0x36; i < 0x7E; i += 0x12) + { // read through descriptor blocks... + if (edid[i] != 0x00 && edid[i + 1] != 0x00) + { // a dtd + uint32_t hactive = edid[i+2] + ((edid[i + 4] & 0xf0) << 4); + uint32_t hblank = edid[i+3] + ((edid[i + 4] & 0x0f) << 8); + uint32_t vactive = edid[i+5] + ((edid[i + 7] & 0xf0) << 4); + uint32_t vblank = edid[i+6] + ((edid[i + 7] & 0x0f) << 8); + uint32_t pixclk = (edid[i+1] << 8) | (edid[i]); + *width = hactive; + *height = vactive; + *refreshRate = (double)pixclk * 10000 / (double)(hactive+hblank) / (double)(vactive+vblank); + return; + } + } +} + void ffEdidGetVendorAndModel(const uint8_t edid[128], FFstrbuf* result) { // https://github.com/jinksong/read_edid/blob/master/parse-edid/parse-edid.c diff --git a/src/util/edidHelper.h b/src/util/edidHelper.h index 6e768ddf9a..cad9e6ce92 100644 --- a/src/util/edidHelper.h +++ b/src/util/edidHelper.h @@ -5,6 +5,7 @@ void ffEdidGetVendorAndModel(const uint8_t edid[128], FFstrbuf* result); bool ffEdidGetName(const uint8_t edid[128], FFstrbuf* name); +void ffEdidGetPreferredResolutionAndRefreshRate(const uint8_t edid[128], uint32_t* width, uint32_t* height, double* refreshRate); void ffEdidGetPhysicalResolution(const uint8_t edid[128], uint32_t* width, uint32_t* height); void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height); // in mm void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week); From 180cbce3a23844fbaa9845bd78817584feb79a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 14:16:08 +0800 Subject: [PATCH 17/40] Doc: update changelog --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c5026de20..19115fe7b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 2.10.0 + +Features: +* We now use `wlr-randr` to detect displays for hyprland, which correctly reports fractional scale factors. Experimental (Display, Linux) +* Support GPU memory usage detection for AMD GPUs (GPU, Linux) +* Improve performance of Gnome version detection (DE, Linux) +* Improve performance of kitty version detection (Terminal, Linux) +* Detect refresh rate when using `--ds-force-drm sysfs-only` (Display, Linux) + +Bugfixes: +* Correctly detect `/bin/sh` as current shell if it's used as default shell (Shell, Linux) + # 2.9.2 Changes: From ef440a015d0a03d2739a92e6fac31c4a2890a599 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 18 Apr 2024 15:18:37 +0800 Subject: [PATCH 18/40] Display (Linux): don't display `(null)` --- src/detection/displayserver/linux/wlroots.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/detection/displayserver/linux/wlroots.c b/src/detection/displayserver/linux/wlroots.c index 19fc21374a..dadb9c4c3c 100644 --- a/src/detection/displayserver/linux/wlroots.c +++ b/src/detection/displayserver/linux/wlroots.c @@ -66,7 +66,13 @@ const char* ffdsConnectWlroots(FFDisplayServerResult* result) FF_STRBUF_AUTO_DESTROY displayName = ffStrbufCreate(); if (!ffdsMatchDrmConnector(connName, &displayName)) - ffStrbufSetS(&displayName, yyjson_get_str(yyjson_obj_get(device, "description"))); + { + const char* desc = yyjson_get_str(yyjson_obj_get(device, "description")); + if (ffStrContains(desc, "(null)")) + ffStrbufSetS(&displayName, connName); + else + ffStrbufSetS(&displayName, desc); + } uint32_t rotation = 0; const char* transform = yyjson_get_str(yyjson_obj_get(device, "transform")); if (!ffStrEquals(transform, "normal")) From 41df915f29c10442e5937047dc8c6365782cb9f7 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 18 Apr 2024 15:22:13 +0800 Subject: [PATCH 19/40] Terminal (Linux): make fast path of kitty version detection actually work --- src/detection/terminalshell/terminalshell.c | 27 +++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index af661a30c6..1545721799 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -474,24 +474,21 @@ static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) { #ifdef __linux__ // kitty is written in python. `kitty --version` can be expensive - if (ffStrbufStartsWithS(exe, FASTFETCH_TARGET_DIR_USR "/bin/")) + char buffer[1024] = {}; + if (ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) || + ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer)) { - char buffer[1024] = {}; - if (ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) || - ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer)) + // Starts from version 0.17.0 + // https://github.com/kovidgoyal/kitty/blob/master/kitty/constants.py#L25 + const char* p = memmem(buffer, sizeof(buffer) - 1, "version: Version = Version(", strlen("version: Version = Version(")); + if (p) { - // Starts from version 0.17.0 - // https://github.com/kovidgoyal/kitty/blob/master/kitty/constants.py#L25 - const char* p = memmem(buffer, sizeof(buffer) - 1, "version: Version = Version(", strlen("version: Version = Version(")); - if (p) + p += strlen("version: Version = Version("); + int major, minor, patch; + if (sscanf(p, "%d,%d,%d", &major, &minor, &patch) == 3) { - p += strlen("version: Version = Version("); - int major, minor, patch; - if (sscanf(p, "%d,%d,%d", &major, &minor, &patch) == 3) - { - ffStrbufSetF(version, "%d.%d.%d", major, minor, patch); - return true; - } + ffStrbufSetF(version, "%d.%d.%d", major, minor, patch); + return true; } } } From f8cbe4c0082139ce8e50622d97a7d59040e6c63c Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 18 Apr 2024 16:41:19 +0800 Subject: [PATCH 20/40] GPU (Linux): improve detection performance of Apple Silicon --- src/detection/cpu/cpu.c | 21 +++ src/detection/cpu/cpu.h | 1 + src/detection/cpu/cpu_linux.c | 19 +-- src/detection/gpu/gpu_linux.c | 261 +++++++++++++++++++++------------- 4 files changed, 188 insertions(+), 114 deletions(-) diff --git a/src/detection/cpu/cpu.c b/src/detection/cpu/cpu.c index 5edc2f4340..68d23ac885 100644 --- a/src/detection/cpu/cpu.c +++ b/src/detection/cpu/cpu.c @@ -18,3 +18,24 @@ const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu) ffStrbufTrimRight(&cpu->name, ' '); //If we removed the @ in previous step there was most likely a space before it return NULL; } + +const char* ffCPUAppleCodeToName(uint32_t code) +{ + // https://github.com/AsahiLinux/docs/wiki/Codenames + switch (code) + { + case 8103: return "Apple M1"; + case 6000: return "Apple M1 Pro"; + case 6001: return "Apple M1 Max"; + case 6002: return "Apple M1 Ultra"; + case 8112: return "Apple M2"; + case 6020: return "Apple M2 Pro"; + case 6021: return "Apple M2 Max"; + case 6022: return "Apple M2 Ultra"; + case 8122: return "Apple M3"; + case 6030: return "Apple M3 Pro"; + case 6031: + case 6034: return "Apple M3 Max"; + default: return "Apple Silicon"; + } +} diff --git a/src/detection/cpu/cpu.h b/src/detection/cpu/cpu.h index 628698e4b0..bba2aba472 100644 --- a/src/detection/cpu/cpu.h +++ b/src/detection/cpu/cpu.h @@ -22,3 +22,4 @@ typedef struct FFCPUResult const char* ffCPUDetectByCpuid(FFCPUResult* cpu); const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu); +const char* ffCPUAppleCodeToName(uint32_t code); diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index acce51ca8a..e100eb60c2 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -210,23 +210,8 @@ void detectAsahi(FFCPUResult* cpu) char* modelName = memchr(content, '\0', (size_t) length) + 1; if (modelName - content < length && ffStrStartsWith(modelName, "apple,t")) { - // https://github.com/AsahiLinux/docs/wiki/Codenames - switch (strtoul(modelName + strlen("apple,t"), NULL, 10)) - { - case 8103: ffStrbufSetStatic(&cpu->name, "Apple M1"); break; - case 6000: ffStrbufSetStatic(&cpu->name, "Apple M1 Pro"); break; - case 6001: ffStrbufSetStatic(&cpu->name, "Apple M1 Max"); break; - case 6002: ffStrbufSetStatic(&cpu->name, "Apple M1 Ultra"); break; - case 8112: ffStrbufSetStatic(&cpu->name, "Apple M2"); break; - case 6020: ffStrbufSetStatic(&cpu->name, "Apple M2 Pro"); break; - case 6021: ffStrbufSetStatic(&cpu->name, "Apple M2 Max"); break; - case 6022: ffStrbufSetStatic(&cpu->name, "Apple M2 Ultra"); break; - case 8122: ffStrbufSetStatic(&cpu->name, "Apple M3"); break; - case 6030: ffStrbufSetStatic(&cpu->name, "Apple M3 Pro"); break; - case 6031: - case 6034: ffStrbufSetStatic(&cpu->name, "Apple M3 Max"); break; - default: ffStrbufSetStatic(&cpu->name, "Apple Silicon"); break; - } + uint32_t deviceId = (uint32_t) strtoul(modelName + strlen("apple,t"), NULL, 10); + ffStrbufSetStatic(&cpu->name, ffCPUAppleCodeToName(deviceId)); } } } diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 27a7d64a00..cba6aa3f8d 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -1,6 +1,7 @@ #include "detection/gpu/gpu.h" #include "detection/vulkan/vulkan.h" #include "detection/temps/temps_linux.h" +#include "detection/cpu/cpu.h" #include "common/io/io.h" #include "common/properties.h" #include "util/stringUtils.h" @@ -57,6 +58,7 @@ static void pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer static void pciDetectVmem(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) { + // Works for AMD GPUs // https://www.kernel.org/doc/html/v5.10/gpu/amdgpu.html#mem-info-vis-vram-total ffStrbufAppendS(pciDir, "/mem_info_vis_vram_total"); uint64_t size = 0; @@ -80,6 +82,21 @@ static void pciDetectVmem(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) } } +static void pciDetectVfreq(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) +{ + // Works for Intel GPUs + // https://patchwork.kernel.org/project/intel-gfx/patch/1422039866-11572-3-git-send-email-ville.syrjala@linux.intel.com/ + ffStrbufSetNS(buffer, ffStrbufLastIndexC(pciDir, '/'), pciDir->chars); + ffStrbufAppendS(buffer, "/gt_cur_freq_mhz"); + char str[16]; + ssize_t len = ffReadFileData(buffer->chars, sizeof(str) - 1, str); + if (len > 1) + { + str[len] = '\0'; + gpu->frequency = (double) strtoul(str, NULL, 10) / 1000.0; + } +} + static bool loadPciIds(FFstrbuf* pciids) { #ifdef FF_CUSTOM_PCI_IDS_PATH @@ -99,121 +116,171 @@ static bool loadPciIds(FFstrbuf* pciids) return false; } -static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) +static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir) { - //https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci - const char* pciDirPath = "/sys/bus/pci/devices/"; + const uint32_t drmDirPathLength = drmDir->length; + uint32_t vendorId, deviceId, subVendorId, subDeviceId; + uint8_t classId, subclassId; + if (sscanf(buffer->chars + strlen("pci:"), "v%8" SCNx32 "d%8" SCNx32 "sv%8" SCNx32 "sd%8" SCNx32 "bc%2" SCNx8 "sc%2" SCNx8, &vendorId, &deviceId, &subVendorId, &subDeviceId, &classId, &subclassId) != 6) + return "Invalid modalias string"; + + if (classId != 0x03 /*PCI_BASE_CLASS_DISPLAY*/) + return "Should not happen"; + + char pciPath[PATH_MAX]; + ssize_t pathLength = readlink(drmDir->chars, pciPath, sizeof(pciPath) - 1); + if (pathLength <= 0) + return "Unable to get PCI device path"; + pciPath[pathLength] = '\0'; + const char* pPciPath = strrchr(pciPath, '/'); + if (pPciPath) + pPciPath++; + else + pPciPath = pciPath; + + uint32_t pciDomain, pciBus, pciDevice, pciFunc; + if (sscanf(pPciPath, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4) + return "Invalid PCI device path"; + + FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); + ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString((uint16_t) vendorId)); + ffStrbufInit(&gpu->name); + ffStrbufInit(&gpu->driver); + ffStrbufInit(&gpu->platformApi); + gpu->temperature = FF_GPU_TEMP_UNSET; + gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->type = FF_GPU_TYPE_UNKNOWN; + gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; + gpu->deviceId = ((uint64_t) pciDomain << 6) | ((uint64_t) pciBus << 4) | (deviceId << 2) | pciFunc; + gpu->frequency = FF_GPU_FREQUENCY_UNSET; + + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) + { + ffStrbufAppendS(drmDir, "/revision"); + if (ffReadFileBuffer(drmDir->chars, buffer)) + { + char* pend; + uint64_t revision = strtoul(buffer->chars, &pend, 16); + if (pend != buffer->chars) + { + char query[32]; + snprintf(query, sizeof(query), "%X,\t%X,", (unsigned) deviceId, (unsigned) revision); + ffParsePropFileData("libdrm/amdgpu.ids", query, &gpu->name); + } + } + ffStrbufSubstrBefore(drmDir, drmDirPathLength); + } - FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDirPath); - if(dirp == NULL) - return "Failed to open `/sys/bus/pci/devices/`"; + if (gpu->name.length == 0) + { + static FFstrbuf pciids; + if (pciids.chars == NULL) + { + ffStrbufInit(&pciids); + loadPciIds(&pciids); + } + if (pciids.length) + ffGPUParsePciIds(&pciids, subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); + } - FF_STRBUF_AUTO_DESTROY pciDir = ffStrbufCreateA(64); - ffStrbufAppendS(&pciDir, pciDirPath); + pciDetectVmem(gpu, drmDir, buffer); + ffStrbufSubstrBefore(drmDir, drmDirPathLength); - const uint32_t pciBaseDirLength = pciDir.length; + pciDetectVfreq(gpu, drmDir, buffer); + ffStrbufSubstrBefore(drmDir, drmDirPathLength); - FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); - FF_STRBUF_AUTO_DESTROY pciids = ffStrbufCreate(); + pciDetectDriver(gpu, drmDir, buffer); + ffStrbufSubstrBefore(drmDir, drmDirPathLength); - struct dirent* entry; - while((entry = readdir(dirp)) != NULL) + #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && (options->temp || options->driverSpecific)) { - if(entry->d_name[0] == '.') - continue; - - ffStrbufSubstrBefore(&pciDir, pciBaseDirLength); - ffStrbufAppendS(&pciDir, entry->d_name); + ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { + .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, + .pciBusId = { + .domain = pciDomain, + .bus = pciBus, + .device = pciDevice, + .func = pciFunc, + }, + }, (FFGpuDriverResult) { + .temp = options->temp ? &gpu->temperature : NULL, + .memory = options->driverSpecific ? &gpu->dedicated : NULL, + .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, + .type = &gpu->type, + .frequency = &gpu->frequency, + }, "libnvidia-ml.so"); + + if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) + gpu->type = gpu->dedicated.total > (uint64_t)1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + } + #endif // FF_USE_PROPRIETARY_GPU_DRIVER_API - const uint32_t pciDevDirLength = pciDir.length; + #ifdef __linux__ + if(options->temp && gpu->temperature != gpu->temperature) + pciDetectTemp(gpu, ((uint32_t) classId << 8) + subclassId); + #endif - ffStrbufAppendS(&pciDir, "/modalias"); - if (!ffReadFileBuffer(pciDir.chars, &buffer)) - continue; - ffStrbufSubstrBefore(&pciDir, pciDevDirLength); + return NULL; +} - uint32_t vendorId, deviceId, subVendorId, subDeviceId; - uint8_t classId, subclassId; - if (sscanf(buffer.chars, "pci:v%8" SCNx32 "d%8" SCNx32 "sv%8" SCNx32 "sd%8" SCNx32 "bc%2" SCNx8 "sc%2" SCNx8, &vendorId, &deviceId, &subVendorId, &subDeviceId, &classId, &subclassId) != 6) - continue; +FF_MAYBE_UNUSED static const char* detectAsahi(FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir) +{ + uint32_t index = ffStrbufFirstIndexS(buffer, "apple,agx-t"); + if (index == buffer->length) return "display-subsystem?"; + index += strlen("apple,agx-t"); + + FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); + gpu->deviceId = strtoul(buffer->chars + index, NULL, 10); + ffStrbufInitStatic(&gpu->name, ffCPUAppleCodeToName(gpu->deviceId)); + ffStrbufInitStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE); + ffStrbufInit(&gpu->driver); + ffStrbufInit(&gpu->platformApi); + gpu->temperature = FF_GPU_TEMP_UNSET; + gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->type = FF_GPU_TYPE_INTEGRATED; + gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; + gpu->frequency = FF_GPU_FREQUENCY_UNSET; + + pciDetectDriver(gpu, drmDir, buffer); - if (classId != 0x03 /*PCI_BASE_CLASS_DISPLAY*/) - continue; + return NULL; +} - uint32_t pciDomain, pciBus, pciDevice, pciFunc; - if (sscanf(entry->d_name, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4) - continue; +static const char* drmDetectGPUs(const FFGPUOptions* options, FFlist* gpus) +{ + FF_STRBUF_AUTO_DESTROY drmDir = ffStrbufCreateA(64); + ffStrbufAppendS(&drmDir, "/sys/class/drm/"); + const uint32_t drmDirLength = drmDir.length; - FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); - ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString((uint16_t) vendorId)); - ffStrbufInit(&gpu->name); - ffStrbufInit(&gpu->driver); - ffStrbufInit(&gpu->platformApi); - gpu->temperature = FF_GPU_TEMP_UNSET; - gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; - gpu->type = FF_GPU_TYPE_UNKNOWN; - gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; - gpu->deviceId = ((uint64_t) pciDomain << 6) | ((uint64_t) pciBus << 4) | (deviceId << 2) | pciFunc; - gpu->frequency = FF_GPU_FREQUENCY_UNSET; - - if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) - { - ffStrbufAppendS(&pciDir, "/revision"); - if (ffReadFileBuffer(pciDir.chars, &buffer)) - { - char* pend; - uint64_t revision = strtoul(buffer.chars, &pend, 16); - if (pend != buffer.chars) - { - char query[32]; - snprintf(query, sizeof(query), "%X,\t%X,", (unsigned) deviceId, (unsigned) revision); - ffParsePropFileData("libdrm/amdgpu.ids", query, &gpu->name); - } - } - ffStrbufSubstrBefore(&pciDir, pciDevDirLength); - } + FF_AUTO_CLOSE_DIR DIR* dir = opendir(drmDir.chars); + if(dir == NULL) + return "/sys/class/drm doesn't exist"; - if (gpu->name.length == 0) - { - if (!pciids.length) - loadPciIds(&pciids); - ffGPUParsePciIds(&pciids, subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); - } + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); - pciDetectVmem(gpu, &pciDir, &buffer); - ffStrbufSubstrBefore(&pciDir, pciDevDirLength); + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) + { + if (!ffStrStartsWith(entry->d_name, "card") || + strchr(entry->d_name + 4, '-') != NULL) + continue; - pciDetectDriver(gpu, &pciDir, &buffer); - ffStrbufSubstrBefore(&pciDir, pciDevDirLength); + ffStrbufAppendS(&drmDir, entry->d_name); - #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API - if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && (options->temp || options->driverSpecific)) - { - ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { - .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, - .pciBusId = { - .domain = pciDomain, - .bus = pciBus, - .device = pciDevice, - .func = pciFunc, - }, - }, (FFGpuDriverResult) { - .temp = options->temp ? &gpu->temperature : NULL, - .memory = options->driverSpecific ? &gpu->dedicated : NULL, - .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, - .type = &gpu->type, - .frequency = &gpu->frequency, - }, "libnvidia-ml.so"); - - if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) - gpu->type = gpu->dedicated.total > (uint64_t)1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; - } - #endif // FF_USE_PROPRIETARY_GPU_DRIVER_API + ffStrbufAppendS(&drmDir, "/device/modalias"); + if (!ffReadFileBuffer(drmDir.chars, &buffer)) + continue; + ffStrbufSubstrBefore(&drmDir, drmDir.length - (uint32_t) strlen("/modalias")); - #ifdef __linux__ - if(options->temp && gpu->temperature != gpu->temperature) - pciDetectTemp(gpu, ((uint32_t) classId << 8) + subclassId); + if (ffStrbufStartsWithS(&buffer, "pci:")) + detectPci(options, gpus, &buffer, &drmDir); + #ifdef __aarch64__ + else if (ffStrbufStartsWithS(&buffer, "of:")) + detectAsahi(gpus, &buffer, &drmDir); #endif + + ffStrbufSubstrBefore(&drmDir, drmDirLength); } return NULL; @@ -227,5 +294,5 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) return NULL; #endif - return pciDetectGPUs(options, gpus); + return drmDetectGPUs(options, gpus); } From 5f7b9d247345184242f39bd81b9bad1d62e667a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 21:00:47 +0800 Subject: [PATCH 21/40] GPU (Linux): bring back `pciDetectGPUs` --- src/detection/gpu/gpu_linux.c | 60 +++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index cba6aa3f8d..f4ab7ead42 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -84,10 +84,13 @@ static void pciDetectVmem(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) static void pciDetectVfreq(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) { + if (ffStrbufEndsWithS(pciDir, "/device")) // Must be in `/sys/class/drm/cardN/device` + return; + // Works for Intel GPUs // https://patchwork.kernel.org/project/intel-gfx/patch/1422039866-11572-3-git-send-email-ville.syrjala@linux.intel.com/ - ffStrbufSetNS(buffer, ffStrbufLastIndexC(pciDir, '/'), pciDir->chars); - ffStrbufAppendS(buffer, "/gt_cur_freq_mhz"); + ffStrbufSetNS(buffer, pciDir->length - (uint32_t) strlen("device"), pciDir->chars); + ffStrbufAppendS(buffer, "gt_cur_freq_mhz"); char str[16]; ssize_t len = ffReadFileData(buffer->chars, sizeof(str) - 1, str); if (len > 1) @@ -125,7 +128,7 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf return "Invalid modalias string"; if (classId != 0x03 /*PCI_BASE_CLASS_DISPLAY*/) - return "Should not happen"; + return "Not a GPU device"; char pciPath[PATH_MAX]; ssize_t pathLength = readlink(drmDir->chars, pciPath, sizeof(pciPath) - 1); @@ -232,7 +235,7 @@ FF_MAYBE_UNUSED static const char* detectAsahi(FFlist* gpus, FFstrbuf* buffer, F FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); gpu->deviceId = strtoul(buffer->chars + index, NULL, 10); - ffStrbufInitStatic(&gpu->name, ffCPUAppleCodeToName(gpu->deviceId)); + ffStrbufInitStatic(&gpu->name, ffCPUAppleCodeToName((uint32_t) gpu->deviceId)); ffStrbufInitStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE); ffStrbufInit(&gpu->driver); ffStrbufInit(&gpu->platformApi); @@ -255,7 +258,7 @@ static const char* drmDetectGPUs(const FFGPUOptions* options, FFlist* gpus) FF_AUTO_CLOSE_DIR DIR* dir = opendir(drmDir.chars); if(dir == NULL) - return "/sys/class/drm doesn't exist"; + return "Failed to open `/sys/class/drm/`"; FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); @@ -286,13 +289,56 @@ static const char* drmDetectGPUs(const FFGPUOptions* options, FFlist* gpus) return NULL; } + +static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) +{ + //https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci + const char* pciDirPath = "/sys/bus/pci/devices/"; + + FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDirPath); + if(dirp == NULL) + return "Failed to open `/sys/bus/pci/devices/`"; + + FF_STRBUF_AUTO_DESTROY pciDir = ffStrbufCreateA(64); + ffStrbufAppendS(&pciDir, pciDirPath); + + const uint32_t pciBaseDirLength = pciDir.length; + + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) + { + if(entry->d_name[0] == '.') + continue; + + ffStrbufSubstrBefore(&pciDir, pciBaseDirLength); + ffStrbufAppendS(&pciDir, entry->d_name); + const uint32_t pciDevDirLength = pciDir.length; + + ffStrbufAppendS(&pciDir, "/modalias"); + if (!ffReadFileBuffer(pciDir.chars, &buffer)) + continue; + ffStrbufSubstrBefore(&pciDir, pciDevDirLength); + assert(ffStrbufStartsWithS(&buffer, "pci:")); + + detectPci(options, gpus, &buffer, &pciDir); + ffStrbufSubstrBefore(&pciDir, pciBaseDirLength); + } + + return NULL; +} + const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) { #ifdef FF_HAVE_DIRECTX_HEADERS const char* ffGPUDetectByDirectX(const FFGPUOptions* options, FFlist* gpus); - if (!ffGPUDetectByDirectX(options, gpus)) + if (ffGPUDetectByDirectX(options, gpus) == NULL) return NULL; #endif - return drmDetectGPUs(options, gpus); + if (drmDetectGPUs(options, gpus) == NULL && gpus->length > 0) + return NULL; + + return pciDetectGPUs(options, gpus); } From 0e7dda4043fd056c495065350c766b6d9374b218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 22:38:22 +0800 Subject: [PATCH 22/40] Shell: remove unused vars --- src/modules/shell/option.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/shell/option.h b/src/modules/shell/option.h index 4d430e8184..b356cbac93 100644 --- a/src/modules/shell/option.h +++ b/src/modules/shell/option.h @@ -8,6 +8,4 @@ typedef struct FFShellOptions { FFModuleBaseInfo moduleInfo; FFModuleArgs moduleArgs; - - bool version; } FFShellOptions; From fd0b35f21a125359c3574440fa2e6eac310ba978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 22:53:31 +0800 Subject: [PATCH 23/40] Display: add option `--ts-version` to hide terminal and shell version --- doc/json_schema.json | 5 +++++ src/data/help.json | 10 ++++++++++ src/detection/terminalshell/terminalshell.c | 4 ++++ src/options/display.c | 10 ++++++---- src/options/display.h | 1 + 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/doc/json_schema.json b/doc/json_schema.json index 387fda5ed8..49c0596c75 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -549,6 +549,11 @@ "type": "boolean", "description": "Whether to disable the stdout application buffer", "default": false + }, + "tsVersion": { + "type": "boolean", + "description": "Whether to detect and display the version of terminal and shell. Mainly for benchmarking", + "default": true } } }, diff --git a/src/data/help.json b/src/data/help.json index b509808004..f7cdfb2ec0 100644 --- a/src/data/help.json +++ b/src/data/help.json @@ -702,6 +702,16 @@ "type": "color", "default": "light_red" } + }, + { + "long": "ts-version", + "desc": "Whether to detect and display the version of terminal and shell", + "remark": "Mainly for benchmarking", + "arg": { + "type": "bool", + "optional": true, + "default": true + } } ], "Library path": [ diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 1545721799..521ea0bde5 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -223,6 +223,8 @@ static bool getShellVersionGeneric(FFstrbuf* exe, const char* exeName, FFstrbuf* bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) { + if (!instance.config.display.tsVersion) return false; + if(strcasecmp(exeName, "bash") == 0 || strcasecmp(exeName, "sh") == 0) return getShellVersionBash(exe, version); if(strcasecmp(exeName, "zsh") == 0) @@ -530,6 +532,8 @@ static bool getTerminalVersionConEmu(FFstrbuf* exe, FFstrbuf* version) bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe, FFstrbuf* version) { + if (!instance.config.display.tsVersion) return false; + #ifdef __ANDROID__ if(ffStrbufEqualS(processName, "com.termux")) diff --git a/src/options/display.c b/src/options/display.c index 528c46e80b..6744b8f423 100644 --- a/src/options/display.c +++ b/src/options/display.c @@ -189,10 +189,8 @@ const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_va options->noBuffer = yyjson_get_bool(val); else if (ffStrEqualsIgnCase(key, "keyWidth")) options->keyWidth = (uint32_t) yyjson_get_uint(val); - else if (ffStrStartsWithIgnCase(key, "percent")) - return "Property `display.percentX` has been changed to `display.percent.x`"; - else if (ffStrStartsWithIgnCase(key, "size")) - return "Property `display.sizeX` has been changed to `display.size.x`"; + else if (ffStrEndsWithIgnCase(key, "tsVersion")) + options->tsVersion = yyjson_get_bool(val); else return "Unknown display property"; } @@ -337,6 +335,8 @@ bool ffOptionsParseDisplayCommandLine(FFOptionsDisplay* options, const char* key else return false; } + else if(ffStrEqualsIgnCase(key, "--ts-version")) + options->tsVersion = ffOptionParseBoolean(value); else return false; return true; @@ -382,6 +382,8 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) ffStrbufInitStatic(&options->percentColorGreen, FF_COLOR_FG_GREEN); ffStrbufInitStatic(&options->percentColorYellow, FF_COLOR_FG_LIGHT_YELLOW); ffStrbufInitStatic(&options->percentColorRed, FF_COLOR_FG_LIGHT_RED); + + options->tsVersion = true; } void ffOptionsDestroyDisplay(FFOptionsDisplay* options) diff --git a/src/options/display.h b/src/options/display.h index ef02336cbf..a56a224e79 100644 --- a/src/options/display.h +++ b/src/options/display.h @@ -50,6 +50,7 @@ typedef struct FFOptionsDisplay FFstrbuf percentColorRed; bool noBuffer; uint32_t keyWidth; + bool tsVersion; } FFOptionsDisplay; const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_val* root); From cb3ca73f60ddf944a08b394b07cabf8e59096fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 22:55:04 +0800 Subject: [PATCH 24/40] Doc: update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19115fe7b0..ecf910a679 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,11 @@ Features: * We now use `wlr-randr` to detect displays for hyprland, which correctly reports fractional scale factors. Experimental (Display, Linux) * Support GPU memory usage detection for AMD GPUs (GPU, Linux) +* Support GPU frequency detection for Intel GPUs (GPU, Linux) * Improve performance of Gnome version detection (DE, Linux) * Improve performance of kitty version detection (Terminal, Linux) * Detect refresh rate when using `--ds-force-drm sysfs-only` (Display, Linux) +* Add option `--ts-version` to disable terminal and shell version detection Bugfixes: * Correctly detect `/bin/sh` as current shell if it's used as default shell (Shell, Linux) From 47000f2aafcedcc4778e3a12c83ebfa53da17668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 23:20:58 +0800 Subject: [PATCH 25/40] GPU (Linux): fix freq detection oops... --- src/detection/gpu/gpu_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index f4ab7ead42..7dba510132 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -84,7 +84,7 @@ static void pciDetectVmem(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) static void pciDetectVfreq(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) { - if (ffStrbufEndsWithS(pciDir, "/device")) // Must be in `/sys/class/drm/cardN/device` + if (!ffStrbufEndsWithS(pciDir, "/device")) // Must be in `/sys/class/drm/cardN/device` return; // Works for Intel GPUs From 9b23ce94ab0838f7fd483833c8fc677023214e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Apr 2024 23:34:27 +0800 Subject: [PATCH 26/40] Chore: oops... --- src/options/display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options/display.c b/src/options/display.c index 6744b8f423..4231772864 100644 --- a/src/options/display.c +++ b/src/options/display.c @@ -189,7 +189,7 @@ const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_va options->noBuffer = yyjson_get_bool(val); else if (ffStrEqualsIgnCase(key, "keyWidth")) options->keyWidth = (uint32_t) yyjson_get_uint(val); - else if (ffStrEndsWithIgnCase(key, "tsVersion")) + else if (ffStrEqualsIgnCase(key, "tsVersion")) options->tsVersion = yyjson_get_bool(val); else return "Unknown display property"; From efb0f335e18f7ab0f3d95673d558abf0dd576486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 19 Apr 2024 08:48:22 +0800 Subject: [PATCH 27/40] Chore: silence compiler warnings --- src/util/edidHelper.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/util/edidHelper.c b/src/util/edidHelper.c index 09931d22da..7724c6136a 100644 --- a/src/util/edidHelper.c +++ b/src/util/edidHelper.c @@ -13,11 +13,11 @@ void ffEdidGetPreferredResolutionAndRefreshRate(const uint8_t edid[128], uint32_ { // read through descriptor blocks... if (edid[i] != 0x00 && edid[i + 1] != 0x00) { // a dtd - uint32_t hactive = edid[i+2] + ((edid[i + 4] & 0xf0) << 4); - uint32_t hblank = edid[i+3] + ((edid[i + 4] & 0x0f) << 8); - uint32_t vactive = edid[i+5] + ((edid[i + 7] & 0xf0) << 4); - uint32_t vblank = edid[i+6] + ((edid[i + 7] & 0x0f) << 8); - uint32_t pixclk = (edid[i+1] << 8) | (edid[i]); + uint32_t hactive = edid[i+2] + (uint32_t) ((edid[i + 4] & 0xf0) << 4); + uint32_t hblank = edid[i+3] + (uint32_t) ((edid[i + 4] & 0x0f) << 8); + uint32_t vactive = edid[i+5] + (uint32_t) ((edid[i + 7] & 0xf0) << 4); + uint32_t vblank = edid[i+6] + (uint32_t) ((edid[i + 7] & 0x0f) << 8); + uint32_t pixclk = ((uint32_t) edid[i+1] << 8) | (edid[i]); *width = hactive; *height = vactive; *refreshRate = (double)pixclk * 10000 / (double)(hactive+hblank) / (double)(vactive+vblank); From 526324be38ebb7f933a3e69763c439eaa538c54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 19 Apr 2024 15:19:46 +0800 Subject: [PATCH 28/40] Terminal (FreeBSD): implement kitty version fast path for FreeBSD --- src/detection/terminalshell/terminalshell.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 521ea0bde5..68ff5313a1 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -5,6 +5,9 @@ #include "util/stringUtils.h" #include +#ifdef __FreeBSD__ + #include +#endif #ifdef _WIN32 @@ -474,11 +477,17 @@ static bool getTerminalVersionZellij(FFstrbuf* exe, FFstrbuf* version) #ifndef _WIN32 static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) { - #ifdef __linux__ + #if defined(__linux__) || defined(__FreeBSD__) // kitty is written in python. `kitty --version` can be expensive char buffer[1024] = {}; - if (ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) || - ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer)) + if ( + #ifdef __linux__ + ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) || + ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) + #else + ffReadFileData(_PATH_LOCALBASE "/share/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) + #endif + ) { // Starts from version 0.17.0 // https://github.com/kovidgoyal/kitty/blob/master/kitty/constants.py#L25 From 6c0c4fa2cf979b0516852685487b45c0947e50fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 21 Apr 2024 17:28:40 +0800 Subject: [PATCH 29/40] Host (Linux): improve performance of WSL version detection --- src/detection/host/host_linux.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/detection/host/host_linux.c b/src/detection/host/host_linux.c index 92b1c49f94..e6311f0fd6 100644 --- a/src/detection/host/host_linux.c +++ b/src/detection/host/host_linux.c @@ -78,20 +78,12 @@ const char* ffDetectHost(FFHostResult* host) ffStrbufAppendF(&host->name, " - %s", wslDistroName); ffStrbufAppendS(&host->family, "WSL"); - FF_STRBUF_AUTO_DESTROY wslVer = ffStrbufCreate(); //Wide characters - if(!ffProcessAppendStdOut(&wslVer, (char* const[]){ - "wsl.exe", - "--version", - NULL - }) && wslVer.length > 0) - { - ffStrbufSubstrBeforeFirstC(&wslVer, '\r'); //CRLF - ffStrbufSubstrAfterLastC(&wslVer, ' '); - for(uint32_t i = 0; i < wslVer.length; ++i) { - if(wslVer.chars[i]) //don't append \0 - ffStrbufAppendC(&host->version, wslVer.chars[i]); - } - } + ffProcessAppendStdOut(&host->version, (char* const[]){ + "wslinfo", + "--wsl-version", + "-n", + NULL, + }); // supported in 2.2.3 and later } } From 4580f3fd54fd33bd50ec3367e8ec4d39353c648f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 21 Apr 2024 17:35:47 +0800 Subject: [PATCH 30/40] Disk (Linux): hide drivers folder in WSL --- src/detection/disk/disk_linux.c | 2 +- src/util/stringUtils.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/detection/disk/disk_linux.c b/src/detection/disk/disk_linux.c index 89194414f0..31715e6d59 100644 --- a/src/detection/disk/disk_linux.c +++ b/src/detection/disk/disk_linux.c @@ -31,7 +31,7 @@ static bool isPhysicalDevice(const struct mntent* device) //DrvFs is a filesystem plugin to WSL that was designed to support interop between WSL and the Windows filesystem. if(ffStrEquals(device->mnt_type, "9p")) - return true; + return ffStrContains(device->mnt_opts, "aname=drvfs"); //ZFS pool if(ffStrEquals(device->mnt_type, "zfs")) diff --git a/src/util/stringUtils.h b/src/util/stringUtils.h index 4de9578411..dd5d7e5ba1 100644 --- a/src/util/stringUtils.h +++ b/src/util/stringUtils.h @@ -64,3 +64,8 @@ static inline bool ffStrContainsIgnCase(const char* str, const char* compareTo) { return strcasestr(str, compareTo) != NULL; } + +static inline bool ffStrContainsC(const char* str, char compareTo) +{ + return strchr(str, compareTo) != NULL; +} From aa58a0999d4e2c41d438645bc09eead5de31f869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 21 Apr 2024 17:57:32 +0800 Subject: [PATCH 31/40] CPU (Windows): try detecting invalid data in SMBIOS Work around #800 --- src/detection/cpu/cpu_windows.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index 855ae7d298..2a8b69bfb1 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -64,13 +64,12 @@ static const char* detectMaxSpeedBySmbios(FFCPUResult* cpu) return "No active CPU is found in SMBIOS data"; } - if (data->MaxSpeed > 0 && data->MaxSpeed < 30000) // VMware reports weird values - { - double speed = data->MaxSpeed / 1000.0; - if (cpu->frequencyBase < speed) - cpu->frequencyMax = speed; - } + double speed = data->MaxSpeed / 1000.0; + // Sometimes SMBIOS reports invalid value. We assume that max speed is small than 2x of base + if (speed < cpu->frequencyBase || speed > cpu->frequencyBase * 2) + return "Possible invalid CPU max speed in SMBIOS data. See #800"; + cpu->frequencyMax = speed; return NULL; } From 7cc381339c57f0ba061d4ea59f90eae417cb52de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 21 Apr 2024 19:19:26 +0800 Subject: [PATCH 32/40] 3rdparty: update --- src/3rdparty/igcl/igcl_api.h | 257 ++++++++++++ src/3rdparty/igcl/repo.json | 2 +- src/3rdparty/nvml/nvml.h | 765 +++++++++++++++++++++++++++++++++-- src/3rdparty/nvml/repo.json | 2 +- 4 files changed, 999 insertions(+), 27 deletions(-) diff --git a/src/3rdparty/igcl/igcl_api.h b/src/3rdparty/igcl/igcl_api.h index ff83226c57..e931d1a9b8 100644 --- a/src/3rdparty/igcl/igcl_api.h +++ b/src/3rdparty/igcl/igcl_api.h @@ -1229,6 +1229,18 @@ typedef struct _ctl_lda_args_t ctl_lda_args_t; /// @brief Forward-declare ctl_dce_args_t typedef struct _ctl_dce_args_t ctl_dce_args_t; +/////////////////////////////////////////////////////////////////////////////// +/// @brief Forward-declare ctl_wire_format_t +typedef struct _ctl_wire_format_t ctl_wire_format_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Forward-declare ctl_get_set_wire_format_config_t +typedef struct _ctl_get_set_wire_format_config_t ctl_get_set_wire_format_config_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Forward-declare ctl_display_settings_t +typedef struct _ctl_display_settings_t ctl_display_settings_t; + /////////////////////////////////////////////////////////////////////////////// /// @brief Forward-declare ctl_engine_properties_t typedef struct _ctl_engine_properties_t ctl_engine_properties_t; @@ -2029,6 +2041,8 @@ typedef enum _ctl_std_display_feature_flag_t CTL_STD_DISPLAY_FEATURE_FLAG_VESA_COMPRESSION = CTL_BIT(4), ///< [out] Is display compression (VESA DSC) supported CTL_STD_DISPLAY_FEATURE_FLAG_HDR = CTL_BIT(5), ///< [out] Is HDR supported CTL_STD_DISPLAY_FEATURE_FLAG_HDMI_QMS = CTL_BIT(6), ///< [out] Is HDMI QMS supported + CTL_STD_DISPLAY_FEATURE_FLAG_HDR10_PLUS_CERTIFIED = CTL_BIT(7), ///< [out] Is HDR10+ certified + CTL_STD_DISPLAY_FEATURE_FLAG_VESA_HDR_CERTIFIED = CTL_BIT(8), ///< [out] Is VESA HDR certified - for future use CTL_STD_DISPLAY_FEATURE_FLAG_MAX = 0x80000000 } ctl_std_display_feature_flag_t; @@ -2042,6 +2056,7 @@ typedef enum _ctl_intel_display_feature_flag_t CTL_INTEL_DISPLAY_FEATURE_FLAG_DPST = CTL_BIT(0), ///< [out] Is DPST supported CTL_INTEL_DISPLAY_FEATURE_FLAG_LACE = CTL_BIT(1), ///< [out] Is LACE supported CTL_INTEL_DISPLAY_FEATURE_FLAG_DRRS = CTL_BIT(2), ///< [out] Is DRRS supported + CTL_INTEL_DISPLAY_FEATURE_FLAG_ARC_ADAPTIVE_SYNC_CERTIFIED = CTL_BIT(3),///< [out] Is Intel Arc certified adaptive sync display CTL_INTEL_DISPLAY_FEATURE_FLAG_MAX = 0x80000000 } ctl_intel_display_feature_flag_t; @@ -4398,6 +4413,232 @@ ctlGetSetDynamicContrastEnhancement( ctl_dce_args_t* pDceArgs ///< [in,out] Dynamic Contrast Enhancement arguments ); +/////////////////////////////////////////////////////////////////////////////// +/// @brief Color model +typedef enum _ctl_wire_format_color_model_t +{ + CTL_WIRE_FORMAT_COLOR_MODEL_RGB = 0, ///< Color model RGB + CTL_WIRE_FORMAT_COLOR_MODEL_YCBCR_420 = 1, ///< Color model YCBCR 420 + CTL_WIRE_FORMAT_COLOR_MODEL_YCBCR_422 = 2, ///< Color model YCBCR 422 + CTL_WIRE_FORMAT_COLOR_MODEL_YCBCR_444 = 3, ///< Color model YCBCR 444 + CTL_WIRE_FORMAT_COLOR_MODEL_MAX + +} ctl_wire_format_color_model_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Operation type +typedef enum _ctl_wire_format_operation_type_t +{ + CTL_WIRE_FORMAT_OPERATION_TYPE_GET = 0, ///< Get request + CTL_WIRE_FORMAT_OPERATION_TYPE_SET = 1, ///< Set request + CTL_WIRE_FORMAT_OPERATION_TYPE_RESTORE_DEFAULT = 2, ///< Restore to default values + CTL_WIRE_FORMAT_OPERATION_TYPE_MAX + +} ctl_wire_format_operation_type_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Wire Format +typedef struct _ctl_wire_format_t +{ + uint32_t Size; ///< [in] size of this structure + uint8_t Version; ///< [in] version of this structure + ctl_wire_format_color_model_t ColorModel; ///< [in,out] Color model + ctl_output_bpc_flags_t ColorDepth; ///< [in,out] Color Depth + +} ctl_wire_format_t; + +/////////////////////////////////////////////////////////////////////////////// +#ifndef CTL_MAX_WIREFORMAT_COLOR_MODELS_SUPPORTED +/// @brief Maximum Wire Formats Supported +#define CTL_MAX_WIREFORMAT_COLOR_MODELS_SUPPORTED 4 +#endif // CTL_MAX_WIREFORMAT_COLOR_MODELS_SUPPORTED + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Get Set Wire Format +typedef struct _ctl_get_set_wire_format_config_t +{ + uint32_t Size; ///< [in] size of this structure + uint8_t Version; ///< [in] version of this structure + ctl_wire_format_operation_type_t Operation; ///< [in] Get/Set Operation + ctl_wire_format_t SupportedWireFormat[CTL_MAX_WIREFORMAT_COLOR_MODELS_SUPPORTED]; ///< [out] Array of WireFormats supported + ctl_wire_format_t WireFormat; ///< [in,out] Current/Requested WireFormat based on Operation. During SET + ///< Operation, if multiple bpc is set, the MIN bpc will be applied + +} ctl_get_set_wire_format_config_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Get/Set Color Format and Color Depth +/// +/// @details +/// - Get and Set the Color Format and Color Depth of a target +/// +/// @returns +/// - CTL_RESULT_SUCCESS +/// - CTL_RESULT_ERROR_UNINITIALIZED +/// - CTL_RESULT_ERROR_DEVICE_LOST +/// - CTL_RESULT_ERROR_INVALID_NULL_HANDLE +/// + `nullptr == hDisplayOutput` +/// - CTL_RESULT_ERROR_INVALID_NULL_POINTER +/// + `nullptr == pGetSetWireFormatSetting` +/// - ::CTL_RESULT_ERROR_UNSUPPORTED_VERSION - "Unsupported version" +/// - ::CTL_RESULT_ERROR_INVALID_ARGUMENT - "Invalid data passed as argument, WireFormat is not supported" +/// - ::CTL_RESULT_ERROR_DISPLAY_NOT_ACTIVE - "Display not active" +/// - ::CTL_RESULT_ERROR_INVALID_OPERATION_TYPE - "Invalid operation type" +/// - ::CTL_RESULT_ERROR_NULL_OS_DISPLAY_OUTPUT_HANDLE - "Null OS display output handle" +/// - ::CTL_RESULT_ERROR_NULL_OS_INTERFACE - "Null OS interface" +/// - ::CTL_RESULT_ERROR_NULL_OS_ADAPATER_HANDLE - "Null OS adapter handle" +CTL_APIEXPORT ctl_result_t CTL_APICALL +ctlGetSetWireFormat( + ctl_display_output_handle_t hDisplayOutput, ///< [in][release] Handle to display output + ctl_get_set_wire_format_config_t* pGetSetWireFormatSetting ///< [in][release] Get/Set Wire Format settings to be fetched/applied + ); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Various display settings +typedef uint32_t ctl_display_setting_flags_t; +typedef enum _ctl_display_setting_flag_t +{ + CTL_DISPLAY_SETTING_FLAG_LOW_LATENCY = CTL_BIT(0), ///< Low latency + CTL_DISPLAY_SETTING_FLAG_SOURCE_TM = CTL_BIT(1),///< Source tone mapping + CTL_DISPLAY_SETTING_FLAG_CONTENT_TYPE = CTL_BIT(2), ///< Content type + CTL_DISPLAY_SETTING_FLAG_QUANTIZATION_RANGE = CTL_BIT(3), ///< Quantization range, full range or limited range + CTL_DISPLAY_SETTING_FLAG_PICTURE_AR = CTL_BIT(4), ///< Picture aspect ratio + CTL_DISPLAY_SETTING_FLAG_AUDIO = CTL_BIT(5), ///< Audio settings + CTL_DISPLAY_SETTING_FLAG_MAX = 0x80000000 + +} ctl_display_setting_flag_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Low latency setting +typedef enum _ctl_display_setting_low_latency_t +{ + CTL_DISPLAY_SETTING_LOW_LATENCY_DEFAULT = 0, ///< Default + CTL_DISPLAY_SETTING_LOW_LATENCY_DISABLED = 1, ///< Disabled + CTL_DISPLAY_SETTING_LOW_LATENCY_ENABLED = 2, ///< Enabled + CTL_DISPLAY_SETTING_LOW_LATENCY_MAX + +} ctl_display_setting_low_latency_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Source tone mapping setting +typedef enum _ctl_display_setting_sourcetm_t +{ + CTL_DISPLAY_SETTING_SOURCETM_DEFAULT = 0, ///< Default + CTL_DISPLAY_SETTING_SOURCETM_DISABLED = 1, ///< Disabled + CTL_DISPLAY_SETTING_SOURCETM_ENABLED = 2, ///< Enabled + CTL_DISPLAY_SETTING_SOURCETM_MAX + +} ctl_display_setting_sourcetm_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Content type settings +typedef enum _ctl_display_setting_content_type_t +{ + CTL_DISPLAY_SETTING_CONTENT_TYPE_DEFAULT = 0, ///< Default content type used by driver. Driver will use internal + ///< techniques to determine content type and indicate to panel + CTL_DISPLAY_SETTING_CONTENT_TYPE_DISABLED = 1, ///< Content type indication is disabled + CTL_DISPLAY_SETTING_CONTENT_TYPE_DESKTOP = 2, ///< Typical desktop with a mix of text and graphics + CTL_DISPLAY_SETTING_CONTENT_TYPE_MEDIA = 3, ///< Video or media content + CTL_DISPLAY_SETTING_CONTENT_TYPE_GAMING = 4, ///< Gaming content + CTL_DISPLAY_SETTING_CONTENT_TYPE_MAX + +} ctl_display_setting_content_type_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Quantization range +typedef enum _ctl_display_setting_quantization_range_t +{ + CTL_DISPLAY_SETTING_QUANTIZATION_RANGE_DEFAULT = 0, ///< Default based on video format + CTL_DISPLAY_SETTING_QUANTIZATION_RANGE_LIMITED_RANGE = 1, ///< Limited range + CTL_DISPLAY_SETTING_QUANTIZATION_RANGE_FULL_RANGE = 2, ///< Full range + CTL_DISPLAY_SETTING_QUANTIZATION_RANGE_MAX + +} ctl_display_setting_quantization_range_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Picture aspect ratio +typedef uint32_t ctl_display_setting_picture_ar_flags_t; +typedef enum _ctl_display_setting_picture_ar_flag_t +{ + CTL_DISPLAY_SETTING_PICTURE_AR_FLAG_DEFAULT = CTL_BIT(0), ///< Default picture aspect ratio + CTL_DISPLAY_SETTING_PICTURE_AR_FLAG_DISABLED = CTL_BIT(1), ///< Picture aspect ratio indication is explicitly disabled + CTL_DISPLAY_SETTING_PICTURE_AR_FLAG_AR_4_3 = CTL_BIT(2),///< Aspect ratio of 4:3 + CTL_DISPLAY_SETTING_PICTURE_AR_FLAG_AR_16_9 = CTL_BIT(3), ///< Aspect ratio of 16:9 + CTL_DISPLAY_SETTING_PICTURE_AR_FLAG_AR_64_27 = CTL_BIT(4), ///< Aspect ratio of 64:27 or 21:9 anamorphic + CTL_DISPLAY_SETTING_PICTURE_AR_FLAG_AR_256_135 = CTL_BIT(5),///< Aspect ratio of 256:135 + CTL_DISPLAY_SETTING_PICTURE_AR_FLAG_MAX = 0x80000000 + +} ctl_display_setting_picture_ar_flag_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Audio settings +typedef enum _ctl_display_setting_audio_t +{ + CTL_DISPLAY_SETTING_AUDIO_DEFAULT = 0, ///< Default audio settings, always enumerated and enabled if display + ///< supports it + CTL_DISPLAY_SETTING_AUDIO_DISABLED = 1, ///< Forcefully disable display audio end point enumeration to OS + CTL_DISPLAY_SETTING_AUDIO_MAX + +} ctl_display_setting_audio_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Get/Set end display settings +typedef struct _ctl_display_settings_t +{ + uint32_t Size; ///< [in] size of this structure + uint8_t Version; ///< [in] version of this structure + bool Set; ///< [in] Flag to indicate Set or Get operation. Default option for all + ///< features are reserved for Set=true calls, which will reset the setting + ///< to driver defaults. + ctl_display_setting_flags_t SupportedFlags; ///< [out] Display setting flags supported by the display. + ctl_display_setting_flags_t ControllableFlags; ///< [out] Display setting flags which can be controlled by the caller. + ///< Features which doesn't have this flag set cannot be changed by caller. + ctl_display_setting_flags_t ValidFlags; ///< [in,out] Display setting flags which caller can use to indicate the + ///< features it's interested in. This cannot have a bit set which is not + ///< supported by SupportedFlags and ControllableFlags. + ctl_display_setting_low_latency_t LowLatency; ///< [in,out] Low latency state of panel. For HDR10+ Gaming this need to be + ///< in ENABLED state. + ctl_display_setting_sourcetm_t SourceTM; ///< [in,out] Source tone mapping state known to panel. For HDR10+ Gaming + ///< this need to be in ENABLED state. + ctl_display_setting_content_type_t ContentType; ///< [in,out] Source content type known to panel. + ctl_display_setting_quantization_range_t QuantizationRange; ///< [in,out] Quantization range + ctl_display_setting_picture_ar_flags_t SupportedPictureAR; ///< [out] Supported Picture aspect ratios + ctl_display_setting_picture_ar_flag_t PictureAR;///< [in,out] Picture aspect ratio + ctl_display_setting_audio_t AudioSettings; ///< [in,out] Audio settings + uint32_t Reserved[25]; ///< [out] Reserved fields for future enumerations + +} ctl_display_settings_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Get/Set Display settings +/// +/// @details +/// - To get/set end display settings like low latency, HDR10+ signaling +/// etc. which are controlled via info-frames/secondary data packets +/// +/// @returns +/// - CTL_RESULT_SUCCESS +/// - CTL_RESULT_ERROR_UNINITIALIZED +/// - CTL_RESULT_ERROR_DEVICE_LOST +/// - CTL_RESULT_ERROR_INVALID_NULL_HANDLE +/// + `nullptr == hDisplayOutput` +/// - CTL_RESULT_ERROR_INVALID_NULL_POINTER +/// + `nullptr == pDisplaySettings` +/// - ::CTL_RESULT_ERROR_UNSUPPORTED_VERSION - "Unsupported version" +/// - ::CTL_RESULT_ERROR_NULL_OS_DISPLAY_OUTPUT_HANDLE - "Null OS display output handle" +/// - ::CTL_RESULT_ERROR_NULL_OS_INTERFACE - "Null OS interface" +/// - ::CTL_RESULT_ERROR_NULL_OS_ADAPATER_HANDLE - "Null OS adapter handle" +/// - ::CTL_RESULT_ERROR_KMD_CALL - "Kernel mode driver call failure" +/// - ::CTL_RESULT_ERROR_INVALID_NULL_HANDLE - "Invalid or Null handle passed" +/// - ::CTL_RESULT_ERROR_INVALID_NULL_POINTER - "Invalid null pointer" +/// - ::CTL_RESULT_ERROR_INVALID_OPERATION_TYPE - "Invalid operation type" +/// - ::CTL_RESULT_ERROR_INVALID_ARGUMENT - "Invalid combination of parameters" +CTL_APIEXPORT ctl_result_t CTL_APICALL +ctlGetSetDisplaySettings( + ctl_display_output_handle_t hDisplayOutput, ///< [in] Handle to display output + ctl_display_settings_t* pDisplaySettings ///< [in,out] End display capabilities + ); + #if !defined(__GNUC__) #pragma endregion // display @@ -7229,6 +7470,22 @@ typedef ctl_result_t (CTL_APICALL *ctl_pfnGetSetDynamicContrastEnhancement_t)( ); +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function-pointer for ctlGetSetWireFormat +typedef ctl_result_t (CTL_APICALL *ctl_pfnGetSetWireFormat_t)( + ctl_display_output_handle_t, + ctl_get_set_wire_format_config_t* + ); + + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function-pointer for ctlGetSetDisplaySettings +typedef ctl_result_t (CTL_APICALL *ctl_pfnGetSetDisplaySettings_t)( + ctl_display_output_handle_t, + ctl_display_settings_t* + ); + + /////////////////////////////////////////////////////////////////////////////// /// @brief Function-pointer for ctlEnumEngineGroups typedef ctl_result_t (CTL_APICALL *ctl_pfnEnumEngineGroups_t)( diff --git a/src/3rdparty/igcl/repo.json b/src/3rdparty/igcl/repo.json index b14aaacc0d..231bc7e2f9 100644 --- a/src/3rdparty/igcl/repo.json +++ b/src/3rdparty/igcl/repo.json @@ -1,6 +1,6 @@ { "home": "https://github.com/intel/drivers.gpu.control-library", "license": "./LICENSE", - "version": "e1776b7be2ceead1296204371447b27860d25a66", + "version": "v184", "author": "Intel Corporation" } diff --git a/src/3rdparty/nvml/nvml.h b/src/3rdparty/nvml/nvml.h index 191d41da5c..c651c7f038 100644 --- a/src/3rdparty/nvml/nvml.h +++ b/src/3rdparty/nvml/nvml.h @@ -153,6 +153,27 @@ typedef struct nvmlDevice_st* nvmlDevice_t; */ #define NVML_DEVICE_PCI_BUS_ID_BUFFER_V2_SIZE 16 +/** + * PCI information about a GPU device. + */ +typedef struct +{ + unsigned int version; //!< The version number of this struct + unsigned int domain; //!< The PCI domain on which the device's bus resides, 0 to 0xffffffff + unsigned int bus; //!< The bus on which the device resides, 0 to 0xff + unsigned int device; //!< The device's id on the bus, 0 to 31 + + unsigned int pciDeviceId; //!< The combined 16-bit device id and 16-bit vendor id + unsigned int pciSubSystemId; //!< The 32-bit Sub System Device ID + + unsigned int baseClass; //!< The 8-bit PCI base class code + unsigned int subClass; //!< The 8-bit PCI sub class code + + char busId[NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE]; //!< The tuple domain:bus:device.function PCI identifier (& NULL terminator) +} nvmlPciInfoExt_v1_t; +typedef nvmlPciInfoExt_v1_t nvmlPciInfoExt_t; +#define nvmlPciInfoExt_v1 NVML_STRUCT_VERSION(PciInfoExt, 1) + /** * PCI information about a GPU device. */ @@ -482,11 +503,16 @@ typedef enum nvmlGpuP2PStatus_enum typedef enum nvmlGpuP2PCapsIndex_enum { NVML_P2P_CAPS_INDEX_READ = 0, - NVML_P2P_CAPS_INDEX_WRITE, - NVML_P2P_CAPS_INDEX_NVLINK, - NVML_P2P_CAPS_INDEX_ATOMICS, - NVML_P2P_CAPS_INDEX_PROP, - NVML_P2P_CAPS_INDEX_UNKNOWN + NVML_P2P_CAPS_INDEX_WRITE = 1, + NVML_P2P_CAPS_INDEX_NVLINK = 2, + NVML_P2P_CAPS_INDEX_ATOMICS = 3, + NVML_P2P_CAPS_INDEX_PCI = 4, + /* + * DO NOT USE! NVML_P2P_CAPS_INDEX_PROP is deprecated. + * Use NVML_P2P_CAPS_INDEX_PCI instead. + */ + NVML_P2P_CAPS_INDEX_PROP = NVML_P2P_CAPS_INDEX_PCI, + NVML_P2P_CAPS_INDEX_UNKNOWN = 5, }nvmlGpuP2PCapsIndex_t; /** @@ -526,6 +552,8 @@ typedef enum nvmlSamplingType_enum NVML_PROCESSOR_CLK_SAMPLES = 5, //!< To represent processor clock samples NVML_MEMORY_CLK_SAMPLES = 6, //!< To represent memory clock samples NVML_MODULE_POWER_SAMPLES = 7, //!< To represent module power samples for total module starting Grace Hopper + NVML_JPG_UTILIZATION_SAMPLES = 8, //!< To represent percent of time during which NVJPG remains busy + NVML_OFA_UTILIZATION_SAMPLES = 9, //!< To represent percent of time during which NVOFA remains busy // Keep this last NVML_SAMPLINGTYPE_COUNT @@ -1005,6 +1033,7 @@ typedef enum nvmlReturn_enum NVML_ERROR_DEPRECATED = 26, //!< The requested functionality has been deprecated NVML_ERROR_NOT_READY = 27, //!< The system is not ready for the request NVML_ERROR_GPU_NOT_FOUND = 28, //!< No GPUs were found + NVML_ERROR_INVALID_STATE = 29, //!< Resource not in correct state to perform requested operation NVML_ERROR_UNKNOWN = 999 //!< An internal driver error occurred } nvmlReturn_t; @@ -1150,11 +1179,14 @@ typedef enum nvmlVgpuDriverCapability_enum */ typedef enum nvmlDeviceVgpuCapability_enum { - NVML_DEVICE_VGPU_CAP_FRACTIONAL_MULTI_VGPU = 0, //!< Fractional vGPU profiles on this GPU can be used in multi-vGPU configurations - NVML_DEVICE_VGPU_CAP_HETEROGENEOUS_TIMESLICE_PROFILES = 1, //!< Supports concurrent execution of timesliced vGPU profiles of differing types - NVML_DEVICE_VGPU_CAP_HETEROGENEOUS_TIMESLICE_SIZES = 2, //!< Supports concurrent execution of timesliced vGPU profiles of differing framebuffer sizes - NVML_DEVICE_VGPU_CAP_READ_DEVICE_BUFFER_BW = 3, //!< GPU device's read_device_buffer expected bandwidth capacity in megabytes per second - NVML_DEVICE_VGPU_CAP_WRITE_DEVICE_BUFFER_BW = 4, //!< GPU device's write_device_buffer expected bandwidth capacity in megabytes per second + NVML_DEVICE_VGPU_CAP_FRACTIONAL_MULTI_VGPU = 0, //!< Query if the fractional vGPU profiles on this GPU can be used in multi-vGPU configurations + NVML_DEVICE_VGPU_CAP_HETEROGENEOUS_TIMESLICE_PROFILES = 1, //!< Query if the GPU support concurrent execution of timesliced vGPU profiles of differing types + NVML_DEVICE_VGPU_CAP_HETEROGENEOUS_TIMESLICE_SIZES = 2, //!< Query if the GPU support concurrent execution of timesliced vGPU profiles of differing framebuffer sizes + NVML_DEVICE_VGPU_CAP_READ_DEVICE_BUFFER_BW = 3, //!< Query the GPU's read_device_buffer expected bandwidth capacity in megabytes per second + NVML_DEVICE_VGPU_CAP_WRITE_DEVICE_BUFFER_BW = 4, //!< Query the GPU's write_device_buffer expected bandwidth capacity in megabytes per second + NVML_DEVICE_VGPU_CAP_DEVICE_STREAMING = 5, //!< Query if vGPU profiles on the GPU supports migration data streaming + NVML_DEVICE_VGPU_CAP_MINI_QUARTER_GPU = 6, //!< Set/Get support for mini-quarter vGPU profiles + NVML_DEVICE_VGPU_CAP_COMPUTE_MEDIA_ENGINE_GPU = 7, //!< Set/Get support for compute media engine vGPU profiles // Keep this last NVML_DEVICE_VGPU_CAP_COUNT } nvmlDeviceVgpuCapability_t; @@ -1181,6 +1213,8 @@ typedef enum nvmlDeviceVgpuCapability_enum #define INVALID_GPU_INSTANCE_ID 0xFFFFFFFF +#define NVML_INVALID_VGPU_PLACEMENT_ID 0xFFFF + /*! * Macros for vGPU instance's virtualization capabilities bitfield. */ @@ -1207,6 +1241,41 @@ typedef unsigned int nvmlVgpuTypeId_t; typedef unsigned int nvmlVgpuInstance_t; +/** + * Structure to store the vGPU heterogeneous mode of device -- version 1 + */ +typedef struct +{ + unsigned int version; //!< The version number of this struct + unsigned int mode; //!< The vGPU heterogeneous mode +} nvmlVgpuHeterogeneousMode_v1_t; +typedef nvmlVgpuHeterogeneousMode_v1_t nvmlVgpuHeterogeneousMode_t; +#define nvmlVgpuHeterogeneousMode_v1 NVML_STRUCT_VERSION(VgpuHeterogeneousMode, 1) + +/** + * Structure to store the placement ID of vGPU instance -- version 1 + */ +typedef struct +{ + unsigned int version; //!< The version number of this struct + unsigned int placementId; //!< Placement ID of the active vGPU instance +} nvmlVgpuPlacementId_v1_t; +typedef nvmlVgpuPlacementId_v1_t nvmlVgpuPlacementId_t; +#define nvmlVgpuPlacementId_v1 NVML_STRUCT_VERSION(VgpuPlacementId, 1) + +/** + * Structure to store the list of vGPU placements -- version 1 + */ +typedef struct +{ + unsigned int version; //!< The version number of this struct + unsigned int placementSize; //!< The number of slots occupied by the vGPU type + unsigned int count; //!< Count of placement IDs fetched + unsigned int *placementIds; //!< Placement IDs for the vGPU type +} nvmlVgpuPlacementList_v1_t; +typedef nvmlVgpuPlacementList_v1_t nvmlVgpuPlacementList_t; +#define nvmlVgpuPlacementList_v1 NVML_STRUCT_VERSION(VgpuPlacementList, 1) + /** * Structure to store Utilization Value and vgpuInstance */ @@ -1220,6 +1289,35 @@ typedef struct nvmlVgpuInstanceUtilizationSample_st nvmlValue_t decUtil; //!< Decoder Util Value } nvmlVgpuInstanceUtilizationSample_t; +/** + * Structure to store Utilization Value and vgpuInstance Info -- Version 1 + */ +typedef struct +{ + unsigned long long timeStamp; //!< CPU Timestamp in microseconds + nvmlVgpuInstance_t vgpuInstance; //!< vGPU Instance + nvmlValue_t smUtil; //!< SM (3D/Compute) Util Value + nvmlValue_t memUtil; //!< Frame Buffer Memory Util Value + nvmlValue_t encUtil; //!< Encoder Util Value + nvmlValue_t decUtil; //!< Decoder Util Value + nvmlValue_t jpgUtil; //!< Jpeg Util Value + nvmlValue_t ofaUtil; //!< Ofa Util Value +} nvmlVgpuInstanceUtilizationInfo_v1_t; + +/** + * Structure to store recent utilization for vGPU instances running on a device -- version 1 + */ +typedef struct +{ + unsigned int version; //!< The version number of this struct + nvmlValueType_t sampleValType; //!< Hold the type of returned sample values + unsigned int vgpuInstanceCount; //!< Hold the number of vGPU instances + unsigned long long lastSeenTimeStamp; //!< Return only samples with timestamp greater than lastSeenTimeStamp + nvmlVgpuInstanceUtilizationInfo_v1_t *vgpuUtilArray; //!< The array (allocated by caller) in which vGPU utilization are returned +} nvmlVgpuInstancesUtilizationInfo_v1_t; +typedef nvmlVgpuInstancesUtilizationInfo_v1_t nvmlVgpuInstancesUtilizationInfo_t; +#define nvmlVgpuInstancesUtilizationInfo_v1 NVML_STRUCT_VERSION(VgpuInstancesUtilizationInfo, 1) + /** * Structure to store Utilization Value, vgpuInstance and subprocess information */ @@ -1235,6 +1333,36 @@ typedef struct nvmlVgpuProcessUtilizationSample_st unsigned int decUtil; //!< Decoder Util Value } nvmlVgpuProcessUtilizationSample_t; +/** + * Structure to store Utilization Value, vgpuInstance and subprocess information for process running on vGPU instance -- version 1 + */ +typedef struct +{ + char processName[NVML_VGPU_NAME_BUFFER_SIZE]; //!< Name of process running within the vGPU VM + unsigned long long timeStamp; //!< CPU Timestamp in microseconds + nvmlVgpuInstance_t vgpuInstance; //!< vGPU Instance + unsigned int pid; //!< PID of process running within the vGPU VM + unsigned int smUtil; //!< SM (3D/Compute) Util Value + unsigned int memUtil; //!< Frame Buffer Memory Util Value + unsigned int encUtil; //!< Encoder Util Value + unsigned int decUtil; //!< Decoder Util Value + unsigned int jpgUtil; //!< Jpeg Util Value + unsigned int ofaUtil; //!< Ofa Util Value +} nvmlVgpuProcessUtilizationInfo_v1_t; + +/** + * Structure to store recent utilization, vgpuInstance and subprocess information for processes running on vGPU instances active on a device -- version 1 + */ +typedef struct +{ + unsigned int version; //!< The version number of this struct + unsigned int vgpuProcessCount; //!< Hold the number of processes running on vGPU instances + unsigned long long lastSeenTimeStamp; //!< Return only samples with timestamp greater than lastSeenTimeStamp + nvmlVgpuProcessUtilizationInfo_v1_t *vgpuProcUtilArray; //!< The array (allocated by caller) in which utilization of processes running on vGPU instances are returned +} nvmlVgpuProcessesUtilizationInfo_v1_t; +typedef nvmlVgpuProcessesUtilizationInfo_v1_t nvmlVgpuProcessesUtilizationInfo_t; +#define nvmlVgpuProcessesUtilizationInfo_v1 NVML_STRUCT_VERSION(VgpuProcessesUtilizationInfo, 1) + /** * vGPU scheduler policies */ @@ -1392,6 +1520,34 @@ typedef struct nvmlProcessUtilizationSample_st unsigned int decUtil; //!< Decoder Util Value } nvmlProcessUtilizationSample_t; +/** + * Structure to store utilization value and process Id -- version 1 + */ +typedef struct +{ + unsigned long long timeStamp; //!< CPU Timestamp in microseconds + unsigned int pid; //!< PID of process + unsigned int smUtil; //!< SM (3D/Compute) Util Value + unsigned int memUtil; //!< Frame Buffer Memory Util Value + unsigned int encUtil; //!< Encoder Util Value + unsigned int decUtil; //!< Decoder Util Value + unsigned int jpgUtil; //!< Jpeg Util Value + unsigned int ofaUtil; //!< Ofa Util Value +} nvmlProcessUtilizationInfo_v1_t; + +/** + * Structure to store utilization and process ID for each running process -- version 1 + */ +typedef struct +{ + unsigned int version; //!< The version number of this struct + unsigned int processSamplesCount; //!< Caller-supplied array size, and returns number of processes running + unsigned long long lastSeenTimeStamp; //!< Return only samples with timestamp greater than lastSeenTimeStamp + nvmlProcessUtilizationInfo_v1_t *procUtilArray; //!< The array (allocated by caller) of the utilization of GPU SM, framebuffer, video encoder, video decoder, JPEG, and OFA +} nvmlProcessesUtilizationInfo_v1_t; +typedef nvmlProcessesUtilizationInfo_v1_t nvmlProcessesUtilizationInfo_t; +#define nvmlProcessesUtilizationInfo_v1 NVML_STRUCT_VERSION(ProcessesUtilizationInfo, 1) + /** * Structure to store license expiry date and time values */ @@ -1429,6 +1585,29 @@ typedef struct nvmlGridLicensableFeatures_st nvmlGridLicensableFeature_t gridLicensableFeatures[NVML_GRID_LICENSE_FEATURE_MAX_COUNT]; //!< Array of vGPU software licensable features. } nvmlGridLicensableFeatures_t; +/** + * Structure to store SRAM uncorrectable error counters + */ +typedef struct +{ + unsigned int version; //!< the API version number + unsigned long long aggregateUncParity; //!< aggregate uncorrectable parity error count + unsigned long long aggregateUncSecDed; //!< aggregate uncorrectable SEC-DED error count + unsigned long long aggregateCor; //!< aggregate correctable error count + unsigned long long volatileUncParity; //!< volatile uncorrectable parity error count + unsigned long long volatileUncSecDed; //!< volatile uncorrectable SEC-DED error count + unsigned long long volatileCor; //!< volatile correctable error count + unsigned long long aggregateUncBucketL2; //!< aggregate uncorrectable error count for L2 cache bucket + unsigned long long aggregateUncBucketSm; //!< aggregate uncorrectable error count for SM bucket + unsigned long long aggregateUncBucketPcie; //!< aggregate uncorrectable error count for PCIE bucket + unsigned long long aggregateUncBucketMcu; //!< aggregate uncorrectable error count for Microcontroller bucket + unsigned long long aggregateUncBucketOther; //!< aggregate uncorrectable error count for Other bucket + unsigned int bThresholdExceeded; //!< if the error threshold of field diag is exceeded +} nvmlEccSramErrorStatus_v1_t; + +typedef nvmlEccSramErrorStatus_v1_t nvmlEccSramErrorStatus_t; +#define nvmlEccSramErrorStatus_v1 NVML_STRUCT_VERSION(EccSramErrorStatus, 1) + /** * GSP firmware */ @@ -1837,7 +2016,9 @@ typedef struct nvmlGpuDynamicPstatesInfo_st #define NVML_FI_DEV_TEMPERATURE_MEM_MAX_TLIMIT 195 //!< T.Limit temperature after which GPU may begin SW slowdown due to memory temperature #define NVML_FI_DEV_TEMPERATURE_GPU_MAX_TLIMIT 196 //!< T.Limit temperature after which GPU may be throttled below base clock -#define NVML_FI_MAX 197 //!< One greater than the largest field ID defined above +#define NVML_FI_DEV_IS_MIG_MODE_INDEPENDENT_MIG_QUERY_CAPABLE 199 //!< MIG mode independent, MIG query capable device. 1=yes. 0=no. + +#define NVML_FI_MAX 200 //!< One greater than the largest field ID defined above /** * Information for a Field Value Sample @@ -2248,9 +2429,10 @@ typedef struct nvmlAccountingStats_st { */ typedef enum nvmlEncoderQueryType_enum { - NVML_ENCODER_QUERY_H264 = 0, //!< H264 encoder - NVML_ENCODER_QUERY_HEVC = 1, //!< HEVC encoder - NVML_ENCODER_QUERY_AV1 = 2 //!< AV1 encoder + NVML_ENCODER_QUERY_H264 = 0x00, //!< H264 encoder + NVML_ENCODER_QUERY_HEVC = 0x01, //!< HEVC encoder + NVML_ENCODER_QUERY_AV1 = 0x02, //!< AV1 encoder + NVML_ENCODER_QUERY_UNKNOWN = 0xFF //!< Unknown encoder }nvmlEncoderType_t; /** @@ -2399,6 +2581,26 @@ typedef struct nvmlConfComputeSystemState_st { unsigned int devToolsMode; } nvmlConfComputeSystemState_t; +/** + * Confidential Compute Multigpu mode values + */ +#define NVML_CC_SYSTEM_MULTIGPU_NONE 0 +#define NVML_CC_SYSTEM_MULTIGPU_PROTECTED_PCIE 1 + +/** + * Confidential Compute System settings + */ +typedef struct { + unsigned int version; + unsigned int environment; + unsigned int ccFeature; + unsigned int devToolsMode; + unsigned int multiGpuMode; +} nvmlSystemConfComputeSettings_v1_t; + +typedef nvmlSystemConfComputeSettings_v1_t nvmlSystemConfComputeSettings_t; +#define nvmlSystemConfComputeSettings_v1 NVML_STRUCT_VERSION(SystemConfComputeSettings, 1) + /** * Protected memory size */ @@ -2436,6 +2638,8 @@ typedef struct nvmlConfComputeGpuCertificate_st { #define NVML_CC_GPU_CEC_ATTESTATION_REPORT_SIZE 0x1000 #define NVML_CC_CEC_ATTESTATION_REPORT_NOT_PRESENT 0 #define NVML_CC_CEC_ATTESTATION_REPORT_PRESENT 1 +#define NVML_CC_KEY_ROTATION_THRESHOLD_ATTACKER_ADVANTAGE_MIN 50 +#define NVML_CC_KEY_ROTATION_THRESHOLD_ATTACKER_ADVANTAGE_MAX 75 typedef struct nvmlConfComputeGpuAttestationReport_st { unsigned int isCecAttestationReportPresent; @@ -2446,6 +2650,24 @@ typedef struct nvmlConfComputeGpuAttestationReport_st { unsigned char cecAttestationReport[NVML_CC_GPU_CEC_ATTESTATION_REPORT_SIZE]; } nvmlConfComputeGpuAttestationReport_t; +typedef struct nvmlConfComputeSetKeyRotationThresholdInfo_st { + unsigned int version; + unsigned long long maxAttackerAdvantage; +} nvmlConfComputeSetKeyRotationThresholdInfo_v1_t; + +typedef nvmlConfComputeSetKeyRotationThresholdInfo_v1_t nvmlConfComputeSetKeyRotationThresholdInfo_t; +#define nvmlConfComputeSetKeyRotationThresholdInfo_v1 \ + NVML_STRUCT_VERSION(ConfComputeSetKeyRotationThresholdInfo, 1) + +typedef struct nvmlConfComputeGetKeyRotationThresholdInfo_st { + unsigned int version; + unsigned long long attackerAdvantage; +} nvmlConfComputeGetKeyRotationThresholdInfo_v1_t; + +typedef nvmlConfComputeGetKeyRotationThresholdInfo_v1_t nvmlConfComputeGetKeyRotationThresholdInfo_t; +#define nvmlConfComputeGetKeyRotationThresholdInfo_v1 \ + NVML_STRUCT_VERSION(ConfComputeGetKeyRotationThresholdInfo, 1) + /** @} */ #define NVML_GPU_FABRIC_UUID_LEN 16 @@ -2464,6 +2686,55 @@ typedef struct { nvmlGpuFabricState_t state; //!< Current state of GPU registration process } nvmlGpuFabricInfo_t; +#define NVML_GPU_FABRIC_HEALTH_MASK_DEGRADED_BW_NOT_SUPPORTED 0 +#define NVML_GPU_FABRIC_HEALTH_MASK_DEGRADED_BW_TRUE 1 +#define NVML_GPU_FABRIC_HEALTH_MASK_DEGRADED_BW_FALSE 2 + +#define NVML_GPU_FABRIC_HEALTH_MASK_SHIFT_DEGRADED_BW 0 +#define NVML_GPU_FABRIC_HEALTH_MASK_WIDTH_DEGRADED_BW 0x11 + +/** + * GPU Fabric Health Status Mask for various fields can be obtained + * using the below macro. + * Ex - NVML_GPU_FABRIC_HEALTH_GET(var, _DEGRADED_BW) + */ +#define NVML_GPU_FABRIC_HEALTH_GET(var, type) \ + (((var) >> NVML_GPU_FABRIC_HEALTH_MASK_SHIFT##type) & \ + (NVML_GPU_FABRIC_HEALTH_MASK_WIDTH##type)) + +/** + * GPU Fabric Health Status Mask for various fields can be tested + * using the below macro. + * Ex - NVML_GPU_FABRIC_HEALTH_TEST(var, _DEGRADED_BW, _TRUE) + */ +#define NVML_GPU_FABRIC_HEALTH_TEST(var, type, val) \ + (NVML_GPU_FABRIC_HEALTH_GET(var, type) == \ + NVML_GPU_FABRIC_HEALTH_MASK##type##val) + +/** +* GPU Fabric information (v2). +* +* Version 2 adds the \ref nvmlGpuFabricInfo_v2_t.version field +* to the start of the structure, and the \ref nvmlGpuFabricInfo_v2_t.healthMask +* field to the end. This structure is not backwards-compatible with +* \ref nvmlGpuFabricInfo_t. +*/ +typedef struct { + unsigned int version; //!< Structure version identifier (set to \ref nvmlGpuFabricInfo_v2) + unsigned char clusterUuid[NVML_GPU_FABRIC_UUID_LEN]; //!< Uuid of the cluster to which this GPU belongs + nvmlReturn_t status; //!< Error status, if any. Must be checked only if state returns "complete". + unsigned int cliqueId; //!< ID of the fabric clique to which this GPU belongs + nvmlGpuFabricState_t state; //!< Current state of GPU registration process + unsigned int healthMask; //!< GPU Fabric health Status Mask +} nvmlGpuFabricInfo_v2_t; + +typedef nvmlGpuFabricInfo_v2_t nvmlGpuFabricInfoV_t; + +/** +* Version identifier value for \ref nvmlGpuFabricInfo_v2_t.version. +*/ +#define nvmlGpuFabricInfo_v2 NVML_STRUCT_VERSION(GpuFabricInfo, 2) + /** * Device Scope - This is useful to retrieve the telemetry at GPU and module (e.g. GPU + CPU) level */ @@ -3414,6 +3685,19 @@ nvmlReturn_t DECLDIR nvmlDeviceSetCpuAffinity(nvmlDevice_t device); */ nvmlReturn_t DECLDIR nvmlDeviceClearCpuAffinity(nvmlDevice_t device); +/** + * Get the NUMA node of the given GPU device. + * This only applies to platforms where the GPUs are NUMA nodes. + * + * @param[in] device The device handle + * @param[out] node NUMA node ID of the device + * + * @returns + * - \ref NVML_SUCCESS if the NUMA node is retrieved successfully + * - \ref NVML_ERROR_NOT_SUPPORTED if request is not supported on the current platform + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device \a node is invalid + */ +nvmlReturn_t DECLDIR nvmlDeviceGetNumaNodeId(nvmlDevice_t device, unsigned int *node); /** * Retrieve the common ancestor for two devices * For all products. @@ -3726,6 +4010,25 @@ nvmlReturn_t DECLDIR nvmlDeviceGetDisplayActive(nvmlDevice_t device, nvmlEnableS */ nvmlReturn_t DECLDIR nvmlDeviceGetPersistenceMode(nvmlDevice_t device, nvmlEnableState_t *mode); +/** + * Retrieves PCI attributes of this device. + * + * For all products. + * + * See \ref nvmlPciInfoExt_t for details on the available PCI info. + * + * @param device The identifier of the target device + * @param pci Reference in which to return the PCI info + * + * @return + * - \ref NVML_SUCCESS if \a pci has been populated + * - \ref NVML_ERROR_UNINITIALIZED if the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid or \a pci is NULL + * - \ref NVML_ERROR_GPU_IS_LOST if the target GPU has fallen off the bus or is otherwise inaccessible + * - \ref NVML_ERROR_UNKNOWN on any unexpected error + */ +nvmlReturn_t DECLDIR nvmlDeviceGetPciInfoExt(nvmlDevice_t device, nvmlPciInfoExt_t *pci); + /** * Retrieves the PCI attributes of this device. * @@ -5423,7 +5726,7 @@ nvmlReturn_t DECLDIR nvmlDeviceGetMPSComputeRunningProcesses_v3(nvmlDevice_t dev * * To determine the size of the @ref plist->procArray array to allocate, call the function with * @ref plist->numProcArrayEntries set to zero and @ref plist->procArray set to NULL. The return - * code will be either NVML_ERROR_INSUFFICIENT_SIZE (if there are valid processes of type + * code will be either NVML_ERROR_INSUFFICIENT_SIZE (if there are valid processes of type * @ref plist->mode to report on, in which case the @ref plist->numProcArrayEntries field will * indicate the required number of entries in the array) or NVML_SUCCESS (if no processes of type * @ref plist->mode exist). @@ -5708,7 +6011,7 @@ nvmlReturn_t DECLDIR nvmlDeviceGetPcieSpeed(nvmlDevice_t device, unsigned int *p * * @param device The identifier of the target device * @param adaptiveClockStatus The current adaptive clocking status, either - * @ref NVML_ADAPTIVE_CLOCKING_INFO_STATUS_DISABLED + * @ref NVML_ADAPTIVE_CLOCKING_INFO_STATUS_DISABLED * or @ref NVML_ADAPTIVE_CLOCKING_INFO_STATUS_ENABLED * * @return @@ -5735,7 +6038,10 @@ nvmlReturn_t DECLDIR nvmlDeviceGetAdaptiveClockInfoStatus(nvmlDevice_t device, u */ nvmlReturn_t DECLDIR nvmlDeviceGetBusType(nvmlDevice_t device, nvmlBusType_t *type); -/** + + /** + * Deprecated: Will be deprecated in a future release. Use \ref nvmlDeviceGetGpuFabricInfoV instead + * * Get fabric information associated with the device. * * %HOPPER_OR_NEWER% @@ -5746,6 +6052,7 @@ nvmlReturn_t DECLDIR nvmlDeviceGetBusType(nvmlDevice_t device, nvmlBusType_t *ty * This API reports the current state of the GPU in the NVLink fabric * along with other useful information. * + * * @param device The identifier of the target device * @param gpuFabricInfo Information about GPU fabric state * @@ -5755,6 +6062,30 @@ nvmlReturn_t DECLDIR nvmlDeviceGetBusType(nvmlDevice_t device, nvmlBusType_t *ty */ nvmlReturn_t DECLDIR nvmlDeviceGetGpuFabricInfo(nvmlDevice_t device, nvmlGpuFabricInfo_t *gpuFabricInfo); +/** +* Versioned wrapper around \ref nvmlDeviceGetGpuFabricInfo that accepts a versioned +* \ref nvmlGpuFabricInfo_v2_t or later output structure. +* +* @note The caller must set the \ref nvmlGpuFabricInfoV_t.version field to the +* appropriate version prior to calling this function. For example: +* \code +* nvmlGpuFabricInfoV_t fabricInfo = +* { .version = nvmlGpuFabricInfo_v2 }; +* nvmlReturn_t result = nvmlDeviceGetGpuFabricInfoV(device,&fabricInfo); +* \endcode +* +* %HOPPER_OR_NEWER% +* +* @param device The identifier of the target device +* @param gpuFabricInfo Information about GPU fabric state +* +* @return +* - \ref NVML_SUCCESS Upon success +* - \ref NVML_ERROR_NOT_SUPPORTED If \a device doesn't support gpu fabric +*/ +nvmlReturn_t DECLDIR nvmlDeviceGetGpuFabricInfoV(nvmlDevice_t device, + nvmlGpuFabricInfoV_t *gpuFabricInfo); + /** * Get Conf Computing System capabilities. * @@ -5877,6 +6208,42 @@ nvmlReturn_t DECLDIR nvmlDeviceGetConfComputeGpuCertificate(nvmlDevice_t device, */ nvmlReturn_t DECLDIR nvmlDeviceGetConfComputeGpuAttestationReport(nvmlDevice_t device, nvmlConfComputeGpuAttestationReport_t *gpuAtstReport); +/** + * Get Conf Computing key rotation threshold detail. + * + * %HOPPER_OR_NEWER% + * Supported on Linux, Windows TCC. + * + * @param pKeyRotationThrInfo Reference in which to return the key rotation threshold data + * + * @return + * - \ref NVML_SUCCESS if \a gpu key rotation threshold info has been populated + * - \ref NVML_ERROR_UNINITIALIZED if the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid or \a memory is NULL + * - \ref NVML_ERROR_NOT_SUPPORTED if this query is not supported by the device + * - \ref NVML_ERROR_UNKNOWN on any unexpected error + */ +nvmlReturn_t DECLDIR nvmlSystemGetConfComputeKeyRotationThresholdInfo( + nvmlConfComputeGetKeyRotationThresholdInfo_t *pKeyRotationThrInfo); + +/** + * Get Conf Computing System Settings. + * + * %HOPPER_OR_NEWER% + * Supported on Linux, Windows TCC. + * + * @param settings System CC settings + * + * @return + * - \ref NVML_SUCCESS if the query is success + * - \ref NVML_ERROR_UNINITIALIZED if the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid or \a counters is NULL + * - \ref NVML_ERROR_NOT_SUPPORTED if the device does not support this feature + * - \ref NVML_ERROR_GPU_IS_LOST if the target GPU has fallen off the bus or is otherwise inaccessible + * - \ref NVML_ERROR_ARGUMENT_VERSION_MISMATCH if the provided version is invalid/unsupported + * - \ref NVML_ERROR_UNKNOWN on any unexpected error + */ +nvmlReturn_t DECLDIR nvmlSystemGetConfComputeSettings(nvmlSystemConfComputeSettings_t *settings); /** * Retrieve GSP firmware version. @@ -6241,6 +6608,52 @@ nvmlReturn_t DECLDIR nvmlDeviceGetClkMonStatus(nvmlDevice_t device, nvmlClkMonSt nvmlReturn_t DECLDIR nvmlDeviceGetProcessUtilization(nvmlDevice_t device, nvmlProcessUtilizationSample_t *utilization, unsigned int *processSamplesCount, unsigned long long lastSeenTimeStamp); +/** + * Retrieves the recent utilization and process ID for all running processes + * + * For Maxwell &tm; or newer fully supported devices. + * + * Reads recent utilization of GPU SM (3D/Compute), framebuffer, video encoder, and video decoder, jpeg decoder, OFA (Optical Flow Accelerator) + * for all running processes. Utilization values are returned as an array of utilization sample structures in the caller-supplied buffer pointed at + * by \a procesesUtilInfo->procUtilArray. One utilization sample structure is returned per process running, that had some non-zero utilization + * during the last sample period. It includes the CPU timestamp at which the samples were recorded. Individual utilization values + * are returned as "unsigned int" values. + * + * The caller should allocate a buffer of size processSamplesCount * sizeof(nvmlProcessUtilizationInfo_t). If the buffer is too small, the API will + * return \a NVML_ERROR_INSUFFICIENT_SIZE, with the recommended minimal buffer size at \a procesesUtilInfo->processSamplesCount. The caller should + * invoke the function again with the allocated buffer passed in \a procesesUtilInfo->procUtilArray, and \a procesesUtilInfo->processSamplesCount + * set to the number no less than the recommended value by the previous API return. + * + * On successful return, the function updates \a procesesUtilInfo->processSamplesCount with the number of process utilization info structures + * that were actually written. This may differ from a previously read value as instances are created or destroyed. + * + * \a procesesUtilInfo->lastSeenTimeStamp represents the CPU timestamp in microseconds at which utilization samples were last read. Set it to 0 + * to read utilization based on all the samples maintained by the driver's internal sample buffer. Set \a procesesUtilInfo->lastSeenTimeStamp + * to a timeStamp retrieved from a previous query to read utilization since the previous query. + * + * \a procesesUtilInfo->version is the version number of the structure nvmlProcessesUtilizationInfo_t, the caller should set the correct version + * number to retrieve the specific version of processes utilization information. + * + * @note On MIG-enabled GPUs, querying process utilization is not currently supported. + * + * @param device The identifier of the target device + * @param procesesUtilInfo Pointer to the caller-provided structure of nvmlProcessesUtilizationInfo_t. + + * @return + * - \ref NVML_SUCCESS if \a procesesUtilInfo->procUtilArray has been populated + * - \ref NVML_ERROR_UNINITIALIZED if the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid, or \a procesesUtilInfo is NULL + * - \ref NVML_ERROR_NOT_SUPPORTED if the device does not support this feature + * - \ref NVML_ERROR_NOT_FOUND if sample entries are not found + * - \ref NVML_ERROR_GPU_IS_LOST if the target GPU has fallen off the bus or is otherwise inaccessible + * - \ref NVML_ERROR_VERSION_MISMATCH if the version of \a procesesUtilInfo is invalid + * - \ref NVML_ERROR_INSUFFICIENT_SIZE if \a procesesUtilInfo->procUtilArray is NULL, or the buffer size of procesesUtilInfo->procUtilArray is too small. + * The caller should check the minimul array size from the returned procesesUtilInfo->processSamplesCount, and call + * the function again with a buffer no smaller than procesesUtilInfo->processSamplesCount * sizeof(nvmlProcessUtilizationInfo_t) + * - \ref NVML_ERROR_UNKNOWN on any unexpected error + */ +nvmlReturn_t DECLDIR nvmlDeviceGetProcessesUtilizationInfo(nvmlDevice_t device, nvmlProcessesUtilizationInfo_t *procesesUtilInfo); + /** @} */ /***************************************************************************************************/ @@ -6960,6 +7373,30 @@ nvmlReturn_t DECLDIR nvmlDeviceSetConfComputeUnprotectedMemSize(nvmlDevice_t dev */ nvmlReturn_t DECLDIR nvmlSystemSetConfComputeGpusReadyState(unsigned int isAcceptingWork); +/** + * Set Conf Computing key rotation threshold. + * + * %HOPPER_OR_NEWER% + * Supported on Linux, Windows TCC. + * + * This function is to set the confidential compute key rotation threshold parameters. + * @ref pKeyRotationThrInfo->maxAttackerAdvantage should be in the range from + * NVML_CC_KEY_ROTATION_THRESHOLD_ATTACKER_ADVANTAGE_MIN to NVML_CC_KEY_ROTATION_THRESHOLD_ATTACKER_ADVANTAGE_MAX. + * Default value is 60. + * + * @param pKeyRotationThrInfo Reference to the key rotation threshold data + * + * @return + * - \ref NVML_SUCCESS if \a key rotation threashold max attacker advantage has been set + * - \ref NVML_ERROR_UNINITIALIZED if the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid or \a memory is NULL + * - \ref NVML_ERROR_INVALID_STATE if confidential compute GPU ready state is enabled + * - \ref NVML_ERROR_NOT_SUPPORTED if this query is not supported by the device + * - \ref NVML_ERROR_UNKNOWN on any unexpected error + */ +nvmlReturn_t DECLDIR nvmlSystemSetConfComputeKeyRotationThresholdInfo( + nvmlConfComputeSetKeyRotationThresholdInfo_t *pKeyRotationThrInfo); + /** * @} */ @@ -7648,15 +8085,180 @@ nvmlReturn_t DECLDIR nvmlDeviceGetHostVgpuMode(nvmlDevice_t device, nvmlHostVgpu * @param virtualMode virtualization mode. One of NVML_GPU_VIRTUALIZATION_? * * @return - * - \ref NVML_SUCCESS if \a pVirtualMode is set + * - \ref NVML_SUCCESS if \a virtualMode is set * - \ref NVML_ERROR_UNINITIALIZED if the library has not been successfully initialized - * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid or \a pVirtualMode is NULL + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid or \a virtualMode is NULL * - \ref NVML_ERROR_GPU_IS_LOST if the target GPU has fallen off the bus or is otherwise inaccessible * - \ref NVML_ERROR_NOT_SUPPORTED if setting of virtualization mode is not supported. * - \ref NVML_ERROR_NO_PERMISSION if setting of virtualization mode is not allowed for this client. */ nvmlReturn_t DECLDIR nvmlDeviceSetVirtualizationMode(nvmlDevice_t device, nvmlGpuVirtualizationMode_t virtualMode); +/** + * Get the vGPU heterogeneous mode for the device. + * + * When in heterogeneous mode, a vGPU can concurrently host timesliced vGPUs with differing framebuffer sizes. + * + * On successful return, the function returns \a pHeterogeneousMode->mode with the current vGPU heterogeneous mode. + * \a pHeterogeneousMode->version is the version number of the structure nvmlVgpuHeterogeneousMode_t, the caller should + * set the correct version number to retrieve the vGPU heterogeneous mode. + * \a pHeterogeneousMode->mode can either be \ref NVML_FEATURE_ENABLED or \ref NVML_FEATURE_DISABLED. + * + * @param device The identifier of the target device + * @param pHeterogeneousMode Pointer to the caller-provided structure of nvmlVgpuHeterogeneousMode_t + * + * @return + * - \ref NVML_SUCCESS Upon success + * - \ref NVML_ERROR_UNINITIALIZED If library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT If \a device is invalid or \a pHeterogeneousMode is NULL + * - \ref NVML_ERROR_NOT_SUPPORTED If \a device doesn't support this feature + * - \ref NVML_ERROR_VERSION_MISMATCH If the version of \a pHeterogeneousMode is invalid + * - \ref NVML_ERROR_UNKNOWN On any unexpected error + */ +nvmlReturn_t DECLDIR nvmlDeviceGetVgpuHeterogeneousMode(nvmlDevice_t device, nvmlVgpuHeterogeneousMode_t *pHeterogeneousMode); + +/** + * Enable or disable vGPU heterogeneous mode for the device. + * + * When in heterogeneous mode, a vGPU can concurrently host timesliced vGPUs with differing framebuffer sizes. + * + * API would return an appropriate error code upon unsuccessful activation. For example, the heterogeneous mode + * set will fail with error \ref NVML_ERROR_IN_USE if any vGPU instance is active on the device. The caller of this API + * is expected to shutdown the vGPU VMs and retry setting the \a mode. + * On successful return, the function updates the vGPU heterogeneous mode with the user provided \a pHeterogeneousMode->mode. + * \a pHeterogeneousMode->version is the version number of the structure nvmlVgpuHeterogeneousMode_t, the caller should + * set the correct version number to set the vGPU heterogeneous mode. + * + * @param device Identifier of the target device + * @param pHeterogeneousMode Pointer to the caller-provided structure of nvmlVgpuHeterogeneousMode_t + * + * @return + * - \ref NVML_SUCCESS Upon success + * - \ref NVML_ERROR_UNINITIALIZED If library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT If \a device or \a pHeterogeneousMode is NULL or \a pHeterogeneousMode->mode is invalid + * - \ref NVML_ERROR_IN_USE If the \a device is in use + * - \ref NVML_ERROR_NO_PERMISSION If user doesn't have permission to perform the operation + * - \ref NVML_ERROR_NOT_SUPPORTED If MIG is enabled or \a device doesn't support this feature + * - \ref NVML_ERROR_VERSION_MISMATCH If the version of \a pHeterogeneousMode is invalid + * - \ref NVML_ERROR_UNKNOWN On any unexpected error + */ +nvmlReturn_t DECLDIR nvmlDeviceSetVgpuHeterogeneousMode(nvmlDevice_t device, const nvmlVgpuHeterogeneousMode_t *pHeterogeneousMode); + +/** + * Query the placement ID of active vGPU instance. + * + * When in vGPU heterogeneous mode, this function returns a valid placement ID as \a pPlacement->placementId + * else NVML_INVALID_VGPU_PLACEMENT_ID is returned. + * \a pPlacement->version is the version number of the structure nvmlVgpuPlacementId_t, the caller should + * set the correct version number to get placement id of the vGPU instance \a vgpuInstance. + * + * @param vgpuInstance Identifier of the target vGPU instance + * @param pPlacement Pointer to vGPU placement ID structure \a nvmlVgpuPlacementId_t + * + * @return + * - \ref NVML_SUCCESS If information is successfully retrieved + * - \ref NVML_ERROR_NOT_FOUND If \a vgpuInstance does not match a valid active vGPU instance + * - \ref NVML_ERROR_INVALID_ARGUMENT If \a vgpuInstance is invalid or \a pPlacement is NULL + * - \ref NVML_ERROR_VERSION_MISMATCH If the version of \a pPlacement is invalid + * - \ref NVML_ERROR_UNKNOWN On any unexpected error + */ +nvmlReturn_t DECLDIR nvmlVgpuInstanceGetPlacementId(nvmlVgpuInstance_t vgpuInstance, nvmlVgpuPlacementId_t *pPlacement); + +/** + * Query the supported vGPU placement ID of the vGPU type. + * + * An array of supported vGPU placement IDs for the vGPU type ID indicated by \a vgpuTypeId is returned in the + * caller-supplied buffer of \a pPlacementList->placementIds. Memory needed for the placementIds array should be + * allocated based on maximum instances of a vGPU type which can be queried via \ref nvmlVgpuTypeGetMaxInstances(). + * + * This function will return supported placement IDs even if GPU is not in vGPU heterogeneous mode. + * + * @param device Identifier of the target device + * @param vgpuTypeId Handle to vGPU type. The vGPU type ID + * @param pPlacementList Pointer to the vGPU placement structure \a nvmlVgpuPlacementList_t + * + * @return + * - \ref NVML_SUCCESS Upon success + * - \ref NVML_ERROR_UNINITIALIZED If library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT If \a device or \a vgpuTypeId is invalid or \a pPlacementList is NULL + * - \ref NVML_ERROR_NOT_SUPPORTED If \a device or \a vgpuTypeId isn't supported + * - \ref NVML_ERROR_NO_PERMISSION If user doesn't have permission to perform the operation + * - \ref NVML_ERROR_VERSION_MISMATCH If the version of \a pPlacementList is invalid + * - \ref NVML_ERROR_UNKNOWN On any unexpected error + */ +nvmlReturn_t DECLDIR nvmlDeviceGetVgpuTypeSupportedPlacements(nvmlDevice_t device, nvmlVgpuTypeId_t vgpuTypeId, nvmlVgpuPlacementList_t *pPlacementList); + +/** + * Query the creatable vGPU placement ID of the vGPU type. + * + * An array of creatable vGPU placement IDs for the vGPU type ID indicated by \a vgpuTypeId is returned in the + * caller-supplied buffer of \a pPlacementList->placementIds. Memory needed for the placementIds array should be + * allocated based on maximum instances of a vGPU type which can be queried via \ref nvmlVgpuTypeGetMaxInstances(). + * The creatable vGPU placement IDs may differ over time, as there may be restrictions on what type of vGPU the + * vGPU instance is running. + * + * The function will return \ref NVML_ERROR_NOT_SUPPORTED if the \a device is not in vGPU heterogeneous mode. + * + * @param device The identifier of the target device + * @param vgpuTypeId Handle to vGPU type. The vGPU type ID + * @param pPlacementList Pointer to the list of vGPU placement structure \a nvmlVgpuPlacementList_t + * + * @return + * - \ref NVML_SUCCESS Upon success + * - \ref NVML_ERROR_UNINITIALIZED If library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT If \a device or \a vgpuTypeId is invalid or \a pPlacementList is NULL + * - \ref NVML_ERROR_NOT_SUPPORTED If \a device or \a vgpuTypeId isn't supported + * - \ref NVML_ERROR_NO_PERMISSION If user doesn't have permission to perform the operation + * - \ref NVML_ERROR_VERSION_MISMATCH If the version of \a pPlacementList is invalid + * - \ref NVML_ERROR_UNKNOWN On any unexpected error + */ +nvmlReturn_t DECLDIR nvmlDeviceGetVgpuTypeCreatablePlacements(nvmlDevice_t device, nvmlVgpuTypeId_t vgpuTypeId, nvmlVgpuPlacementList_t *pPlacementList); + +/** + * Retrieve the static GSP heap size of the vGPU type in bytes + * + * @param vgpuTypeId Handle to vGPU type + * @param gspHeapSize Reference to return the GSP heap size value + * @return + * - \ref NVML_SUCCESS Successful completion + * - \ref NVML_ERROR_UNINITIALIZED If the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT If \a vgpuTypeId is invalid, or \a gspHeapSize is NULL + * - \ref NVML_ERROR_UNKNOWN On any unexpected error + */ +nvmlReturn_t DECLDIR nvmlVgpuTypeGetGspHeapSize(nvmlVgpuTypeId_t vgpuTypeId, unsigned long long *gspHeapSize); + +/** + * Retrieve the static framebuffer reservation of the vGPU type in bytes + * + * @param vgpuTypeId Handle to vGPU type + * @param fbReservation Reference to return the framebuffer reservation + * @return + * - \ref NVML_SUCCESS Successful completion + * - \ref NVML_ERROR_UNINITIALIZED If the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT If \a vgpuTypeId is invalid, or \a fbReservation is NULL + * - \ref NVML_ERROR_UNKNOWN On any unexpected error + */ +nvmlReturn_t DECLDIR nvmlVgpuTypeGetFbReservation(nvmlVgpuTypeId_t vgpuTypeId, unsigned long long *fbReservation); + +/** + * Set the desirable vGPU capability of a device + * + * Refer to the \a nvmlDeviceVgpuCapability_t structure for the specific capabilities that can be set. + * See \ref nvmlEnableState_t for available state. + * + * @param device The identifier of the target device + * @param capability Specifies the \a nvmlDeviceVgpuCapability_t to be set + * @param state The target capability mode + * + * @return + * - \ref NVML_SUCCESS Successful completion + * - \ref NVML_ERROR_UNINITIALIZED If the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT If \a device is invalid, or \a capability is invalid, or \a state is invalid + * - \ref NVML_ERROR_NOT_SUPPORTED The API is not supported in current state, or \a device not in vGPU mode + * - \ref NVML_ERROR_UNKNOWN On any unexpected error +*/ +nvmlReturn_t DECLDIR nvmlDeviceSetVgpuCapabilities(nvmlDevice_t device, nvmlDeviceVgpuCapability_t capability, nvmlEnableState_t state); + /** * Retrieve the vGPU Software licensable features. * @@ -8774,6 +9376,52 @@ nvmlReturn_t DECLDIR nvmlDeviceGetVgpuUtilization(nvmlDevice_t device, unsigned nvmlValueType_t *sampleValType, unsigned int *vgpuInstanceSamplesCount, nvmlVgpuInstanceUtilizationSample_t *utilizationSamples); +/** + * Retrieves recent utilization for vGPU instances running on a physical GPU (device). + * + * For Kepler &tm; or newer fully supported devices. + * + * Reads recent utilization of GPU SM (3D/Compute), framebuffer, video encoder, video decoder, jpeg decoder, and OFA for vGPU + * instances running on a device. Utilization values are returned as an array of utilization sample structures in the caller-supplied + * buffer pointed at by \a vgpuUtilInfo->vgpuUtilArray. One utilization sample structure is returned per vGPU instance, and includes the + * CPU timestamp at which the samples were recorded. Individual utilization values are returned as "unsigned int" values + * in nvmlValue_t unions. The function sets the caller-supplied \a vgpuUtilInfo->sampleValType to NVML_VALUE_TYPE_UNSIGNED_INT to + * indicate the returned value type. + * + * To read utilization values, first determine the size of buffer required to hold the samples by invoking the function with + * \a vgpuUtilInfo->vgpuUtilArray set to NULL. The function will return NVML_ERROR_INSUFFICIENT_SIZE, with the current vGPU instance + * count in \a vgpuUtilInfo->vgpuInstanceCount, or NVML_SUCCESS if the current vGPU instance count is zero. The caller should allocate + * a buffer of size vgpuUtilInfo->vgpuInstanceCount * sizeof(nvmlVgpuInstanceUtilizationInfo_t). Invoke the function again with + * the allocated buffer passed in \a vgpuUtilInfo->vgpuUtilArray, and \a vgpuUtilInfo->vgpuInstanceCount set to the number of entries the + * buffer is sized for. + * + * On successful return, the function updates \a vgpuUtilInfo->vgpuInstanceCount with the number of vGPU utilization sample + * structures that were actually written. This may differ from a previously read value as vGPU instances are created or + * destroyed. + * + * \a vgpuUtilInfo->lastSeenTimeStamp represents the CPU timestamp in microseconds at which utilization samples were last read. Set it to 0 + * to read utilization based on all the samples maintained by the driver's internal sample buffer. Set \a vgpuUtilInfo->lastSeenTimeStamp + * to a timeStamp retrieved from a previous query to read utilization since the previous query. + * + * @param device The identifier for the target device + * @param vgpuUtilInfo Pointer to the caller-provided structure of nvmlVgpuInstancesUtilizationInfo_t + + * @return + * - \ref NVML_SUCCESS if utilization samples are successfully retrieved + * - \ref NVML_ERROR_UNINITIALIZED if the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid, \a vgpuUtilInfo is NULL, or \a vgpuUtilInfo->vgpuInstanceCount is 0 + * - \ref NVML_ERROR_NOT_SUPPORTED if vGPU is not supported by the device + * - \ref NVML_ERROR_GPU_IS_LOST if the target GPU has fallen off the bus or is otherwise inaccessible + * - \ref NVML_ERROR_VERSION_MISMATCH if the version of \a vgpuUtilInfo is invalid + * - \ref NVML_ERROR_INSUFFICIENT_SIZE if \a vgpuUtilInfo->vgpuUtilArray is NULL, or the buffer size of vgpuUtilInfo->vgpuInstanceCount is too small. + * The caller should check the current vGPU instance count from the returned vgpuUtilInfo->vgpuInstanceCount, and call + * the function again with a buffer of size vgpuUtilInfo->vgpuInstanceCount * sizeof(nvmlVgpuInstanceUtilizationInfo_t) + * - \ref NVML_ERROR_NOT_FOUND if sample entries are not found + * - \ref NVML_ERROR_UNKNOWN on any unexpected error + */ +nvmlReturn_t DECLDIR nvmlDeviceGetVgpuInstancesUtilizationInfo(nvmlDevice_t device, + nvmlVgpuInstancesUtilizationInfo_t *vgpuUtilInfo); + /** * Retrieves current utilization for processes running on vGPUs on a physical GPU (device). * @@ -8820,6 +9468,52 @@ nvmlReturn_t DECLDIR nvmlDeviceGetVgpuUtilization(nvmlDevice_t device, unsigned nvmlReturn_t DECLDIR nvmlDeviceGetVgpuProcessUtilization(nvmlDevice_t device, unsigned long long lastSeenTimeStamp, unsigned int *vgpuProcessSamplesCount, nvmlVgpuProcessUtilizationSample_t *utilizationSamples); + +/** + * Retrieves recent utilization for processes running on vGPU instances on a physical GPU (device). + * + * For Maxwell &tm; or newer fully supported devices. + * + * Reads recent utilization of GPU SM (3D/Compute), framebuffer, video encoder, video decoder, jpeg decoder, and OFA for processes running + * on vGPU instances active on a device. Utilization values are returned as an array of utilization sample structures in the caller-supplied + * buffer pointed at by \a vgpuProcUtilInfo->vgpuProcUtilArray. One utilization sample structure is returned per process running + * on vGPU instances, that had some non-zero utilization during the last sample period. It includes the CPU timestamp at which + * the samples were recorded. Individual utilization values are returned as "unsigned int" values. + * + * To read utilization values, first determine the size of buffer required to hold the samples by invoking the function with + * \a vgpuProcUtilInfo->vgpuProcUtilArray set to NULL. The function will return NVML_ERROR_INSUFFICIENT_SIZE, with the current processes' count + * running on vGPU instances in \a vgpuProcUtilInfo->vgpuProcessCount. The caller should allocate a buffer of size + * vgpuProcUtilInfo->vgpuProcessCount * sizeof(nvmlVgpuProcessUtilizationSample_t). Invoke the function again with the allocated buffer passed + * in \a vgpuProcUtilInfo->vgpuProcUtilArray, and \a vgpuProcUtilInfo->vgpuProcessCount set to the number of entries the buffer is sized for. + * + * On successful return, the function updates \a vgpuProcUtilInfo->vgpuProcessCount with the number of vGPU sub process utilization sample + * structures that were actually written. This may differ from a previously read value depending on the number of processes that are active + * in any given sample period. + * + * vgpuProcUtilInfo->lastSeenTimeStamp represents the CPU timestamp in microseconds at which utilization samples were last read. Set it to 0 + * to read utilization based on all the samples maintained by the driver's internal sample buffer. Set vgpuProcUtilInfo->lastSeenTimeStamp + * to a timeStamp retrieved from a previous query to read utilization since the previous query. + * + * @param device The identifier for the target device + * @param vgpuProcUtilInfo Pointer to the caller-provided structure of nvmlVgpuProcessesUtilizationInfo_t + + * @return + * - \ref NVML_SUCCESS if utilization samples are successfully retrieved + * - \ref NVML_ERROR_UNINITIALIZED if the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid, or \a vgpuProcUtilInfo is null + * - \ref NVML_ERROR_VERSION_MISMATCH if the version of \a vgpuProcUtilInfo is invalid + * - \ref NVML_ERROR_INSUFFICIENT_SIZE if \a vgpuProcUtilInfo->vgpuProcUtilArray is null, or supplied \a vgpuProcUtilInfo->vgpuProcessCount + * is too small to return samples for all processes on vGPU instances currently executing on the device. + * The caller should check the current processes count from the returned \a vgpuProcUtilInfo->vgpuProcessCount, + * and call the function again with a buffer of size + * vgpuProcUtilInfo->vgpuProcessCount * sizeof(nvmlVgpuProcessUtilizationSample_t) + * - \ref NVML_ERROR_NOT_SUPPORTED if vGPU is not supported by the device + * - \ref NVML_ERROR_GPU_IS_LOST if the target GPU has fallen off the bus or is otherwise inaccessible + * - \ref NVML_ERROR_NOT_FOUND if sample entries are not found + * - \ref NVML_ERROR_UNKNOWN on any unexpected error + */ +nvmlReturn_t DECLDIR nvmlDeviceGetVgpuProcessesUtilizationInfo(nvmlDevice_t device, nvmlVgpuProcessesUtilizationInfo_t *vgpuProcUtilInfo); + /** * Queries the state of per process accounting mode on vGPU. * @@ -9304,7 +9998,7 @@ nvmlReturn_t DECLDIR nvmlDeviceSetMigMode(nvmlDevice_t device, unsigned int mode nvmlReturn_t DECLDIR nvmlDeviceGetMigMode(nvmlDevice_t device, unsigned int *currentMode, unsigned int *pendingMode); /** - * Get GPU instance profile information. + * Get GPU instance profile information * * Information provided by this API is immutable throughout the lifetime of a MIG mode. * @@ -9319,7 +10013,7 @@ nvmlReturn_t DECLDIR nvmlDeviceGetMigMode(nvmlDevice_t device, unsigned int *cur * - \ref NVML_SUCCESS Upon success * - \ref NVML_ERROR_UNINITIALIZED If library has not been successfully initialized * - \ref NVML_ERROR_INVALID_ARGUMENT If \a device, \a profile or \a info are invalid - * - \ref NVML_ERROR_NOT_SUPPORTED If \a device doesn't have MIG mode enabled or \a profile isn't supported + * - \ref NVML_ERROR_NOT_SUPPORTED If \a device doesn't support MIG or \a profile isn't supported * - \ref NVML_ERROR_NO_PERMISSION If user doesn't have permission to perform the operation */ nvmlReturn_t DECLDIR nvmlDeviceGetGpuInstanceProfileInfo(nvmlDevice_t device, unsigned int profile, @@ -9360,7 +10054,7 @@ nvmlReturn_t DECLDIR nvmlDeviceGetGpuInstanceProfileInfoV(nvmlDevice_t device, u * Get GPU instance placements. * * A placement represents the location of a GPU instance within a device. This API only returns all the possible - * placements for the given profile. + * placements for the given profile regardless of whether MIG is enabled or not. * A created GPU instance occupies memory slices described by its placement. Creation of new GPU instance will * fail if there is overlap with the already occupied memory slices. * @@ -9379,7 +10073,7 @@ nvmlReturn_t DECLDIR nvmlDeviceGetGpuInstanceProfileInfoV(nvmlDevice_t device, u * - \ref NVML_SUCCESS Upon success * - \ref NVML_ERROR_UNINITIALIZED If library has not been successfully initialized * - \ref NVML_ERROR_INVALID_ARGUMENT If \a device, \a profileId or \a count are invalid - * - \ref NVML_ERROR_NOT_SUPPORTED If \a device doesn't have MIG mode enabled or \a profileId isn't supported + * - \ref NVML_ERROR_NOT_SUPPORTED If \a device doesn't support MIG or \a profileId isn't supported * - \ref NVML_ERROR_NO_PERMISSION If user doesn't have permission to perform the operation */ nvmlReturn_t DECLDIR nvmlDeviceGetGpuInstancePossiblePlacements_v2(nvmlDevice_t device, unsigned int profileId, @@ -10287,7 +10981,7 @@ nvmlReturn_t DECLDIR nvmlSystemGetNvlinkBwMode(unsigned int *nvlinkBwMode); * - \ref NVML_ERROR_GPU_IS_LOST if the target GPU has fallen off the bus or is otherwise inaccessible * - \ref NVML_ERROR_UNKNOWN on any unexpected error * - * @see NVML_FI_DEV_POWER_AVERAGE + * @see NVML_FI_DEV_POWER_AVERAGE * @see NVML_FI_DEV_POWER_INSTANT * @see NVML_FI_DEV_POWER_MIN_LIMIT * @see NVML_FI_DEV_POWER_MAX_LIMIT @@ -10295,7 +10989,28 @@ nvmlReturn_t DECLDIR nvmlSystemGetNvlinkBwMode(unsigned int *nvlinkBwMode); */ nvmlReturn_t DECLDIR nvmlDeviceSetPowerManagementLimit_v2(nvmlDevice_t device, nvmlPowerValue_v2_t *powerValue); - +/** + * Get SRAM ECC error status of this device. + * + * For Ampere &tm; or newer fully supported devices. + * Requires root/admin permissions. + * + * See \ref nvmlEccSramErrorStatus_v1_t for more information on the struct. + * + * @param device The identifier of the target device + * @param status Returns SRAM ECC error status + * + * @return + * - \ref NVML_SUCCESS if \a limit has been set + * - \ref NVML_ERROR_UNINITIALIZED if the library has not been successfully initialized + * - \ref NVML_ERROR_INVALID_ARGUMENT if \a device is invalid or \a counters is NULL + * - \ref NVML_ERROR_NOT_SUPPORTED if the device does not support this feature + * - \ref NVML_ERROR_GPU_IS_LOST if the target GPU has fallen off the bus or is otherwise inaccessible + * - \ref NVML_ERROR_VERSION_MISMATCH if the version of \a nvmlEccSramErrorStatus_t is invalid + * - \ref NVML_ERROR_UNKNOWN on any unexpected error + */ +nvmlReturn_t DECLDIR nvmlDeviceGetSramEccErrorStatus(nvmlDevice_t device, + nvmlEccSramErrorStatus_t *status); /** * NVML API versioning support */ diff --git a/src/3rdparty/nvml/repo.json b/src/3rdparty/nvml/repo.json index ae01214633..525b4be27b 100644 --- a/src/3rdparty/nvml/repo.json +++ b/src/3rdparty/nvml/repo.json @@ -1,6 +1,6 @@ { "home": "https://github.com/NVIDIA/nvidia-settings/blob/main/src/nvml.h", "license": "Embed in source", - "version": "545.23.06", + "version": "550.54.14", "author": "NVIDIA Corporation" } From ae3c6f1e637a3ee8014dbce9934534303370dc54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 21 Apr 2024 19:07:42 +0800 Subject: [PATCH 33/40] GPU: detect max freq instead of current freq --- src/detection/gpu/gpu_intel.c | 25 ++++++++++++------------- src/detection/gpu/gpu_linux.c | 2 +- src/detection/gpu/gpu_nvidia.c | 6 +++--- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/detection/gpu/gpu_intel.c b/src/detection/gpu/gpu_intel.c index b41d6e8aef..c61992a461 100644 --- a/src/detection/gpu/gpu_intel.c +++ b/src/detection/gpu/gpu_intel.c @@ -14,7 +14,7 @@ struct FFIgclData { FF_LIBRARY_SYMBOL(ctlEnumMemoryModules) FF_LIBRARY_SYMBOL(ctlMemoryGetState) FF_LIBRARY_SYMBOL(ctlEnumFrequencyDomains) - FF_LIBRARY_SYMBOL(ctlFrequencyGetState) + FF_LIBRARY_SYMBOL(ctlFrequencyGetProperties) bool inited; ctl_api_handle_t apiHandle; @@ -44,7 +44,7 @@ const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverRe FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumMemoryModules) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlMemoryGetState) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumFrequencyDomains) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlFrequencyGetState) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlFrequencyGetProperties) if (ffctlInit(&(ctl_init_args_t) { .AppVersion = CTL_MAKE_VERSION(CTL_IMPL_MAJOR_VERSION, CTL_IMPL_MINOR_VERSION), @@ -177,22 +177,21 @@ const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverRe if (result.frequency) { - ctl_freq_handle_t freqs[16]; - uint32_t freqCount = sizeof(freqs) / sizeof(freqs[0]); - if (igclData.ffctlEnumFrequencyDomains(device, &freqCount, freqs) == CTL_RESULT_SUCCESS && freqCount > 0) + ctl_freq_handle_t domains[16]; + uint32_t domainCount = sizeof(domains) / sizeof(domains[0]); + if (igclData.ffctlEnumFrequencyDomains(device, &domainCount, domains) == CTL_RESULT_SUCCESS && domainCount > 0) { - double sumValue = 0; - uint32_t availableCount = 0; - for (uint32_t iFreq = 0; iFreq < freqCount; iFreq++) + double maxValue = 0; + ctl_freq_properties_t props = { .Size = sizeof(props), .Version = 0 }; + for (uint32_t iDomain = 0; iDomain < domainCount; iDomain++) { - ctl_freq_state_t state = { .Size = sizeof(state), .Version = 0 }; - if (igclData.ffctlFrequencyGetState(freqs[iFreq], &state) == CTL_RESULT_SUCCESS) + if (igclData.ffctlFrequencyGetProperties(domains[iDomain], &props) == CTL_RESULT_SUCCESS) { - sumValue += state.actual; - availableCount++; + if (props.type == CTL_FREQ_DOMAIN_GPU && props.max > maxValue) + maxValue = props.max; } } - *result.frequency = (sumValue / availableCount) / 1000.; + *result.frequency = maxValue / 1000; } } diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 7dba510132..b497a8a5aa 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -90,7 +90,7 @@ static void pciDetectVfreq(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) // Works for Intel GPUs // https://patchwork.kernel.org/project/intel-gfx/patch/1422039866-11572-3-git-send-email-ville.syrjala@linux.intel.com/ ffStrbufSetNS(buffer, pciDir->length - (uint32_t) strlen("device"), pciDir->chars); - ffStrbufAppendS(buffer, "gt_cur_freq_mhz"); + ffStrbufAppendS(buffer, "gt_max_freq_mhz"); char str[16]; ssize_t len = ffReadFileData(buffer->chars, sizeof(str) - 1, str); if (len > 1) diff --git a/src/detection/gpu/gpu_nvidia.c b/src/detection/gpu/gpu_nvidia.c index b55cd32584..33fdb527c8 100644 --- a/src/detection/gpu/gpu_nvidia.c +++ b/src/detection/gpu/gpu_nvidia.c @@ -11,7 +11,7 @@ struct FFNvmlData { FF_LIBRARY_SYMBOL(nvmlDeviceGetTemperature) FF_LIBRARY_SYMBOL(nvmlDeviceGetMemoryInfo_v2) FF_LIBRARY_SYMBOL(nvmlDeviceGetNumGpuCores) - FF_LIBRARY_SYMBOL(nvmlDeviceGetClockInfo) + FF_LIBRARY_SYMBOL(nvmlDeviceGetMaxClockInfo) FF_LIBRARY_SYMBOL(nvmlDeviceGetBrand) bool inited; @@ -32,7 +32,7 @@ const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverR FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetTemperature) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMemoryInfo_v2) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetNumGpuCores) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetClockInfo) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMaxClockInfo) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetBrand) if (ffnvmlInit_v2() != NVML_SUCCESS) @@ -122,7 +122,7 @@ const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverR if (result.frequency) { uint32_t clockMHz; - if (nvmlData.ffnvmlDeviceGetClockInfo(device, NVML_CLOCK_GRAPHICS, &clockMHz) == NVML_SUCCESS) + if (nvmlData.ffnvmlDeviceGetMaxClockInfo(device, NVML_CLOCK_GRAPHICS, &clockMHz) == NVML_SUCCESS) *result.frequency = clockMHz / 1000.; } From 8c2dd1aeac8f40d9aa53c35f0bd1280bcd2510c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 21 Apr 2024 20:12:42 +0800 Subject: [PATCH 34/40] IO: don't print ANSI escape codes in `--pipe` mode --- src/common/io/io_unix.c | 5 ++++- src/common/io/io_windows.c | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/common/io/io_unix.c b/src/common/io/io_unix.c index 2f1205260e..d204de1ea0 100644 --- a/src/common/io/io_unix.c +++ b/src/common/io/io_unix.c @@ -1,6 +1,6 @@ #include "io.h" +#include "fastfetch.h" #include "util/stringUtils.h" -#include "util/unused.h" #include #include @@ -133,6 +133,9 @@ bool ffPathExpandEnv(FF_MAYBE_UNUSED const char* in, FF_MAYBE_UNUSED FFstrbuf* o const char* ffGetTerminalResponse(const char* request, const char* format, ...) { + if (instance.config.display.pipe) + return "Not supported in --pipe mode"; + struct termios oldTerm, newTerm; if(tcgetattr(STDIN_FILENO, &oldTerm) == -1) return "tcgetattr(STDIN_FILENO, &oldTerm) failed"; diff --git a/src/common/io/io_windows.c b/src/common/io/io_windows.c index 1e0a889f52..be8e7e5738 100644 --- a/src/common/io/io_windows.c +++ b/src/common/io/io_windows.c @@ -1,4 +1,5 @@ #include "io.h" +#include "fastfetch.h" #include "util/stringUtils.h" #include @@ -174,6 +175,9 @@ void ffListFilesRecursively(const char* path, bool pretty) const char* ffGetTerminalResponse(const char* request, const char* format, ...) { + if (instance.config.display.pipe) + return "Not supported in --pipe mode"; + HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE); DWORD prev_mode; GetConsoleMode(hInput, &prev_mode); From 80f8b086caf496ab497ae31849b3316c030783ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 21 Apr 2024 20:13:22 +0800 Subject: [PATCH 35/40] Doc: update changelog --- CHANGELOG.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecf910a679..fb1081aa81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,23 @@ # 2.10.0 +Changes: +* We now always detect max frequency of GPUs for consistent, instead of current frequency + Features: -* We now use `wlr-randr` to detect displays for hyprland, which correctly reports fractional scale factors. Experimental (Display, Linux) -* Support GPU memory usage detection for AMD GPUs (GPU, Linux) -* Support GPU frequency detection for Intel GPUs (GPU, Linux) +* We now use `wlr-randr` to detect displays for hyprland, which correctly reports fractional scale factors (Display, Linux) +* Improve GPU detection on Linux (GPU, Linux) + * Support GPU memory usage detection for AMD GPUs + * Support GPU frequency detection for Intel GPUs * Improve performance of Gnome version detection (DE, Linux) * Improve performance of kitty version detection (Terminal, Linux) * Detect refresh rate when using `--ds-force-drm sysfs-only` (Display, Linux) -* Add option `--ts-version` to disable terminal and shell version detection +* Add option `--ts-version` to disable terminal and shell version detection. Mainly for benchmarking purposes +* Improve performance of detecting WSL version (Host, Linux) Bugfixes: -* Correctly detect `/bin/sh` as current shell if it's used as default shell (Shell, Linux) +* Correctly detect `/bin/sh` as current shell if it's used as default shell (#798, Shell, Linux) +* Work around an issue which CPU module reports incorrect CPU frequency that is too high (#800, CPU, Linux) +* Don't print ANSI escape codes in `--pipe` mode # 2.9.2 From 6647980ce34bd379f34aa742663520a4b45ae32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 21 Apr 2024 23:21:13 +0800 Subject: [PATCH 36/40] DisplayServer (Linux): refactor & support wlroots protocol --- CHANGELOG.md | 2 +- CMakeLists.txt | 11 +- .../displayserver/linux/displayserver_linux.c | 14 - src/detection/displayserver/linux/wayland.c | 312 ---- .../linux/wayland/global-output.c | 134 ++ .../displayserver/linux/wayland/wayland.c | 149 ++ .../displayserver/linux/wayland/wayland.h | 57 + ...t-management-unstable-v1-client-protocol.h | 1298 +++++++++++++++++ ...r-output-management-unstable-v1-protocol.c | 160 ++ .../displayserver/linux/wayland/zwlr-output.c | 191 +++ src/detection/displayserver/linux/wlroots.c | 111 -- 11 files changed, 1998 insertions(+), 441 deletions(-) delete mode 100644 src/detection/displayserver/linux/wayland.c create mode 100644 src/detection/displayserver/linux/wayland/global-output.c create mode 100644 src/detection/displayserver/linux/wayland/wayland.c create mode 100644 src/detection/displayserver/linux/wayland/wayland.h create mode 100644 src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h create mode 100644 src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c create mode 100644 src/detection/displayserver/linux/wayland/zwlr-output.c delete mode 100644 src/detection/displayserver/linux/wlroots.c diff --git a/CHANGELOG.md b/CHANGELOG.md index fb1081aa81..29e1d7fffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changes: * We now always detect max frequency of GPUs for consistent, instead of current frequency Features: -* We now use `wlr-randr` to detect displays for hyprland, which correctly reports fractional scale factors (Display, Linux) +* We now use `wlr-randr` to detect displays for wlroots based WMs, which correctly reports fractional scale factors (Display, Linux) * Improve GPU detection on Linux (GPU, Linux) * Support GPU memory usage detection for AMD GPUs * Support GPU frequency detection for Intel GPUs diff --git a/CMakeLists.txt b/CMakeLists.txt index be5a66486d..05e7ad5002 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -400,8 +400,10 @@ if(LINUX) src/detection/diskio/diskio_linux.c src/detection/displayserver/linux/displayserver_linux.c src/detection/displayserver/linux/drm.c - src/detection/displayserver/linux/wlroots.c - src/detection/displayserver/linux/wayland.c + src/detection/displayserver/linux/wayland/wayland.c + src/detection/displayserver/linux/wayland/global-output.c + src/detection/displayserver/linux/wayland/zwlr-output.c + src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c src/detection/displayserver/linux/wmde.c src/detection/displayserver/linux/xcb.c src/detection/displayserver/linux/xlib.c @@ -519,7 +521,10 @@ elseif(BSD) src/detection/diskio/diskio_bsd.c src/detection/displayserver/linux/displayserver_linux.c src/detection/displayserver/linux/drm.c - src/detection/displayserver/linux/wayland.c + src/detection/displayserver/linux/wayland/wayland.c + src/detection/displayserver/linux/wayland/global-output.c + src/detection/displayserver/linux/wayland/zwlr-output.c + src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c src/detection/displayserver/linux/wmde.c src/detection/displayserver/linux/xcb.c src/detection/displayserver/linux/xlib.c diff --git a/src/detection/displayserver/linux/displayserver_linux.c b/src/detection/displayserver/linux/displayserver_linux.c index 713a2626a0..807facd9ec 100644 --- a/src/detection/displayserver/linux/displayserver_linux.c +++ b/src/detection/displayserver/linux/displayserver_linux.c @@ -11,20 +11,6 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) { if (instance.config.general.dsForceDrm == FF_DS_FORCE_DRM_TYPE_FALSE) { - #ifdef __linux__ - { - const char* desktopSession = getenv("DESKTOP_SESSION"); - if (desktopSession && ffStrEquals(desktopSession, "hyprland")) - { - ffStrbufSetStatic(&ds->wmProcessName, "xdg-desktop-portal-hyprland"); - ffStrbufSetStatic(&ds->wmPrettyName, FF_WM_PRETTY_HYPRLAND); - ffStrbufSetStatic(&ds->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); - if (ffdsConnectWlroots(ds) == NULL) - return; - } - } - #endif - //We try wayland as our preferred display server, as it supports the most features. //This method can't detect the name of our WM / DE ffdsConnectWayland(ds); diff --git a/src/detection/displayserver/linux/wayland.c b/src/detection/displayserver/linux/wayland.c deleted file mode 100644 index f987036170..0000000000 --- a/src/detection/displayserver/linux/wayland.c +++ /dev/null @@ -1,312 +0,0 @@ -#include "displayserver_linux.h" -#include "util/stringUtils.h" - -#include -#include - -#ifdef FF_HAVE_WAYLAND -#include "common/library.h" -#include "common/io/io.h" -#include "common/thread.h" - -#include -#include -#include -#include - -typedef struct WaylandData -{ - FFDisplayServerResult* result; - FF_LIBRARY_SYMBOL(wl_proxy_marshal_constructor_versioned) - FF_LIBRARY_SYMBOL(wl_proxy_add_listener) - FF_LIBRARY_SYMBOL(wl_proxy_destroy) - FF_LIBRARY_SYMBOL(wl_display_roundtrip) - struct wl_display* display; - const struct wl_interface* ffwl_output_interface; -} WaylandData; - -typedef struct WaylandDisplay -{ - int32_t width; - int32_t height; - int32_t refreshRate; - int32_t scale; - enum wl_output_transform transform; - FFDisplayType type; - FFstrbuf name; - FFstrbuf description; - FFstrbuf vendorAndModelId; - FFstrbuf edidName; -} WaylandDisplay; - -#ifndef __FreeBSD__ -static void waylandDetectWM(int fd, FFDisplayServerResult* result) -{ - struct ucred ucred; - socklen_t len = sizeof(struct ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) - return; - - FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreate(); - ffStrbufAppendF(&procPath, "/proc/%d/cmdline", ucred.pid); //We check the cmdline for the process name, because it is not trimmed. - ffReadFileBuffer(procPath.chars, &result->wmProcessName); - ffStrbufTrimRightSpace(&result->wmProcessName); - ffStrbufSubstrBeforeFirstC(&result->wmProcessName, '\0'); //Trim the arguments - ffStrbufSubstrAfterLastC(&result->wmProcessName, '/'); //Trim the path -} -#else -static void waylandDetectWM(int fd, FFDisplayServerResult* result) -{ - FF_UNUSED(fd, result); -} -#endif - -static void stubListener(void* data, ...) -{ - (void) data; -} - -static void waylandOutputModeListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refreshRate) -{ - if(!(flags & WL_OUTPUT_MODE_CURRENT)) - return; - - WaylandDisplay* display = data; - display->width = width; - display->height = height; - display->refreshRate = refreshRate; -} - -static void waylandOutputScaleListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, int32_t scale) -{ - WaylandDisplay* display = data; - display->scale = scale; -} - -static void waylandOutputGeometryListener(void *data, - FF_MAYBE_UNUSED struct wl_output *output, - FF_MAYBE_UNUSED int32_t x, - FF_MAYBE_UNUSED int32_t y, - FF_MAYBE_UNUSED int32_t physical_width, - FF_MAYBE_UNUSED int32_t physical_height, - FF_MAYBE_UNUSED int32_t subpixel, - const char *make, - const char *model, - int32_t transform) -{ - WaylandDisplay* display = data; - display->transform = (enum wl_output_transform) transform; - if (make && !ffStrEqualsIgnCase(make, "unknown") && model && !ffStrEqualsIgnCase(model, "unknown")) - { - ffStrbufAppendS(&display->vendorAndModelId, make); - ffStrbufAppendS(&display->vendorAndModelId, model); - } -} - -static void waylandOutputNameListener(void *data, FF_MAYBE_UNUSED struct wl_output *output, const char *name) -{ - WaylandDisplay* display = data; - if(ffStrStartsWith(name, "eDP-")) - display->type = FF_DISPLAY_TYPE_BUILTIN; - else if(ffStrStartsWith(name, "HDMI-")) - display->type = FF_DISPLAY_TYPE_EXTERNAL; - ffdsMatchDrmConnector(name, &display->edidName); - ffStrbufAppendS(&display->name, name); -} - -static void waylandOutputDescriptionListener(void* data, FF_MAYBE_UNUSED struct wl_output* wl_output, const char* description) -{ - WaylandDisplay* display = data; - while (*description == ' ') ++description; - if (!ffStrEquals(description, "Unknown Display")) - ffStrbufAppendS(&display->description, description); -} - -static void waylandOutputHandler(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) -{ - struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, wldata->ffwl_output_interface, version, name, wldata->ffwl_output_interface->name, version, NULL); - if(output == NULL) - return; - - WaylandDisplay display = { - .width = 0, - .height = 0, - .refreshRate = 0, - .scale = 1, - .transform = WL_OUTPUT_TRANSFORM_NORMAL, - .type = FF_DISPLAY_TYPE_UNKNOWN, - }; - ffStrbufInit(&display.name); - ffStrbufInit(&display.description); - ffStrbufInit(&display.vendorAndModelId); - ffStrbufInit(&display.edidName); - - // Dirty hack for #477 - // The order of these callbacks MUST follow `struct wl_output_listener` - void* outputListener[] = { - waylandOutputGeometryListener, // geometry - waylandOutputModeListener, // mode - stubListener, // done - waylandOutputScaleListener, // scale - waylandOutputNameListener, // name - waylandOutputDescriptionListener, // description - }; - static_assert( - sizeof(outputListener) >= sizeof(struct wl_output_listener), - "sizeof(outputListener) is too small. Please report it to fastfetch github issue" - ); - - wldata->ffwl_proxy_add_listener(output, (void(**)(void)) &outputListener, &display); - wldata->ffwl_display_roundtrip(wldata->display); - wldata->ffwl_proxy_destroy(output); - - if(display.width <= 0 || display.height <= 0) - return; - - switch(display.transform) - { - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_270: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: { - int32_t temp = display.width; - display.width = display.height; - display.height = temp; - break; - } - default: - break; - } - - uint32_t rotation; - switch(display.transform) - { - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_90: - rotation = 90; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - case WL_OUTPUT_TRANSFORM_180: - rotation = 180; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - case WL_OUTPUT_TRANSFORM_270: - rotation = 270; - break; - default: - rotation = 0; - break; - } - - ffdsAppendDisplay(wldata->result, - (uint32_t) display.width, - (uint32_t) display.height, - display.refreshRate / 1000.0, - (uint32_t) (display.width / display.scale), - (uint32_t) (display.height / display.scale), - rotation, - display.edidName.length - ? &display.edidName - : display.description.length - ? &display.description - : display.vendorAndModelId.length - ? &display.vendorAndModelId : &display.name, - display.type, - false, - 0 - ); - - ffStrbufDestroy(&display.description); - ffStrbufDestroy(&display.vendorAndModelId); - ffStrbufDestroy(&display.name); - ffStrbufDestroy(&display.edidName); -} - -static void waylandGlobalAddListener(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) -{ - WaylandData* wldata = data; - - if(ffStrEquals(interface, wldata->ffwl_output_interface->name)) - waylandOutputHandler(wldata, registry, name, version); -} - -bool detectWayland(FFDisplayServerResult* result) -{ - FF_LIBRARY_LOAD(wayland, &instance.config.library.libWayland, false, "libwayland-client" FF_LIBRARY_EXTENSION, 1) - - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_connect, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_get_fd, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_dispatch, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_proxy_marshal_constructor, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_disconnect, false) - FF_LIBRARY_LOAD_SYMBOL(wayland, wl_registry_interface, false) - - WaylandData data; - - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_marshal_constructor_versioned, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_add_listener, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_destroy, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_display_roundtrip, false) - FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_output_interface, false) - - data.display = ffwl_display_connect(NULL); - if(data.display == NULL) - return false; - - waylandDetectWM(ffwl_display_get_fd(data.display), result); - - struct wl_proxy* registry = ffwl_proxy_marshal_constructor((struct wl_proxy*) data.display, WL_DISPLAY_GET_REGISTRY, ffwl_registry_interface, NULL); - if(registry == NULL) - { - ffwl_display_disconnect(data.display); - return false; - } - - data.result = result; - - struct wl_registry_listener registry_listener = { - .global = waylandGlobalAddListener, - .global_remove = (void*) stubListener - }; - - data.ffwl_proxy_add_listener(registry, (void(**)(void)) ®istry_listener, &data); - ffwl_display_dispatch(data.display); - data.ffwl_display_roundtrip(data.display); - - data.ffwl_proxy_destroy(registry); - ffwl_display_disconnect(data.display); - - //We successfully connected to wayland and detected the display. - //So we can set set the session type to wayland. - //This is used as an indicator that we are running wayland by the x11 backends. - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); - return true; -} -#endif - -void ffdsConnectWayland(FFDisplayServerResult* result) -{ - //Wayland requires this to be set - if(getenv("XDG_RUNTIME_DIR") == NULL) - return; - - #ifdef FF_HAVE_WAYLAND - if(detectWayland(result)) - return; - #endif - - const char* xdgSessionType = getenv("XDG_SESSION_TYPE"); - - //If XDG_SESSION_TYPE is set, and doesn't contain "wayland", we are probably not running in a wayland session. - if(xdgSessionType != NULL && strcasecmp(xdgSessionType, "wayland") != 0) - return; - - //If XDG_SESSION_TYPE is not set, check if WAYLAND_DISPLAY or WAYLAND_SOCKET is set. - //If not, there is no indicator for a wayland session - if(xdgSessionType == NULL && getenv("WAYLAND_DISPLAY") == NULL && getenv("WAYLAND_SOCKET") == NULL) - return; - - //We are probably running a wayland compositor at this point, - //but fastfetch was compiled without the required library, or loading the library failed. - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); -} diff --git a/src/detection/displayserver/linux/wayland/global-output.c b/src/detection/displayserver/linux/wayland/global-output.c new file mode 100644 index 0000000000..c38ceee03e --- /dev/null +++ b/src/detection/displayserver/linux/wayland/global-output.c @@ -0,0 +1,134 @@ +#ifdef FF_HAVE_WAYLAND + +#include "wayland.h" +#include "util/stringUtils.h" + +static void waylandOutputModeListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refreshRate) +{ + if(!(flags & WL_OUTPUT_MODE_CURRENT)) + return; + + WaylandDisplay* display = data; + display->width = width; + display->height = height; + display->refreshRate = refreshRate; +} + +static void waylandOutputScaleListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, int32_t scale) +{ + WaylandDisplay* display = data; + display->scale = scale; +} + +static void waylandOutputGeometryListener(void *data, + FF_MAYBE_UNUSED struct wl_output *output, + FF_MAYBE_UNUSED int32_t x, + FF_MAYBE_UNUSED int32_t y, + FF_MAYBE_UNUSED int32_t physical_width, + FF_MAYBE_UNUSED int32_t physical_height, + FF_MAYBE_UNUSED int32_t subpixel, + FF_MAYBE_UNUSED const char *make, + FF_MAYBE_UNUSED const char *model, + int32_t transform) +{ + WaylandDisplay* display = data; + display->transform = (enum wl_output_transform) transform; +} + +void ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) +{ + struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, wldata->ffwl_output_interface, version, name, wldata->ffwl_output_interface->name, version, NULL); + if(output == NULL) + return; + + WaylandDisplay display = { + .parent = wldata, + .width = 0, + .height = 0, + .refreshRate = 0, + .scale = 1, + .transform = WL_OUTPUT_TRANSFORM_NORMAL, + .type = FF_DISPLAY_TYPE_UNKNOWN, + .name = ffStrbufCreate(), + .description = ffStrbufCreate(), + .edidName = ffStrbufCreate(), + }; + + // Dirty hack for #477 + // The order of these callbacks MUST follow `struct wl_output_listener` + void* outputListener[] = { + waylandOutputGeometryListener, // geometry + waylandOutputModeListener, // mode + stubListener, // done + waylandOutputScaleListener, // scale + ffWaylandOutputNameListener, // name + ffWaylandOutputDescriptionListener, // description + }; + static_assert( + sizeof(outputListener) >= sizeof(struct wl_output_listener), + "sizeof(outputListener) is too small. Please report it to fastfetch github issue" + ); + + wldata->ffwl_proxy_add_listener(output, (void(**)(void)) &outputListener, &display); + wldata->ffwl_display_roundtrip(wldata->display); + wldata->ffwl_proxy_destroy(output); + + if(display.width <= 0 || display.height <= 0) + return; + + uint32_t rotation; + switch(display.transform) + { + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_90: + rotation = 90; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_180: + rotation = 180; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + case WL_OUTPUT_TRANSFORM_270: + rotation = 270; + break; + default: + rotation = 0; + break; + } + + switch(rotation) + { + case 90: + case 270: { + int32_t temp = display.width; + display.width = display.height; + display.height = temp; + break; + } + default: + break; + } + + ffdsAppendDisplay(wldata->result, + (uint32_t) display.width, + (uint32_t) display.height, + display.refreshRate / 1000.0, + (uint32_t) (display.width / display.scale), + (uint32_t) (display.height / display.scale), + rotation, + display.edidName.length + ? &display.edidName + : display.description.length + ? &display.description + : &display.name, + display.type, + false, + 0 + ); + + ffStrbufDestroy(&display.description); + ffStrbufDestroy(&display.name); + ffStrbufDestroy(&display.edidName); +} + +#endif diff --git a/src/detection/displayserver/linux/wayland/wayland.c b/src/detection/displayserver/linux/wayland/wayland.c new file mode 100644 index 0000000000..09ae7ae8e0 --- /dev/null +++ b/src/detection/displayserver/linux/wayland/wayland.c @@ -0,0 +1,149 @@ +#include "../displayserver_linux.h" +#include "common/io/io.h" +#include "util/stringUtils.h" + +#include +#include + +#ifdef FF_HAVE_WAYLAND + +#include + +#include "wayland.h" +#include "wlr-output-management-unstable-v1-client-protocol.h" + +#ifndef __FreeBSD__ +static void waylandDetectWM(int fd, FFDisplayServerResult* result) +{ + struct ucred ucred; + socklen_t len = sizeof(struct ucred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) + return; + + FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreate(); + ffStrbufAppendF(&procPath, "/proc/%d/cmdline", ucred.pid); //We check the cmdline for the process name, because it is not trimmed. + ffReadFileBuffer(procPath.chars, &result->wmProcessName); + ffStrbufTrimRightSpace(&result->wmProcessName); + ffStrbufSubstrBeforeFirstC(&result->wmProcessName, '\0'); //Trim the arguments + ffStrbufSubstrAfterLastC(&result->wmProcessName, '/'); //Trim the path +} +#else +static void waylandDetectWM(int fd, FFDisplayServerResult* result) +{ + FF_UNUSED(fd, result); +} +#endif + +static void waylandGlobalAddListener(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) +{ + WaylandData* wldata = data; + + if((wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_NONE || wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_GLOBAL) && ffStrEquals(interface, wldata->ffwl_output_interface->name)) + { + wldata->protocolType = FF_WAYLAND_PROTOCOL_TYPE_GLOBAL; + ffWaylandHandleGlobalOutput(wldata, registry, name, version); + } + else if((wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_NONE || wldata->protocolType == FF_WAYLAND_PROTOCOL_TYPE_ZWLR) && ffStrEquals(interface, zwlr_output_manager_v1_interface.name)) + { + wldata->protocolType = FF_WAYLAND_PROTOCOL_TYPE_ZWLR; + ffWaylandHandleZwlrOutput(wldata, registry, name, version); + } +} + +bool detectWayland(FFDisplayServerResult* result) +{ + FF_LIBRARY_LOAD(wayland, &instance.config.library.libWayland, false, "libwayland-client" FF_LIBRARY_EXTENSION, 1) + + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_connect, false) + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_get_fd, false) + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_proxy_marshal_constructor, false) + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_disconnect, false) + FF_LIBRARY_LOAD_SYMBOL(wayland, wl_registry_interface, false) + + WaylandData data = {}; + + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_marshal_constructor_versioned, false) + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_add_listener, false) + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_proxy_destroy, false) + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_display_roundtrip, false) + FF_LIBRARY_LOAD_SYMBOL_VAR(wayland, data, wl_output_interface, false) + + data.display = ffwl_display_connect(NULL); + if(data.display == NULL) + return false; + + waylandDetectWM(ffwl_display_get_fd(data.display), result); + + struct wl_proxy* registry = ffwl_proxy_marshal_constructor((struct wl_proxy*) data.display, WL_DISPLAY_GET_REGISTRY, ffwl_registry_interface, NULL); + if(registry == NULL) + { + ffwl_display_disconnect(data.display); + return false; + } + + data.result = result; + + struct wl_registry_listener registry_listener = { + .global = waylandGlobalAddListener, + .global_remove = (void*) stubListener + }; + + data.ffwl_proxy_add_listener(registry, (void(**)(void)) ®istry_listener, &data); + data.ffwl_display_roundtrip(data.display); + + data.ffwl_proxy_destroy(registry); + ffwl_display_disconnect(data.display); + + //We successfully connected to wayland and detected the display. + //So we can set set the session type to wayland. + //This is used as an indicator that we are running wayland by the x11 backends. + ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); + return true; +} + +void ffWaylandOutputNameListener(void* data, FF_MAYBE_UNUSED void* output, const char *name) +{ + WaylandDisplay* display = data; + if(ffStrStartsWith(name, "eDP-")) + display->type = FF_DISPLAY_TYPE_BUILTIN; + else if(ffStrStartsWith(name, "HDMI-")) + display->type = FF_DISPLAY_TYPE_EXTERNAL; + ffdsMatchDrmConnector(name, &display->edidName); + ffStrbufAppendS(&display->name, name); +} + +void ffWaylandOutputDescriptionListener(void* data, FF_MAYBE_UNUSED void* output, const char* description) +{ + WaylandDisplay* display = data; + while (*description == ' ') ++description; + if (!ffStrEquals(description, "Unknown Display") && !ffStrContains(description, "(null)")) + ffStrbufAppendS(&display->description, description); +} +#endif + +void ffdsConnectWayland(FFDisplayServerResult* result) +{ + //Wayland requires this to be set + if(getenv("XDG_RUNTIME_DIR") == NULL) + return; + + #ifdef FF_HAVE_WAYLAND + if(detectWayland(result)) + return; + #endif + + const char* xdgSessionType = getenv("XDG_SESSION_TYPE"); + + //If XDG_SESSION_TYPE is set, and doesn't contain "wayland", we are probably not running in a wayland session. + if(xdgSessionType != NULL && ffStrEqualsIgnCase(xdgSessionType, "wayland")) + return; + + //If XDG_SESSION_TYPE is not set, check if WAYLAND_DISPLAY or WAYLAND_SOCKET is set. + //If not, there is no indicator for a wayland session + if(xdgSessionType == NULL && getenv("WAYLAND_DISPLAY") == NULL && getenv("WAYLAND_SOCKET") == NULL) + return; + + //We are probably running a wayland compositor at this point, + //but fastfetch was compiled without the required library, or loading the library failed. + ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND); +} diff --git a/src/detection/displayserver/linux/wayland/wayland.h b/src/detection/displayserver/linux/wayland/wayland.h new file mode 100644 index 0000000000..4a65a98ad3 --- /dev/null +++ b/src/detection/displayserver/linux/wayland/wayland.h @@ -0,0 +1,57 @@ +#pragma once + +#ifdef FF_HAVE_WAYLAND + +#include "common/library.h" +#include "util/stringUtils.h" + +#include + +#include "../displayserver_linux.h" + +typedef enum WaylandProtocolType +{ + FF_WAYLAND_PROTOCOL_TYPE_NONE, + FF_WAYLAND_PROTOCOL_TYPE_GLOBAL, + FF_WAYLAND_PROTOCOL_TYPE_ZWLR, +} WaylandProtocolType; + +typedef struct WaylandData +{ + FFDisplayServerResult* result; + FF_LIBRARY_SYMBOL(wl_proxy_marshal_constructor_versioned) + FF_LIBRARY_SYMBOL(wl_proxy_add_listener) + FF_LIBRARY_SYMBOL(wl_proxy_destroy) + FF_LIBRARY_SYMBOL(wl_display_roundtrip) + struct wl_display* display; + const struct wl_interface* ffwl_output_interface; + WaylandProtocolType protocolType; +} WaylandData; + +typedef struct WaylandDisplay +{ + WaylandData* parent; + int32_t width; + int32_t height; + int32_t refreshRate; + double scale; + enum wl_output_transform transform; + FFDisplayType type; + FFstrbuf name; + FFstrbuf description; + FFstrbuf edidName; + void* internal; +} WaylandDisplay; + +inline static void stubListener(void* data, ...) +{ + (void) data; +} + +void ffWaylandOutputNameListener(void* data, FF_MAYBE_UNUSED void* output, const char *name); +void ffWaylandOutputDescriptionListener(void* data, FF_MAYBE_UNUSED void* output, const char* description); + +void ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version); +void ffWaylandHandleZwlrOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version); + +#endif diff --git a/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h new file mode 100644 index 0000000000..e00668ffdd --- /dev/null +++ b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h @@ -0,0 +1,1298 @@ +/* Generated by wayland-scanner 1.22.0 */ + +#ifndef WLR_OUTPUT_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define WLR_OUTPUT_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_wlr_output_management_unstable_v1 The wlr_output_management_unstable_v1 protocol + * protocol to configure output devices + * + * @section page_desc_wlr_output_management_unstable_v1 Description + * + * This protocol exposes interfaces to obtain and modify output device + * configuration. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + * + * @section page_ifaces_wlr_output_management_unstable_v1 Interfaces + * - @subpage page_iface_zwlr_output_manager_v1 - output device configuration manager + * - @subpage page_iface_zwlr_output_head_v1 - output device + * - @subpage page_iface_zwlr_output_mode_v1 - output mode + * - @subpage page_iface_zwlr_output_configuration_v1 - output configuration + * - @subpage page_iface_zwlr_output_configuration_head_v1 - head configuration + * @section page_copyright_wlr_output_management_unstable_v1 Copyright + *
+ *
+ * Copyright © 2019 Purism SPC
+ *
+ * Permission to use, copy, modify, distribute, and sell this
+ * software and its documentation for any purpose is hereby granted
+ * without fee, provided that the above copyright notice appear in
+ * all copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of
+ * the copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ * THIS SOFTWARE.
+ * 
+ */ +struct zwlr_output_configuration_head_v1; +struct zwlr_output_configuration_v1; +struct zwlr_output_head_v1; +struct zwlr_output_manager_v1; +struct zwlr_output_mode_v1; + +#ifndef ZWLR_OUTPUT_MANAGER_V1_INTERFACE +#define ZWLR_OUTPUT_MANAGER_V1_INTERFACE +/** + * @page page_iface_zwlr_output_manager_v1 zwlr_output_manager_v1 + * @section page_iface_zwlr_output_manager_v1_desc Description + * + * This interface is a manager that allows reading and writing the current + * output device configuration. + * + * Output devices that display pixels (e.g. a physical monitor or a virtual + * output in a window) are represented as heads. Heads cannot be created nor + * destroyed by the client, but they can be enabled or disabled and their + * properties can be changed. Each head may have one or more available modes. + * + * Whenever a head appears (e.g. a monitor is plugged in), it will be + * advertised via the head event. Immediately after the output manager is + * bound, all current heads are advertised. + * + * Whenever a head's properties change, the relevant wlr_output_head events + * will be sent. Not all head properties will be sent: only properties that + * have changed need to. + * + * Whenever a head disappears (e.g. a monitor is unplugged), a + * wlr_output_head.finished event will be sent. + * + * After one or more heads appear, change or disappear, the done event will + * be sent. It carries a serial which can be used in a create_configuration + * request to update heads properties. + * + * The information obtained from this protocol should only be used for output + * configuration purposes. This protocol is not designed to be a generic + * output property advertisement protocol for regular clients. Instead, + * protocols such as xdg-output should be used. + * @section page_iface_zwlr_output_manager_v1_api API + * See @ref iface_zwlr_output_manager_v1. + */ +/** + * @defgroup iface_zwlr_output_manager_v1 The zwlr_output_manager_v1 interface + * + * This interface is a manager that allows reading and writing the current + * output device configuration. + * + * Output devices that display pixels (e.g. a physical monitor or a virtual + * output in a window) are represented as heads. Heads cannot be created nor + * destroyed by the client, but they can be enabled or disabled and their + * properties can be changed. Each head may have one or more available modes. + * + * Whenever a head appears (e.g. a monitor is plugged in), it will be + * advertised via the head event. Immediately after the output manager is + * bound, all current heads are advertised. + * + * Whenever a head's properties change, the relevant wlr_output_head events + * will be sent. Not all head properties will be sent: only properties that + * have changed need to. + * + * Whenever a head disappears (e.g. a monitor is unplugged), a + * wlr_output_head.finished event will be sent. + * + * After one or more heads appear, change or disappear, the done event will + * be sent. It carries a serial which can be used in a create_configuration + * request to update heads properties. + * + * The information obtained from this protocol should only be used for output + * configuration purposes. This protocol is not designed to be a generic + * output property advertisement protocol for regular clients. Instead, + * protocols such as xdg-output should be used. + */ +extern const struct wl_interface zwlr_output_manager_v1_interface; +#endif +#ifndef ZWLR_OUTPUT_HEAD_V1_INTERFACE +#define ZWLR_OUTPUT_HEAD_V1_INTERFACE +/** + * @page page_iface_zwlr_output_head_v1 zwlr_output_head_v1 + * @section page_iface_zwlr_output_head_v1_desc Description + * + * A head is an output device. The difference between a wl_output object and + * a head is that heads are advertised even if they are turned off. A head + * object only advertises properties and cannot be used directly to change + * them. + * + * A head has some read-only properties: modes, name, description and + * physical_size. These cannot be changed by clients. + * + * Other properties can be updated via a wlr_output_configuration object. + * + * Properties sent via this interface are applied atomically via the + * wlr_output_manager.done event. No guarantees are made regarding the order + * in which properties are sent. + * @section page_iface_zwlr_output_head_v1_api API + * See @ref iface_zwlr_output_head_v1. + */ +/** + * @defgroup iface_zwlr_output_head_v1 The zwlr_output_head_v1 interface + * + * A head is an output device. The difference between a wl_output object and + * a head is that heads are advertised even if they are turned off. A head + * object only advertises properties and cannot be used directly to change + * them. + * + * A head has some read-only properties: modes, name, description and + * physical_size. These cannot be changed by clients. + * + * Other properties can be updated via a wlr_output_configuration object. + * + * Properties sent via this interface are applied atomically via the + * wlr_output_manager.done event. No guarantees are made regarding the order + * in which properties are sent. + */ +extern const struct wl_interface zwlr_output_head_v1_interface; +#endif +#ifndef ZWLR_OUTPUT_MODE_V1_INTERFACE +#define ZWLR_OUTPUT_MODE_V1_INTERFACE +/** + * @page page_iface_zwlr_output_mode_v1 zwlr_output_mode_v1 + * @section page_iface_zwlr_output_mode_v1_desc Description + * + * This object describes an output mode. + * + * Some heads don't support output modes, in which case modes won't be + * advertised. + * + * Properties sent via this interface are applied atomically via the + * wlr_output_manager.done event. No guarantees are made regarding the order + * in which properties are sent. + * @section page_iface_zwlr_output_mode_v1_api API + * See @ref iface_zwlr_output_mode_v1. + */ +/** + * @defgroup iface_zwlr_output_mode_v1 The zwlr_output_mode_v1 interface + * + * This object describes an output mode. + * + * Some heads don't support output modes, in which case modes won't be + * advertised. + * + * Properties sent via this interface are applied atomically via the + * wlr_output_manager.done event. No guarantees are made regarding the order + * in which properties are sent. + */ +extern const struct wl_interface zwlr_output_mode_v1_interface; +#endif +#ifndef ZWLR_OUTPUT_CONFIGURATION_V1_INTERFACE +#define ZWLR_OUTPUT_CONFIGURATION_V1_INTERFACE +/** + * @page page_iface_zwlr_output_configuration_v1 zwlr_output_configuration_v1 + * @section page_iface_zwlr_output_configuration_v1_desc Description + * + * This object is used by the client to describe a full output configuration. + * + * First, the client needs to setup the output configuration. Each head can + * be either enabled (and configured) or disabled. It is a protocol error to + * send two enable_head or disable_head requests with the same head. It is a + * protocol error to omit a head in a configuration. + * + * Then, the client can apply or test the configuration. The compositor will + * then reply with a succeeded, failed or cancelled event. Finally the client + * should destroy the configuration object. + * @section page_iface_zwlr_output_configuration_v1_api API + * See @ref iface_zwlr_output_configuration_v1. + */ +/** + * @defgroup iface_zwlr_output_configuration_v1 The zwlr_output_configuration_v1 interface + * + * This object is used by the client to describe a full output configuration. + * + * First, the client needs to setup the output configuration. Each head can + * be either enabled (and configured) or disabled. It is a protocol error to + * send two enable_head or disable_head requests with the same head. It is a + * protocol error to omit a head in a configuration. + * + * Then, the client can apply or test the configuration. The compositor will + * then reply with a succeeded, failed or cancelled event. Finally the client + * should destroy the configuration object. + */ +extern const struct wl_interface zwlr_output_configuration_v1_interface; +#endif +#ifndef ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_INTERFACE +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_INTERFACE +/** + * @page page_iface_zwlr_output_configuration_head_v1 zwlr_output_configuration_head_v1 + * @section page_iface_zwlr_output_configuration_head_v1_desc Description + * + * This object is used by the client to update a single head's configuration. + * + * It is a protocol error to set the same property twice. + * @section page_iface_zwlr_output_configuration_head_v1_api API + * See @ref iface_zwlr_output_configuration_head_v1. + */ +/** + * @defgroup iface_zwlr_output_configuration_head_v1 The zwlr_output_configuration_head_v1 interface + * + * This object is used by the client to update a single head's configuration. + * + * It is a protocol error to set the same property twice. + */ +extern const struct wl_interface zwlr_output_configuration_head_v1_interface; +#endif + +/** + * @ingroup iface_zwlr_output_manager_v1 + * @struct zwlr_output_manager_v1_listener + */ +struct zwlr_output_manager_v1_listener { + /** + * introduce a new head + * + * This event introduces a new head. This happens whenever a new + * head appears (e.g. a monitor is plugged in) or after the output + * manager is bound. + */ + void (*head)(void *data, + struct zwlr_output_manager_v1 *zwlr_output_manager_v1, + struct zwlr_output_head_v1 *head); + /** + * sent all information about current configuration + * + * This event is sent after all information has been sent after + * binding to the output manager object and after any subsequent + * changes. This applies to child head and mode objects as well. In + * other words, this event is sent whenever a head or mode is + * created or destroyed and whenever one of their properties has + * been changed. Not all state is re-sent each time the current + * configuration changes: only the actual changes are sent. + * + * This allows changes to the output configuration to be seen as + * atomic, even if they happen via multiple events. + * + * A serial is sent to be used in a future create_configuration + * request. + * @param serial current configuration serial + */ + void (*done)(void *data, + struct zwlr_output_manager_v1 *zwlr_output_manager_v1, + uint32_t serial); + /** + * the compositor has finished with the manager + * + * This event indicates that the compositor is done sending + * manager events. The compositor will destroy the object + * immediately after sending this event, so it will become invalid + * and the client should release any resources associated with it. + */ + void (*finished)(void *data, + struct zwlr_output_manager_v1 *zwlr_output_manager_v1); +}; + +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +static inline int +zwlr_output_manager_v1_add_listener(struct zwlr_output_manager_v1 *zwlr_output_manager_v1, + const struct zwlr_output_manager_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_output_manager_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_OUTPUT_MANAGER_V1_CREATE_CONFIGURATION 0 +#define ZWLR_OUTPUT_MANAGER_V1_STOP 1 + +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_HEAD_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_FINISHED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_CREATE_CONFIGURATION_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_manager_v1 + */ +#define ZWLR_OUTPUT_MANAGER_V1_STOP_SINCE_VERSION 1 + +/** @ingroup iface_zwlr_output_manager_v1 */ +static inline void +zwlr_output_manager_v1_set_user_data(struct zwlr_output_manager_v1 *zwlr_output_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_manager_v1, user_data); +} + +/** @ingroup iface_zwlr_output_manager_v1 */ +static inline void * +zwlr_output_manager_v1_get_user_data(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_manager_v1); +} + +static inline uint32_t +zwlr_output_manager_v1_get_version(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1); +} + +/** @ingroup iface_zwlr_output_manager_v1 */ +static inline void +zwlr_output_manager_v1_destroy(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_output_manager_v1); +} + +/** + * @ingroup iface_zwlr_output_manager_v1 + * + * Create a new output configuration object. This allows to update head + * properties. + */ +static inline struct zwlr_output_configuration_v1 * +zwlr_output_manager_v1_create_configuration(struct zwlr_output_manager_v1 *zwlr_output_manager_v1, uint32_t serial) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_manager_v1, + ZWLR_OUTPUT_MANAGER_V1_CREATE_CONFIGURATION, &zwlr_output_configuration_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1), 0, NULL, serial); + + return (struct zwlr_output_configuration_v1 *) id; +} + +/** + * @ingroup iface_zwlr_output_manager_v1 + * + * Indicates the client no longer wishes to receive events for output + * configuration changes. However the compositor may emit further events, + * until the finished event is emitted. + * + * The client must not send any more requests after this one. + */ +static inline void +zwlr_output_manager_v1_stop(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_manager_v1, + ZWLR_OUTPUT_MANAGER_V1_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1), 0); +} + +#ifndef ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENUM +#define ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENUM +enum zwlr_output_head_v1_adaptive_sync_state { + /** + * adaptive sync is disabled + */ + ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED = 0, + /** + * adaptive sync is enabled + */ + ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED = 1, +}; +#endif /* ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENUM */ + +/** + * @ingroup iface_zwlr_output_head_v1 + * @struct zwlr_output_head_v1_listener + */ +struct zwlr_output_head_v1_listener { + /** + * head name + * + * This event describes the head name. + * + * The naming convention is compositor defined, but limited to + * alphanumeric characters and dashes (-). Each name is unique + * among all wlr_output_head objects, but if a wlr_output_head + * object is destroyed the same name may be reused later. The names + * will also remain consistent across sessions with the same + * hardware and software configuration. + * + * Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. + * However, do not assume that the name is a reflection of an + * underlying DRM connector, X11 connection, etc. + * + * If the compositor implements the xdg-output protocol and this + * head is enabled, the xdg_output.name event must report the same + * name. + * + * The name event is sent after a wlr_output_head object is + * created. This event is only sent once per object, and the name + * does not change over the lifetime of the wlr_output_head object. + */ + void (*name)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *name); + /** + * head description + * + * This event describes a human-readable description of the head. + * + * The description is a UTF-8 string with no convention defined for + * its contents. Examples might include 'Foocorp 11" Display' or + * 'Virtual X11 output via :1'. However, do not assume that the + * name is a reflection of the make, model, serial of the + * underlying DRM connector or the display name of the underlying + * X11 connection, etc. + * + * If the compositor implements xdg-output and this head is + * enabled, the xdg_output.description must report the same + * description. + * + * The description event is sent after a wlr_output_head object is + * created. This event is only sent once per object, and the + * description does not change over the lifetime of the + * wlr_output_head object. + */ + void (*description)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *description); + /** + * head physical size + * + * This event describes the physical size of the head. This event + * is only sent if the head has a physical size (e.g. is not a + * projector or a virtual device). + * @param width width in millimeters of the output + * @param height height in millimeters of the output + */ + void (*physical_size)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + int32_t width, + int32_t height); + /** + * introduce a mode + * + * This event introduces a mode for this head. It is sent once + * per supported mode. + */ + void (*mode)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + struct zwlr_output_mode_v1 *mode); + /** + * head is enabled or disabled + * + * This event describes whether the head is enabled. A disabled + * head is not mapped to a region of the global compositor space. + * + * When a head is disabled, some properties (current_mode, + * position, transform and scale) are irrelevant. + * @param enabled zero if disabled, non-zero if enabled + */ + void (*enabled)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + int32_t enabled); + /** + * current mode + * + * This event describes the mode currently in use for this head. + * It is only sent if the output is enabled. + */ + void (*current_mode)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + struct zwlr_output_mode_v1 *mode); + /** + * current position + * + * This events describes the position of the head in the global + * compositor space. It is only sent if the output is enabled. + * @param x x position within the global compositor space + * @param y y position within the global compositor space + */ + void (*position)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + int32_t x, + int32_t y); + /** + * current transformation + * + * This event describes the transformation currently applied to + * the head. It is only sent if the output is enabled. + */ + void (*transform)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + int32_t transform); + /** + * current scale + * + * This events describes the scale of the head in the global + * compositor space. It is only sent if the output is enabled. + */ + void (*scale)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + wl_fixed_t scale); + /** + * the head has disappeared + * + * This event indicates that the head is no longer available. The + * head object becomes inert. Clients should send a destroy request + * and release any resources associated with it. + */ + void (*finished)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1); + /** + * head manufacturer + * + * This event describes the manufacturer of the head. + * + * This must report the same make as the wl_output interface does + * in its geometry event. + * + * Together with the model and serial_number events the purpose is + * to allow clients to recognize heads from previous sessions and + * for example load head-specific configurations back. + * + * It is not guaranteed this event will be ever sent. A reason for + * that can be that the compositor does not have information about + * the make of the head or the definition of a make is not sensible + * in the current setup, for example in a virtual session. Clients + * can still try to identify the head by available information from + * other events but should be aware that there is an increased risk + * of false positives. + * + * It is not recommended to display the make string in UI to users. + * For that the string provided by the description event should be + * preferred. + * @since 2 + */ + void (*make)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *make); + /** + * head model + * + * This event describes the model of the head. + * + * This must report the same model as the wl_output interface does + * in its geometry event. + * + * Together with the make and serial_number events the purpose is + * to allow clients to recognize heads from previous sessions and + * for example load head-specific configurations back. + * + * It is not guaranteed this event will be ever sent. A reason for + * that can be that the compositor does not have information about + * the model of the head or the definition of a model is not + * sensible in the current setup, for example in a virtual session. + * Clients can still try to identify the head by available + * information from other events but should be aware that there is + * an increased risk of false positives. + * + * It is not recommended to display the model string in UI to + * users. For that the string provided by the description event + * should be preferred. + * @since 2 + */ + void (*model)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *model); + /** + * head serial number + * + * This event describes the serial number of the head. + * + * Together with the make and model events the purpose is to allow + * clients to recognize heads from previous sessions and for + * example load head- specific configurations back. + * + * It is not guaranteed this event will be ever sent. A reason for + * that can be that the compositor does not have information about + * the serial number of the head or the definition of a serial + * number is not sensible in the current setup. Clients can still + * try to identify the head by available information from other + * events but should be aware that there is an increased risk of + * false positives. + * + * It is not recommended to display the serial_number string in UI + * to users. For that the string provided by the description event + * should be preferred. + * @since 2 + */ + void (*serial_number)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + const char *serial_number); + /** + * current adaptive sync state + * + * This event describes whether adaptive sync is currently + * enabled for the head or not. Adaptive sync is also known as + * Variable Refresh Rate or VRR. + * @since 4 + */ + void (*adaptive_sync)(void *data, + struct zwlr_output_head_v1 *zwlr_output_head_v1, + uint32_t state); +}; + +/** + * @ingroup iface_zwlr_output_head_v1 + */ +static inline int +zwlr_output_head_v1_add_listener(struct zwlr_output_head_v1 *zwlr_output_head_v1, + const struct zwlr_output_head_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_output_head_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_OUTPUT_HEAD_V1_RELEASE 0 + +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_NAME_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_DESCRIPTION_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_PHYSICAL_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_ENABLED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_CURRENT_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_POSITION_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_TRANSFORM_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_SCALE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_FINISHED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_MAKE_SINCE_VERSION 2 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_MODEL_SINCE_VERSION 2 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_SERIAL_NUMBER_SINCE_VERSION 2 +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_SINCE_VERSION 4 + +/** + * @ingroup iface_zwlr_output_head_v1 + */ +#define ZWLR_OUTPUT_HEAD_V1_RELEASE_SINCE_VERSION 3 + +/** @ingroup iface_zwlr_output_head_v1 */ +static inline void +zwlr_output_head_v1_set_user_data(struct zwlr_output_head_v1 *zwlr_output_head_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_head_v1, user_data); +} + +/** @ingroup iface_zwlr_output_head_v1 */ +static inline void * +zwlr_output_head_v1_get_user_data(struct zwlr_output_head_v1 *zwlr_output_head_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_head_v1); +} + +static inline uint32_t +zwlr_output_head_v1_get_version(struct zwlr_output_head_v1 *zwlr_output_head_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_head_v1); +} + +/** @ingroup iface_zwlr_output_head_v1 */ +static inline void +zwlr_output_head_v1_destroy(struct zwlr_output_head_v1 *zwlr_output_head_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_output_head_v1); +} + +/** + * @ingroup iface_zwlr_output_head_v1 + * + * This request indicates that the client will no longer use this head + * object. + */ +static inline void +zwlr_output_head_v1_release(struct zwlr_output_head_v1 *zwlr_output_head_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_head_v1, + ZWLR_OUTPUT_HEAD_V1_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_head_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwlr_output_mode_v1 + * @struct zwlr_output_mode_v1_listener + */ +struct zwlr_output_mode_v1_listener { + /** + * mode size + * + * This event describes the mode size. The size is given in + * physical hardware units of the output device. This is not + * necessarily the same as the output size in the global compositor + * space. For instance, the output may be scaled or transformed. + * @param width width of the mode in hardware units + * @param height height of the mode in hardware units + */ + void (*size)(void *data, + struct zwlr_output_mode_v1 *zwlr_output_mode_v1, + int32_t width, + int32_t height); + /** + * mode refresh rate + * + * This event describes the mode's fixed vertical refresh rate. + * It is only sent if the mode has a fixed refresh rate. + * @param refresh vertical refresh rate in mHz + */ + void (*refresh)(void *data, + struct zwlr_output_mode_v1 *zwlr_output_mode_v1, + int32_t refresh); + /** + * mode is preferred + * + * This event advertises this mode as preferred. + */ + void (*preferred)(void *data, + struct zwlr_output_mode_v1 *zwlr_output_mode_v1); + /** + * the mode has disappeared + * + * This event indicates that the mode is no longer available. The + * mode object becomes inert. Clients should send a destroy request + * and release any resources associated with it. + */ + void (*finished)(void *data, + struct zwlr_output_mode_v1 *zwlr_output_mode_v1); +}; + +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +static inline int +zwlr_output_mode_v1_add_listener(struct zwlr_output_mode_v1 *zwlr_output_mode_v1, + const struct zwlr_output_mode_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_output_mode_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_OUTPUT_MODE_V1_RELEASE 0 + +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_REFRESH_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_PREFERRED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_FINISHED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_output_mode_v1 + */ +#define ZWLR_OUTPUT_MODE_V1_RELEASE_SINCE_VERSION 3 + +/** @ingroup iface_zwlr_output_mode_v1 */ +static inline void +zwlr_output_mode_v1_set_user_data(struct zwlr_output_mode_v1 *zwlr_output_mode_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_mode_v1, user_data); +} + +/** @ingroup iface_zwlr_output_mode_v1 */ +static inline void * +zwlr_output_mode_v1_get_user_data(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_mode_v1); +} + +static inline uint32_t +zwlr_output_mode_v1_get_version(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_mode_v1); +} + +/** @ingroup iface_zwlr_output_mode_v1 */ +static inline void +zwlr_output_mode_v1_destroy(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_output_mode_v1); +} + +/** + * @ingroup iface_zwlr_output_mode_v1 + * + * This request indicates that the client will no longer use this mode + * object. + */ +static inline void +zwlr_output_mode_v1_release(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_mode_v1, + ZWLR_OUTPUT_MODE_V1_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_mode_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifndef ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ENUM +#define ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ENUM +enum zwlr_output_configuration_v1_error { + /** + * head has been configured twice + */ + ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ALREADY_CONFIGURED_HEAD = 1, + /** + * head has not been configured + */ + ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_UNCONFIGURED_HEAD = 2, + /** + * request sent after configuration has been applied or tested + */ + ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ALREADY_USED = 3, +}; +#endif /* ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ENUM */ + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * @struct zwlr_output_configuration_v1_listener + */ +struct zwlr_output_configuration_v1_listener { + /** + * configuration changes succeeded + * + * Sent after the compositor has successfully applied the changes + * or tested them. + * + * Upon receiving this event, the client should destroy this + * object. + * + * If the current configuration has changed, events to describe the + * changes will be sent followed by a wlr_output_manager.done + * event. + */ + void (*succeeded)(void *data, + struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1); + /** + * configuration changes failed + * + * Sent if the compositor rejects the changes or failed to apply + * them. The compositor should revert any changes made by the apply + * request that triggered this event. + * + * Upon receiving this event, the client should destroy this + * object. + */ + void (*failed)(void *data, + struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1); + /** + * configuration has been cancelled + * + * Sent if the compositor cancels the configuration because the + * state of an output changed and the client has outdated + * information (e.g. after an output has been hotplugged). + * + * The client can create a new configuration with a newer serial + * and try again. + * + * Upon receiving this event, the client should destroy this + * object. + */ + void (*cancelled)(void *data, + struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1); +}; + +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +static inline int +zwlr_output_configuration_v1_add_listener(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, + const struct zwlr_output_configuration_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_output_configuration_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_OUTPUT_CONFIGURATION_V1_ENABLE_HEAD 0 +#define ZWLR_OUTPUT_CONFIGURATION_V1_DISABLE_HEAD 1 +#define ZWLR_OUTPUT_CONFIGURATION_V1_APPLY 2 +#define ZWLR_OUTPUT_CONFIGURATION_V1_TEST 3 +#define ZWLR_OUTPUT_CONFIGURATION_V1_DESTROY 4 + +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_SUCCEEDED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_FAILED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_CANCELLED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_ENABLE_HEAD_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_DISABLE_HEAD_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_APPLY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_TEST_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_zwlr_output_configuration_v1 */ +static inline void +zwlr_output_configuration_v1_set_user_data(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_configuration_v1, user_data); +} + +/** @ingroup iface_zwlr_output_configuration_v1 */ +static inline void * +zwlr_output_configuration_v1_get_user_data(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_configuration_v1); +} + +static inline uint32_t +zwlr_output_configuration_v1_get_version(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1); +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Enable a head. This request creates a head configuration object that can + * be used to change the head's properties. + */ +static inline struct zwlr_output_configuration_head_v1 * +zwlr_output_configuration_v1_enable_head(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, struct zwlr_output_head_v1 *head) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_ENABLE_HEAD, &zwlr_output_configuration_head_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0, NULL, head); + + return (struct zwlr_output_configuration_head_v1 *) id; +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Disable a head. + */ +static inline void +zwlr_output_configuration_v1_disable_head(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, struct zwlr_output_head_v1 *head) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_DISABLE_HEAD, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0, head); +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Apply the new output configuration. + * + * In case the configuration is successfully applied, there is no guarantee + * that the new output state matches completely the requested + * configuration. For instance, a compositor might round the scale if it + * doesn't support fractional scaling. + * + * After this request has been sent, the compositor must respond with an + * succeeded, failed or cancelled event. Sending a request that isn't the + * destructor is a protocol error. + */ +static inline void +zwlr_output_configuration_v1_apply(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_APPLY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0); +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Test the new output configuration. The configuration won't be applied, + * but will only be validated. + * + * Even if the compositor succeeds to test a configuration, applying it may + * fail. + * + * After this request has been sent, the compositor must respond with an + * succeeded, failed or cancelled event. Sending a request that isn't the + * destructor is a protocol error. + */ +static inline void +zwlr_output_configuration_v1_test(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_TEST, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0); +} + +/** + * @ingroup iface_zwlr_output_configuration_v1 + * + * Using this request a client can tell the compositor that it is not going + * to use the configuration object anymore. Any changes to the outputs + * that have not been applied will be discarded. + * + * This request also destroys wlr_output_configuration_head objects created + * via this object. + */ +static inline void +zwlr_output_configuration_v1_destroy(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, + ZWLR_OUTPUT_CONFIGURATION_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifndef ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ENUM +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ENUM +enum zwlr_output_configuration_head_v1_error { + /** + * property has already been set + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET = 1, + /** + * mode doesn't belong to head + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_MODE = 2, + /** + * mode is invalid + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_CUSTOM_MODE = 3, + /** + * transform value outside enum + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_TRANSFORM = 4, + /** + * scale negative or zero + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_SCALE = 5, + /** + * invalid enum value used in the set_adaptive_sync request + * @since 4 + */ + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE = 6, +}; +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE_SINCE_VERSION 4 +#endif /* ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ENUM */ + +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_MODE 0 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_CUSTOM_MODE 1 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_POSITION 2 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_TRANSFORM 3 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_SCALE 4 +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_ADAPTIVE_SYNC 5 + + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_CUSTOM_MODE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_POSITION_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_TRANSFORM_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_SCALE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + */ +#define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_ADAPTIVE_SYNC_SINCE_VERSION 4 + +/** @ingroup iface_zwlr_output_configuration_head_v1 */ +static inline void +zwlr_output_configuration_head_v1_set_user_data(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_output_configuration_head_v1, user_data); +} + +/** @ingroup iface_zwlr_output_configuration_head_v1 */ +static inline void * +zwlr_output_configuration_head_v1_get_user_data(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_output_configuration_head_v1); +} + +static inline uint32_t +zwlr_output_configuration_head_v1_get_version(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1); +} + +/** @ingroup iface_zwlr_output_configuration_head_v1 */ +static inline void +zwlr_output_configuration_head_v1_destroy(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_output_configuration_head_v1); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request sets the head's mode. + */ +static inline void +zwlr_output_configuration_head_v1_set_mode(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, struct zwlr_output_mode_v1 *mode) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, mode); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request assigns a custom mode to the head. The size is given in + * physical hardware units of the output device. If set to zero, the + * refresh rate is unspecified. + * + * It is a protocol error to set both a mode and a custom mode. + */ +static inline void +zwlr_output_configuration_head_v1_set_custom_mode(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t width, int32_t height, int32_t refresh) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_CUSTOM_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, width, height, refresh); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request sets the head's position in the global compositor space. + */ +static inline void +zwlr_output_configuration_head_v1_set_position(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_POSITION, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, x, y); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request sets the head's transform. + */ +static inline void +zwlr_output_configuration_head_v1_set_transform(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t transform) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_TRANSFORM, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, transform); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request sets the head's scale. + */ +static inline void +zwlr_output_configuration_head_v1_set_scale(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, wl_fixed_t scale) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_SCALE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, scale); +} + +/** + * @ingroup iface_zwlr_output_configuration_head_v1 + * + * This request enables/disables adaptive sync. Adaptive sync is also + * known as Variable Refresh Rate or VRR. + */ +static inline void +zwlr_output_configuration_head_v1_set_adaptive_sync(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, uint32_t state) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, + ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_ADAPTIVE_SYNC, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, state); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c new file mode 100644 index 0000000000..aaa2663e6f --- /dev/null +++ b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c @@ -0,0 +1,160 @@ +#ifdef FF_HAVE_WAYLAND + +/* Generated by wayland-scanner 1.22.0 */ + +/* + * Copyright © 2019 Purism SPC + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * the copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +#include +#include +#include + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface zwlr_output_configuration_head_v1_interface; +extern const struct wl_interface zwlr_output_configuration_v1_interface; +extern const struct wl_interface zwlr_output_head_v1_interface; +extern const struct wl_interface zwlr_output_mode_v1_interface; + +static const struct wl_interface *wlr_output_management_unstable_v1_types[] = { + NULL, + NULL, + NULL, + &zwlr_output_configuration_v1_interface, + NULL, + &zwlr_output_head_v1_interface, + &zwlr_output_mode_v1_interface, + &zwlr_output_mode_v1_interface, + &zwlr_output_configuration_head_v1_interface, + &zwlr_output_head_v1_interface, + &zwlr_output_head_v1_interface, + &zwlr_output_mode_v1_interface, +}; + +static const struct wl_message zwlr_output_manager_v1_requests[] = { + { "create_configuration", "nu", wlr_output_management_unstable_v1_types + 3 }, + { "stop", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_output_manager_v1_events[] = { + { "head", "n", wlr_output_management_unstable_v1_types + 5 }, + { "done", "u", wlr_output_management_unstable_v1_types + 0 }, + { "finished", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_manager_v1_interface = { + "zwlr_output_manager_v1", 4, + 2, zwlr_output_manager_v1_requests, + 3, zwlr_output_manager_v1_events, +}; + +static const struct wl_message zwlr_output_head_v1_requests[] = { + { "release", "3", wlr_output_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_output_head_v1_events[] = { + { "name", "s", wlr_output_management_unstable_v1_types + 0 }, + { "description", "s", wlr_output_management_unstable_v1_types + 0 }, + { "physical_size", "ii", wlr_output_management_unstable_v1_types + 0 }, + { "mode", "n", wlr_output_management_unstable_v1_types + 6 }, + { "enabled", "i", wlr_output_management_unstable_v1_types + 0 }, + { "current_mode", "o", wlr_output_management_unstable_v1_types + 7 }, + { "position", "ii", wlr_output_management_unstable_v1_types + 0 }, + { "transform", "i", wlr_output_management_unstable_v1_types + 0 }, + { "scale", "f", wlr_output_management_unstable_v1_types + 0 }, + { "finished", "", wlr_output_management_unstable_v1_types + 0 }, + { "make", "2s", wlr_output_management_unstable_v1_types + 0 }, + { "model", "2s", wlr_output_management_unstable_v1_types + 0 }, + { "serial_number", "2s", wlr_output_management_unstable_v1_types + 0 }, + { "adaptive_sync", "4u", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_head_v1_interface = { + "zwlr_output_head_v1", 4, + 1, zwlr_output_head_v1_requests, + 14, zwlr_output_head_v1_events, +}; + +static const struct wl_message zwlr_output_mode_v1_requests[] = { + { "release", "3", wlr_output_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_output_mode_v1_events[] = { + { "size", "ii", wlr_output_management_unstable_v1_types + 0 }, + { "refresh", "i", wlr_output_management_unstable_v1_types + 0 }, + { "preferred", "", wlr_output_management_unstable_v1_types + 0 }, + { "finished", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_mode_v1_interface = { + "zwlr_output_mode_v1", 3, + 1, zwlr_output_mode_v1_requests, + 4, zwlr_output_mode_v1_events, +}; + +static const struct wl_message zwlr_output_configuration_v1_requests[] = { + { "enable_head", "no", wlr_output_management_unstable_v1_types + 8 }, + { "disable_head", "o", wlr_output_management_unstable_v1_types + 10 }, + { "apply", "", wlr_output_management_unstable_v1_types + 0 }, + { "test", "", wlr_output_management_unstable_v1_types + 0 }, + { "destroy", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_output_configuration_v1_events[] = { + { "succeeded", "", wlr_output_management_unstable_v1_types + 0 }, + { "failed", "", wlr_output_management_unstable_v1_types + 0 }, + { "cancelled", "", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_configuration_v1_interface = { + "zwlr_output_configuration_v1", 4, + 5, zwlr_output_configuration_v1_requests, + 3, zwlr_output_configuration_v1_events, +}; + +static const struct wl_message zwlr_output_configuration_head_v1_requests[] = { + { "set_mode", "o", wlr_output_management_unstable_v1_types + 11 }, + { "set_custom_mode", "iii", wlr_output_management_unstable_v1_types + 0 }, + { "set_position", "ii", wlr_output_management_unstable_v1_types + 0 }, + { "set_transform", "i", wlr_output_management_unstable_v1_types + 0 }, + { "set_scale", "f", wlr_output_management_unstable_v1_types + 0 }, + { "set_adaptive_sync", "4u", wlr_output_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_output_configuration_head_v1_interface = { + "zwlr_output_configuration_head_v1", 4, + 6, zwlr_output_configuration_head_v1_requests, + 0, NULL, +}; + +#endif diff --git a/src/detection/displayserver/linux/wayland/zwlr-output.c b/src/detection/displayserver/linux/wayland/zwlr-output.c new file mode 100644 index 0000000000..62ea41ed22 --- /dev/null +++ b/src/detection/displayserver/linux/wayland/zwlr-output.c @@ -0,0 +1,191 @@ +#ifdef FF_HAVE_WAYLAND + +#include "wayland.h" +#include "wlr-output-management-unstable-v1-client-protocol.h" + +static void waylandZwlrTransformListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, int32_t transform) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + wldata->transform = transform; +} + +static void waylandZwlrScaleListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, wl_fixed_t scale) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + wldata->scale = wl_fixed_to_double(scale); +} + +typedef struct WaylandZwlrMode +{ + int32_t width; + int32_t height; + int32_t refreshRate; + struct zwlr_output_mode_v1* pMode; +} WaylandZwlrMode; + +static void waylandZwlrSizeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_mode_v1 *zwlr_output_mode_v1, int32_t width, int32_t height) +{ + WaylandZwlrMode* mode = (WaylandZwlrMode*) data; + mode->width = width; + mode->height = height; +} + +static void waylandZwlrRefreshListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_mode_v1 *zwlr_output_mode_v1, int32_t rate) +{ + WaylandZwlrMode* mode = (WaylandZwlrMode*) data; + mode->refreshRate = rate; +} + +static void waylandZwlrModeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, struct zwlr_output_mode_v1 *mode) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + WaylandZwlrMode* newMode = ffListAdd((FFlist*) wldata->internal); + newMode->pMode = mode; + const struct zwlr_output_mode_v1_listener listener = { + .size = waylandZwlrSizeListener, + .refresh = waylandZwlrRefreshListener, + .preferred = (void*) stubListener, + .finished = (void*) stubListener, + }; + + // Strangely, the listener is called only in this function, but not in `waylandZwlrCurrentModeListener` + wldata->parent->ffwl_proxy_add_listener((struct wl_proxy *) mode, (void (**)(void)) &listener, newMode); + wldata->parent->ffwl_display_roundtrip(wldata->parent->display); +} + +static void waylandZwlrCurrentModeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, struct zwlr_output_mode_v1 *mode) +{ + // waylandZwlrModeListener is always run before this + WaylandDisplay* wldata = (WaylandDisplay*) data; + WaylandZwlrMode* current = NULL; + FF_LIST_FOR_EACH(WaylandZwlrMode, m, *(FFlist*) wldata->internal) + { + if (m->pMode == mode) + { + current = m; + break; + } + } + wldata->width = current->width; + wldata->height = current->height; + wldata->refreshRate = current->refreshRate; +} + +static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output_manager_v1 *zwlr_output_manager_v1, struct zwlr_output_head_v1 *head) +{ + WaylandData* wldata = data; + + const struct zwlr_output_head_v1_listener headListener = { + .name = (void*) ffWaylandOutputNameListener, + .description = (void*) ffWaylandOutputDescriptionListener, + .physical_size = (void*) stubListener, + .mode = waylandZwlrModeListener, + .enabled = (void*) stubListener, + .current_mode = waylandZwlrCurrentModeListener, + .position = (void*) stubListener, + .transform = waylandZwlrTransformListener, + .scale = waylandZwlrScaleListener, + .finished = (void*) stubListener, + .serial_number = (void*) stubListener, + .adaptive_sync = (void*) stubListener, + }; + + FF_LIST_AUTO_DESTROY modes = ffListCreate(sizeof(WaylandZwlrMode)); + WaylandDisplay display = { + .parent = wldata, + .width = 0, + .height = 0, + .refreshRate = 0, + .scale = 1, + .transform = WL_OUTPUT_TRANSFORM_NORMAL, + .type = FF_DISPLAY_TYPE_UNKNOWN, + .name = ffStrbufCreate(), + .description = ffStrbufCreate(), + .edidName = ffStrbufCreate(), + .internal = &modes, + }; + + wldata->ffwl_proxy_add_listener((struct wl_proxy*) head, (void(**)(void)) &headListener, &display); + wldata->ffwl_display_roundtrip(wldata->display); + + if(display.width <= 0 || display.height <= 0) + return; + + uint32_t rotation; + switch(display.transform) + { + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_90: + rotation = 90; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_180: + rotation = 180; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + case WL_OUTPUT_TRANSFORM_270: + rotation = 270; + break; + default: + rotation = 0; + break; + } + + switch(rotation) + { + case 90: + case 270: { + int32_t temp = display.width; + display.width = display.height; + display.height = temp; + break; + } + default: + break; + } + + ffdsAppendDisplay(wldata->result, + (uint32_t) display.width, + (uint32_t) display.height, + display.refreshRate / 1000.0, + (uint32_t) (display.width / display.scale), + (uint32_t) (display.height / display.scale), + rotation, + display.edidName.length + ? &display.edidName + : display.description.length + ? &display.description + : &display.name, + display.type, + false, + 0 + ); + + ffStrbufDestroy(&display.description); + ffStrbufDestroy(&display.name); + ffStrbufDestroy(&display.edidName); + + // These must be released manually + FF_LIST_FOR_EACH(WaylandZwlrMode, m, modes) + wldata->ffwl_proxy_destroy((void*) m->pMode); + wldata->ffwl_proxy_destroy((void*) head); +} + +void ffWaylandHandleZwlrOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) +{ + struct wl_proxy* output = wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy*) registry, WL_REGISTRY_BIND, &zwlr_output_manager_v1_interface, version, name, zwlr_output_manager_v1_interface.name, version, NULL); + if(output == NULL) + return; + + const struct zwlr_output_manager_v1_listener outputListener = { + .head = waylandHandleZwlrHead, + .done = (void*) stubListener, + .finished = (void*) stubListener, + }; + + wldata->ffwl_proxy_add_listener(output, (void(**)(void)) &outputListener, wldata); + wldata->ffwl_display_roundtrip(wldata->display); + wldata->ffwl_proxy_destroy(output); +} + +#endif diff --git a/src/detection/displayserver/linux/wlroots.c b/src/detection/displayserver/linux/wlroots.c deleted file mode 100644 index dadb9c4c3c..0000000000 --- a/src/detection/displayserver/linux/wlroots.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "displayserver_linux.h" -#include "util/stringUtils.h" -#include "common/processing.h" - -static inline void wrapYyjsonFree(yyjson_doc** doc) -{ - assert(doc); - if (*doc) - yyjson_doc_free(*doc); -} - -const char* ffdsConnectWlroots(FFDisplayServerResult* result) -{ - FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); - if (ffProcessAppendStdOut(&buffer, (char* const[]){ - "wlr-randr", - "--json", - NULL - }) != NULL || buffer.length == 0) - return "Running wlr-randr failed"; - - yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(buffer.chars, buffer.length, 0, NULL, NULL); - if (!doc) - return "Failed to parse wlr-randr info"; - - yyjson_val* root = yyjson_doc_get_root(doc); - if (!yyjson_is_arr(root)) - return "Battery info result is not a JSON array"; - - yyjson_val* device; - size_t idev, mdev; - yyjson_arr_foreach(root, idev, mdev, device) - { - yyjson_val* modes = yyjson_obj_get(device, "modes"); - if (!yyjson_is_arr(modes)) - continue; - - if (!yyjson_get_bool(yyjson_obj_get(device, "enabled"))) - continue; - - yyjson_val* mode; - size_t imode, mmode; - yyjson_arr_foreach(modes, imode, mmode, mode) - { - if (!yyjson_is_obj(mode)) - continue; - - if (!yyjson_get_bool(yyjson_obj_get(mode, "current"))) - continue; - - uint32_t width = (uint32_t) yyjson_get_uint(yyjson_obj_get(mode, "width")); - uint32_t height = (uint32_t) yyjson_get_uint(yyjson_obj_get(mode, "height")); - - if (width == 0 || height == 0) - continue; - - double refreshRate = yyjson_get_real(yyjson_obj_get(mode, "refresh")); - - double scale = (double) yyjson_get_real(yyjson_obj_get(device, "scale")); - const char* connName = yyjson_get_str(yyjson_obj_get(device, "name")); - FFDisplayType type = FF_DISPLAY_TYPE_UNKNOWN; - if(ffStrStartsWith(connName, "eDP-")) - type = FF_DISPLAY_TYPE_BUILTIN; - else if(ffStrStartsWith(connName, "HDMI-")) - type = FF_DISPLAY_TYPE_EXTERNAL; - - FF_STRBUF_AUTO_DESTROY displayName = ffStrbufCreate(); - if (!ffdsMatchDrmConnector(connName, &displayName)) - { - const char* desc = yyjson_get_str(yyjson_obj_get(device, "description")); - if (ffStrContains(desc, "(null)")) - ffStrbufSetS(&displayName, connName); - else - ffStrbufSetS(&displayName, desc); - } - uint32_t rotation = 0; - const char* transform = yyjson_get_str(yyjson_obj_get(device, "transform")); - if (!ffStrEquals(transform, "normal")) - { - // 90 | flipped-90 - if (ffStrEndsWith(transform, "90")) - rotation = 90; - else if (ffStrEndsWith(transform, "180")) - rotation = 180; - else if (ffStrEndsWith(transform, "270")) - rotation = 270; - } - if (rotation % 180 != 0) - { - uint32_t temp = width; - width = height; - height = temp; - } - ffdsAppendDisplay(result, - width, - height, - refreshRate, - (uint32_t) (width / scale), - (uint32_t) (height / scale), - rotation, - &displayName, - type, - false, - 0 - ); - break; - } - } - - return NULL; -} From 9c56839c973a4a9e89a808cbe27534ee0ab988ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 22 Apr 2024 19:06:13 +0800 Subject: [PATCH 37/40] DisplayServer (Linux): fix crash --- .../displayserver/linux/wayland/zwlr-output.c | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/detection/displayserver/linux/wayland/zwlr-output.c b/src/detection/displayserver/linux/wayland/zwlr-output.c index 62ea41ed22..dacb52d371 100644 --- a/src/detection/displayserver/linux/wayland/zwlr-output.c +++ b/src/detection/displayserver/linux/wayland/zwlr-output.c @@ -36,21 +36,21 @@ static void waylandZwlrRefreshListener(void* data, FF_MAYBE_UNUSED struct zwlr_o mode->refreshRate = rate; } +static const struct zwlr_output_mode_v1_listener modeListener = { + .size = waylandZwlrSizeListener, + .refresh = waylandZwlrRefreshListener, + .preferred = (void*) stubListener, + .finished = (void*) stubListener, +}; + static void waylandZwlrModeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, struct zwlr_output_mode_v1 *mode) { WaylandDisplay* wldata = (WaylandDisplay*) data; WaylandZwlrMode* newMode = ffListAdd((FFlist*) wldata->internal); newMode->pMode = mode; - const struct zwlr_output_mode_v1_listener listener = { - .size = waylandZwlrSizeListener, - .refresh = waylandZwlrRefreshListener, - .preferred = (void*) stubListener, - .finished = (void*) stubListener, - }; // Strangely, the listener is called only in this function, but not in `waylandZwlrCurrentModeListener` - wldata->parent->ffwl_proxy_add_listener((struct wl_proxy *) mode, (void (**)(void)) &listener, newMode); - wldata->parent->ffwl_display_roundtrip(wldata->parent->display); + wldata->parent->ffwl_proxy_add_listener((struct wl_proxy *) mode, (void (**)(void)) &modeListener, newMode); } static void waylandZwlrCurrentModeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, struct zwlr_output_mode_v1 *mode) @@ -86,6 +86,8 @@ static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output .transform = waylandZwlrTransformListener, .scale = waylandZwlrScaleListener, .finished = (void*) stubListener, + .make = (void*) stubListener, + .model = (void*) stubListener, .serial_number = (void*) stubListener, .adaptive_sync = (void*) stubListener, }; @@ -148,8 +150,8 @@ static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output (uint32_t) display.width, (uint32_t) display.height, display.refreshRate / 1000.0, - (uint32_t) (display.width / display.scale), - (uint32_t) (display.height / display.scale), + (uint32_t) (display.width / display.scale + 0.5), + (uint32_t) (display.height / display.scale + 0.5), rotation, display.edidName.length ? &display.edidName From ed7b12e7330cad2cf4f47b3875952c3a68596b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 22 Apr 2024 19:15:11 +0800 Subject: [PATCH 38/40] DisplayServer (Linux): fix compiling for old wayland version --- ...t-management-unstable-v1-client-protocol.h | 436 +++++++++--------- 1 file changed, 218 insertions(+), 218 deletions(-) diff --git a/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h index e00668ffdd..dfade7510e 100644 --- a/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h +++ b/src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-client-protocol.h @@ -377,38 +377,38 @@ zwlr_output_manager_v1_destroy(struct zwlr_output_manager_v1 *zwlr_output_manage wl_proxy_destroy((struct wl_proxy *) zwlr_output_manager_v1); } -/** - * @ingroup iface_zwlr_output_manager_v1 - * - * Create a new output configuration object. This allows to update head - * properties. - */ -static inline struct zwlr_output_configuration_v1 * -zwlr_output_manager_v1_create_configuration(struct zwlr_output_manager_v1 *zwlr_output_manager_v1, uint32_t serial) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_manager_v1, - ZWLR_OUTPUT_MANAGER_V1_CREATE_CONFIGURATION, &zwlr_output_configuration_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1), 0, NULL, serial); - - return (struct zwlr_output_configuration_v1 *) id; -} - -/** - * @ingroup iface_zwlr_output_manager_v1 - * - * Indicates the client no longer wishes to receive events for output - * configuration changes. However the compositor may emit further events, - * until the finished event is emitted. - * - * The client must not send any more requests after this one. - */ -static inline void -zwlr_output_manager_v1_stop(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_manager_v1, - ZWLR_OUTPUT_MANAGER_V1_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1), 0); -} +// /** +// * @ingroup iface_zwlr_output_manager_v1 +// * +// * Create a new output configuration object. This allows to update head +// * properties. +// */ +// static inline struct zwlr_output_configuration_v1 * +// zwlr_output_manager_v1_create_configuration(struct zwlr_output_manager_v1 *zwlr_output_manager_v1, uint32_t serial) +// { +// struct wl_proxy *id; + +// id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_manager_v1, +// ZWLR_OUTPUT_MANAGER_V1_CREATE_CONFIGURATION, &zwlr_output_configuration_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1), 0, NULL, serial); + +// return (struct zwlr_output_configuration_v1 *) id; +// } + +// /** +// * @ingroup iface_zwlr_output_manager_v1 +// * +// * Indicates the client no longer wishes to receive events for output +// * configuration changes. However the compositor may emit further events, +// * until the finished event is emitted. +// * +// * The client must not send any more requests after this one. +// */ +// static inline void +// zwlr_output_manager_v1_stop(struct zwlr_output_manager_v1 *zwlr_output_manager_v1) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_manager_v1, +// ZWLR_OUTPUT_MANAGER_V1_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_manager_v1), 0); +// } #ifndef ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENUM #define ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENUM @@ -759,18 +759,18 @@ zwlr_output_head_v1_destroy(struct zwlr_output_head_v1 *zwlr_output_head_v1) wl_proxy_destroy((struct wl_proxy *) zwlr_output_head_v1); } -/** - * @ingroup iface_zwlr_output_head_v1 - * - * This request indicates that the client will no longer use this head - * object. - */ -static inline void -zwlr_output_head_v1_release(struct zwlr_output_head_v1 *zwlr_output_head_v1) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_head_v1, - ZWLR_OUTPUT_HEAD_V1_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_head_v1), WL_MARSHAL_FLAG_DESTROY); -} +// /** +// * @ingroup iface_zwlr_output_head_v1 +// * +// * This request indicates that the client will no longer use this head +// * object. +// */ +// static inline void +// zwlr_output_head_v1_release(struct zwlr_output_head_v1 *zwlr_output_head_v1) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_head_v1, +// ZWLR_OUTPUT_HEAD_V1_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_head_v1), WL_MARSHAL_FLAG_DESTROY); +// } /** * @ingroup iface_zwlr_output_mode_v1 @@ -881,18 +881,18 @@ zwlr_output_mode_v1_destroy(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) wl_proxy_destroy((struct wl_proxy *) zwlr_output_mode_v1); } -/** - * @ingroup iface_zwlr_output_mode_v1 - * - * This request indicates that the client will no longer use this mode - * object. - */ -static inline void -zwlr_output_mode_v1_release(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_mode_v1, - ZWLR_OUTPUT_MODE_V1_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_mode_v1), WL_MARSHAL_FLAG_DESTROY); -} +// /** +// * @ingroup iface_zwlr_output_mode_v1 +// * +// * This request indicates that the client will no longer use this mode +// * object. +// */ +// static inline void +// zwlr_output_mode_v1_release(struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_mode_v1, +// ZWLR_OUTPUT_MODE_V1_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_mode_v1), WL_MARSHAL_FLAG_DESTROY); +// } #ifndef ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ENUM #define ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ENUM @@ -1032,92 +1032,92 @@ zwlr_output_configuration_v1_get_version(struct zwlr_output_configuration_v1 *zw return wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1); } -/** - * @ingroup iface_zwlr_output_configuration_v1 - * - * Enable a head. This request creates a head configuration object that can - * be used to change the head's properties. - */ -static inline struct zwlr_output_configuration_head_v1 * -zwlr_output_configuration_v1_enable_head(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, struct zwlr_output_head_v1 *head) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, - ZWLR_OUTPUT_CONFIGURATION_V1_ENABLE_HEAD, &zwlr_output_configuration_head_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0, NULL, head); - - return (struct zwlr_output_configuration_head_v1 *) id; -} - -/** - * @ingroup iface_zwlr_output_configuration_v1 - * - * Disable a head. - */ -static inline void -zwlr_output_configuration_v1_disable_head(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, struct zwlr_output_head_v1 *head) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, - ZWLR_OUTPUT_CONFIGURATION_V1_DISABLE_HEAD, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0, head); -} - -/** - * @ingroup iface_zwlr_output_configuration_v1 - * - * Apply the new output configuration. - * - * In case the configuration is successfully applied, there is no guarantee - * that the new output state matches completely the requested - * configuration. For instance, a compositor might round the scale if it - * doesn't support fractional scaling. - * - * After this request has been sent, the compositor must respond with an - * succeeded, failed or cancelled event. Sending a request that isn't the - * destructor is a protocol error. - */ -static inline void -zwlr_output_configuration_v1_apply(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, - ZWLR_OUTPUT_CONFIGURATION_V1_APPLY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0); -} - -/** - * @ingroup iface_zwlr_output_configuration_v1 - * - * Test the new output configuration. The configuration won't be applied, - * but will only be validated. - * - * Even if the compositor succeeds to test a configuration, applying it may - * fail. - * - * After this request has been sent, the compositor must respond with an - * succeeded, failed or cancelled event. Sending a request that isn't the - * destructor is a protocol error. - */ -static inline void -zwlr_output_configuration_v1_test(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, - ZWLR_OUTPUT_CONFIGURATION_V1_TEST, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0); -} - -/** - * @ingroup iface_zwlr_output_configuration_v1 - * - * Using this request a client can tell the compositor that it is not going - * to use the configuration object anymore. Any changes to the outputs - * that have not been applied will be discarded. - * - * This request also destroys wlr_output_configuration_head objects created - * via this object. - */ -static inline void -zwlr_output_configuration_v1_destroy(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, - ZWLR_OUTPUT_CONFIGURATION_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), WL_MARSHAL_FLAG_DESTROY); -} +// /** +// * @ingroup iface_zwlr_output_configuration_v1 +// * +// * Enable a head. This request creates a head configuration object that can +// * be used to change the head's properties. +// */ +// static inline struct zwlr_output_configuration_head_v1 * +// zwlr_output_configuration_v1_enable_head(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, struct zwlr_output_head_v1 *head) +// { +// struct wl_proxy *id; + +// id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, +// ZWLR_OUTPUT_CONFIGURATION_V1_ENABLE_HEAD, &zwlr_output_configuration_head_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0, NULL, head); + +// return (struct zwlr_output_configuration_head_v1 *) id; +// } + +// /** +// * @ingroup iface_zwlr_output_configuration_v1 +// * +// * Disable a head. +// */ +// static inline void +// zwlr_output_configuration_v1_disable_head(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1, struct zwlr_output_head_v1 *head) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, +// ZWLR_OUTPUT_CONFIGURATION_V1_DISABLE_HEAD, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0, head); +// } + +// /** +// * @ingroup iface_zwlr_output_configuration_v1 +// * +// * Apply the new output configuration. +// * +// * In case the configuration is successfully applied, there is no guarantee +// * that the new output state matches completely the requested +// * configuration. For instance, a compositor might round the scale if it +// * doesn't support fractional scaling. +// * +// * After this request has been sent, the compositor must respond with an +// * succeeded, failed or cancelled event. Sending a request that isn't the +// * destructor is a protocol error. +// */ +// static inline void +// zwlr_output_configuration_v1_apply(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, +// ZWLR_OUTPUT_CONFIGURATION_V1_APPLY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0); +// } + +// /** +// * @ingroup iface_zwlr_output_configuration_v1 +// * +// * Test the new output configuration. The configuration won't be applied, +// * but will only be validated. +// * +// * Even if the compositor succeeds to test a configuration, applying it may +// * fail. +// * +// * After this request has been sent, the compositor must respond with an +// * succeeded, failed or cancelled event. Sending a request that isn't the +// * destructor is a protocol error. +// */ +// static inline void +// zwlr_output_configuration_v1_test(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, +// ZWLR_OUTPUT_CONFIGURATION_V1_TEST, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), 0); +// } + +// /** +// * @ingroup iface_zwlr_output_configuration_v1 +// * +// * Using this request a client can tell the compositor that it is not going +// * to use the configuration object anymore. Any changes to the outputs +// * that have not been applied will be discarded. +// * +// * This request also destroys wlr_output_configuration_head objects created +// * via this object. +// */ +// static inline void +// zwlr_output_configuration_v1_destroy(struct zwlr_output_configuration_v1 *zwlr_output_configuration_v1) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_v1, +// ZWLR_OUTPUT_CONFIGURATION_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_v1), WL_MARSHAL_FLAG_DESTROY); +// } #ifndef ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ENUM #define ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ENUM @@ -1214,82 +1214,82 @@ zwlr_output_configuration_head_v1_destroy(struct zwlr_output_configuration_head_ wl_proxy_destroy((struct wl_proxy *) zwlr_output_configuration_head_v1); } -/** - * @ingroup iface_zwlr_output_configuration_head_v1 - * - * This request sets the head's mode. - */ -static inline void -zwlr_output_configuration_head_v1_set_mode(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, struct zwlr_output_mode_v1 *mode) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, - ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, mode); -} - -/** - * @ingroup iface_zwlr_output_configuration_head_v1 - * - * This request assigns a custom mode to the head. The size is given in - * physical hardware units of the output device. If set to zero, the - * refresh rate is unspecified. - * - * It is a protocol error to set both a mode and a custom mode. - */ -static inline void -zwlr_output_configuration_head_v1_set_custom_mode(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t width, int32_t height, int32_t refresh) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, - ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_CUSTOM_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, width, height, refresh); -} - -/** - * @ingroup iface_zwlr_output_configuration_head_v1 - * - * This request sets the head's position in the global compositor space. - */ -static inline void -zwlr_output_configuration_head_v1_set_position(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t x, int32_t y) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, - ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_POSITION, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, x, y); -} - -/** - * @ingroup iface_zwlr_output_configuration_head_v1 - * - * This request sets the head's transform. - */ -static inline void -zwlr_output_configuration_head_v1_set_transform(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t transform) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, - ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_TRANSFORM, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, transform); -} - -/** - * @ingroup iface_zwlr_output_configuration_head_v1 - * - * This request sets the head's scale. - */ -static inline void -zwlr_output_configuration_head_v1_set_scale(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, wl_fixed_t scale) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, - ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_SCALE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, scale); -} - -/** - * @ingroup iface_zwlr_output_configuration_head_v1 - * - * This request enables/disables adaptive sync. Adaptive sync is also - * known as Variable Refresh Rate or VRR. - */ -static inline void -zwlr_output_configuration_head_v1_set_adaptive_sync(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, uint32_t state) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, - ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_ADAPTIVE_SYNC, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, state); -} +// /** +// * @ingroup iface_zwlr_output_configuration_head_v1 +// * +// * This request sets the head's mode. +// */ +// static inline void +// zwlr_output_configuration_head_v1_set_mode(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, struct zwlr_output_mode_v1 *mode) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, +// ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, mode); +// } + +// /** +// * @ingroup iface_zwlr_output_configuration_head_v1 +// * +// * This request assigns a custom mode to the head. The size is given in +// * physical hardware units of the output device. If set to zero, the +// * refresh rate is unspecified. +// * +// * It is a protocol error to set both a mode and a custom mode. +// */ +// static inline void +// zwlr_output_configuration_head_v1_set_custom_mode(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t width, int32_t height, int32_t refresh) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, +// ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_CUSTOM_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, width, height, refresh); +// } + +// /** +// * @ingroup iface_zwlr_output_configuration_head_v1 +// * +// * This request sets the head's position in the global compositor space. +// */ +// static inline void +// zwlr_output_configuration_head_v1_set_position(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t x, int32_t y) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, +// ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_POSITION, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, x, y); +// } + +// /** +// * @ingroup iface_zwlr_output_configuration_head_v1 +// * +// * This request sets the head's transform. +// */ +// static inline void +// zwlr_output_configuration_head_v1_set_transform(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, int32_t transform) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, +// ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_TRANSFORM, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, transform); +// } + +// /** +// * @ingroup iface_zwlr_output_configuration_head_v1 +// * +// * This request sets the head's scale. +// */ +// static inline void +// zwlr_output_configuration_head_v1_set_scale(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, wl_fixed_t scale) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, +// ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_SCALE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, scale); +// } + +// /** +// * @ingroup iface_zwlr_output_configuration_head_v1 +// * +// * This request enables/disables adaptive sync. Adaptive sync is also +// * known as Variable Refresh Rate or VRR. +// */ +// static inline void +// zwlr_output_configuration_head_v1_set_adaptive_sync(struct zwlr_output_configuration_head_v1 *zwlr_output_configuration_head_v1, uint32_t state) +// { +// wl_proxy_marshal_flags((struct wl_proxy *) zwlr_output_configuration_head_v1, +// ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_ADAPTIVE_SYNC, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_output_configuration_head_v1), 0, state); +// } #ifdef __cplusplus } From 75e6f48dfd1cec31feeef7e9acee932446c6799b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 22 Apr 2024 19:44:16 +0800 Subject: [PATCH 39/40] Doc: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29e1d7fffd..e7aebf98ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changes: * We now always detect max frequency of GPUs for consistent, instead of current frequency Features: -* We now use `wlr-randr` to detect displays for wlroots based WMs, which correctly reports fractional scale factors (Display, Linux) +* Improve display detection for wlroots based WMs. Fastfetch now correctly reports fractional scale factors in hyprland (Display, Linux) * Improve GPU detection on Linux (GPU, Linux) * Support GPU memory usage detection for AMD GPUs * Support GPU frequency detection for Intel GPUs From a1c9b24755d64031af18ada83141cb726c248f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 22 Apr 2024 21:22:50 +0800 Subject: [PATCH 40/40] Release: v2.10.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05e7ad5002..6ee082f535 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url project(fastfetch - VERSION 2.9.2 + VERSION 2.10.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"