├── .github └── workflows │ ├── build-appimage.yml │ └── build-flatpak.yml ├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── build-aux ├── flatpak │ ├── io.github.radiolamp.mangojuice.json │ └── patches │ │ ├── 0001-wayland-dlopen-correct-library.patch │ │ └── libxnvctrl_so_0.patch └── mangojuice-appimage.sh ├── data ├── assets │ ├── boosty_qrcode.png │ ├── donationalerts_qrcode.png │ ├── icons │ │ ├── io.github.radiolamp.mangojuice-extras-symbolic.svg │ │ ├── io.github.radiolamp.mangojuice-metrics-symbolic.svg │ │ ├── io.github.radiolamp.mangojuice-other-symbolic.svg │ │ ├── io.github.radiolamp.mangojuice-performance-symbolic.svg │ │ ├── io.github.radiolamp.mangojuice-visual-symbolic.svg │ │ ├── io.github.radiolamp.mangojuice.donate-symbolic.svg │ │ └── list-drag-handle-symbolic.svg │ └── tbank_qrcode.png ├── gresource.xml ├── icons │ └── hicolor │ │ └── scalable │ │ └── apps │ │ └── io.github.radiolamp.mangojuice.svg ├── images │ ├── advanced.png │ ├── default.png │ ├── fps.png │ ├── full.png │ └── minimal.png ├── io.github.radiolamp.mangojuice.desktop ├── io.github.radiolamp.mangojuice.metainfo.xml ├── meson.build └── style.css ├── docs └── README-ru.md ├── meson.build ├── meson_options.txt ├── po ├── LINGUAS ├── POTFILES ├── mangojuice.pot ├── meson.build ├── pt_BR.po ├── ru.po └── update_potfiles.sh └── src ├── advanced.vala ├── config.vapi ├── dialog.vala ├── intel_power_fix_handler.vala ├── load_states.vala ├── mangojuice.vala ├── meson.build ├── other ├── OtherBox.vala ├── other_load.vala └── other_save.vala ├── reset_manager.vala └── save_states.vala /.github/workflows/build-appimage.yml: -------------------------------------------------------------------------------- 1 | name: Build MangoJuice AppImage 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: {} 9 | 10 | jobs: 11 | build: 12 | name: "${{ matrix.name }} (${{ matrix.arch }})" 13 | runs-on: ${{ matrix.runs-on }} 14 | strategy: 15 | matrix: 16 | include: 17 | - runs-on: ubuntu-latest 18 | name: "mangojuice build" 19 | arch: x86_64 20 | - runs-on: ubuntu-24.04-arm 21 | name: "mangojuice build" 22 | arch: aarch64 23 | container: ghcr.io/pkgforge-dev/archlinux:latest 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | persist-credentials: false 29 | 30 | - name: Update system and install dependencies 31 | run: | 32 | pacman -Syu --noconfirm \ 33 | appstream \ 34 | base-devel \ 35 | cmocka \ 36 | dbus \ 37 | fmt \ 38 | fontconfig \ 39 | gcc-libs \ 40 | gettext \ 41 | git \ 42 | glew \ 43 | glfw \ 44 | glib2 \ 45 | glslang \ 46 | gtk4 \ 47 | hicolor-icon-theme \ 48 | libadwaita \ 49 | libgee \ 50 | libglvnd \ 51 | libsodium \ 52 | libx11 \ 53 | libxkbcommon \ 54 | libxrandr \ 55 | meson \ 56 | ninja \ 57 | nlohmann-json \ 58 | patchelf \ 59 | python \ 60 | python-mako \ 61 | python-matplotlib \ 62 | python-numpy \ 63 | strace \ 64 | vala \ 65 | vulkan-headers \ 66 | vulkan-tools \ 67 | wget \ 68 | xorg-server-xvfb \ 69 | zsync 70 | 71 | - name: Build mangohud 72 | run: | 73 | echo "Building mangohud..." 74 | echo "---------------------------------------------------------------" 75 | sed -i 's|EUID == 0|EUID == 69|g' /usr/bin/makepkg 76 | mkdir -p /usr/local/bin 77 | cp /usr/bin/makepkg /usr/local/bin 78 | 79 | sed -i -e 's|-O2|-Os|' \ 80 | -e 's|MAKEFLAGS=.*|MAKEFLAGS="-j$(nproc)"|' \ 81 | -e 's|#MAKEFLAGS|MAKEFLAGS|' \ 82 | /etc/makepkg.conf 83 | 84 | cat /etc/makepkg.conf 85 | 86 | git clone https://gitlab.archlinux.org/archlinux/packaging/packages/mangohud.git ./mangohud 87 | ( cd ./mangohud 88 | sed -i -e "s|x86_64|$(uname -m)|" \ 89 | -e 's|-Dmangohudctl=true|-Dmangohudctl=true -Dwith_xnvctrl=disabled|' \ 90 | -e '/libxnvctrl/d' ./PKGBUILD 91 | makepkg -f 92 | ls -la . 93 | pacman --noconfirm -U *.pkg.tar.* 94 | ) 95 | rm -rf ./mangohud 96 | 97 | - name: Build and install MangoJuice 98 | run: | 99 | meson setup build --prefix=/usr 100 | ninja -C build 101 | sudo ninja -C build install 102 | 103 | - name: Create AppImage 104 | run: | 105 | chmod +x ./build-aux/mangojuice-appimage.sh 106 | ./build-aux/mangojuice-appimage.sh 107 | 108 | - name: Upload AppImage artifact 109 | uses: actions/upload-artifact@v4 110 | with: 111 | name: MangoJuice-AppImagename-${{ matrix.arch }} 112 | path: MangoJuice-*.AppImage 113 | -------------------------------------------------------------------------------- /.github/workflows/build-flatpak.yml: -------------------------------------------------------------------------------- 1 | name: Flatpak Build 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | paths-ignore: 7 | - '**/README.md' 8 | pull_request: 9 | branches: [ "main" ] 10 | types: [ "review_requested", "ready_for_review" ] 11 | workflow_dispatch: 12 | 13 | permissions: 14 | id-token: write 15 | contents: read 16 | 17 | jobs: 18 | flatpak: 19 | name: "Build Flatpak" 20 | runs-on: ubuntu-latest 21 | container: 22 | image: fedora:latest 23 | options: --privileged 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Install dependencies 28 | run: | 29 | dnf install -y flatpak flatpak-builder 30 | 31 | - name: Setup Flatpak 32 | run: | 33 | flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 34 | flatpak install --user -y flathub org.gnome.Platform//48 35 | flatpak install --user -y flathub org.gnome.Sdk//48 36 | flatpak install --user -y flathub org.freedesktop.Platform.VulkanLayer.MangoHud//24.08 37 | 38 | - name: Build 39 | run: | 40 | flatpak-builder --user --force-clean --arch=x86_64 build-dir build-aux/flatpak/io.github.radiolamp.mangojuice.json 41 | 42 | - name: Create Bundle 43 | run: | 44 | flatpak build-export repo build-dir 45 | flatpak build-bundle repo io.github.radiolamp.mangojuice-x86_64.flatpak io.github.radiolamp.mangojuice 46 | 47 | - name: Upload Artifact 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: mangojuice-x86_64.flatpak 51 | path: io.github.radiolamp.mangojuice-x86_64.flatpak -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | Ссылка на MangoHud.conf 3 | /.flatpak-builder 4 | /.flatpak -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "mesonbuild.configureOnOpen": true, 3 | "files.watcherExclude": { 4 | "**/.git/objects/**": true, 5 | "**/.git/subtree-cache/**": true, 6 | "**/.hg/store/**": true, 7 | ".flatpak/**": true, 8 | "_build/**": true 9 | } 10 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build and run", 6 | "type": "shell", 7 | "command": "rm -rf ./build/ && meson setup build --reconfigure -Dis_devel=true --buildtype=debug && ninja -C build && ./build/src/mangojuice", 8 | "group": { 9 | "kind": "build", 10 | "isDefault": true 11 | }, 12 | "problemMatcher": [] 13 | 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![En](https://img.shields.io/badge/en-green)](README.md) [![Ru](https://img.shields.io/badge/ru-gray)](docs/README-ru.md) 2 | 3 |
4 |

5 | 9 | MangoJuice 10 |

11 | 12 | 13 | ### This program will be a convenient alternative to GOverlay for setting up MangoHud 14 | 15 |

16 | Screenshot 17 |

