diff --git a/README.md b/README.md index 5f793a5..6b39d39 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ nixos/ ├── modules/ │ ├── home-manager/ # User-level configurations (apps, shell, WM, plugins) │ ├── nixos/ # System-level modules (daemons, services, udev, ly) +│ ├── qt/ # qt modules (qtbatticon) │ └── scripts/ # Custom scripts called from keybinds or tools ├── other/ │ ├── kblayouts/ # Framework 16 RGB macropad layout @@ -208,6 +209,7 @@ All moving macros with ``, `g`, and the recording with `q` are disabled. ## Useful Information +- `qtbatticon` is a custom battery icon tray, because `cbatticon` and `batticonplus` did not work. Click on it to get more info about the battery. - `man` and `manix` use `fzf` for fuzzy search of pages. - `pkgs-unstable` can be used for unstable nixpkgs packages. - Nix garbage collection runs weekly, deleting generations older than 7 days. diff --git a/assets/pepe-music.gif b/assets/pepe-music.gif new file mode 100644 index 0000000..8a7d44d Binary files /dev/null and b/assets/pepe-music.gif differ diff --git a/assets/recording_20260410_15-51-06.mp4 b/assets/recording_20260410_15-51-06.mp4 new file mode 100644 index 0000000..491fb3d Binary files /dev/null and b/assets/recording_20260410_15-51-06.mp4 differ diff --git a/assets/sisyphe.gif b/assets/sisyphe.gif new file mode 100644 index 0000000..c727a3a Binary files /dev/null and b/assets/sisyphe.gif differ diff --git a/hosts/laptop/configuration.nix b/hosts/laptop/configuration.nix index bff63ad..4d85eb2 100755 --- a/hosts/laptop/configuration.nix +++ b/hosts/laptop/configuration.nix @@ -256,7 +256,10 @@ fuzzel papirus-icon-theme papirus-folders - power-profiles-daemon + #power-profiles-daemon #we use tlp + stalonetray + libappindicator + snixembed ]; # fonts diff --git a/hosts/laptop/home.nix b/hosts/laptop/home.nix index 7de7596..5b65c7a 100755 --- a/hosts/laptop/home.nix +++ b/hosts/laptop/home.nix @@ -80,6 +80,7 @@ (pkgs.callPackage ../../modules/scripts/manix.nix {}) (pkgs.callPackage ../../modules/scripts/man.nix {}) (pkgs.callPackage ../../modules/scripts/trimmer.nix {}) + (pkgs.callPackage ../../modules/qt/qtbatticon.nix {}) #(pkgs.callPackage ../../modules/scripts/obsidianbackup.nix {}) # periodically syncs obsidian's note to kdrive #(pkgs.callPackage ../../modules/scripts/obsidianprofiles.nix {}) # fzf vault selector # obsidian was replaced with a custom solution # no longer used scripts for quickshell. see caelestia-shell diff --git a/modules/home-manager/1 b/modules/home-manager/1 deleted file mode 100644 index ee61787..0000000 --- a/modules/home-manager/1 +++ /dev/null @@ -1,373 +0,0 @@ -{ - config, - pkgs, - pkgs-unstable, - ... -}: { - - programs.neovim = { - enable = true; - vimAlias = false; - viAlias = true; - package = pkgs-unstable.neovim-unwrapped; # required for unwrapped version - plugins = [ - pkgs-unstable.vimPlugins.vivify-vim - pkgs-unstable.vimPlugins.vimtex - pkgs-unstable.vimPlugins.zoxide-vim - pkgs-unstable.vimPlugins.gruvbox - pkgs-unstable.vimPlugins.neovim-sensible - pkgs-unstable.vimPlugins.nvim-web-devicons - pkgs-unstable.vimPlugins.nvim-tree-lua - pkgs-unstable.vimPlugins.nvim-surround - pkgs-unstable.vimPlugins.floaterm - pkgs-unstable.vimPlugins.nvim-lspconfig - pkgs-unstable.vimPlugins.cmp-nvim-lsp - pkgs-unstable.vimPlugins.cmp-nvim-lsp-signature-help - pkgs-unstable.vimPlugins.cmp-under-comparator - pkgs-unstable.vimPlugins.cmp-buffer - pkgs-unstable.vimPlugins.cmp-path - pkgs-unstable.vimPlugins.cmp-cmdline - pkgs-unstable.vimPlugins.nvim-cmp - pkgs-unstable.vimPlugins.cmp-vsnip - pkgs-unstable.vimPlugins.vim-vsnip - pkgs-unstable.vimPlugins.dashboard-nvim - pkgs-unstable.vimPlugins.lualine-nvim - pkgs-unstable.vimPlugins.vim-nix - pkgs-unstable.vimPlugins.plenary-nvim - pkgs-unstable.vimPlugins.blink-ripgrep-nvim - pkgs-unstable.vimPlugins.nvim-treesitter - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.rasi - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.regex - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.udev - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.zsh - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.c - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.bibtex - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.bash - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.gitignore - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.comment - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.json - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.hyprlang - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.lua - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.latex - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.kitty - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.markdown - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.nix - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.printf - pkgs-unstable.vimPlugins.nvim-treesitter-parsers.python - pkgs-unstable.vimPlugins.telescope-nvim - pkgs-unstable.vimPlugins.nvim-scrollbar - pkgs-unstable.vimPlugins.promise-async - pkgs-unstable.vimPlugins.nvim-ufo - pkgs-unstable.vimPlugins.cheatsheet-nvim - pkgs-unstable.vimPlugins.popup-nvim - pkgs-unstable.vimPlugins.vim-visual-multi - pkgs-unstable.vimPlugins.fugit2-nvim - pkgs-unstable.vimPlugins.nvim-lastplace - pkgs-unstable.vimPlugins.runner-nvim # terminal - ]; - extraLuaConfig = '' - - -- Notes about keybinds - -- § -> cheatsheet - -- [1-2] -> folding - -- e[1-4] -> error handling (1-2 move to prev and next error; 3 see error definiton; 4 list of error) - -- f[1-4] -> telescope () - -- g[1-5] -> lsp info (show definition, - -- - -- - local vim = vim - - -- ========================= - -- LEADER & KEY DISABLE - -- ========================= - vim.g.mapleader = ' ' - vim.g.maplocalleader = ' ' - vim.keymap.set({'n', 'v'}, '', '', { desc = 'Leader key, disable default behavior' }) - vim.keymap.set('n', 'q', '', { desc = 'Disable macro recording' }) -vim.keymap.set('n', 'gg', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gG', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g0', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g^', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g$', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gj', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gk', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g~', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gu', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gU', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g=', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gq', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g@', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g?', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gC', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gD', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gx', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'gr', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g[', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g]', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g;', '', { desc = 'Disable g motion' }) -vim.keymap.set('n', 'g,', '', { desc = 'Disable g motion' }) - -- ========================= - -- GENERAL OPTIONS - -- ========================= - vim.g.have_nerd_font = true - vim.opt.relativenumber = false - vim.opt.number = true - vim.opt.mouse = 'a' - vim.opt.showmode = false - vim.schedule(function() - vim.opt.clipboard = 'unnamedplus' - end) - vim.opt.breakindent = true - vim.opt.undofile = true - vim.opt.ignorecase = true - vim.opt.smartcase = true - vim.opt.signcolumn = 'yes' - vim.opt.updatetime = 250 - vim.opt.timeoutlen = 300 - vim.opt.splitright = true - vim.opt.splitbelow = true - vim.opt.list = true - vim.opt.listchars = { tab = '» ', trail = '·', nbsp = '␣' } - vim.opt.expandtab = true - vim.opt.smartindent = true - vim.opt.tabstop = 4 - vim.opt.shiftwidth = 4 - vim.opt.inccommand = 'split' - vim.opt.cursorline = true vim.opt.scrolloff = 10 vim.opt.termguicolors = true vim.o.background = "dark" vim.cmd("colorscheme gruvbox") - - -- ========================= - -- BASIC KEYMAPS - -- ========================= - vim.keymap.set('n', '', 'nohlsearch') - vim.keymap.set('t', '', '', { desc = 'Exit terminal mode' }) - - -- Disable arrow keys in normal mode - vim.keymap.set('n', '', 'echo "Use h to move!!"') - vim.keymap.set('n', '', 'echo "Use l to move!!"') - vim.keymap.set('n', '', 'echo "Use k to move!!"') - vim.keymap.set('n', '', 'echo "Use j to move!!"') - - -- Window navigation - vim.keymap.set('n', '', '', { desc = 'Move focus to the left window' }) - vim.keymap.set('n', '', '', { desc = 'Move focus to the right window' }) - vim.keymap.set('n', '', '', { desc = 'Move focus to the lower window' }) - vim.keymap.set('n', '', '', { desc = 'Move focus to the upper window' }) - - -- Cheatsheet - vim.keymap.set("n","§", "Cheatsheet") - require("cheatsheet").setup({ - bundled_cheatsheets = { enabled = { "default" } }, - bundled_plugin_cheatsheets = { disabled = { "gitsigns.nvim" } } - }) - - -- Highlight yanked text - vim.api.nvim_create_autocmd('TextYankPost', { - desc = 'Highlight on yank', - group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }), - callback = function() vim.highlight.on_yank() end, - }) - - -- ======================== - -- terminal - -- ======================= - require("runner-nvim").setup() - vim.keymap.set("n", "", "Runnver") - -- ========================= - -- SCROLLBAR - -- ========================= - require("scrollbar").setup() - - -- ========================= - -- UFO FOLDING - -- ========================= - vim.o.foldcolumn = '1' - vim.o.foldlevel = 99 - vim.o.foldlevelstart = 99 - vim.o.foldenable = true - vim.keymap.set('n', '1', "foldopen") - vim.keymap.set('n', '2', "foldclose") - require('ufo').setup({ - provider_selector = function() return {'treesitter', 'indent'} end - }) - - -- ========================= - -- treesitter - -- ======================== - require('nvim-treesitter').setup({ - ensure_installed = {}, - highlight = { - enable = true, - additional_vim_regex_highlighting = false, - }, - indent = { enable = true }, -}) - -- ========================= - -- NVIM-TREE - -- ========================= - vim.g.loaded_netrw = 1 - vim.g.loaded_netrwPlugin = 1 - require("nvim-tree").setup({ - sort = { sorter = "case_sensitive" }, - view = { width = 30 }, - renderer = { group_empty = true }, - filters = { dotfiles = true }, - }) - - -- ========================= - -- CMP CONFIGURATION - -- ========================= - local cmp = require'cmp' - cmp.setup({ - snippet = { expand = function(args) vim.fn["vsnip#anonymous"](args.body) end }, - mapping = cmp.mapping.preset.insert({ - [''] = cmp.mapping.scroll_docs(-4), - [''] = cmp.mapping.scroll_docs(4), - [''] = cmp.mapping.complete(), - [''] = cmp.mapping.abort(), - [''] = cmp.mapping.confirm({ select = true }), - }), - sources = cmp.config.sources({ { name = 'nvim_lsp' }, { name = 'vsnip' } }, { { name = 'buffer' } }), - }) - - cmp.setup.cmdline({ '/', '?' }, { mapping = cmp.mapping.preset.cmdline(), sources = { { name = 'buffer' } } }) - cmp.setup.cmdline(':', { mapping = cmp.mapping.preset.cmdline(), sources = cmp.config.sources({ { name = 'path' } }, { { name = 'cmdline' } }), matching = { disallow_symbol_nonprefix_matching = false } }) - --- ========================= --- LSP CONFIGURATION --- ========================= -local capabilities = require('cmp_nvim_lsp').default_capabilities() - - -- Diagnostics - vim.keymap.set("n", "e1", function() vim.diagnostic.jump({ prev=true, count = 1 }) end, { desc = "Previous diagnostic" }) - vim.keymap.set("n", "e2", function() vim.diagnostic.jump({ prev=false, count = 1 }) end, { desc = "Next diagnostic" }) - vim.keymap.set("n", "e3", vim.diagnostic.open_float, { desc = "Show diagnostic" }) - vim.keymap.set("n", "e4", vim.diagnostic.setloclist, { desc = "Diagnostic list" }) - - -- LSP functionality - vim.keymap.set("n", "g1", vim.lsp.buf.hover, { desc = "Hover documentation" }) - vim.keymap.set("n", "g2", vim.lsp.buf.definition, { desc = "Go to definition" }) - vim.keymap.set("n", "g3", vim.lsp.buf.declaration, { desc = "Go to declaration" }) - vim.keymap.set("n", "g4", vim.lsp.buf.implementation, { desc = "Go to implementation" }) - vim.keymap.set("n", "g5", vim.lsp.buf.references, { desc = "Show references" }) - --- LSP server setup -vim.lsp.config("lua_ls", { cmd = { "lua-language-server" }, filetypes = { "lua" }, root_dir = vim.fs.dirname, on_attach = on_attach }) - vim.lsp.config("clangd", { - cmd = { "clangd" }, - filetypes = { "c","cpp","objc","objcpp" }, - on_attach = on_attach , - init_options = { - clangdFileStatus = true, - usePlaceholders = true, - completeUnimported = true, - semanticHighlighting = true} - }) - vim.lsp.config("nixd", { - cmd = { "nixd" }, - filetyypes = { "nix" }, - on_attach = on_attach, - }) -vim.lsp.config("pylsp", { - cmd = { "pylsp" }, - filetypes = { "python" }, - on_attach = on_attach, - settings = { - pylsp = { - plugins = { - pylsp_mypy = { enabled = true }, - jedi_completion = { fuzzy = true } - } - } - } -}) -vim.lsp.config("texlab", { cmd = { "texlab" }, filetypes = { "tex" }, on_attach = on_attach }) - -vim.lsp.enable({ "lua_ls", "clangd", "pylsp", "texlab", "nixd"}) - -- ========================= - -- DASHBOARD - -- ========================= - require('dashboard').setup { - theme = 'hyper', - config = { - header = { - " ... ... . ", - " .=*8888n..\"%888: @88> ", - " X ?8888f '8888 u. ... .. %8P .. . : ", - " 88x. '8888X 8888> .u ...ue888b :~\"\"888h.:^\"888: . .888: x888 x888. ", - "'8888k 8888X '\"*8h. ud8888. 888R Y888r 8X `8888X 8888> .@88u ~`8888~'888X`?888f` ", - " \"8888 X888X .xH8 :888'8888. 888R I888> X888n. 8888X ?888> '\"888E` X888 888X '888> ", - " `8\" X888!:888X d888 '88%\" 888R I888> '88888 8888X ?**h. 888E X888 888X '888> ", - " =~` X888 X888X 8888.+\" 888R I888> `*88 8888~ x88x. 888E X888 888X '888> ", - " :h. X8*` !888X 8888L u8888cJ888 ..<\" 88*` 88888X 888E X888 888X '888> ", - " X888xX\" '8888..: '8888c. .+ \"*888*P\" ..XC. `*8888k 888& \"*88%\"\"*88\" '888!` ", - ":~`888f '*888*\" \"88888% 'Y\" :888888H. `%88> R888\" `~ \" `\"` ", - " \"\" `\"` \"YP' < `\"888888: X\" \"\" ", - " %888888x.-` ", - " \"\"**\"\" ", - "", - "" - }, - shortcut = { { desc = " Open File Tree", group = "", key = 't', action = 'NvimTreeOpen' } }, - packages = { enable = false }, - project = { enable = true, limit = 8, icon = ' New file', action = 'Telescope find_files cwd=' }, - mru = { limit = 10, icon = ' History' }, - footer = {}, - } - } - - -- jumps cursor when was last cursor last time - require("nvim-lastplace").setup({ - -- Filetypes to ignore - ignore_filetypes = { - "gitcommit", "gitrebase", "svn", "hgcommit", "xxd", "COMMIT_EDITMSG" - }, - - -- Buffer types to ignore - ignore_buftypes = { - "quickfix", "nofile", "help", "terminal" - }, - - -- Center cursor after jumping - center_on_jump = true, - - -- Only jump if target line is not visible - jump_only_if_not_visible = false, - - -- Minimum lines required to enable jumping - min_lines = 10, - - -- Maximum line to jump to (0 = no limit) - max_line = 0, - - -- Open folds after jumping - open_folds = true, - - -- Enable debug messages - debug = false, - }) - - -- ========================= - -- LUALINE - -- ========================= - require('lualine').setup { options = { theme = "gruvbox_dark" } } - - - --==================0 - -- Some utils - --================== - vim.keymap.set('n', 'r1', 'Fugit2', { desc = 'Open git helper' }) - - -- ========================= - -- telescope - -- ======================= - local builtin = require('telescope.builtin') - vim.keymap.set('n', 'f1', builtin.find_files, { desc = 'Telescope find files' }) - vim.keymap.set('n', 'f2', builtin.live_grep, { desc = 'Telescope live grep' }) - vim.keymap.set('n', 'f3', builtin.buffers, { desc = 'Telescope buffers' }) - vim.keymap.set('n', 'f4', builtin.help_tags, { desc = 'Telescope help tags' }) - - -- Disable relative numbers even if plugins override - vim.api.nvim_create_autocmd("VimEnter", { callback = function() vim.opt.colorcolumn = ""; vim.opt.relativenumber = false end }) - ''; - }; -} diff --git a/modules/home-manager/caelestia.nix b/modules/home-manager/caelestia.nix index 9648cf4..d0a51b8 100644 --- a/modules/home-manager/caelestia.nix +++ b/modules/home-manager/caelestia.nix @@ -4,7 +4,11 @@ pkgs-unstable, caelestia-shell, ... -}: { +}: +let + sisyphe = ../../assets/sisyphe.gif; + pepe-music = ../../assets/pepe-music.gif; +in { home.file.".face" = { # this is were caelestia will search for profiles pictures source = ../../assets/sisyphePFP.png; @@ -13,13 +17,13 @@ programs.caelestia = { enable = true; systemd = { - enable = false;# if you prefer starting from your compositor + enable = false; # if we launch it form here, the keybinds don't work target = "graphical-session.target"; environment = []; }; settings = { # default config https://github.com/caelestia-dots/shell?tab=readme-ov-file#example-configuration appearance = { - mediaGifSpeedAdjustment = 300; + mediaGifSpeedAdjustment = 500; sessionGifSpeed = 0.7; anim = { duration = { @@ -173,7 +177,7 @@ } { id = "statusIcon"; - enbaled = true; + enabled = true; } { id = "power"; @@ -193,14 +197,14 @@ }; showOnHover = true; status = { - showAudio = true; - showBattery = true; - showBluetooth = true; + showAudio = false; + showBattery = false; # use powerprofile daemon + showBluetooth = false; showKbLayout = false; showMicrophone = false; - showNetwork = true; + showNetwork = false; showWifi = true; - showLockStatus = true; + showLockStatus = false; }; tray = { background = true; # default true @@ -387,8 +391,8 @@ hideDelay = 2000; }; paths = { - mediaGif = "root:/assets/bongocat.gif"; - sessionGif = "root:/assets/kurukuru.gif"; + mediaGif = pepe-music; # default "root:/assets/bongocat.gif" + sessionGif = sisyphe; # default "root:/assets/kurukuru.gif" noNotifsPic = "root:/assets/dino.png"; lockNoNotifsPic = "root:/assets/dino.png"; wallpaperDir = "~/Pictures/Wallpapers"; diff --git a/modules/home-manager/cbatticon.nix b/modules/home-manager/cbatticon.nix new file mode 100644 index 0000000..d00a5ae --- /dev/null +++ b/modules/home-manager/cbatticon.nix @@ -0,0 +1,17 @@ +# don't works. some GTK error. I let it here if someday, someone is interest in this shit. +# ChatGPT half baked a replacement that works on my system. +# see ../qt/qtbatticon +{ + config, + pkgs, + ... +}: { + services.cbatticon = { + enable = true; + lowLevelPercent = 20; + criticalLevelPercent = 5; + batteryId = "BAT1"; + hideNotification = true; # we use another script for battery notifications + updateIntervalSeconds = 10; + }; +} diff --git a/modules/home-manager/hyprland.nix b/modules/home-manager/hyprland.nix index b62ab1e..094b61a 100644 --- a/modules/home-manager/hyprland.nix +++ b/modules/home-manager/hyprland.nix @@ -208,6 +208,7 @@ in { "wl-paste --type image --watch cliphist store" # clipboard store image data "custom-batterynotify" "custom-batterywarning" + "qtbatticon" # custom battery tray #wallpapers/b "swww img ${wallpaper}" "swww-daemon" @@ -215,9 +216,9 @@ in { "custom-mountkdrive" #"waybar" "custom-gitnotify" - "custom-obsidianbackup" # backups the obsidian notes to kdrive and to a timed hidden dir (~/.Notes.backup/) - "QS-notifycache" # builds the cache that will be used for the notification history - + #"custom-obsidianbackup" # backups the obsidian notes to kdrive and to a timed hidden dir (~/.Notes.backup/) + #"QS-notifycache" # builds the cache that will be used for the notification history + "sleep 2 & caelestia-shell" #works better if it sleeps a bit before /* We stopped using that ######################### maybe we should desactivate those scripts # login autostart ####################### diff --git a/modules/home-manager/neovim.nix b/modules/home-manager/neovim.nix index 9652081..70df624 100644 --- a/modules/home-manager/neovim.nix +++ b/modules/home-manager/neovim.nix @@ -255,6 +255,9 @@ local capabilities = require('cmp_nvim_lsp').default_capabilities() vim.keymap.set("n", "e3", vim.diagnostic.open_float, { desc = "Show diagnostic" }) vim.keymap.set("n", "e4", vim.diagnostic.setloclist, { desc = "Diagnostic list" }) + vim.diagnostic.config({ + update_in_insert = false, -- i don't get any new error when in insert + }) require("better-diagnostic-virtual-text").setup({ ui = { wrap_line_after = false, -- wrap the line after this length to avoid the virtual text is too long diff --git a/modules/qt/qtbatticon.nix b/modules/qt/qtbatticon.nix new file mode 100644 index 0000000..46fe766 --- /dev/null +++ b/modules/qt/qtbatticon.nix @@ -0,0 +1,176 @@ +# because cbatticon, batticonplus and other things didn't work. +# chatGpt did this. +# https://chatgpt.com/c/69da11b3-ba24-8331-b7b0-a58a72e69d42 +# at your risk and perils +# click on the icon to get a notification with state, percentage and wattage +{ lib +, stdenv +, qt6 +, pkg-config +, pkgs +}: + +stdenv.mkDerivation { + pname = "qtbatticon"; + version = "1.0"; + + src = pkgs.writeTextFile { + name = "qtbatticon.cpp"; + text = '' +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QString readFile(const QString &path) { + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) return ""; + QTextStream in(&f); + return in.readAll().trimmed(); +} + +int readInt(const QString &path) { + return readFile(path).toInt(); +} + +double readDouble(const QString &path) { + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) return 0; + QTextStream in(&f); + return in.readAll().trimmed().toDouble(); +} + +int toStep(int capacity) { + return (capacity / 10) * 10; +} + +QString pad3(int n) { + return QString("%1").arg(n, 3, 10, QChar('0')); +} + +double getPowerW(const QString &batPath) { + double current_uA = readDouble(batPath + "/current_now"); + double voltage_uV = readDouble(batPath + "/voltage_now"); + + if (current_uA > 0 && voltage_uV > 0) { + return (current_uA * voltage_uV) / 1e12; + } + return -1; +} + +QIcon batteryIcon(int capacity, bool charging) { + int step = toStep(capacity); + QString num = pad3(step); + + QString base = "/run/current-system/sw/share/icons/Papirus/24x24/panel/"; + + QString file; + if (charging) { + file = base + "battery-" + num + "-charging.svg"; + } else { + file = base + "battery-" + num + ".svg"; + } + + QIcon icon(file); + + if (icon.isNull()) { + qDebug() << "FAILED icon:" << file; + icon = QIcon(base + "battery-100.svg"); + } + + return icon; +} + +void sendNotification(const QString &title, const QString &body) { + QString cmd = "notify-send \"" + title + "\" \"" + body + "\""; + system(cmd.toUtf8().constData()); +} + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + QString batPath = "/sys/class/power_supply/BAT1"; + QSystemTrayIcon tray; + + auto updateIcon = [&]() { + int capacity = readInt(batPath + "/capacity"); + QString status = readFile(batPath + "/status"); + bool charging = status.contains("Charging"); + + tray.setIcon(batteryIcon(capacity, charging)); + }; + + auto notify = [&]() { + int capacity = readInt(batPath + "/capacity"); + QString status = readFile(batPath + "/status"); + + bool charging = status.contains("Charging"); + bool discharging = status.contains("Discharging"); + + double power = getPowerW(batPath); + + QString state; + if (charging) state = "Charging"; + else if (discharging) state = "Discharging"; + else state = status; + + QString powerStr = (power > 0.01) + ? QString::number(power, 'f', 2) + " W" + : "N/A"; + + QString msg = + QString("%1\n%2%\n%3") + .arg(state) + .arg(capacity) + .arg(powerStr); + + sendNotification("Battery status", msg); + }; + + QObject::connect(&tray, &QSystemTrayIcon::activated, + [&](QSystemTrayIcon::ActivationReason r) { + if (r == QSystemTrayIcon::Trigger) { + notify(); + } + }); + + updateIcon(); + tray.setVisible(true); + + QTimer timer; + QObject::connect(&timer, &QTimer::timeout, updateIcon); + timer.start(5000); + + return app.exec(); +} + ''; + }; + + nativeBuildInputs = [ + pkg-config + qt6.wrapQtAppsHook + ]; + + buildInputs = [ + qt6.qtbase + qt6.qtsvg + pkgs.papirus-icon-theme + ]; + + dontUnpack = true; + + buildPhase = '' + g++ $src -o qtbatticon \ + $(pkg-config --cflags --libs Qt6Widgets) + ''; + + installPhase = '' + mkdir -p $out/bin + install -m755 qtbatticon $out/bin/ + ''; +}