Compare commits
12 Commits
boxy-app-l
...
569ac528a5
| Author | SHA1 | Date | |
|---|---|---|---|
| 569ac528a5 | |||
| 6cc8fa4f5d | |||
| 67a82f14fd | |||
| 4b68e3f051 | |||
| 81a3657759 | |||
| 32e1b81034 | |||
| 6f00c72540 | |||
| d26007aa61 | |||
| 1caa8bba3e | |||
| d3cb09040a | |||
| 4bfacffa17 | |||
| a6961f05ca |
47
flake.lock
generated
47
flake.lock
generated
@@ -23,11 +23,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1752428473,
|
"lastModified": 1761423376,
|
||||||
"narHash": "sha256-IsE7fdAYbRlZuc0H5FtPfhhuHvlxnDGoAxdlnjpVNCU=",
|
"narHash": "sha256-pMy3cnUFfue4vz/y0jx71BfcPGxZf+hk/DtnzWvfU0c=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "1fad66b55144ab6beaecd900172a21ac3c34dc52",
|
"rev": "a1f695665771841a988afc965526cbf99160cd77",
|
||||||
"revCount": 10,
|
"revCount": 11,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.johnogle.info/johno/google-cookie-retrieval.git"
|
"url": "https://git.johnogle.info/johno/google-cookie-retrieval.git"
|
||||||
},
|
},
|
||||||
@@ -43,15 +43,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1759172751,
|
"lastModified": 1758463745,
|
||||||
"narHash": "sha256-E8W8sRXfrvkFW26GuuiWq6QfReU7m5+cngwHuRo/3jc=",
|
"narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "12fa8548feefa9a10266ba65152fd1a787cdde8f",
|
"rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
|
"ref": "release-25.05",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -63,11 +64,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1758805352,
|
"lastModified": 1761339987,
|
||||||
"narHash": "sha256-BHdc43Lkayd+72W/NXRKHzX5AZ+28F3xaUs3a88/Uew=",
|
"narHash": "sha256-IUaawVwItZKi64IA6kF6wQCLCzpXbk2R46dHn8sHkig=",
|
||||||
"owner": "nix-darwin",
|
"owner": "nix-darwin",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "c48e963a5558eb1c3827d59d21c5193622a1477c",
|
"rev": "7cd9aac79ee2924a85c211d21fafd394b06a38de",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -82,11 +83,11 @@
|
|||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1758785683,
|
"lastModified": 1760536587,
|
||||||
"narHash": "sha256-mRn51IeEBXeNh5a6xNLylk4PKBX0s/QQxgkEbYoPq/w=",
|
"narHash": "sha256-wfWqt+igns/VazjPLkyb4Z/wpn4v+XIjUeI3xY/1ENg=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "NixOS-WSL",
|
"repo": "NixOS-WSL",
|
||||||
"rev": "1bfb978f2f6261b6086e04af17f9418e1fe36d70",
|
"rev": "f98ee1de1fa36eca63c67b600f5d617e184e82ea",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -98,11 +99,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1758277210,
|
"lastModified": 1759733170,
|
||||||
"narHash": "sha256-iCGWf/LTy+aY0zFu8q12lK8KuZp7yvdhStehhyX1v8w=",
|
"narHash": "sha256-TXnlsVb5Z8HXZ6mZoeOAIwxmvGHp1g4Dw89eLvIwKVI=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "8eaee110344796db060382e15d3af0a9fc396e0e",
|
"rev": "8913c168d1c56dc49a7718685968f38752171c3b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -114,16 +115,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1759036355,
|
"lastModified": 1761173472,
|
||||||
"narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=",
|
"narHash": "sha256-m9W0dYXflzeGgKNravKJvTMR4Qqa2MVD11AwlGMufeE=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127",
|
"rev": "c8aa8cc00a5cb57fada0851a038d35c08a36a2bb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"ref": "nixos-unstable",
|
"ref": "nixos-25.05",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -138,11 +139,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1759157415,
|
"lastModified": 1761078382,
|
||||||
"narHash": "sha256-Fg8cOnVoIe0uQ38UpR6XZzRCwDsjjozVwfevW9yCLI0=",
|
"narHash": "sha256-JNJesbe9MMN1Brq41BHEpuH+Z+Zg74y/nI5AFZX84Vw=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "plasma-manager",
|
"repo": "plasma-manager",
|
||||||
"rev": "df5b3e6da631f732c26c6044c7cccb8706b4f479",
|
"rev": "27dfa61b64d0cdb8e4ba6f3aaa4d4e067d64cb5c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
description = "A very basic flake";
|
description = "A very basic flake";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";
|
||||||
nixos-wsl.url = "github:nix-community/NixOS-WSL/main";
|
nixos-wsl.url = "github:nix-community/NixOS-WSL/main";
|
||||||
|
|
||||||
nix-darwin = {
|
nix-darwin = {
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
url = "github:nix-community/home-manager";
|
url = "github:nix-community/home-manager/release-25.05";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
let
|
let
|
||||||
customPkgs = pkgs.callPackage ../packages {};
|
customPkgs = pkgs.callPackage ../packages {};
|
||||||
|
leader = "cmd"; # Change this to experiment with different leader keys (e.g., "cmd", "ctrl")
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Provide arguments to role modules
|
# Provide arguments to role modules
|
||||||
@@ -13,6 +14,26 @@ in
|
|||||||
home.homeDirectory = lib.mkForce "/Users/johno";
|
home.homeDirectory = lib.mkForce "/Users/johno";
|
||||||
home.stateVersion = "24.05";
|
home.stateVersion = "24.05";
|
||||||
|
|
||||||
|
# System packages
|
||||||
|
home.packages = with pkgs; [
|
||||||
|
autoraise
|
||||||
|
];
|
||||||
|
|
||||||
|
# Auto-start autoraise on login
|
||||||
|
launchd.agents.autoraise = {
|
||||||
|
enable = true;
|
||||||
|
config = {
|
||||||
|
ProgramArguments = [
|
||||||
|
"${pkgs.autoraise}/bin/AutoRaise"
|
||||||
|
"-pollMillis" "50"
|
||||||
|
"-delay" "2"
|
||||||
|
"-focusDelay" "2"
|
||||||
|
];
|
||||||
|
RunAtLoad = true;
|
||||||
|
KeepAlive = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Override Darwin-incompatible settings from base role
|
# Override Darwin-incompatible settings from base role
|
||||||
programs.rbw.settings.pinentry = lib.mkForce pkgs.pinentry_mac;
|
programs.rbw.settings.pinentry = lib.mkForce pkgs.pinentry_mac;
|
||||||
|
|
||||||
@@ -43,6 +64,86 @@ in
|
|||||||
|
|
||||||
home.shell.enableShellIntegration = true;
|
home.shell.enableShellIntegration = true;
|
||||||
|
|
||||||
|
# TODO: Move this to its own role and/or module
|
||||||
|
programs.aerospace = {
|
||||||
|
enable = true;
|
||||||
|
launchd.enable = true;
|
||||||
|
userSettings.mode.main.binding = {
|
||||||
|
"${leader}-slash" = "layout tiles horizontal vertical";
|
||||||
|
"${leader}-comma" = "layout accordion horizontal vertical";
|
||||||
|
"${leader}-shift-q" = "close";
|
||||||
|
"${leader}-shift-f" = "fullscreen";
|
||||||
|
"${leader}-h" = "focus left";
|
||||||
|
"${leader}-j" = "focus down";
|
||||||
|
"${leader}-k" = "focus up";
|
||||||
|
"${leader}-l" = "focus right";
|
||||||
|
"${leader}-shift-h" = "move left";
|
||||||
|
"${leader}-shift-j" = "move down";
|
||||||
|
"${leader}-shift-k" = "move up";
|
||||||
|
"${leader}-shift-l" = "move right";
|
||||||
|
"${leader}-minus" = "resize smart -50";
|
||||||
|
"${leader}-equal" = "resize smart +50";
|
||||||
|
"${leader}-1" = "workspace 1";
|
||||||
|
"${leader}-2" = "workspace 2";
|
||||||
|
"${leader}-3" = "workspace 3";
|
||||||
|
"${leader}-4" = "workspace 4";
|
||||||
|
"${leader}-5" = "workspace 5";
|
||||||
|
"${leader}-6" = "workspace 6";
|
||||||
|
"${leader}-7" = "workspace 7";
|
||||||
|
"${leader}-8" = "workspace 8";
|
||||||
|
"${leader}-9" = "workspace 9";
|
||||||
|
"${leader}-0" = "workspace 10";
|
||||||
|
"${leader}-shift-1" = "move-node-to-workspace 1";
|
||||||
|
"${leader}-shift-2" = "move-node-to-workspace 2";
|
||||||
|
"${leader}-shift-3" = "move-node-to-workspace 3";
|
||||||
|
"${leader}-shift-4" = "move-node-to-workspace 4";
|
||||||
|
"${leader}-shift-5" = "move-node-to-workspace 5";
|
||||||
|
"${leader}-shift-6" = "move-node-to-workspace 6";
|
||||||
|
"${leader}-shift-7" = "move-node-to-workspace 7";
|
||||||
|
"${leader}-shift-8" = "move-node-to-workspace 8";
|
||||||
|
"${leader}-shift-9" = "move-node-to-workspace 9";
|
||||||
|
"${leader}-shift-0" = "move-node-to-workspace 10";
|
||||||
|
"${leader}-tab" = "workspace-back-and-forth";
|
||||||
|
"${leader}-shift-tab" = "move-workspace-to-monitor --wrap-around next";
|
||||||
|
|
||||||
|
"${leader}-enter" = ''
|
||||||
|
exec-and-forget osascript <<'APPLESCRIPT'
|
||||||
|
tell application "Terminal"
|
||||||
|
set newWindow to make new window
|
||||||
|
activate
|
||||||
|
tell newWindow to set index to 1
|
||||||
|
end tell
|
||||||
|
APPLESCRIPT
|
||||||
|
'';
|
||||||
|
|
||||||
|
"${leader}-shift-enter" = ''
|
||||||
|
exec-and-forget osascript <<'APPLESCRIPT'
|
||||||
|
tell application "Google Chrome"
|
||||||
|
set newWindow to make new window
|
||||||
|
activate
|
||||||
|
tell newWindow to set index to 1
|
||||||
|
end tell
|
||||||
|
APPLESCRIPT
|
||||||
|
'';
|
||||||
|
|
||||||
|
"${leader}-shift-e" = "exec-and-forget zsh --login -c \"emacsclient -c -n\"";
|
||||||
|
|
||||||
|
"${leader}-i" = "mode service";
|
||||||
|
};
|
||||||
|
|
||||||
|
userSettings.mode.service.binding = {
|
||||||
|
esc = ["reload-config" "mode main"];
|
||||||
|
r = ["flatten-workspace-tree" "mode main"]; # reset layout
|
||||||
|
f = ["layout floating tiling" "mode main"]; # Toggle between floating and tiling layout
|
||||||
|
backspace = ["close-all-windows-but-current" "mode main"];
|
||||||
|
|
||||||
|
"${leader}-shift-h" = ["join-with left" "mode main"];
|
||||||
|
"${leader}-shift-j" = ["join-with down" "mode main"];
|
||||||
|
"${leader}-shift-k" = ["join-with up" "mode main"];
|
||||||
|
"${leader}-shift-l" = ["join-with right" "mode main"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
home.roles = {
|
home.roles = {
|
||||||
base.enable = true;
|
base.enable = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ let
|
|||||||
terminal = "kitty";
|
terminal = "kitty";
|
||||||
defaultWorkspace = "workspace number 1";
|
defaultWorkspace = "workspace number 1";
|
||||||
|
|
||||||
|
bars = [{
|
||||||
|
position = "bottom";
|
||||||
|
statusCommand = "i3status";
|
||||||
|
trayOutput = "primary"; # Enable system tray on primary output
|
||||||
|
}];
|
||||||
|
|
||||||
keybindings = {
|
keybindings = {
|
||||||
"${shared_config.modifier}+Return" = "exec ${terminal}";
|
"${shared_config.modifier}+Return" = "exec ${terminal}";
|
||||||
"${shared_config.modifier}+Shift+q" = "kill";
|
"${shared_config.modifier}+Shift+q" = "kill";
|
||||||
@@ -113,6 +119,26 @@ in {
|
|||||||
"${shared_config.modifier}+Shift+e" =
|
"${shared_config.modifier}+Shift+e" =
|
||||||
"exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
|
"exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
|
||||||
};
|
};
|
||||||
|
startup = [
|
||||||
|
# GNOME polkit authentication agent
|
||||||
|
{
|
||||||
|
command = "/run/current-system/sw/libexec/polkit-gnome-authentication-agent-1";
|
||||||
|
always = false;
|
||||||
|
notification = false;
|
||||||
|
}
|
||||||
|
# Picom compositor for smooth rendering and no tearing (important for Nvidia)
|
||||||
|
{
|
||||||
|
command = "picom -b";
|
||||||
|
always = false;
|
||||||
|
notification = false;
|
||||||
|
}
|
||||||
|
# NetworkManager system tray applet
|
||||||
|
{
|
||||||
|
command = "nm-applet";
|
||||||
|
always = false;
|
||||||
|
notification = false;
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ in
|
|||||||
|
|
||||||
programs.ssh = {
|
programs.ssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
addKeysToAgent = "yes";
|
||||||
matchBlocks = {
|
matchBlocks = {
|
||||||
"nucdeb1" = {
|
"nucdeb1" = {
|
||||||
hostname = "nucdeb1.oglehome";
|
hostname = "nucdeb1.oglehome";
|
||||||
user = "root";
|
user = "root";
|
||||||
addKeysToAgent = "yes";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ in
|
|||||||
home.packages = [
|
home.packages = [
|
||||||
# Communication apps
|
# Communication apps
|
||||||
pkgs.element-desktop
|
pkgs.element-desktop
|
||||||
pkgs.fluffychat
|
#pkgs.fluffychat #marked insecure as of nixos 25.05
|
||||||
pkgs.nextcloud-talk-desktop
|
pkgs.nextcloud-talk-desktop
|
||||||
|
|
||||||
# For logging back into google chat
|
# For logging back into google chat
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ in
|
|||||||
home.packages = [
|
home.packages = [
|
||||||
pkgs.claude-code
|
pkgs.claude-code
|
||||||
pkgs.codex
|
pkgs.codex
|
||||||
pkgs.goose-cli
|
|
||||||
|
|
||||||
# Custom packages
|
# Custom packages
|
||||||
customPkgs.tea-rbw
|
customPkgs.tea-rbw
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ with lib;
|
|||||||
};
|
};
|
||||||
kodi = {
|
kodi = {
|
||||||
enable = true;
|
enable = true;
|
||||||
autologin = false;
|
autologin = true;
|
||||||
wayland = true;
|
wayland = true;
|
||||||
};
|
};
|
||||||
users.enable = true;
|
users.enable = true;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ with lib;
|
|||||||
kde = true;
|
kde = true;
|
||||||
sddm = true;
|
sddm = true;
|
||||||
wayland = true;
|
wayland = true;
|
||||||
|
x11 = true;
|
||||||
};
|
};
|
||||||
nfs-mounts.enable = true;
|
nfs-mounts.enable = true;
|
||||||
nvidia.enable = true;
|
nvidia.enable = true;
|
||||||
@@ -51,6 +52,11 @@ with lib;
|
|||||||
hardware.graphics.enable = true;
|
hardware.graphics.enable = true;
|
||||||
hardware.graphics.enable32Bit = true;
|
hardware.graphics.enable32Bit = true;
|
||||||
|
|
||||||
|
# Set DP-0 as primary display for system tray and multi-monitor setup
|
||||||
|
services.xserver.displayManager.sessionCommands = ''
|
||||||
|
${pkgs.xorg.xrandr}/bin/xrandr --output DP-0 --primary
|
||||||
|
'';
|
||||||
|
|
||||||
hardware.nvidia = {
|
hardware.nvidia = {
|
||||||
# Modesetting is required.
|
# Modesetting is required.
|
||||||
modesetting.enable = true;
|
modesetting.enable = true;
|
||||||
|
|||||||
176
packages/app-launcher-server/app-launcher-server.py
Normal file
176
packages/app-launcher-server/app-launcher-server.py
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Allowlisted applications that can be launched
|
||||||
|
ALLOWED_APPS = {
|
||||||
|
'firefox': 'firefox',
|
||||||
|
'kodi': 'kodi'
|
||||||
|
}
|
||||||
|
|
||||||
|
def is_app_running(app_name):
|
||||||
|
"""Check if an application is already running, returns (is_running, pid)"""
|
||||||
|
command = ALLOWED_APPS.get(app_name)
|
||||||
|
if not command:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
logger.debug(f"Looking for processes related to app '{app_name}' (command: '{command}')")
|
||||||
|
|
||||||
|
for proc in psutil.process_iter(['name', 'cmdline', 'pid']):
|
||||||
|
try:
|
||||||
|
proc_name = proc.info['name']
|
||||||
|
cmdline = proc.info['cmdline'] or []
|
||||||
|
|
||||||
|
logger.debug(f"Checking process PID {proc.info['pid']}: name='{proc_name}', cmdline={cmdline}")
|
||||||
|
|
||||||
|
# Check multiple patterns for the application:
|
||||||
|
# 1. Process name exactly matches command
|
||||||
|
# 2. Process name contains the command (e.g., "kodi.bin" contains "kodi")
|
||||||
|
# 3. Command line starts with the command
|
||||||
|
# 4. Command line contains the wrapped version (e.g., ".kodi-wrapped")
|
||||||
|
# 5. Any command line argument ends with the command executable
|
||||||
|
|
||||||
|
matches = False
|
||||||
|
match_reason = ""
|
||||||
|
|
||||||
|
if proc_name == command:
|
||||||
|
matches = True
|
||||||
|
match_reason = f"exact process name match: '{proc_name}'"
|
||||||
|
elif command in proc_name:
|
||||||
|
matches = True
|
||||||
|
match_reason = f"process name contains command: '{proc_name}' contains '{command}'"
|
||||||
|
elif cmdline and cmdline[0] == command:
|
||||||
|
matches = True
|
||||||
|
match_reason = f"exact cmdline match: '{cmdline[0]}'"
|
||||||
|
elif cmdline and cmdline[0].endswith('/' + command):
|
||||||
|
matches = True
|
||||||
|
match_reason = f"cmdline path ends with command: '{cmdline[0]}'"
|
||||||
|
elif cmdline and any(f'.{command}-wrapped' in arg for arg in cmdline):
|
||||||
|
matches = True
|
||||||
|
match_reason = f"wrapped command in cmdline: {cmdline}"
|
||||||
|
elif cmdline and any(f'{command}.bin' in arg for arg in cmdline):
|
||||||
|
matches = True
|
||||||
|
match_reason = f"binary command in cmdline: {cmdline}"
|
||||||
|
|
||||||
|
if matches:
|
||||||
|
logger.info(f"Found running {app_name} process: PID {proc.info['pid']} ({match_reason})")
|
||||||
|
return True, proc.info['pid']
|
||||||
|
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.debug(f"No running process found for {app_name}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
class AppLauncherHandler(BaseHTTPRequestHandler):
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
logger.info(format % args)
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
if self.path == '/':
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type', 'application/json')
|
||||||
|
self.end_headers()
|
||||||
|
response = {
|
||||||
|
'status': 'running',
|
||||||
|
'available_apps': list(ALLOWED_APPS.keys()),
|
||||||
|
'usage': 'POST /launch/<app_name> to launch an application'
|
||||||
|
}
|
||||||
|
self.wfile.write(json.dumps(response, indent=2).encode())
|
||||||
|
else:
|
||||||
|
self.send_error(404)
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
parsed_path = urlparse(self.path)
|
||||||
|
path_parts = parsed_path.path.strip('/').split('/')
|
||||||
|
|
||||||
|
if len(path_parts) == 2 and path_parts[0] == 'launch':
|
||||||
|
app_name = path_parts[1]
|
||||||
|
self.launch_app(app_name)
|
||||||
|
else:
|
||||||
|
self.send_error(404, "Invalid endpoint. Use /launch/<app_name>")
|
||||||
|
|
||||||
|
def launch_app(self, app_name):
|
||||||
|
if app_name not in ALLOWED_APPS:
|
||||||
|
self.send_error(400, f"Application '{app_name}' not allowed. Available apps: {list(ALLOWED_APPS.keys())}")
|
||||||
|
return
|
||||||
|
|
||||||
|
command = ALLOWED_APPS[app_name]
|
||||||
|
|
||||||
|
# Check if app is already running
|
||||||
|
is_running, existing_pid = is_app_running(app_name)
|
||||||
|
if is_running:
|
||||||
|
logger.info(f"Application {app_name} is already running (PID: {existing_pid}), skipping launch")
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type', 'application/json')
|
||||||
|
self.end_headers()
|
||||||
|
response = {
|
||||||
|
'status': 'success',
|
||||||
|
'message': f'{app_name} is already running',
|
||||||
|
'pid': existing_pid,
|
||||||
|
'already_running': True
|
||||||
|
}
|
||||||
|
self.wfile.write(json.dumps(response).encode())
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Launch the application in the background
|
||||||
|
# Ensure we have the proper environment for GUI apps
|
||||||
|
env = os.environ.copy()
|
||||||
|
|
||||||
|
logger.info(f"Launching application: {command}")
|
||||||
|
process = subprocess.Popen(
|
||||||
|
[command],
|
||||||
|
env=env,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
start_new_session=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type', 'application/json')
|
||||||
|
self.end_headers()
|
||||||
|
response = {
|
||||||
|
'status': 'success',
|
||||||
|
'message': f'Successfully launched {app_name}',
|
||||||
|
'pid': process.pid,
|
||||||
|
'already_running': False
|
||||||
|
}
|
||||||
|
self.wfile.write(json.dumps(response).encode())
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.error(f"Application not found: {command}")
|
||||||
|
self.send_error(500, f"Application '{app_name}' not found on system")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error launching {command}: {e}")
|
||||||
|
self.send_error(500, f"Failed to launch {app_name}: {str(e)}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8081
|
||||||
|
|
||||||
|
server = HTTPServer(('0.0.0.0', port), AppLauncherHandler)
|
||||||
|
logger.info(f"App launcher server starting on port {port}")
|
||||||
|
logger.info(f"Available applications: {list(ALLOWED_APPS.keys())}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
server.serve_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logger.info("Server shutting down...")
|
||||||
|
server.server_close()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
10
packages/app-launcher-server/default.nix
Normal file
10
packages/app-launcher-server/default.nix
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{ pkgs }:
|
||||||
|
|
||||||
|
let
|
||||||
|
python = pkgs.python3.withPackages (ps: with ps; [
|
||||||
|
psutil
|
||||||
|
]);
|
||||||
|
in
|
||||||
|
pkgs.writeShellScriptBin "app-launcher-server" ''
|
||||||
|
exec ${python}/bin/python3 ${./app-launcher-server.py} "$@"
|
||||||
|
''
|
||||||
@@ -2,4 +2,5 @@
|
|||||||
{
|
{
|
||||||
vulkanHDRLayer = pkgs.callPackage ./vulkan-hdr-layer {};
|
vulkanHDRLayer = pkgs.callPackage ./vulkan-hdr-layer {};
|
||||||
tea-rbw = pkgs.callPackage ./tea-rbw {};
|
tea-rbw = pkgs.callPackage ./tea-rbw {};
|
||||||
|
app-launcher-server = pkgs.callPackage ./app-launcher-server {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ in
|
|||||||
ryubing
|
ryubing
|
||||||
dolphin-emu
|
dolphin-emu
|
||||||
];
|
];
|
||||||
|
# TODO: Remove me once dolphin-emu and dolphin-emu-primehack update
|
||||||
|
# dependencies to mbedtls from mbedtls_2 (which is currently)
|
||||||
|
# unmaintained
|
||||||
|
nixpkgs.config.permittedInsecurePackages = [ "mbedtls-2.28.10" ];
|
||||||
|
|
||||||
|
warnings = [
|
||||||
|
"Using insecure mbedtls-2.28.10 for Dolphin Emu - check for updates regularly"
|
||||||
|
];
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,14 @@ in
|
|||||||
|
|
||||||
windowManager.i3 = {
|
windowManager.i3 = {
|
||||||
enable = true;
|
enable = true;
|
||||||
extraPackages = with pkgs; [ dmenu i3status i3lock ];
|
extraPackages = with pkgs; [
|
||||||
|
dmenu
|
||||||
|
i3status
|
||||||
|
i3lock
|
||||||
|
polkit_gnome # GNOME polkit authentication agent (more stable with i3)
|
||||||
|
picom # Compositor for smooth rendering (important for Nvidia)
|
||||||
|
networkmanagerapplet # NetworkManager system tray applet
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ with lib;
|
|||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.roles.kodi;
|
cfg = config.roles.kodi;
|
||||||
|
customPkgs = pkgs.callPackage ../../packages {};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.roles.kodi = {
|
options.roles.kodi = {
|
||||||
@@ -14,6 +15,18 @@ in
|
|||||||
wayland = mkOption {
|
wayland = mkOption {
|
||||||
default = true;
|
default = true;
|
||||||
};
|
};
|
||||||
|
appLauncherServer = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Enable HTTP app launcher server for remote control";
|
||||||
|
};
|
||||||
|
port = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 8081;
|
||||||
|
description = "Port for the app launcher HTTP server";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -33,24 +46,39 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
networking.firewall = {
|
networking.firewall = {
|
||||||
allowedTCPPorts = [ 8080 ];
|
allowedTCPPorts = [ 8080 ] ++ optional cfg.appLauncherServer.enable cfg.appLauncherServer.port;
|
||||||
allowedUDPPorts = [ 8080 ];
|
allowedUDPPorts = [ 8080 ];
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
kodiPkg
|
kodiPkg
|
||||||
wget
|
wget
|
||||||
];
|
firefox
|
||||||
|
] ++ optional cfg.appLauncherServer.enable customPkgs.app-launcher-server;
|
||||||
|
|
||||||
programs.kdeconnect.enable = true;
|
programs.kdeconnect.enable = true;
|
||||||
|
|
||||||
services = if cfg.autologin then {
|
systemd.user.services = mkIf cfg.appLauncherServer.enable {
|
||||||
displayManager = {
|
app-launcher-server = {
|
||||||
autoLogin.enable = true;
|
description = "HTTP App Launcher Server";
|
||||||
autoLogin.user = "kodi";
|
wantedBy = [ "graphical-session.target" ];
|
||||||
defaultSession = "kodi";
|
after = [ "graphical-session.target" ];
|
||||||
sessionData.autologinSession = "plasma";
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
ExecStart = "${customPkgs.app-launcher-server}/bin/app-launcher-server ${toString cfg.appLauncherServer.port}";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "5s";
|
||||||
|
Environment = [
|
||||||
|
"PATH=${pkgs.firefox}/bin:${kodiPkg}/bin:/run/current-system/sw/bin"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
} else {};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.displayManager = mkIf cfg.autologin {
|
||||||
|
autoLogin.enable = true;
|
||||||
|
autoLogin.user = "kodi";
|
||||||
|
defaultSession = "plasma";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,5 +26,11 @@ in
|
|||||||
model = "everywhere";
|
model = "everywhere";
|
||||||
}];
|
}];
|
||||||
hardware.printers.ensureDefaultPrinter = "MFC-L8900CDW_series";
|
hardware.printers.ensureDefaultPrinter = "MFC-L8900CDW_series";
|
||||||
|
|
||||||
|
# Fix ensure-printers service to wait for network availability
|
||||||
|
systemd.services.ensure-printers = {
|
||||||
|
after = [ "cups.service" "network-online.target" ];
|
||||||
|
wants = [ "cups.service" "network-online.target" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user