Compare commits
9 Commits
boxy-app-l
...
4b68e3f051
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b68e3f051 | |||
| 81a3657759 | |||
| 32e1b81034 | |||
| 6f00c72540 | |||
| d26007aa61 | |||
| 1caa8bba3e | |||
| d3cb09040a | |||
| 4bfacffa17 | |||
| a6961f05ca |
@@ -2,6 +2,7 @@
|
||||
|
||||
let
|
||||
customPkgs = pkgs.callPackage ../packages {};
|
||||
leader = "cmd"; # Change this to experiment with different leader keys (e.g., "cmd", "ctrl")
|
||||
in
|
||||
{
|
||||
# Provide arguments to role modules
|
||||
@@ -13,6 +14,26 @@ in
|
||||
home.homeDirectory = lib.mkForce "/Users/johno";
|
||||
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
|
||||
programs.rbw.settings.pinentry = lib.mkForce pkgs.pinentry_mac;
|
||||
|
||||
@@ -43,6 +64,86 @@ in
|
||||
|
||||
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 = {
|
||||
base.enable = true;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,6 @@ in
|
||||
home.packages = [
|
||||
pkgs.claude-code
|
||||
pkgs.codex
|
||||
pkgs.goose-cli
|
||||
|
||||
# Custom packages
|
||||
customPkgs.tea-rbw
|
||||
|
||||
@@ -24,7 +24,7 @@ with lib;
|
||||
};
|
||||
kodi = {
|
||||
enable = true;
|
||||
autologin = false;
|
||||
autologin = true;
|
||||
wayland = true;
|
||||
};
|
||||
users.enable = true;
|
||||
|
||||
@@ -7,6 +7,7 @@ import subprocess
|
||||
import sys
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from urllib.parse import urlparse
|
||||
import psutil
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
@@ -21,6 +22,60 @@ ALLOWED_APPS = {
|
||||
'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)
|
||||
@@ -56,6 +111,22 @@ class AppLauncherHandler(BaseHTTPRequestHandler):
|
||||
|
||||
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
|
||||
@@ -76,7 +147,8 @@ class AppLauncherHandler(BaseHTTPRequestHandler):
|
||||
response = {
|
||||
'status': 'success',
|
||||
'message': f'Successfully launched {app_name}',
|
||||
'pid': process.pid
|
||||
'pid': process.pid,
|
||||
'already_running': False
|
||||
}
|
||||
self.wfile.write(json.dumps(response).encode())
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{ pkgs }:
|
||||
|
||||
let
|
||||
python = pkgs.python3.withPackages (ps: with ps; [
|
||||
psutil
|
||||
]);
|
||||
in
|
||||
pkgs.writeShellScriptBin "app-launcher-server" ''
|
||||
exec ${pkgs.python3}/bin/python3 ${./app-launcher-server.py} "$@"
|
||||
exec ${python}/bin/python3 ${./app-launcher-server.py} "$@"
|
||||
''
|
||||
@@ -75,13 +75,10 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
services = if cfg.autologin then {
|
||||
displayManager = {
|
||||
autoLogin.enable = true;
|
||||
autoLogin.user = "kodi";
|
||||
defaultSession = "kodi";
|
||||
sessionData.autologinSession = "plasma";
|
||||
};
|
||||
} else {};
|
||||
services.displayManager = mkIf cfg.autologin {
|
||||
autoLogin.enable = true;
|
||||
autoLogin.user = "kodi";
|
||||
defaultSession = "plasma";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user