Compare commits

...

17 Commits

Author SHA1 Message Date
c42e09e972 [i3] Add compositor 2025-10-27 19:03:54 -07:00
4d986c0b48 [home] Add wallpaper 2025-10-27 19:03:46 -07:00
4e3fdd78d2 Fix KDE systemsettings default applications menu issue
KDE applications in i3 were showing only "Other..." in default
applications due to missing applications.menu file. Plasma 6 renamed
applications.menu to plasma-applications.menu but KDE components
still look for the old name.

This creates a symlink from the new location to the expected location
using XDG config file management.

Fixes kmail and systemsettings not detecting installed applications.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 19:02:26 -07:00
d3703fc5a9 Add XDG desktop portal configuration for i3 + KDE integration
Enables proper desktop integration services when using KDE applications
in i3 window manager, including file dialogs, theme integration, and
screen sharing capabilities.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 19:01:57 -07:00
35c2ebb592 [zix790prors] Set correct refresh rate for x11 2025-10-27 16:51:19 -07:00
569ac528a5 [zix790prors] Add i3 + fixes 2025-10-26 12:00:13 -07:00
6cc8fa4f5d [printing] Ensure network is live 2025-10-25 15:05:19 -07:00
67a82f14fd [nixos] Downgrade from unstable to 25.05 2025-10-25 15:05:08 -07:00
4b68e3f051 [darwin] Configure AutoRaise
Add delays. This works way better with accordion views where the cursor
is often hovering right around window boundaries
2025-10-16 15:29:41 -07:00
81a3657759 [darwin] Add AutoRaise
provides focus-follows-mouse
2025-10-16 10:00:12 -07:00
32e1b81034 [aerospace] Fix fullscreen chord
Cmd-F is too ingrained in muscle memory for Find
2025-10-15 11:14:50 -07:00
6f00c72540 [app-launcher-server] process detection fixes 2025-10-14 18:09:36 -07:00
d26007aa61 [aerospace] More tweaking 2025-10-14 13:43:44 -07:00
1caa8bba3e [aerospace] Further tweaks 2025-10-14 08:26:51 -07:00
d3cb09040a [kodi] Fix autologin for boxy 2025-10-13 14:32:28 -07:00
4bfacffa17 [development] Remove goose 2025-10-13 14:26:08 -07:00
a6961f05ca [app-launcher] Add app-launcher to boxy 2025-10-13 14:25:51 -07:00
20 changed files with 525 additions and 40 deletions

47
flake.lock generated
View File

@@ -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": {

View File

@@ -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";
}; };

View File

@@ -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;
}; };

View File

@@ -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,32 @@ 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 --backend glx -b";
always = false;
notification = false;
}
# NetworkManager system tray applet
{
command = "nm-applet";
always = false;
notification = false;
}
# Set wallpaper with feh
{
command = "feh --bg-scale ${../../wallpapers/metroid-samus-returns-kz-3440x1440.jpg}";
always = false;
notification = false;
}
];
}; };
in { in {
enable = true; enable = true;
@@ -137,6 +169,11 @@ in {
dwt = "enabled"; dwt = "enabled";
}; };
}; };
output = {
"*" = {
bg = "${../../wallpapers/metroid-samus-returns-kz-3440x1440.jpg} fill";
};
};
}; };
in { in {
enable = true; enable = true;

View File

@@ -167,6 +167,12 @@
AutoRaiseInterval = 750; # Delay in ms before auto-raise (if enabled) AutoRaiseInterval = 750; # Delay in ms before auto-raise (if enabled)
DelayFocusInterval = 0; # Delay in ms before focus follows mouse DelayFocusInterval = 0; # Delay in ms before focus follows mouse
}; };
# Desktop wallpaper configuration
plasma-localerc.Formats.LANG = "en_US.UTF-8";
# Set wallpaper for all desktops
plasmarc.Wallpapers.usersWallpapers = "${../../wallpapers/metroid-samus-returns-kz-3440x1440.jpg}";
}; };
}; };
} }

View File

@@ -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";
}; };
}; };
}; };

View File

@@ -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

View File