18 | 19 | | Page 1 | Page 2 | Page 3 | Page 4 | Page 5 | 20 | | :---------------------------------: | :---------------------------------: | :---------------------------------: | :---------------------------------: | :---------------------------------: | 21 | | ![screen1](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen1.png?raw=true) | ![screen2](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen2.png?raw=true) | ![screen3](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen3.png?raw=true) | ![screen4](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen4.png?raw=true) | ![screen5](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen0.png?raw=true) | 22 | 23 | ## Install 24 | 25 | **Flathub:** 26 | 27 | 28 | Download on Flathub 29 | 30 | 31 |
32 | 33 | ## Repositories 34 | 35 | [![Packaging status](https://repology.org/badge/vertical-allrepos/mangojuice.svg)](https://repology.org/project/mangojuice/versions) 36 | 37 | And also in the repository for [`openSUSE`](https://software.opensuse.org/package/mangojuice) 38 | 39 | ## Source code build 40 | 41 | ### Dependencies 42 | 43 | #### Build utilities 44 | 45 | - `meson` 46 | - `ninja` 47 | - `cmake` 48 | - `gcc` 49 | - `valac` 50 | 51 | #### Build requires 52 | 53 | - `gtk4` 54 | - `libadwaita-1` 55 | - `gio-2.0` 56 | - `fontconfig` 57 | - `mangohud` 58 | 59 | #### Optional dependencies 60 | 61 | - `mesa-demos` 62 | - `vulkan-tools` 63 | - `vkbasalt` 64 | 65 | ### Build 66 | 67 | ```shell 68 | meson setup build 69 | ``` 70 | 71 | ### Install 72 | 73 | ```shell 74 | sudo ninja -C build install 75 | ``` 76 | 77 | ### Uninstall 78 | 79 | ```shell 80 | sudo ninja -C build uninstall 81 | ``` 82 | 83 | ## Devel 84 | 85 | Also in the app there is Devel mode. It is intended for development and it is not recommended to use it on a permanent basis. In this mode new features of mangojuice and [mangohud-git](https://aur.archlinux.org/packages/mangohud-git) versions are tested. If you still want to use it, there is [mangojuice-git](https://aur.archlinux.org/packages/mangojuice-git) package in AUR or you can build it yourself with the following command: 86 | 87 | ```shell 88 | meson setup build 89 | meson configure build --no-pager -Dis_devel=true 90 | sudo ninja -C build install 91 | ``` 92 | 93 | ## Support 94 | 95 | You can support in several ways: 96 | 97 | - Create an issue with a problem or a suggestion for improvement; 98 | - Submit a merge request with a fix or new functionality; 99 | - Support financially (please include your nickname in message when sending via T-Bank). 100 | 101 |
102 | 103 |
104 | 105 | Boosty 106 | 107 | 108 | Donation Alerts 109 | 110 | 111 | T-Bank 112 | 113 |
114 | 115 | ## Gratitude 116 | 117 | Thank to [Rirusha](https://gitlab.gnome.org/Rirusha) for important clarifications about Vala and GTK4. 118 | 119 | ### Projects that have become muses 120 | 121 | - [`MangoHud`](https://github.com/flightlessmango/MangoHud) 122 | - [`Goverlay`](https://github.com/benjamimgois/goverlay) 123 | - [`Colloid`](https://github.com/vinceliuice/Colloid-icon-theme/) 124 | 125 | ### Attention, this is my first project on GTK4 + Vala, so please treat with understanding. 126 | -------------------------------------------------------------------------------- /build-aux/flatpak/io.github.radiolamp.mangojuice.json: -------------------------------------------------------------------------------- 1 | { 2 | "app-id": "io.github.radiolamp.mangojuice", 3 | "runtime": "org.gnome.Platform", 4 | "runtime-version": "48", 5 | "sdk": "org.gnome.Sdk", 6 | "command": "mangojuice", 7 | "finish-args": [ 8 | "--socket=wayland", 9 | "--share=ipc", 10 | "--socket=fallback-x11", 11 | "--device=dri", 12 | "--env=PATH=/app/bin:/usr/bin:/app/utils/bin", 13 | "--filesystem=home", 14 | "--filesystem=xdg-config/MangoHud:create" 15 | ], 16 | "cleanup": [ 17 | "/include", 18 | "/lib/pkgconfig", 19 | "/man", 20 | "/share/doc", 21 | "/share/gtk-doc", 22 | "/share/man", 23 | "/share/pkgconfig", 24 | "/share/vala", 25 | "*.la", 26 | "*.a" 27 | ], 28 | "modules": [ 29 | { 30 | "name": "vulkan-tools", 31 | "buildsystem": "cmake-ninja", 32 | "config-opts": [ 33 | "-DGLSLANG_INSTALL_DIR=/app", 34 | "-DVULKAN_HEADERS_INSTALL_DIR=/app", 35 | "-DCMAKE_BUILD_TYPE=Release" 36 | ], 37 | "sources": [ 38 | { 39 | "type": "archive", 40 | "url": "https://github.com/KhronosGroup/Vulkan-Tools/archive/v1.3.297/Vulkan-Tools-1.3.297.tar.gz", 41 | "sha256": "95bffa39d90f3ec81d8e3a0fa6c846ac1a10442152cc0b6d0d6567ce48932f89" 42 | } 43 | ], 44 | "modules": [ 45 | { 46 | "name": "volk", 47 | "buildsystem": "cmake-ninja", 48 | "config-opts": [ 49 | "-DVOLK_INSTALL=ON" 50 | ], 51 | "sources": [ 52 | { 53 | "type": "archive", 54 | "url": "https://github.com/zeux/volk/archive/vulkan-sdk-1.3.296.0.tar.gz", 55 | "sha256": "8ffd0e81e29688f4abaa39e598937160b098228f37503903b10d481d4862ab85" 56 | } 57 | ], 58 | "modules": [ 59 | { 60 | "name": "vulkan-headers", 61 | "buildsystem": "cmake-ninja", 62 | "sources": [ 63 | { 64 | "type": "archive", 65 | "url": "https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.3.297/Vulkan-Headers-v1.3.297.tar.gz", 66 | "sha256": "1d679e2edc43cb7ad818b81dea960e374f1d6dd082325eb9b4c6113e76263c02" 67 | } 68 | ] 69 | } 70 | ] 71 | } 72 | ] 73 | }, 74 | { 75 | "name": "MangoHud", 76 | "buildsystem": "meson", 77 | "config-opts": [ 78 | "-Dwith_xnvctrl=enabled", 79 | "-Dappend_libdir_mangohud=false", 80 | "-Dmangoplot=disabled", 81 | "-Dmangoapp=true" 82 | ], 83 | "sources": [ 84 | { 85 | "type": "archive", 86 | "url": "https://github.com/flightlessmango/MangoHud/releases/download/v0.8.1/MangoHud-v0.8.1-Source.tar.xz", 87 | "sha256": "4c8d8098f5deff7737978d792eef909b7469933f456a47132dccc06804825482", 88 | "x-checker-data": { 89 | "type": "json", 90 | "url": "https://api.github.com/repos/flightlessmango/MangoHud/releases", 91 | "url-query": "map(select(.tag_name | startswith(\"v\"))) | first |\n.assets | map((select(.name | endswith(\"Source.tar.xz\")))) | first | .browser_download_url\n", 92 | "version-query": "map(select(.tag_name | startswith(\"v\"))) | first |\n.tag_name | sub(\"^[vV]\"; \"\")\n" 93 | } 94 | }, 95 | { 96 | "type": "patch", 97 | "path": "patches/0001-wayland-dlopen-correct-library.patch" 98 | } 99 | ], 100 | "modules": [ 101 | { 102 | "name": "glew", 103 | "no-autogen": true, 104 | "make-args": [ 105 | "GLEW_PREFIX=${FLATPAK_DEST}", 106 | "GLEW_DEST=${FLATPAK_DEST}", 107 | "LIBDIR=${FLATPAK_DEST}/lib", 108 | "PKGDIR=${FLATPAK_DEST}/lib/pkgconfig", 109 | "CFLAGS.EXTRA:=${CFLAGS} -fPIC", 110 | "LDFLAGS.EXTRA=${LDFLAGS}" 111 | ], 112 | "make-install-args": [ 113 | "GLEW_PREFIX=${FLATPAK_DEST}", 114 | "GLEW_DEST=${FLATPAK_DEST}", 115 | "LIBDIR=${FLATPAK_DEST}/lib", 116 | "PKGDIR=${FLATPAK_DEST}/lib/pkgconfig", 117 | "CFLAGS.EXTRA:=${CFLAGS} -fPIC", 118 | "LDFLAGS.EXTRA=${LDFLAGS}" 119 | ], 120 | "sources": [ 121 | { 122 | "type": "archive", 123 | "url": "https://downloads.sourceforge.net/project/glew/glew/2.2.0/glew-2.2.0.tgz", 124 | "sha256": "d4fc82893cfb00109578d0a1a2337fb8ca335b3ceccf97b97e5cc7f08e4353e1" 125 | } 126 | ], 127 | "modules": [ 128 | { 129 | "name": "glu", 130 | "buildsystem": "meson", 131 | "sources": [ 132 | { 133 | "type": "archive", 134 | "url": "https://archive.mesa3d.org/glu/glu-9.0.3.tar.xz", 135 | "sha256": "bd43fe12f374b1192eb15fe20e45ff456b9bc26ab57f0eee919f96ca0f8a330f" 136 | } 137 | ] 138 | } 139 | ] 140 | }, 141 | { 142 | "name": "glfw", 143 | "buildsystem": "cmake-ninja", 144 | "config-opts": [ 145 | "-DGLFW_BUILD_EXAMPLES:BOOL=OFF", 146 | "-DGLFW_BUILD_TESTS:BOOL=OFF", 147 | "-DGLFW_BUILD_DOCS:BOOL=OFF" 148 | ], 149 | "sources": [ 150 | { 151 | "type": "git", 152 | "url": "https://github.com/glfw/glfw.git", 153 | "tag": "3.4", 154 | "commit": "7b6aead9fb88b3623e3b3725ebb42670cbe4c579" 155 | } 156 | ] 157 | }, 158 | { 159 | "name": "nlohmann-json", 160 | "buildsystem": "cmake-ninja", 161 | "config-opts": [ 162 | "-DJSON_BuildTests:BOOL=OFF" 163 | ], 164 | "sources": [ 165 | { 166 | "type": "git", 167 | "url": "https://github.com/nlohmann/json.git", 168 | "tag": "v3.11.2", 169 | "commit": "bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d" 170 | } 171 | ] 172 | }, 173 | { 174 | "name": "libNVCtrl", 175 | "no-autogen": true, 176 | "no-make-install": true, 177 | "build-commands": [ 178 | "mkdir -p ${FLATPAK_DEST}/lib", 179 | "cp -a libXNVCtrl.so* ${FLATPAK_DEST}/lib/", 180 | "install -D *.h -t ${FLATPAK_DEST}/include/NVCtrl" 181 | ], 182 | "subdir": "src/libXNVCtrl", 183 | "sources": [ 184 | { 185 | "type": "archive", 186 | "archive-type": "tar", 187 | "url": "https://api.github.com/repos/NVIDIA/nvidia-settings/tarball/refs/tags/470.63.01", 188 | "sha256": "0ede63515851d85ac0d1ed5f00e355f539968e6d1fd226120a27b2c66c3575de" 189 | }, 190 | { 191 | "type": "patch", 192 | "path": "patches/libxnvctrl_so_0.patch", 193 | "x-checker-data": { 194 | "type": "json", 195 | "url": "https://api.github.com/repos/NVIDIA/nvidia-settings/tags", 196 | "url-query": ".[0].tarball_url", 197 | "version-query": ".[0].na" 198 | } 199 | } 200 | ] 201 | } 202 | ] 203 | }, 204 | { 205 | "name": "mangojuice", 206 | "buildsystem": "meson", 207 | "sources": [ 208 | { 209 | "type": "dir", 210 | "path": "../../" 211 | } 212 | ] 213 | } 214 | ] 215 | } -------------------------------------------------------------------------------- /build-aux/flatpak/patches/0001-wayland-dlopen-correct-library.patch: -------------------------------------------------------------------------------- 1 | From 6d2176a0e6280b13fa128589d2b3362be6de19e3 Mon Sep 17 00:00:00 2001 2 | From: 17314642 <48242771@protonmail.ch> 3 | Date: Fri, 4 Apr 2025 07:58:39 +0300 4 | Subject: [PATCH] wayland: dlopen correct library 5 | 6 | --- 7 | src/gl/inject_egl.cpp | 4 ++-- 8 | src/vulkan.cpp | 2 +- 9 | 2 files changed, 3 insertions(+), 3 deletions(-) 10 | 11 | diff --git a/src/gl/inject_egl.cpp b/src/gl/inject_egl.cpp 12 | index e40d174..34fea7a 100644 13 | --- a/src/gl/inject_egl.cpp 14 | +++ b/src/gl/inject_egl.cpp 15 | @@ -99,7 +99,7 @@ EXPORT_C_(void*) eglGetPlatformDisplay( unsigned int platform, void* native_disp 16 | { 17 | wl_display_ptr = (struct wl_display*)native_display; 18 | HUDElements.display_server = HUDElements.display_servers::WAYLAND; 19 | - wl_handle = real_dlopen("libwayland-client.so", RTLD_LAZY); 20 | + wl_handle = real_dlopen("libwayland-client.so.0", RTLD_LAZY); 21 | init_wayland_data(); 22 | } 23 | #endif 24 | @@ -125,7 +125,7 @@ EXPORT_C_(void*) eglGetDisplay( void* native_display ) 25 | { 26 | wl_display_ptr = (struct wl_display*)native_display; 27 | HUDElements.display_server = HUDElements.display_servers::WAYLAND; 28 | - wl_handle = real_dlopen("libwayland-client.so", RTLD_LAZY); 29 | + wl_handle = real_dlopen("libwayland-client.so.0", RTLD_LAZY); 30 | init_wayland_data(); 31 | } 32 | } 33 | diff --git a/src/vulkan.cpp b/src/vulkan.cpp 34 | index 9a10d61..d50c43a 100644 35 | --- a/src/vulkan.cpp 36 | +++ b/src/vulkan.cpp 37 | @@ -2034,7 +2034,7 @@ static VkResult overlay_CreateWaylandSurfaceKHR( 38 | { 39 | struct instance_data *instance_data = FIND(struct instance_data, instance); 40 | if (!wl_handle) 41 | - wl_handle = real_dlopen("libwayland-client.so", RTLD_LAZY); 42 | + wl_handle = real_dlopen("libwayland-client.so.0", RTLD_LAZY); 43 | wl_display_ptr = pCreateInfo->display; 44 | HUDElements.display_server = HUDElements.display_servers::WAYLAND; 45 | init_wayland_data(); 46 | -- 47 | 2.49.0 48 | 49 | -------------------------------------------------------------------------------- /build-aux/flatpak/patches/libxnvctrl_so_0.patch: -------------------------------------------------------------------------------- 1 | diff -up nvidia-settings-435.17/src/libXNVCtrl/Makefile.shared nvidia-settings-435.17/src/libXNVCtrl/Makefile 2 | --- nvidia-settings-435.17/src/libXNVCtrl/Makefile.shared 2019-08-07 06:12:32.000000000 +0200 3 | +++ nvidia-settings-435.17/src/libXNVCtrl/Makefile 2019-08-26 16:23:41.921778088 +0200 4 | @@ -50,8 +50,9 @@ LDFLAGS += $(XNVCTRL_LDFLAGS) 5 | 6 | .PHONY: clean 7 | 8 | -all: $(LIBXNVCTRL) 9 | +all: $(LIBXNVCTRL) libXNVCtrl.so 10 | 11 | clean: 12 | rm -rf $(LIBXNVCTRL) *~ \ 13 | $(OUTPUTDIR)/*.o $(OUTPUTDIR)/*.d 14 | + rm -f libXNVCtrl.so libXNVCtrl.so.* 15 | diff -up nvidia-settings-435.17/src/Makefile.shared nvidia-settings-435.17/src/Makefile 16 | --- nvidia-settings-435.17/src/Makefile.shared 2019-08-07 06:12:31.000000000 +0200 17 | +++ nvidia-settings-435.17/src/Makefile 2019-08-26 16:17:31.133406921 +0200 18 | @@ -106,6 +106,9 @@ endif 19 | XNVCTRL_DIR ?= libXNVCtrl 20 | XCONFIG_PARSER_DIR ?= XF86Config-parser 21 | COMMON_UTILS_DIR ?= common-utils 22 | +XNVCTRL_SHARED ?= $(XNVCTRL_DIR)/libXNVCtrl.so.0 23 | +#XNVCTRL_LIB ?= $(XNVCTRL_ARCHIVE) 24 | +XNVCTRL_LIB ?= $(XNVCTRL_SHARED) 25 | COMMON_UNIX_DIR ?= common-unix 26 | VIRTUAL_RESOLUTIONS_DIR ?= $(COMMON_UNIX_DIR)/virtual-resolutions 27 | 28 | diff -up nvidia-settings-435.17/src/libXNVCtrl/xnvctrl.mk.shared nvidia-settings-435.17/src/libXNVCtrl/xnvctrl.mk 29 | --- nvidia-settings-435.17/src/libXNVCtrl/xnvctrl.mk.shared 2019-08-07 06:12:32.000000000 +0200 30 | +++ nvidia-settings-435.17/src/libXNVCtrl/xnvctrl.mk 2019-08-26 16:54:44.518016474 +0200 31 | @@ -47,3 +47,8 @@ $(eval $(call DEFINE_OBJECT_RULE,TARGET, 32 | 33 | $(LIBXNVCTRL) : $(LIBXNVCTRL_OBJ) 34 | $(call quiet_cmd,AR) ru $@ $(LIBXNVCTRL_OBJ) 35 | + 36 | +libXNVCtrl.so: $(LIBXNVCTRL_OBJ) 37 | + $(CC) -shared -Wl,-soname=$@.0 -o $@.0.0.0 $(LDFLAGS) $^ -lXext -lX11 38 | + ln -s $@.0.0.0 $@.0 39 | + ln -s $@.0 $@ 40 | -------------------------------------------------------------------------------- /build-aux/mangojuice-appimage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | export ARCH="$(uname -m)" 6 | export APPIMAGE_EXTRACT_AND_RUN=1 7 | export VERSION="${GITHUB_SHA:-$(date +%Y%m%d)}" 8 | UPINFO="gh-releases-zsync|${GITHUB_REPOSITORY:-radiolamp/mangojuice}|latest|*$ARCH.AppImage.zsync" 9 | 10 | LIB4BN="https://raw.githubusercontent.com/VHSgunzo/sharun/refs/heads/main/lib4bin" 11 | URUNTIME="https://github.com/VHSgunzo/uruntime/releases/latest/download/uruntime-appimage-dwarfs-$ARCH" 12 | URUNTIME_LITE="https://github.com/VHSgunzo/uruntime/releases/latest/download/uruntime-appimage-dwarfs-lite-$ARCH" 13 | 14 | # add debloated packages 15 | if [ "$(uname -m)" = 'x86_64' ]; then 16 | PKG_TYPE='x86_64.pkg.tar.zst' 17 | else 18 | PKG_TYPE='aarch64.pkg.tar.xz' 19 | fi 20 | 21 | LLVM_URL="https://github.com/pkgforge-dev/llvm-libs-debloated/releases/download/continuous/llvm-libs-nano-$PKG_TYPE" 22 | LIBXML_URL="https://github.com/pkgforge-dev/llvm-libs-debloated/releases/download/continuous/libxml2-iculess-$PKG_TYPE" 23 | MESA_URL="https://github.com/pkgforge-dev/llvm-libs-debloated/releases/download/continuous/mesa-mini-$PKG_TYPE" 24 | VK_RADEON_URL="https://github.com/pkgforge-dev/llvm-libs-debloated/releases/download/continuous/vulkan-radeon-mini-$PKG_TYPE" 25 | VK_INTEL_URL="https://github.com/pkgforge-dev/llvm-libs-debloated/releases/download/continuous/vulkan-intel-mini-$PKG_TYPE" 26 | VK_NOUVEAU_URL="https://github.com/pkgforge-dev/llvm-libs-debloated/releases/download/continuous/vulkan-nouveau-mini-$PKG_TYPE" 27 | VK_PANFROST_URL="https://github.com/pkgforge-dev/llvm-libs-debloated/releases/download/continuous/vulkan-panfrost-mini-$PKG_TYPE" 28 | VK_FREEDRENO_URL="https://github.com/pkgforge-dev/llvm-libs-debloated/releases/download/continuous/vulkan-freedreno-mini-$PKG_TYPE" 29 | VK_BROADCOM_URL="https://github.com/pkgforge-dev/llvm-libs-debloated/releases/download/continuous/vulkan-broadcom-mini-$PKG_TYPE" 30 | 31 | echo "Installing debloated pckages..." 32 | echo "---------------------------------------------------------------" 33 | wget --retry-connrefused --tries=30 "$LLVM_URL" -O ./llvm-libs.pkg.tar.zst 34 | wget --retry-connrefused --tries=30 "$LIBXML_URL" -O ./libxml2.pkg.tar.zst 35 | wget --retry-connrefused --tries=30 "$MESA_URL" -O ./mesa.pkg.tar.zst 36 | wget --retry-connrefused --tries=30 "$VK_RADEON_URL" -O ./vulkan-radeon.pkg.tar.zst 37 | wget --retry-connrefused --tries=30 "$VK_NOUVEAU_URL" -O ./vulkan-nouveau.pkg.tar.zst 38 | 39 | if [ "$(uname -m)" = 'x86_64' ]; then 40 | wget --retry-connrefused --tries=30 "$VK_INTEL_URL" -O ./vulkan-intel.pkg.tar.zst 41 | else 42 | wget --retry-connrefused --tries=30 "$VK_PANFROST_URL" -O ./vulkan-panfrost.pkg.tar.zst 43 | wget --retry-connrefused --tries=30 "$VK_FREEDRENO_URL" -O ./vulkan-freedreno.pkg.tar.zst 44 | wget --retry-connrefused --tries=30 "$VK_BROADCOM_URL" -O ./vulkan-broadcom.pkg.tar.zst 45 | fi 46 | 47 | pacman -U --noconfirm ./*.pkg.tar.zst 48 | rm -f ./*.pkg.tar.zst 49 | 50 | echo "Making AppImage..." 51 | echo "---------------------------------------------------------------" 52 | mkdir -p ./AppDir 53 | cd ./AppDir || exit 54 | 55 | if [ -f "/usr/share/applications/io.github.radiolamp.mangojuice.desktop" ]; then 56 | cp "/usr/share/applications/io.github.radiolamp.mangojuice.desktop" ./ 57 | sed -i 's|^Exec=.*|Exec=mangojuice|' ./io.github.radiolamp.mangojuice.desktop 58 | else 59 | echo "Ошибка: desktop-файл не найден" 60 | exit 1 61 | fi 62 | 63 | if [ -f "/usr/share/icons/hicolor/scalable/apps/io.github.radiolamp.mangojuice.svg" ]; then 64 | cp "/usr/share/icons/hicolor/scalable/apps/io.github.radiolamp.mangojuice.svg" ./ 65 | cp "./io.github.radiolamp.mangojuice.svg" ./.DirIcon 66 | else 67 | echo "Предупреждение: значёк не найден" 68 | fi 69 | 70 | for icon in /usr/share/icons/hicolor/scalable/apps/io.github.radiolamp.mangojuice*.svg; do 71 | mkdir -p ./share/icons/hicolor/scalable/apps/ 72 | cp "$icon" ./share/icons/hicolor/scalable/apps/ 73 | done 74 | 75 | mkdir -p ./share/locale 76 | for mo_file in /usr/share/locale/*/LC_MESSAGES/mangojuice.mo; do 77 | lang_dir=$(dirname "$(dirname "$mo_file")") 78 | lang=$(basename "$lang_dir") 79 | if [[ "$lang" =~ ^[a-z]{2}$ ]]; then 80 | lang="${lang}_${lang^^}" 81 | fi 82 | mkdir -p "./share/locale/$lang/LC_MESSAGES" 83 | cp "$mo_file" "./share/locale/$lang/LC_MESSAGES/mangojuice.mo" 84 | done 85 | 86 | echo "Загрузка lib4bin..." 87 | wget --retry-connrefused --tries=30 "$LIB4BN" -O ./lib4bin || { 88 | echo "Ошибка загрузки lib4bin" 89 | exit 1 90 | } 91 | chmod +x ./lib4bin 92 | 93 | echo "Копирование файлов mangojuice..." 94 | xvfb-run -a -- ./lib4bin -p -v -e -s -k \ 95 | /usr/bin/mangojuice \ 96 | /usr/bin/vkcube \ 97 | /usr/lib/mangohud/* \ 98 | /usr/bin/lspci \ 99 | /usr/lib/libintl.so* \ 100 | /usr/lib/gdk-pixbuf-*/*/*/* || { 101 | echo "Ошибка при копировании файлов" 102 | exit 1 103 | } 104 | 105 | mkdir -p ./share/vulkan 106 | if [ -d "/usr/share/vulkan/implicit_layer.d" ]; then 107 | cp -rv "/usr/share/vulkan/implicit_layer.d" "./share/vulkan/" 108 | sed -i 's|/usr/lib/mangohud/||' ./share/vulkan/implicit_layer.d/* 109 | else 110 | echo "Предупреждение: Vulkan layers не найдены" 111 | fi 112 | 113 | # mangojuice is also going to run mangohud vkcube so we need to wrap this 114 | echo '#!/bin/sh 115 | CURRENTDIR="$(dirname "$(readlink -f "$0")")" 116 | export MANGOHUD=1 117 | shift 118 | "$CURRENTDIR"/vkcube "$@"' > ./bin/mangohud 119 | chmod +x ./bin/mangohud 120 | 121 | # copy anything that remains in ./usr/share to ./share 122 | cp -rv ./usr/share/* ./share || true 123 | rm -rf ./usr/share 124 | 125 | # prepare sharun 126 | ln ./sharun ./AppRun 127 | ./sharun -g 128 | 129 | echo 'MANGOJUICE=1' > ./.env 130 | echo 'TEXTDOMAINDIR="${SHARUN_DIR}/share/locale' >> ./.env 131 | echo 'TEXTDOMAIN="mangojuice' >> ./.env 132 | echo 'libMangoHud_shim.so' > ./.preload 133 | 134 | cd .. || exit 135 | echo "Загрузка uruntime..." 136 | wget --retry-connrefused --tries=30 "$URUNTIME" -O ./uruntime || { 137 | echo "Ошибка загрузки uruntime" 138 | exit 1 139 | } 140 | wget --retry-connrefused --tries=30 "$URUNTIME_LITE" -O ./uruntime-lite || { 141 | echo "Ошибка загрузки uruntime-lite" 142 | exit 1 143 | } 144 | chmod +x ./uruntime* 145 | 146 | echo "Добавление информации об обновлениях..." 147 | ./uruntime-lite --appimage-addupdinfo "$UPINFO" || { 148 | echo "Ошибка добавления информации об обновлении" 149 | exit 1 150 | } 151 | 152 | echo "Создание AppImage..." 153 | ./uruntime --appimage-mkdwarfs -f \ 154 | --set-owner 0 --set-group 0 \ 155 | --no-history --no-create-timestamp \ 156 | --compression zstd:level=22 -S26 -B8 \ 157 | --header uruntime-lite \ 158 | -i ./AppDir -o "MangoJuice-${VERSION}-${ARCH}.AppImage" || { 159 | echo "Ошибка создания AppImage" 160 | exit 1 161 | } 162 | 163 | echo "Готово! Создан AppImage: MangoJuice-${VERSION}-${ARCH}.AppImage" 164 | -------------------------------------------------------------------------------- /data/assets/boosty_qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiolamp/mangojuice/ac8c390be976e61b4f8cc8d31c253c84e47783ee/data/assets/boosty_qrcode.png -------------------------------------------------------------------------------- /data/assets/donationalerts_qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiolamp/mangojuice/ac8c390be976e61b4f8cc8d31c253c84e47783ee/data/assets/donationalerts_qrcode.png -------------------------------------------------------------------------------- /data/assets/icons/io.github.radiolamp.mangojuice-extras-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /data/assets/icons/io.github.radiolamp.mangojuice-metrics-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /data/assets/icons/io.github.radiolamp.mangojuice-other-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /data/assets/icons/io.github.radiolamp.mangojuice-performance-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /data/assets/icons/io.github.radiolamp.mangojuice-visual-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /data/assets/icons/io.github.radiolamp.mangojuice.donate-symbolic.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/assets/icons/list-drag-handle-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /data/assets/tbank_qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiolamp/mangojuice/ac8c390be976e61b4f8cc8d31c253c84e47783ee/data/assets/tbank_qrcode.png -------------------------------------------------------------------------------- /data/gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | io.github.radiolamp.mangojuice.metainfo.xml 5 | style.css 6 | images/fps.png 7 | images/minimal.png 8 | images/default.png 9 | images/advanced.png 10 | images/full.png 11 | 12 | 13 | -------------------------------------------------------------------------------- /data/images/advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiolamp/mangojuice/ac8c390be976e61b4f8cc8d31c253c84e47783ee/data/images/advanced.png -------------------------------------------------------------------------------- /data/images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiolamp/mangojuice/ac8c390be976e61b4f8cc8d31c253c84e47783ee/data/images/default.png -------------------------------------------------------------------------------- /data/images/fps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiolamp/mangojuice/ac8c390be976e61b4f8cc8d31c253c84e47783ee/data/images/fps.png -------------------------------------------------------------------------------- /data/images/full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiolamp/mangojuice/ac8c390be976e61b4f8cc8d31c253c84e47783ee/data/images/full.png -------------------------------------------------------------------------------- /data/images/minimal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiolamp/mangojuice/ac8c390be976e61b4f8cc8d31c253c84e47783ee/data/images/minimal.png -------------------------------------------------------------------------------- /data/io.github.radiolamp.mangojuice.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=MangoJuice 3 | Comment=A convenient alternative to GOverlay for configuring MangoHud 4 | Exec=mangojuice 5 | Icon=io.github.radiolamp.mangojuice 6 | Terminal=false 7 | Type=Application 8 | Categories=Utility;Application; 9 | -------------------------------------------------------------------------------- /data/io.github.radiolamp.mangojuice.metainfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.github.radiolamp.mangojuice 4 | CC0-1.0 5 | GPL-3.0-or-later 6 | Mango Juice 7 | Customizable performance display 8 | 9 |

