Fix NixOS GUI issues

Fixes #380, #414
This commit is contained in:
2026-04-13 09:27:21 +03:00
parent 489f16e462
commit 071567fa73
4 changed files with 124 additions and 63 deletions
+27 -30
View File
@@ -18,18 +18,30 @@
"i686-windows"
"x86_64-windows"
];
in flake-utils.lib.eachSystem systems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
lib = pkgs.lib;
naersk-lib = naersk.lib.${system};
isLinux = pkgs.stdenv.hostPlatform.isLinux;
alfis = { webgui ? true, doh ? true, edge ? false }:
guiBuildInputs = lib.optionals isLinux (with pkgs; [
gtk3
webkitgtk_4_1
xdotool
libayatana-appindicator
]);
guiNativeBuildInputs = [ pkgs.pkg-config ]
++ lib.optionals isLinux [ pkgs.makeWrapper pkgs.wrapGAppsHook ];
guiRuntimeTools = lib.optionals isLinux [ pkgs.kdePackages.kdialog ];
guiRuntimeLibPath = lib.optionalString isLinux (lib.makeLibraryPath guiBuildInputs);
alfis = { webgui ? true, doh ? true }:
let
features = builtins.concatStringsSep " " (builtins.concatMap
({ option, features }: pkgs.lib.optionals option features) [
({ option, features }: lib.optionals option features) [
{
option = webgui;
features = [ "webgui" ];
@@ -38,55 +50,40 @@
option = doh;
features = [ "doh" ];
}
{
option = edge;
features = [ "edge" ];
}
]);
in naersk-lib.buildPackage {
pname = "alfis";
nativeBuildInputs = with pkgs; [ pkg-config webkitgtk kdialog ];
dontWrapQtApps = true;
root = ./.;
nativeBuildInputs = guiNativeBuildInputs;
buildInputs = guiBuildInputs;
cargoBuildOptions = opts:
opts ++ [ "--no-default-features" ]
++ [ "--features" ''"${features}"'' ];
root = ./.;
++ lib.optionals (features != "") [ "--features" features ];
preFixup = lib.optionalString isLinux ''
gappsWrapperArgs+=(--prefix PATH : "${lib.makeBinPath guiRuntimeTools}")
gappsWrapperArgs+=(--prefix LD_LIBRARY_PATH : "${guiRuntimeLibPath}")
'';
};
isWindows = builtins.elem system [ "i686-windows" "x86_64-windows" ];
in rec {
packages = {
alfis = alfis {
webgui = true;
doh = true;
edge = false;
};
alfisWithoutGUI = alfis {
webgui = false;
doh = true;
edge = false;
};
} // pkgs.lib.optionalAttrs isWindows {
alfisEdge = alfis {
webgui = false;
doh = true;
edge = true;
};
};
defaultPackage = packages.alfis;
apps = with flake-utils.lib;
{
apps = with flake-utils.lib; {
alfis = mkApp { drv = packages.alfis; };
alfisWithoutGUI = mkApp { drv = packages.alfisWithoutGUI; };
} // pkgs.lib.optionalAttrs isWindows {
alfisEdge = mkApp { drv = packages.alfisEdge; };
};
defaultApp = apps.alfis;
devShell = import ./shell.nix { inherit pkgs; };
});
}
+17 -2
View File
@@ -1,6 +1,21 @@
{ pkgs ? import <nixpkgs> { } }:
let
runtimeLibs = with pkgs; [
gtk3
webkitgtk_4_1
xdotool
libayatana-appindicator
];
packages = with pkgs; [
cargo
rustc
pkg-config
kdePackages.kdialog
] ++ runtimeLibs;
in
pkgs.mkShell {
buildInputs =
[ pkgs.cargo pkgs.rustc pkgs.webkitgtk pkgs.pkg-config pkgs.kdialog ];
buildInputs = packages;
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath runtimeLibs;
}
+14
View File
@@ -148,6 +148,20 @@ fn main() {
no_gui = true;
}
#[cfg(all(feature = "webgui", target_os = "linux"))]
if !no_gui {
let running_via_sudo = env::var_os("SUDO_UID").is_some();
let has_graphical_session = env::var_os("DISPLAY").is_some() || env::var_os("WAYLAND_DISPLAY").is_some();
if running_via_sudo {
warn!(target: LOG_TARGET_MAIN, "Running GUI via sudo is not supported on Linux, starting without GUI");
no_gui = true;
} else if !has_graphical_session {
warn!(target: LOG_TARGET_MAIN, "No graphical session detected, starting without GUI");
no_gui = true;
}
}
#[cfg(windows)]
if opt_matches.opt_present("service") {
let appdata = env::var("PROGRAMDATA").expect("Failed to get APPDATA directory");
+43 -8
View File
@@ -3,6 +3,7 @@ extern crate serde;
extern crate serde_json;
extern crate tinyfiledialogs as tfd;
use std::panic::{self, AssertUnwindSafe};
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
@@ -61,13 +62,8 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, hid
#[cfg(not(target_os = "windows"))]
let icon = load_icon_from_png();
let tray_icon = TrayIconBuilder::new()
.with_menu(Box::new(tray_menu))
.with_tooltip(&title)
.with_icon(icon)
.with_menu_on_left_click(false)
.build()
.unwrap();
let tray_icon = build_tray_icon(&title, tray_menu, icon);
let tray_available = tray_icon.is_some();
let window_size = tao::dpi::LogicalSize::new(1024, 720);
// Get primary monitor and calculate center position
@@ -90,7 +86,7 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, hid
.with_inner_size(window_size)
.with_min_inner_size(tao::dpi::LogicalSize::new(773, 350))
.with_resizable(true)
.with_visible(!hide);
.with_visible(!hide || !tray_available);
if let Some(position) = position {
builder = builder.with_position(position);
@@ -319,6 +315,7 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, hid
true
});
if tray_available {
let proxy = event_loop.create_proxy();
TrayIconEvent::set_event_handler(Some(move |event| {
let _ = proxy.send_event(UserEvent::TrayIconEvent(event));
@@ -328,6 +325,7 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, hid
MenuEvent::set_event_handler(Some(move |event| {
let _ = proxy.send_event(UserEvent::MenuEvent(event));
}));
}
let proxy = event_loop.create_proxy();
@@ -340,7 +338,14 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, hid
event: WindowEvent::CloseRequested,
..
} => {
if tray_available {
window.set_visible(false);
} else {
info!("Interface closed, exiting");
post(Event::ActionQuit);
thread::sleep(Duration::from_millis(100));
*control_flow = ControlFlow::Exit;
}
}
TaoEvent::UserEvent(user_event) => {
let wv = webview_clone.lock().unwrap();
@@ -364,6 +369,7 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, hid
show_warning(&wv, &text);
}
UserEvent::TrayIconEvent(event) => {
if let Some(tray_icon) = tray_icon.as_ref() {
match event {
TrayIconEvent::DoubleClick { button, .. } => {
if button == tray_icon::MouseButton::Left {
@@ -379,6 +385,7 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, hid
_ => {}
}
}
}
UserEvent::MenuEvent(event) => {
if event.id == show_item.id() {
window.set_visible(true);
@@ -396,6 +403,34 @@ pub fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>, hid
});
}
fn build_tray_icon(title: &str, tray_menu: Menu, icon: tray_icon::Icon) -> Option<tray_icon::TrayIcon> {
let previous_hook = panic::take_hook();
panic::set_hook(Box::new(|_| {}));
let result = panic::catch_unwind(AssertUnwindSafe(|| {
TrayIconBuilder::new()
.with_menu(Box::new(tray_menu))
.with_tooltip(title)
.with_icon(icon)
.with_menu_on_left_click(false)
.build()
}));
panic::set_hook(previous_hook);
match result {
Ok(Ok(tray_icon)) => Some(tray_icon),
Ok(Err(error)) => {
warn!("Tray icon is unavailable: {error}");
None
}
Err(_) => {
warn!("Tray icon is unavailable: failed to load appindicator library, continuing without tray support");
None
}
}
}
#[derive(Debug)]
enum UserEvent {
EvalJs(String),