rofi steam game list... and lots of other stuff

This commit is contained in:
chayleaf 2023-03-07 18:20:08 +07:00
parent 741a5dfb18
commit 6af0cdbacf
17 changed files with 898 additions and 65 deletions

View file

@ -1,15 +1,56 @@
{ config, pkgs, lib, ... }:
{
# TODO: theme (it's using fish variables...)
programs.fish = {
programs.fish =
let nom-compat = pkgs.runCommand "any-shell-nom-compat" {} ''
mkdir -p $out/bin
for cmd in $(echo nix nix-shell nix-build); do
echo '#! ${pkgs.bash}/bin/bash' > $out/bin/$cmd
echo -n 'PATH=`echo $PATH | tr ":" "\n" | grep -v "any-shell-nom-compat" | tr "\n" ":"` ' >> $out/bin/$cmd
cmd1=$(echo $cmd | sed 's/nix/nom/')
echo "$cmd1"' "$@"' >> $out/bin/$cmd
chmod +x $out/bin/$cmd
done
'';
in {
enable = true;
# not sure this is needed, but just in case
shellInit = ''
source /etc/fish/config.fish
'';
interactiveShellInit = ''
${pkgs.any-nix-shell}/bin/any-nix-shell fish --info-right | source
# ${pkgs.any-nix-shell}/bin/any-nix-shell fish | source
function nix-shell
${pkgs.any-nix-shell}/bin/.any-nix-shell-wrapper fish $argv
end
function nix
if test $argv[1] = shell
set argv[1] fish
${pkgs.any-nix-shell}/bin/.any-nix-wrapper $argv
else if test $argv[1] = develop
set argv[1] fish
command nix develop --command $argv
else
command nix $argv
end
end
function nom-shell
PATH="${nom-compat}/bin:$PATH" ${pkgs.any-nix-shell}/bin/.any-nix-shell-wrapper fish $argv
end
function nom
if test $argv[1] = shell
set argv[1] fish
PATH="${nom-compat}/bin:$PATH" ${pkgs.any-nix-shell}/bin/.any-nix-wrapper $argv
else if test $argv[1] = develop
set argv[1] fish
${pkgs.nix-output-monitor}/bin/nom develop --command $argv
else
command nix $argv
end
end
export NOMCOMPAT=${nom-compat}
# for posix compatibility
set -gx SHELL zsh

View file

@ -56,12 +56,12 @@
# for preview
exa bat
ffmpeg ffmpegthumbnailer nsxiv imagemagick
libarchive unzip gnutar atool
libarchive atool
libreoffice poppler_utils fontpreview djvulibre
glow w3m
# for opening
p7zip unrar-wrapper zathura mpv odt2txt
];
p7zip unrar-wrapper zathura odt2txt
] ++ lib.optionals (!config.programs.mpv.enable) [ mpv ];
plugins = {
src = pluginSrc;
mappings = {
@ -144,6 +144,6 @@
home.packages = with pkgs; [
rclone sshfs fuse
file jq python3Full killall
appimage-run comma
appimage-run comma nix-output-monitor
];
}

View file

@ -1,9 +1,31 @@
{ config, pkgs, lib, ... }:
{
imports = [ ./terminal.nix ];
i18n.inputMethod = let fcitx5-qt = pkgs.libsForQt5.fcitx5-qt; in {
enabled = "fcitx5";
fcitx5.addons = with pkgs; [ fcitx5-lua fcitx5-gtk fcitx5-mozc fcitx5-configtool fcitx5-qt ];
};
home.sessionVariables = {
GTK_IM_MODULE = "fcitx";
QT_IM_MODULE = "fcitx";
XMODIFIERS = "@im=fcitx";
SDL_IM_MODULE = "fcitx";
XIM_SERVERS = "fcitx";
INPUT_METHOD = "fcitx";
SUDO_ASKPASS = pkgs.writeScript "sudo-askpass" ''
#! ${pkgs.bash}/bin/bash
${pkgs.libsecret}/bin/secret-tool lookup root password
'';
SDL_AUDIODRIVER = "pipewire,pulse,dsound";
# SDL 3
SDL_AUDIO_DRIVER = "pipewire,pulseaudio,dsound";
ALSOFT_CONF = "${config.xdg.configHome}/.config/alsoft.conf";
SDL_AUDIODRIVER = "pipewire";
# TODO: set to sdl3 compat when SDL3 releases
# this is for steam games, I set the launch options to:
# `SDL_DYNAMIC_API=$SDL2_DYNAMIC_API %command%`
# Steam itself doesn't work with SDL_DYNAMIC_API set, so it's
# a bad idea to set SDL_DYNAMIC_API globally
SDL2_DYNAMIC_API = "${pkgs.SDL2}/lib/libSDL2.so";
};
xdg.configFile."alsoft.conf".text = ''
[general]
@ -189,10 +211,6 @@
];
};
};
i18n.inputMethod = let fcitx5-qt = pkgs.libsForQt5.fcitx5-qt; in {
enabled = "fcitx5";
fcitx5.addons = with pkgs; [ fcitx5-lua fcitx5-gtk fcitx5-mozc fcitx5-configtool fcitx5-qt ];
};
services.gammastep.enable = true;
fonts.fontconfig.enable = true;
gtk = {

View file

@ -80,7 +80,7 @@ commonConfig = {
inherit modifier;
startup = [
{ command = builtins.toString (with pkgs; writeShellScript "init-wm" ''
${callPackage ./home-daemon.nix {}}/bin/dotfiles-home-daemon system76-scheduler&
${callPackage ../home-daemon {}}/bin/dotfiles-home-daemon system76-scheduler&
${gnome.zenity}/bin/zenity --password | (${keepassxc}/bin/keepassxc --pw-stdin ~/Nextcloud/keepass.kdbx&)
# nextcloud and nheko need secret service access
${nextcloud-client}/bin/nextcloud --background&
@ -166,16 +166,6 @@ in
imports = [ ./options.nix ./gui.nix ./waybar.nix ];
home.sessionVariables = {
_JAVA_AWT_WM_NONREPARENTING = "1";
GTK_IM_MODULE = "fcitx";
QT_IM_MODULE = "fcitx";
XMODIFIERS = "@im=fcitx";
SDL_IM_MODULE = "fcitx";
XIM_SERVERS = "fcitx";
INPUT_METHOD = "fcitx";
SUDO_ASKPASS = pkgs.writeScript "sudo-askpass" ''
#! ${pkgs.bash}/bin/bash
${pkgs.libsecret}/bin/secret-tool lookup root password
'';
};
xdg.configFile."xdg-desktop-portal-wlr/config".source = (pkgs.formats.ini {}).generate "xdg-desktop-portal-wlr.ini" {
screencast = {
@ -189,7 +179,7 @@ in
systemd.user.services = lib.mkIf config.wayland.windowManager.sway.enable {
gammastep.Unit.ConditionEnvironment = "WAYLAND_DISPLAY";
};
programs.mako = {
services.mako = {
enable = lib.mkDefault config.wayland.windowManager.sway.enable;
# ms
defaultTimeout = 7500;
@ -336,7 +326,9 @@ in
}; in commonConfig // swayConfig;
extraSessionCommands = ''
export WLR_RENDERER=vulkan
export SDL_VIDEODRIVER=wayland
export SDL_VIDEODRIVER=wayland,x11,kmsdrm,windows,directx
# SDL3
export SDL_VIDEO_DRIVER=wayland,x11,kmsdrm,windows
export QT_QPA_PLATFORM=wayland
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
export QT_QPA_PLATFORMTHEME=gnome
@ -345,12 +337,6 @@ in
export GTK_USE_PORTAL=1
export XDG_CURRENT_DESKTOP=sway
export XDG_SESSION_DESKTOP=sway
# TODO: set to sdl3 compat when SDL3 releases
# this is for steam games, I set the launch options to:
# `SDL_DYNAMIC_API=$SDL2_DYNAMIC_API %command%`
# Steam itself doesn't work with SDL_DYNAMIC_API set, so it's
# a bad idea to set SDL_DYNAMIC_API globally
export SDL2_DYNAMIC_API=${pkgs.SDL2.out}/lib/libSDL2.so
'';
};
services.swayidle = let swaylock-start = builtins.toString (with pkgs; writeScript "swaylock-start" ''
@ -462,7 +448,7 @@ in
};
terminal = config.terminalBin;
extraConfig = {
modi = [ "calc" "drun" "run" "ssh" ];
modi = [ "steam:${pkgs.callPackage ../rofi-steam-game-list {}}/bin/rofi-steam-game-list" "drun" "run" "ssh" ];
icon-theme = "hicolor";
drun-match-fields = [ "name" "generic" "exec" "keywords" ];
show-icons = true;

View file

@ -19,13 +19,13 @@
];
# extraPython3Packages = pyPkgs: with pyPkgs; [ python-lsp-server ];
extraConfig = ''
autocmd BufReadPost * if @% !~# '\.git[\/\\]COMMIT_EDITMSG$' && line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g`\"" | endif
syntax on
au FileType markdown set colorcolumn=73 textwidth=72
au FileType gitcommit set colorcolumn=73
highlight NormalFloat guibg=NONE
au BufReadPre * set foldmethod=syntax
au BufReadPost * folddoc foldopen!
autocmd BufReadPost * if @% !~# '\.git[\/\\]COMMIT_EDITMSG$' && line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g`\"" | endif
'';
viAlias = true;
vimAlias = true;
@ -40,6 +40,36 @@
vim-svelte
# TODO remove on next nvim update (0.8.3/0.9)
vim-nix
{ plugin = pkgs.vimUtils.buildVimPluginFrom2Nix {
pname = "vscode-nvim";
version = "2023-02-10";
src = pkgs.fetchFromGitHub {
owner = "Mofiqul";
repo = "vscode.nvim";
rev = "db9ee339b5556aa832ca58871fd18f9467a18520";
sha256 = "sha256-X2IgIjO5NNq7vJdl09hBY1TFqHlsfF1xfllKr4osILI=";
};
};
config = lua ''
require("vscode").setup({
transparent = true,
color_overrides = {
vscGray = "#745b5f",
vscViolet = "#${config.colors.magenta}",
vscBlue = "#6ddfd8",
vscDarkBlue = "#${config.colors.blue}",
vscGreen = "#${config.colors.green}",
vscBlueGreen = "#73bf88",
vscLightGreen = "#6acf6e",
vscRed = "#${config.colors.red}",
vscOrange = "#e89666",
vscLightRed = "#e64e4e",
vscYellowOrange = "#e8b166",
vscYellow = "#${config.colors.yellow}",
vscPink = "#cf83c4",
},
})
''; }
{ plugin = nvim-web-devicons;
config = lua ''
require'nvim-web-devicons'.setup {

View file

@ -8,11 +8,11 @@
"utils": "utils"
},
"locked": {
"lastModified": 1676367705,
"narHash": "sha256-un5UbRat9TwruyImtwUGcKF823rCEp4fQxnsaLFL7CM=",
"lastModified": 1677783711,
"narHash": "sha256-eq5mOVk3gv5HITtLhPjKwi8bFnOaQplA3X0WFgHnmxE=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "da72e6fc6b7dc0c3f94edbd310aae7cd95c678b5",
"rev": "b9e3a29864798d55ec1d6579ab97876bb1ee9664",
"type": "github"
},
"original": {
@ -28,11 +28,11 @@
]
},
"locked": {
"lastModified": 1676564857,
"narHash": "sha256-E2O086asoR2sFhsEvPVCdneVYNoP1JbQ4B8OcIdpdPU=",
"lastModified": 1677773085,
"narHash": "sha256-TtNq5ooEUvyHMwOnFr1nUIpqzslM3pPGmbZKe+7BU4w=",
"owner": "fufexan",
"repo": "nix-gaming",
"rev": "dbe7029d83ff8ab3db3e379c7a1fb65a309c5c54",
"rev": "d59c534258d8d9779a5497e5a6a0f4e9616365ce",
"type": "github"
},
"original": {
@ -43,10 +43,10 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1676795730,
"narHash": "sha256-X69A9BdcPTySJJ7DqS4wc8b6eqGKi32jCSyaBsz4WB0=",
"path": "/nix/store/wyanhqy905a9n7zy3syq5aiikd8y3ksl-source",
"rev": "efc59894b1ba73cb745676616c56c780383d6788",
"lastModified": 1677932085,
"narHash": "sha256-+AB4dYllWig8iO6vAiGGYl0NEgmMgGHpy9gzWJ3322g=",
"path": "/nix/store/qilsyakhi0qmjq9iwxn49g3qbl1rp5m7-source",
"rev": "3c5319ad3aa51551182ac82ea17ab1c6b0f0df89",
"type": "path"
},
"original": {
@ -56,11 +56,11 @@
},
"nur": {
"locked": {
"lastModified": 1676785553,
"narHash": "sha256-nJLp4LeU1MDfmyNkids+tIbpGx1tCwP4nI0gXOwKidg=",
"lastModified": 1678002043,
"narHash": "sha256-CKAoPQaUA+kitq4ChzlM5O3oTGHuQnlSV4hNSI1Ht0g=",
"owner": "nix-community",
"repo": "NUR",
"rev": "15ac68824a1d403aa0da7a92618b3ac379f3cf71",
"rev": "45ce037949e32a72bc65be6f20dc87fa73c5039d",
"type": "github"
},
"original": {

View file

@ -53,7 +53,7 @@
home.packages = with pkgs; [
openrgb piper
steam-run steam
osu-lazer-bin
osu-lazer-bin taisei
easyeffects
# wineWowPackages.waylandFull
winetricks

1
home/rofi-steam-game-list/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

225
home/rofi-steam-game-list/Cargo.lock generated Normal file
View file

@ -0,0 +1,225 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "block-buffer"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
dependencies = [
"generic-array",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cpufeatures"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "fork"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9788ce090af4bf8d6e8f43d3f7d12305c787456387bd2d88856fcda3aa1f0dca"
dependencies = [
"libc",
]
[[package]]
name = "generic-array"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "keyvalues-parser"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d990301996c856ea07a84bc291e76f1273db52683663efc05c8d355976897e5"
dependencies = [
"pest",
"pest_derive",
"thiserror",
]
[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "pest"
version = "2.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7"
dependencies = [
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]]
name = "proc-macro2"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rofi-steam-game-list"
version = "0.1.0"
dependencies = [
"fork",
"keyvalues-parser",
]
[[package]]
name = "sha2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ucd-trie"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"

View file

@ -0,0 +1,10 @@
[package]
name = "rofi-steam-game-list"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
keyvalues-parser = "0.1"
fork = "0.1"

View file

@ -0,0 +1,14 @@
{ lib, rustPlatform }:
rustPlatform.buildRustPackage {
pname = "rofi-steam-game-list";
version = "0.1";
src = ../rofi-steam-game-list;
cargoLock.lockFile = ../rofi-steam-game-list/Cargo.lock;
meta = with lib; {
description = "A program to list Steam games for Rofi";
license = licenses.bsd0;
};
}

View file

@ -0,0 +1,510 @@
//! I tried to create a proper parser, but those abstractions turned out to be not-so-zero cost!
//! Here's a simple version instead
#![allow(clippy::similar_names)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::needless_pass_by_value)]
use fork::daemon;
use std::collections::{HashMap, HashSet};
use std::io::{self, prelude::*};
use std::sync::mpsc;
use std::time::{Duration, SystemTime};
fn read_file(p: impl AsRef<std::path::Path>) -> io::Result<Vec<u8>> {
let p = p.as_ref().to_owned();
let mut vec = Vec::new();
let mut file = std::fs::File::open(p)?;
file.read_to_end(&mut vec)?;
Ok(vec)
}
fn read_file_s(p: impl AsRef<std::path::Path>) -> io::Result<String> {
let p = p.as_ref().to_owned();
let mut s = String::new();
let mut file = std::fs::File::open(p)?;
std::io::Read::read_to_string(&mut file, &mut s)?;
Ok(s)
}
fn write_file(p: impl AsRef<std::path::Path>, data: Vec<u8>) -> io::Result<()> {
let p = p.as_ref().to_owned();
let mut file = std::fs::File::create(p)?;
std::io::Write::write_all(&mut file, &data)
}
#[derive(Clone, Debug, PartialEq)]
pub struct AppInfoEntry {
pub app_id: u32,
pub info_state: u32,
pub last_updated: u32,
pub pics_token: u64,
pub text_vdf_sha1: [u8; 20],
pub change_number: u32,
pub info: HashMap<Vec<u8>, Value>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct AppInfo {
pub magic: u32,
pub universe: u32,
pub entries: Vec<AppInfoEntry>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Map(HashMap<Vec<u8>, Value>),
String(Vec<u8>),
}
#[allow(clippy::missing_const_for_fn)]
impl Value {
fn into_map(self) -> Option<HashMap<Vec<u8>, Self>> {
if let Self::Map(map) = self {
Some(map)
} else {
None
}
}
fn into_string(self) -> Option<Vec<u8>> {
if let Self::String(s) = self {
Some(s)
} else {
None
}
}
}
fn read_map(reader: &mut impl io::Read) -> io::Result<HashMap<Vec<u8>, Value>> {
let mut ret = HashMap::new();
let mut buf = [0u8];
let mut buf2 = [0u8; 2];
let mut buf4 = [0u8; 4];
let mut buf8 = [0u8; 8];
loop {
reader.read_exact(&mut buf)?;
let kind = buf[0];
if kind == 8 || kind == 11 {
break Ok(ret);
}
let mut key = vec![];
loop {
reader.read_exact(&mut buf)?;
if buf[0] == 0 {
break;
}
key.push(buf[0]);
}
#[allow(clippy::match_same_arms)]
match kind {
0 => {
ret.insert(key, Value::Map(read_map(reader)?));
}
1 => {
let mut s = vec![];
loop {
reader.read_exact(&mut buf)?;
if buf[0] == 0 {
break;
}
s.push(buf[0]);
}
ret.insert(key, Value::String(s));
}
2 => {
reader.read_exact(&mut buf4)?;
// ret.insert(key, Value::I32(i32::from_le_bytes(buf4)))?;
}
3 => {
reader.read_exact(&mut buf4)?;
// ret.insert(key, Value::F32(f32::from_le_bytes(buf4)))?;
}
4 => {
reader.read_exact(&mut buf4)?;
// ret.insert(key, Value::Pointer(i32::from_le_bytes(buf4)))?;
}
5 => {
let mut s = vec![0u16; 2];
loop {
reader.read_exact(&mut buf2)?;
if buf2 == [0u8, 0u8] {
break;
}
s.extend_from_slice(&[u16::from_le_bytes(buf2)]);
}
// utf-8 is used instead of utf-16 here
// ret.insert(key, Value::WideString(s))?;
}
7 => {
reader.read_exact(&mut buf8)?;
// ret.insert(key, Value::U64(u64::from_le_bytes(buf8)))?;
}
10 => {
reader.read_exact(&mut buf8)?;
// ret.insert(key, Value::I64(i64::from_le_bytes(buf8)))?;
}
n => panic!("invalid vdf data type: {n}"),
}
}
}
fn read_app_info(reader: &mut impl io::Read) -> io::Result<AppInfo> {
let mut buf4 = [0u8; 4];
// let mut buf8 = [0u8; 8];
// let mut buf20 = [0u8; 20];
let mut buf64 = [0u8; 64];
reader.read_exact(&mut buf4)?;
assert_eq!(buf4, [0x28, 0x44, 0x56, 0x07]);
reader.read_exact(&mut buf4)?;
assert_eq!(u32::from_le_bytes(buf4), 1);
let mut ret = AppInfo {
magic: 0x0756_4428,
universe: 1,
entries: vec![],
};
loop {
reader.read_exact(&mut buf4)?;
let app_id = u32::from_le_bytes(buf4);
if app_id == 0 {
break Ok(ret);
}
let mut entry = AppInfoEntry {
app_id,
info_state: 0,
last_updated: 0,
pics_token: 0,
text_vdf_sha1: [0u8; 20],
change_number: 0,
info: HashMap::new(),
};
reader.read_exact(&mut buf64[..4 * 3 + 8 + 20 + 4 + 20])?;
// reader.read_exact(&mut buf4)?;
// size
// reader.read_exact(&mut buf4)?;
// entry.info_state = u32::from_le_bytes(buf4);
// reader.read_exact(&mut buf4)?;
// entry.last_updated = u32::from_le_bytes(buf4);
// reader.read_exact(&mut buf8)?;
// entry.pics_token = u64::from_le_bytes(buf8);
// reader.read_exact(&mut buf20)?;
// entry.text_vdf_sha1 = buf20;
// reader.read_exact(&mut buf4)?;
// entry.change_number = u32::from_le_bytes(buf4);
// reader.read_exact(&mut buf20)?;
// bin sha1
entry.info = read_map(reader)?;
ret.entries.push(entry);
}
}
fn home() -> String {
std::env::var("HOME").unwrap()
}
fn xdg_home() -> String {
std::env::var("XDG_DATA_HOME").unwrap_or_else(|_| home() + "/.local/share")
}
fn xdg_cache() -> String {
std::env::var("XDG_CACHE_HOME").unwrap_or_else(|_| home() + "/.cache")
}
fn cache_dir() -> String {
let dir = xdg_cache() + "/rofi-steam-game-list";
let _ = std::fs::create_dir_all(&dir);
dir
}
fn history_dir() -> String {
let dir = xdg_home() + "/rofi-steam-game-list";
let _ = std::fs::create_dir_all(&dir);
dir
}
fn history(k: &str) -> HashMap<u32, u32> {
let dir = history_dir();
let mut ret = HashMap::new();
let Ok(data) = read_file(dir + "/history_" + k) else {
return ret;
};
if data.len() < 8 {
return ret;
}
let count = u32::from_le_bytes(data[4..8].try_into().unwrap());
let data = &mut &data[8..];
let mut buf4 = [0u8; 4];
for _ in 0..count {
if std::io::Read::read_exact(data, &mut buf4).is_err() {
return ret;
}
let k = u32::from_le_bytes(buf4);
if std::io::Read::read_exact(data, &mut buf4).is_err() {
return ret;
}
let v = u32::from_le_bytes(buf4);
ret.insert(k, v);
}
ret
}
fn write_history(m: &HashMap<u32, u32>, k: &str) {
let dir = history_dir();
let mut data = vec![];
data.extend_from_slice(&[0; 4]);
data.extend_from_slice(&(m.len() as u32).to_le_bytes());
for (k, v) in m.iter() {
data.extend_from_slice(&k.to_le_bytes());
data.extend_from_slice(&v.to_le_bytes());
}
let _ = write_file(dir + "/history_" + k, data);
}
fn read_time(s: String) -> io::Result<SystemTime> {
std::fs::metadata(s + "/Steam/appcache/appinfo.vdf")?.modified()
}
fn read_appinfo(target_type: String, s: String) -> io::Result<(SystemTime, Vec<(u32, String)>)> {
let time = read_time(s.clone())?;
let vec = read_file(s + "/Steam/appcache/appinfo.vdf")?;
let data = read_app_info(&mut &vec[..])?;
let mut ret = Vec::new();
for mut info in data.entries {
if let Some(mut x) = info
.info
.remove(&b"appinfo"[..])
.and_then(Value::into_map)
.and_then(|mut x| x.remove(&b"common"[..]))
.and_then(Value::into_map)
{
if let Some(mut t) = x
.remove(&b"type"[..])
.and_then(Value::into_string)
.and_then(|x| String::from_utf8(x).ok())
{
if let Some(n) = x
.remove(&b"name"[..])
.and_then(Value::into_string)
.and_then(|x| String::from_utf8(x).ok())
{
t.make_ascii_lowercase();
if t == target_type {
ret.push((info.app_id, n));
}
}
}
}
}
Ok((time, ret))
}
fn list_appids(s: &str) -> HashSet<u32> {
let Ok(data) = read_file_s(s.to_owned() + "/Steam/steamapps/libraryfolders.vdf") else {
return HashSet::new();
};
let ret = keyvalues_parser::Vdf::parse(&data)
.unwrap()
.value
.get_obj()
.unwrap()
.values()
.flat_map(|x| {
x.iter().flat_map(|x| {
x.get_obj().unwrap().get("apps").into_iter().flat_map(|x| {
x.iter().flat_map(|x| {
x.get_obj()
.unwrap()
.keys()
.filter_map(|x| x.parse::<u32>().ok())
})
})
})
})
.collect::<HashSet<u32>>();
ret
}
fn cache_time(k: &str) -> std::io::Result<SystemTime> {
let path = cache_dir() + "/type_" + k;
let mut file = std::fs::File::open(path)?;
let mut data = Vec::new();
file.read_to_end(&mut data)?;
if data.len() >= 12 {
let (_, data) = data.split_at(4);
let (first, _data) = data.split_at(8);
let time = SystemTime::UNIX_EPOCH
+ Duration::from_millis(u64::from_le_bytes(first.try_into().unwrap()));
Ok(time)
} else {
Err(io::Error::from(io::ErrorKind::Other))
}
}
fn read_cache(k: &str) -> io::Result<(SystemTime, Vec<(u32, String)>)> {
let path = cache_dir() + "/type_" + k;
let mut file = std::fs::File::open(path)?;
let mut data = Vec::new();
file.read_to_end(&mut data)?;
if data.len() >= 16 {
let (_, data) = data.split_at(4);
let (first, data) = data.split_at(8);
let time = SystemTime::UNIX_EPOCH
+ Duration::from_millis(u64::from_le_bytes(first.try_into().unwrap()));
let (first, data) = data.split_at(4);
let count = u32::from_le_bytes(first.try_into().unwrap());
let data = &mut &data[..];
let mut buf4 = [0u8; 4];
let mut buf1 = [0u8; 1];
let mut ret = Vec::with_capacity(count as usize);
for _ in 0..count {
std::io::Read::read_exact(data, &mut buf4)?;
std::io::Read::read_exact(data, &mut buf1)?;
let len = if buf1[0] == 255 {
std::io::Read::read_exact(data, &mut buf1)?;
255 + buf1[0] as usize
} else {
buf1[0] as usize
};
let mut buf = vec![0; len];
std::io::Read::read_exact(data, &mut buf)?;
if let Ok(s) = String::from_utf8(buf) {
ret.push((u32::from_le_bytes(buf4), s));
}
}
Ok((time, ret))
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"invalid app id cache format",
))
}
}
fn write_cache(k: &str, time: SystemTime, ids: &[(u32, String)]) {
let mut data = Vec::new();
data.extend_from_slice(&[0; 4]);
data.extend_from_slice(
&(time
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis() as u64)
.to_le_bytes(),
);
data.extend_from_slice(&(ids.len() as u32).to_le_bytes());
for (id, s) in ids {
if s.len() > u8::MAX as usize + u8::MAX as usize {
continue;
}
data.extend_from_slice(&id.to_le_bytes());
if s.len() > u8::MAX as usize {
data.extend_from_slice(&255u8.to_le_bytes());
data.extend_from_slice(&((s.len() - u8::MAX as usize) as u8).to_le_bytes());
} else {
data.extend_from_slice(&(s.len() as u8).to_le_bytes());
}
data.extend_from_slice(s.as_bytes());
}
let path = cache_dir() + "/type_" + k;
if let Ok(mut file) = std::fs::File::create(path) {
let _ = file.write_all(&data);
}
}
struct PendingFut;
impl std::future::Future for PendingFut {
type Output = ();
fn poll(self: std::pin::Pin<&mut Self>, _: &mut std::task::Context<'_>) -> std::task::Poll<()> {
std::task::Poll::Pending
}
}
fn main() {
let target_type = std::env::args().nth(1).map_or_else(
|| "game".to_owned(),
|mut x| {
x.make_ascii_lowercase();
x
},
);
if let Ok(appid) = std::env::var("ROFI_INFO") {
let _ = daemon(true, false);
let mut cmd = std::process::Command::new("xdg-open")
.arg(&format!("steam://rungameid/{appid}"))
.spawn()
.unwrap();
if let Ok(x) = appid.parse::<u32>() {
let mut history = history(&target_type);
history.entry(x).and_modify(|curr| *curr += 1).or_insert(1);
write_history(&history, &target_type);
}
let _ = cmd.wait();
return;
}
/*
* Flow1: read app info -> print app info -> write cache
* Flow2: read cache -> print cache -> check app info mod time -> write cache
* */
let xdg_home = xdg_home();
let xdg_home2 = xdg_home.clone();
let xdg_home3 = xdg_home;
let target_type2 = target_type.clone();
let target_type3 = target_type.clone();
let target_type4 = target_type.clone();
let a = std::thread::spawn(move || history(&target_type3));
let (tx0, rx0) = mpsc::channel();
let (tx2, rx2) = mpsc::channel();
let b = std::thread::spawn(move || {
let tx1 = tx0.clone();
std::thread::spawn(move || {
tx0.send(
read_appinfo(target_type2, crate::xdg_home()).map_or_else(|_| {
let _ = tx2.send(None);
None
}, |info| {
let _ = tx2.send(Some(info.clone()));
Some((info, false))
})
)
});
std::thread::spawn(move || tx1.send(read_cache(&target_type4).ok().map(|x| (x, true))));
#[allow(clippy::same_functions_in_if_condition)]
if let Ok(Some(x)) = rx0.recv() {
x
} else if let Ok(Some(x)) = rx0.recv() {
x
} else {
panic!()
}
});
let c = std::thread::spawn(move || list_appids(&xdg_home2));
let history = a.join().unwrap();
let ((time, app_info), is_cache) = b.join().unwrap();
let installed_games = c.join().unwrap();
let mut app_info_2 = app_info
.iter()
.filter_map(|x| {
if installed_games.contains(&x.0) {
Some(x.clone())
} else {
None
}
})
.collect::<Vec<_>>();
app_info_2.sort_by_key(|x| u32::MAX - history.get(&x.0).unwrap_or(&0));
for (app_id, n) in &app_info_2 {
let icon = format!("{xdg_home3}/Steam/appcache/librarycache/{app_id}_icon.jpg");
print!("{n}\0info\x1f{app_id}");
if std::fs::metadata(&icon).is_ok() {
print!("\x1ficon\x1f{icon}");
}
println!();
}
let _ = daemon(true, false);
if is_cache {
if read_time(xdg_home3).unwrap() <= time {
return;
}
if let Some((time, app_info)) = rx2.recv().unwrap() {
write_cache(&target_type, time, &app_info);
}
} else {
if let Ok(ctime) = cache_time(&target_type) {
if time <= ctime {
return;
}
}
write_cache(&target_type, time, &app_info);
}
}

View file

@ -94,13 +94,11 @@
"vfio"
"vfio_iommu_type1"
"vfio_pci"
"vfio_virqfd"
] else []);
initrd.availableKernelModules = lib.mkIf (!cfg.passGpuAtBoot) [
"vfio"
"vfio_iommu_type1"
"vfio_pci"
"vfio_virqfd"
];
extraModulePackages =
with config.boot.kernelPackages;
@ -118,8 +116,7 @@
];
kernelModules = [
"vhost-net"
] ++ (if cfg.passGpuAtBoot then [] else [ "vfio_virqfd" ])
++ (if enableIvshmem then [ "kvmfr" ] else []);
] ++ (if enableIvshmem then [ "kvmfr" ] else []);
};
services.udev.extraRules = lib.mkIf enableIvshmem
(lib.concatStringsSep

View file

@ -52,11 +52,11 @@
]
},
"locked": {
"lastModified": 1676564857,
"narHash": "sha256-E2O086asoR2sFhsEvPVCdneVYNoP1JbQ4B8OcIdpdPU=",
"lastModified": 1677773085,
"narHash": "sha256-TtNq5ooEUvyHMwOnFr1nUIpqzslM3pPGmbZKe+7BU4w=",
"owner": "fufexan",
"repo": "nix-gaming",
"rev": "dbe7029d83ff8ab3db3e379c7a1fb65a309c5c54",
"rev": "d59c534258d8d9779a5497e5a6a0f4e9616365ce",
"type": "github"
},
"original": {
@ -67,11 +67,11 @@
},
"nixos-hardware": {
"locked": {
"lastModified": 1676775543,
"narHash": "sha256-VI0e60l94RY9Sc90OwDZpOf/nyLy41n2ULK6I6YkoP8=",
"lastModified": 1677949148,
"narHash": "sha256-dEdcn+UYs8TUK3VTNCQk9TsJapJLEq50A4q7eC3/PTU=",
"owner": "NixOS",
"repo": "nixos-hardware",
"rev": "525177a78023e1363bee482f520d4f2471ada03a",
"rev": "d63e86cbed3d399c4162594943bd8c1d8392e550",
"type": "github"
},
"original": {
@ -82,15 +82,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1676795730,
"narHash": "sha256-X69A9BdcPTySJJ7DqS4wc8b6eqGKi32jCSyaBsz4WB0=",
"lastModified": 1677932085,
"narHash": "sha256-+AB4dYllWig8iO6vAiGGYl0NEgmMgGHpy9gzWJ3322g=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "efc59894b1ba73cb745676616c56c780383d6788",
"rev": "3c5319ad3aa51551182ac82ea17ab1c6b0f0df89",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
@ -113,11 +114,11 @@
]
},
"locked": {
"lastModified": 1676773870,
"narHash": "sha256-RhG7QmA14xih1lv6SB2WDVER4fbJ1cLwr0ntCpIjKbQ=",
"lastModified": 1677983714,
"narHash": "sha256-2A5uDpF0vN4w9tvo5N+918bK0yRYfg4FdNZ/qccgH6s=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "a6fa42390d46ef1326fbe98288b65d3b586870da",
"rev": "1a9f6285d441ff438a6a1422dc3fde109d8615bf",
"type": "github"
},
"original": {

View file

@ -2,8 +2,7 @@
description = "NixOS configuration";
inputs = {
# nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixpkgs.url = "github:nixos/nixpkgs";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
utils.url = "github:gytis-ivaskevicius/flake-utils-plus";
nixos-hardware.url = "github:NixOS/nixos-hardware";
impermanence.url = "github:nix-community/impermanence";

View file

@ -43,6 +43,7 @@ in {
# resume_offset = $(btrfs inspect-internal map-swapfile -r path/to/swapfile)
"resume_offset=533760"
"fbcon=font:TER16x32"
"consoleblank=60"
];
cleanTmpDir = true;
loader = {