10 | MangoJuice is an application for easily editing MangoHUD settings, which in turn is an Overlay solution for tracking load and other parameters in games and other software. 11 |

12 |
13 | https://github.com/radiolamp/mangojuice 14 | https://github.com/radiolamp/mangojuice/issues 15 | 16 | 17 | https://github.com/radiolamp/mangojuice-donate/blob/0.3/images/screen1.png?raw=true 18 | Main interface of MangoJuice 19 | 20 | 21 | https://github.com/radiolamp/mangojuice-donate/blob/0.3/images/screen2.png?raw=true 22 | Extra 23 | 24 | 25 | https://github.com/radiolamp/mangojuice-donate/blob/0.3/images/screen3.png?raw=true 26 | Metric 27 | 28 | 29 | https://github.com/radiolamp/mangojuice-donate/blob/0.3/images/screen4.png?raw=true 30 | Visual 31 | 32 | 33 | https://github.com/radiolamp/mangojuice-donate/blob/0.3/images/screen5.png?raw=true 34 | Your profiles 35 | 36 | 37 | https://github.com/radiolamp/mangojuice-donate/blob/0.3/images/screen7.png?raw=true 38 | Change order 39 | 40 | 41 | https://github.com/radiolamp/mangojuice-donate/blob/0.3/images/screen6.png?raw=true 42 | Visual 43 | 44 | 45 | 46 | Radiolamp 47 | 48 | anton.osi@outlook.com 49 | 50 | 51 | 52 |

Profiles and sorting

53 |
    54 |
  • Full update information is available on GitHub.
  • 55 |
56 |
57 |
58 | 59 | 60 |

Initial release of MangoJuice.