@@ -19,9 +19,11 @@ in
kitty kitty
# Desktop utilities # Desktop utilities
feh # Image viewer and wallpaper setter for X11
solaar # Logitech management software solaar # Logitech management software
waybar waybar
wofi wofi
xdg-utils # XDG utilities for opening files/URLs with default applications
# System utilities with GUI components # System utilities with GUI components
(snapcast.override { pulseaudioSupport = true; }) (snapcast.override { pulseaudioSupport = true; })
@@ -37,6 +39,14 @@ in
kdePackages.kaddressbook kdePackages.kaddressbook
kdePackages.kontact kdePackages.kontact
# KDE System components needed for proper integration
kdePackages.kded
kdePackages.systemsettings
kdePackages.kmenuedit
# Desktop menu support
kdePackages.plasma-desktop # Contains applications.menu
# KDE Online Accounts support # KDE Online Accounts support
kdePackages.kaccounts-integration kdePackages.kaccounts-integration
kdePackages.kaccounts-providers kdePackages.kaccounts-providers
@@ -70,7 +80,79 @@ in
enable = true; enable = true;
}; };
xdg.enable = true; # KDE environment variables for proper integration
home.sessionVariables = {
QT_QPA_PLATFORMTHEME = "kde";
KDE_SESSION_VERSION = "6";
};
xdg = {
enable = true;
# Ensure desktop files are made available for discovery
desktopEntries = {}; # This creates the desktop files directory structure
mimeApps = {
enable = true;
associations.added = {
# Ensure associations are properly registered
"text/html" = "firefox.desktop";
"x-scheme-handler/http" = "firefox.desktop";
"x-scheme-handler/https" = "firefox.desktop";
};
defaultApplications = {
# Web browsers
"text/html" = "firefox.desktop";
"x-scheme-handler/http" = "firefox.desktop";
"x-scheme-handler/https" = "firefox.desktop";
"x-scheme-handler/about" = "firefox.desktop";
"x-scheme-handler/unknown" = "firefox.desktop";
# Documents
"application/pdf" = "okular.desktop";
"text/plain" = "kate.desktop";
"text/x-tex" = "kate.desktop";
"text/x-c" = "kate.desktop";
"text/x-python" = "kate.desktop";
"application/x-shellscript" = "kate.desktop";
# Images
"image/png" = "gwenview.desktop";
"image/jpeg" = "gwenview.desktop";
"image/jpg" = "gwenview.desktop";
"image/gif" = "gwenview.desktop";
"image/bmp" = "gwenview.desktop";
"image/tiff" = "gwenview.desktop";
"image/webp" = "gwenview.desktop";
# Archives
"application/zip" = "ark.desktop";
"application/x-tar" = "ark.desktop";
"application/x-compressed-tar" = "ark.desktop";
"application/x-7z-compressed" = "ark.desktop";
"application/x-rar" = "ark.desktop";
# Audio
"audio/mpeg" = "elisa.desktop";
"audio/mp4" = "elisa.desktop";
"audio/flac" = "elisa.desktop";
"audio/ogg" = "elisa.desktop";
"audio/wav" = "elisa.desktop";
# Email
"message/rfc822" = "kmail.desktop";
"x-scheme-handler/mailto" = "kmail.desktop";
# Calendar
"text/calendar" = "korganizer.desktop";
"application/x-vnd.akonadi.calendar.event" = "korganizer.desktop";
};
};
};
# Fix for KDE applications.menu file issue on Plasma 6
# KDE still looks for applications.menu but Plasma 6 renamed it to plasma-applications.menu
xdg.configFile."menus/applications.menu".source = "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu";
# Note: modules must be imported at top-level home config # Note: modules must be imported at top-level home config
}; };

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@@ -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;

View File

@@ -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 with 164.90Hz refresh rate
services.xserver.displayManager.sessionCommands = ''
${pkgs.xorg.xrandr}/bin/xrandr --output DP-0 --mode 3440x1440 --rate 164.90 --primary
'';
hardware.nvidia = { hardware.nvidia = {
# Modesetting is required. # Modesetting is required.
modesetting.enable = true; modesetting.enable = true;

View 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()

View 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} "$@"
''

View File

@@ -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 {};
} }

View File

@@ -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"
];
}) })
]; ];
} }

View File

@@ -16,5 +16,22 @@ in
programs.dconf.enable = true; programs.dconf.enable = true;
services.gnome.gnome-keyring.enable = true; services.gnome.gnome-keyring.enable = true;
programs.kdeconnect.enable = true; programs.kdeconnect.enable = true;
# XDG Desktop Portal for default application handling in non-KDE environments
xdg.portal = {
enable = true;
extraPortals = with pkgs; [
kdePackages.xdg-desktop-portal-kde # For KDE application integration
xdg-desktop-portal-gtk # Fallback for GTK applications
];
config = {
common = {
default = "kde";
};
i3 = {
default = ["kde" "gtk"];
};
};
};
}; };
} }

View File

@@ -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
];
}; };
}; };
}; };

View File

@@ -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 = {
description = "HTTP App Launcher Server";
wantedBy = [ "graphical-session.target" ];
after = [ "graphical-session.target" ];
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"
];
};
};
};
services.displayManager = mkIf cfg.autologin {
autoLogin.enable = true; autoLogin.enable = true;
autoLogin.user = "kodi"; autoLogin.user = "kodi";
defaultSession = "kodi"; defaultSession = "plasma";
sessionData.autologinSession = "plasma";
}; };
} else {};
}; };
} }

View File

@@ -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" ];
};
}; };
} }