Change:
requires to be running in order to be used otherwise it aborts and crashes (see ). It would make sense to have a NixOS configuration option in order to automatically configure to run. Some NixOS module option (perhaps ) that automatically configures and runs as a systemd service in the background.
This issue has been mentioned on NixOS Discourse. There might be relevant details there:

Discussion:
- [isolate](https://github.com/ioi/isolate), a sandbox for securely executing untrusted programs. Available as [security.isolate](#opt-security.isolate.enable). <ins> - [ydotool](https://github.com/ReimuNotMoe/ydotool), a generic command-line automation tool now has a module. Available as [programs.ydotool](#opt-programs.ydotool.enable). </ins> - [private-gpt](https://github.com/zylon-ai/private-gpt), a service to interact with your documents using the power of LLMs, 100% privately, no data leaks. Available as [services.private-gpt](#opt-services.private-gpt.enable). ## Backward Incompatibilities {#sec-release-24.05-incompatibilities}

./programs/xwayland.nix ./programs/yabar.nix ./programs/yazi.nix <ins> ./programs/ydotool.nix </ins> ./programs/yubikey-touch-detector.nix ./programs/zmap.nix ./programs/zsh/oh-my-zsh.nix

<ins> { config, lib, pkgs, ... }: let cfg = config.programs.ydotool; in { meta = { maintainers = with lib.maintainers; [ quantenzitrone ]; }; options.programs.ydotool = { enable = lib.mkEnableOption '' ydotoold system service and install ydotool. Add yourself to the 'ydotool' group to be able to use it. ''; }; config = lib.mkIf cfg.enable { users.groups.ydotool = { }; systemd.services.ydotoold = { description = "ydotoold - backend for ydotool"; wantedBy = [ "multi-user.target" ]; partOf = [ "multi-user.target" ]; serviceConfig = { Group = "ydotool"; RuntimeDirectory = "ydotoold"; RuntimeDirectoryMode = "0750"; ExecStart = "${lib.getExe' pkgs.ydotool "ydotoold"} --socket-path=/run/ydotoold/socket --socket-perm=0660"; # hardening ## allow access to uinput DeviceAllow = [ "/dev/uinput" ]; DevicePolicy = "closed"; ## allow creation of unix sockets RestrictAddressFamilies = [ "AF_UNIX" ]; CapabilityBoundingSet = ""; IPAddressDeny = "any"; LockPersonality = true; MemoryDenyWriteExecute = true; NoNewPrivileges = true; PrivateNetwork = true; PrivateTmp = true; PrivateUsers = true; ProcSubset = "pid"; ProtectClock = true; ProtectControlGroups = true; ProtectHome = true; ProtectHostname = true; ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; ProtectProc = "invisible"; ProtectSystem = "strict"; ProtectUser = true; RestrictNamespaces = true; RestrictRealtime = true; RestrictSUIDSGID = true; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; UMask = "0077"; # -> systemd-analyze security score 0.7 SAFE 😀 }; }; environment.variables = { YDOTOOL_SOCKET = "/run/ydotoold/socket"; }; environment.systemPackages = with pkgs; [ ydotool ]; }; } </ins>

xterm = handleTest ./xterm.nix {}; xxh = handleTest ./xxh.nix {}; yabar = handleTest ./yabar.nix {}; <ins> ydotool = handleTest ./ydotool.nix {}; </ins> yggdrasil = handleTest ./yggdrasil.nix {}; zammad = handleTest ./zammad.nix {}; zeronet-conservancy = handleTest ./zeronet-conservancy.nix {};

<ins> import ./make-test-python.nix ( { pkgs, lib, ... }: let textInput = "This works."; inputBoxText = "Enter input"; inputBox = pkgs.writeShellScript "zenity-input" '' ${lib.getExe pkgs.gnome.zenity} --entry --text '${inputBoxText}:' > /tmp/output & ''; in { name = "ydotool"; meta = { maintainers = with lib.maintainers; [ OPNA2608 quantenzitrone ]; }; nodes = { headless = { config, ... }: { imports = [ ./common/user-account.nix ]; users.users.alice.extraGroups = [ "ydotool" ]; programs.ydotool.enable = true; services.getty.autologinUser = "alice"; }; x11 = { config, ... }: { imports = [ ./common/user-account.nix ./common/auto.nix ./common/x11.nix ]; users.users.alice.extraGroups = [ "ydotool" ]; programs.ydotool.enable = true; test-support.displayManager.auto = { enable = true; user = "alice"; }; services.xserver.windowManager.dwm.enable = true; services.displayManager.defaultSession = lib.mkForce "none+dwm"; }; wayland = { config, ... }: { imports = [ ./common/user-account.nix ]; services.cage = { enable = true; user = "alice"; }; programs.ydotool.enable = true; services.cage.program = inputBox; }; }; enableOCR = true; testScript = { nodes, ... }: '' def as_user(cmd: str): """ Return a shell command for running a shell command as a specific user. """ return f"sudo -u alice -i {cmd}" start_all() # Headless headless.wait_for_unit("multi-user.target") headless.wait_for_text("alice") headless.succeed(as_user("ydotool type 'echo ${textInput} > /tmp/output'")) # text input headless.succeed(as_user("ydotool key 28:1 28:0")) # text input headless.screenshot("headless_input") headless.wait_for_file("/tmp/output") headless.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input # X11 x11.wait_for_x() x11.execute(as_user("${inputBox}")) x11.wait_for_text("${inputBoxText}") x11.succeed(as_user("ydotool type '${textInput}'")) # text input x11.screenshot("x11_input") x11.succeed(as_user("ydotool mousemove -a 400 110")) # mouse input x11.succeed(as_user("ydotool click 0xC0")) # mouse input x11.wait_for_file("/tmp/output") x11.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input # Wayland wayland.wait_for_unit("graphical.target") wayland.wait_for_text("${inputBoxText}") wayland.succeed("ydotool type '${textInput}'") # text input wayland.screenshot("wayland_input") wayland.succeed("ydotool mousemove -a 100 100") # mouse input wayland.succeed("ydotool click 0xC0") # mouse input wayland.wait_for_file("/tmp/output") wayland.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input ''; } ) </ins>

<ins> { lib, stdenv, fetchFromGitHub, cmake, scdoc, util-linux, xorg, nixosTests, }: stdenv.mkDerivation (finalAttrs: { pname = "ydotool"; version = "1.0.4"; src = fetchFromGitHub { owner = "ReimuNotMoe"; repo = "ydotool"; rev = "v${finalAttrs.version}"; hash = "sha256-MtanR+cxz6FsbNBngqLE+ITKPZFHmWGsD1mBDk0OVng="; }; postPatch = '' substituteInPlace Daemon/ydotoold.c  --replace "/usr/bin/xinput" "${xorg.xinput}/bin/xinput" substituteInPlace Daemon/ydotool.service.in  --replace "/usr/bin/kill" "${util-linux}/bin/kill" ''; strictDeps = true; nativeBuildInputs = [ cmake scdoc ]; passthru.tests.basic = nixosTests.ydotool; meta = { description = "Generic Linux command-line automation tool"; homepage = "https://github.com/ReimuNotMoe/ydotool"; license = lib.licenses.agpl3Plus; mainProgram = "ydotool"; maintainers = with lib.maintainers; [ willibutz kraem ]; platforms = lib.platforms.linux; }; }) </ins>

<del> { lib, stdenv, fetchFromGitHub, cmake, scdoc, util-linux, xorg }: stdenv.mkDerivation rec { pname = "ydotool"; version = "1.0.4"; src = fetchFromGitHub { owner = "ReimuNotMoe"; repo = "ydotool"; rev = "v${version}"; hash = "sha256-MtanR+cxz6FsbNBngqLE+ITKPZFHmWGsD1mBDk0OVng="; }; postPatch = '' substituteInPlace Daemon/ydotoold.c  --replace "/usr/bin/xinput" "${xorg.xinput}/bin/xinput" substituteInPlace Daemon/ydotool.service.in  --replace "/usr/bin/kill" "${util-linux}/bin/kill" ''; strictDeps = true; nativeBuildInputs = [ cmake scdoc ]; meta = with lib; { homepage = "https://github.com/ReimuNotMoe/ydotool"; description = "Generic Linux command-line automation tool"; license = licenses.agpl3Plus; maintainers = with maintainers; [ willibutz kraem ]; platforms = with platforms; linux; }; } </del>

wtype = callPackage ../tools/wayland/wtype { }; <del> ydotool = callPackage ../tools/wayland/ydotool { }; </del> cambalache = callPackage ../development/tools/cambalache { }; cambrinary = python3Packages.callPackage ../applications/misc/cambrinary { };