61 |
62 |
63 |
64 | 65 | Utility 66 | 67 | 68 | 69 | io.github.radiolamp.mangojuice.desktop 70 | 71 | #fee372 72 | #dd624b 73 | 74 |
75 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | resources = gnome.compile_resources( 2 | 'resources', 3 | 'gresource.xml', 4 | source_dir: meson.current_build_dir() 5 | ) 6 | 7 | install_data( 8 | 'assets/icons/io.github.radiolamp.mangojuice-metrics-symbolic.svg', 9 | 'assets/icons/io.github.radiolamp.mangojuice-extras-symbolic.svg', 10 | 'assets/icons/io.github.radiolamp.mangojuice-performance-symbolic.svg', 11 | 'assets/icons/io.github.radiolamp.mangojuice-visual-symbolic.svg', 12 | 'assets/icons/io.github.radiolamp.mangojuice-other-symbolic.svg', 13 | 'assets/icons/io.github.radiolamp.mangojuice.donate-symbolic.svg', 14 | 'assets/icons/list-drag-handle-symbolic.svg', 15 | 'icons/hicolor/scalable/apps/io.github.radiolamp.mangojuice.svg', 16 | install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', 'scalable', 'apps') 17 | ) 18 | 19 | install_data( 20 | 'io.github.radiolamp.mangojuice.desktop', 21 | install_dir: get_option('datadir') / 'applications' 22 | ) 23 | 24 | install_data( 25 | 'io.github.radiolamp.mangojuice.metainfo.xml', 26 | install_dir: join_paths(get_option('datadir'), 'metainfo') 27 | ) -------------------------------------------------------------------------------- /data/style.css: -------------------------------------------------------------------------------- 1 | .love-hover { 2 | color: rgb(245, 94, 119); 3 | } 4 | .row-drop-indicator { 5 | border-top: 2px solid @accent_color; 6 | } 7 | 8 | -------------------------------------------------------------------------------- /docs/README-ru.md: -------------------------------------------------------------------------------- 1 | [![En](https://img.shields.io/badge/en-gray)](/README.md) [![Ru](https://img.shields.io/badge/ru-green)](/docs/README-ru.md) 2 | 3 |
4 |

5 | 9 | MangoJuice 10 |

11 | 12 | ### Эта программа станет удобной альтернативой GOverlay для настройки MangoHud 13 | 14 |

15 | Скриншот 16 |

17 | 18 | | Страница 1 | Страница 2 | Страница 3 | Страница 4 | Страница 5 | 19 | | :---------------------------------: | :---------------------------------: | :---------------------------------: | :---------------------------------: | :---------------------------------: | 20 | | ![screen1](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen1.png?raw=true) | ![screen2](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen2.png?raw=true) | ![screen3](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen3.png?raw=true) | ![screen4](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen4.png?raw=true) | ![screen5](https://github.com/radiolamp/mangojuice-donate/blob/main/images/screen0.png?raw=true) | 21 | 22 | ## Установить 23 | 24 | **Flathub:** 25 | 26 | 27 | Download on Flathub 28 | 29 | 30 |
31 | 32 | ## Репозитории 33 | 34 | [![Статус сборки](https://repology.org/badge/vertical-allrepos/mangojuice.svg)](https://repology.org/project/mangojuice/versions) 35 | 36 | Также доступен в репозитории для [`openSUSE`](https://software.opensuse.org/package/mangojuice). 37 | 38 | ## Сборка исходного кода 39 | 40 | ### Зависимости 41 | 42 | #### Инструменты сборки 43 | 44 | - `meson` 45 | - `ninja` 46 | - `cmake` 47 | - `gcc` 48 | - `valac` 49 | 50 | #### Требования для сборки 51 | 52 | - `gtk4` 53 | - `libadwaita-1` 54 | - `gio-2.0` 55 | - `fontconfig` 56 | - `mangohud` 57 | 58 | #### Опциональные зависимости 59 | 60 | - `mesa-demos` 61 | - `vulkan-tools` 62 | - `vkbasalt` 63 | 64 | ### Сборка 65 | 66 | ```shell 67 | meson setup build 68 | ``` 69 | 70 | ### Установка 71 | 72 | ```shell 73 | sudo ninja -C build install 74 | ``` 75 | 76 | ### Удаление 77 | 78 | ```shell 79 | sudo ninja -C build uninstall 80 | ``` 81 | 82 | ## Режим разрабочика 83 | 84 | Также в приложении есть режим Devel. Он предназначен для разработки и не рекомендуется использовать его на постоянной основе. В этом режиме тестируются новые возможности версий mangojuice и [mangohud-git](https://aur.archlinux.org/packages/mangohud-git). Если вы все же хотите использовать его, в AUR есть пакет [mangojuice-git]((https://aur.archlinux.org/packages/mangojuice-git)) или вы можете собрать его самостоятельно с помощью следующей команды: 85 | 86 | ```shell 87 | meson setup build 88 | meson configure build --no-pager -Dis_devel=true 89 | sudo ninja -C build install 90 | ``` 91 | 92 | ## Поддержка проекта 93 | 94 | Вы можете поддержать проект несколькими способами: 95 | 96 | - Создать задачу с проблемой или предложением по улучшению; 97 | - Отправить запрос на слияние с исправлениями или новой функциональностью; 98 | - Оказать финансовую поддержку (укажите ваш ник в сообщении при отправке через Т-Банк). 99 | 100 |
101 | 102 |
103 | 104 | Boosty 105 | 106 | 107 | Donation Alerts 108 | 109 | 110 | Т-Банк 111 | 112 |
113 | 114 | ## Благодарность 115 | 116 | Благодарю [Rirusha](https://gitlab.gnome.org/Rirusha) за важные разъяснения по Vala и GTK4. 117 | 118 | ### Проекты, ставшие вдохновлением 119 | 120 | - [`MangoHud`](https://github.com/flightlessmango/MangoHud) 121 | - [`Goverlay`](https://github.com/benjamimgois/goverlay) 122 | - [`Colloid`](https://github.com/vinceliuice/Colloid-icon-theme/) 123 | 124 | ### Обращаю ваше внимание, что это мой первый проект на GTK4 + Vala, прошу отнестись с пониманием. 125 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'mangojuice', 'vala', 3 | version: '0.8.5', 4 | default_options: [ 5 | 'warning_level=0', 6 | ] 7 | ) 8 | 9 | i18n = import('i18n') 10 | gnome = import('gnome') 11 | 12 | add_project_arguments( 13 | '-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()), 14 | language: 'c' 15 | ) 16 | 17 | name_suffix = '' 18 | app_id_suffix = '' 19 | version_suffix = '' 20 | 21 | if get_option('is_devel') 22 | name_suffix = ' (Devel)' 23 | app_id_suffix = '-Devel' 24 | 25 | find_program('git', required: true) 26 | vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD', check: true).stdout().strip() 27 | version_suffix = '-dev.@0@'.format(vcs_tag) 28 | endif 29 | 30 | conf = configuration_data() 31 | conf.set10('IS_DEVEL', get_option('is_devel')) 32 | conf.set_quoted('DATADIR', join_paths(get_option('prefix'), get_option('datadir'))) 33 | conf.set_quoted('GNOMELOCALEDIR', join_paths(get_option('prefix'), get_option('localedir'))) 34 | conf.set_quoted('VERSION', meson.project_version() + version_suffix) 35 | 36 | add_project_arguments( 37 | '-Wno-discarded-qualifiers', 38 | language: 'c' 39 | ) 40 | 41 | subdir('data') 42 | subdir('src') 43 | subdir('po') -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option( 2 | 'is_devel', 3 | type: 'boolean', 4 | value: false 5 | ) 6 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | ru 2 | pt_BR 3 | -------------------------------------------------------------------------------- /po/POTFILES: -------------------------------------------------------------------------------- 1 | data/io.github.radiolamp.mangojuice.desktop 2 | data/io.github.radiolamp.mangojuice.metainfo.xml 3 | src/advanced.vala 4 | src/dialog.vala 5 | src/intel_power_fix_handler.vala 6 | src/load_states.vala 7 | src/mangojuice.vala 8 | src/other/OtherBox.vala 9 | src/reset_manager.vala 10 | src/save_states.vala 11 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n.gettext(meson.project_name(), preset: 'glib') 2 | -------------------------------------------------------------------------------- /po/update_potfiles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Should run from project root dir 3 | 4 | touch ./po/unsort-POTFILES 5 | 6 | find ./src -iname "*.vala" -type f -exec grep -lrE '_\(|C_|ngettext' {} + | while read file; do echo "${file#./}" >> ./po/unsort-POTFILES; done 7 | find ./data/ -iname "*.desktop" | while read file; do echo "${file#./}" >> ./po/unsort-POTFILES; done 8 | find ./data/ -iname "*.metainfo.xml" | while read file; do echo "${file#./}" >> ./po/unsort-POTFILES; done 9 | 10 | cat ./po/unsort-POTFILES | sort | uniq > ./po/POTFILES 11 | 12 | rm ./po/unsort-POTFILES 13 | 14 | # To add translation, please use Damned Lies service https://l10n.gnome.org/ 15 | -------------------------------------------------------------------------------- /src/advanced.vala: -------------------------------------------------------------------------------- 1 | /* advanced.vala // Licence: GPL-v3.0 */ 2 | using Gtk; 3 | using Adw; 4 | 5 | public class AdvancedDialog : Adw.Dialog { 6 | File config_file; 7 | List all_config_lines; 8 | List filtered_config_lines; 9 | ListBox list_box; 10 | private Gtk.ListBoxRow? drop_indicator_row = null; 11 | 12 | string[] allowed_prefixes = { "custom_text_center=", "custom_text=", "gpu_stats", "vram", "cpu_stats", 13 | "core_load", "ram", "io_read", "io_write", "procmem", "swap", "fan", "fps", "fps_metrics=avg,0.01", 14 | "fps_metrics=avg,0.1", "version", "gamemode", "vkbasalt", "exec_name", "fsr", "hdr", "vulkan_driver", 15 | "engine_version", "refresh_rate", "resolution", "arch", "present_mode", "display_server", "show_fps_limit", 16 | "frame_timing", "frame_count", "battery", "battery_watt", "battery_time", "device_battery_icon", 17 | "device_battery=gamepad,mouse", "network", "media_player", "wine", "winesync" }; 18 | 19 | public static void show_advanced_dialog(Gtk.Window parent_window, MangoJuice app) { 20 | var advanced_dialog = new AdvancedDialog(parent_window, app); 21 | advanced_dialog.set_content_width(1000); 22 | advanced_dialog.set_content_height(2000); 23 | advanced_dialog.present(parent_window); 24 | } 25 | 26 | public AdvancedDialog (Gtk.Window parent, MangoJuice app) { 27 | Object (); 28 | 29 | var header_bar = new Adw.HeaderBar (); 30 | header_bar.add_css_class ("flat"); 31 | header_bar.set_size_request (320, -1); 32 | 33 | var warning_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6); 34 | warning_box.set_halign(Gtk.Align.CENTER); 35 | warning_box.set_margin_start(12); 36 | warning_box.set_margin_end(12); 37 | 38 | var warning_icon = new Gtk.Image.from_icon_name("dialog-warning-symbolic"); 39 | var warning_label = new Gtk.Label(_("The setting is reset when the configuration is changed")); 40 | warning_label.set_ellipsize(Pango.EllipsizeMode.END); 41 | warning_label.add_css_class("warning"); 42 | 43 | warning_box.append(warning_icon); 44 | warning_box.append(warning_label); 45 | header_bar.set_title_widget(warning_box); 46 | 47 | var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); 48 | main_box.append (header_bar); 49 | 50 | main_box.append (create_advanced_content ()); 51 | 52 | this.closed.connect(() => { 53 | // LoadStates.load_states_from_file.begin(app); 54 | }); 55 | 56 | this.set_child (main_box); 57 | this.present (parent); 58 | } 59 | 60 | Gtk.Widget create_advanced_content () { 61 | var scrolled_window = new Gtk.ScrolledWindow (); 62 | scrolled_window.set_hexpand (true); 63 | scrolled_window.set_vexpand (true); 64 | 65 | var clamp = new Adw.Clamp (); 66 | var group = new Adw.PreferencesGroup (); 67 | 68 | list_box = new ListBox (); 69 | list_box.set_selection_mode (SelectionMode.NONE); 70 | list_box.set_hexpand (true); 71 | list_box.set_margin_start (12); 72 | list_box.set_margin_end (12); 73 | list_box.set_margin_top (12); 74 | list_box.set_margin_bottom (12); 75 | list_box.add_css_class ("boxed-list"); 76 | 77 | var config_dir = File.new_for_path (Environment.get_home_dir ()).get_child (".config").get_child ("MangoHud"); 78 | config_file = config_dir.get_child ("MangoHud.conf"); 79 | 80 | if (config_file.query_exists ()) { 81 | try { 82 | var input_stream = config_file.read (); 83 | var data_stream = new DataInputStream (input_stream); 84 | string line; 85 | 86 | all_config_lines = new List (); 87 | filtered_config_lines = new List (); 88 | 89 | while ((line = data_stream.read_line ()) != null) { 90 | all_config_lines.append (line); 91 | 92 | if (!line.contains ("color") && 93 | !line.contains ("fps_limit_method=") && 94 | !line.contains ("media_player_format=") && 95 | !line.contains ("fps_value=")) { 96 | 97 | foreach (var prefix in allowed_prefixes) { 98 | if (line.has_prefix(prefix) || line.has_suffix ("#custom_command")) { 99 | filtered_config_lines.append (line); 100 | add_config_row (list_box, line); 101 | break; 102 | } 103 | } 104 | } 105 | } 106 | input_stream.close (); 107 | } catch (Error e) { 108 | print (_("Error when selecting a file: %s\n"), e.message); 109 | } 110 | } else { 111 | print (_("MangoHud.conf does not exist at: %s\n"), config_file.get_path ()); 112 | } 113 | 114 | clamp.set_child (list_box); 115 | group.add (clamp); 116 | scrolled_window.set_child (group); 117 | 118 | var overlay = new Gtk.Overlay (); 119 | overlay.set_child (scrolled_window); 120 | 121 | var add_space_button = new Gtk.Button.with_label (_("Add space")); 122 | add_space_button.add_css_class ("suggested-action"); 123 | add_space_button.set_halign (Gtk.Align.CENTER); 124 | add_space_button.set_valign (Gtk.Align.END); 125 | add_space_button.set_margin_bottom (16); 126 | add_space_button.clicked.connect (() => { 127 | var space_line = "custom_text= #space"; 128 | filtered_config_lines.append (space_line); 129 | add_config_row (list_box, space_line); 130 | save_config_to_file (list_box); 131 | }); 132 | 133 | overlay.add_overlay (add_space_button); 134 | 135 | return overlay; 136 | } 137 | 138 | public void add_config_row (ListBox list_box, string line) { 139 | var action_row = new Adw.ActionRow (); 140 | 141 | string key = line.split ("=")[0]; 142 | if (line.strip() == "custom_text= #space") { 143 | action_row.title = "space"; 144 | } else { 145 | action_row.title = get_localized_title (key); 146 | } 147 | action_row.subtitle = line; 148 | 149 | var drag_icon = new Gtk.Image.from_icon_name("list-drag-handle-symbolic"); 150 | drag_icon.add_css_class("dim-label"); 151 | action_row.add_prefix (drag_icon); 152 | 153 | var up_button = new Gtk.Button (); 154 | up_button.icon_name = "go-up-symbolic"; 155 | up_button.add_css_class("circular"); 156 | up_button.set_valign (Align.CENTER); 157 | up_button.has_frame = false; 158 | up_button.clicked.connect (() => { 159 | move_row_up (list_box, action_row); 160 | save_config_to_file (list_box); 161 | }); 162 | action_row.add_suffix (up_button); 163 | 164 | var down_button = new Gtk.Button (); 165 | down_button.icon_name = "go-down-symbolic"; 166 | down_button.add_css_class("circular"); 167 | down_button.set_valign (Align.CENTER); 168 | down_button.has_frame = false; 169 | down_button.clicked.connect (() => { 170 | move_row_down (list_box, action_row); 171 | save_config_to_file (list_box); 172 | }); 173 | action_row.add_suffix (down_button); 174 | 175 | var delete_button = new Gtk.Button (); 176 | delete_button.icon_name = "user-trash-symbolic"; 177 | delete_button.add_css_class ("circular"); 178 | delete_button.set_valign (Align.CENTER); 179 | delete_button.has_frame = false; 180 | delete_button.clicked.connect (() => { 181 | list_box.remove (action_row); 182 | save_config_to_file (list_box); 183 | }); 184 | action_row.add_suffix (delete_button); 185 | 186 | enable_drag_and_drop (action_row, list_box, action_row); 187 | 188 | list_box.append (action_row); 189 | } 190 | 191 | void enable_drag_and_drop(Gtk.Widget widget, ListBox list_box, ListBoxRow row) { 192 | var drag_source = new Gtk.DragSource(); 193 | drag_source.set_actions(Gdk.DragAction.MOVE); 194 | 195 | drag_source.drag_begin.connect((source, drag) => { 196 | row.add_css_class("card"); 197 | var paintable = new Gtk.WidgetPaintable(row); 198 | drag_source.set_icon(paintable, 0, 0); 199 | }); 200 | 201 | drag_source.drag_end.connect((source, drag) => { 202 | row.remove_css_class("card"); 203 | clear_drop_highlight(list_box); 204 | }); 205 | 206 | drag_source.prepare.connect((source, x, y) => { 207 | Value value = Value(typeof(ListBoxRow)); 208 | value.set_object(row); 209 | return new Gdk.ContentProvider.for_value(value); 210 | }); 211 | 212 | widget.add_controller(drag_source); 213 | 214 | var drop_target = new Gtk.DropTarget(typeof(ListBoxRow), Gdk.DragAction.MOVE); 215 | 216 | drop_target.drop.connect((target, value, x, y) => { 217 | var source_row = value.get_object() as ListBoxRow; 218 | var dest_row = list_box.get_row_at_y((int)y); 219 | 220 | if (source_row == null || dest_row == null || source_row == dest_row) { 221 | return false; 222 | } 223 | 224 | var scrolled_window = get_scrolled_parent(list_box); 225 | double scroll_position = 0; 226 | if (scrolled_window != null) { 227 | scroll_position = get_scroll_position(scrolled_window); 228 | } 229 | 230 | int source_index = get_row_index(list_box, source_row); 231 | int dest_index = get_row_index(list_box, dest_row); 232 | 233 | if (source_index < dest_index) { 234 | dest_index++; 235 | } 236 | 237 | list_box.remove(source_row); 238 | list_box.insert(source_row, dest_index); 239 | save_config_to_file(list_box); 240 | 241 | if (scrolled_window != null) { 242 | restore_scroll_position(scrolled_window, scroll_position); 243 | } 244 | 245 | return true; 246 | }); 247 | 248 | drop_target.enter.connect((target, x, y) => { 249 | update_drop_highlight(list_box, (int)y); 250 | return Gdk.DragAction.MOVE; 251 | }); 252 | 253 | drop_target.motion.connect((target, x, y) => { 254 | update_drop_highlight(list_box, (int)y); 255 | return Gdk.DragAction.MOVE; 256 | }); 257 | 258 | drop_target.leave.connect((target) => { 259 | clear_drop_highlight(list_box); 260 | }); 261 | 262 | list_box.add_controller(drop_target); 263 | } 264 | 265 | private void update_drop_highlight (ListBox list_box, int y) { 266 | clear_drop_highlight (list_box); 267 | 268 | var dest_row = list_box.get_row_at_y (y); 269 | if (dest_row != null) { 270 | drop_indicator_row = dest_row; 271 | drop_indicator_row.add_css_class ("row-drop-indicator"); 272 | } 273 | } 274 | 275 | private void clear_drop_highlight (ListBox list_box) { 276 | if (drop_indicator_row != null) { 277 | drop_indicator_row.remove_css_class ("row-drop-indicator"); 278 | drop_indicator_row = null; 279 | } 280 | } 281 | 282 | int get_row_index (ListBox list_box, ListBoxRow row) { 283 | int index = 0; 284 | var child = list_box.get_first_child (); 285 | while (child != null) { 286 | if (child == row) return index; 287 | index++; 288 | child = child.get_next_sibling (); 289 | } 290 | return -1; 291 | } 292 | 293 | int get_row_count (ListBox list_box) { 294 | int count = 0; 295 | var child = list_box.get_first_child (); 296 | while (child != null) { 297 | count++; 298 | child = child.get_next_sibling (); 299 | } 300 | return count; 301 | } 302 | 303 | void move_row_up (ListBox list_box, ListBoxRow row) { 304 | var scrolled_window = get_scrolled_parent(list_box); 305 | if (scrolled_window != null) { 306 | double vpos = get_scroll_position(scrolled_window); 307 | 308 | int index = get_row_index (list_box, row); 309 | if (index > 0) { 310 | list_box.remove (row); 311 | list_box.insert (row, index - 1); 312 | } 313 | 314 | restore_scroll_position(scrolled_window, vpos); 315 | } 316 | } 317 | 318 | void move_row_down (ListBox list_box, ListBoxRow row) { 319 | var scrolled_window = get_scrolled_parent(list_box); 320 | if (scrolled_window != null) { 321 | double vpos = get_scroll_position(scrolled_window); 322 | 323 | int index = get_row_index (list_box, row); 324 | if (index < get_row_count (list_box) - 1) { 325 | list_box.remove (row); 326 | list_box.insert (row, index + 1); 327 | } 328 | 329 | restore_scroll_position(scrolled_window, vpos); 330 | } 331 | } 332 | 333 | Gtk.ScrolledWindow? get_scrolled_parent(Gtk.Widget widget) { 334 | Gtk.Widget? parent = widget.get_parent(); 335 | while (parent != null) { 336 | if (parent is Gtk.ScrolledWindow) { 337 | return parent as Gtk.ScrolledWindow; 338 | } 339 | parent = parent.get_parent(); 340 | } 341 | return null; 342 | } 343 | 344 | double get_scroll_position(Gtk.ScrolledWindow scrolled_window) { 345 | var vadjustment = scrolled_window.get_vadjustment(); 346 | return vadjustment.get_value(); 347 | } 348 | 349 | void restore_scroll_position(Gtk.ScrolledWindow scrolled_window, double value) { 350 | var vadjustment = scrolled_window.get_vadjustment(); 351 | Idle.add(() => { 352 | vadjustment.set_value(value); 353 | return false; 354 | }); 355 | } 356 | 357 | void save_config_to_file (ListBox list_box) { 358 | try { 359 | var output_stream = config_file.replace ( 360 | null, 361 | false, 362 | FileCreateFlags.NONE, 363 | null 364 | ); 365 | var data_stream = new DataOutputStream (output_stream); 366 | 367 | data_stream.put_string ("# PRO MangoJuice #\n"); 368 | 369 | var child = list_box.get_first_child (); 370 | while (child != null) { 371 | var action_row = child as Adw.ActionRow; 372 | if (action_row != null) { 373 | data_stream.put_string (action_row.subtitle + "\n", null); 374 | } 375 | child = child.get_next_sibling (); 376 | } 377 | 378 | foreach (var config_line in all_config_lines) { 379 | if (filtered_config_lines.find_custom (config_line, strcmp) == null) { 380 | data_stream.put_string (config_line + "\n", null); 381 | } 382 | } 383 | 384 | output_stream.close (); 385 | } catch (Error e) { 386 | print (_("Error writing to the file: %s\n"), e.message); 387 | } 388 | } 389 | 390 | string get_localized_title (string key) { 391 | switch (key) { 392 | case "custom_text_center": 393 | return _("Your text"); 394 | case "custom_text": 395 | return _("Your text"); 396 | case "gpu_stats": 397 | return _("Load GPU"); 398 | case "vulkan_driver": 399 | return _("Vulkan Driver"); 400 | case "vram": 401 | return _("VRAM"); 402 | case "cpu_stats": 403 | return _("Load CPU"); 404 | case "core_load": 405 | return _("Load per core"); 406 | case "ram": 407 | return _("RAM"); 408 | case "io_read": 409 | return _("Disk"); 410 | case "io_write": 411 | return _("Disk"); 412 | case "procmem": 413 | return _("Resident memory"); 414 | case "swap": 415 | return _("Swap"); 416 | case "fan": 417 | return _("Fan"); 418 | case "fps": 419 | return _("FPS"); 420 | case "fps_metrics=avg,0.01": 421 | return _("Lowest 0.1%"); 422 | case "fps_metrics=avg,0.1": 423 | return _("Lowest 1%"); 424 | case "version": 425 | return _("Version"); 426 | case "engine_version": 427 | return _("Engine version"); 428 | case "gamemode": 429 | return _("Gamemode"); 430 | case "vkbasalt": 431 | return _("vkBasalt"); 432 | case "exec_name": 433 | return _("Exe name"); 434 | case "fsr": 435 | return _("FSR"); 436 | case "hdr": 437 | return _("HDR"); 438 | case "refresh_rate": 439 | return _("Refresh rate"); 440 | case "resolution": 441 | return _("Resolution"); 442 | case "arch": 443 | return _("Architecture"); 444 | case "present_mode": 445 | return _("VPS"); 446 | case "display_server": 447 | return _("Session type"); 448 | case "show_fps_limit": 449 | return _("Frame limit"); 450 | case "frame_timing": 451 | return _("Frame graph"); 452 | case "frame_count": 453 | return _("Frame"); 454 | case "battery": 455 | return _("Battery charge"); 456 | case "battery_watt": 457 | return _("Battery power"); 458 | case "battery_time": 459 | return _("Time remain"); 460 | case "device_battery_icon": 461 | return _("Battery icon"); 462 | case "device_battery=gamepad,mouse": 463 | return _("Other batteries"); 464 | case "network": 465 | return _("Network"); 466 | case "media_player": 467 | return _("Media"); 468 | case "wine": 469 | return _("Version"); 470 | case "winesync": 471 | return _("Winesync"); 472 | default: 473 | return _("Other"); 474 | } 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /src/config.vapi: -------------------------------------------------------------------------------- 1 | /* config.vapi // Licence: GPL-v3.0 */ 2 | [CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")] 3 | namespace Config { 4 | public const string GETTEXT_PACKAGE; 5 | public const string GNOMELOCALEDIR; 6 | public const string VERSION; 7 | public const bool IS_DEVEL; 8 | } -------------------------------------------------------------------------------- /src/dialog.vala: -------------------------------------------------------------------------------- 1 | /* dialog // Licence: GPL-v3.0 */ 2 | 3 | using Gtk; 4 | using Adw; 5 | 6 | namespace AboutDialog { 7 | 8 | public void show_about_dialog (Gtk.Window parent_window) { 9 | 10 | const string[] developers = { 11 | "Radiolamp https://github.com/radiolamp", 12 | "Rirusha https://rirusha.space", 13 | "Boria138 https://github.com/Boria138", 14 | "SpikedPaladin https://github.com/SpikedPaladin", 15 | "slserg https://github.com/slserg", 16 | "Samueru-sama https://github.com/Samueru-sama", 17 | "x1z53 https://gitverse.ru/x1z53" 18 | }; 19 | 20 | var dialog = new Adw.AboutDialog.from_appdata ( 21 | "/io/github/radiolamp/mangojuice/io.github.radiolamp.mangojuice.metainfo.xml", 22 | null 23 | ); 24 | 25 | dialog.application_icon = "io.github.radiolamp.mangojuice"; 26 | dialog.version = Config.VERSION; 27 | dialog.translator_credits = _("translator-credits"); 28 | dialog.set_developers(developers); 29 | dialog.add_link (_("Financial support") + " (Donation Alerts)", "https://www.donationalerts.com/r/radiolamp"); 30 | dialog.add_link (_("Financial support") + " (Tinkoff)", "https://www.tbank.ru/cf/3PPTstulqEq"); 31 | dialog.add_link (_("Financial support") + " (Boosty)", "https://boosty.to/radiolamp"); 32 | 33 | dialog.present (parent_window); 34 | } 35 | 36 | int profile_count = 0; 37 | 38 | delegate void DeleteCallback(); 39 | 40 | void update_group_state(Adw.PreferencesGroup group, Adw.StatusPage status_page) { 41 | if (profile_count == 0 && status_page.get_parent() == null) { 42 | group.add(status_page); 43 | } else if (profile_count > 0 && status_page.get_parent() != null) { 44 | group.remove(status_page); 45 | } 46 | } 47 | 48 | public void preset_dialog(Gtk.Window parent_window, MangoJuice app) { 49 | var dialog = new Adw.Dialog(); 50 | dialog.set_content_width(800); 51 | dialog.set_content_height(600); 52 | dialog.set_size_request(320, 240); 53 | 54 | var breakpoint_450px = new Adw.Breakpoint(Adw.BreakpointCondition.parse("max-width: 450px")); 55 | dialog.add_breakpoint(breakpoint_450px); 56 | 57 | var toast_overlay = new Adw.ToastOverlay(); 58 | dialog.set_child(toast_overlay); 59 | 60 | var main_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); 61 | toast_overlay.set_child(main_box); 62 | 63 | var window_handle = new Gtk.WindowHandle(); 64 | main_box.append(window_handle); 65 | 66 | var header_bar = new Adw.HeaderBar(); 67 | header_bar.set_show_start_title_buttons(true); 68 | header_bar.set_show_end_title_buttons(true); 69 | header_bar.add_css_class("flat"); 70 | main_box.append(header_bar); 71 | 72 | var info_button = new Gtk.Button(); 73 | info_button.set_icon_name("dialog-information-symbolic"); 74 | info_button.add_css_class("circular"); 75 | info_button.set_tooltip_text(_("To get ready presets, click on the title.")); 76 | header_bar.pack_start(info_button); 77 | 78 | var presets_button = new Gtk.Button(); 79 | presets_button.set_hexpand(true); 80 | presets_button.add_css_class("flat"); 81 | var button_content = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6); 82 | button_content.set_halign(Gtk.Align.CENTER); 83 | var presets_label = new Gtk.Label(_("Presets")); 84 | var arrow_icon = new Gtk.Image.from_icon_name("go-next-symbolic"); 85 | button_content.append(presets_label); 86 | button_content.append(arrow_icon); 87 | presets_button.set_child(button_content); 88 | header_bar.set_title_widget(presets_button); 89 | 90 | var content_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); 91 | content_box.set_hexpand(true); 92 | content_box.set_vexpand(true); 93 | main_box.append(content_box); 94 | 95 | var clamp = new Adw.Clamp() { 96 | maximum_size = 800, 97 | tightening_threshold = 450, 98 | margin_top = 12, 99 | margin_bottom = 12 100 | }; 101 | content_box.append(clamp); 102 | 103 | var clamped_content = new Gtk.Box(Gtk.Orientation.VERTICAL, 12); 104 | clamped_content.set_margin_start(24); 105 | clamped_content.set_margin_end(24); 106 | clamp.set_child(clamped_content); 107 | 108 | var group = new Adw.PreferencesGroup(); 109 | var status_page = new Adw.StatusPage() { 110 | title = _("No profiles yet"), 111 | icon_name = "emoji-symbols-symbolic" 112 | }; 113 | status_page.add_css_class("dim-label"); 114 | status_page.set_vexpand(true); 115 | 116 | var add_button = new Gtk.Button.with_label(_("Add Profile")); 117 | add_button.set_size_request(-1, 40); 118 | 119 | add_button.clicked.connect(() => { 120 | var row = add_option_button(group, app, toast_overlay, () => { 121 | profile_count--; 122 | update_group_state(group, status_page); 123 | }); 124 | group.add(row); 125 | profile_count++; 126 | update_group_state(group, status_page); 127 | }); 128 | 129 | try { 130 | var config_dir = File.new_for_path(Environment.get_home_dir()) 131 | .get_child(".config") 132 | .get_child("MangoHud"); 133 | 134 | if (config_dir.query_exists()) { 135 | var enumerator = config_dir.enumerate_children(FileAttribute.STANDARD_NAME, 0); 136 | FileInfo info; 137 | var profiles = new GLib.List(); 138 | 139 | while ((info = enumerator.next_file()) != null) { 140 | string name = info.get_name(); 141 | if (name.has_suffix(".conf") && name != "MangoHud.conf" && name != ".MangoHud.backup") { 142 | string profile_name = name[0:-5].replace("-", " "); 143 | profiles.append(profile_name); 144 | } 145 | } 146 | 147 | profiles.sort((a, b) => { 148 | return a.collate(b); 149 | }); 150 | 151 | foreach (string profile_name in profiles) { 152 | var row = add_option_button(group, app, toast_overlay, () => { 153 | profile_count--; 154 | update_group_state(group, status_page); 155 | }, profile_name, true); 156 | group.add(row); 157 | profile_count++; 158 | } 159 | } 160 | } catch (Error e) { 161 | warning("Error loading profiles: %s", e.message); 162 | } 163 | 164 | update_group_state(group, status_page); 165 | 166 | var scrolled = new Gtk.ScrolledWindow(); 167 | scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); 168 | 169 | var profile_container = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); 170 | profile_container.set_margin_top(2); 171 | profile_container.set_margin_bottom(2); 172 | profile_container.set_margin_start(2); 173 | profile_container.set_margin_end(2); 174 | profile_container.append(group); 175 | 176 | scrolled.set_child(profile_container); 177 | scrolled.set_vexpand(true); 178 | clamped_content.append(scrolled); 179 | clamped_content.append(add_button); 180 | 181 | presets_button.clicked.connect(() => { 182 | show_presets_carousel_dialog((Gtk.Window)dialog, app); 183 | }); 184 | 185 | dialog.closed.connect(() => { 186 | try { 187 | Process.spawn_command_line_async("pkill vkcube"); 188 | Process.spawn_command_line_async("pkill glxgears"); 189 | } catch (Error e) { 190 | stderr.printf(_("Error when executing the command: %s\n"), e.message); 191 | } 192 | }); 193 | 194 | dialog.present(parent_window); 195 | } 196 | 197 | Adw.ActionRow add_option_button(Adw.PreferencesGroup group, MangoJuice app, Adw.ToastOverlay toast_overlay, owned DeleteCallback on_delete, string initial_name = _("Profile"), bool is_existing_profile = false) { 198 | string profile_name = initial_name; 199 | 200 | if (!is_existing_profile) { 201 | if (profile_name.has_suffix(".exe")) { 202 | profile_name = "wine-" + profile_name.substring(0, profile_name.length - 4); 203 | } else if (initial_name == _("Profile")) { 204 | profile_name = generate_unique_profile_name(initial_name); 205 | } 206 | create_profile_config(profile_name); 207 | } 208 | 209 | var row = new Adw.ActionRow(); 210 | row.set_title(profile_name); 211 | row.set_activatable(true); 212 | row.set_selectable(false); 213 | row.set_tooltip_text(_("Profile preview")); 214 | 215 | var edit_btn = new Gtk.Button(); 216 | edit_btn.set_icon_name("document-edit-symbolic"); 217 | edit_btn.set_focusable(false); 218 | edit_btn.add_css_class("flat"); 219 | edit_btn.add_css_class("circular"); 220 | edit_btn.set_tooltip_text(_("Renaming. Name the name of the game, or name.exe for Wine games, e.g. DOOM.exe. Attention case is important!")); 221 | edit_btn.set_valign(Gtk.Align.CENTER); 222 | 223 | var reset_btn = new Gtk.Button(); 224 | reset_btn.set_icon_name("view-refresh-symbolic"); 225 | reset_btn.set_focusable(false); 226 | reset_btn.add_css_class("flat"); 227 | reset_btn.add_css_class("circular"); 228 | reset_btn.set_tooltip_text(_("Overwrite profile")); 229 | reset_btn.set_valign(Gtk.Align.CENTER); 230 | 231 | var close_btn = new Gtk.Button(); 232 | close_btn.set_icon_name("edit-delete-symbolic"); 233 | close_btn.set_focusable(false); 234 | close_btn.add_css_class("flat"); 235 | close_btn.set_tooltip_text(_("Delete profile")); 236 | close_btn.set_valign(Gtk.Align.CENTER); 237 | close_btn.add_css_class("circular"); 238 | 239 | var button_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 2); 240 | button_box.append(edit_btn); 241 | button_box.append(reset_btn); 242 | button_box.append(close_btn); 243 | row.add_suffix(button_box); 244 | 245 | var entry = new Gtk.Entry(); 246 | entry.set_text(profile_name); 247 | entry.set_visible(false); 248 | entry.set_hexpand(true); 249 | entry.set_valign(Gtk.Align.CENTER); 250 | row.add_prefix(entry); 251 | 252 | edit_btn.clicked.connect(() => { 253 | entry.set_text(profile_name); 254 | row.set_title(""); 255 | entry.set_visible(true); 256 | entry.grab_focus(); 257 | }); 258 | 259 | reset_btn.clicked.connect(() => { 260 | try { 261 | var config_dir = File.new_for_path(Environment.get_home_dir()) 262 | .get_child(".config") 263 | .get_child("MangoHud"); 264 | 265 | var original_config = config_dir.get_child("MangoHud.conf"); 266 | var profile_config = config_dir.get_child( 267 | profile_name.replace(" ", "-") + ".conf" 268 | ); 269 | 270 | if (original_config.query_exists()) { 271 | original_config.copy(profile_config, FileCopyFlags.OVERWRITE); 272 | } 273 | 274 | var toast = new Adw.Toast(_("Changed")); 275 | toast.set_timeout(3); 276 | toast_overlay.add_toast(toast); 277 | } catch (Error e) { 278 | warning("Failed to reset profile: %s", e.message); 279 | } 280 | }); 281 | 282 | entry.activate.connect(() => { 283 | string new_name = entry.get_text().strip(); 284 | 285 | if (new_name.has_suffix(".exe")) { 286 | new_name = "wine " + new_name.substring(0, new_name.length - 4); 287 | } 288 | 289 | if (new_name != "" && new_name != profile_name) { 290 | rename_profile_config(profile_name, new_name); 291 | profile_name = new_name; 292 | } 293 | 294 | entry.set_visible(false); 295 | row.set_title(profile_name); 296 | }); 297 | 298 | var play_btn = new Gtk.Button.from_icon_name("media-playback-start-symbolic"); 299 | play_btn.add_css_class("flat"); 300 | play_btn.set_tooltip_text(_("Apply the profile to the entire system")); 301 | play_btn.set_valign(Gtk.Align.CENTER); 302 | play_btn.add_css_class("circular"); 303 | play_btn.clicked.connect(() => { 304 | app.run_test(); 305 | apply_profile_config(profile_name); 306 | LoadStates.load_states_from_file.begin(app); 307 | app.reset_manager.reset_all_widgets(); 308 | 309 | var toast = new Adw.Toast(_("Applied")); 310 | toast.set_timeout(3); 311 | toast_overlay.add_toast(toast); 312 | }); 313 | row.add_prefix(play_btn); 314 | 315 | var focus_controller = new Gtk.EventControllerFocus(); 316 | focus_controller.leave.connect(() => { 317 | if (entry.get_visible()) { 318 | entry.activate(); 319 | } 320 | }); 321 | entry.add_controller(focus_controller); 322 | 323 | close_btn.clicked.connect(() => { 324 | delete_profile_config(profile_name); 325 | group.remove(row); 326 | on_delete(); 327 | var toast = new Adw.Toast(_("Deleted")); 328 | toast.set_timeout(3); 329 | toast_overlay.add_toast(toast); 330 | }); 331 | 332 | string? wayland_display = Environment.get_variable("WAYLAND_DISPLAY"); 333 | bool is_wayland = (wayland_display != null && wayland_display != ""); 334 | 335 | row.activated.connect(() => { 336 | try { 337 | Process.spawn_command_line_async("pkill vkcube"); 338 | Process.spawn_command_line_async("pkill glxgears"); 339 | 340 | string config_path = Path.build_filename( 341 | Environment.get_home_dir(), 342 | ".config", 343 | "MangoHud", 344 | profile_name.replace(" ", "-") + ".conf" 345 | ); 346 | 347 | string base_cmd = @"env MANGOHUD_CONFIGFILE='$config_path' mangohud"; 348 | 349 | if (app.is_flatpak ()) { 350 | Process.spawn_command_line_sync ("pkill vkcube"); 351 | if (is_wayland) { 352 | Process.spawn_command_line_async (base_cmd + " mangohud vkcube-wayland"); 353 | } else { 354 | Process.spawn_command_line_async (base_cmd + " mangohud vkcube"); 355 | } 356 | } else if (app.is_vkcube_available ()) { 357 | Process.spawn_command_line_sync ("pkill vkcube"); 358 | Process.spawn_command_line_async (base_cmd + " mangohud vkcube"); 359 | } else if (app.is_glxgears_available ()) { 360 | Process.spawn_command_line_sync ("pkill glxgears"); 361 | Process.spawn_command_line_async (base_cmd + " mangohud glxgears"); 362 | } 363 | } catch (Error e) { 364 | warning("%s", e.message); 365 | } 366 | }); 367 | 368 | return row; 369 | } 370 | 371 | void show_presets_carousel_dialog(Gtk.Window parent_dialog, MangoJuice app) { 372 | var dialog = new Adw.Dialog(); 373 | dialog.set_content_width(800); 374 | dialog.set_content_height(600); 375 | dialog.set_size_request(320, 240); 376 | 377 | var breakpoint_mobile = new Adw.Breakpoint(Adw.BreakpointCondition.parse("max-width: 450px")); 378 | var breakpoint_desktop = new Adw.Breakpoint(Adw.BreakpointCondition.parse("min-width: 451px")); 379 | dialog.add_breakpoint(breakpoint_mobile); 380 | dialog.add_breakpoint(breakpoint_desktop); 381 | 382 | var main_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); 383 | dialog.set_child(main_box); 384 | 385 | var header = new Adw.HeaderBar(); 386 | header.set_show_start_title_buttons(true); 387 | header.set_show_end_title_buttons(true); 388 | header.add_css_class("flat"); 389 | main_box.append(header); 390 | 391 | var header_center_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6); 392 | header_center_box.set_halign(Gtk.Align.CENTER); 393 | header_center_box.set_valign(Gtk.Align.CENTER); 394 | header_center_box.set_hexpand(true); 395 | header_center_box.set_vexpand(false); 396 | header.set_title_widget(header_center_box); 397 | 398 | var carousel = new Adw.Carousel(); 399 | carousel.set_hexpand(true); 400 | carousel.set_vexpand(true); 401 | 402 | var header_indicators = new Adw.CarouselIndicatorDots(); 403 | header_indicators.set_carousel(carousel); 404 | header_indicators.set_valign(Gtk.Align.CENTER); 405 | header_indicators.set_halign(Gtk.Align.CENTER); 406 | 407 | header_center_box.append(header_indicators); 408 | 409 | var restore_button = new Gtk.Button.with_label(_("Restore")); 410 | ((Gtk.Label)restore_button.get_child()).set_ellipsize(Pango.EllipsizeMode.END); 411 | restore_button.clicked.connect(() => { 412 | try { 413 | var backup_file = File.new_for_path(Environment.get_home_dir()) 414 | .get_child(".config") 415 | .get_child("MangoHud") 416 | .get_child(".MangoHud.backup"); 417 | app.restore_config_from_file(backup_file.get_path()); 418 | backup_file.delete(); 419 | } catch (Error e) { 420 | stderr.printf("Error: %s\n", e.message); 421 | } 422 | }); 423 | header.pack_start(restore_button); 424 | 425 | var apply_button = new Gtk.Button.with_label(_("Apply")); 426 | ((Gtk.Label)apply_button.get_child()).set_ellipsize(Pango.EllipsizeMode.END); 427 | apply_button.add_css_class("suggested-action"); 428 | apply_button.clicked.connect(() => { 429 | apply_preset(carousel, app); 430 | }); 431 | header.pack_end(apply_button); 432 | 433 | var content_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 12); 434 | content_box.set_margin_top(12); 435 | content_box.set_margin_bottom(12); 436 | content_box.set_margin_start(12); 437 | content_box.set_margin_end(12); 438 | content_box.set_hexpand(true); 439 | content_box.set_vexpand(true); 440 | main_box.append(content_box); 441 | 442 | content_box.append(carousel); 443 | 444 | var bottom_indicators = new Adw.CarouselIndicatorDots(); 445 | bottom_indicators.set_carousel(carousel); 446 | bottom_indicators.set_valign(Gtk.Align.CENTER); 447 | bottom_indicators.set_halign(Gtk.Align.CENTER); 448 | bottom_indicators.set_margin_top(10); 449 | bottom_indicators.set_visible(false); 450 | content_box.append(bottom_indicators); 451 | 452 | breakpoint_mobile.add_setter(header_indicators, "visible", false); 453 | breakpoint_mobile.add_setter(bottom_indicators, "visible", true); 454 | breakpoint_desktop.add_setter(header_indicators, "visible", true); 455 | breakpoint_desktop.add_setter(bottom_indicators, "visible", false); 456 | 457 | string[] titles = { _("Minimal"), _("Default"), _("Advanced"), _("Full"), _("Only FPS") }; 458 | string[] icons = { "minimal", "default", "advanced", "full", "fps" }; 459 | 460 | for (int i = 0; i < titles.length; i++) { 461 | var page = new Gtk.Box(Gtk.Orientation.VERTICAL, 6); 462 | page.set_hexpand(true); 463 | page.set_vexpand(true); 464 | 465 | var center = new Gtk.Box(Gtk.Orientation.VERTICAL, 6); 466 | center.set_hexpand(true); 467 | center.set_vexpand(true); 468 | 469 | var picture = new Gtk.Picture(); 470 | picture.set_resource("/io/github/radiolamp/mangojuice/images/" + icons[i] + ".png"); 471 | picture.set_hexpand(true); 472 | picture.set_vexpand(true); 473 | picture.set_can_shrink(false); 474 | 475 | var label = new Gtk.Label(titles[i]); 476 | label.add_css_class("title-2"); 477 | label.set_margin_top(10); 478 | label.set_margin_bottom(10); 479 | 480 | center.append(picture); 481 | center.append(label); 482 | page.append(center); 483 | 484 | carousel.append(page); 485 | } 486 | 487 | dialog.present(parent_dialog); 488 | } 489 | 490 | 491 | void apply_preset(Adw.Carousel carousel, MangoJuice app) { 492 | int current_page = (int)carousel.get_position(); 493 | string[] page_names = { _("Minimal"), _("Default"), _("Advanced"), _("Full"), _("Only FPS") }; 494 | stdout.printf(_("Applying %s layout\n"), page_names[current_page]); 495 | 496 | switch (current_page) { 497 | case 0: 498 | string[] profile1_vars = { "background_alpha=0.4", "round_corners=0", "background_color=000000", "text_color=FFFFFF", "position=top-left", 499 | "table_columns=2", "cpu_stats", "gpu_stats", "ram", "vram", "hud_compact", "fps", "hud_no_margin" }; 500 | set_preset(profile1_vars); 501 | break; 502 | case 1: 503 | string[] profile2_vars = { "background_alpha=0.4", "round_corners=10", "cpu_stats", "gpu_load_change", "gpu_load_value=30,60", 504 | "gpu_load_color=FFFFFF,FFAA7F,CC0000", "gpu_temp", "gpu_fan", "gpu_power", "gpu_stats", "cpu_load_change", "cpu_load_value=30,60", 505 | "cpu_load_color=FFFFFF,FFAA7F,CC0000", "cpu_mhz", "cpu_temp", "cpu_color=2E97CB", "vram", "vram_color=AD64C1", "ram", "ram_color=C26693", 506 | "wine_color=EB4B4B", "fps", "frametime_color=00e4ff", "toggle_fps_limit=Shift_L+F1", "fps_limit=0,30,60", "fps_color_change", 507 | "fps_color=ff0000,FDFD09,ffffff", "fps_value=30,60", "engine_short_names", "frame_timing" }; 508 | set_preset(profile2_vars); 509 | break; 510 | case 2: 511 | string[] profile3_vars = { "background_alpha=0.4", "round_corners=10", "gpu_text=GPU", "gpu_stats", "gpu_load_change", "gpu_load_value=30,60", 512 | "gpu_load_color=ffffff,ffaa7f,cc0000", "gpu_temp", "gpu_fan", "gpu_power", "gpu_color=2E9762", "cpu_text=CPU", "cpu_stats", "cpu_load_change", 513 | "cpu_load_value=30,60", "cpu_load_color=ffffff,ffaa7f,cc0000", "cpu_mhz", "cpu_temp", "cpu_color=2E97CB", "vram", "vram_color=AD64C1", "ram", 514 | "ram_color=C26693", "fps", "engine_short_names", "gpu_name", "wine", "wine_color=eb4b4b", "frame_timing", "frametime_color=00edff", 515 | "toggle_fps_limit=Shift_L+F1", "show_fps_limit", "fps_limit=0,30,60", "resolution", "fsr", "hdr", "refresh_rate", "fps_color_change", 516 | "fps_color=ff0000,fdfd09,ffffff", "fps_value=30,60", "media_player", "media_player_color=FFFFFF" }; 517 | set_preset(profile3_vars); 518 | break; 519 | case 3: 520 | string[] profile4_vars = { "background_alpha=0.4", "round_corners=10", "table_columns=4", "gpu_stats", "gpu_load_change", "gpu_load_value=30,60", 521 | "gpu_load_color=FFFFFF,FFAA7F,CC0000", "gpu_temp", "gpu_fan", "gpu_power", "gpu_color=2E9762", "cpu_stats", "cpu_load_change", "cpu_load_value=30,60", 522 | "cpu_load_color=FFFFFF,FFAA7F,CC0000", "cpu_mhz", "cpu_temp", "cpu_color=2E97CB", "vram", "vram_color=AD64C1", "ram", "procmem", "io_read", "io_write", 523 | "ram_color=C26693", "fps", "engine_short_names", "gpu_name", "wine_color=EB4B4B", "frame_timing", "frame_count", "frametime_color=00FF00", "show_fps_limit", 524 | "fps_limit=0,120,240", "resolution", "fsr", "hdr", "refresh_rate", "fps_color_change", "fps_color=ff0000,FDFD09,ffffff", "fps_value=80,140", "media_player", 525 | "media_player_color=FFFFFF", "cpu_power", "gpu_junction_temp", "gpu_core_clock", "gpu_mem_temp", "gpu_mem_clock", "gpu_voltage", "procmem_shared", "procmem_virt", 526 | "battery", "battery_icon", "device_battery_icon", "battery_watt", "battery_time", "exec_name", "throttling_status_graph", "arch", "full" }; 527 | set_preset(profile4_vars); 528 | break; 529 | case 4: 530 | string[] profile5_vars = { "fps_only", "background_alpha=0" }; 531 | set_preset(profile5_vars); 532 | break; 533 | } 534 | LoadStates.load_states_from_file.begin(app); 535 | app.reset_manager.reset_all_widgets(); 536 | } 537 | 538 | string generate_unique_profile_name(string base_name) { 539 | string name = base_name; 540 | int counter = 1; 541 | 542 | while (true) { 543 | string config_path = Path.build_filename( 544 | Environment.get_home_dir(), 545 | ".config", 546 | "MangoHud", 547 | name.replace(" ", "-") + ".conf" 548 | ); 549 | 550 | if (!File.new_for_path(config_path).query_exists()) { 551 | break; 552 | } 553 | 554 | name = @"$base_name $counter"; 555 | counter++; 556 | } 557 | 558 | return name; 559 | } 560 | 561 | void create_profile_config(string profile_name) { 562 | try { 563 | var config_dir = File.new_for_path(Environment.get_home_dir()) 564 | .get_child(".config") 565 | .get_child("MangoHud"); 566 | 567 | var original_config = config_dir.get_child("MangoHud.conf"); 568 | var new_config = config_dir.get_child( 569 | profile_name.replace(" ", "-") + ".conf" 570 | ); 571 | if (original_config.query_exists()) { 572 | original_config.copy(new_config, FileCopyFlags.OVERWRITE); 573 | } 574 | } catch (Error e) { 575 | warning("Failed to create profile: %s", e.message); 576 | } 577 | } 578 | 579 | void rename_profile_config(string old_name, string new_name) { 580 | try { 581 | string old_path = Path.build_filename( 582 | Environment.get_home_dir(), 583 | ".config", 584 | "MangoHud", 585 | old_name.replace(" ", "-") + ".conf" 586 | ); 587 | string new_path = Path.build_filename( 588 | Environment.get_home_dir(), 589 | ".config", 590 | "MangoHud", 591 | new_name.replace(" ", "-") + ".conf" 592 | ); 593 | 594 | var old_file = File.new_for_path(old_path); 595 | if (old_file.query_exists()) { 596 | old_file.move(File.new_for_path(new_path), FileCopyFlags.OVERWRITE); 597 | } 598 | } catch (Error e) { 599 | warning("Failed to rename profile: %s", e.message); 600 | } 601 | } 602 | 603 | void delete_profile_config(string profile_name) { 604 | try { 605 | string path = Path.build_filename( 606 | Environment.get_home_dir(), 607 | ".config", 608 | "MangoHud", 609 | profile_name.replace(" ", "-") + ".conf" 610 | ); 611 | 612 | var file = File.new_for_path(path); 613 | if (file.query_exists()) { 614 | file.delete(); 615 | } 616 | } catch (Error e) { 617 | warning("Failed to delete profile: %s", e.message); 618 | } 619 | } 620 | 621 | void apply_profile_config(string profile_name) { 622 | try { 623 | string profile_path = Path.build_filename( 624 | Environment.get_home_dir(), 625 | ".config", 626 | "MangoHud", 627 | profile_name.replace(" ", "-") + ".conf" 628 | ); 629 | 630 | string target_path = Path.build_filename( 631 | Environment.get_home_dir(), 632 | ".config", 633 | "MangoHud", 634 | "MangoHud.conf" 635 | ); 636 | 637 | var profile_file = File.new_for_path(profile_path); 638 | if (profile_file.query_exists()) { 639 | profile_file.copy(File.new_for_path(target_path), FileCopyFlags.OVERWRITE); 640 | } 641 | } catch (Error e) { 642 | warning("Failed to apply profile: %s", e.message); 643 | } 644 | } 645 | 646 | void set_preset (string[] preset_values) { 647 | var file = File.new_for_path (Environment.get_home_dir ()).get_child (".config").get_child("MangoHud").get_child ("MangoHud.conf"); 648 | var backup_file = File.new_for_path (Environment.get_home_dir ()).get_child (".config").get_child ("MangoHud").get_child (".MangoHud.backup"); 649 | try { 650 | if (file.query_exists () && !backup_file.query_exists ()) { 651 | file.copy (backup_file, FileCopyFlags.OVERWRITE); 652 | } 653 | var output_stream = new DataOutputStream (file.replace(null, false, FileCreateFlags.NONE)); 654 | output_stream.put_string ("#Preset config by MangoJuice #\n"); 655 | output_stream.put_string ("legacy_layout=false\n"); 656 | foreach (string value in preset_values) { 657 | output_stream.put_string ("%s\n".printf (value)); 658 | } 659 | } catch (Error e) { 660 | stderr.printf ("Error: %s\n", e.message); 661 | } 662 | } 663 | } 664 | -------------------------------------------------------------------------------- /src/intel_power_fix_handler.vala: -------------------------------------------------------------------------------- 1 | using Gtk; 2 | using GLib; 3 | using Adw; 4 | 5 | public async void on_intel_power_fix_button_clicked(Button button) { 6 | try { 7 | bool is_service_active = false; 8 | try { 9 | int exit_status; 10 | Process.spawn_command_line_sync("systemctl is-active --quiet powercap-permissions.service", null, null, out exit_status); 11 | is_service_active = (exit_status == 0); 12 | } catch (Error e) { 13 | stderr.printf("Service check failed: %s\n", e.message); 14 | } 15 | 16 | var dialog = new Adw.AlertDialog(_("Warning"), _("You are changing the rights to intel energy_uj, which could potentially lead to security issues.")); 17 | 18 | dialog.add_response("cancel", _("Cancel")); 19 | dialog.add_response("temporary", _("Until Reboot")); 20 | dialog.add_response("permanent", _("Permanently")); 21 | dialog.set_default_response("cancel"); 22 | dialog.set_close_response("cancel"); 23 | 24 | if (is_service_active) { 25 | dialog.set_response_appearance("permanent", Adw.ResponseAppearance.SUGGESTED); 26 | } 27 | 28 | var window = (Gtk.Window) button.get_root(); 29 | string response = yield dialog.choose(window, null); 30 | 31 | if (response == "cancel") { 32 | return; 33 | } 34 | 35 | var file = File.new_for_path("/sys/class/powercap/intel-rapl:0/energy_uj"); 36 | var info = yield file.query_info_async("*", FileQueryInfoFlags.NONE); 37 | var current_mode = info.get_attribute_uint32(FileAttribute.UNIX_MODE); 38 | 39 | string new_mode = ((current_mode & 0777) == 0644) ? "0600" : "0644"; 40 | 41 | if (response == "temporary") { 42 | Process.spawn_command_line_sync("pkexec chmod " + new_mode + " /sys/class/powercap/intel-rapl\\:0/energy_uj"); 43 | } else if (response == "permanent") { 44 | bool service_exists = FileUtils.test("/etc/systemd/system/powercap-permissions.service", FileTest.EXISTS); 45 | 46 | if (service_exists) { 47 | Process.spawn_command_line_sync("pkexec sh -c 'systemctl stop powercap-permissions.service && " + 48 | "systemctl disable powercap-permissions.service && " + 49 | "rm /etc/systemd/system/powercap-permissions.service && " + 50 | "pkexec chmod 0600 /sys/class/powercap/intel-rapl\\:0/energy_uj && " + 51 | "systemctl daemon-reload'"); 52 | } else { 53 | string service_content = "[Unit]\n" + 54 | "Description=Change permissions of intel-rapl energy_uj file\n" + 55 | "After=sysinit.target\n\n" + 56 | "[Service]\n" + 57 | "Type=oneshot\n" + 58 | "ExecStart=/bin/chmod 0644 /sys/class/powercap/intel-rapl:0/energy_uj\n" + 59 | "RemainAfterExit=yes\n\n" + 60 | "[Install]\n" + 61 | "WantedBy=multi-user.target"; 62 | 63 | Process.spawn_command_line_sync("pkexec sh -c 'echo \"" + service_content + "\" > /etc/systemd/system/powercap-permissions.service && " + 64 | "systemctl daemon-reload && " + 65 | "systemctl enable powercap-permissions.service && " + 66 | "systemctl start powercap-permissions.service'"); 67 | 68 | var reboot_dialog = new Adw.AlertDialog( 69 | _("Warning"), 70 | _("For the permanent changes to take full effect, a reboot is recommended. Would you like to reboot now?") 71 | ); 72 | reboot_dialog.add_response("no", _("Later")); 73 | reboot_dialog.add_response("yes", _("Reboot Now")); 74 | reboot_dialog.set_response_appearance("yes", Adw.ResponseAppearance.DESTRUCTIVE); 75 | 76 | string reboot_response = yield reboot_dialog.choose(window, null); 77 | if (reboot_response == "yes") { 78 | Process.spawn_command_line_async("pkexec reboot"); 79 | } 80 | } 81 | } 82 | 83 | yield check_file_permissions_async(button); 84 | 85 | } catch (Error e) { 86 | stderr.printf("Error: %s\n", e.message); 87 | 88 | var error_dialog = new Adw.AlertDialog( 89 | _("Error"), 90 | e.message 91 | ); 92 | error_dialog.add_response("ok", _("OK")); 93 | 94 | var window = (Gtk.Window) button.get_root(); 95 | error_dialog.present(window); 96 | } 97 | } 98 | 99 | public async void check_file_permissions_async(Button button) { 100 | string file_path = "/sys/class/powercap/intel-rapl:0/energy_uj"; 101 | bool has_permissions = false; 102 | bool permanent_solution = false; 103 | bool is_service_active = false; 104 | 105 | try { 106 | var file = File.new_for_path(file_path); 107 | var info = yield file.query_info_async("*", FileQueryInfoFlags.NONE); 108 | has_permissions = (info.get_attribute_uint32(FileAttribute.UNIX_MODE) & 0777) == 0644; 109 | 110 | permanent_solution = FileUtils.test("/etc/systemd/system/powercap-permissions.service", FileTest.EXISTS); 111 | 112 | if (permanent_solution) { 113 | int exit_status; 114 | Process.spawn_command_line_sync("systemctl is-active --quiet powercap-permissions.service", null, null, out exit_status); 115 | is_service_active = (exit_status == 0); 116 | } 117 | } catch (Error e) { 118 | stderr.printf("Permission check failed: %s\n", e.message); 119 | } 120 | 121 | Idle.add(() => { 122 | if (has_permissions) { 123 | button.add_css_class("suggested-action"); 124 | button.set_tooltip_text(is_service_active ? _("Permissions set permanently (systemd service active)") : 125 | (permanent_solution ? _("Service exists but not active") : _("Permissions set until reboot"))); 126 | } else { 127 | button.remove_css_class("suggested-action"); 128 | button.set_tooltip_text(is_service_active ? _("Service active but permissions not set") : 129 | (permanent_solution ? _("Service exists but inactive") : _("Permissions not set"))); 130 | } 131 | return false; 132 | }); 133 | } -------------------------------------------------------------------------------- /src/load_states.vala: -------------------------------------------------------------------------------- 1 | /* load_states.vala // Licence: GPL-v3.0 */ 2 | 3 | using Gtk; 4 | using Gee; 5 | 6 | public class LoadStates { 7 | public static async void load_states_from_file (MangoJuice mango_juice) { 8 | var config_dir = File.new_for_path (Environment.get_home_dir ()).get_child (".config").get_child ("MangoHud"); 9 | var file = config_dir.get_child ("MangoHud.conf"); 10 | 11 | mango_juice.is_loading = true; 12 | 13 | if (!file.query_exists ()) { 14 | try { 15 | if (!config_dir.query_exists ()) { 16 | config_dir.make_directory_with_parents (); 17 | } 18 | file.create (FileCreateFlags.NONE); 19 | } catch (Error e) { 20 | stderr.printf ("Error creating the file: %s\n", e.message); 21 | mango_juice.is_loading = false; 22 | return; 23 | } 24 | } 25 | 26 | try { 27 | var file_stream = yield file.read_async (); 28 | 29 | var data_stream = new DataInputStream (file_stream); 30 | string line; 31 | 32 | while ((line = yield data_stream.read_line_async ()) != null) { 33 | load_switch_from_file (line, mango_juice.gpu_switches, mango_juice.gpu_config_vars); 34 | load_switch_from_file (line, mango_juice.cpu_switches, mango_juice.cpu_config_vars); 35 | load_switch_from_file (line, mango_juice.memory_switches, mango_juice.memory_config_vars); 36 | if (Config.IS_DEVEL) { 37 | load_switch_from_file (line, mango_juice.git_switches, mango_juice.git_config_vars); 38 | } 39 | load_switch_from_file (line, mango_juice.system_switches, mango_juice.system_config_vars); 40 | load_switch_from_file (line, mango_juice.wine_switches, mango_juice.wine_config_vars); 41 | load_switch_from_file (line, mango_juice.battery_switches, mango_juice.battery_config_vars); 42 | load_switch_from_file (line, mango_juice.other_extra_switches, mango_juice.other_extra_config_vars); 43 | load_switch_from_file (line, mango_juice.inform_switches, mango_juice.inform_config_vars); 44 | load_switch_from_file (line, mango_juice.options_switches, mango_juice.options_config_vars); 45 | 46 | if (line.contains("#custom_command")) { 47 | var custom_command_value = line.split(" #custom_command")[0].strip(); 48 | mango_juice.custom_command_entry.text = custom_command_value; 49 | } 50 | 51 | if (line.has_prefix ("toggle_logging=")) { 52 | var logs_key = line.substring ("toggle_logging=".length); 53 | for (uint i = 0; i < mango_juice.logs_key_model.get_n_items (); i++) { 54 | var item = mango_juice.logs_key_model.get_item (i) as StringObject; 55 | if (item != null && item.get_string () == logs_key) { 56 | mango_juice.logs_key_combo.selected = i; 57 | break; 58 | } 59 | } 60 | } 61 | 62 | if (line.has_prefix ("toggle_hud_position=")) { 63 | var toggle_hud_position = line.substring ("toggle_hud_position=".length); 64 | for (uint i = 0; i < mango_juice.toggle_hud_key_model.get_n_items (); i++) { 65 | var item = mango_juice.toggle_hud_key_model.get_item (i) as StringObject; 66 | if (item != null && item.get_string () == toggle_hud_position) { 67 | mango_juice.toggle_hud_key_combo.selected = i; 68 | break; 69 | } 70 | } 71 | } 72 | 73 | if (line.has_prefix ("pci_dev=")) { 74 | if (mango_juice.gpu_dropdown != null) { 75 | string selected_pci_address = line.substring ("pci_dev=".length).strip (); 76 | 77 | selected_pci_address = selected_pci_address.replace ("0000:", ""); 78 | 79 | var model = mango_juice.gpu_dropdown.model; 80 | 81 | uint index = 0; 82 | bool found = false; 83 | for (uint i = 0; i < model.get_n_items (); i++) { 84 | var item = model.get_item (i) as Gtk.StringObject; 85 | if (item != null) { 86 | string item_text = item.get_string (); 87 | if (item_text.contains (selected_pci_address)) { 88 | index = i; 89 | found = true; 90 | break; 91 | } 92 | } 93 | } 94 | 95 | if (found) { 96 | mango_juice.gpu_dropdown.selected = index; 97 | } 98 | } 99 | } 100 | 101 | if (line.has_prefix ("log_duration=")) { 102 | if (mango_juice.duracion_scale != null) { 103 | int duracion_value = int.parse (line.substring ("log_duration=".length)); 104 | mango_juice.duracion_scale.set_value (duracion_value); 105 | if (mango_juice.duracion_entry != null) { 106 | mango_juice.duracion_entry.text = "%d".printf (duracion_value); 107 | } 108 | } 109 | } 110 | 111 | if (line.has_prefix ("autostart_log=")) { 112 | if (mango_juice.autostart_scale != null) { 113 | int autostart_value = int.parse (line.substring ("autostart_log=".length)); 114 | mango_juice.autostart_scale.set_value (autostart_value); 115 | if (mango_juice.autostart_entry != null) { 116 | mango_juice.autostart_entry.text = "%d".printf (autostart_value); 117 | } 118 | } 119 | } 120 | 121 | if (line.has_prefix ("log_interval=")) { 122 | if (mango_juice.interval_scale != null) { 123 | int interval_value = int.parse (line.substring ("log_interval=".length)); 124 | mango_juice.interval_scale.set_value (interval_value); 125 | if (mango_juice.interval_entry != null) { 126 | mango_juice.interval_entry.text = "%d".printf (interval_value); 127 | } 128 | } 129 | } 130 | 131 | if (line.has_prefix ("output_folder=")) { 132 | mango_juice.custom_logs_path_entry.text = line.substring ("output_folder=".length); 133 | } 134 | 135 | if (line.has_prefix ("fps_limit_method=")) { 136 | var fps_limit_method_value = line.substring ("fps_limit_method=".length); 137 | for (uint i = 0; i < mango_juice.fps_limit_method.model.get_n_items (); i++) { 138 | var item = mango_juice.fps_limit_method.model.get_item (i) as StringObject; 139 | if (item != null && item.get_string () == fps_limit_method_value) { 140 | mango_juice.fps_limit_method.selected = i; 141 | break; 142 | } 143 | } 144 | } 145 | 146 | if (line.has_prefix ("toggle_fps_limit=")) { 147 | var toggle_fps_limit_value = line.substring ("toggle_fps_limit=".length); 148 | for (uint i = 0; i < mango_juice.toggle_fps_limit.model.get_n_items (); i++) { 149 | var item = mango_juice.toggle_fps_limit.model.get_item (i) as StringObject; 150 | if (item != null && item.get_string () == toggle_fps_limit_value) { 151 | mango_juice.toggle_fps_limit.selected = i; 152 | break; 153 | } 154 | } 155 | } 156 | 157 | if (line.has_prefix ("fps_limit=")) { 158 | var fps_limits = line.substring ("fps_limit=".length).split (","); 159 | if (fps_limits.length == 3) { 160 | mango_juice.fps_limit_entry_1.text = fps_limits[0]; 161 | mango_juice.fps_limit_entry_2.text = fps_limits[1]; 162 | mango_juice.fps_limit_entry_3.text = fps_limits[2]; 163 | } 164 | } 165 | 166 | if (line.has_prefix ("vsync=")) { 167 | var vulkan_config_value = line.substring ("vsync=".length); 168 | var vulkan_value = mango_juice.get_vulkan_value_from_config (vulkan_config_value); 169 | for (uint i = 0; i < mango_juice.vulkan_dropdown.model.get_n_items (); i++) { 170 | var item = mango_juice.vulkan_dropdown.model.get_item (i) as StringObject; 171 | if (item != null && item.get_string () == vulkan_value) { 172 | mango_juice.vulkan_dropdown.selected = i; 173 | break; 174 | } 175 | } 176 | } 177 | 178 | if (line.has_prefix ("gl_vsync=")) { 179 | var opengl_config_value = line.substring ("gl_vsync=".length); 180 | var opengl_value = mango_juice.get_opengl_value_from_config (opengl_config_value); 181 | for (uint i = 0; i < mango_juice.opengl_dropdown.model.get_n_items (); i++) { 182 | var item = mango_juice.opengl_dropdown.model.get_item (i) as StringObject; 183 | if (item != null && item.get_string () == opengl_value) { 184 | mango_juice.opengl_dropdown.selected = i; 185 | break; 186 | } 187 | } 188 | } 189 | 190 | if (line.contains ("#filters")) { 191 | var filter_value = line.split ("#filters")[0].strip (); 192 | for (uint i = 0; i < mango_juice.filter_dropdown.model.get_n_items (); i++) { 193 | var item = mango_juice.filter_dropdown.model.get_item (i) as StringObject; 194 | if (item != null && item.get_string () == filter_value) { 195 | mango_juice.filter_dropdown.selected = i; 196 | break; 197 | } 198 | } 199 | } 200 | 201 | if (line.has_prefix ("af=")) { 202 | if (mango_juice.af != null) { 203 | int af_value = int.parse (line.substring ("af=".length)); 204 | mango_juice.af.set_value (af_value); 205 | if (mango_juice.af_entry != null) { 206 | mango_juice.af_entry.text = "%d".printf (af_value); 207 | } 208 | } 209 | } 210 | 211 | if (line.has_prefix ("picmip=")) { 212 | if (mango_juice.picmip != null) { 213 | int picmip_value = int.parse (line.substring ("picmip=".length)); 214 | mango_juice.picmip.set_value (picmip_value); 215 | if (mango_juice.picmip_entry != null) { 216 | mango_juice.picmip_entry.text = "%d".printf (picmip_value); 217 | } 218 | } 219 | } 220 | 221 | if (line.has_prefix ("custom_text_center=")) { 222 | mango_juice.custom_text_center_entry.text = line.substring ("custom_text_center=".length); 223 | } 224 | 225 | if (line.has_prefix ("horizontal")) { 226 | mango_juice.custom_switch.active = true; 227 | } 228 | 229 | if (line.has_prefix ("round_corners=")) { 230 | if (mango_juice.borders_scale != null) { 231 | int borders_value = int.parse (line.substring ("round_corners=".length)); 232 | mango_juice.borders_scale.set_value (borders_value); 233 | if (mango_juice.borders_entry != null) { 234 | mango_juice.borders_entry.text = "%d".printf (borders_value); 235 | } 236 | } 237 | } 238 | 239 | if (line.has_prefix ("background_alpha=")) { 240 | if (mango_juice.alpha_scale != null) { 241 | double alpha_value = double.parse (line.substring ("background_alpha=".length)); 242 | mango_juice.alpha_scale.set_value (alpha_value * 100); 243 | if (mango_juice.alpha_value_label != null) { 244 | mango_juice.alpha_value_label.label = "%.1f".printf (alpha_value); 245 | } 246 | } 247 | } 248 | 249 | if (line.has_prefix ("position=")) { 250 | var position_value = line.substring ("position=".length); 251 | for (uint i = 0; i < mango_juice.position_dropdown.model.get_n_items (); i++) { 252 | var item = mango_juice.position_dropdown.model.get_item (i) as StringObject; 253 | if (item != null && item.get_string () == position_value) { 254 | mango_juice.position_dropdown.selected = i; 255 | break; 256 | } 257 | } 258 | } 259 | 260 | if (line.has_prefix ("table_columns=")) { 261 | if (mango_juice.colums_scale != null) { 262 | int colums_value = int.parse (line.substring ("table_columns=".length)); 263 | mango_juice.colums_scale.set_value (colums_value); 264 | if (mango_juice.colums_entry != null) { 265 | mango_juice.colums_entry.text = "%d".printf (colums_value); 266 | } 267 | } 268 | } 269 | 270 | if (line.has_prefix ("toggle_hud=")) { 271 | var toggle_hud_value = line.substring ("toggle_hud=".length); 272 | mango_juice.toggle_hud_entry.text = toggle_hud_value; 273 | } 274 | 275 | if (line.has_prefix ("font_size=")) { 276 | if (mango_juice.font_size_scale != null) { 277 | int font_size_value = int.parse (line.substring ("font_size=".length)); 278 | mango_juice.font_size_scale.set_value (font_size_value); 279 | if (mango_juice.font_size_entry != null) { 280 | mango_juice.font_size_entry.text = "%d".printf (font_size_value); 281 | } 282 | } 283 | } 284 | 285 | if (line.has_prefix ("font_file=")) { 286 | var font_file = line.substring ("font_file=".length); 287 | if (font_file.strip() == "") { 288 | mango_juice.font_button.label = _("Default"); 289 | } else { 290 | var font_name = Path.get_basename (font_file); 291 | mango_juice.font_button.label = font_name; 292 | } 293 | } 294 | 295 | if (line.has_prefix ("gpu_text=")) { 296 | mango_juice.gpu_text_entry.text = line.substring ("gpu_text=".length); 297 | } 298 | 299 | if (line.has_prefix ("gpu_color=")) { 300 | var gpu_color = line.substring ("gpu_color=".length); 301 | var rgba = Gdk.RGBA (); 302 | rgba.parse ("#" + gpu_color); 303 | mango_juice.gpu_color_button.set_rgba (rgba); 304 | } 305 | 306 | if (line.has_prefix ("cpu_text=")) { 307 | mango_juice.cpu_text_entry.text = line.substring ("cpu_text=".length); 308 | } 309 | 310 | if (line.has_prefix ("cpu_color=")) { 311 | var cpu_color = line.substring ("cpu_color=".length); 312 | var rgba = Gdk.RGBA (); 313 | rgba.parse ("#" + cpu_color); 314 | mango_juice.cpu_color_button.set_rgba (rgba); 315 | } 316 | 317 | if (line.has_prefix ("fps_value=")) { 318 | var fps_values = line.substring ("fps_value=".length).split (","); 319 | if (fps_values.length == 2) { 320 | mango_juice.fps_value_entry_1.text = fps_values[0]; 321 | mango_juice.fps_value_entry_2.text = fps_values[1]; 322 | } 323 | } 324 | 325 | if (line.has_prefix ("fps_color=")) { 326 | var fps_colors = line.substring ("fps_color=".length).split (","); 327 | if (fps_colors.length == 3) { 328 | var rgba_1 = Gdk.RGBA (); 329 | rgba_1.parse ("#" + fps_colors[0]); 330 | mango_juice.fps_color_button_1.set_rgba (rgba_1); 331 | 332 | var rgba_2 = Gdk.RGBA (); 333 | rgba_2.parse ("#" + fps_colors[1]); 334 | mango_juice.fps_color_button_2.set_rgba (rgba_2); 335 | 336 | var rgba_3 = Gdk.RGBA (); 337 | rgba_3.parse ("#" + fps_colors[2]); 338 | mango_juice.fps_color_button_3.set_rgba (rgba_3); 339 | } 340 | } 341 | 342 | if (line.has_prefix ("gpu_load_value=")) { 343 | var gpu_load_values = line.substring ("gpu_load_value=".length).split (","); 344 | if (gpu_load_values.length == 2) { 345 | mango_juice.gpu_load_value_entry_1.text = gpu_load_values[0]; 346 | mango_juice.gpu_load_value_entry_2.text = gpu_load_values[1]; 347 | } 348 | } 349 | 350 | if (line.has_prefix ("gpu_load_color=")) { 351 | var gpu_load_colors = line.substring ("gpu_load_color=".length).split (","); 352 | if (gpu_load_colors.length == 3) { 353 | var rgba_1 = Gdk.RGBA (); 354 | rgba_1.parse ("#" + gpu_load_colors[0]); 355 | mango_juice.gpu_load_color_button_1.set_rgba (rgba_1); 356 | 357 | var rgba_2 = Gdk.RGBA (); 358 | rgba_2.parse ("#" + gpu_load_colors[1]); 359 | mango_juice.gpu_load_color_button_2.set_rgba (rgba_2); 360 | 361 | var rgba_3 = Gdk.RGBA (); 362 | rgba_3.parse ("#" + gpu_load_colors[2]); 363 | mango_juice.gpu_load_color_button_3.set_rgba (rgba_3); 364 | } 365 | } 366 | 367 | if (line.has_prefix ("cpu_load_value=")) { 368 | var cpu_load_values = line.substring ("cpu_load_value=".length).split (","); 369 | if (cpu_load_values.length == 2) { 370 | mango_juice.cpu_load_value_entry_1.text = cpu_load_values[0]; 371 | mango_juice.cpu_load_value_entry_2.text = cpu_load_values[1]; 372 | } 373 | } 374 | 375 | if (line.has_prefix ("cpu_load_color=")) { 376 | var cpu_load_colors = line.substring ("cpu_load_color=".length).split (","); 377 | if (cpu_load_colors.length == 3) { 378 | var rgba_1 = Gdk.RGBA (); 379 | rgba_1.parse ("#" + cpu_load_colors[0]); 380 | mango_juice.cpu_load_color_button_1.set_rgba (rgba_1); 381 | 382 | var rgba_2 = Gdk.RGBA (); 383 | rgba_2.parse ("#" + cpu_load_colors[1]); 384 | mango_juice.cpu_load_color_button_2.set_rgba (rgba_2); 385 | 386 | var rgba_3 = Gdk.RGBA (); 387 | rgba_3.parse ("#" + cpu_load_colors[2]); 388 | mango_juice.cpu_load_color_button_3.set_rgba (rgba_3); 389 | } 390 | } 391 | 392 | if (line.has_prefix ("background_color=")) { 393 | var background_color = line.substring ("background_color=".length); 394 | var rgba = Gdk.RGBA (); 395 | rgba.parse ("#" + background_color); 396 | mango_juice.background_color_button.set_rgba (rgba); 397 | } 398 | 399 | if (line.has_prefix ("frametime_color=")) { 400 | var frametime_color = line.substring ("frametime_color=".length); 401 | var rgba = Gdk.RGBA (); 402 | rgba.parse ("#" + frametime_color); 403 | mango_juice.frametime_color_button.set_rgba (rgba); 404 | } 405 | 406 | if (line.has_prefix ("vram_color=")) { 407 | var vram_color = line.substring ("vram_color=".length); 408 | var rgba = Gdk.RGBA (); 409 | rgba.parse ("#" + vram_color); 410 | mango_juice.vram_color_button.set_rgba (rgba); 411 | } 412 | 413 | if (line.has_prefix ("ram_color=")) { 414 | var ram_color = line.substring ("ram_color=".length); 415 | var rgba = Gdk.RGBA (); 416 | rgba.parse ("#" + ram_color); 417 | mango_juice.ram_color_button.set_rgba (rgba); 418 | } 419 | 420 | if (line.has_prefix ("wine_color=")) { 421 | var wine_color = line.substring ("wine_color=".length); 422 | var rgba = Gdk.RGBA (); 423 | rgba.parse ("#" + wine_color); 424 | mango_juice.wine_color_button.set_rgba (rgba); 425 | } 426 | 427 | if (line.has_prefix ("engine_color=")) { 428 | var engine_color = line.substring ("engine_color=".length); 429 | var rgba = Gdk.RGBA (); 430 | rgba.parse ("#" + engine_color); 431 | mango_juice.engine_color_button.set_rgba (rgba); 432 | } 433 | 434 | if (line.has_prefix ("text_color=")) { 435 | var text_color = line.substring ("text_color=".length); 436 | var rgba = Gdk.RGBA (); 437 | rgba.parse ("#" + text_color); 438 | mango_juice.text_color_button.set_rgba (rgba); 439 | } 440 | 441 | if (line.has_prefix ("media_player_color=")) { 442 | var media_player_color = line.substring ("media_player_color=".length); 443 | var rgba = Gdk.RGBA (); 444 | rgba.parse ("#" + media_player_color); 445 | mango_juice.media_player_color_button.set_rgba (rgba); 446 | } 447 | 448 | if (line.has_prefix ("network_color=")) { 449 | var network_color = line.substring ("network_color=".length); 450 | var rgba = Gdk.RGBA (); 451 | rgba.parse ("#" + network_color); 452 | mango_juice.network_color_button.set_rgba (rgba); 453 | } 454 | 455 | if (line.has_prefix ("battery_color=")) { 456 | var battery_color = line.substring ("battery_color=".length); 457 | var rgba = Gdk.RGBA (); 458 | rgba.parse ("#" + battery_color); 459 | mango_juice.battery_color_button.set_rgba (rgba); 460 | } 461 | 462 | if (line.has_prefix ("offset_x=")) { 463 | if (mango_juice.offset_x_scale != null) { 464 | mango_juice.offset_x_scale.set_value (int.parse (line.substring ("offset_x=".length))); 465 | if (mango_juice.offset_x_value_label != null) { 466 | mango_juice.offset_x_value_label.label = "%d".printf ((int)mango_juice.offset_x_scale.get_value ()); 467 | } 468 | } 469 | } 470 | 471 | if (line.has_prefix ("offset_y=")) { 472 | if (mango_juice.offset_y_scale != null) { 473 | mango_juice.offset_y_scale.set_value (int.parse (line.substring ("offset_y=".length))); 474 | if (mango_juice.offset_y_value_label != null) { 475 | mango_juice.offset_y_value_label.label = "%d".printf ((int)mango_juice.offset_y_scale.get_value ()); 476 | } 477 | } 478 | } 479 | 480 | if (line.has_prefix ("fps_sampling_period=")) { 481 | if (mango_juice.fps_sampling_period_scale != null) { 482 | mango_juice.fps_sampling_period_scale.set_value (int.parse (line.substring ("fps_sampling_period=".length))); 483 | if (mango_juice.fps_sampling_period_value_label != null) { 484 | mango_juice.fps_sampling_period_value_label.label = "%d ms".printf ((int)mango_juice.fps_sampling_period_scale.get_value ()); 485 | } 486 | } 487 | } 488 | 489 | if (line.has_prefix ("blacklist=")) { 490 | mango_juice.blacklist_entry.text = line.substring ("blacklist=".length); 491 | } 492 | 493 | if (line.has_prefix ("gpu_list=")) { 494 | mango_juice.gpu_entry.text = line.substring ("gpu_list=".length); 495 | } 496 | 497 | if (line.has_prefix("media_player_format=")) { 498 | var format_str = line.substring("media_player_format=".length).strip(); 499 | 500 | if (format_str.has_prefix("{") && format_str.has_suffix("}")) { 501 | format_str = format_str.substring(1, format_str.length-2); 502 | } 503 | 504 | string[] format_parts = format_str.split("};{"); 505 | 506 | for (int i = 0; i < 3; i++) { 507 | string part = "none"; 508 | if (i < format_parts.length) { 509 | part = format_parts[i].strip(); 510 | } 511 | 512 | if (i < mango_juice.media_format_dropdowns.size) { 513 | var dropdown = mango_juice.media_format_dropdowns.get(i); 514 | var model = dropdown.model as Gtk.StringList; 515 | 516 | bool found = false; 517 | for (uint j = 0; j < model.get_n_items(); j++) { 518 | var item = model.get_item(j) as StringObject; 519 | if (item != null && item.get_string() == part) { 520 | dropdown.selected = j; 521 | found = true; 522 | break; 523 | } 524 | } 525 | 526 | if (!found && part == "none") { 527 | for (uint j = 0; j < model.get_n_items(); j++) { 528 | var item = model.get_item(j) as StringObject; 529 | if (item != null && item.get_string() == "none") { 530 | dropdown.selected = j; 531 | break; 532 | } 533 | } 534 | } 535 | } 536 | } 537 | } 538 | } 539 | } catch (Error e) { 540 | stderr.printf ("Error reading the file: %s\n", e.message); 541 | } 542 | mango_juice.is_loading = false; 543 | } 544 | public static void load_switch_from_file (string line, Switch[] switches, string[] config_vars) { 545 | for (int i = 0; i < config_vars.length; i++) { 546 | string config_var = config_vars[i]; 547 | if (config_var == "io_read \n io_write") { 548 | if (line == "io_read" || line == "io_write") { 549 | switches[i].active = true; 550 | } 551 | } else if (line == config_var) { 552 | switches[i].active = true; 553 | } 554 | } 555 | } 556 | } 557 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | deps = [ 2 | dependency('libadwaita-1'), 3 | dependency('gee-0.8') 4 | ] 5 | 6 | vapi_sources = files('config.vapi') 7 | 8 | sources = files( 9 | 'dialog.vala', 10 | 'mangojuice.vala', 11 | 'save_states.vala', 12 | 'other/OtherBox.vala', 13 | 'other/other_save.vala', 14 | 'other/other_load.vala', 15 | 'load_states.vala', 16 | 'reset_manager.vala', 17 | 'intel_power_fix_handler.vala', 18 | 'advanced.vala' 19 | ) 20 | 21 | configure_file(output: 'config.h', configuration: conf) 22 | #config_h_dir = include_directories('.') 23 | 24 | executable( 25 | 'mangojuice', 26 | 27 | resources, 28 | vapi_sources, 29 | sources, 30 | vala_args: ['--vapidir', meson.current_source_dir()], 31 | dependencies: deps, 32 | install: true 33 | ) -------------------------------------------------------------------------------- /src/other/OtherBox.vala: -------------------------------------------------------------------------------- 1 | /* OtherBox.vala // Licence: GPL-v3.0 */ 2 | using Gtk; 3 | using Gee; 4 | 5 | public class OtherBox : Box { 6 | 7 | const int MAX_WIDTH_CHARS = 6; 8 | const int WIDTH_CHARS = 4; 9 | const int FLOW_BOX_MARGIN = 12; 10 | const int FLOW_BOX_ROW_SPACING = 6; 11 | const int FLOW_BOX_COLUMN_SPACING = 6; 12 | const int MAIN_BOX_SPACING = 6; 13 | 14 | public ArrayList scales { get; set; } 15 | public ArrayList entries { get; set; } 16 | public ArrayList